diff options
145 files changed, 2570 insertions, 1374 deletions
diff --git a/.github/workflows/build_test.sh b/.github/workflows/build_test.sh index 4e6f6a1af1..b60db29efc 100755 --- a/.github/workflows/build_test.sh +++ b/.github/workflows/build_test.sh @@ -45,6 +45,7 @@ PACKAGES=( libxkbcommon-dev libxtables-dev libzstd-dev + mold mount net-tools perl @@ -121,27 +122,28 @@ ninja --version 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" diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 9a0f8f9bd3..cd6937e390 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -27,7 +27,7 @@ jobs: 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 }} diff --git a/.github/workflows/issue_labeler.yml b/.github/workflows/issue_labeler.yml index 094f72c4b9..3a7b0d044c 100644 --- a/.github/workflows/issue_labeler.yml +++ b/.github/workflows/issue_labeler.yml @@ -22,10 +22,6 @@ jobs: steps: - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - - uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 - with: - node-version: '16' - - name: Parse issue form uses: stefanbuck/github-issue-parser@fc06b2a0adc5ccb7702ab6b641fd8a742a5e9cc0 id: issue-parser diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml index dddeb5fcb3..c2c12f3393 100644 --- a/.github/workflows/mkosi.yml +++ b/.github/workflows/mkosi.yml @@ -45,13 +45,11 @@ jobs: - 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 @@ -70,22 +68,20 @@ jobs: 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; }" diff --git a/.github/workflows/run_mkosi.sh b/.github/workflows/run_mkosi.sh deleted file mode 100755 index acdb3fcee1..0000000000 --- a/.github/workflows/run_mkosi.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/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 diff --git a/.lgtm/cpp-queries/PotentiallyDangerousFunction.ql b/.lgtm/cpp-queries/PotentiallyDangerousFunction.ql index 39e8dddd13..63fd14e75f 100644 --- a/.lgtm/cpp-queries/PotentiallyDangerousFunction.ql +++ b/.lgtm/cpp-queries/PotentiallyDangerousFunction.ql @@ -46,6 +46,9 @@ predicate potentiallyDangerousFunction(Function f, string message) { ) 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." ) } @@ -2488,7 +2488,7 @@ CHANGES WITH 248: units. systemd-oomd is now considered fully supported (the usual - backwards-compatiblity promises apply). Swap is not required for + backwards-compatibility promises apply). Swap is not required for operation, but it is still recommended. * systemd-timesyncd gained a new ConnectionRetrySec= setting which @@ -77,6 +77,11 @@ Janitorial Clean-ups: * 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. diff --git a/docs/CODING_STYLE.md b/docs/CODING_STYLE.md index b7e700237a..ac35dc38d5 100644 --- a/docs/CODING_STYLE.md +++ b/docs/CODING_STYLE.md @@ -667,11 +667,11 @@ SPDX-License-Identifier: LGPL-2.1-or-later 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). diff --git a/hwdb.d/60-keyboard.hwdb b/hwdb.d/60-keyboard.hwdb index 095bb34cd8..9ed972f655 100644 --- a/hwdb.d/60-keyboard.hwdb +++ b/hwdb.d/60-keyboard.hwdb @@ -858,6 +858,7 @@ evdev:name:ThinkPad Extra Buttons:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:* KEYBOARD_KEY_17=prog1 KEYBOARD_KEY_1a=f20 # Microphone mute button; should be micmute KEYBOARD_KEY_45=bookmarks + KEYBOARD_KEY_46=prog2 # ThinkPad Keyboard with TrackPoint evdev:input:b0003v17EFp6009* diff --git a/man/bootctl.xml b/man/bootctl.xml index 85820ff606..d7eea90a97 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -248,11 +248,11 @@ <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> @@ -289,7 +289,7 @@ 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> @@ -342,9 +342,9 @@ <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> diff --git a/man/check-os-release.py b/man/check-os-release.py index 91a5494b4a..1a57c7a20d 100644 --- a/man/check-os-release.py +++ b/man/check-os-release.py @@ -17,7 +17,8 @@ def read_os_release(): 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) diff --git a/man/crypttab.xml b/man/crypttab.xml index a296949595..0469d365ef 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -109,15 +109,15 @@ 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 @@ -751,24 +751,25 @@ 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> diff --git a/man/journalctl.xml b/man/journalctl.xml index 97b781a4c7..fb7da5446e 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -161,9 +161,9 @@ <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 @@ -493,9 +493,9 @@ <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> @@ -765,11 +765,11 @@ <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> diff --git a/man/machinectl.xml b/man/machinectl.xml index 5bc82e5d1a..1b39e42e38 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -192,7 +192,7 @@ 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 @@ -205,40 +205,35 @@ <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> @@ -544,11 +539,9 @@ 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 @@ -586,9 +579,9 @@ <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> diff --git a/man/org.freedesktop.import1.xml b/man/org.freedesktop.import1.xml index 5be21dad6c..62d753e009 100644 --- a/man/org.freedesktop.import1.xml +++ b/man/org.freedesktop.import1.xml @@ -183,14 +183,14 @@ node /org/freedesktop/import1 { 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; diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index 4cc9f27891..dc67e01009 100644 --- a/man/org.freedesktop.login1.xml +++ b/man/org.freedesktop.login1.xml @@ -873,8 +873,9 @@ node /org/freedesktop/login1/seat/seat0 { <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 @@ -907,8 +908,8 @@ node /org/freedesktop/login1/seat/seat0 { 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> @@ -1000,7 +1001,7 @@ node /org/freedesktop/login1/user/_1000 { <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> @@ -1050,8 +1051,8 @@ node /org/freedesktop/login1/user/_1000 { 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> diff --git a/man/org.freedesktop.resolve1.xml b/man/org.freedesktop.resolve1.xml index 1dc6dcbd73..54f0a18418 100644 --- a/man/org.freedesktop.resolve1.xml +++ b/man/org.freedesktop.resolve1.xml @@ -606,7 +606,7 @@ node /org/freedesktop/resolve1 { <para>The <varname>DNSSECStatistics</varname> property contains information about the DNSSEC validations executed so far. It contains four 64-bit counters: the number of secure, insecure, bogus, and indeterminate DNSSEC validations so far. The counters are increased for each validated RRset, and - each non-existance proof. The secure counter is increased for each operation that successfully verified + each non-existence proof. The secure counter is increased for each operation that successfully verified a signed reply, the insecure counter is increased for each operation that successfully verified that an unsigned reply is rightfully unsigned. The bogus counter is increased for each operation where the validation did not check out and the data is likely to have been tempered with. Finally the diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 120ffbc8ef..689ca3ec88 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -1898,6 +1898,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { 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 { ... }; @@ -2229,6 +2230,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { <variablelist class="dbus-property" generated="True" extra-ref="Refs"/> + <variablelist class="dbus-property" generated="True" extra-ref="ActivationDetails"/> + <!--End of Autogenerated section--> <refsect2> @@ -2406,6 +2409,18 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { <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> @@ -10650,6 +10665,8 @@ node /org/freedesktop/systemd1/job/666 { @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 { ... }; @@ -10681,6 +10698,8 @@ node /org/freedesktop/systemd1/job/666 { <variablelist class="dbus-property" generated="True" extra-ref="State"/> + <variablelist class="dbus-property" generated="True" extra-ref="ActivationDetails"/> + <!--End of Autogenerated section--> <refsect2> @@ -10709,6 +10728,9 @@ node /org/freedesktop/systemd1/job/666 { <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> diff --git a/man/repart.d.xml b/man/repart.d.xml index 93c85b6138..d9ec521160 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -399,7 +399,7 @@ <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> @@ -493,9 +493,9 @@ <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> diff --git a/man/sd_bus_add_match.xml b/man/sd_bus_add_match.xml index 3de7a6a53e..429a2fd412 100644 --- a/man/sd_bus_add_match.xml +++ b/man/sd_bus_add_match.xml @@ -99,7 +99,7 @@ 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 diff --git a/man/sd_bus_enqueue_for_read.xml b/man/sd_bus_enqueue_for_read.xml index 82b91cbe3c..9bdf498195 100644 --- a/man/sd_bus_enqueue_for_read.xml +++ b/man/sd_bus_enqueue_for_read.xml @@ -39,7 +39,7 @@ <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 diff --git a/man/sd_pid_get_owner_uid.xml b/man/sd_pid_get_owner_uid.xml index 3e30acad8d..c516083a5b 100644 --- a/man/sd_pid_get_owner_uid.xml +++ b/man/sd_pid_get_owner_uid.xml @@ -229,12 +229,10 @@ <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> diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 8061743c56..5789b61a0a 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -215,7 +215,7 @@ multi-user.target reached after 47.820s in userspace <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 diff --git a/man/systemd-ask-password.xml b/man/systemd-ask-password.xml index 97735eb657..27f77751fe 100644 --- a/man/systemd-ask-password.xml +++ b/man/systemd-ask-password.xml @@ -218,9 +218,9 @@ <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> diff --git a/man/systemd-cat.xml b/man/systemd-cat.xml index aff295bd87..a4b6139a8c 100644 --- a/man/systemd-cat.xml +++ b/man/systemd-cat.xml @@ -115,12 +115,10 @@ <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> @@ -156,10 +154,9 @@ <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> diff --git a/man/systemd-debug-generator.xml b/man/systemd-debug-generator.xml index 81d1efb446..65de9caa9a 100644 --- a/man/systemd-debug-generator.xml +++ b/man/systemd-debug-generator.xml @@ -37,7 +37,7 @@ <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 diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 8ac4de4047..a8c089d0b5 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -62,7 +62,7 @@ </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> @@ -138,6 +138,11 @@ </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> <entry>OpenVZ/Virtuozzo</entry> diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml index 66d19a0072..65cb2e503e 100644 --- a/man/systemd-measure.xml +++ b/man/systemd-measure.xml @@ -92,6 +92,14 @@ </varlistentry> <varlistentry> + <term><option>--current</option></term> + <listitem><para>When used with the <command>calculate</command> verb, takes the PCR 11 values + currently in effect for the system (which should typically reflect the hashes of the currently booted + kernel). This can be used in place of <option>--linux=</option> and the other switches listed + above.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>--bank=DIGEST</option></term> <listitem><para>Controls the PCR banks to pre-calculate the PCR values for – in case diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 975ddd0be3..a5058a7811 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -660,7 +660,7 @@ <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> @@ -737,9 +737,9 @@ 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 @@ -1235,8 +1235,8 @@ After=sys-subsystem-net-devices-ens1.device</programlisting> <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 diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 794ff635ed..00f1586a5b 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -67,11 +67,12 @@ 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 @@ -234,8 +235,8 @@ <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> @@ -354,9 +355,9 @@ <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> diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml index f4dd150a53..aa0d42d83c 100644 --- a/man/systemd-sysext.xml +++ b/man/systemd-sysext.xml @@ -26,6 +26,7 @@ <cmdsynopsis> <command>systemd-sysext</command> <arg choice="opt" rep="repeat">OPTIONS</arg> + <arg choice="plain">COMMAND</arg> </cmdsynopsis> <para><literallayout><filename>systemd-sysext.service</filename></literallayout></para> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index f90a1e25d0..2e843be2e5 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1227,7 +1227,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> 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> @@ -2536,14 +2536,15 @@ SystemCallErrorNumber=EPERM</programlisting> <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 @@ -2933,8 +2934,8 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX <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 @@ -3381,14 +3382,14 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX <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> @@ -3426,7 +3427,7 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX <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> @@ -3694,6 +3695,21 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX 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 diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml index d50e89f0bb..320b782992 100644 --- a/man/systemd.journal-fields.xml +++ b/man/systemd.journal-fields.xml @@ -398,6 +398,14 @@ 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> diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 76a924cec7..818df99e5a 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1900,12 +1900,12 @@ Table=1234</programlisting></para> <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 @@ -2399,11 +2399,11 @@ Token=prefixstable:2002:da8:1::</programlisting></para> <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 @@ -2924,7 +2924,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para> <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 @@ -2979,7 +2979,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para> <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> diff --git a/man/systemd.path.xml b/man/systemd.path.xml index 0392f0dae0..f143208cb4 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -122,7 +122,7 @@ <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 @@ -208,6 +208,10 @@ <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>, diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 2c36e390f6..98c8bf8c6d 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -1152,9 +1152,9 @@ <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> diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 49bcb18be5..953faa9b33 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -366,6 +366,10 @@ <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>, diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index c7def6bdcb..767c7186cd 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1263,6 +1263,7 @@ <literal>uml</literal>, <literal>bhyve</literal>, <literal>qnx</literal>, + <literal>apple</literal>, <literal>openvz</literal>, <literal>lxc</literal>, <literal>lxc-libvirt</literal>, diff --git a/meson.build b/meson.build index aa2ae27a93..e80634de7a 100644 --- a/meson.build +++ b/meson.build @@ -3668,12 +3668,6 @@ if conf.get('ENABLE_REPART') == 1 install : true, install_dir : rootbindir) public_programs += exe - - if want_tests != 'false' - test('test-repart', - test_repart_sh, - args : [exe.full_path(), udevadm.full_path()]) - endif endif executable( diff --git a/mkosi.build b/mkosi.build index 210811e768..76a813bf0a 100755 --- a/mkosi.build +++ b/mkosi.build @@ -47,6 +47,15 @@ if [ "$(locale charmap 2>/dev/null)" != "UTF-8" ] ; then 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) @@ -66,7 +75,81 @@ if [ ! -f "$BUILDDIR"/build.ninja ] ; then -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" @@ -172,3 +255,7 @@ TTYVHangup=no 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" diff --git a/mkosi.default.d/10-systemd.conf b/mkosi.default.d/10-systemd.conf index 8efd1e97fa..1c69dc46e0 100644 --- a/mkosi.default.d/10-systemd.conf +++ b/mkosi.default.d/10-systemd.conf @@ -14,6 +14,46 @@ OutputDirectory=mkosi.output 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 diff --git a/mkosi.default.d/arch/10-mkosi.arch b/mkosi.default.d/arch/10-mkosi.arch index 12f46c71c7..bd54b08c54 100644 --- a/mkosi.default.d/arch/10-mkosi.arch +++ b/mkosi.default.d/arch/10-mkosi.arch @@ -9,65 +9,28 @@ 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 diff --git a/mkosi.default.d/centos_epel/10-mkosi.centos_epel b/mkosi.default.d/centos_epel/10-mkosi.centos_epel index 5e726d4aef..482b5935ce 100644 --- a/mkosi.default.d/centos_epel/10-mkosi.centos_epel +++ b/mkosi.default.d/centos_epel/10-mkosi.centos_epel @@ -11,39 +11,54 @@ Format=gpt_xfs 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) @@ -61,30 +76,3 @@ BuildPackages= 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 diff --git a/mkosi.default.d/debian/10-mkosi.debian b/mkosi.default.d/debian/10-mkosi.debian index 2488eeb557..2b712d6778 100644 --- a/mkosi.default.d/debian/10-mkosi.debian +++ b/mkosi.default.d/debian/10-mkosi.debian @@ -8,17 +8,31 @@ Distribution=debian 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 @@ -26,59 +40,28 @@ BuildPackages= 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 diff --git a/mkosi.default.d/fedora/10-mkosi.fedora b/mkosi.default.d/fedora/10-mkosi.fedora index c1d8a57557..9561117789 100644 --- a/mkosi.default.d/fedora/10-mkosi.fedora +++ b/mkosi.default.d/fedora/10-mkosi.fedora @@ -8,29 +8,42 @@ Distribution=fedora 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) @@ -39,8 +52,6 @@ BuildPackages= pkgconfig(libgcrypt) pkgconfig(libidn2) pkgconfig(libkmod) - pkgconfig(liblz4) - pkgconfig(liblzma) pkgconfig(libmicrohttpd) pkgconfig(libpcre2-8) pkgconfig(libqrencode) @@ -58,33 +69,3 @@ BuildPackages= 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 diff --git a/mkosi.default.d/opensuse/10-mkosi.opensuse b/mkosi.default.d/opensuse/10-mkosi.opensuse index 16fdecdede..8dbb1dc50f 100644 --- a/mkosi.default.d/opensuse/10-mkosi.opensuse +++ b/mkosi.default.d/opensuse/10-mkosi.opensuse @@ -8,72 +8,59 @@ Distribution=opensuse 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 diff --git a/mkosi.default.d/ubuntu/10-mkosi.ubuntu b/mkosi.default.d/ubuntu/10-mkosi.ubuntu index 2d73746f3f..60e1bcfa66 100644 --- a/mkosi.default.d/ubuntu/10-mkosi.ubuntu +++ b/mkosi.default.d/ubuntu/10-mkosi.ubuntu @@ -9,75 +9,60 @@ Release=jammy 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 diff --git a/mkosi.postinst b/mkosi.postinst index 1c24b4f51a..fb59d31115 100755 --- a/mkosi.postinst +++ b/mkosi.postinst @@ -18,6 +18,9 @@ EOF # `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 diff --git a/shell-completion/bash/systemd-sysext b/shell-completion/bash/systemd-sysext new file mode 100644 index 0000000000..b3f9f32fd5 --- /dev/null +++ b/shell-completion/bash/systemd-sysext @@ -0,0 +1,85 @@ +# systemd-sysext(8) completion -*- shell-script -*- +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see <https://www.gnu.org/licenses/>. + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_systemd-sysext() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword + local -A OPTS=( + [STANDALONE]='-h --help --version + --no-pager + --no-legend + --force' + [ARG]='--root + --json' + ) + + local -A VERBS=( + [STANDALONE]='status + merge + unmerge + refresh + list' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --root) + comps=$(compgen -A directory -- "$cur" ) + compopt -o dirnames + ;; + --json) + comps='pretty short off' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z ${verb-} ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _systemd-sysext systemd-sysext diff --git a/src/analyze/analyze-verify-util.c b/src/analyze/analyze-verify-util.c index 531144ba57..c309c07516 100644 --- a/src/analyze/analyze-verify-util.c +++ b/src/analyze/analyze-verify-util.c @@ -61,9 +61,9 @@ int verify_prepare_filename(const char *filename, char **ret) { 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) @@ -73,24 +73,30 @@ int verify_prepare_filename(const char *filename, char **ret) { 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. @@ -106,10 +112,11 @@ int verify_generate_path(char **var, char **filenames) { return r; } - *var = strv_join(ans, ":"); - if (!*var) + joined = strv_join(ans, ":"); + if (!joined) return -ENOMEM; + *ret = TAKE_PTR(joined); return 0; } diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c index e93419d635..ce50ff9726 100644 --- a/src/basic/chase-symlinks.c +++ b/src/basic/chase-symlinks.c @@ -417,12 +417,12 @@ int chase_symlinks_and_open( _cleanup_free_ char *p = NULL; int r; - if (chase_flags & CHASE_NONEXISTENT) + if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)) return -EINVAL; if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { /* Shortcut this call if none of the special features of this call are requested */ - r = open(path, open_flags); + r = open(path, open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); if (r < 0) return -errno; @@ -458,7 +458,7 @@ int chase_symlinks_and_opendir( if (!ret_dir) return -EINVAL; - if (chase_flags & CHASE_NONEXISTENT) + if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)) return -EINVAL; if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { @@ -502,12 +502,13 @@ int chase_symlinks_and_stat( assert(path); assert(ret_stat); - if (chase_flags & CHASE_NONEXISTENT) + if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)) return -EINVAL; - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0 && !ret_fd) { /* Shortcut this call if none of the special features of this call are requested */ - if (stat(path, ret_stat) < 0) + + if (fstatat(AT_FDCWD, path, ret_stat, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0) return -errno; return 1; @@ -529,6 +530,49 @@ int chase_symlinks_and_stat( return 1; } +int chase_symlinks_and_access( + const char *path, + const char *root, + ChaseSymlinksFlags chase_flags, + int access_mode, + char **ret_path, + int *ret_fd) { + + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + int r; + + assert(path); + + if (chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)) + return -EINVAL; + + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0 && !ret_fd) { + /* Shortcut this call if none of the special features of this call are requested */ + + if (faccessat(AT_FDCWD, path, access_mode, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0) + return -errno; + + return 1; + } + + r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); + if (r < 0) + return r; + assert(path_fd >= 0); + + r = access_fd(path_fd, access_mode); + if (r < 0) + return r; + + if (ret_path) + *ret_path = TAKE_PTR(p); + if (ret_fd) + *ret_fd = TAKE_FD(path_fd); + + return 1; +} + int chase_symlinks_and_fopen_unlocked( const char *path, const char *root, diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h index 491138a698..7e45b0cbab 100644 --- a/src/basic/chase-symlinks.h +++ b/src/basic/chase-symlinks.h @@ -28,5 +28,5 @@ int chase_symlinks(const char *path_with_prefix, const char *root, ChaseSymlinks int chase_symlinks_and_open(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int open_flags, char **ret_path); int chase_symlinks_and_opendir(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, DIR **ret_dir); int chase_symlinks_and_stat(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); - +int chase_symlinks_and_access(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int access_mode, char **ret_path, int *ret_fd); int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, ChaseSymlinksFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file); diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 82c6dc5677..532c9d19b8 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -5,6 +5,7 @@ #include <stdio.h> #include <stdlib.h> +#include "chase-symlinks.h" #include "conf-files.h" #include "def.h" #include "dirent-util.h" @@ -28,23 +29,19 @@ static int files_add( unsigned flags, const char *path) { + _cleanup_free_ char *dirpath = NULL; _cleanup_closedir_ DIR *dir = NULL; - const char *dirpath; int r; assert(h); assert((flags & CONF_FILES_FILTER_MASKED) == 0 || masked); assert(path); - dirpath = prefix_roota(root, path); - - dir = opendir(dirpath); - if (!dir) { - if (errno == ENOENT) - return 0; - - return log_debug_errno(errno, "Failed to open directory '%s': %m", dirpath); - } + r = chase_symlinks_and_opendir(path, root, CHASE_PREFIX_ROOT, &dirpath, &dir); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_debug_errno(r, "Failed to open directory '%s/%s': %m", empty_or_root(root) ? "" : root, dirpath); FOREACH_DIRENT(de, dir, return -errno) { struct stat st; diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c index e026b29478..fd60a6eda2 100644 --- a/src/basic/glob-util.c +++ b/src/basic/glob-util.c @@ -47,17 +47,28 @@ int safe_glob(const char *path, int flags, glob_t *pglob) { 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; } diff --git a/src/basic/glob-util.h b/src/basic/glob-util.h index fc86e990dd..7ca26cc27f 100644 --- a/src/basic/glob-util.h +++ b/src/basic/glob-util.h @@ -10,7 +10,9 @@ /* 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); diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index d2c6b96a38..cd966cba94 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -191,34 +191,37 @@ int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, g } 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) @@ -230,7 +233,7 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m 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; diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 88657d5775..c5f30b5be4 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -7,12 +7,6 @@ #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" @@ -774,27 +768,6 @@ int fsck_exists(const char *fstype) { 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 == '/') diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 757ed722d5..41bbc7bb86 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -151,7 +151,6 @@ int fsck_exists(const char *fstype); _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); diff --git a/src/basic/virt.c b/src/basic/virt.c index e5277b4a4b..fd8954b3be 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -156,21 +156,22 @@ static Virtualization detect_vm_dmi_vendor(void) { 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; @@ -1018,6 +1019,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_QNX] = "qnx", [VIRTUALIZATION_ACRN] = "acrn", [VIRTUALIZATION_POWERVM] = "powervm", + [VIRTUALIZATION_APPLE] = "apple", [VIRTUALIZATION_VM_OTHER] = "vm-other", [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", diff --git a/src/basic/virt.h b/src/basic/virt.h index 6da08b4af1..e19a238939 100644 --- a/src/basic/virt.h +++ b/src/basic/virt.h @@ -25,6 +25,7 @@ typedef enum Virtualization { VIRTUALIZATION_QNX, VIRTUALIZATION_ACRN, VIRTUALIZATION_POWERVM, + VIRTUALIZATION_APPLE, VIRTUALIZATION_VM_OTHER, VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER, diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c index 315a1a37ed..554a716d8a 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -399,7 +399,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { } 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; @@ -448,12 +448,12 @@ static int verb_set(int argc, char *argv[], void *userdata) { 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; @@ -463,22 +463,18 @@ static int verb_set(int argc, char *argv[], void *userdata) { /* 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"); diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index ddeeed0c3d..33bde8b30e 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -476,7 +476,7 @@ static int enumerate_binaries( bool *is_first) { _cleanup_closedir_ DIR *d = NULL; - const char *p; + _cleanup_free_ char *p = NULL; int c = 0, r; assert(esp_path); @@ -484,14 +484,11 @@ static int enumerate_binaries( assert(previous); assert(is_first); - p = prefix_roota(esp_path, path); - d = opendir(p); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to read \"%s\": %m", p); - } + r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT, &p, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to read \"%s/%s\": %m", esp_path, path); FOREACH_DIRENT(de, d, break) { _cleanup_free_ char *v = NULL; @@ -1116,11 +1113,15 @@ static int remove_from_order(uint16_t slot) { return 0; } -static int install_variables(const char *esp_path, - uint32_t part, uint64_t pstart, uint64_t psize, - sd_id128_t uuid, const char *path, - bool first) { - const char *p; +static int install_variables( + const char *esp_path, + uint32_t part, + uint64_t pstart, + uint64_t psize, + sd_id128_t uuid, + const char *path, + bool first) { + uint16_t slot; int r; @@ -1135,13 +1136,11 @@ static int install_variables(const char *esp_path, return 0; } - p = prefix_roota(esp_path, path); - if (access(p, F_OK) < 0) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Cannot access \"%s\": %m", p); - } + r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT, F_OK, NULL, NULL); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Cannot access \"%s/%s\": %m", esp_path, path); r = find_slot(uuid, path, &slot); if (r < 0) @@ -1165,17 +1164,14 @@ static int install_variables(const char *esp_path, static int remove_boot_efi(const char *esp_path) { _cleanup_closedir_ DIR *d = NULL; - const char *p; + _cleanup_free_ char *p = NULL; int r, c = 0; - p = prefix_roota(esp_path, "/EFI/BOOT"); - d = opendir(p); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open directory \"%s\": %m", p); - } + r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT, &p, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path); FOREACH_DIRENT(de, d, break) { _cleanup_close_ int fd = -1; diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 95785dfd5d..efe056c225 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -60,7 +60,7 @@ efi_ld = get_option('efi-ld') 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 diff --git a/src/boot/measure.c b/src/boot/measure.c index bc1f3c8273..33117fe939 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -26,6 +26,7 @@ static char *arg_sections[_UNIFIED_SECTION_MAX] = {}; static char **arg_banks = NULL; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; +static bool arg_current = false; STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep); @@ -59,6 +60,7 @@ static int help(int argc, char *argv[], void *userdata) { " --initrd=PATH Path to initrd image\n" " --splash=PATH Path to splash bitmap\n" " --dtb=PATH Path to Devicetree file\n" + " -c --current Use current PCR values\n" " --bank=DIGEST Select TPM bank (SHA1, SHA256)\n" " --json=MODE Output as JSON\n" " -j Same as --json=pretty on tty, --json=short otherwise\n" @@ -99,6 +101,7 @@ static int parse_argv(int argc, char *argv[]) { { "initrd", required_argument, NULL, ARG_INITRD }, { "splash", required_argument, NULL, ARG_SPLASH }, { "dtb", required_argument, NULL, ARG_DTB }, + { "current", no_argument, NULL, 'c' }, { "bank", required_argument, NULL, ARG_BANK }, { "json", required_argument, NULL, ARG_JSON }, {} @@ -112,7 +115,7 @@ static int parse_argv(int argc, char *argv[]) { /* Make sure the arguments list and the section list, stays in sync */ assert_cc(_ARG_SECTION_FIRST + _UNIFIED_SECTION_MAX == _ARG_SECTION_LAST + 1); - while ((c = getopt_long(argc, argv, "hj", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hjc", options, NULL)) >= 0) switch (c) { case 'h': @@ -135,6 +138,10 @@ static int parse_argv(int argc, char *argv[]) { break; } + case 'c': + arg_current = true; + break; + case ARG_BANK: { const EVP_MD *implementation; @@ -176,6 +183,11 @@ static int parse_argv(int argc, char *argv[]) { strv_sort(arg_banks); strv_uniq(arg_banks); + if (arg_current) + for (UnifiedSection us = 0; us < _UNIFIED_SECTION_MAX; us++) + if (arg_sections[us]) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --current switch cannot be used in combination with --linux= and related switches."); + return 1; } @@ -255,6 +267,32 @@ static int measure_pcr(PcrState *pcr_states, size_t n) { assert(n > 0); assert(pcr_states); + 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(); + + r = read_virtual_file(p, 4096, &s, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read '%s': %m", p); + + 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(); @@ -345,8 +383,8 @@ static int verb_calculate(int argc, char *argv[], void *userdata) { size_t n = 0; int r; - if (!arg_sections[UNIFIED_SECTION_LINUX]) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--linux= switch must be specified, refusing."); + if (!arg_sections[UNIFIED_SECTION_LINUX] && !arg_current) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Either --linux= or --current must be specified, refusing."); pcr_states = new0(PcrState, strv_length(arg_banks) + 1); if (!pcr_states) diff --git a/src/core/automount.c b/src/core/automount.c index 5adec9e966..39c716fb81 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -134,12 +134,13 @@ static int automount_add_trigger_dependencies(Automount *a) { 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); } diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 8ecbd69031..746c7cdfed 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -2295,6 +2295,7 @@ static int unit_attach_pid_to_cgroup_via_bus(Unit *u, pid_t pid, const char *suf } int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) { + _cleanup_free_ char *joined = NULL; CGroupMask delegated_mask; const char *p; void *pidp; @@ -2320,8 +2321,13 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) { if (isempty(suffix_path)) p = u->cgroup_path; - else - p = prefix_roota(u->cgroup_path, suffix_path); + else { + joined = path_join(u->cgroup_path, suffix_path); + if (!joined) + return -ENOMEM; + + p = joined; + } delegated_mask = unit_get_delegate_mask(u); diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index de474e6d4e..7b1438266b 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -7,6 +7,7 @@ #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" @@ -136,6 +137,7 @@ const sd_bus_vtable bus_job_vtable[] = { 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 }; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index ee013e1bc5..7a16471758 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -951,6 +951,7 @@ const sd_bus_vtable bus_unit_vtable[] = { 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), diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c index 264a4f55b6..edfa0eb69a 100644 --- a/src/core/dbus-util.c +++ b/src/core/dbus-util.c @@ -228,3 +228,35 @@ int bus_read_mount_options( 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); +} diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h index 799136737b..e12631a0e2 100644 --- a/src/core/dbus-util.h +++ b/src/core/dbus-util.h @@ -251,3 +251,5 @@ static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t 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); diff --git a/src/core/efi-random.c b/src/core/efi-random.c index e8d8ccd117..4086b12739 100644 --- a/src/core/efi-random.c +++ b/src/core/efi-random.c @@ -76,7 +76,7 @@ int efi_take_random_seed(void) { return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring."); /* Before we use the seed, let's mark it as used, so that we never credit it twice. Also, it's a nice - * way to let users known that we successfully acquired entropy from the boot laoder. */ + * way to let users known that we successfully acquired entropy from the boot loader. */ r = touch("/run/systemd/efi-random-seed-taken"); if (r < 0) return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m"); diff --git a/src/core/execute.c b/src/core/execute.c index e68d231739..0ce18159bd 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3104,9 +3104,9 @@ static int setup_credentials_internal( /* 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; } diff --git a/src/core/job.c b/src/core/job.c index 6653dbde84..dd8d858bd2 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -104,6 +104,8 @@ Job* job_free(Job *j) { sd_bus_track_unref(j->bus_track); strv_free(j->deserialized_clients); + activation_details_unref(j->activation_details); + return mfree(j); } @@ -180,9 +182,13 @@ static void job_merge_into_installed(Job *j, Job *other) { 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; @@ -776,6 +782,7 @@ static void job_emit_done_message(Unit *u, uint32_t job_id, JobType t, JobResult } static int job_perform_on_unit(Job **j) { + ActivationDetails *a; uint32_t id; Manager *m; JobType t; @@ -795,10 +802,11 @@ static int job_perform_on_unit(Job **j) { 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: @@ -1160,6 +1168,8 @@ int job_serialize(Job *j, FILE *f) { bus_track_serialize(j->bus_track, f, "subscribed"); + activation_details_serialize(j->activation_details, f); + /* End marker */ fputc('\n', f); return 0; @@ -1257,6 +1267,11 @@ int job_deserialize(Job *j, FILE *f) { 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); } @@ -1636,3 +1651,11 @@ int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep) { 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); +} diff --git a/src/core/job.h b/src/core/job.h index c033c8a4fa..0305e0ea44 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -10,6 +10,7 @@ #include "unit-name.h" #include "unit.h" +typedef struct ActivationDetails ActivationDetails; typedef struct Job Job; typedef struct JobDependency JobDependency; typedef enum JobType JobType; @@ -151,6 +152,9 @@ struct Job { 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; @@ -243,3 +247,5 @@ JobResult job_result_from_string(const char *s) _pure_; 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); diff --git a/src/core/mount.c b/src/core/mount.c index 9dc04cd07e..52acd4345b 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -51,6 +51,9 @@ static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { 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) { @@ -300,9 +303,9 @@ static int mount_add_mount_dependencies(Mount *m) { /* 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) @@ -762,23 +765,17 @@ static void mount_set_state(Mount *m, MountState state) { 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) @@ -789,15 +786,52 @@ static int mount_coldplug(Unit *u) { 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; @@ -2227,6 +2261,7 @@ const UnitVTable mount_vtable = { .done = mount_done, .coldplug = mount_coldplug, + .catchup = mount_catchup, .dump = mount_dump, diff --git a/src/core/path.c b/src/core/path.c index 69bbddf158..2810e30573 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -197,9 +197,13 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) { 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: @@ -207,7 +211,7 @@ static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_no break; case PATH_EXISTS_GLOB: - good = glob_exists(s->path) > 0; + good = glob_first(s->path, &trigger) > 0; break; case PATH_DIRECTORY_NOT_EMPTY: { @@ -229,6 +233,15 @@ static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_no ; } + if (good) { + if (!trigger) { + trigger = strdup(s->path); + if (!trigger) + (void) log_oom_debug(); + } + *ret_trigger_path = TAKE_PTR(trigger); + } + return good; } @@ -494,9 +507,11 @@ static void path_enter_dead(Path *p, PathResult f) { 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); @@ -518,10 +533,22 @@ static void path_enter_running(Path *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); @@ -532,17 +559,19 @@ fail: 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; @@ -554,9 +583,9 @@ static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) 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; } @@ -568,9 +597,9 @@ static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) * 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; } @@ -759,7 +788,7 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v goto fail; if (changed) - path_enter_running(p); + path_enter_running(p, found->path); else path_enter_waiting(p, false, false); @@ -832,6 +861,89 @@ static int path_can_start(Unit *u) { 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", @@ -890,3 +1002,13 @@ const UnitVTable path_vtable = { .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, +}; diff --git a/src/core/path.h b/src/core/path.h index d835c24166..c76103cc12 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -3,6 +3,7 @@ typedef struct Path Path; typedef struct PathSpec PathSpec; +typedef struct ActivationDetailsPath ActivationDetailsPath; #include "unit.h" @@ -66,9 +67,15 @@ struct Path { 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_; @@ -77,3 +84,4 @@ const char* path_result_to_string(PathResult i) _const_; PathResult path_result_from_string(const char *s) _pure_; DEFINE_CAST(PATH, Path); +DEFINE_ACTIVATION_DETAILS_CAST(ACTIVATION_DETAILS_PATH, ActivationDetailsPath, PATH); diff --git a/src/core/service.c b/src/core/service.c index a715a1d1dc..fa37207dcc 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1641,6 +1641,16 @@ static int service_spawn_internal( } } + 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; diff --git a/src/core/timer.c b/src/core/timer.c index 9de325ba66..b89d593b75 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -576,8 +576,10 @@ fail: } 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); @@ -593,11 +595,20 @@ static void timer_enter_running(Timer *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); @@ -892,6 +903,91 @@ static int timer_can_start(Unit *u) { 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", @@ -954,3 +1050,12 @@ const UnitVTable timer_vtable = { .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, +}; diff --git a/src/core/timer.h b/src/core/timer.h index 551e283341..914e42580e 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -2,6 +2,7 @@ #pragma once typedef struct Timer Timer; +typedef struct ActivationDetailsTimer ActivationDetailsTimer; #include "calendarspec.h" #include "unit.h" @@ -64,11 +65,17 @@ struct Timer { 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_; @@ -77,3 +84,4 @@ const char* timer_result_to_string(TimerResult i) _const_; TimerResult timer_result_from_string(const char *s) _pure_; DEFINE_CAST(TIMER, Timer); +DEFINE_ACTIVATION_DETAILS_CAST(ACTIVATION_DETAILS_TIMER, ActivationDetailsTimer, TIMER); diff --git a/src/core/unit.c b/src/core/unit.c index 0798c29c9d..5f1c6109b0 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -41,6 +41,7 @@ #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" @@ -594,12 +595,10 @@ static void unit_remove_transient(Unit *u) { 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 */ @@ -809,6 +808,8 @@ Unit* unit_free(Unit *u) { set_free_free(u->aliases); free(u->id); + activation_details_unref(u->activation_details); + return mfree(u); } @@ -1190,6 +1191,9 @@ int unit_merge(Unit *u, Unit *other) { 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) @@ -1863,7 +1867,7 @@ static bool unit_verify_deps(Unit *u) { * -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; @@ -1920,7 +1924,7 @@ int unit_start(Unit *u) { 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. */ @@ -1941,6 +1945,9 @@ int unit_start(Unit *u) { 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); } @@ -5921,3 +5928,154 @@ int unit_get_dependency_array(const Unit *u, UnitDependencyAtom atom, Unit ***re 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); diff --git a/src/core/unit.h b/src/core/unit.h index 4dabbb9a3c..fc8edaade5 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -110,6 +110,75 @@ typedef union UnitDependencyInfo { } _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) { @@ -363,6 +432,9 @@ typedef struct Unit { 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; @@ -813,7 +885,7 @@ bool unit_can_start(Unit *u) _pure_; 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); diff --git a/src/delta/delta.c b/src/delta/delta.c index aa5a546bce..a08d35e43c 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -369,10 +369,12 @@ static int enumerate_dir( static int should_skip_path(const char *prefix, const char *suffix) { #if HAVE_SPLIT_USR - _cleanup_free_ char *target = NULL; - const char *dirname, *p; + _cleanup_free_ char *target = NULL, *dirname = NULL; + const char *p; - dirname = prefix_roota(prefix, suffix); + dirname = path_join(prefix, suffix); + if (!dirname) + return -ENOMEM; if (chase_symlinks(dirname, NULL, 0, &target, NULL) < 0) return false; diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index d9f3fab835..4ae24e7153 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -791,9 +791,9 @@ static int action_copy(DissectedImage *m, LoopDevice *d) { 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) diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index fa56a8322d..a95f384ecb 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -415,9 +415,9 @@ static int add_automount( const char *description, usec_t timeout) { - _cleanup_free_ char *unit = NULL; + _cleanup_free_ char *unit = NULL, *p = NULL; _cleanup_fclose_ FILE *f = NULL; - const char *opt = "noauto", *p; + const char *opt = "noauto"; int r; assert(id); @@ -443,7 +443,10 @@ static int add_automount( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - p = prefix_roota(arg_dest, unit); + p = path_join(arg_dest, unit); + if (!p) + return log_oom(); + f = fopen(p, "wxe"); if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", unit); diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 1d8ededcae..8a389f7216 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -2176,9 +2176,9 @@ static int home_get_disk_status_luks( 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); diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 5f3e79a67a..c83292df7d 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -1989,10 +1989,11 @@ static int wait_for_devlink(const char *path) { 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); @@ -2002,10 +2003,13 @@ static int wait_for_devlink(const char *path) { } 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); diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 5f09e6d0eb..8c0b63503f 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -415,7 +415,7 @@ static char* context_get_chassis(Context *c) { if (!isempty(c->data[PROP_CHASSIS])) return strdup(c->data[PROP_CHASSIS]); - if (get_dmi_data("ID_CHASSIS", NULL, &dmi) >= 0) + if (get_dmi_data("ID_CHASSIS", NULL, &dmi) > 0) return dmi; fallback = fallback_chassis(); diff --git a/src/import/export-raw.c b/src/import/export-raw.c index 6617a9c9b6..a3ff6a3934 100644 --- a/src/import/export-raw.c +++ b/src/import/export-raw.c @@ -2,12 +2,6 @@ #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" diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 15b6844d5d..0061abadff 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1035,6 +1035,11 @@ static void dispatch_message_real( 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)) diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 01244e1ce1..ee8f374190 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -179,7 +179,7 @@ struct Server { #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 diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index cba1ab2953..b553f15396 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -9,12 +9,6 @@ #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" diff --git a/src/libsystemd/sd-bus/test-bus-watch-bind.c b/src/libsystemd/sd-bus/test-bus-watch-bind.c index fdc8772f84..6e522ae54b 100644 --- a/src/libsystemd/sd-bus/test-bus-watch-bind.c +++ b/src/libsystemd/sd-bus/test-bus-watch-bind.c @@ -53,8 +53,7 @@ static void* thread_server(void *p) { 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); diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 3d670a3afc..d993b77b49 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -18,6 +18,7 @@ static inline int device_new_from_watch_handle(sd_device **ret, int wd) { } 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); diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 8574337bda..51bee24d51 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -2128,8 +2128,8 @@ int device_get_cached_sysattr_value(sd_device *device, const char *key, const ch /* We cache all sysattr lookups. If an attribute does not exist, it is stored * with a NULL value in the cache, otherwise the returned string is stored */ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) { - _cleanup_free_ char *value = NULL; - const char *path, *syspath; + _cleanup_free_ char *value = NULL, *path = NULL; + const char *syspath; struct stat statbuf; int r; @@ -2145,7 +2145,10 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, if (r < 0) return r; - path = prefix_roota(syspath, sysattr); + path = path_join(syspath, sysattr); + if (!path) + return -ENOMEM; + if (lstat(path, &statbuf) < 0) { int k; @@ -2203,6 +2206,25 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, 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; @@ -2227,8 +2249,8 @@ static void device_remove_cached_sysattr_value(sd_device *device, const char *_k } _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) { - _cleanup_free_ char *value = NULL; - const char *syspath, *path; + _cleanup_free_ char *value = NULL, *path = NULL; + const char *syspath; size_t len; int r; @@ -2247,7 +2269,9 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, if (r < 0) return r; - path = prefix_roota(syspath, sysattr); + path = path_join(syspath, sysattr); + if (!path) + return -ENOMEM; len = strlen(_value); diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index 8172c64e45..3deb9beaac 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -180,6 +180,13 @@ static void test_sd_device_one(sd_device *d) { 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) { diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c index 3318f9217d..77d03ec95f 100644 --- a/src/libsystemd/sd-journal/sd-journal.c +++ b/src/libsystemd/sd-journal/sd-journal.c @@ -1375,7 +1375,7 @@ static int add_file_by_name( const char *prefix, const char *filename) { - const char *path; + _cleanup_free_ char *path = NULL; assert(j); assert(prefix); @@ -1387,28 +1387,35 @@ static int add_file_by_name( if (!file_type_wanted(j->flags, filename)) return 0; - path = prefix_roota(prefix, filename); + path = path_join(prefix, filename); + if (!path) + return -ENOMEM; + return add_any_file(j, -1, path); } -static void remove_file_by_name( +static int remove_file_by_name( sd_journal *j, const char *prefix, const char *filename) { - const char *path; + _cleanup_free_ char *path = NULL; JournalFile *f; assert(j); assert(prefix); assert(filename); - path = prefix_roota(prefix, filename); + path = path_join(prefix, filename); + if (!path) + return -ENOMEM; + f = ordered_hashmap_get(j->files, path); if (!f) - return; + return 0; remove_file_real(j, f); + return 1; } static void remove_file_real(sd_journal *j, JournalFile *f) { @@ -1851,9 +1858,9 @@ static int add_current_paths(sd_journal *j) { _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) @@ -2620,7 +2627,7 @@ static void process_inotify_event(sd_journal *j, const struct inotify_event *e) if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) (void) add_file_by_name(j, d->path, e->name); else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) - remove_file_by_name(j, d->path, e->name); + (void) remove_file_by_name(j, d->path, e->name); } else if (!d->is_root && e->len == 0) { diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index bd203b37dd..87d04c3d58 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -4,12 +4,6 @@ #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" @@ -902,7 +896,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu } 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; @@ -910,7 +905,6 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro bool copy_from; pid_t child; uid_t uid_shift; - char *t; int r; assert(message); @@ -984,11 +978,13 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro 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) @@ -1019,9 +1015,9 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro 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; } diff --git a/src/partition/meson.build b/src/partition/meson.build index 5422fdd575..a7a6c29379 100644 --- a/src/partition/meson.build +++ b/src/partition/meson.build @@ -1,5 +1,3 @@ # SPDX-License-Identifier: LGPL-2.1-or-later systemd_repart_sources = files('repart.c') - -test_repart_sh = find_program('test-repart.sh') diff --git a/src/partition/repart.c b/src/partition/repart.c index f44e239b37..8824c105ff 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -1466,7 +1466,7 @@ static int determine_current_padding( offset *= secsz; n_partitions = fdisk_table_get_nents(t); - for (size_t i = 0; i < n_partitions; i++) { + for (size_t i = 0; i < n_partitions; i++) { struct fdisk_partition *q; uint64_t start; @@ -1731,7 +1731,7 @@ static int context_load_partition_table( return log_error_errno(r, "Failed to acquire partition table: %m"); n_partitions = fdisk_table_get_nents(t); - for (size_t i = 0; i < n_partitions; i++) { + for (size_t i = 0; i < n_partitions; i++) { _cleanup_free_ char *label_copy = NULL; Partition *last = NULL; struct fdisk_partition *p; @@ -2804,7 +2804,7 @@ static int context_copy_blocks(Context *context) { return log_error_errno(errno, "Failed to lock LUKS device: %m"); target_fd = encrypted_dev_fd; - } else { + } else { if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1) return log_error_errno(errno, "Failed to seek to partition offset: %m"); @@ -3115,7 +3115,7 @@ static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *re struct { sd_id128_t type_uuid; uint64_t counter; - } _packed_ plaintext = {}; + } _packed_ plaintext = {}; union { uint8_t md[SHA256_DIGEST_SIZE]; sd_id128_t id; diff --git a/src/partition/test-repart.sh b/src/partition/test-repart.sh deleted file mode 100755 index 1aac7ac108..0000000000 --- a/src/partition/test-repart.sh +++ /dev/null @@ -1,331 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -[[ -e /dev/loop-control ]] || exit 77 - -repart="${1:?}" -udevadm="${2:?}" -test -x "$repart" -test -x "$udevadm" - -PATH=$PATH:/sbin:/usr/sbin - -D="$(mktemp --tmpdir --directory "test-repart.XXXXXXXXXX")" - -# shellcheck disable=SC2064 -trap "rm -rf '$D'" EXIT INT QUIT PIPE -mkdir -p "$D/definitions" - -SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8 - -echo "### Testing systemd-repart --empty=create ###" - -"$repart" "$D/zzz" --empty=create --size=1G --seed="$SEED" --no-pager - -sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/empty" - -cmp "$D/empty" - <<EOF -label: gpt -label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D -device: $D/zzz -unit: sectors -first-lba: 2048 -last-lba: 2097118 -EOF - -echo "### Testing with root, root2, home, & swap ###" - -cat >"$D/definitions/root.conf" <<EOF -[Partition] -Type=root-x86-64 -EOF - -ln -s root.conf "$D/definitions/root2.conf" - -cat >"$D/definitions/home.conf" <<EOF -[Partition] -Type=home -Label=home-first -Label=home-always-too-long-xxxxxxxxxxxxxx-%v -EOF - -cat >"$D/definitions/swap.conf" <<EOF -[Partition] -Type=swap -SizeMaxBytes=64M -PaddingMinBytes=92M -EOF - -"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager - -sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated" - -cmp "$D/populated" - <<EOF -label: gpt -label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D -device: $D/zzz -unit: sectors -first-lba: 2048 -last-lba: 2097118 -$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59" -$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59" -$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59" -$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap" -EOF - -echo "### Testing with root, root2, home, swap, & another partition ###" - -cat >"$D/definitions/swap.conf" <<EOF -[Partition] -Type=swap -SizeMaxBytes=64M -EOF - -cat >"$D/definitions/extra.conf" <<EOF -[Partition] -Type=linux-generic -Label=custom_label -UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf -EOF - -echo "Label=ignored_label" >>"$D/definitions/home.conf" -echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$D/definitions/home.conf" - -"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager - -sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated2" - -cmp "$D/populated2" - <<EOF -label: gpt -label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D -device: $D/zzz -unit: sectors -first-lba: 2048 -last-lba: 2097118 -$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59" -$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59" -$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59" -$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap" -$D/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label" -EOF - -echo "### Resizing to 2G ###" - -"$repart" "$D/zzz" --size=2G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager - -sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated3" - -cmp "$D/populated3" - <<EOF -label: gpt -label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D -device: $D/zzz -unit: sectors -first-lba: 2048 -last-lba: 4194270 -$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59" -$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59" -$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59" -$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap" -$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label" -EOF - -dd if=/dev/urandom of="$D/block-copy" bs=4096 count=10240 - -echo "### Testing with root, root2, home, swap, another partition, & partition copy ###" - -cat >"$D/definitions/extra2.conf" <<EOF -[Partition] -Type=linux-generic -Label=block-copy -UUID=2a1d97e1d0a346cca26eadc643926617 -CopyBlocks=$D/block-copy -EOF - -"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager - -sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated4" - -cmp "$D/populated4" - <<EOF -label: gpt -label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D -device: $D/zzz -unit: sectors -first-lba: 2048 -last-lba: 6291422 -$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59" -$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59" -$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59" -$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap" -$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label" -$D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy" -EOF - -cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) "$D/block-copy" "$D/zzz" - -if [ "$(id -u)" -eq 0 ] && type -P cryptsetup diff losetup >/dev/null ; then - echo "### Testing Format=/Encrypt=/CopyFiles=" - - # These tests require privileges unfortunately - - cat >"$D/definitions/extra3.conf" <<EOF -[Partition] -Type=linux-generic -Label=luks-format-copy -UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0 -Format=ext4 -Encrypt=yes -CopyFiles=$D/definitions:/def -SizeMinBytes=48M -EOF - - "$repart" "$D/zzz" --size=auto --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager - - sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated5" - - cmp "$D/populated5" - <<EOF -label: gpt -label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D -device: $D/zzz -unit: sectors -first-lba: 2048 -last-lba: 6389726 -$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59" -$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59" -$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59" -$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap" -$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label" -$D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy" -$D/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name="luks-format-copy" -EOF - - LOOP="$(losetup -P --show --find "$D/zzz")" - "${udevadm:?}" wait --timeout 60 --settle "${LOOP:?}" - - VOLUME="test-repart-$RANDOM" - - touch "$D/empty-password" - cryptsetup open --type=luks2 --key-file="$D/empty-password" "${LOOP}p7" "$VOLUME" - mkdir "$D/mount" - mount -t ext4 "/dev/mapper/$VOLUME" "$D/mount" - # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount - cryptsetup close --deferred "$VOLUME" - losetup -d "$LOOP" - diff -r "$D/mount/def" "$D/definitions" >/dev/null - umount "$D/mount" -else - echo "### Skipping Format=/Encrypt=/CopyFiles= test, lacking privileges or missing cryptsetup/diff/losetup" -fi - -echo "### Testing json output ###" -"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=help -"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=pretty -"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=short - -echo "### Testing drop-in overrides ###" - -mkdir -p "$D/definitions-overrides" - -cat >"$D/definitions-overrides/root.conf" <<EOF -[Partition] -Type=swap -SizeMaxBytes=64M -UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3 -EOF - -mkdir -p "$D/definitions-overrides/root.conf.d" - -cat >"$D/definitions-overrides/root.conf.d/override1.conf" <<EOF -[Partition] -Label=label1 -SizeMaxBytes=32M -EOF - -cat >"$D/definitions-overrides/root.conf.d/override2.conf" <<EOF -[Partition] -Label=label2 -EOF - -rm -f test-drop-in-image - -JSON_OUTPUT=$("$repart" --definitions="$D/definitions-overrides" --dry-run=yes --empty=create --size=100M --json=pretty test-drop-in-image) - -diff <(echo "$JSON_OUTPUT") - <<EOF -[ - { - "type" : "swap", - "label" : "label2", - "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3", - "file" : "root.conf", - "node" : "test-drop-in-image1", - "offset" : 1048576, - "old_size" : 0, - "raw_size" : 33554432, - "old_padding" : 0, - "raw_padding" : 0, - "activity" : "create", - "drop-in_files" : [ - "$D/definitions-overrides/root.conf.d/override1.conf", - "$D/definitions-overrides/root.conf.d/override2.conf" - ] - } -] -EOF - -echo "### Testing list of definitions directories ###" - -mkdir -p "$D/definitions1" - -cat >"$D/definitions1/root1.conf" <<EOF -[Partition] -Type=swap -SizeMaxBytes=32M -UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0 -Label=label1 -EOF - -mkdir -p "$D/definitions2" - -cat >"$D/definitions2/root2.conf" <<EOF -[Partition] -Type=swap -SizeMaxBytes=32M -UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3 -Label=label2 -EOF - -rm -f test-definitions - -JSON_OUTPUT=$("$repart" --definitions="$D/definitions1" --definitions="$D/definitions2" --dry-run=yes --empty=create --size=100M --json=pretty test-definitions) - -diff <(echo "$JSON_OUTPUT") - <<EOF -[ - { - "type" : "swap", - "label" : "label1", - "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0", - "file" : "root1.conf", - "node" : "test-definitions1", - "offset" : 1048576, - "old_size" : 0, - "raw_size" : 33554432, - "old_padding" : 0, - "raw_padding" : 0, - "activity" : "create" - }, - { - "type" : "swap", - "label" : "label2", - "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3", - "file" : "root2.conf", - "node" : "test-definitions2", - "offset" : 34603008, - "old_size" : 0, - "raw_size" : 33554432, - "old_padding" : 0, - "raw_padding" : 0, - "activity" : "create" - } -] -EOF diff --git a/src/portable/portable.c b/src/portable/portable.c index c6e74e9c27..256362355c 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -873,6 +873,8 @@ static int portable_changes_add_with_prefix( const char *path, const char *source) { + _cleanup_free_ char *path_buf = NULL, *source_buf = NULL; + assert(path); assert(!changes == !n_changes); @@ -880,10 +882,19 @@ static int portable_changes_add_with_prefix( return 0; if (prefix) { - path = prefix_roota(prefix, path); + path_buf = path_join(prefix, path); + if (!path_buf) + return -ENOMEM; + + path = path_buf; + + if (source) { + source_buf = path_join(prefix, source); + if (!source_buf) + return -ENOMEM; - if (source) - source = prefix_roota(prefix, source); + source = source_buf; + } } return portable_changes_add(changes, n_changes, type_or_errno, path, source); @@ -1098,7 +1109,8 @@ static int attach_unit_file( _cleanup_(unlink_and_freep) char *chroot_dropin = NULL, *profile_dropin = NULL; _cleanup_(rmdir_and_freep) char *dropin_dir = NULL; - const char *where, *path; + _cleanup_free_ char *path = NULL; + const char *where; int r; assert(paths); @@ -1115,7 +1127,10 @@ static int attach_unit_file( } else (void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, where, NULL); - path = prefix_roota(where, m->name); + path = path_join(where, m->name); + if (!path) + return -ENOMEM; + dropin_dir = strjoin(path, ".d"); if (!dropin_dir) return -ENOMEM; diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 594984685b..4c004c4a48 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -58,7 +58,7 @@ struct DnsCacheItem { /* Returns true if this is a cache item created as result of an explicit lookup, or created as "side-effect" * of another request. "Primary" entries will carry the full answer data (with NSEC, …) that can aso prove - * wildcard expansion, non-existance and such, while entries that were created as "side-effect" just contain + * wildcard expansion, non-existence and such, while entries that were created as "side-effect" just contain * immediate RR data for the specified RR key, but nothing else. */ #define DNS_CACHE_ITEM_IS_PRIMARY(item) (!!(item)->answer) diff --git a/src/shared/efi-api.c b/src/shared/efi-api.c index b9faae025b..f3f1091ad4 100644 --- a/src/shared/efi-api.c +++ b/src/shared/efi-api.c @@ -180,9 +180,6 @@ static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) { /* Returns the size of the string in bytes without the terminating two zero bytes */ - if (buf_len_bytes % sizeof(uint16_t) != 0) - return -EINVAL; - while (l < buf_len_bytes / sizeof(uint16_t)) { if (s[l] == 0) return (l + 1) * sizeof(uint16_t); diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index 14c1ce0b45..8a20fa7e47 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -280,9 +280,9 @@ static int verify_fsroot_dir( * 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)); } diff --git a/src/shared/generator.c b/src/shared/generator.c index b4efcf6d0b..681b97c6bd 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -29,11 +29,13 @@ int generator_open_unit_file( const char *name, FILE **file) { - const char *unit; + _cleanup_free_ char *unit = NULL; FILE *f; int r; - unit = prefix_roota(dest, name); + unit = path_join(dest, name); + if (!unit) + return log_oom(); r = fopen_unlocked(unit, "wxe", &f); if (r < 0) { @@ -352,8 +354,8 @@ int generator_hook_up_mkswap( const char *what) { _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_free_ char *unit_file = NULL; _cleanup_fclose_ FILE *f = NULL; - const char *unit_file; int r; node = fstab_node_to_udev_node(what); @@ -371,7 +373,10 @@ int generator_hook_up_mkswap( return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", node); - unit_file = prefix_roota(dir, unit); + unit_file = path_join(dir, unit); + if (!unit_file) + return log_oom(); + log_debug("Creating %s", unit_file); escaped = cescape(node); @@ -421,9 +426,8 @@ int generator_hook_up_mkfs( const char *where, const char *type) { - _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_free_ char *node = NULL, *unit = NULL, *unit_file = NULL, *escaped = NULL, *where_unit = NULL; _cleanup_fclose_ FILE *f = NULL; - const char *unit_file; int r; node = fstab_node_to_udev_node(what); @@ -446,7 +450,10 @@ int generator_hook_up_mkfs( return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", node); - unit_file = prefix_roota(dir, unit); + unit_file = path_join(dir, unit); + if (!unit_file) + return log_oom(); + log_debug("Creating %s", unit_file); escaped = cescape(node); @@ -499,9 +506,8 @@ int generator_hook_up_growfs( const char *where, const char *target) { - _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL, *unit_file = NULL; _cleanup_fclose_ FILE *f = NULL; - const char *unit_file; int r; assert(dir); @@ -521,7 +527,10 @@ int generator_hook_up_growfs( return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", where); - unit_file = prefix_roota(dir, unit); + unit_file = path_join(dir, unit); + if (!unit_file) + return log_oom(); + log_debug("Creating %s", unit_file); f = fopen(unit_file, "wxe"); diff --git a/src/shared/gpt.c b/src/shared/gpt.c index 37f3919724..86819cdf5e 100644 --- a/src/shared/gpt.c +++ b/src/shared/gpt.c @@ -34,7 +34,7 @@ const GptPartitionType gpt_partition_type_table[] = { _GPT_ARCH_SEXTET(PARISC, "parisc"), _GPT_ARCH_SEXTET(PPC, "ppc"), _GPT_ARCH_SEXTET(PPC64, "ppc64"), - _GPT_ARCH_SEXTET(PPC64_LE, "ppc64-le"), + _GPT_ARCH_SEXTET(PPC64_LE, "ppc64-le"), _GPT_ARCH_SEXTET(RISCV32, "riscv32"), _GPT_ARCH_SEXTET(RISCV64, "riscv64"), _GPT_ARCH_SEXTET(S390, "s390"), diff --git a/src/shared/gpt.h b/src/shared/gpt.h index f49cfdac61..59a323f9c2 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -22,8 +22,8 @@ #define GPT_ROOT_PPC64_LE SD_ID128_MAKE(c3,1c,45,e6,3f,39,41,2e,80,fb,48,09,c4,98,05,99) #define GPT_ROOT_RISCV32 SD_ID128_MAKE(60,d5,a7,fe,8e,7d,43,5c,b7,14,3d,d8,16,21,44,e1) #define GPT_ROOT_RISCV64 SD_ID128_MAKE(72,ec,70,a6,cf,74,40,e6,bd,49,4b,da,08,e8,f2,24) -#define GPT_ROOT_S390 SD_ID128_MAKE(08,a7,ac,ea,62,4c,4a,20,91,e8,6e,0f,a6,7d,23,f9) #define GPT_ROOT_S390X SD_ID128_MAKE(5e,ea,d9,a9,fe,09,4a,1e,a1,d7,52,0d,00,53,13,06) +#define GPT_ROOT_S390 SD_ID128_MAKE(08,a7,ac,ea,62,4c,4a,20,91,e8,6e,0f,a6,7d,23,f9) #define GPT_ROOT_TILEGX SD_ID128_MAKE(c5,0c,dd,70,38,62,4c,c3,90,e1,80,9a,8c,93,ee,2c) #define GPT_ROOT_X86 SD_ID128_MAKE(44,47,95,40,f2,97,41,b2,9a,f7,d1,31,d5,f0,45,8a) #define GPT_ROOT_X86_64 SD_ID128_MAKE(4f,68,bc,e3,e8,cd,4d,b1,96,e7,fb,ca,f9,84,b7,09) @@ -41,8 +41,8 @@ #define GPT_USR_PPC64_LE SD_ID128_MAKE(15,bb,03,af,77,e7,4d,4a,b1,2b,c0,d0,84,f7,49,1c) #define GPT_USR_RISCV32 SD_ID128_MAKE(b9,33,fb,22,5c,3f,4f,91,af,90,e2,bb,0f,a5,07,02) #define GPT_USR_RISCV64 SD_ID128_MAKE(be,ae,c3,4b,84,42,43,9b,a4,0b,98,43,81,ed,09,7d) -#define GPT_USR_S390 SD_ID128_MAKE(cd,0f,86,9b,d0,fb,4c,a0,b1,41,9e,a8,7c,c7,8d,66) #define GPT_USR_S390X SD_ID128_MAKE(8a,4f,57,70,50,aa,4e,d3,87,4a,99,b7,10,db,6f,ea) +#define GPT_USR_S390 SD_ID128_MAKE(cd,0f,86,9b,d0,fb,4c,a0,b1,41,9e,a8,7c,c7,8d,66) #define GPT_USR_TILEGX SD_ID128_MAKE(55,49,70,29,c7,c1,44,cc,aa,39,81,5e,d1,55,86,30) #define GPT_USR_X86 SD_ID128_MAKE(75,25,0d,76,8c,c6,45,8e,bd,66,bd,47,cc,81,a8,12) #define GPT_USR_X86_64 SD_ID128_MAKE(84,84,68,0c,95,21,48,c6,9c,11,b0,72,06,56,f6,9e) @@ -238,14 +238,6 @@ # define GPT_USR_NATIVE_VERITY GPT_USR_RISCV64_VERITY # define GPT_USR_NATIVE_VERITY_SIG GPT_USR_RISCV64_VERITY_SIG -#elif defined(__s390__) -# define GPT_ROOT_NATIVE GPT_ROOT_S390 -# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_S390_VERITY -# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_S390_VERITY_SIG -# define GPT_USR_NATIVE GPT_USR_S390 -# define GPT_USR_NATIVE_VERITY GPT_USR_S390_VERITY -# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_S390_VERITY_SIG - #elif defined(__s390x__) # define GPT_ROOT_NATIVE GPT_ROOT_S390X # define GPT_ROOT_NATIVE_VERITY GPT_ROOT_S390X_VERITY @@ -254,6 +246,14 @@ # define GPT_USR_NATIVE_VERITY GPT_USR_S390X_VERITY # define GPT_USR_NATIVE_VERITY_SIG GPT_USR_S390X_VERITY_SIG +#elif defined(__s390__) +# define GPT_ROOT_NATIVE GPT_ROOT_S390 +# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_S390_VERITY +# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_S390_VERITY_SIG +# define GPT_USR_NATIVE GPT_USR_S390 +# define GPT_USR_NATIVE_VERITY GPT_USR_S390_VERITY +# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_S390_VERITY_SIG + #elif defined(__tilegx__) # define GPT_ROOT_NATIVE GPT_ROOT_TILEGX # define GPT_ROOT_NATIVE_VERITY GPT_ROOT_TILEGX_VERITY diff --git a/src/shared/install.c b/src/shared/install.c index 5ae39f6a04..1a0a536080 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -102,12 +102,13 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType); 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); } @@ -135,13 +136,14 @@ static const char* skip_root(const char *root_dir, const char *path) { 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) || @@ -150,26 +152,28 @@ static int path_is_generator(const LookupPaths *lp, const char *path) { 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); @@ -177,6 +181,7 @@ static int path_is_control(const LookupPaths *lp, const char *path) { 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); @@ -185,9 +190,9 @@ static int path_is_config(const LookupPaths *lp, const char *path, bool check_pa * 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; } @@ -199,6 +204,7 @@ static int path_is_config(const LookupPaths *lp, const char *path, bool check_pa 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); @@ -211,9 +217,9 @@ static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_p 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; } @@ -1793,9 +1799,9 @@ int unit_file_verify_alias( 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) diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index d9923e9de8..efc066c4f2 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -45,6 +45,7 @@ #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); @@ -526,6 +527,68 @@ int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret) { 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; diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index 54fe65007e..6645c3e596 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -65,6 +65,8 @@ int estimate_battery_discharge_rate_per_hour( 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_; diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 0c6dea4264..2845e62c79 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -661,7 +661,7 @@ static int device_is_power_sink(sd_device *device) { 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; @@ -678,22 +678,15 @@ int on_ac_power(void) { 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; } @@ -702,43 +695,48 @@ int on_ac_power(void) { 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) { diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 14191cfc61..d8e6380d0a 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -262,7 +262,7 @@ static int execute( 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; @@ -335,7 +335,55 @@ static int execute_s2h(const SleepConfig *sleep_config) { 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); diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index b81f1ebe73..a8593cec4d 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -886,7 +886,7 @@ static int verb_help(int argc, char **argv, void *userdata) { if (r < 0) return log_oom(); - printf("%1$s [OPTIONS...] [DEVICE]\n" + printf("%1$s [OPTIONS...] COMMAND\n" "\n%5$sMerge extension images into /usr/ and /opt/ hierarchies.%6$s\n" "\n%3$sCommands:%4$s\n" " status Show current merge status (default)\n" diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c index 446dfd7dd6..a363d7a494 100644 --- a/src/systemctl/systemctl-edit.c +++ b/src/systemctl/systemctl-edit.c @@ -579,9 +579,9 @@ end: 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); diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index 36a0748c14..beeffda316 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -393,10 +393,10 @@ static void print_status_info( 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" diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 14ae873dc0..3c5df6c3ec 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -80,16 +80,16 @@ static void free_sysvstub_hashmapp(Hashmap **h) { } static int add_alias(const char *service, const char *alias) { - const char *link; - int r; + _cleanup_free_ char *link = NULL; assert(service); assert(alias); - link = prefix_roota(arg_dest, alias); + link = path_join(arg_dest, alias); + if (!link) + return -ENOMEM; - r = symlink(service, link); - if (r < 0) { + if (symlink(service, link) < 0) { if (errno == EEXIST) return 0; @@ -100,9 +100,8 @@ static int add_alias(const char *service, const char *alias) { } static int generate_unit_file(SysvStub *s) { - _cleanup_free_ char *path_escaped = NULL; + _cleanup_free_ char *path_escaped = NULL, *unit = NULL; _cleanup_fclose_ FILE *f = NULL; - const char *unit; int r; assert(s); @@ -114,7 +113,9 @@ static int generate_unit_file(SysvStub *s) { if (!path_escaped) return log_oom(); - unit = prefix_roota(arg_dest, s->name); + unit = path_join(arg_dest, s->name); + if (!unit) + return log_oom(); /* We might already have a symlink with the same name from a Provides:, * or from backup files like /etc/init.d/foo.bak. Real scripts always win, diff --git a/src/test/test-bpf-devices.c b/src/test/test-bpf-devices.c index 587591cf04..e175483734 100644 --- a/src/test/test-bpf-devices.c +++ b/src/test/test-bpf-devices.c @@ -295,7 +295,7 @@ int main(int argc, char *argv[]) { 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); diff --git a/src/test/test-bpf-firewall.c b/src/test/test-bpf-firewall.c index 3b99c5aaec..d655058d3d 100644 --- a/src/test/test-bpf-firewall.c +++ b/src/test/test-bpf-firewall.c @@ -176,7 +176,7 @@ int main(int argc, char *argv[]) { 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); @@ -201,7 +201,7 @@ int main(int argc, char *argv[]) { 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); diff --git a/src/test/test-bpf-foreign-programs.c b/src/test/test-bpf-foreign-programs.c index 061426f287..0445c39855 100644 --- a/src/test/test-bpf-foreign-programs.c +++ b/src/test/test-bpf-foreign-programs.c @@ -246,7 +246,7 @@ static int test_bpf_cgroup_programs(Manager *m, const char *unit_name, const Tes 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"); diff --git a/src/test/test-bpf-lsm.c b/src/test/test-bpf-lsm.c index 9709053d0a..e1df62f1a6 100644 --- a/src/test/test-bpf-lsm.c +++ b/src/test/test-bpf-lsm.c @@ -39,7 +39,7 @@ static int test_restrict_filesystems(Manager *m, const char *unit_name, const ch 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"); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 82e4cf5e54..0283caeca6 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -210,7 +210,7 @@ static void _test(const char *file, unsigned line, const char *func, 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) \ @@ -223,7 +223,7 @@ static void _test_service(const char *file, unsigned line, const char *func, 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) \ diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c index ec8b74f48f..566b68b589 100644 --- a/src/test/test-glob-util.c +++ b/src/test/test-glob-util.c @@ -13,6 +13,27 @@ #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; diff --git a/src/test/test-path.c b/src/test/test-path.c index 7fb1f7363c..a440ca08b4 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -136,7 +136,7 @@ static void test_path_exists(Manager *m) { 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; @@ -170,7 +170,7 @@ static void test_path_existsglob(Manager *m) { 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; @@ -205,7 +205,7 @@ static void test_path_changed(Manager *m) { 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; @@ -247,7 +247,7 @@ static void test_path_modified(Manager *m) { 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; @@ -288,7 +288,7 @@ static void test_path_unit(Manager *m) { 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; @@ -319,7 +319,7 @@ static void test_path_directorynotempty(Manager *m) { 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; @@ -356,7 +356,7 @@ static void test_path_makedirectory_directorymode(Manager *m) { 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); diff --git a/src/test/test-socket-bind.c b/src/test/test-socket-bind.c index dadd2c0dff..d7d9110343 100644 --- a/src/test/test-socket-bind.c +++ b/src/test/test-socket-bind.c @@ -78,7 +78,7 @@ static int test_socket_bind( 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"); diff --git a/src/udev/dmi_memory_id/dmi_memory_id.c b/src/udev/dmi_memory_id/dmi_memory_id.c index bae05b8ecd..338cbc72c0 100644 --- a/src/udev/dmi_memory_id/dmi_memory_id.c +++ b/src/udev/dmi_memory_id/dmi_memory_id.c @@ -86,7 +86,7 @@ static bool verify_checksum(const uint8_t *buf, size_t len) { } /* - * Type-independant Stuff + * Type-independent Stuff */ static const char *dmi_string(const struct dmi_header *dm, uint8_t s) { diff --git a/src/udev/udevd.c b/src/udev/udevd.c index a6926bbfb7..01162bc7b6 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -898,7 +898,7 @@ static int event_is_blocked(Event *event) { if (r < 0) return r; - if (event->retry_again_next_usec <= now_usec) + if (event->retry_again_next_usec > now_usec) return true; } diff --git a/test/TEST-58-REPART/test.sh b/test/TEST-58-REPART/test.sh index 362236c0a8..4aff2788e4 100755 --- a/test/TEST-58-REPART/test.sh +++ b/test/TEST-58-REPART/test.sh @@ -3,9 +3,15 @@ set -e TEST_DESCRIPTION="test systemd-repart" -TEST_NO_NSPAWN=1 # shellcheck source=test/test-functions . "$TEST_BASE_DIR/test-functions" +test_append_files() { + if ! get_bool "${TEST_NO_QEMU:=}"; then + install_dmevent + generate_module_dependencies + fi +} + do_test "$@" diff --git a/test/TEST-63-ISSUE-17433/Makefile b/test/TEST-63-PATH/Makefile index e9f93b1104..e9f93b1104 120000 --- a/test/TEST-63-ISSUE-17433/Makefile +++ b/test/TEST-63-PATH/Makefile diff --git a/test/TEST-63-ISSUE-17433/test.sh b/test/TEST-63-PATH/test.sh index 65f41ed1de..65f41ed1de 100755 --- a/test/TEST-63-ISSUE-17433/test.sh +++ b/test/TEST-63-PATH/test.sh diff --git a/test/TEST-75-RESOLVED/test.sh b/test/TEST-75-RESOLVED/test.sh index b0021712e3..cbf35ee292 100755 --- a/test/TEST-75-RESOLVED/test.sh +++ b/test/TEST-75-RESOLVED/test.sh @@ -27,6 +27,7 @@ test_append_files() { 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/" diff --git a/test/hwdb-test.sh b/test/hwdb-test.sh index 29183e6829..3efbad21b6 100755 --- a/test/hwdb-test.sh +++ b/test/hwdb-test.sh @@ -22,7 +22,7 @@ D="$(mktemp --tmpdir --directory "hwdb-test.XXXXXXXXXX")" # shellcheck disable=SC2064 trap "rm -rf '$D'" EXIT INT QUIT PIPE mkdir -p "$D/etc/udev" -ln -s "$ROOTDIR/hwdb.d" "$D/etc/udev/hwdb.d" +cp -a "$ROOTDIR/hwdb.d" "$D/etc/udev/hwdb.d" # Test "good" properties" — no warnings or errors allowed err=$("$SYSTEMD_HWDB" update --root "$D" 2>&1 >/dev/null) && rc= || rc=$? @@ -41,9 +41,9 @@ if [ ! -e "$D/etc/udev/hwdb.bin" ]; then fi # Test "bad" properties" — warnings required, errors not allowed -rm -f "$D/etc/udev/hwdb.bin" "$D/etc/udev/hwdb.d" +rm -rf "$D/etc/udev/hwdb.bin" "$D/etc/udev/hwdb.d" -ln -s "$ROOTDIR/test/hwdb.d" "$D/etc/udev/hwdb.d" +cp -a "$ROOTDIR/test/hwdb.d" "$D/etc/udev/hwdb.d" err=$("$SYSTEMD_HWDB" update --root "$D" 2>&1 >/dev/null) && rc= || rc=$? if [ -n "$rc" ]; then echo "$SYSTEMD_HWDB returned $rc" diff --git a/test/testsuite-63.units/test63-glob.path b/test/testsuite-63.units/test63-glob.path new file mode 100644 index 0000000000..5f237a9fcf --- /dev/null +++ b/test/testsuite-63.units/test63-glob.path @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Path] +PathExistsGlob=/tmp/test63-glob* diff --git a/test/testsuite-63.units/test63-glob.service b/test/testsuite-63.units/test63-glob.service new file mode 100644 index 0000000000..3f49dd4620 --- /dev/null +++ b/test/testsuite-63.units/test63-glob.service @@ -0,0 +1,7 @@ +# 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 diff --git a/test/testsuite-63.units/test63.service b/test/testsuite-63.units/test63.service index 1a8721d82c..01a928b8d6 100644 --- a/test/testsuite-63.units/test63.service +++ b/test/testsuite-63.units/test63.service @@ -3,4 +3,6 @@ ConditionPathExists=/tmp/nonexistent [Service] +ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63' +ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63.path' ExecStart=true diff --git a/test/units/testsuite-07.sh b/test/units/testsuite-07.sh index 5e9fe64ea9..95ebe3876f 100755 --- a/test/units/testsuite-07.sh +++ b/test/units/testsuite-07.sh @@ -8,6 +8,9 @@ set -o pipefail 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 diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index f2d937daba..6170c8ab63 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -187,6 +187,7 @@ if [ "${HAVE_OPENSSL}" -eq 1 ]; then 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 diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index 8dadcc0a95..f7b903c682 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -8,76 +8,448 @@ if ! command -v systemd-repart &>/dev/null; then exit 0 fi +# shellcheck source=test/units/assert.sh +. "$(dirname "$0")"/assert.sh + export SYSTEMD_LOG_LEVEL=debug export PAGER=cat +seed=750b6cd5c4ae4012a15e7be3c29e6a47 + +if ! systemd-detect-virt --quiet --container; then + mkdir -p /run/systemd/system/systemd-udevd.service.d + cat >/run/systemd/system/systemd-udevd.service.d/debug.conf <<EOF +[Service] +Environment=SYSTEMD_LOG_LEVEL=debug +EOF + + systemctl daemon-reload + udevadm settle + systemctl restart systemd-udevd.service + udevadm control --ping +fi + machine="$(uname -m)" if [ "${machine}" = "x86_64" ]; then - root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 + root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709 root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0 - usr_guid=8484680c-9521-48c6-9c11-b0720656f69e + root_uuid2=73A4CCD2-EAF5-44DA-A366-F99188210FDC + usr_guid=8484680C-9521-48C6-9C11-B0720656F69E usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16 architecture="x86-64" elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then - root_guid=44479540-f297-41b2-9af7-d131d5f0458a - root_uuid=02b4253f-29a4-404e-8972-1669d3b03c87 - usr_guid=75250d76-8cc6-458e-bd66-bd47cc81a812 - usr_uuid=7b42ffb0-b0e1-4395-b20b-c78f4a571648 + root_guid=44479540-F297-41B2-9AF7-D131D5F0458A + root_uuid=02B4253F-29A4-404E-8972-1669D3B03C87 + root_uuid2=268E0FD3-B468-4806-A823-E533FE9BB9CC + usr_guid=75250D76-8CC6-458E-BD66-BD47CC81A812 + usr_uuid=7B42FFB0-B0E1-4395-B20B-C78F4A571648 architecture="x86" elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then - root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae - root_uuid=055d0227-53a6-4033-85c3-9a5973eff483 - usr_guid=b0e01050-ee5f-4390-949a-9101b17104e9 - usr_uuid=fce3c75e-d6a4-44c0-87f0-4c105183fb1f + root_guid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE + root_uuid=055D0227-53A6-4033-85C3-9A5973EFF483 + root_uuid2=F7DBBE48-8FD0-4833-8411-AA34E7C8E60A + usr_guid=B0E01050-EE5F-4390-949A-9101B17104E9 + usr_uuid=FCE3C75E-D6A4-44C0-87F0-4C105183FB1F architecture="arm64" elif [ "${machine}" = "arm" ]; then - root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3 - root_uuid=567da89e-8de2-4499-8d10-18f212dff034 - usr_guid=7d0359a3-02b3-4f0a-865c-654403e70625 - usr_uuid=71e93dc2-5073-42cb-8a84-a354e64d8966 + root_guid=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3 + root_uuid=567DA89E-8DE2-4499-8D10-18F212DFF034 + root_uuid2=813ECFE5-4C89-4193-8A52-437493F2F96E + usr_guid=7D0359A3-02B3-4F0A-865C-654403E70625 + usr_uuid=71E93DC2-5073-42CB-8A84-A354E64D8966 architecture="arm" elif [ "${machine}" = "loongarch64" ]; then - root_guid=77055800-792c-4f94-b39a-98c91b762bb6 - root_uuid=d8efc2d2-0133-41e4-bdcb-3b9f4cfddde8 - usr_guid=e611c702-575c-4cbe-9a46-434fa0bf7e3f - usr_uuid=031ffa75-00bb-49b6-a70d-911d2d82a5b7 + root_guid=77055800-792C-4F94-B39A-98C91B762BB6 + root_uuid=D8EFC2D2-0133-41E4-BDCB-3B9F4CFDDDE8 + root_uuid2=36499F9E-0688-40C1-A746-EA8FD9543C56 + usr_guid=E611C702-575C-4CBE-9A46-434FA0BF7E3F + usr_uuid=031FFA75-00BB-49B6-A70D-911D2D82A5B7 architecture="loongarch64" elif [ "${machine}" = "ia64" ]; then - root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97 - root_uuid=dcf33449-0896-4ea9-bc24-7d58aeef522d - usr_guid=4301d2a6-4e3b-4b2a-bb94-9e0b2c4225ea - usr_uuid=bc2bcce7-80d6-449a-85cc-637424ce5241 + root_guid=993D8D3D-F80E-4225-855A-9DAF8ED7EA97 + root_uuid=DCF33449-0896-4EA9-BC24-7D58AEEF522D + root_uuid2=C2A6CAB7-ABEA-4FBA-8C48-CB4C52E6CA38 + usr_guid=4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA + usr_uuid=BC2BCCE7-80D6-449A-85CC-637424CE5241 architecture="ia64" elif [ "${machine}" = "s390x" ]; then - root_guid=5eead9a9-fe09-4a1e-a1d7-520d00531306 - root_uuid=7ebe0c85-e27e-48ec-b164-f4807606232e - usr_guid=8a4f5770-50aa-4ed3-874a-99b710db6fea - usr_uuid=51171d30-35cf-4a49-b8b5-9478b9b796a5 + root_guid=5EEAD9A9-FE09-4A1E-A1D7-520D00531306 + root_uuid=7EBE0C85-E27E-48EC-B164-F4807606232E + root_uuid2=2A074E1C-2A19-4094-A0C2-24B1A5D52FCB + usr_guid=8A4F5770-50AA-4ED3-874A-99B710DB6FEA + usr_uuid=51171D30-35CF-4A49-B8B5-9478B9B796A5 architecture="s390x" elif [ "${machine}" = "ppc64le" ]; then - root_guid=c31c45e6-3f39-412e-80fb-4809c4980599 - root_uuid=061e67a1-092f-482f-8150-b525d50d6654 - usr_guid=15bb03af-77e7-4d4a-b12b-c0d084f7491c - usr_uuid=c0d0823b-8040-4c7c-a629-026248e297fb + root_guid=C31C45E6-3F39-412E-80FB-4809C4980599 + root_uuid=061E67A1-092F-482F-8150-B525D50D6654 + root_uuid2=A6687CEF-4E4F-44E7-90B3-CDA52EA81739 + usr_guid=15BB03AF-77E7-4D4A-B12B-C0D084F7491C + usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB architecture="ppc64-le" else echo "Unexpected uname -m: ${machine} in testsuite-58.sh, please fix me" exit 1 fi -rm -f /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img /tmp/testsuite-58.dump -mkdir -p /tmp/testsuite-58-defs/ +test_basic() { + local defs imgs output + local loop volume + + defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + + # 1. create an empty image + + systemd-repart --empty=create \ + --size=1G \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 2097118" + + # 2. Testing with root, root2, home, and swap + + cat >"$defs/root.conf" <<EOF +[Partition] +Type=root +EOF + + ln -s root.conf "$defs/root2.conf" + + cat >"$defs/home.conf" <<EOF +[Partition] +Type=home +Label=home-first +Label=home-always-too-long-xxxxxxxxxxxxxx-%v +EOF + + cat >"$defs/swap.conf" <<EOF +[Partition] +Type=swap +SizeMaxBytes=64M +PaddingMinBytes=92M +EOF + + systemd-repart --definitions="$defs" \ + --dry-run=no \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 2097118 +$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" +$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" +$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"" + + # 3. Testing with root, root2, home, swap, and another partition + + cat >"$defs/swap.conf" <<EOF +[Partition] +Type=swap +SizeMaxBytes=64M +EOF + + cat >"$defs/extra.conf" <<EOF +[Partition] +Type=linux-generic +Label=custom_label +UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf +EOF + + echo "Label=ignored_label" >>"$defs/home.conf" + echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf" + + systemd-repart --definitions="$defs" \ + --dry-run=no \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 2097118 +$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" +$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" +$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" +$imgs/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"" + + # 4. Resizing to 2G + + systemd-repart --definitions="$defs" \ + --size=2G \ + --dry-run=no \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 4194270 +$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" +$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" +$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" +$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"" + + # 5. Testing with root, root2, home, swap, another partition, and partition copy + + dd if=/dev/urandom of="$imgs/block-copy" bs=4096 count=10240 + + cat >"$defs/extra2.conf" <<EOF +[Partition] +Type=linux-generic +Label=block-copy +UUID=2a1d97e1d0a346cca26eadc643926617 +CopyBlocks=$imgs/block-copy +EOF + + systemd-repart --definitions="$defs" \ + --size=3G \ + --dry-run=no \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 6291422 +$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" +$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" +$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" +$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\" +$imgs/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\"" + + cmp --bytes=$((4096*10240)) --ignore-initial=0:$((512*4194264)) "$imgs/block-copy" "$imgs/zzz" + + if systemd-detect-virt --quiet --container; then + echo "Skipping encrypt tests in container." + return + fi + + # 6. Testing Format=/Encrypt=/CopyFiles= + + cat >"$defs/extra3.conf" <<EOF +[Partition] +Type=linux-generic +Label=luks-format-copy +UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0 +Format=ext4 +Encrypt=yes +CopyFiles=$defs:/def +SizeMinBytes=48M +EOF + + systemd-repart --definitions="$defs" \ + --size=auto \ + --dry-run=no \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 6389726 +$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" +$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" +$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" +$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\" +$imgs/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\" +$imgs/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name=\"luks-format-copy\"" + + loop="$(losetup -P --show --find "$imgs/zzz")" + udevadm wait --timeout 60 --settle "${loop:?}" + + volume="test-repart-$RANDOM" + + touch "$imgs/empty-password" + cryptsetup open --type=luks2 --key-file="$imgs/empty-password" "${loop}p7" "$volume" + mkdir -p "$imgs/mount" + mount -t ext4 "/dev/mapper/$volume" "$imgs/mount" + # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount + cryptsetup close --deferred "$volume" + losetup -d "$loop" + diff -r "$imgs/mount/def" "$defs" >/dev/null + umount "$imgs/mount" +} + +test_dropin() { + local defs imgs output -# First part: create a disk image and verify its in order + defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN -cat >/tmp/testsuite-58-defs/esp.conf <<EOF + cat >"$defs/root.conf" <<EOF +[Partition] +Type=swap +SizeMaxBytes=64M +UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3 +EOF + + mkdir -p "$defs/root.conf.d" + cat >"$defs/root.conf.d/override1.conf" <<EOF +[Partition] +Label=label1 +SizeMaxBytes=32M +EOF + + cat >"$defs/root.conf.d/override2.conf" <<EOF +[Partition] +Label=label2 +EOF + + output=$(systemd-repart --definitions="$defs" --empty=create --size=100M --json=pretty "$imgs/zzz") + + diff <(echo "$output") - <<EOF +[ + { + "type" : "swap", + "label" : "label2", + "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3", + "file" : "root.conf", + "node" : "$imgs/zzz1", + "offset" : 1048576, + "old_size" : 0, + "raw_size" : 33554432, + "size" : "→ 32.0M", + "old_padding" : 0, + "raw_padding" : 0, + "padding" : "→ 0B", + "activity" : "create", + "drop-in_files" : [ + "$defs/root.conf.d/override1.conf", + "$defs/root.conf.d/override2.conf" + ] + } +] +EOF +} + +test_multiple_definitions() { + local defs imgs output + + defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + + mkdir -p "$defs/1" + + cat >"$defs/1/root1.conf" <<EOF +[Partition] +Type=swap +SizeMaxBytes=32M +UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0 +Label=label1 +EOF + + mkdir -p "$defs/2" + + cat >"$defs/2/root2.conf" <<EOF +[Partition] +Type=swap +SizeMaxBytes=32M +UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3 +Label=label2 +EOF + + output=$(systemd-repart --definitions="$defs/1" --definitions="$defs/2" --empty=create --size=100M --json=pretty "$imgs/zzz") + + diff <(echo "$output") - <<EOF +[ + { + "type" : "swap", + "label" : "label1", + "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0", + "file" : "root1.conf", + "node" : "$imgs/zzz1", + "offset" : 1048576, + "old_size" : 0, + "raw_size" : 33554432, + "size" : "→ 32.0M", + "old_padding" : 0, + "raw_padding" : 0, + "padding" : "→ 0B", + "activity" : "create" + }, + { + "type" : "swap", + "label" : "label2", + "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3", + "file" : "root2.conf", + "node" : "$imgs/zzz2", + "offset" : 34603008, + "old_size" : 0, + "raw_size" : 33554432, + "size" : "→ 32.0M", + "old_padding" : 0, + "raw_padding" : 0, + "padding" : "→ 0B", + "activity" : "create" + } +] +EOF +} + +test_copy_blocks() { + local defs imgs output + + if systemd-detect-virt --quiet --container; then + echo "Skipping copy blocks tests in container." + return + fi + + defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + + # First, create a disk image and verify its in order + + cat >"$defs/esp.conf" <<EOF [Partition] Type=esp SizeMinBytes=10M Format=vfat EOF -cat >/tmp/testsuite-58-defs/usr.conf <<EOF + cat >"$defs/usr.conf" <<EOF [Partition] Type=usr-${architecture} SizeMinBytes=10M @@ -85,7 +457,7 @@ Format=ext4 ReadOnly=yes EOF -cat >/tmp/testsuite-58-defs/root.conf <<EOF + cat >"$defs/root.conf" <<EOF [Partition] Type=root-${architecture} SizeMinBytes=10M @@ -93,148 +465,193 @@ Format=ext4 MakeDirectories=/usr /efi EOF -systemd-repart --definitions=/tmp/testsuite-58-defs/ \ - --empty=create \ - --size=auto \ - --seed=750b6cd5c4ae4012a15e7be3c29e6a47 \ - /var/tmp/testsuite-58.img + systemd-repart --definitions="$defs" \ + --empty=create \ + --size=auto \ + --seed="$seed" \ + "$imgs/zzz" -sfdisk --dump /var/tmp/testsuite-58.img | tee /tmp/testsuite-58.dump + output=$(sfdisk --dump "$imgs/zzz") -grep -qixF "/var/tmp/testsuite-58.img1 : start= 2048, size= 20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name=\"esp\"" /tmp/testsuite-58.dump -grep -qixF "/var/tmp/testsuite-58.img2 : start= 22528, size= 20480, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" /tmp/testsuite-58.dump -grep -qixF "/var/tmp/testsuite-58.img3 : start= 43008, size= 20480, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" /tmp/testsuite-58.dump + assert_in "$imgs/zzz1 : start= 2048, size= 20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name=\"esp\"" "$output" + assert_in "$imgs/zzz2 : start= 22528, size= 20480, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output" + assert_in "$imgs/zzz3 : start= 43008, size= 20480, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" "$output" -# Second part, duplicate it with CopyBlocks=auto + # Then, create another image with CopyBlocks=auto -cat >/tmp/testsuite-58-defs/esp.conf <<EOF + cat >"$defs/esp.conf" <<EOF [Partition] Type=esp CopyBlocks=auto EOF -cat >/tmp/testsuite-58-defs/usr.conf <<EOF + cat >"$defs/usr.conf" <<EOF [Partition] Type=usr-${architecture} ReadOnly=yes CopyBlocks=auto EOF -cat >/tmp/testsuite-58-defs/root.conf <<EOF + cat >"$defs/root.conf" <<EOF [Partition] Type=root-${architecture} CopyBlocks=auto EOF -systemd-repart --definitions=/tmp/testsuite-58-defs/ \ - --empty=create \ - --size=auto \ - --seed=750b6cd5c4ae4012a15e7be3c29e6a47 \ - --image=/var/tmp/testsuite-58.img \ - /var/tmp/testsuite-58.2.img + systemd-repart --definitions="$defs" \ + --empty=create \ + --size=auto \ + --seed="$seed" \ + --image="$imgs/zzz" \ + "$imgs/yyy" -cmp /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img + cmp "$imgs/zzz" "$imgs/yyy" +} -rm /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img /tmp/testsuite-58.dump -rm -r /tmp/testsuite-58-defs/ +test_unaligned_partition() { + local defs imgs output -# Third part: operate on an an image with unaligned partition, to see if that works. + defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN -rm -f /var/tmp/testsuite-58.3.img /tmp/testsuite-58-3.dump -mkdir -p /tmp/testsuite-58.3-defs/ + # Operate on an image with unaligned partition. -cat >/tmp/testsuite-58.3-defs/root.conf <<EOF + cat >"$defs/root.conf" <<EOF [Partition] Type=root-${architecture} EOF -truncate -s 10g /var/tmp/testsuite-58.3.img -sfdisk /var/tmp/testsuite-58.3.img <<EOF + truncate -s 10g "$imgs/unaligned" + sfdisk "$imgs/unaligned" <<EOF label: gpt start=2048, size=69044 start=71092, size=3591848 EOF -systemd-repart --definitions=/tmp/testsuite-58.3-defs/ \ - --seed=750b6cd5c4ae4012a15e7be3c29e6a47 \ - --dry-run=no \ - /var/tmp/testsuite-58.3.img + systemd-repart --definitions="$defs" \ + --seed="$seed" \ + --dry-run=no \ + "$imgs/unaligned" + + output=$(sfdisk --dump "$imgs/unaligned") + + assert_in "$imgs/unaligned1 : start= 2048, size= 69044," "$output" + assert_in "$imgs/unaligned2 : start= 71092, size= 3591848," "$output" + assert_in "$imgs/unaligned3 : start= 3662944, size= 17308536, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output" +} -sfdisk --dump /var/tmp/testsuite-58.3.img | tee /tmp/testsuite-58.3.dump +test_issue_21817() { + local defs imgs output -grep -qF '/var/tmp/testsuite-58.3.img1 : start= 2048, size= 69044,' /tmp/testsuite-58.3.dump -grep -qF '/var/tmp/testsuite-58.3.img2 : start= 71092, size= 3591848,' /tmp/testsuite-58.3.dump -grep -qixF "/var/tmp/testsuite-58.3.img3 : start= 3662944, size= 17308536, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" /tmp/testsuite-58.3.dump + # testcase for #21817 -rm /var/tmp/testsuite-58.3.img /tmp/testsuite-58.3.dump -rm -r /tmp/testsuite-58.3-defs/ + defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN -# testcase for #21817 -mkdir -p /tmp/testsuite-58-issue-21817-defs/ -truncate -s 100m /var/tmp/testsuite-58-issue-21817.img -sfdisk /var/tmp/testsuite-58-issue-21817.img <<EOF + cat >"$defs/test.conf" <<EOF +[Partition] +Type=root +EOF + + truncate -s 100m "$imgs/21817.img" + sfdisk "$imgs/21817.img" <<EOF label: gpt size=50M, type=${root_guid} , EOF -cat >/tmp/testsuite-58-issue-21817-defs/test.conf <<EOF -[Partition] -Type=root -EOF -systemd-repart --pretty=yes --definitions /tmp/testsuite-58-issue-21817-defs/ --dry-run=no /var/tmp/testsuite-58-issue-21817.img -sfdisk --dump /var/tmp/testsuite-58-issue-21817.img | tee /tmp/testsuite-58-issue-21817.dump -grep -qiF "/var/tmp/testsuite-58-issue-21817.img1 : start= 2048, size= 102400, type=${root_guid}," /tmp/testsuite-58-issue-21817.dump -# Accept both unpadded (pre-v2.38 util-linux) and padded (v2.38+ util-linux) sizes -grep -qE "/var/tmp/testsuite-58-issue-21817.img2 : start= 104448, size= (100319| 98304)," /tmp/testsuite-58-issue-21817.dump + systemd-repart --pretty=yes \ + --definitions "$imgs" \ + --seed="$seed" \ + --dry-run=no \ + "$imgs/21817.img" -rm /var/tmp/testsuite-58-issue-21817.img /tmp/testsuite-58-issue-21817.dump -rm -r /tmp/testsuite-58-issue-21817-defs/ + output=$(sfdisk --dump "$imgs/21817.img") -testsector() -{ - echo "Running sector test with sector size $1..." + assert_in "$imgs/21817.img1 : start= 2048, size= 102400, type=${root_guid}," "$output" + # Accept both unpadded (pre-v2.38 util-linux) and padded (v2.38+ util-linux) sizes + assert_in "$imgs/21817.img2 : start= 104448, size= (100319| 98304)," "$output" +} - mkdir -p /tmp/testsuite-58-sector - cat > /tmp/testsuite-58-sector/a.conf <<EOF +test_sector() { + local defs imgs output loop + local start size ratio + local sector="${1?}" + + if systemd-detect-virt --quiet --container; then + echo "Skipping sector size tests in container." + return + fi + + defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + + cat > "$defs/a.conf" <<EOF [Partition] Type=root SizeMaxBytes=15M SizeMinBytes=15M EOF - cat > /tmp/testsuite-58-sector/b.conf <<EOF + cat > "$defs/b.conf" <<EOF [Partition] Type=linux-generic Weight=250 EOF - cat > /tmp/testsuite-58-sector/c.conf <<EOF + cat > "$defs/c.conf" <<EOF [Partition] Type=linux-generic Weight=750 EOF - truncate -s 100m "/tmp/testsuite-58-sector-$1.img" - LOOP=$(losetup -b "$1" -P --show -f "/tmp/testsuite-58-sector-$1.img" ) - udevadm wait --timeout 60 --settle "${LOOP:?}" - systemd-repart --pretty=yes --definitions=/tmp/testsuite-58-sector/ --seed=750b6cd5c4ae4012a15e7be3c29e6a47 --empty=require --dry-run=no "$LOOP" - rm -rf /tmp/testsuite-58-sector - sfdisk --verify "$LOOP" - sfdisk --dump "$LOOP" - losetup -d "$LOOP" - - rm "/tmp/testsuite-58-sector-$1.img" + truncate -s 100m "$imgs/$sector.img" + loop=$(losetup -b "$sector" -P --show -f "$imgs/$sector.img" ) + udevadm wait --timeout 60 --settle "${loop:?}" + systemd-repart --pretty=yes \ + --definitions="$defs" \ + --seed="$seed" \ + --empty=require \ + --dry-run=no \ + "$loop" + + sfdisk --verify "$loop" + output=$(sfdisk --dump "$loop") + losetup -d "$loop" + + ratio=$(( sector / 512 )) + start=$(( 2048 / ratio )) + size=$(( 30720 / ratio )) + assert_in "${loop}p1 : start= *${start}, size= *${size}, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output" + start=$(( start + size )) + size=$(( 42992 / ratio )) + assert_in "${loop}p2 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DF71F5E3-080A-4D16-824B-18591B881380, name=\"linux-generic\"" "$output" + start=$(( start + size )) + size=$(( 129000 / ratio )) + assert_in "${loop}p3 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DB081670-07AE-48CA-9F5E-813D5E40B976, name=\"linux-generic-2\"" "$output" } +test_basic +test_dropin +test_multiple_definitions +test_copy_blocks +test_unaligned_partition +test_issue_21817 + # Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and # must be powers of 2. Which leaves exactly four different ones to test on # typical hardware -testsector 512 -testsector 1024 -testsector 2048 -testsector 4096 +test_sector 512 +test_sector 1024 +test_sector 2048 +test_sector 4096 echo OK >/testok diff --git a/test/units/testsuite-60.sh b/test/units/testsuite-60.sh index 13fe139a38..a29364568d 100755 --- a/test/units/testsuite-60.sh +++ b/test/units/testsuite-60.sh @@ -202,6 +202,45 @@ EOF } } +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 @@ -263,6 +302,9 @@ test_dependencies # 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 diff --git a/test/units/testsuite-62-4.service b/test/units/testsuite-62-4.service index 94c7b53468..053e6d205f 100644 --- a/test/units/testsuite-62-4.service +++ b/test/units/testsuite-62-4.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] -Description=TEST-62-RESTRICT-IFACES-empty-assigment +Description=TEST-62-RESTRICT-IFACES-empty-assignment [Service] ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1' ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5' diff --git a/test/units/testsuite-62-5.service b/test/units/testsuite-62-5.service index 38664da493..a8f268d1d8 100644 --- a/test/units/testsuite-62-5.service +++ b/test/units/testsuite-62-5.service @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] -Description=TEST-62-RESTRICT-IFACES-invert-assigment +Description=TEST-62-RESTRICT-IFACES-invert-assignment [Service] ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.1' ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5' diff --git a/test/units/testsuite-63.service b/test/units/testsuite-63.service index 40422127ff..483c6a859b 100644 --- a/test/units/testsuite-63.service +++ b/test/units/testsuite-63.service @@ -1,30 +1,8 @@ # 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' diff --git a/test/units/testsuite-63.sh b/test/units/testsuite-63.sh new file mode 100755 index 0000000000..7ee7fc1513 --- /dev/null +++ b/test/units/testsuite-63.sh @@ -0,0 +1,46 @@ +#!/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 diff --git a/test/units/testsuite-71.sh b/test/units/testsuite-71.sh index 34fcaad961..2382cccb3a 100755 --- a/test/units/testsuite-71.sh +++ b/test/units/testsuite-71.sh @@ -44,9 +44,55 @@ test_hostname() { fi } +restore_machine_info() { + if [[ -e /tmp/machine-info.bak ]]; then + mv /tmp/machine-info.bak /etc/machine-info + else + rm -f /etc/machine-info + fi +} + +get_chassis() ( + # shellcheck source=/dev/null + . /etc/machine-info + + echo "$CHASSIS" +) + +test_chassis() { + local i + + if [[ -f /etc/machine-info ]]; then + cp /etc/machine-info /tmp/machine-info.bak + fi + + trap restore_machine_info RETURN + + # Invalid chassis type is refused + assert_rc 1 hostnamectl chassis hoge + + # Valid chassis types + for i in vm container desktop laptop convertible server tablet handset watch embedded; do + hostnamectl chassis "$i" + assert_eq "$(hostnamectl chassis)" "$i" + assert_eq "$(get_chassis)" "$i" + done + + systemctl stop systemd-hostnamed.service + rm -f /etc/machine-info + + # fallback chassis type + if systemd-detect-virt --quiet --container; then + assert_eq "$(hostnamectl chassis)" container + elif systemd-detect-virt --quiet --vm; then + assert_eq "$(hostnamectl chassis)" vm + fi +} + : >/failed test_hostname +test_chassis touch /testok rm /failed |