diff options
44 files changed, 782 insertions, 393 deletions
diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml index e42f304115..4e77af75d7 100644 --- a/.github/workflows/mkosi.yml +++ b/.github/workflows/mkosi.yml @@ -73,7 +73,7 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: systemd/mkosi@779484101987048b820b07a6111bcb0e8d5ebc1d + - uses: systemd/mkosi@268a9374d5a93a4021db6521a39db2c2ed9fd755 - name: Configure run: | diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index b81e6bdaab..dc3c999321 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -58,6 +58,162 @@ <para>The tool supports only LUKS2 volumes, as it stores token meta-information in the LUKS2 JSON token area, which is not available in other encryption formats.</para> + + <refsect2> + <title>TPM2 PCRs and policies</title> + + <para>PCRs allow binding of the encryption of secrets to specific software versions and system state, + so that the enrolled key is only accessible (may be "unsealed") if specific trusted software and/or + configuration is used. Such bindings may be created with the option <option>--tpm2-pcrs=</option> + described below.</para> + + <para>Secrets may also be bound indirectly: a signed policy for a state of some combination of PCR + values is provided, and the secret is bound to the public part of the key used to sign this policy. + This means that the owner of a key can generate a sequence of signed policies, for specific software + versions and system states, and the secret can be decrypted as long as the machine state matches one of + those policies. For example, a vendor may provide such a policy for each kernel+initrd update, allowing + users to encrypt secrets so that they can be decrypted when running any kernel+initrd signed by the + vendor. Such bindings may be created with the options <option>--tpm2-public-key=</option>, + <option>--tpm2-public-key-pcrs=</option>, <option>--tpm2-signature=</option> described below. + </para> + + <para>See <ulink url="https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/">Linux TPM + PCR Registry</ulink> for an authoritative list of PCRs and how they are updated. The table below + contains a quick reference, describing in particular the PCRs modified by systemd.</para> + + <table> + <title>Well-known PCR Definitions</title> + + <!-- See: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ --> + <!-- See: https://github.com/rhboot/shim/blob/main/README.tpm --> + <!-- See: https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html --> + <!-- See: https://sourceforge.net/p/linux-ima/wiki/Home/ --> + <!-- See: https://github.com/tianocore-docs/edk2-TrustedBootChain/blob/main/4_Other_Trusted_Boot_Chains.md --> + <!-- See: https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers --> + + <tgroup cols='3' align='left' colsep='1' rowsep='1'> + <colspec colname="pcr" /> + <colspec colname="name" /> + <colspec colname="definition" /> + + <thead> + <row> + <entry>PCR</entry> + <entry>name</entry> + <entry>Explanation</entry> + </row> + </thead> + + <tbody> + <row> + <entry>0</entry> + <entry>platform-code</entry> + <entry>Core system firmware executable code; changes on firmware updates</entry> + </row> + + <row> + <entry>1</entry> + <entry>platform-config</entry> + <entry>Core system firmware data/host platform configuration; typically contains serial and model numbers, changes on basic hardware/CPU/RAM replacements</entry> + </row> + + <row> + <entry>2</entry> + <entry>external-code</entry> + <entry>Extended or pluggable executable code; includes option ROMs on pluggable hardware</entry> + </row> + + <row> + <entry>3</entry> + <entry>external-config</entry> + <entry>Extended or pluggable firmware data; includes information about pluggable hardware</entry> + </row> + + <row> + <entry>4</entry> + <entry>boot-loader-code</entry> + <entry>Boot loader and additional drivers, PE binaries invoked by the boot loader; changes on boot loader updates. <citerefentry><refentrytitle>sd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures system extension images read from the ESP here too (see <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).</entry> + </row> + + <row> + <entry>5</entry> + <entry>boot-loader-config</entry> + <entry>GPT/Partition table; changes when the partitions are added, modified, or removed</entry> + </row> + + <row> + <entry>7</entry> + <entry>secure-boot-policy</entry> + <entry>Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes.</entry> + </row> + + <row> + <entry>9</entry> + <entry>kernel-initrd</entry> + <entry>The Linux kernel measures all initrds it receives into this PCR.</entry> + <!-- Strictly speaking only Linux >= 5.17 using the LOAD_FILE2 protocol, see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df --> + </row> + + <row> + <entry>10</entry> + <entry>ima</entry> + <entry>The IMA project measures its runtime state into this PCR.</entry> + </row> + + <row> + <entry>11</entry> + <entry>kernel-boot</entry> + <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures boot phase strings into this PCR at various milestones of the boot process.</entry> + </row> + + <row> + <entry>12</entry> + <entry>kernel-config</entry> + <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR.</entry> + </row> + + <row> + <entry>13</entry> + <entry>sysexts</entry> + <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it passes to the booted kernel into this PCR.</entry> + </row> + + <row> + <entry>14</entry> + <entry>shim-policy</entry> + <entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry> + </row> + + <row> + <entry>15</entry> + <entry>system-identity</entry> + <entry><citerefentry><refentrytitle>systemd-cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> optionally measures the volume key of activated LUKS volumes into this PCR. <citerefentry><refentrytitle>systemd-pcrmachine.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures the <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> into this PCR. <citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures mount points, file system UUIDs, labels, partion UUIDs of the root and <filename>/var/</filename> filesystems into this PCR.</entry> + </row> + + <row> + <entry>16</entry> + <entry>debug</entry> + <entry>Debug</entry> + </row> + + <row> + <entry>23</entry> + <entry>application-support</entry> + <entry>Application Support</entry> + </row> + </tbody> + </tgroup> + </table> + + <para>In general, encrypted volumes would be bound to some combination of PCRs 7, 11, and 14 (if + shim/MOK is used). In order to allow firmware and OS version updates, it is typically not advisable to + use PCRs such as 0 and 2, since the program code they cover should already be covered indirectly + through the certificates measured into PCR 7. Validation through certificates hashes is typically + preferable over validation through direct measurements as it is less brittle in context of OS/firmware + updates: the measurements will change on every update, but signatures should remain unchanged. See the + <ulink url="https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/">Linux TPM PCR + Registry</ulink> for more discussion.</para> + </refsect2> </refsect1> <refsect1> @@ -234,154 +390,15 @@ <varlistentry> <term><option>--tpm2-pcrs=</option><arg rep="repeat">PCR</arg></term> - <listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the enrollment - requested via <option>--tpm2-device=</option> to. Takes a <literal>+</literal> separated list of - numeric PCR indexes in the range 0…23. If not used, defaults to PCR 7 only. If an empty string is - specified, binds the enrollment to no PCRs at all. - Registers may also be specified using string aliases.</para> - <para>For instance <option>--tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config</option> to bind to the registers - 4, 1, and 5. Check the PCR definitions table below for a full list - of available string aliases. - PCRs allow binding the enrollment to specific - software versions and system state, so that the enrolled unlocking key is only accessible (may be - "unsealed") if specific trusted software and/or configuration is used.</para> - - <table> - <title>Well-known PCR Definitions</title> - - <!-- See: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ --> - <!-- See: https://github.com/rhboot/shim/blob/main/README.tpm --> - <!-- See: https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html --> - <!-- See: https://sourceforge.net/p/linux-ima/wiki/Home/ --> - <!-- See: https://github.com/tianocore-docs/edk2-TrustedBootChain/blob/main/4_Other_Trusted_Boot_Chains.md --> - <!-- See: https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers --> - - <tgroup cols='3' align='left' colsep='1' rowsep='1'> - <colspec colname="pcr" /> - <colspec colname="string_alias" /> - <colspec colname="definition" /> - - <thead> - <row> - <entry>PCR</entry> - <entry>alias</entry> - <entry>Explanation</entry> - </row> - </thead> - - <tbody> - <row> - <entry>0</entry> - <entry>platform-code</entry> - <entry>Core system firmware executable code; changes on firmware updates</entry> - </row> - - <row> - <entry>1</entry> - <entry>platform-config</entry> - <entry>Core system firmware data/host platform configuration; typically contains serial and model numbers, changes on basic hardware/CPU/RAM replacements</entry> - </row> - - <row> - <entry>2</entry> - <entry>external-code</entry> - <entry>Extended or pluggable executable code; includes option ROMs on pluggable hardware</entry> - </row> - - <row> - <entry>3</entry> - <entry>external-config</entry> - <entry>Extended or pluggable firmware data; includes information about pluggable hardware</entry> - </row> - - <row> - <entry>4</entry> - <entry>boot-loader-code</entry> - <entry>Boot loader and additional drivers; changes on boot loader updates. The shim project will measure the PE binary it chain loads into this PCR. If the Linux kernel is invoked as UEFI PE binary, it is measured here, too. <citerefentry><refentrytitle>sd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures system extension images read from the ESP here too (see <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).</entry> - </row> - - <row> - <entry>5</entry> - <entry>boot-loader-config</entry> - <entry>GPT/Partition table; changes when the partitions are added, modified or removed</entry> - </row> - - <row> - <entry>7</entry> - <entry>secure-boot-policy</entry> - <entry>Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry> - </row> - - <!-- Grub measures all its commands and the kernel command line into PCR 8… --> - <!-- Grub measures all files it reads (including kernel image, initrd, …) into PCR 9… --> - - <row> - <entry>9</entry> - <entry>kernel-initrd</entry> - <entry>The Linux kernel measures all initrds it receives into this PCR.</entry> - <!-- Strictly speaking only Linux >= 5.17 using the LOAD_FILE2 protocol, see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df --> - </row> - - <row> - <entry>10</entry> - <entry>ima</entry> - <entry>The IMA project measures its runtime state into this PCR.</entry> - </row> - - <row> - <entry>11</entry> - <entry>kernel-boot</entry> - <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initrd. <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures boot phase strings into this PCR at various milestones of the boot process.</entry> - </row> - - <row> - <entry>12</entry> - <entry>kernel-config</entry> - <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry> - </row> - - <row> - <entry>13</entry> - <entry>sysexts</entry> - <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it loads and passed to the booted kernel into this PCR.</entry> - </row> - - <row> - <entry>14</entry> - <entry>shim-policy</entry> - <entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry> - </row> - - <row> - <entry>15</entry> - <entry>system-identity</entry> - <entry><citerefentry><refentrytitle>systemd-cryptsetup</refentrytitle><manvolnum>7</manvolnum></citerefentry> optionally measures the volume key of activated LUKS volumes into this PCR.</entry> - </row> - - <row> - <entry>16</entry> - <entry>debug</entry> - <entry>Debug</entry> - </row> - - <row> - <entry>23</entry> - <entry>application-support</entry> - <entry>Application Support</entry> - </row> - </tbody> - </tgroup> - </table> - - <para>For most applications it should be sufficient to bind against PCR 7 (and possibly PCR 14, if - shim/MOK is desired), as this includes measurements of the trusted certificates (and possibly hashes) - that are used to validate all components of the boot process up to and including the OS kernel. In - order to simplify firmware and OS version updates it's typically not advisable to include PCRs such - as 0 and 2 in the binding of the enrollment, since the program code they cover should already be - protected indirectly through the certificates measured into PCR 7. Validation through these - certificates is typically preferable over validation through direct measurements as it is less - brittle in context of OS/firmware updates: the measurements will change on every update, but code - signatures likely will validate against pre-existing certificates.</para></listitem> + <listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind to when + enrollment is requested via <option>--tpm2-device=</option>. Takes a list of PCR names or numeric + indices in the range 0…23. Multiple PCR indexes are separated by <literal>+</literal>. If not + specified, the default is to use PCR 7 only. If an empty string is specified, binds the enrollment to + no PCRs at all. See the table above for a list of available PCRs.</para> + + <para>Example: <option>--tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config</option> + specifies that PCR registers 4, 1, and 5 should be used.</para> + </listitem> </varlistentry> <varlistentry> @@ -422,20 +439,21 @@ <option>--tpm2-public-key-pcrs=</option>: the former binds decryption to the current, specific PCR values; the latter binds decryption to any set of PCR values for which a signature by the specified public key can be provided. The latter is hence more useful in scenarios where software updates shell - be possible without losing access to all previously encrypted LUKS2 volumes. - Like with <option>--tpm2-pcrs=</option>, string aliases as defined in the table above can also be used - to specify the registers, for instance <option>--tpm2-public-key-pcrs=boot-loader-code+system-identity</option>.</para> + be possible without losing access to all previously encrypted LUKS2 volumes. Like with + <option>--tpm2-pcrs=</option>, names defined in the table above can also be used to specify the + registers, for instance + <option>--tpm2-public-key-pcrs=boot-loader-code+system-identity</option>.</para> - <para>The <option>--tpm2-signature=</option> option takes a path to a TPM2 PCR signature file - as generated by the + <para>The <option>--tpm2-signature=</option> option takes a path to a TPM2 PCR signature file as + generated by the <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> - tool. If this is not specified explicitly a suitable signature file + tool. If this is not specified explicitly, a suitable signature file <filename>tpm2-pcr-signature.json</filename> is searched for in <filename>/etc/systemd/</filename>, - <filename>/run/systemd/</filename>, <filename>/usr/lib/systemd/</filename> (in this order) and - used. If a signature file is specified or found it is used to verify if the volume can be unlocked - with it given the current PCR state, before the new slot is written to disk. This is intended as - safety net to ensure that access to a volume is not lost if a public key is enrolled for which no - valid signature for the current PCR state is available. If the supplied signature does not unlock the + <filename>/run/systemd/</filename>, <filename>/usr/lib/systemd/</filename> (in this order) and used. + If a signature file is specified or found it is used to verify if the volume can be unlocked with it + given the current PCR state, before the new slot is written to disk. This is intended as safety net + to ensure that access to a volume is not lost if a public key is enrolled for which no valid + signature for the current PCR state is available. If the supplied signature does not unlock the current PCR state and public key combination, no slot is enrolled and the operation will fail. If no signature file is specified or found no such safety verification is done.</para></listitem> </varlistentry> diff --git a/man/systemd-fsck@.service.xml b/man/systemd-fsck@.service.xml index e928aebdb3..403286829e 100644 --- a/man/systemd-fsck@.service.xml +++ b/man/systemd-fsck@.service.xml @@ -51,17 +51,17 @@ <para><filename>systemd-fsck</filename> does not know any details about specific filesystems, and simply executes file system checkers specific to each filesystem type - (<filename>/sbin/fsck.<replaceable>type</replaceable></filename>). These checkers will decide if + (<filename>fsck.<replaceable>type</replaceable></filename>). These checkers will decide if the filesystem should actually be checked based on the time since last check, number of mounts, unclean unmount, etc.</para> <para><filename>systemd-fsck-root.service</filename> and <filename>systemd-fsck-usr.service</filename> - will activate <filename>reboot.target</filename> if <filename>/sbin/fsck</filename> returns the "System - should reboot" condition, or <filename>emergency.target</filename> if <filename>/sbin/fsck</filename> + will activate <filename>reboot.target</filename> if <filename>fsck</filename> returns the "System + should reboot" condition, or <filename>emergency.target</filename> if <filename>fsck</filename> returns the "Filesystem errors left uncorrected" condition.</para> <para><filename>systemd-fsck@.service</filename> will fail if - <filename>/sbin/fsck</filename> returns with either "System should reboot" + <filename>fsck</filename> returns with either "System should reboot" or "Filesystem errors left uncorrected" conditions. For filesystems listed in <filename>/etc/fstab</filename> without <literal>nofail</literal> or <literal>noauto</literal> options, <literal>local-fs.target</literal> diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml index ab730d2cc2..5d46a88c3e 100644 --- a/man/systemd.preset.xml +++ b/man/systemd.preset.xml @@ -62,23 +62,23 @@ <refsect1> <title>Preset File Format</title> - <para>The preset files contain a list of directives consisting of - either the word <literal>enable</literal> or - <literal>disable</literal> followed by a space and a unit name - (possibly with shell style wildcards), separated by newlines. - Empty lines and lines whose first non-whitespace character is <literal>#</literal> or - <literal>;</literal> are ignored. Multiple instance names for unit - templates may be specified as a space separated list at the end of - the line instead of the customary position between <literal>@</literal> - and the unit suffix.</para> + <para>The preset files contain a list of directives, one per line. Empty lines and lines whose first + non-whitespace character is <literal>#</literal> or <literal>;</literal> are ignored. Each directive + consists of one of the words <literal>enable</literal>, <literal>disable</literal>, or + <literal>ignore</literal>, followed by whitespace and a unit name. The unit name may contain shell-style + wildcards.</para> + + <para>For the enable directive for template units, one or more instance names may be specified as a + space-separated list after the unit name. In this case, those instances will be enabled instead of the + instance specified via DefaultInstance= in the unit.</para> <para>Presets must refer to the "real" unit file, and not to any aliases. See <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for a description of unit aliasing.</para> - <para>Two different directives are understood: - <literal>enable</literal> may be used to enable units by default, - <literal>disable</literal> to disable units by default.</para> + <para>Three different directives are understood: <literal>enable</literal> may be used to enable units by + default, <literal>disable</literal> to disable units by default, and <literal>ignore</literal> to ignore + units and leave existing configuration intact.</para> <para>If multiple lines apply to a unit name, the first matching one takes precedence over all others.</para> diff --git a/mkosi.conf.d/20-debian.conf b/mkosi.conf.d/20-debian.conf index 9e61b53eba..61a86310e4 100644 --- a/mkosi.conf.d/20-debian.conf +++ b/mkosi.conf.d/20-debian.conf @@ -10,7 +10,7 @@ Release=testing Packages= btrfs-progs cryptsetup-bin - dbus + dbus-broker default-dbus-session-bus f2fs-tools fdisk diff --git a/mkosi.conf.d/20-ubuntu.conf b/mkosi.conf.d/20-ubuntu.conf index 132f72a8d3..11c2721877 100644 --- a/mkosi.conf.d/20-ubuntu.conf +++ b/mkosi.conf.d/20-ubuntu.conf @@ -11,7 +11,7 @@ Repositories=universe Packages= btrfs-progs cryptsetup-bin - dbus + dbus-broker default-dbus-session-bus f2fs-tools fdisk diff --git a/mkosi.extra/usr/lib/systemd/system-preset/00-mkosi.preset b/mkosi.extra/usr/lib/systemd/system-preset/00-mkosi.preset new file mode 100644 index 0000000000..e21493123c --- /dev/null +++ b/mkosi.extra/usr/lib/systemd/system-preset/00-mkosi.preset @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +disable ssh.service +disable sshd.service +disable dnsmasq.service +disable isc-dhcp-server.service +disable isc-dhcp-server6.service diff --git a/mkosi.postinst b/mkosi.postinst index b84f034235..5aad60f97d 100755 --- a/mkosi.postinst +++ b/mkosi.postinst @@ -47,11 +47,6 @@ EOF systemctl mask systemd-hwdb-update.service fi -# Make sure dnsmasq doesn't start on boot on Debian/Ubuntu. -systemctl disable dnsmasq -# Make sure sshd doesn't start on Opensuse. -echo "disable sshd.service" > /usr/lib/systemd/system-preset/00-mkosi.preset - if [ -n "$IMAGE_ID" ] ; then sed -n \ -i \ @@ -1,19 +1,21 @@ # SPDX-License-Identifier: LGPL-2.1-or-later # # Fran Dieguez <frandieguez@gnome.org>, 2015. +# Fran Diéguez <frandieguez@gnome.org>, 2023. msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-10-20 10:35+0200\n" -"PO-Revision-Date: 2019-12-29 22:30+0100\n" +"PO-Revision-Date: 2023-04-14 18:20+0000\n" "Last-Translator: Fran Diéguez <frandieguez@gnome.org>\n" -"Language-Team: gnome-l10n-gl@gnome.org\n" +"Language-Team: Galician <https://translate.fedoraproject.org/projects/" +"systemd/master/gl/>\n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 2.2.4\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.15.2\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -70,63 +72,58 @@ msgstr "Requírese autenticación para recargar o estado de systemd." #: src/home/org.freedesktop.home1.policy:13 msgid "Create a home area" -msgstr "" +msgstr "Crear un área persoal" #: src/home/org.freedesktop.home1.policy:14 -#, fuzzy msgid "Authentication is required to create a user's home area." -msgstr "Requírese autenticación para estabelecer os servidores NTP." +msgstr "Requírese autenticación para crear un área persoal." #: src/home/org.freedesktop.home1.policy:23 msgid "Remove a home area" -msgstr "" +msgstr "Eliminar un área persoal" #: src/home/org.freedesktop.home1.policy:24 -#, fuzzy msgid "Authentication is required to remove a user's home area." -msgstr "Requírese autenticación para estabelecer os servidores NTP." +msgstr "Requírese autenticación para eliminar un área persoal." #: src/home/org.freedesktop.home1.policy:33 msgid "Check credentials of a home area" -msgstr "" +msgstr "Comprobar as credenciais dun área persoal" #: src/home/org.freedesktop.home1.policy:34 -#, fuzzy msgid "" "Authentication is required to check credentials against a user's home area." msgstr "" -"Requírese autenticación para anexar ou desanexar unha imaxe de servizo " -"portábel." +"Requírese autenticación para comprobar as credenciais do espazo persoal dun " +"usuario." #: src/home/org.freedesktop.home1.policy:43 msgid "Update a home area" -msgstr "" +msgstr "Actualizar un espazo persoal" #: src/home/org.freedesktop.home1.policy:44 -#, fuzzy msgid "Authentication is required to update a user's home area." -msgstr "Requírese autenticación para anexar un dispositivo a un asento." +msgstr "Requírese autenticación para actualizar o espazo persoal dun usuario." #: src/home/org.freedesktop.home1.policy:53 msgid "Resize a home area" -msgstr "" +msgstr "Redimensionar un espazo persoal" #: src/home/org.freedesktop.home1.policy:54 -#, fuzzy msgid "Authentication is required to resize a user's home area." -msgstr "Requírese autenticación para estabelecer os servidores NTP." +msgstr "" +"Requírese autenticación para redimensionar o espazo persoal dun usuario." #: src/home/org.freedesktop.home1.policy:63 msgid "Change password of a home area" -msgstr "" +msgstr "Cambiar contrasinal dun espazo persoal" #: src/home/org.freedesktop.home1.policy:64 -#, fuzzy msgid "" "Authentication is required to change the password of a user's home area." msgstr "" -"Requírese autenticación para xestionar as sesións, usuarios e asentos " -"activos." +"Requírese autenticación para cambiar o contrasinal dun espazo persoal dun " +"usuario." #: src/hostname/org.freedesktop.hostname1.policy:20 msgid "Set hostname" @@ -168,22 +165,19 @@ msgstr "Requírese autenticación para obter o UUID de produto." #: src/hostname/org.freedesktop.hostname1.policy:61 msgid "Get hardware serial number" -msgstr "" +msgstr "Obter o número de serie do hardware" #: src/hostname/org.freedesktop.hostname1.policy:62 -#, fuzzy msgid "Authentication is required to get hardware serial number." -msgstr "Requírese autenticación para estabelecer a hora do sistema." +msgstr "Requírese autenticación para obter o número serie do hardware." #: src/hostname/org.freedesktop.hostname1.policy:71 -#, fuzzy msgid "Get system description" -msgstr "Estabelecer o fuso horario" +msgstr "Obter a descrición do sistema" #: src/hostname/org.freedesktop.hostname1.policy:72 -#, fuzzy msgid "Authentication is required to get system description." -msgstr "Requírese autenticación para estabelecer o fuso horario do sistema." +msgstr "Requírese autenticación para obter a descrición do sistema." #: src/import/org.freedesktop.import1.policy:22 msgid "Import a VM or container image" @@ -334,13 +328,11 @@ msgstr "" "do sistema do interruptor da tapa." #: src/login/org.freedesktop.login1.policy:117 -#, fuzzy msgid "Allow applications to inhibit system handling of the reboot key" msgstr "" -"Permitir ás aplicacións inhibir a xestión do sistema da tecla de acendido" +"Permitir ás aplicacións inhibir a xestión do sistema da tecla de reinicio" #: src/login/org.freedesktop.login1.policy:118 -#, fuzzy msgid "" "Authentication is required for an application to inhibit system handling of " "the reboot key." @@ -475,7 +467,6 @@ msgid "Halt the system while an application is inhibiting this" msgstr "Deter o sistema cando unha aplicación solicitou a súa inhibición" #: src/login/org.freedesktop.login1.policy:258 -#, fuzzy msgid "" "Authentication is required to halt the system while an application is " "inhibiting this." @@ -623,12 +614,11 @@ msgstr "Requírese autenticación para estabelecer unha mensaxe de muro" #: src/login/org.freedesktop.login1.policy:406 msgid "Change Session" -msgstr "" +msgstr "Cambiar sesión" #: src/login/org.freedesktop.login1.policy:407 -#, fuzzy msgid "Authentication is required to change the virtual terminal." -msgstr "Requírese autenticación para deter o sistema." +msgstr "Requírese autenticación para cambiar o terminal virtual." #: src/machine/org.freedesktop.machine1.policy:22 msgid "Log into a local container" @@ -813,12 +803,11 @@ msgstr "Requírese autenticación para restabelecer as preferencias de DNS." #: src/network/org.freedesktop.network1.policy:143 msgid "DHCP server sends force renew message" -msgstr "" +msgstr "O servidor DHCP envía un mensaxe de renovación forzada" #: src/network/org.freedesktop.network1.policy:144 -#, fuzzy msgid "Authentication is required to send force renew message." -msgstr "Requírese autenticación para estabelecer unha mensaxe de muro" +msgstr "Requírese autenticación para enviar o mensaxe de renovación forzada." #: src/network/org.freedesktop.network1.policy:154 msgid "Renew dynamic addresses" @@ -983,12 +972,9 @@ msgstr "" "'$(unit)'." #: src/core/dbus-unit.c:762 -#, fuzzy msgid "" "Authentication is required to freeze or thaw the processes of '$(unit)' unit." -msgstr "" -"Requírese autenticación para enviarlle un sinal UNIX aos procesos de " -"'$(unit)'." +msgstr "Requírese autenticación para conxelar o proceso da unidade '$(unit)'." #~ msgid "" #~ "Authentication is required to halt the system while an application asked " diff --git a/src/basic/chase.c b/src/basic/chase.c index e8d38279fd..eb4bda07a6 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -85,13 +85,14 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT)); assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME)); assert(!FLAGS_SET(flags, CHASE_TRAIL_SLASH|CHASE_EXTRACT_FILENAME)); + assert(!FLAGS_SET(flags, CHASE_MKDIR_0755) || (flags & (CHASE_NONEXISTENT | CHASE_PARENT)) != 0); assert(dir_fd >= 0 || dir_fd == AT_FDCWD); /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ - if ((flags & CHASE_NONEXISTENT)) + if (FLAGS_SET(flags, CHASE_NONEXISTENT)) assert(!ret_fd); - if ((flags & CHASE_STEP)) + if (FLAGS_SET(flags, CHASE_STEP)) assert(!ret_fd); if (isempty(path)) @@ -176,7 +177,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root * set and doesn't care about any of the other special features we provide either. */ - r = openat(dir_fd, path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); + r = openat(dir_fd, path, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); if (r < 0) return -errno; @@ -221,7 +222,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fstat(fd, &st) < 0) return -errno; - if (flags & CHASE_TRAIL_SLASH) + if (FLAGS_SET(flags, CHASE_TRAIL_SLASH)) append_trail_slash = ENDSWITH_SET(buffer, "/", "/."); for (todo = buffer;;) { @@ -283,10 +284,10 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int } else return r; - if (flags & CHASE_STEP) + if (FLAGS_SET(flags, CHASE_STEP)) goto chased_one; - if (flags & CHASE_SAFE && + if (FLAGS_SET(flags, CHASE_SAFE) && unsafe_transition(&st, &st_parent)) return log_unsafe_transition(fd, fd_parent, path, flags); @@ -317,7 +318,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int return -ENOMEM; break; - } else if (flags & CHASE_NONEXISTENT) { + } else if (FLAGS_SET(flags, CHASE_NONEXISTENT)) { if (!path_extend(&done, first, todo)) return -ENOMEM; @@ -330,18 +331,18 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fstat(child, &st_child) < 0) return -errno; - if ((flags & CHASE_SAFE) && + if (FLAGS_SET(flags, CHASE_SAFE) && unsafe_transition(&st, &st_child)) return log_unsafe_transition(fd, child, path, flags); - if ((flags & CHASE_NO_AUTOFS) && + if (FLAGS_SET(flags, CHASE_NO_AUTOFS) && fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) return log_autofs_mount_point(child, path, flags); - if (S_ISLNK(st_child.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { + if (S_ISLNK(st_child.st_mode) && !(FLAGS_SET(flags, CHASE_NOFOLLOW) && isempty(todo))) { _cleanup_free_ char *destination = NULL; - if (flags & CHASE_PROHIBIT_SYMLINKS) + if (FLAGS_SET(flags, CHASE_PROHIBIT_SYMLINKS)) return log_prohibited_symlink(child, flags); /* This is a symlink, in this case read the destination. But let's make sure we @@ -368,7 +369,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fstat(fd, &st) < 0) return -errno; - if (flags & CHASE_SAFE && + if (FLAGS_SET(flags, CHASE_SAFE) && unsafe_transition(&st_child, &st)) return log_unsafe_transition(child, fd, path, flags); @@ -385,7 +386,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int free_and_replace(buffer, destination); todo = buffer; - if (flags & CHASE_STEP) + if (FLAGS_SET(flags, CHASE_STEP)) goto chased_one; continue; @@ -403,7 +404,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int close_and_replace(fd, child); } - if (flags & CHASE_PARENT) { + if (FLAGS_SET(flags, CHASE_PARENT)) { r = stat_verify_directory(&st); if (r < 0) return r; @@ -438,7 +439,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int *ret_fd = TAKE_FD(fd); } - if (flags & CHASE_STEP) + if (FLAGS_SET(flags, CHASE_STEP)) return 1; return exists; @@ -499,7 +500,7 @@ int chase(const char *path, const char *original_root, ChaseFlags flags, char ** assert(path_is_absolute(root)); assert(!empty_or_root(root)); - if (flags & CHASE_PREFIX_ROOT) { + if (FLAGS_SET(flags, CHASE_PREFIX_ROOT)) { absolute = path_join(root, path); if (!absolute) return -ENOMEM; @@ -516,7 +517,7 @@ int chase(const char *path, const char *original_root, ChaseFlags flags, char ** path = path_startswith(absolute, empty_to_root(root)); if (!path) - return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, + return log_full_errno(FLAGS_SET(flags, CHASE_WARN) ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(ECHRNG), "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", absolute, empty_to_root(root)); diff --git a/src/basic/chase.h b/src/basic/chase.h index 7e9ebe0685..40121f7d70 100644 --- a/src/basic/chase.h +++ b/src/basic/chase.h @@ -24,8 +24,14 @@ typedef enum ChaseFlags { * full path is still stored in ret_path and only the returned * file descriptor will point to the parent directory. Note that * the result path is the root or '.', then the file descriptor - * also points to the result path even if this flag is set. */ - CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. */ + * also points to the result path even if this flag is set. + * When this specified, chase() will succeed with 1 even if the + * file points to the last path component does not exist. */ + CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. This + * needs to be set with CHASE_NONEXISTENT and/or CHASE_PARENT. + * Note, chase_and_open() or friends always add CHASE_PARENT flag + * when internally call chase(), hence CHASE_MKDIR_0755 can be + * safely set without CHASE_NONEXISTENT and CHASE_PARENT. */ CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved path */ } ChaseFlags; diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 751d5a35ce..db36b2f2f6 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -94,7 +94,9 @@ int name_to_handle_at_loop( /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */ n = h->handle_bytes; - if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */ + + /* paranoia: check for overlow (note that .handle_bytes is unsigned only) */ + if (n > UINT_MAX - offsetof(struct file_handle, f_handle)) return -EOVERFLOW; } } @@ -213,9 +215,14 @@ int fd_is_mount_point(int fd, const char *filename, int flags) { * reported. Also, btrfs subvolumes have different st_dev, even though they aren't real mounts of * their own. */ - if (statx(fd, filename, (FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW) | - (flags & AT_EMPTY_PATH) | - AT_NO_AUTOMOUNT, STATX_TYPE, &sx) < 0) { + if (statx(fd, + filename, + (FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW) | + (flags & AT_EMPTY_PATH) | + AT_NO_AUTOMOUNT | /* don't trigger automounts – mounts are a local concept, hence no need to trigger automounts to determine STATX_ATTR_MOUNT_ROOT */ + AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */ + STATX_TYPE, + &sx) < 0) { if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) return -errno; @@ -264,9 +271,9 @@ int fd_is_mount_point(int fd, const char *filename, int flags) { /* If the file handle for the directory we are interested in and its parent are identical, * we assume this is the root directory, which is a mount point. */ - if (h->handle_bytes == h_parent->handle_bytes && - h->handle_type == h_parent->handle_type && - memcmp(h->f_handle, h_parent->f_handle, h->handle_bytes) == 0) + if (h->handle_type == h_parent->handle_type && + memcmp_nn(h->f_handle, h->handle_bytes, + h_parent->f_handle, h_parent->handle_bytes) == 0) return 1; return mount_id != mount_id_parent; @@ -360,7 +367,9 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) { if (statx(dir_fd, path, - AT_NO_AUTOMOUNT|(isempty(path) ? AT_EMPTY_PATH : AT_SYMLINK_NOFOLLOW), + (isempty(path) ? AT_EMPTY_PATH : AT_SYMLINK_NOFOLLOW) | + AT_NO_AUTOMOUNT | /* don't trigger automounts, mnt_id is a local concept */ + AT_STATX_DONT_SYNC, /* don't go to the network, mnt_id is a local concept */ STATX_MNT_ID, &buf.sx) < 0) { if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) @@ -559,12 +568,12 @@ int dev_is_devtmpfs(void) { if (mid != mount_id) continue; - e = strstr(line, " - "); + e = strstrafter(line, " - "); if (!e) continue; /* accept any name that starts with the currently expected type */ - if (startswith(e + 3, "devtmpfs")) + if (startswith(e, "devtmpfs")) return true; } diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 1755b3275a..75483924af 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -29,6 +29,18 @@ static inline char* strstr_ptr(const char *haystack, const char *needle) { return strstr(haystack, needle); } +static inline char *strstrafter(const char *haystack, const char *needle) { + char *p; + + /* Returns NULL if not found, or pointer to first character after needle if found */ + + p = strstr_ptr(haystack, needle); + if (!p) + return NULL; + + return p + strlen(needle); +} + static inline const char* strnull(const char *s) { return s ?: "(null)"; } diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index c01f41cb44..dd36cae860 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -235,9 +235,7 @@ static int property_get_unit_file_preset( r = unit_get_unit_file_preset(u); - return sd_bus_message_append(reply, "s", - r < 0 ? NULL: - r > 0 ? "enabled" : "disabled"); + return sd_bus_message_append(reply, "s", preset_action_past_tense_to_string(r)); } static int property_get_job( diff --git a/src/core/unit.c b/src/core/unit.c index 298a6de7b3..76d823aed0 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -4094,7 +4094,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) { return u->unit_file_state; } -int unit_get_unit_file_preset(Unit *u) { +PresetAction unit_get_unit_file_preset(Unit *u) { int r; assert(u); diff --git a/src/core/unit.h b/src/core/unit.h index d3eb5ba881..ea236d933c 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -11,6 +11,7 @@ #include "bpf-program.h" #include "condition.h" #include "emergency-action.h" +#include "install.h" #include "list.h" #include "show-status.h" #include "set.h" @@ -358,7 +359,7 @@ typedef struct Unit { /* Cached unit file state and preset */ UnitFileState unit_file_state; - int unit_file_preset; + PresetAction unit_file_preset; /* Where the cpu.stat or cpuacct.usage was at the time the unit was started */ nsec_t cpu_usage_base; @@ -954,7 +955,7 @@ void unit_start_on_failure(Unit *u, const char *dependency_name, UnitDependencyA void unit_trigger_notify(Unit *u); UnitFileState unit_get_unit_file_state(Unit *u); -int unit_get_unit_file_preset(Unit *u); +PresetAction unit_get_unit_file_preset(Unit *u); Unit* unit_ref_set(UnitRef *ref, Unit *source, Unit *target); void unit_ref_unset(UnitRef *ref); diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index cfdc6b24bf..4d29babbd3 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -345,6 +345,7 @@ static int run(int argc, char *argv[]) { if (r == 0) { char dash_c[STRLEN("-C") + DECIMAL_STR_MAX(int) + 1]; int progress_socket = -1; + _cleanup_free_ char *fsck_path = NULL; const char *cmdline[9]; int i = 0; @@ -365,7 +366,13 @@ static int run(int argc, char *argv[]) { } else dash_c[0] = 0; - cmdline[i++] = "/sbin/fsck"; + r = find_executable("fsck", &fsck_path); + if (r < 0) { + log_error_errno(r, "Cannot find fsck binary: %m"); + _exit(FSCK_OPERATIONAL_ERROR); + } + + cmdline[i++] = fsck_path; cmdline[i++] = arg_repair; cmdline[i++] = "-T"; diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index ac3d2b417c..dda63d8fc0 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -215,6 +215,7 @@ static int block_get_size_by_path(const char *path, uint64_t *ret) { static int run_fsck(const char *node, const char *fstype) { int r, exit_status; pid_t fsck_pid; + _cleanup_free_ char *fsck_path = NULL; assert(node); assert(fstype); @@ -227,6 +228,14 @@ static int run_fsck(const char *node, const char *fstype) { return 0; } + r = find_executable("fsck", &fsck_path); + /* We proceed anyway if we can't determine whether the fsck + * binary for some specific fstype exists, + * but the lack of the main fsck binary should be considered + * an error. */ + if (r < 0) + return log_error_errno(r, "Cannot find fsck binary: %m"); + r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, &fsck_pid); @@ -234,7 +243,7 @@ static int run_fsck(const char *node, const char *fstype) { return r; if (r == 0) { /* Child */ - execl("/sbin/fsck", "/sbin/fsck", "-aTl", node, NULL); + execl(fsck_path, fsck_path, "-aTl", node, NULL); log_open(); log_error_errno(errno, "Failed to execute fsck: %m"); _exit(FSCK_OPERATIONAL_ERROR); diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 0bd2c89eb6..f3f59e2ac3 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -281,11 +281,10 @@ static int sound_device_compare(const char *devpath_a, const char *devpath_b) { * kernel makes this guarantee when creating those devices, and hence we should too when * enumerating them. */ - sound_a = strstr(devpath_a, "/sound/card"); + sound_a = strstrafter(devpath_a, "/sound/card"); if (!sound_a) return 0; - sound_a += STRLEN("/sound/card"); sound_a = strchr(devpath_a, '/'); if (!sound_a) return 0; diff --git a/src/locale/localed-util.c b/src/locale/localed-util.c index 5247d139eb..17d96aea1d 100644 --- a/src/locale/localed-util.c +++ b/src/locale/localed-util.c @@ -71,8 +71,7 @@ void x11_context_replace(X11Context *dest, X11Context *src) { assert(src); x11_context_clear(dest); - *dest = *src; - *src = (X11Context) {}; + *dest = TAKE_STRUCT(*src); } bool x11_context_isempty(const X11Context *xc) { @@ -196,8 +195,7 @@ void vc_context_replace(VCContext *dest, VCContext *src) { assert(src); vc_context_clear(dest); - *dest = *src; - *src = (VCContext) {}; + *dest = TAKE_STRUCT(*src); } bool vc_context_isempty(const VCContext *vc) { diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index bb3a04c9e4..6477cc43fa 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -4578,8 +4578,7 @@ static int merge_settings(Settings *settings, const char *path) { log_warning("Ignoring CPUAffinity= setting, file '%s' is not trusted.", path); else { cpu_set_reset(&arg_cpu_set); - arg_cpu_set = settings->cpu_set; - settings->cpu_set = (CPUSet) {}; + arg_cpu_set = TAKE_STRUCT(settings->cpu_set); } } diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index cf0e2be5fa..6acae48c2b 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -285,6 +285,8 @@ int etc_hosts_parse(EtcHosts *hosts, FILE *f) { unsigned nr = 0; int r; + assert(hosts); + for (;;) { _cleanup_free_ char *line = NULL; char *l; @@ -313,8 +315,7 @@ int etc_hosts_parse(EtcHosts *hosts, FILE *f) { strip_localhost(&t); etc_hosts_clear(hosts); - *hosts = t; - t = (EtcHosts) {}; /* prevent cleanup */ + *hosts = TAKE_STRUCT(t); return 0; } diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 7cc63e8848..a5ae2478fe 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -389,8 +389,7 @@ static int boot_entry_load_type1( return log_syntax(NULL, LOG_ERR, tmp.path, line, r, "Error while parsing: %m"); } - *entry = tmp; - tmp = (BootEntry) {}; + *entry = TAKE_STRUCT(tmp); return 0; } @@ -744,8 +743,7 @@ static int boot_entry_load_unified( return log_oom(); } - *ret = tmp; - tmp = (BootEntry) {}; + *ret = TAKE_STRUCT(tmp); return 0; } diff --git a/src/shared/bus-get-properties.c b/src/shared/bus-get-properties.c index 3d0887e6df..53e5d6b99f 100644 --- a/src/shared/bus-get-properties.c +++ b/src/shared/bus-get-properties.c @@ -149,9 +149,9 @@ int bus_property_get_rlimit( s = is_soft ? strndupa_safe(property, is_soft - property) : property; /* Skip over any prefix, such as "Default" */ - assert_se(p = strstr(s, "Limit")); + assert_se(p = strstrafter(s, "Limit")); - z = rlimit_from_string(p + 5); + z = rlimit_from_string(p); assert(z >= 0); (void) getrlimit(z, &buf); diff --git a/src/shared/cpu-set-util.c b/src/shared/cpu-set-util.c index 34c13cf969..d096576cd6 100644 --- a/src/shared/cpu-set-util.c +++ b/src/shared/cpu-set-util.c @@ -145,6 +145,8 @@ int parse_cpu_set_full( _cleanup_(cpu_set_reset) CPUSet c = {}; const char *p = ASSERT_PTR(rvalue); + assert(cpu_set); + for (;;) { _cleanup_free_ char *word = NULL; unsigned cpu_lower, cpu_upper; @@ -181,9 +183,7 @@ int parse_cpu_set_full( } } - /* On success, transfer ownership to the output variable */ - *cpu_set = c; - c = (CPUSet) {}; + *cpu_set = TAKE_STRUCT(c); return 0; } @@ -200,6 +200,8 @@ int parse_cpu_set_extend( _cleanup_(cpu_set_reset) CPUSet cpuset = {}; int r; + assert(old); + r = parse_cpu_set_full(rvalue, &cpuset, true, unit, filename, line, lvalue); if (r < 0) return r; @@ -211,8 +213,7 @@ int parse_cpu_set_extend( } if (!old->set) { - *old = cpuset; - cpuset = (CPUSet) {}; + *old = TAKE_STRUCT(cpuset); return 1; } @@ -286,7 +287,6 @@ int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set) { return r; } - *set = s; - s = (CPUSet) {}; + *set = TAKE_STRUCT(s); return 0; } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 46c42cfc95..b7c049e4c3 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1693,6 +1693,7 @@ static int is_loop_device(const char *path) { static int run_fsck(int node_fd, const char *fstype) { int r, exit_status; pid_t pid; + _cleanup_free_ char *fsck_path = NULL; assert(node_fd >= 0); assert(fstype); @@ -1707,6 +1708,14 @@ static int run_fsck(int node_fd, const char *fstype) { return 0; } + r = find_executable("fsck", &fsck_path); + /* We proceed anyway if we can't determine whether the fsck + * binary for some specific fstype exists, + * but the lack of the main fsck binary should be considered + * an error. */ + if (r < 0) + return log_error_errno(r, "Cannot find fsck binary: %m"); + r = safe_fork_full( "(fsck)", NULL, @@ -1717,7 +1726,7 @@ static int run_fsck(int node_fd, const char *fstype) { return log_debug_errno(r, "Failed to fork off fsck: %m"); if (r == 0) { /* Child */ - execl("/sbin/fsck", "/sbin/fsck", "-aT", FORMAT_PROC_FD_PATH(node_fd), NULL); + execl(fsck_path, fsck_path, "-aT", FORMAT_PROC_FD_PATH(node_fd), NULL); log_open(); log_debug_errno(errno, "Failed to execl() fsck: %m"); _exit(FSCK_OPERATIONAL_ERROR); @@ -1725,7 +1734,7 @@ static int run_fsck(int node_fd, const char *fstype) { exit_status = wait_for_terminate_and_check("fsck", pid, 0); if (exit_status < 0) - return log_debug_errno(exit_status, "Failed to fork off /sbin/fsck: %m"); + return log_debug_errno(exit_status, "Failed to fork off %s: %m", fsck_path); if ((exit_status & ~FSCK_ERROR_CORRECTED) != FSCK_SUCCESS) { log_debug("fsck failed with exit status %i.", exit_status); diff --git a/src/shared/edit-util.c b/src/shared/edit-util.c index 9fd74973aa..96ad721343 100644 --- a/src/shared/edit-util.c +++ b/src/shared/edit-util.c @@ -300,10 +300,8 @@ static int strip_edit_temp_file(EditFile *e) { assert(e->context->marker_end); - contents_start = strstr(old_contents, e->context->marker_start); - if (contents_start) - contents_start += strlen(e->context->marker_start); - else + contents_start = strstrafter(old_contents, e->context->marker_start); + if (!contents_start) contents_start = old_contents; contents_end = strstr(contents_start, e->context->marker_end); diff --git a/src/shared/install.c b/src/shared/install.c index 964092456f..152e517ebc 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -52,18 +52,22 @@ typedef struct { OrderedHashmap *have_processed; } InstallContext; -typedef enum { - PRESET_UNKNOWN, - PRESET_ENABLE, - PRESET_DISABLE, -} PresetAction; - struct UnitFilePresetRule { char *pattern; PresetAction action; char **instances; }; +/* NB! strings use past tense. */ +static const char *const preset_action_past_tense_table[_PRESET_ACTION_MAX] = { + [PRESET_UNKNOWN] = "unknown", + [PRESET_ENABLE] = "enabled", + [PRESET_DISABLE] = "disabled", + [PRESET_IGNORE] = "ignored", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(preset_action_past_tense, PresetAction); + static bool install_info_has_rules(const InstallInfo *i) { assert(i); @@ -78,14 +82,19 @@ static bool install_info_has_also(const InstallInfo *i) { return !strv_isempty(i->also); } -void unit_file_presets_freep(UnitFilePresets *p) { +static void unit_file_preset_rule_done(UnitFilePresetRule *rule) { + assert(rule); + + free(rule->pattern); + strv_free(rule->instances); +} + +void unit_file_presets_done(UnitFilePresets *p) { if (!p) return; - for (size_t i = 0; i < p->n_rules; i++) { - free(p->rules[i].pattern); - strv_free(p->rules[i].instances); - } + FOREACH_ARRAY(rule, p->rules, p->n_rules) + unit_file_preset_rule_done(rule); free(p->rules); p->n_rules = 0; @@ -3215,7 +3224,7 @@ static int presets_find_config(RuntimeScope scope, const char *root_dir, char ** } static int read_presets(RuntimeScope scope, const char *root_dir, UnitFilePresets *presets) { - _cleanup_(unit_file_presets_freep) UnitFilePresets ps = {}; + _cleanup_(unit_file_presets_done) UnitFilePresets ps = {}; _cleanup_strv_free_ char **files = NULL; int r; @@ -3241,7 +3250,7 @@ static int read_presets(RuntimeScope scope, const char *root_dir, UnitFilePreset for (;;) { _cleanup_free_ char *line = NULL; - UnitFilePresetRule rule = {}; + _cleanup_(unit_file_preset_rule_done) UnitFilePresetRule rule = {}; const char *parameter; char *l; @@ -3292,11 +3301,25 @@ static int read_presets(RuntimeScope scope, const char *root_dir, UnitFilePreset }; } + parameter = first_word(l, "ignore"); + if (parameter) { + char *pattern; + + pattern = strdup(parameter); + if (!pattern) + return -ENOMEM; + + rule = (UnitFilePresetRule) { + .pattern = pattern, + .action = PRESET_IGNORE, + }; + } + if (rule.action) { if (!GREEDY_REALLOC(ps.rules, ps.n_rules + 1)) return -ENOMEM; - ps.rules[ps.n_rules++] = rule; + ps.rules[ps.n_rules++] = TAKE_STRUCT(rule); continue; } @@ -3305,8 +3328,7 @@ static int read_presets(RuntimeScope scope, const char *root_dir, UnitFilePreset } ps.initialized = true; - *presets = ps; - ps = (UnitFilePresets){}; + *presets = TAKE_STRUCT(ps); return 0; } @@ -3378,24 +3400,27 @@ static int query_presets(const char *name, const UnitFilePresets *presets, char switch (action) { case PRESET_UNKNOWN: log_debug("Preset files don't specify rule for %s. Enabling.", name); - return 1; + return PRESET_ENABLE; case PRESET_ENABLE: if (instance_name_list && *instance_name_list) STRV_FOREACH(s, *instance_name_list) log_debug("Preset files say enable %s.", *s); else log_debug("Preset files say enable %s.", name); - return 1; + return PRESET_ENABLE; case PRESET_DISABLE: log_debug("Preset files say disable %s.", name); - return 0; + return PRESET_DISABLE; + case PRESET_IGNORE: + log_debug("Preset files say ignore %s.", name); + return PRESET_IGNORE; default: assert_not_reached(); } } -int unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) { - _cleanup_(unit_file_presets_freep) UnitFilePresets tmp = {}; +PresetAction unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) { + _cleanup_(unit_file_presets_done) UnitFilePresets tmp = {}; int r; if (!cached) @@ -3488,7 +3513,7 @@ static int preset_prepare_one( if (r < 0) return r; - if (r > 0) { + if (r == PRESET_ENABLE) { if (instance_name_list) STRV_FOREACH(s, instance_name_list) { r = install_info_discover_and_check(plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, @@ -3503,7 +3528,7 @@ static int preset_prepare_one( return r; } - } else + } else if (r == PRESET_DISABLE) r = install_info_discover(minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &info, changes, n_changes); @@ -3521,7 +3546,7 @@ int unit_file_preset( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; + _cleanup_(unit_file_presets_done) UnitFilePresets presets = {}; const char *config_path; int r; @@ -3560,7 +3585,7 @@ int unit_file_preset_all( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; + _cleanup_(unit_file_presets_done) UnitFilePresets presets = {}; const char *config_path = NULL; int r; diff --git a/src/shared/install.h b/src/shared/install.h index 2067969f83..30b07a725f 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -217,8 +217,20 @@ typedef struct { bool initialized; } UnitFilePresets; -void unit_file_presets_freep(UnitFilePresets *p); -int unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached); +typedef enum PresetAction { + PRESET_UNKNOWN, + PRESET_ENABLE, + PRESET_DISABLE, + PRESET_IGNORE, + _PRESET_ACTION_MAX, + _PRESET_ACTION_INVALID = -EINVAL, + _PRESET_ACTION_ERRNO_MAX = -ERRNO_MAX, /* Ensure this type covers the whole negative errno range */ +} PresetAction; + +const char *preset_action_past_tense_to_string(PresetAction action); + +void unit_file_presets_done(UnitFilePresets *p); +PresetAction unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached); const char *unit_file_state_to_string(UnitFileState s) _const_; UnitFileState unit_file_state_from_string(const char *s) _pure_; diff --git a/src/shared/numa-util.c b/src/shared/numa-util.c index 4f757f0b55..a954ea349e 100644 --- a/src/shared/numa-util.c +++ b/src/shared/numa-util.c @@ -120,8 +120,7 @@ int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *ret) { return r; } - *ret = s; - s = (CPUSet) {}; + *ret = TAKE_STRUCT(s); return 0; } diff --git a/src/shared/xml.c b/src/shared/xml.c index df381d85b8..3b1fb41fef 100644 --- a/src/shared/xml.c +++ b/src/shared/xml.c @@ -87,26 +87,26 @@ int xml_tokenize(const char **p, char **name, void **state, unsigned *line) { if (startswith(b, "!--")) { /* A comment */ - e = strstr(b + 3, "-->"); + e = strstrafter(b + 3, "-->"); if (!e) return -EINVAL; - inc_lines(line, b, e + 3 - b); + inc_lines(line, b, e - b); - c = e + 3; + c = e; continue; } if (*b == '?') { /* Processing instruction */ - e = strstr(b + 1, "?>"); + e = strstrafter(b + 1, "?>"); if (!e) return -EINVAL; - inc_lines(line, b, e + 2 - b); + inc_lines(line, b, e - b); - c = e + 2; + c = e; continue; } diff --git a/src/systemctl/systemctl-list-unit-files.c b/src/systemctl/systemctl-list-unit-files.c index 69f0d977c1..aad248fe1f 100644 --- a/src/systemctl/systemctl-list-unit-files.c +++ b/src/systemctl/systemctl-list-unit-files.c @@ -49,9 +49,24 @@ static bool output_show_unit_file(const UnitFileList *u, char **states, char **p return true; } +static const char* preset_action_to_color(PresetAction action, bool underline) { + assert(action >= 0); + + switch (action) { + case PRESET_ENABLE: + return underline ? ansi_highlight_green_underline() : ansi_highlight_green(); + case PRESET_DISABLE: + return underline ? ansi_highlight_red_underline() : ansi_highlight_red(); + case PRESET_IGNORE: + return underline ? ansi_highlight_yellow_underline() : ansi_highlight_yellow(); + default: + return NULL; + } +} + static int output_unit_file_list(const UnitFileList *units, unsigned c) { _cleanup_(table_unrefp) Table *table = NULL; - _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; + _cleanup_(unit_file_presets_done) UnitFilePresets presets = {}; int r; table = table_new("unit file", "state", "preset"); @@ -98,22 +113,14 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) { return table_log_add_error(r); if (show_preset_for_state(u->state)) { - const char *unit_preset_str, *on_preset_color; + const char *on_preset_color = underline ? on_underline : ansi_normal(); r = unit_file_query_preset(arg_runtime_scope, arg_root, id, &presets); - if (r < 0) { - unit_preset_str = "n/a"; - on_preset_color = underline ? on_underline : ansi_normal(); - } else if (r == 0) { - unit_preset_str = "disabled"; - on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); - } else { - unit_preset_str = "enabled"; - on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green(); - } + if (r >= 0) + on_preset_color = preset_action_to_color(r, underline); r = table_add_many(table, - TABLE_STRING, unit_preset_str, + TABLE_STRING, strna(preset_action_past_tense_to_string(r)), TABLE_SET_BOTH_COLORS, strempty(on_preset_color)); } else r = table_add_many(table, diff --git a/src/test/test-chase.c b/src/test/test-chase.c index dee781929d..1e98f5c6ed 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -509,7 +509,7 @@ TEST(chaseat) { assert_se(streq(result, "q")); result = mfree(result); - assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755, NULL, NULL) == -ENOENT); + assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT); /* Test CHASE_FILENAME */ diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index c866cff022..55b8894ecc 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -580,8 +580,11 @@ TEST(preset_and_list) { UnitFileList *fl; _cleanup_(hashmap_freep) Hashmap *h = NULL; + CLEANUP_ARRAY(changes, n_changes, install_changes_free); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT); assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) == -ENOENT); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); assert_se(write_string_file(p, @@ -593,13 +596,20 @@ TEST(preset_and_list) { "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + p = strjoina(root, "/usr/lib/systemd/system/preset-ignore.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); assert_se(write_string_file(p, "enable *-yes.*\n" + "ignore *-ignore.*\n" "disable *\n", WRITE_STRING_FILE_CREATE) >= 0); assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 1); @@ -612,6 +622,7 @@ TEST(preset_and_list) { assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); @@ -623,6 +634,7 @@ TEST(preset_and_list) { assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 0); @@ -631,6 +643,7 @@ TEST(preset_and_list) { assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_preset_all(RUNTIME_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); @@ -652,6 +665,7 @@ TEST(preset_and_list) { assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(h = hashmap_new(&unit_file_list_hash_ops_free)); assert_se(unit_file_get_list(RUNTIME_SCOPE_SYSTEM, root, h, NULL, NULL) >= 0); @@ -674,6 +688,10 @@ TEST(preset_and_list) { } assert_se(got_yes && got_no); + + assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(revert) { diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 9eb048adfa..65052c627d 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -1247,4 +1247,21 @@ TEST(find_line_startswith) { assert_se(!find_line_startswith(emptystring, "x")); } +TEST(strstrafter) { + static const char buffer[] = "abcdefghijklmnopqrstuvwxyz"; + + assert_se(!strstrafter(NULL, NULL)); + assert_se(!strstrafter("", NULL)); + assert_se(!strstrafter(NULL, "")); + assert_se(streq_ptr(strstrafter("", ""), "")); + + assert_se(strstrafter(buffer, "a") == buffer + 1); + assert_se(strstrafter(buffer, "") == buffer); + assert_se(strstrafter(buffer, "ab") == buffer + 2); + assert_se(strstrafter(buffer, "cde") == buffer + 5); + assert_se(strstrafter(buffer, "xyz") == strchr(buffer, 0)); + assert_se(strstrafter(buffer, buffer) == strchr(buffer, 0)); + assert_se(!strstrafter(buffer, "-")); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 3adafcae83..2eb6e5ea33 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -3637,8 +3637,7 @@ static int parse_line( if (!GREEDY_REALLOC(existing->items, existing->n_items + 1)) return log_oom(); - existing->items[existing->n_items++] = i; - i = (struct Item) {}; + existing->items[existing->n_items++] = TAKE_STRUCT(i); /* Sort item array, to enforce stable ordering of application */ typesafe_qsort(existing->items, existing->n_items, item_compare); diff --git a/test/test-functions b/test/test-functions index 27951cb105..f6f2bd6322 100644 --- a/test/test-functions +++ b/test/test-functions @@ -195,6 +195,7 @@ BASICTOOLS=( losetup lz4cat mkfifo + mknod mktemp modprobe mount diff --git a/test/units/generator-utils.sh b/test/units/generator-utils.sh index 1973efefba..42c6a77353 100644 --- a/test/units/generator-utils.sh +++ b/test/units/generator-utils.sh @@ -54,8 +54,27 @@ opt_filter_consumed() {( run_and_list() { local generator="${1:?}" local out_dir="${2:?}" + local environ + + # If $PID1_ENVIRON is set temporarily overmount /proc/1/environ with + # a temporary file that contains contents of $PID1_ENVIRON. This is + # necessary in cases where the generator reads the environment through + # getenv_for_pid(1, ...) or similar like getty-generator does. + # + # Note: $PID1_ENVIRON should be a NUL separated list of env assignments + if [[ -n "${PID1_ENVIRON:-}" ]]; then + environ="$(mktemp)" + echo -ne "${PID1_ENVIRON}\0" >"${environ:?}" + mount -v --bind "$environ" /proc/1/environ + fi rm -fr "${out_dir:?}"/* - "$generator" "$out_dir" + mkdir -p "$out_dir"/{normal,early,late} + SYSTEMD_LOG_LEVEL="${SYSTEMD_LOG_LEVEL:-debug}" "$generator" "$out_dir/normal" "$out_dir/early" "$out_dir/late" ls -lR "$out_dir" + + if [[ -n "${environ:-}" ]]; then + umount /proc/1/environ + rm -f "$environ" + fi } diff --git a/test/units/testsuite-81.debug-generator.sh b/test/units/testsuite-81.debug-generator.sh index d2b2bf1111..fddf85a54c 100755 --- a/test/units/testsuite-81.debug-generator.sh +++ b/test/units/testsuite-81.debug-generator.sh @@ -37,69 +37,69 @@ ARGS=( : "debug-shell: regular" CMDLINE="ro root=/ ${ARGS[*]} rd.systemd.debug_shell" SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/masked-no-suffix.service" /dev/null -link_eq "$OUT_DIR/masked.service" /dev/null -link_eq "$OUT_DIR/masked.socket" /dev/null -link_endswith "$OUT_DIR/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service -link_endswith "$OUT_DIR/default.target.wants/wanted.service" /lib/systemd/system/wanted.service -link_endswith "$OUT_DIR/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount +link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null +link_eq "$OUT_DIR/early/masked.service" /dev/null +link_eq "$OUT_DIR/early/masked.socket" /dev/null +link_endswith "$OUT_DIR/early/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service +link_endswith "$OUT_DIR/early/default.target.wants/wanted.service" /lib/systemd/system/wanted.service +link_endswith "$OUT_DIR/early/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount # Following stuff should be ignored, as it's prefixed with rd. -test ! -h "$OUT_DIR/masked-initrd.service" -test ! -h "$OUT_DIR/default.target.wants/wants-initrd.service" -test ! -h "$OUT_DIR/default.target.wants/debug-shell.service" -test ! -d "$OUT_DIR/initrd.target.wants" +test ! -h "$OUT_DIR/early/masked-initrd.service" +test ! -h "$OUT_DIR/early/default.target.wants/wants-initrd.service" +test ! -h "$OUT_DIR/early/default.target.wants/debug-shell.service" +test ! -d "$OUT_DIR/early/initrd.target.wants" # Let's re-run the generator with systemd.debug_shell that should be honored : "debug-shell: regular + systemd.debug_shell" CMDLINE="$CMDLINE systemd.debug_shell" SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_endswith "$OUT_DIR/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service +link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service # Same thing, but with custom tty : "debug-shell: regular + systemd.debug_shell=/dev/tty666" CMDLINE="$CMDLINE systemd.debug_shell=/dev/tty666" SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_endswith "$OUT_DIR/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service -grep -F "/dev/tty666" "$OUT_DIR/debug-shell.service.d/50-tty.conf" +link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service +grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf" # Now override the default target via systemd.unit= : "debug-shell: regular + systemd.unit=" CMDLINE="$CMDLINE systemd.unit=my-fancy.target" SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/masked-no-suffix.service" /dev/null -link_eq "$OUT_DIR/masked.service" /dev/null -link_eq "$OUT_DIR/masked.socket" /dev/null -link_endswith "$OUT_DIR/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service -link_endswith "$OUT_DIR/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service -link_endswith "$OUT_DIR/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount -link_endswith "$OUT_DIR/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service -test ! -d "$OUT_DIR/default.target.wants" +link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null +link_eq "$OUT_DIR/early/masked.service" /dev/null +link_eq "$OUT_DIR/early/masked.socket" /dev/null +link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service +link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service +link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount +link_endswith "$OUT_DIR/early/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service +test ! -d "$OUT_DIR/early/default.target.wants" # Initrd scenario : "debug-shell: initrd" CMDLINE="ro root=/ ${ARGS[*]} systemd.debug_shell" SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/masked-initrd.service" /dev/null -link_endswith "$OUT_DIR/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service +link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null +link_endswith "$OUT_DIR/early/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service # The non-initrd stuff (i.e. without the rd. suffix) should be ignored in # this case -test ! -h "$OUT_DIR/masked-no-suffix.service" -test ! -h "$OUT_DIR/masked.service" -test ! -h "$OUT_DIR/masked.socket" -test ! -h "$OUT_DIR/initrd.target.wants/debug-shell.service" -test ! -d "$OUT_DIR/default.target.wants" +test ! -h "$OUT_DIR/early/masked-no-suffix.service" +test ! -h "$OUT_DIR/early/masked.service" +test ! -h "$OUT_DIR/early/masked.socket" +test ! -h "$OUT_DIR/early/initrd.target.wants/debug-shell.service" +test ! -d "$OUT_DIR/early/default.target.wants" # Again, but with rd.systemd.debug_shell : "debug-shell: initrd + rd.systemd.debug_shell" CMDLINE="$CMDLINE rd.systemd.debug_shell" SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_endswith "$OUT_DIR/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service +link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service # Override the default target : "debug-shell: initrd + rd.systemd.unit" CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target" SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/masked-initrd.service" /dev/null -link_endswith "$OUT_DIR/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service -test ! -d "$OUT_DIR/initrd.target.wants" +link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null +link_endswith "$OUT_DIR/early/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service +test ! -d "$OUT_DIR/early/initrd.target.wants" diff --git a/test/units/testsuite-81.fstab-generator.sh b/test/units/testsuite-81.fstab-generator.sh index a9efc0bc7a..d772f8de0a 100755 --- a/test/units/testsuite-81.fstab-generator.sh +++ b/test/units/testsuite-81.fstab-generator.sh @@ -7,8 +7,6 @@ set -o pipefail # shellcheck source=test/units/generator-utils.sh . "$(dirname "$0")/generator-utils.sh" -export SYSTEMD_LOG_LEVEL=debug - GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-fstab-generator" NETWORK_FS_RX="^(afs|ceph|cifs|gfs|gfs2|ncp|ncpfs|nfs|nfs4|ocfs2|orangefs|pvfs2|smb3|smbfs|davfs|glusterfs|lustre|sshfs)$" OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)" @@ -65,7 +63,7 @@ FSTAB_GENERAL=( FSTAB_GENERAL_ROOT=( # rootfs with bunch of options we should ignore and fsck enabled - "/dev/test1 / ext4 noauto,nofail,automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1" + "/dev/test1 / ext4 noauto,nofail,x-systemd.automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1" "${FSTAB_GENERAL[@]}" ) @@ -101,7 +99,7 @@ check_fstab_mount_units() { local what where fstype opts passno unit local item opt split_options filtered_options supp service device arg local array_name="${1:?}" - local out_dir="${2:?}" + local out_dir="${2:?}/normal" # Get a reference to the array from its name local -n fstab_entries="$array_name" @@ -251,8 +249,14 @@ check_fstab_mount_units() { elif [[ "$opt" == x-systemd.automount ]]; then # The $unit should have an accompanying automount unit supp="$(systemd-escape --suffix=automount --path "$where")" - test -e "$out_dir/$supp" - link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp" + if [[ "$where" == / ]]; then + # This option is ignored for rootfs mounts + test ! -e "$out_dir/$supp" + (! link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp") + else + test -e "$out_dir/$supp" + link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp" + fi elif [[ "$opt" =~ ^x-systemd.idle-timeout= ]]; then # The timeout applies to the automount unit, not the original # mount one @@ -285,9 +289,6 @@ check_fstab_mount_units() { done } -# TODO -# - kernel arguments - : "fstab-generator: regular" printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" cat "$FSTAB" @@ -320,10 +321,10 @@ SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check # Check the default stuff that we (almost) always create in initrd : "fstab-generator: initrd default" SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR" -test -e "$OUT_DIR/sysroot.mount" -test -e "$OUT_DIR/systemd-fsck-root.service" -link_eq "$OUT_DIR/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" -link_eq "$OUT_DIR/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" +test -e "$OUT_DIR/normal/sysroot.mount" +test -e "$OUT_DIR/normal/systemd-fsck-root.service" +link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" +link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" : "fstab-generator: run as systemd-sysroot-fstab-check in initrd" ln -svf "$GENERATOR_BIN" /tmp/systemd-sysroot-fstab-check @@ -358,3 +359,43 @@ SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATO SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR" SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" (! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR") + +: "fstab-generator: kernel args - systemd.swap=0" +printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" +cat "$FSTAB" +SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="systemd.swap=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +# No swap units should get created here +[[ "$(find "$OUT_DIR" -name "*.swap" | wc -l)" -eq 0 ]] + +# Possible TODO +# - combine the rootfs & usrfs arguments and mix them with fstab entries +# - systemd.volatile= +: "fstab-generator: kernel args - root= + rootfstype= + rootflags=" +# shellcheck disable=SC2034 +EXPECTED_FSTAB=( + "/dev/disk/by-label/rootfs / ext4 noexec,ro 0 1" +) +CMDLINE="root=LABEL=rootfs rootfstype=ext4 rootflags=noexec" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +# The /proc/cmdline here is a dummy value to tell the in_initrd_host() function +# we're parsing host's fstab, but it's all on the kernel cmdline instead +SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB=/proc/cmdline check_fstab_mount_units EXPECTED_FSTAB "$OUT_DIR" + +# This is a very basic sanity test that involves manual checks, since adding it +# to the check_fstab_mount_units() function would make it way too complex +# (yet another possible TODO) +: "fstab-generator: kernel args - mount.usr= + mount.usrfstype= + mount.usrflags=" +CMDLINE="mount.usr=UUID=be780f43-8803-4a76-9732-02ceda6e9808 mount.usrfstype=ext4 mount.usrflags=noexec,nodev" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +cat "$OUT_DIR/normal/sysroot-usr.mount" "$OUT_DIR/normal/sysusr-usr.mount" +# The general idea here is to mount the device to /sysusr/usr and then +# bind-mount /sysusr/usr to /sysroot/usr +grep -qE "^What=/dev/disk/by-uuid/be780f43-8803-4a76-9732-02ceda6e9808$" "$OUT_DIR/normal/sysusr-usr.mount" +grep -qE "^Where=/sysusr/usr$" "$OUT_DIR/normal/sysusr-usr.mount" +grep -qE "^Type=ext4$" "$OUT_DIR/normal/sysusr-usr.mount" +grep -qE "^Options=noexec,nodev,ro$" "$OUT_DIR/normal/sysusr-usr.mount" +link_eq "$OUT_DIR/normal/initrd-usr-fs.target.requires/sysusr-usr.mount" "../sysusr-usr.mount" +grep -qE "^What=/sysusr/usr$" "$OUT_DIR/normal/sysroot-usr.mount" +grep -qE "^Where=/sysroot/usr$" "$OUT_DIR/normal/sysroot-usr.mount" +grep -qE "^Options=bind$" "$OUT_DIR/normal/sysroot-usr.mount" +link_eq "$OUT_DIR/normal/initrd-fs.target.requires/sysroot-usr.mount" "../sysroot-usr.mount" diff --git a/test/units/testsuite-81.getty-generator.sh b/test/units/testsuite-81.getty-generator.sh new file mode 100755 index 0000000000..103e966191 --- /dev/null +++ b/test/units/testsuite-81.getty-generator.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail +# Disable history expansion so we don't have to escape ! in strings below +set +o histexpand + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-getty-generator" +OUT_DIR="$(mktemp -d /tmp/getty-generator.XXX)" + +at_exit() { + rm -frv "${OUT_DIR:?}" +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +if in_container; then + # Do a limited test in a container, as writing to /dev is usually restrited + : "getty-generator: \$container_ttys env (container)" + # In a container we allow only /dev/pts/* ptys + PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" + + # console-getty.service is always pulled in in containers + link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service" + link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service" + test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service" + test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service" + + exit 0 +fi + +DUMMY_ACTIVE_CONSOLES=( + "hvc99" + "xvc99" + "hvsi99" + "sclp_line99" + "ttysclp99" + "3270!tty99" + "dummy99" +) +DUMMY_INACTIVE_CONSOLES=( + "inactive99" + "xvc199" +) +DUMMY_CONSOLES=( + "${DUMMY_ACTIVE_CONSOLES[@]}" + "${DUMMY_INACTIVE_CONSOLES[@]}" +) +# Create a bunch of dummy consoles +for console in "${DUMMY_CONSOLES[@]}"; do + mknod "/dev/$console" c 4 0 +done +# Sneak in one "not-a-tty" console +touch /dev/notatty99 +# Temporarily replace /sys/class/tty/console/active with our list of dummy +# consoles so getty-generator can process them +echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99 >/tmp/dummy-active-consoles +mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active + +: "getty-generator: no arguments" +# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out +PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do + unit="$(systemd-escape --template serial-getty@.service "$console")" + link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service" +done +for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do + unit="$(systemd-escape --template serial-getty@.service "$console")" + test ! -e "$OUT_DIR/normal/getty.target.wants/$unit" + test ! -h "$OUT_DIR/normal/getty.target.wants/$unit" +done + +: "getty-generator: systemd.getty_auto=0 on kernel cmdline" +SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment" +PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +# Cleanup +umount /sys/class/tty/console/active +rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99 diff --git a/test/units/testsuite-81.run-generator.sh b/test/units/testsuite-81.run-generator.sh new file mode 100755 index 0000000000..9bd74efbaf --- /dev/null +++ b/test/units/testsuite-81.run-generator.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-run-generator" +OUT_DIR="$(mktemp -d /tmp/run-generator.XXX)" + +at_exit() { + rm -frv "${OUT_DIR:?}" +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +check_kernel_cmdline_target() { + local out_dir="${1:?}/normal" + + cat "$out_dir/kernel-command-line.target" + grep -qE "^Requires=kernel-command-line.service$" "$out_dir/kernel-command-line.target" + grep -qE "^After=kernel-command-line.service$" "$out_dir/kernel-command-line.target" + + link_eq "$out_dir/default.target" "kernel-command-line.target" +} + +: "run-generator: empty cmdline" +SYSTEMD_PROC_CMDLINE="" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +: "run-generator: single command" +CMDLINE="systemd.run='echo hello world'" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +check_kernel_cmdline_target "$OUT_DIR" +UNIT="$OUT_DIR/normal/kernel-command-line.service" +cat "$UNIT" +systemd-analyze verify --man=no --recursive-errors=no "$UNIT" +grep -qE "^SuccessAction=exit$" "$UNIT" +grep -qE "^FailureAction=exit$" "$UNIT" +grep -qE "^ExecStart=echo hello world$" "$UNIT" + +: "run-generator: multiple commands + success/failure actions" +ARGS=( + # These should be ignored + "systemd.run" + "systemd.run_success_action" + "systemd.run_failure_action" + + # Set actions which we will overwrite later + "systemd.run_success_action=" + "systemd.run_failure_action=" + + "systemd.run=/bin/false" + "systemd.run=" + "systemd.run=/bin/true" + "systemd.run='echo this is a long string'" + + "systemd.run_success_action=reboot" + "systemd.run_failure_action=poweroff-force" +) +CMDLINE="${ARGS[*]}" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +check_kernel_cmdline_target "$OUT_DIR" +UNIT="$OUT_DIR/normal/kernel-command-line.service" +cat "$UNIT" +systemd-analyze verify --man=no --recursive-errors=no "$UNIT" +grep -qE "^SuccessAction=reboot$" "$UNIT" +grep -qE "^FailureAction=poweroff-force$" "$UNIT" +grep -qE "^ExecStart=/bin/false$" "$UNIT" +grep -qE "^ExecStart=$" "$UNIT" +grep -qE "^ExecStart=/bin/true$" "$UNIT" +grep -qE "^ExecStart=echo this is a long string$" "$UNIT" diff --git a/test/units/testsuite-81.sh b/test/units/testsuite-81.sh index 63f4cb6430..13c767e490 100755 --- a/test/units/testsuite-81.sh +++ b/test/units/testsuite-81.sh @@ -5,8 +5,6 @@ set -o pipefail : >/failed -systemctl log-level debug - for script in "${0%.sh}".*.sh; do echo "Running $script" "./$script" diff --git a/test/units/testsuite-81.system-update-generator.sh b/test/units/testsuite-81.system-update-generator.sh new file mode 100755 index 0000000000..6dfee0d898 --- /dev/null +++ b/test/units/testsuite-81.system-update-generator.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-system-update-generator" +OUT_DIR="$(mktemp -d /tmp/system-update-generator-generator.XXX)" + +at_exit() { + rm -frv "${OUT_DIR:?}" /system-update +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +rm -f /system-update + +: "system-update-generator: no /system-update flag" +run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +: "system-update-generator: with /system-update flag" +touch /system-update +run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target" + +: "system-update-generator: kernel cmdline warnings" +# We should warn if the default target is overriden on the kernel cmdline +# by a runlevel or systemd.unit=, but still generate the symlink +SYSTEMD_PROC_CMDLINE="systemd.unit=foo.bar 3" run_and_list "$GENERATOR_BIN" "$OUT_DIR" |& tee /tmp/system-update-generator.log +link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target" +grep -qE "Offline system update overridden .* systemd.unit=" /tmp/system-update-generator.log +grep -qE "Offline system update overridden .* runlevel" /tmp/system-update-generator.log |