From 34f1d05ca3b350257f1d9ddb0aba57f129c27c67 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 16 Sep 2024 20:26:40 +0200 Subject: Merging upstream version 256.5. Signed-off-by: Daniel Baumann --- .github/workflows/mkosi.yml | 4 +- docs/CONTAINER_INTERFACE.md | 4 +- docs/CONTROL_GROUP_INTERFACE.md | 2 +- docs/HACKING.md | 8 +- man/repart.d.xml | 2 +- man/systemd-detect-virt.xml | 46 ++- man/systemd-path.xml | 6 + man/systemd-system.conf.xml | 8 +- man/systemd-sysusers.xml | 18 +- man/systemd-tmpfiles.xml | 18 +- man/systemd.net-naming-scheme.xml | 6 +- man/systemd.network.xml | 41 ++- man/systemd.service.xml | 6 + meson.build | 3 +- meson.version | 2 +- mkosi.conf | 4 +- mkosi.conf.d/10-centos/mkosi.conf | 5 +- mkosi.conf.d/10-debian-ubuntu/mkosi.conf | 4 + mkosi.conf.d/10-opensuse/mkosi.conf | 1 + mkosi.extra/root/.gdbinit | 4 + mkosi.extra/usr/lib/tmpfiles.d/locale.conf | 1 - mkosi.extra/usr/lib/tmpfiles.d/snapshot.conf | 3 + .../mkosi.conf.d/debian-ubuntu/mkosi.build.chroot | 14 +- .../build/mkosi.conf.d/opensuse/mkosi.conf | 7 +- mkosi.images/exitrd/mkosi.conf.d/10-opensuse.conf | 4 + .../minimal-base/mkosi.conf.d/10-opensuse.conf | 4 + mkosi.postinst.chroot | 15 +- mkosi.repart/10-root.conf | 1 + network/80-wifi-station.network.example | 1 + rules.d/50-udev-default.rules.in | 6 +- src/basic/cgroup-util.c | 10 +- src/basic/confidential-virt.c | 66 ++-- src/basic/confidential-virt.h | 1 + src/basic/hexdecoct.c | 18 +- src/basic/log.c | 11 +- src/basic/log.h | 16 +- src/boot/efi/boot.c | 2 +- src/boot/efi/efi-string.c | 2 +- src/boot/efi/linux.c | 6 +- src/boot/efi/linux.h | 3 +- src/boot/efi/linux_x86.c | 21 +- src/boot/efi/pe.c | 7 +- src/boot/efi/pe.h | 2 +- src/boot/efi/vmm.c | 9 +- src/core/exec-credential.c | 21 +- src/core/exec-invoke.c | 16 +- src/core/execute-serialize.c | 56 ++-- src/core/execute.c | 1 + src/core/load-dropin.c | 2 +- src/core/load-dropin.h | 4 +- src/core/socket.c | 4 +- src/core/unit.c | 2 +- src/firstboot/firstboot.c | 68 +++-- src/fundamental/confidential-virt-fundamental.h | 1 + src/import/import-raw.c | 5 + src/import/import-tar.c | 5 + src/journal/journald-server.c | 7 +- src/journal/journald.c | 3 +- src/journal/journald.conf | 2 +- src/kernel-install/50-depmod.install | 1 + src/kernel-install/90-loaderentry.install.in | 20 +- src/libsystemd/sd-event/sd-event.c | 3 +- src/login/logind-dbus.c | 39 +-- src/network/networkd-link.c | 41 ++- src/network/networkd-ndisc.c | 6 - src/network/networkd-routing-policy-rule.c | 7 +- src/nspawn/nspawn-network.c | 50 ++++ src/nspawn/nspawn-network.h | 1 + src/nspawn/nspawn.c | 4 + src/pcrlock/pcrlock.c | 16 +- src/resolve/resolved-bus.c | 5 + src/resolve/resolved-dns-stream.c | 6 + src/resolve/resolved-dns-transaction.c | 4 +- src/resolve/resolved-varlink.c | 5 + src/shared/base-filesystem.c | 24 +- src/shared/edit-util.c | 2 +- src/shared/resize-fs.h | 6 +- src/shared/tests.h | 37 +++ src/systemctl/systemctl-logind.c | 8 + src/systemctl/systemctl.c | 4 + src/sysusers/sysusers.c | 8 +- src/test/test-conf-parser.c | 8 +- src/test/test-fs-util.c | 76 +++-- src/test/test-id128.c | 332 ++++++++++----------- src/test/test-log.c | 14 +- src/test/test-macro.c | 15 + src/ukify/test/pytest.ini | 2 + test/README.testsuite | 54 +++- test/TEST-04-JOURNAL/test.sh | 2 +- test/TEST-46-HOMED/test.sh | 2 +- test/TEST-54-CREDS/meson.build | 12 +- test/TEST-74-AUX-UTILS/test.sh | 2 +- test/fuzz/fuzz-systemctl-parse-argv/oss-fuzz-70153 | Bin 0 -> 67 bytes test/integration-test-setup.sh | 57 ++++ test/integration-test-wrapper.py | 33 +- test/meson.build | 8 +- test/test-functions | 6 +- test/test-sysusers/test-16.expected-group | 1 + test/test-sysusers/test-16.expected-passwd | 2 + test/test-sysusers/test-16.input | 7 + test/test.service.in | 3 + test/units/TEST-04-JOURNAL.bsod.sh | 4 +- .../TEST-04-JOURNAL.stopped-socket-activation.sh | 10 + test/units/TEST-07-PID1.issue-31752.sh | 44 +++ test/units/TEST-09-REBOOT.sh | 4 + test/units/TEST-17-UDEV.credentials.sh | 2 +- test/units/TEST-19-CGROUP.delegate.sh | 183 ++++++------ test/units/TEST-54-CREDS.sh | 5 +- test/units/TEST-58-REPART.sh | 8 +- test/units/TEST-70-TPM2.cryptsetup.sh | 2 +- test/units/TEST-70-TPM2.pcrlock.sh | 5 + test/units/TEST-73-LOCALE.sh | 11 +- test/units/TEST-74-AUX-UTILS.firstboot.sh | 79 ++++- test/units/TEST-82-SOFTREBOOT.sh | 15 + 114 files changed, 1304 insertions(+), 620 deletions(-) create mode 100644 mkosi.extra/root/.gdbinit delete mode 100644 mkosi.extra/usr/lib/tmpfiles.d/locale.conf create mode 100644 mkosi.extra/usr/lib/tmpfiles.d/snapshot.conf create mode 100644 src/ukify/test/pytest.ini create mode 100644 test/fuzz/fuzz-systemctl-parse-argv/oss-fuzz-70153 create mode 100755 test/integration-test-setup.sh create mode 100644 test/test-sysusers/test-16.expected-group create mode 100644 test/test-sysusers/test-16.expected-passwd create mode 100644 test/test-sysusers/test-16.input create mode 100755 test/units/TEST-04-JOURNAL.stopped-socket-activation.sh create mode 100755 test/units/TEST-07-PID1.issue-31752.sh diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml index 7070e9c..5842158 100644 --- a/.github/workflows/mkosi.yml +++ b/.github/workflows/mkosi.yml @@ -105,7 +105,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - uses: systemd/mkosi@7e975957a6af65c2e70428b6cda0c163ca7e1adc + - uses: systemd/mkosi@4eba736412c702bbbe2c6d4a58a92fa977219249 # Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space # immediately, we remove the files in the background. However, we first move them to a different location @@ -159,8 +159,6 @@ jobs: [Host] QemuMem=4G - # We build with debuginfo so there's no point in mounting the sources into the machine. - RuntimeBuildSources=no EOF - name: Generate secure boot key diff --git a/docs/CONTAINER_INTERFACE.md b/docs/CONTAINER_INTERFACE.md index e64953c..e20c76d 100644 --- a/docs/CONTAINER_INTERFACE.md +++ b/docs/CONTAINER_INTERFACE.md @@ -237,7 +237,9 @@ care should be taken to avoid naming conflicts. `systemd` (and in particular directory: it's used by code outside the container to insert mounts inside it only, and is mostly an internal vehicle to achieve this. Other container managers that want to implement similar functionality might consider using - the same directory. + the same directory. Alternatively, the new mount API may be used by the + container manager to establish new mounts in the container without the need + for the `/run/host/incoming/` directory. 2. The `/run/host/inaccessible/` directory may be set up by the container manager to include six file nodes: `reg`, `dir`, `fifo`, `sock`, `chr`, diff --git a/docs/CONTROL_GROUP_INTERFACE.md b/docs/CONTROL_GROUP_INTERFACE.md index f95cf76..5298fa4 100644 --- a/docs/CONTROL_GROUP_INTERFACE.md +++ b/docs/CONTROL_GROUP_INTERFACE.md @@ -223,7 +223,7 @@ Use these APIs to register any kind of process workload with systemd to be place ### Reading Accounting Information -Note that there's currently no systemd API to retrieve accounting information from cgroups. For now, if you need to retrieve this information use `/proc/$PID/cgroup` to determine the cgroup path for your process in the `cpuacct` controller (or whichever controller matters to you), and then read the attributes directly from the cgroup tree. +Accounting information is available via the `MemoryCurrent`, `MemoryPeak`, `MemorySwapCurrent`, `MemorySwapPeak`, `MemoryZSwapCurrent`, `MemoryAvailable`, `EffectiveMemoryMax`, `EffectiveMemoryHigh`, `CPUUsageNSec`, `EffectiveCPUs`, `EffectiveMemoryNodes`, `TasksCurrent`, `EffectiveTasksMax`, `IPIngressBytes`, `IPIngressPackets`, `IPEgressBytes`, `IPEgressPackets`, `IOReadBytes`, `IOReadOperations`, `IOWriteBytes`, and `IOWriteOperations` D-Bus properties. To read this and other information directly from the cgroup tree, get the unit's cgroup path (relative to `/sys/fs/cgroup`) from the `ControlGroup` property, by calling [`sd_pid_get_cgroup()`](https://www.freedesktop.org/software/systemd/man/latest/sd_pid_get_cgroup.html), or by parsing `/proc/$PID/cgroup`. If you want to collect the exit status and other runtime parameters of your transient scope or service unit after the processes in them ended set the `RemainAfterExit` boolean property when creating it. This will has the effect that the unit will stay around even after all processes in it died, in the `SubState="exited"` state. Simply watch for state changes until this state is reached, then read the status details from the various properties you need, and finally terminate the unit via `StopUnit()` on the `Manager` object or `Stop()` on the `Unit` object itself. diff --git a/docs/HACKING.md b/docs/HACKING.md index a905fc7..ac1efa7 100644 --- a/docs/HACKING.md +++ b/docs/HACKING.md @@ -80,10 +80,10 @@ choose the right one depending on the distribution of the container or virtual machine): ```sh -mkosi -t none && mkosi ssh dnf upgrade --disablerepo="*" "/work/build/*.rpm" # CentOS/Fedora -mkosi -t none && mkosi ssh apt install --reinstall "/work/build/*.deb" # Debian/Ubuntu -mkosi -t none && mkosi ssh pacman -U "/work/build/*.pkg.tar" # Arch Linux -mkosi -t none && mkosi ssh zypper install --allow-unsigned-rpm "/work/build/*.rpm" # OpenSUSE +mkosi -t none && mkosi ssh dnf upgrade --disablerepo="*" --assumeyes "/work/build/*.rpm" # CentOS/Fedora +mkosi -t none && mkosi ssh apt-get install "/work/build/*.deb" # Debian/Ubuntu +mkosi -t none && mkosi ssh pacman --upgrade --needed --noconfirm "/work/build/*.pkg.tar" # Arch Linux +mkosi -t none && mkosi ssh zypper --non-interactive install --allow-unsigned-rpm "/work/build/*.rpm" # OpenSUSE ``` and optionally restart the daemon(s) you're working on using diff --git a/man/repart.d.xml b/man/repart.d.xml index 52e6b97..474521e 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -708,7 +708,7 @@ fstab5. - If both bit 50 and 59 are set for a partition (i.e. the partition is marked both read-only and + If both bit 60 and 59 are set for a partition (i.e. the partition is marked both read-only and marked for file system growing) the latter is typically without effect: the read-only flag takes precedence in most tools reading these flags, and since growing the file system involves writing to the partition it is consequently ignored. diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 2239294..a4fcdfb 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -62,7 +62,7 @@ - VM + VM qemu QEMU software virtualization, without KVM @@ -217,6 +217,50 @@ WSL is categorized as a container for practical purposes. Multiple WSL environments share the same kernel and services should generally behave like when being run in a container. + + When executed with , instead of + printing the virtualization technology, it will display the + confidential virtual machine technology, if any. The + following technologies are currently identified: + + + Known confidential virtualization technologies + + + + + + Arch + ID + Technology + + + + + x86_64 + sev + AMD Secure Encrypted Virtualization + + + sev-es + AMD Secure Encrypted Virtualization - Encrypted State + + + sev-snp + AMD Secure Encrypted Virtualization - Secure Nested Paging + + + tdx + Intel Trust Domain Extensions + + + s390x + protvirt + IBM Protected Virtualization (Secure Execution) + + + +
diff --git a/man/systemd-path.xml b/man/systemd-path.xml index 153a9bd..16f7927 100644 --- a/man/systemd-path.xml +++ b/man/systemd-path.xml @@ -43,6 +43,12 @@ The variables whose name begins with search- do not refer to individual paths, but instead to a list of colon-separated search paths, in their order of precedence. + + Note that paths which depend on environment variables are + computed with systemd-path's invoked + environment, and not the system or user manager's environment. As + such, the output of systemd-path may not + reflect the behavior of manager processes. diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index ae5b61b..39117f6 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -474,10 +474,12 @@ ManagerEnvironment= Takes the same arguments as DefaultEnvironment=, see above. Sets - environment variables just for the manager process itself. In contrast to user managers, these variables - are not inherited by processes spawned by the system manager, use DefaultEnvironment= + environment variables for the manager process itself. These variables are inherited by processes + spawned by user managers, but not the system manager - use DefaultEnvironment= for that. Note that these variables are merged into the existing environment block. In particular, in - case of the system manager, this includes variables set by the kernel based on the kernel command line. + case of the system manager, this includes variables set by the kernel based on the kernel command line. + As with DefaultEnvironment=, this environment block is internal, and changes are not + reflected in the manager's /proc/PID/environ. Setting environment variables for the manager process may be useful to modify its behaviour. See Known Environment Variables for a diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml index 8954227..8ace9a8 100644 --- a/man/systemd-sysusers.xml +++ b/man/systemd-sysusers.xml @@ -42,15 +42,15 @@ If invoked with no arguments, directives from the configuration files found in the directories specified by - sysusers.d5. When - invoked with positional arguments, if option - is specified, arguments specified on the command line are used instead of the configuration file - PATH. Otherwise, just the configuration specified by the command line - arguments is executed. If the string - is specified instead of a filename, the - configuration is read from standard input. If the argument is a file name (without any slashes), all - configuration directories are searched for a matching file and the file found that has the highest - priority is executed. If the argument is a path, that file is used directly without searching the - configuration directories for any other matching file. + sysusers.d5 are + executed. When invoked with positional arguments, if option + is specified, arguments specified on the + command line are used instead of the configuration file PATH. Otherwise, just + the configuration specified by the command line arguments is executed. If the string - + is specified instead of a filename, the configuration is read from standard input. If the argument is a + file name (without any slashes), all configuration directories are searched for a matching file and the + file found that has the highest priority is executed. If the argument is a path, that file is used + directly without searching the configuration directories for any other matching file. diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index e744a4c..923503e 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -65,15 +65,15 @@ If invoked with no arguments, directives from the configuration files found in the directories specified by - tmpfiles.d5. When - invoked with positional arguments, if option - is specified, arguments specified on the command line are used instead of the configuration file - PATH. Otherwise, just the configuration specified by the command line - arguments is executed. If the string - is specified instead of a filename, the - configuration is read from standard input. If the argument is a file name (without any slashes), all - configuration directories are searched for a matching file and the file found that has the highest - priority is executed. If the argument is a path, that file is used directly without searching the - configuration directories for any other matching file. + tmpfiles.d5 are + executed. When invoked with positional arguments, if option + is specified, arguments specified on the + command line are used instead of the configuration file PATH. Otherwise, just + the configuration specified by the command line arguments is executed. If the string - + is specified instead of a filename, the configuration is read from standard input. If the argument is a + file name (without any slashes), all configuration directories are searched for a matching file and the + file found that has the highest priority is executed. If the argument is a path, that file is used + directly without searching the configuration directories for any other matching file. System services (systemd-tmpfiles-setup.service, systemd-tmpfiles-setup-dev-early.service, diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml index ff811c2..7931298 100644 --- a/man/systemd.net-naming-scheme.xml +++ b/man/systemd.net-naming-scheme.xml @@ -478,7 +478,8 @@ bridge as that would create naming conflict when there are more child devices on that bridge. Now, this is relaxed and we will use slot information to generate the name based on it but only if the PCI device has multiple functions. This is safe because distinct function number is a part of - the device name for multifunction devices. + the device name for multifunction devices. Note, this is reverted in v255. + See below. @@ -521,6 +522,9 @@ Naming was changed for SR-IOV virtual device representors to enable the change introduced in v254 by default. + If we detect that a PCI device associated with a slot is a PCI bridge, we no longer set + ID_NET_NAME_SLOT, reverting a change that was introduced in v251. + diff --git a/man/systemd.network.xml b/man/systemd.network.xml index b0efd62..1e3f244 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -708,6 +708,14 @@ Table=1234 number of dynamically created network interfaces with the same network configuration and automatic address range assignment. + If an IPv4 link-local address (169.254.0.0/16) is specified, IPv4 Address Conflict + Detection (RFC 5227) is enabled for the + address. To assign an IPv4 link-local address without IPv4 Address Conflict Detection, please use + [Address] section to configure the address and disable DuplicateAddressDetection=. + [Address] +Address=169.254.10.1/24 +DuplicateAddressDetection=none + If an empty string is specified, then the all previous assignments in both [Network] and [Address] sections are cleared. @@ -845,7 +853,7 @@ Table=1234 IPv6Forwarding= - Configures IPv6 packet forwarding for the interface. Takes a boolean value. This controls the + Configures interface-specific host/router behaviour. Takes a boolean value. This controls the net.ipv6.conf.INTERFACE.forwarding sysctl option of the network interface. See IP Sysctl @@ -859,6 +867,14 @@ Table=1234 networkd.conf5. + Note, unlike IPv4Forwarding=, enabling per-interface + IPv6Forwarding= on two or more interfaces DOES NOT make IPv6 + packets forwarded within the interfaces. This setting just controls the per-interface sysctl value, + and the sysctl value is not directly correlated to whether packets are forwarded. To ensure IPv6 + packets forwarded, the global setting in + networkd.conf5 + needs to be enabled. + @@ -1191,6 +1207,18 @@ Table=1234 carrier. Defaults to false. If enabled, and the IgnoreCarrierLoss= setting is not explicitly set, then it is enabled as well. + With this enabled, to make the interface enter the configured state, + which is required to make systemd-networkd-wait-online work properly for the + interface, all dynamic address configuration mechanisms like DHCP= and + IPv6AcceptRA= (which is enabled by default in most cases) need to be disabled. + Also, DuplicateAddressDetection= (which is enabled by default for IPv4 + link-local addresses and all IPv6 addresses) needs to be disabled for all static address + configurations. Otherwise, without carrier, the interface will be stuck in the + configuring state, and systemd-networkd-wait-online for the + interface will timeout. Also, it is recommended to set + RequiredForOnline=no-carrier to make + systemd-networkd-wait-online work for the interface. + @@ -1366,10 +1394,10 @@ Table=1234 Detection. See RFC 5227. When ipv6, performs IPv6 Duplicate Address Detection. See RFC 4862. Defaults to - ipv4 for IPv4 link-local addresses, ipv6 for IPv6 - addresses, and none otherwise. + ipv4 for IPv4 link-local addresses (169.254.0.0/16), ipv6 + for IPv6 addresses, and none otherwise. - + @@ -1682,7 +1710,8 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix one of predefined names default, main, and local, and names defined in RouteTable= in networkd.conf5, - or a number between 1 and 4294967295. Defaults to main. + or a number between 1 and 4294967295. Defaults to main. + Ignored if L3MasterDevice= is true. @@ -2053,7 +2082,7 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix ip route show table num. If unset and Type= is local, broadcast, anycast, or nat, then local is used. - In other cases, defaults to main. Ignored if L3MasterDevice= is true. + In other cases, defaults to main. diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 6667ac5..397b3cd 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -622,6 +622,12 @@ sd_notify3). + Note that the start timeout is also applied to service reloads, regardless if implemented + through ExecReload= or via the reload logic enabled via Type=notify-reload. + If the reload does not complete within the configured time, the reload will be considered failed and + the service will continue running with the old configuration. This will not affect the running service, + but will be logged and will cause e.g. systemctl reload to fail. + diff --git a/meson.build b/meson.build index 0548e2e..737f9f0 100644 --- a/meson.build +++ b/meson.build @@ -399,6 +399,7 @@ possible_common_cc_flags = [ '-Wstrict-aliasing=2', '-Wstrict-prototypes', '-Wsuggest-attribute=noreturn', + '-Wunterminated-string-initialization', '-Wunused-function', '-Wwrite-strings', '-Wzero-length-bounds', @@ -411,7 +412,7 @@ possible_common_cc_flags = [ '-fno-common', '-fstack-protector', '-fstack-protector-strong', - '-fstrict-flex-arrays', + '-fstrict-flex-arrays=3', '--param=ssp-buffer-size=4', ] diff --git a/meson.version b/meson.version index 86facc5..e162ee4 100644 --- a/meson.version +++ b/meson.version @@ -1 +1 @@ -256.4 +256.5 diff --git a/mkosi.conf b/mkosi.conf index 527d08a..ea7126d 100644 --- a/mkosi.conf +++ b/mkosi.conf @@ -47,9 +47,6 @@ ExtraTrees= Initrds=%O/initrd -Environment= - SYSTEMD_REPART_OVERRIDE_FSTYPE_ROOT=%F - # Disable relabeling by default as it only matters for TEST-06-SELINUX, takes a non-trivial amount of time # and results in lots of errors when building images as a regular user. SELinuxRelabel=no @@ -105,6 +102,7 @@ Packages= kexec-tools kmod less + llvm lvm2 man mdadm diff --git a/mkosi.conf.d/10-centos/mkosi.conf b/mkosi.conf.d/10-centos/mkosi.conf index d97b081..dd462ef 100644 --- a/mkosi.conf.d/10-centos/mkosi.conf +++ b/mkosi.conf.d/10-centos/mkosi.conf @@ -8,8 +8,9 @@ Release=10 [Content] Environment= - # We'd prefer to use XFS here but it fails to mount on duplicate filesystem UUIDs which - # happens when running tests in parallel so we use ext4 instead. + # CentOS doesn't support btrfs so we have to use something else. We'd prefer to use XFS here but it + # fails to mount on duplicate filesystem UUIDs which happens when running tests in parallel so we use + # ext4 instead. SYSTEMD_REPART_OVERRIDE_FSTYPE_ROOT=ext4 # The kernel versions in CentOS Stream 9 doesn't support orphan_file, but later versions of # mkfs.ext4 enabled it by default, so we disable it explicitly. diff --git a/mkosi.conf.d/10-debian-ubuntu/mkosi.conf b/mkosi.conf.d/10-debian-ubuntu/mkosi.conf index 43e0781..d14746f 100644 --- a/mkosi.conf.d/10-debian-ubuntu/mkosi.conf +++ b/mkosi.conf.d/10-debian-ubuntu/mkosi.conf @@ -8,6 +8,10 @@ Distribution=|ubuntu PackageManagerTrees=mkosi-pinning.pref:/etc/apt/preferences.d/mkosi-pinning.pref [Content] +# Debian ships /usr/bin/login from shadow instead of util-linux which doesn't support credentials so we +# enable autologin the old-fashioned way. +Autologin=yes + VolatilePackages= libnss-myhostname libnss-mymachines diff --git a/mkosi.conf.d/10-opensuse/mkosi.conf b/mkosi.conf.d/10-opensuse/mkosi.conf index e741aa4..dc9bf30 100644 --- a/mkosi.conf.d/10-opensuse/mkosi.conf +++ b/mkosi.conf.d/10-opensuse/mkosi.conf @@ -43,6 +43,7 @@ Packages= glibc-locale-base gnutls grep + group(wheel) gzip iputils kernel-default diff --git a/mkosi.extra/root/.gdbinit b/mkosi.extra/root/.gdbinit new file mode 100644 index 0000000..e9c9ec4 --- /dev/null +++ b/mkosi.extra/root/.gdbinit @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +set debuginfod enabled off +set pagination off diff --git a/mkosi.extra/usr/lib/tmpfiles.d/locale.conf b/mkosi.extra/usr/lib/tmpfiles.d/locale.conf deleted file mode 100644 index e1a8e81..0000000 --- a/mkosi.extra/usr/lib/tmpfiles.d/locale.conf +++ /dev/null @@ -1 +0,0 @@ -L /etc/default/locale - - - - ../locale.conf diff --git a/mkosi.extra/usr/lib/tmpfiles.d/snapshot.conf b/mkosi.extra/usr/lib/tmpfiles.d/snapshot.conf new file mode 100644 index 0000000..fea660e --- /dev/null +++ b/mkosi.extra/usr/lib/tmpfiles.d/snapshot.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +L /run/nextroot - - - - /snapshot diff --git a/mkosi.images/build/mkosi.conf.d/debian-ubuntu/mkosi.build.chroot b/mkosi.images/build/mkosi.conf.d/debian-ubuntu/mkosi.build.chroot index 2d50afb..03b3b04 100755 --- a/mkosi.images/build/mkosi.conf.d/debian-ubuntu/mkosi.build.chroot +++ b/mkosi.images/build/mkosi.conf.d/debian-ubuntu/mkosi.build.chroot @@ -102,18 +102,22 @@ if ! build; then sed "s/Package: //" | xargs -d '\n' -I {} sh -c "[ -d debian/{} ] && (cd debian/{} && find . ! -type d ! -path "*dh-exec*" -printf '%P\n')" | # Remove compression suffix from compressed manpages as the manpages in debian/tmp will be uncompressed. - sed --regexp-extended 's/([0-9])\.gz$/\1/' | - sort --unique >/tmp/packaged-files + sed --regexp-extended 's/([0-9])\.gz$/\1/' >/tmp/packaged-files # We figure out the installed files by running find on debian/tmp/ which contains the files installed # by meson install. - (cd debian/tmp/ && find . ! -type d ! -path "*dh-exec*" -printf '%P\n') >/tmp/installed-files + (cd debian/tmp/ && find . ! -type d ! -path "*dh-exec*" -printf '%P\n' | sort --unique) >/tmp/installed-files if [[ -f debian/not-installed ]]; then - grep --invert-match "^#" debian/not-installed >>/tmp/installed-files + pushd debian/tmp + grep --invert-match "^#" ../not-installed | + xargs -I {} bash -O nullglob -c "printf '%s\n' {}" | + sed "/^$/d" | + xargs -I {} sh -c "test -f '{}' && echo '{}' || true" >>/tmp/packaged-files + popd fi - sort --unique --output /tmp/installed-files /tmp/installed-files + sort --unique --output /tmp/packaged-files /tmp/packaged-files # We get all the installed files that were not packaged by finding entries in the installed file that are # not in the packaged file. diff --git a/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf b/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf index 1d55a91..a941457 100644 --- a/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf +++ b/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf @@ -11,8 +11,13 @@ Environment= PKG_SUBDIR=opensuse Packages= - gcc-c++ + diffutils erofs-utils + gcc-c++ git-core + grep + gzip patterns-base-minimal_base rpm-build + sed + xz diff --git a/mkosi.images/exitrd/mkosi.conf.d/10-opensuse.conf b/mkosi.images/exitrd/mkosi.conf.d/10-opensuse.conf index 5fd6466..37f6220 100644 --- a/mkosi.images/exitrd/mkosi.conf.d/10-opensuse.conf +++ b/mkosi.images/exitrd/mkosi.conf.d/10-opensuse.conf @@ -5,7 +5,11 @@ Distribution=opensuse [Content] Packages= + diffutils + grep patterns-base-minimal_base + sed + xz VolatilePackages= libsystemd0 diff --git a/mkosi.images/minimal-base/mkosi.conf.d/10-opensuse.conf b/mkosi.images/minimal-base/mkosi.conf.d/10-opensuse.conf index 9bd40cf..3d891b8 100644 --- a/mkosi.images/minimal-base/mkosi.conf.d/10-opensuse.conf +++ b/mkosi.images/minimal-base/mkosi.conf.d/10-opensuse.conf @@ -5,10 +5,14 @@ Distribution=opensuse [Content] Packages= + diffutils + grep hostname iproute2 ncat patterns-base-minimal_base + sed + xz VolatilePackages= libsystemd0 diff --git a/mkosi.postinst.chroot b/mkosi.postinst.chroot index 7f2666e..1809a30 100755 --- a/mkosi.postinst.chroot +++ b/mkosi.postinst.chroot @@ -3,7 +3,20 @@ set -e set -o nounset -useradd --uid 4711 --create-home --user-group testuser +if [[ "$DISTRIBUTION" =~ ubuntu|debian ]]; then + SUDO_GROUP=sudo +else + SUDO_GROUP=wheel +fi + +useradd \ + --uid 4711 \ + --user-group \ + --create-home \ + --password "$(openssl passwd -1 testuser)" \ + --groups "$SUDO_GROUP",systemd-journal \ + --shell /bin/bash \ + testuser if command -v authselect >/dev/null; then # authselect 1.5.0 renamed the minimal profile to the local profile without keeping backwards compat so diff --git a/mkosi.repart/10-root.conf b/mkosi.repart/10-root.conf index c774086..3c25dbf 100644 --- a/mkosi.repart/10-root.conf +++ b/mkosi.repart/10-root.conf @@ -2,6 +2,7 @@ [Partition] Type=root +Format=btrfs CopyFiles=/ SizeMinBytes=8G SizeMaxBytes=8G diff --git a/network/80-wifi-station.network.example b/network/80-wifi-station.network.example index 160b4eb..600ce4c 100644 --- a/network/80-wifi-station.network.example +++ b/network/80-wifi-station.network.example @@ -12,6 +12,7 @@ [Match] Type=wlan WLANInterfaceType=station +SSID=* [Network] DHCP=yes diff --git a/rules.d/50-udev-default.rules.in b/rules.d/50-udev-default.rules.in index 9b00c70..6f80fee 100644 --- a/rules.d/50-udev-default.rules.in +++ b/rules.d/50-udev-default.rules.in @@ -30,6 +30,9 @@ SUBSYSTEM=="pci|usb|platform", IMPORT{builtin}="path_id" SUBSYSTEM=="net", IMPORT{builtin}="net_driver" +SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK+="ptp_kvm" +SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK+="ptp_hyperv" + ACTION!="add", GOTO="default_end" SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666" @@ -116,7 +119,4 @@ KERNEL=="vhost-net", GROUP="kvm", MODE="{{DEV_KVM_MODE}}", OPTIONS+="static_node KERNEL=="udmabuf", GROUP="kvm" -SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK+="ptp_kvm" -SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK+="ptp_hyperv" - LABEL="default_end" diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 553ee60..b0fe0ec 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -149,7 +149,9 @@ int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags) { if (pid == 0) return -EREMOTE; - if (FLAGS_SET(flags, CGROUP_NO_PIDFD)) { + /* We might read kernel thread pids from cgroup.procs for which we cannot create a pidfd so + * catch those and don't try to create a pidfd for them. */ + if (FLAGS_SET(flags, CGROUP_NO_PIDFD) || pid_is_kernel_thread(pid) > 0) { *ret = PIDREF_MAKE_FROM_PID(pid); return 1; } @@ -369,6 +371,12 @@ static int cg_kill_items( if (set_get(s, PID_TO_PTR(pidref.pid)) == PID_TO_PTR(pidref.pid)) continue; + /* Ignore kernel threads to mimick the behavior of cgroup.kill. */ + if (pidref_is_kernel_thread(&pidref) > 0) { + log_debug("Ignoring kernel thread with pid " PID_FMT " in cgroup '%s'", pidref.pid, path); + continue; + } + if (log_kill) ret_log_kill = log_kill(&pidref, sig, userdata); diff --git a/src/basic/confidential-virt.c b/src/basic/confidential-virt.c index b6521cf..c246636 100644 --- a/src/basic/confidential-virt.c +++ b/src/basic/confidential-virt.c @@ -11,6 +11,7 @@ #include "confidential-virt-fundamental.h" #include "confidential-virt.h" #include "fd-util.h" +#include "fileio.h" #include "missing_threads.h" #include "string-table.h" #include "utf8.h" @@ -76,7 +77,7 @@ static uint64_t msr(uint64_t index) { return ret; } -static bool detect_hyperv_sev(void) { +static bool detect_hyperv_cvm(uint32_t isoltype) { uint32_t eax, ebx, ecx, edx, feat; char sig[13] = {}; @@ -100,7 +101,7 @@ static bool detect_hyperv_sev(void) { ebx = ecx = edx = 0; cpuid(&eax, &ebx, &ecx, &edx); - if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP) + if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == isoltype) return true; } @@ -133,7 +134,7 @@ static ConfidentialVirtualization detect_sev(void) { if (!(eax & EAX_SEV)) { log_debug("No sev in CPUID, trying hyperv CPUID"); - if (detect_hyperv_sev()) + if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_SNP)) return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP; log_debug("No hyperv CPUID"); @@ -171,6 +172,11 @@ static ConfidentialVirtualization detect_tdx(void) { if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0) return CONFIDENTIAL_VIRTUALIZATION_TDX; + log_debug("No tdx in CPUID, trying hyperv CPUID"); + + if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_TDX)) + return CONFIDENTIAL_VIRTUALIZATION_TDX; + return CONFIDENTIAL_VIRTUALIZATION_NONE; } @@ -189,40 +195,62 @@ static bool detect_hypervisor(void) { return is_hv; } -ConfidentialVirtualization detect_confidential_virtualization(void) { - static thread_local ConfidentialVirtualization cached_found = _CONFIDENTIAL_VIRTUALIZATION_INVALID; +static ConfidentialVirtualization detect_confidential_virtualization_impl(void) { char sig[13] = {}; - ConfidentialVirtualization cv = CONFIDENTIAL_VIRTUALIZATION_NONE; - - if (cached_found >= 0) - return cached_found; /* Skip everything on bare metal */ if (detect_hypervisor()) { cpuid_leaf(0, sig, true); if (memcmp(sig, CPUID_SIG_AMD, sizeof(sig)) == 0) - cv = detect_sev(); + return detect_sev(); else if (memcmp(sig, CPUID_SIG_INTEL, sizeof(sig)) == 0) - cv = detect_tdx(); + return detect_tdx(); } - cached_found = cv; - return cv; + return CONFIDENTIAL_VIRTUALIZATION_NONE; } +#elif defined(__s390x__) +static ConfidentialVirtualization detect_confidential_virtualization_impl(void) { + _cleanup_free_ char *s = NULL; + size_t readsize; + int r; + + r = read_full_virtual_file("/sys/firmware/uv/prot_virt_guest", &s, &readsize); + if (r < 0) { + log_debug_errno(r, "Unable to read /sys/firmware/uv/prot_virt_guest: %m"); + return CONFIDENTIAL_VIRTUALIZATION_NONE; + } + + if (readsize >= 1 && s[0] == '1') + return CONFIDENTIAL_VIRTUALIZATION_PROTVIRT; + + return CONFIDENTIAL_VIRTUALIZATION_NONE; +} + #else /* ! x86_64 */ -ConfidentialVirtualization detect_confidential_virtualization(void) { +static ConfidentialVirtualization detect_confidential_virtualization_impl(void) { log_debug("No confidential virtualization detection on this architecture"); return CONFIDENTIAL_VIRTUALIZATION_NONE; } #endif /* ! x86_64 */ +ConfidentialVirtualization detect_confidential_virtualization(void) { + static thread_local ConfidentialVirtualization cached_found = _CONFIDENTIAL_VIRTUALIZATION_INVALID; + + if (cached_found == _CONFIDENTIAL_VIRTUALIZATION_INVALID) + cached_found = detect_confidential_virtualization_impl(); + + return cached_found; +} + static const char *const confidential_virtualization_table[_CONFIDENTIAL_VIRTUALIZATION_MAX] = { - [CONFIDENTIAL_VIRTUALIZATION_NONE] = "none", - [CONFIDENTIAL_VIRTUALIZATION_SEV] = "sev", - [CONFIDENTIAL_VIRTUALIZATION_SEV_ES] = "sev-es", - [CONFIDENTIAL_VIRTUALIZATION_SEV_SNP] = "sev-snp", - [CONFIDENTIAL_VIRTUALIZATION_TDX] = "tdx", + [CONFIDENTIAL_VIRTUALIZATION_NONE] = "none", + [CONFIDENTIAL_VIRTUALIZATION_SEV] = "sev", + [CONFIDENTIAL_VIRTUALIZATION_SEV_ES] = "sev-es", + [CONFIDENTIAL_VIRTUALIZATION_SEV_SNP] = "sev-snp", + [CONFIDENTIAL_VIRTUALIZATION_TDX] = "tdx", + [CONFIDENTIAL_VIRTUALIZATION_PROTVIRT] = "protvirt", }; DEFINE_STRING_TABLE_LOOKUP(confidential_virtualization, ConfidentialVirtualization); diff --git a/src/basic/confidential-virt.h b/src/basic/confidential-virt.h index c02f3b2..f92e3e8 100644 --- a/src/basic/confidential-virt.h +++ b/src/basic/confidential-virt.h @@ -13,6 +13,7 @@ typedef enum ConfidentialVirtualization { CONFIDENTIAL_VIRTUALIZATION_SEV_ES, CONFIDENTIAL_VIRTUALIZATION_SEV_SNP, CONFIDENTIAL_VIRTUALIZATION_TDX, + CONFIDENTIAL_VIRTUALIZATION_PROTVIRT, _CONFIDENTIAL_VIRTUALIZATION_MAX, _CONFIDENTIAL_VIRTUALIZATION_INVALID = -EINVAL, diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 4cb67d9..e02aa9e 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -36,7 +36,7 @@ int undecchar(char c) { } char hexchar(int x) { - static const char table[16] = "0123456789abcdef"; + static const char table[] = "0123456789abcdef"; return table[x & 15]; } @@ -168,8 +168,8 @@ int unhexmem_full( * useful when representing NSEC3 hashes, as one can then verify the * order of hashes directly from their representation. */ char base32hexchar(int x) { - static const char table[32] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUV"; + static const char table[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUV"; return table[x & 31]; } @@ -519,9 +519,9 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l /* https://tools.ietf.org/html/rfc4648#section-4 */ char base64char(int x) { - static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; + static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; return table[x & 63]; } @@ -529,9 +529,9 @@ char base64char(int x) { * since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames). * See section #5 of RFC 4648. */ char urlsafe_base64char(int x) { - static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789-_"; + static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_"; return table[x & 63]; } diff --git a/src/basic/log.c b/src/basic/log.c index 13ad19a..2675d59 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -395,9 +395,10 @@ void log_forget_fds(void) { console_fd_is_tty = -1; } -void log_set_max_level(int level) { +int log_set_max_level(int level) { assert(level == LOG_NULL || LOG_PRI(level) == level); + int old = log_max_level; log_max_level = level; /* Also propagate max log level to libc's syslog(), just in case some other component loaded into our @@ -410,6 +411,8 @@ void log_set_max_level(int level) { /* Ensure that our own LOG_NULL define maps sanely to the log mask */ assert_cc(LOG_UPTO(LOG_NULL) == 0); + + return old; } void log_set_facility(int facility) { @@ -743,7 +746,7 @@ static int write_to_journal( if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_JOURNAL]) return 0; - iovec_len = MIN(6 + _log_context_num_fields * 2, IOVEC_MAX); + iovec_len = MIN(6 + _log_context_num_fields * 3, IOVEC_MAX); iovec = newa(struct iovec, iovec_len); log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra); @@ -1095,7 +1098,7 @@ int log_struct_internal( int r; bool fallback = false; - iovec_len = MIN(17 + _log_context_num_fields * 2, IOVEC_MAX); + iovec_len = MIN(17 + _log_context_num_fields * 3, IOVEC_MAX); iovec = newa(struct iovec, iovec_len); /* If the journal is available do structured logging. @@ -1192,7 +1195,7 @@ int log_struct_iovec_internal( struct iovec *iovec; size_t n = 0, iovec_len; - iovec_len = MIN(1 + n_input_iovec * 2 + _log_context_num_fields * 2, IOVEC_MAX); + iovec_len = MIN(1 + n_input_iovec * 2 + _log_context_num_fields * 3, IOVEC_MAX); iovec = newa(struct iovec, iovec_len); log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL); diff --git a/src/basic/log.h b/src/basic/log.h index 726f035..4543556 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -35,9 +35,8 @@ typedef enum LogTarget{ * used as a regular log level. */ #define LOG_NULL (LOG_EMERG - 1) -/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */ -#define SYNTHETIC_ERRNO(num) (1 << 30 | (num)) -#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) +#define SYNTHETIC_ERRNO(num) (abs(num) | (1 << 30)) +#define IS_SYNTHETIC_ERRNO(val) (((val) >> 30) == 1) #define ERRNO_VALUE(val) (abs(val) & ~(1 << 30)) /* The callback function to be invoked when syntax warnings are seen @@ -57,7 +56,7 @@ int log_set_target_from_string(const char *e); LogTarget log_get_target(void) _pure_; void log_settle_target(void); -void log_set_max_level(int level); +int log_set_max_level(int level); int log_set_max_level_from_string(const char *e); int log_get_max_level(void) _pure_; int log_max_levels_to_string(int level, char **ret); @@ -492,6 +491,15 @@ size_t log_context_num_contexts(void); /* Returns the number of fields in all attached log contexts. */ size_t log_context_num_fields(void); +static inline void _reset_log_level(int *saved_log_level) { + assert(saved_log_level); + + log_set_max_level(*saved_log_level); +} + +#define LOG_CONTEXT_SET_LOG_LEVEL(level) \ + _cleanup_(_reset_log_level) _unused_ int _saved_log_level_ = log_set_max_level(level); + #define LOG_CONTEXT_PUSH(...) \ LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__)) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index e1f0817..93e1236 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -2405,7 +2405,7 @@ static EFI_STATUS image_start( if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) { uint32_t compat_address; - err = pe_kernel_info(loaded_image->ImageBase, &compat_address); + err = pe_kernel_info(loaded_image->ImageBase, &compat_address, /* ret_size_in_memory= */ NULL); if (err != EFI_SUCCESS) { if (err != EFI_UNSUPPORTED) return log_error_status(err, "Error finding kernel compat entry address: %m"); diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c index 3bdb802..db00890 100644 --- a/src/boot/efi/efi-string.c +++ b/src/boot/efi/efi-string.c @@ -481,7 +481,7 @@ char *line_get_key_value(char *s, const char *sep, size_t *pos, char **ret_key, } char16_t *hexdump(const void *data, size_t size) { - static const char hex[16] = "0123456789abcdef"; + static const char hex[] = "0123456789abcdef"; const uint8_t *d = data; assert(data || size == 0); diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 65bc176..c8e5951 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -98,6 +98,7 @@ EFI_STATUS linux_exec( const void *initrd_buffer, size_t initrd_length) { + size_t kernel_size_in_memory = 0; uint32_t compat_address; EFI_STATUS err; @@ -105,7 +106,7 @@ EFI_STATUS linux_exec( assert(linux_buffer && linux_length > 0); assert(initrd_buffer || initrd_length == 0); - err = pe_kernel_info(linux_buffer, &compat_address); + err = pe_kernel_info(linux_buffer, &compat_address, &kernel_size_in_memory); #if defined(__i386__) || defined(__x86_64__) if (err == EFI_UNSUPPORTED) /* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover @@ -116,7 +117,8 @@ EFI_STATUS linux_exec( linux_buffer, linux_length, initrd_buffer, - initrd_length); + initrd_length, + kernel_size_in_memory); #endif if (err != EFI_SUCCESS) return log_error_status(err, "Bad kernel image: %m"); diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h index 46b5f4f..0d74c6a 100644 --- a/src/boot/efi/linux.h +++ b/src/boot/efi/linux.h @@ -16,4 +16,5 @@ EFI_STATUS linux_exec_efi_handover( const void *linux_buffer, size_t linux_length, const void *initrd_buffer, - size_t initrd_length); + size_t initrd_length, + size_t kernel_size_in_memory); diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c index 757902d..58b1b3c 100644 --- a/src/boot/efi/linux_x86.c +++ b/src/boot/efi/linux_x86.c @@ -7,12 +7,13 @@ * this x86 specific linux_exec function passes the initrd by setting the * corresponding fields in the setup_header struct. * - * see https://docs.kernel.org/x86/boot.html + * see https://docs.kernel.org/arch/x86/boot.html */ #include "initrd.h" #include "linux.h" #include "macro-fundamental.h" +#include "memory-util-fundamental.h" #include "util.h" #define KERNEL_SECTOR_SIZE 512u @@ -126,7 +127,8 @@ EFI_STATUS linux_exec_efi_handover( const void *linux_buffer, size_t linux_length, const void *initrd_buffer, - size_t initrd_length) { + size_t initrd_length, + size_t kernel_size_in_memory) { assert(parent); assert(linux_buffer); @@ -153,13 +155,22 @@ EFI_STATUS linux_exec_efi_handover( FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G); /* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this - * just fine, but older kernels will fail even if they otherwise have above 4G boot support. */ + * just fine, but older kernels will fail even if they otherwise have above 4G boot support. + * A PE image's memory footprint can be larger than its file size, due to unallocated virtual + * memory sections. While normally all PE headers should be taken into account, this case only + * involves x86 Linux bzImage kernel images, for which unallocated areas are only part of the last + * header, so parsing SizeOfImage and zeroeing the buffer past the image size is enough. */ _cleanup_pages_ Pages linux_relocated = {}; - if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX) { + if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX || kernel_size_in_memory > linux_length) { linux_relocated = xmalloc_pages( - AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(linux_length), UINT32_MAX); + AllocateMaxAddress, + EfiLoaderCode, + EFI_SIZE_TO_PAGES(kernel_size_in_memory > linux_length ? kernel_size_in_memory : linux_length), + UINT32_MAX); linux_buffer = memcpy( PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), linux_buffer, linux_length); + if (kernel_size_in_memory > linux_length) + memzero((uint8_t *) linux_buffer + linux_length, kernel_size_in_memory - linux_length); } _cleanup_pages_ Pages initrd_relocated = {}; diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index 829266b..fdca0c9 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -209,7 +209,7 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const return 0; } -EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) { +EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t *ret_size_in_memory) { assert(base); assert(ret_compat_address); @@ -221,6 +221,11 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) { if (!verify_pe(pe, /* allow_compatibility= */ true)) return EFI_LOAD_ERROR; + /* When allocating we need to also consider the virtual/uninitialized data sections, so parse it out + * of the SizeOfImage field in the PE header and return it */ + if (ret_size_in_memory) + *ret_size_in_memory = pe->OptionalHeader.SizeOfImage; + /* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */ if (pe->OptionalHeader.MajorImageVersion < 1) return EFI_UNSUPPORTED; diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h index 7e2258f..860cfe5 100644 --- a/src/boot/efi/pe.h +++ b/src/boot/efi/pe.h @@ -16,4 +16,4 @@ EFI_STATUS pe_file_locate_sections( size_t *offsets, size_t *sizes); -EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address); +EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t *ret_size_in_memory); diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c index bfc7acc..ed654f6 100644 --- a/src/boot/efi/vmm.c +++ b/src/boot/efi/vmm.c @@ -346,7 +346,7 @@ static uint64_t msr(uint32_t index) { return val; } -static bool detect_hyperv_sev(void) { +static bool detect_hyperv_cvm(uint32_t isoltype) { uint32_t eax, ebx, ecx, edx, feat; char sig[13] = {}; @@ -363,7 +363,7 @@ static bool detect_hyperv_sev(void) { if (ebx & CPUID_HYPERV_ISOLATION && !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) { __cpuid(CPUID_HYPERV_ISOLATION_CONFIG, eax, ebx, ecx, edx); - if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP) + if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == isoltype) return true; } @@ -388,7 +388,7 @@ static bool detect_sev(void) { * specific CPUID checks. */ if (!(eax & EAX_SEV)) - return detect_hyperv_sev(); + return detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_SNP); msrval = msr(MSR_AMD64_SEV); @@ -412,6 +412,9 @@ static bool detect_tdx(void) { if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0) return true; + if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_TDX)) + return true; + return false; } #endif /* ! __i386__ && ! __x86_64__ */ diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index f4cff57..6157ac4 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -353,6 +353,17 @@ static int load_credential_glob( _cleanup_(erase_and_freep) char *data = NULL; size_t size; + r = path_extract_filename(*p, &fn); + if (r < 0) + return log_debug_errno(r, "Failed to extract filename from '%s': %m", *p); + + if (faccessat(write_dfd, fn, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) { + log_debug("Skipping credential with duplicated ID %s at %s", fn, *p); + continue; + } + if (errno != ENOENT) + return log_debug_errno(errno, "Failed to test if credential %s exists: %m", fn); + /* path is absolute, hence pass AT_FDCWD as nop dir fd here */ r = read_full_file_full( AT_FDCWD, @@ -365,10 +376,6 @@ static int load_credential_glob( if (r < 0) return log_debug_errno(r, "Failed to read credential '%s': %m", *p); - r = path_extract_filename(*p, &fn); - if (r < 0) - return log_debug_errno(r, "Failed to extract filename from '%s': %m", *p); - r = maybe_decrypt_and_write_credential( write_dfd, fn, @@ -378,8 +385,6 @@ static int load_credential_glob( ownership_ok, data, size, left); - if (r == -EEXIST) - continue; if (r < 0) return r; } @@ -717,8 +722,10 @@ static int acquire_credentials( * EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda * slow and involved, hence it's nice to be able to skip that if the credential already * exists anyway. */ - if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) { + log_debug("Skipping credential with duplicated ID %s", sc->id); continue; + } if (errno != ENOENT) return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id); diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 09f98d3..5850a59 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -4402,14 +4402,6 @@ int exec_invoke( } } - if (context->nice_set) { - r = setpriority_closest(context->nice); - if (r < 0) { - *exit_status = EXIT_NICE; - return log_exec_error_errno(context, params, r, "Failed to set up process scheduling priority (nice level): %m"); - } - } - if (context->cpu_sched_set) { struct sched_attr attr = { .size = sizeof(attr), @@ -4425,6 +4417,14 @@ int exec_invoke( } } + if (context->nice_set) { + r = setpriority_closest(context->nice); + if (r < 0) { + *exit_status = EXIT_NICE; + return log_exec_error_errno(context, params, r, "Failed to set up process scheduling priority (nice level): %m"); + } + } + if (context->cpu_affinity_from_numa || context->cpu_set.set) { _cleanup_(cpu_set_reset) CPUSet converted_cpu_set = {}; const CPUSet *cpu_set; diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c index ecd1e70..41b31e9 100644 --- a/src/core/execute-serialize.c +++ b/src/core/execute-serialize.c @@ -434,11 +434,11 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) { if (r < 0) return r; - r = serialize_strv(f, "exec-cgroup-context-ip-ingress-filter-path=", c->ip_filters_ingress); + r = serialize_strv(f, "exec-cgroup-context-ip-ingress-filter-path", c->ip_filters_ingress); if (r < 0) return r; - r = serialize_strv(f, "exec-cgroup-context-ip-egress-filter-path=", c->ip_filters_egress); + r = serialize_strv(f, "exec-cgroup-context-ip-egress-filter-path", c->ip_filters_egress); if (r < 0) return r; @@ -1759,15 +1759,23 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) { if (r < 0) return r; - r = serialize_item(f, "exec-context-working-directory", c->working_directory); + r = serialize_item_escaped(f, "exec-context-working-directory", c->working_directory); if (r < 0) return r; - r = serialize_item(f, "exec-context-root-directory", c->root_directory); + r = serialize_bool_elide(f, "exec-context-working-directory-missing-ok", c->working_directory_missing_ok); if (r < 0) return r; - r = serialize_item(f, "exec-context-root-image", c->root_image); + r = serialize_bool_elide(f, "exec-context-working-directory-home", c->working_directory_home); + if (r < 0) + return r; + + r = serialize_item_escaped(f, "exec-context-root-directory", c->root_directory); + if (r < 0) + return r; + + r = serialize_item_escaped(f, "exec-context-root-image", c->root_image); if (r < 0) return r; @@ -1982,14 +1990,6 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) { return r; } - r = serialize_bool_elide(f, "exec-context-working-directory-missing-ok", c->working_directory_missing_ok); - if (r < 0) - return r; - - r = serialize_bool_elide(f, "exec-context-working-directory-home", c->working_directory_home); - if (r < 0) - return r; - if (c->oom_score_adjust_set) { r = serialize_item_format(f, "exec-context-oom-score-adjust", "%i", c->oom_score_adjust); if (r < 0) @@ -2627,17 +2627,29 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) { if (r < 0) return r; } else if ((val = startswith(l, "exec-context-working-directory="))) { - r = free_and_strdup(&c->working_directory, val); - if (r < 0) - return r; + ssize_t k; + char *p; + + k = cunescape(val, 0, &p); + if (k < 0) + return k; + free_and_replace(c->working_directory, p); } else if ((val = startswith(l, "exec-context-root-directory="))) { - r = free_and_strdup(&c->root_directory, val); - if (r < 0) - return r; + ssize_t k; + char *p; + + k = cunescape(val, 0, &p); + if (k < 0) + return k; + free_and_replace(c->root_directory, p); } else if ((val = startswith(l, "exec-context-root-image="))) { - r = free_and_strdup(&c->root_image, val); - if (r < 0) - return r; + ssize_t k; + char *p; + + k = cunescape(val, 0, &p); + if (k < 0) + return k; + free_and_replace(c->root_image, p); } else if ((val = startswith(l, "exec-context-root-image-options="))) { for (;;) { _cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL; diff --git a/src/core/execute.c b/src/core/execute.c index 513e95e..f74665f 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -379,6 +379,7 @@ int exec_spawn( assert(ret); LOG_CONTEXT_PUSH_UNIT(unit); + LOG_CONTEXT_SET_LOG_LEVEL(context->log_level_max >= 0 ? context->log_level_max : log_get_max_level()); r = exec_context_load_environment(unit, context, ¶ms->files_env); if (r < 0) diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index fd45744..dc9c44e 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -102,7 +102,7 @@ int unit_load_dropin(Unit *u) { return r; /* Load .conf dropins */ - r = unit_find_dropin_paths(u, &l); + r = unit_find_dropin_paths(u, /* use_unit_path_cache = */ true, &l); if (r <= 0) return 0; diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index f0b87d3..141bc7d 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -6,12 +6,12 @@ /* Read service data supplementary drop-in directories */ -static inline int unit_find_dropin_paths(Unit *u, char ***paths) { +static inline int unit_find_dropin_paths(Unit *u, bool use_unit_path_cache, char ***paths) { assert(u); return unit_file_find_dropin_paths(NULL, u->manager->lookup_paths.search_path, - u->manager->unit_path_cache, + use_unit_path_cache ? u->manager->unit_path_cache : NULL, ".d", ".conf", u->id, u->aliases, paths); diff --git a/src/core/socket.c b/src/core/socket.c index 41147d4..0694fe7 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2481,7 +2481,7 @@ static int socket_start(Unit *u) { /* If the service is already active we cannot start the * socket */ if (!IN_SET(service->state, - SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART, + SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_DEAD_RESOURCES_PINNED, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED)) return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), "Socket service %s already active, refusing.", UNIT(service)->id); @@ -3369,7 +3369,7 @@ static void socket_trigger_notify(Unit *u, Unit *other) { return; if (IN_SET(SERVICE(other)->state, - SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART, + SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_DEAD_RESOURCES_PINNED, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED)) socket_enter_listening(s); diff --git a/src/core/unit.c b/src/core/unit.c index 01c9983..4ca7dc4 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3831,7 +3831,7 @@ bool unit_need_daemon_reload(Unit *u) { if (u->load_state == UNIT_LOADED) { _cleanup_strv_free_ char **dropins = NULL; - (void) unit_find_dropin_paths(u, &dropins); + (void) unit_find_dropin_paths(u, /* use_unit_path_cache = */ false, &dropins); if (!strv_equal(u->dropin_paths, dropins)) return true; diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 6afabef..0dbdfc6 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -908,8 +908,7 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch _cleanup_fclose_ FILE *original = NULL, *passwd = NULL; _cleanup_(unlink_and_freep) char *passwd_tmp = NULL; int r; - - assert(password); + bool found = false; r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp); if (r < 0) @@ -929,9 +928,11 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch while ((r = fgetpwent_sane(original, &i)) > 0) { if (streq(i->pw_name, "root")) { - i->pw_passwd = (char *) password; + if (password) + i->pw_passwd = (char *) password; if (shell) i->pw_shell = (char *) shell; + found = true; } r = putpwent_sane(i, passwd); @@ -942,9 +943,15 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch return r; } else { + r = fchmod(fileno(passwd), 0644); + if (r < 0) + return -errno; + } + + if (!found) { struct passwd root = { .pw_name = (char *) "root", - .pw_passwd = (char *) password, + .pw_passwd = (char *) (password ?: PASSWORD_SEE_SHADOW), .pw_uid = 0, .pw_gid = 0, .pw_gecos = (char *) "Super User", @@ -955,10 +962,6 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch if (errno != ENOENT) return -errno; - r = fchmod(fileno(passwd), 0644); - if (r < 0) - return -errno; - r = putpwent_sane(&root, passwd); if (r < 0) return r; @@ -979,8 +982,7 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { _cleanup_fclose_ FILE *original = NULL, *shadow = NULL; _cleanup_(unlink_and_freep) char *shadow_tmp = NULL; int r; - - assert(hashed_password); + bool found = false; r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp); if (r < 0) @@ -1000,8 +1002,11 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { while ((r = fgetspent_sane(original, &i)) > 0) { if (streq(i->sp_namp, "root")) { - i->sp_pwdp = (char *) hashed_password; - i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + if (hashed_password) { + i->sp_pwdp = (char *) hashed_password; + i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + } + found = true; } r = putspent_sane(i, shadow); @@ -1012,9 +1017,15 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { return r; } else { + r = fchmod(fileno(shadow), 0000); + if (r < 0) + return -errno; + } + + if (!found) { struct spwd root = { .sp_namp = (char*) "root", - .sp_pwdp = (char *) hashed_password, + .sp_pwdp = (char *) (hashed_password ?: PASSWORD_LOCKED_AND_INVALID), .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY), .sp_min = -1, .sp_max = -1, @@ -1027,10 +1038,6 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) { if (errno != ENOENT) return -errno; - r = fchmod(fileno(shadow), 0000); - if (r < 0) - return -errno; - r = putspent_sane(&root, shadow); if (r < 0) return r; @@ -1081,13 +1088,6 @@ static int process_root_account(int rfd) { return 0; } - /* Don't create/modify passwd and shadow if not asked */ - if (!(arg_root_password || arg_prompt_root_password || arg_copy_root_password || arg_delete_root_password || - arg_root_shell || arg_prompt_root_shell || arg_copy_root_shell)) { - log_debug("Initialization of root account was not requested, skipping."); - return 0; - } - r = make_lock_file_at(pfd, ETC_PASSWD_LOCK_FILENAME, LOCK_EX, &lock); if (r < 0) return log_error_errno(r, "Failed to take a lock on /etc/passwd: %m"); @@ -1142,10 +1142,22 @@ static int process_root_account(int rfd) { password = PASSWORD_SEE_SHADOW; hashed_password = _hashed_password; - } else if (arg_delete_root_password) - password = hashed_password = PASSWORD_NONE; - else - password = hashed_password = PASSWORD_LOCKED_AND_INVALID; + } else if (arg_delete_root_password) { + password = PASSWORD_SEE_SHADOW; + hashed_password = PASSWORD_NONE; + } else if (!arg_root_password && arg_prompt_root_password) { + /* If the user was prompted, but no password was supplied, lock the account. */ + password = PASSWORD_SEE_SHADOW; + hashed_password = PASSWORD_LOCKED_AND_INVALID; + } else + /* Leave the password as is. */ + password = hashed_password = NULL; + + /* Don't create/modify passwd and shadow if there's nothing to do. */ + if (!(password || hashed_password || arg_root_shell)) { + log_debug("Initialization of root account was not requested, skipping."); + return 0; + } r = write_root_passwd(rfd, pfd, password, arg_root_shell); if (r < 0) diff --git a/src/fundamental/confidential-virt-fundamental.h b/src/fundamental/confidential-virt-fundamental.h index 986923e..618b580 100644 --- a/src/fundamental/confidential-virt-fundamental.h +++ b/src/fundamental/confidential-virt-fundamental.h @@ -65,6 +65,7 @@ #define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf) #define CPUID_HYPERV_ISOLATION_TYPE_SNP 2 +#define CPUID_HYPERV_ISOLATION_TYPE_TDX 3 #define EAX_SEV (UINT32_C(1) << 1) #define MSR_SEV (UINT64_C(1) << 0) diff --git a/src/import/import-raw.c b/src/import/import-raw.c index ee9b297..78775b9 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -409,6 +409,11 @@ static int raw_import_process(RawImport *i) { goto finish; } + if ((size_t) l > sizeof(i->buffer) - i->buffer_size) { + r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Read input file exceeded maximum size."); + goto finish; + } + i->buffer_size += l; if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 39df11b..976c918 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -276,6 +276,11 @@ static int tar_import_process(TarImport *i) { goto finish; } + if ((size_t) l > sizeof(i->buffer) - i->buffer_size) { + r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Read input file exceeded maximum size."); + goto finish; + } + i->buffer_size += l; if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 717c8e4..07748f0 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1349,9 +1349,10 @@ int server_flush_to_var(Server *s, bool require_flag_file) { start = now(CLOCK_MONOTONIC); r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE); - if (r < 0) - return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, - "Failed to read runtime journal: %m"); + if (r < 0) { + log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to read runtime journal: %m"); + goto finish; + } sd_journal_set_data_threshold(j, 0); diff --git a/src/journal/journald.c b/src/journal/journald.c index 2f013c2..87ed22e 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -94,8 +94,7 @@ static int run(int argc, char *argv[]) { /* The retention time is reached, so let's vacuum! */ if (t <= 0) { - log_info("Retention time reached, rotating."); - server_rotate(s); + log_info("Retention time reached, vacuuming."); server_vacuum(s, /* verbose = */ false); continue; } diff --git a/src/journal/journald.conf b/src/journal/journald.conf index 13cdd63..9a12ca7 100644 --- a/src/journal/journald.conf +++ b/src/journal/journald.conf @@ -32,7 +32,7 @@ #RuntimeKeepFree= #RuntimeMaxFileSize= #RuntimeMaxFiles=100 -#MaxRetentionSec= +#MaxRetentionSec=0 #MaxFileSec=1month #ForwardToSyslog=no #ForwardToKMsg=no diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install index 88f858f..08247c7 100755 --- a/src/kernel-install/50-depmod.install +++ b/src/kernel-install/50-depmod.install @@ -44,6 +44,7 @@ case "$COMMAND" in "/lib/modules/$KERNEL_VERSION/modules.dep.bin" \ "/lib/modules/$KERNEL_VERSION/modules.devname" \ "/lib/modules/$KERNEL_VERSION/modules.softdep" \ + "/lib/modules/$KERNEL_VERSION/modules.weakdep" \ "/lib/modules/$KERNEL_VERSION/modules.symbols" \ "/lib/modules/$KERNEL_VERSION/modules.symbols.bin" ;; diff --git a/src/kernel-install/90-loaderentry.install.in b/src/kernel-install/90-loaderentry.install.in index 4ef6aca..c9ba111 100755 --- a/src/kernel-install/90-loaderentry.install.in +++ b/src/kernel-install/90-loaderentry.install.in @@ -79,8 +79,10 @@ elif [ -f /etc/kernel/cmdline ]; then BOOT_OPTIONS="$(tr -s "$IFS" ' ' "$LOADER_ENTRY" || { echo "Error: could not create loader entry '$LOADER_ENTRY'." >&2 diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index a1305ef..73a95e7 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -3866,7 +3866,8 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, i if (_unlikely_(n != sizeof(si))) return -EIO; - assert(SIGNAL_VALID(si.ssi_signo)); + if (_unlikely_(!SIGNAL_VALID(si.ssi_signo))) + return -EIO; if (e->signal_sources) s = e->signal_sources[si.ssi_signo]; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 0521863..ceff268 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "audit-util.h" +#include "bitfield.h" #include "bootspec.h" #include "bus-common-errors.h" #include "bus-error.h" @@ -3550,23 +3551,27 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running"); - r = bus_verify_polkit_async( - message, - w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") : - w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") : - w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" : - w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" : - w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" : - w == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" : - w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : - "org.freedesktop.login1.inhibit-handle-lid-switch", - /* details= */ NULL, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + BIT_FOREACH(i, w) { + const InhibitWhat v = 1U << i; + + r = bus_verify_polkit_async( + message, + v == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") : + v == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") : + v == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" : + v == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" : + v == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" : + v == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" : + v == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : + "org.freedesktop.login1.inhibit-handle-lid-switch", + /* details= */ NULL, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PIDFD, &creds); if (r < 0) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 6b0f099..9ce7536 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1714,6 +1714,13 @@ static int link_carrier_gained(Link *link) { if (r < 0) log_link_warning_errno(link, r, "Failed to disable carrier lost timer, ignoring: %m"); + /* Process BindCarrier= setting specified by other interfaces. This is independent of the .network + * file assigned to this interface, but depends on .network files assigned to other interfaces. + * Hence, this can and should be called earlier. */ + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + /* If a wireless interface was connected to an access point, and the SSID is changed (that is, * both previous_ssid and ssid are non-NULL), then the connected wireless network could be * changed. So, always reconfigure the link. Which means e.g. the DHCP client will be @@ -1747,10 +1754,6 @@ static int link_carrier_gained(Link *link) { if (r != 0) return r; - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - if (link->iftype == ARPHRD_CAN) /* let's shortcut things for CAN which doesn't need most of what's done below. */ return 0; @@ -1769,25 +1772,22 @@ static int link_carrier_gained(Link *link) { } static int link_carrier_lost_impl(Link *link) { - int r, ret = 0; + int ret = 0; assert(link); link->previous_ssid = mfree(link->previous_ssid); + ret = link_handle_bound_by_list(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 0; + return ret; if (!link->network) - return 0; - - r = link_stop_engines(link, false); - if (r < 0) - ret = r; + return ret; - r = link_drop_managed_config(link); - if (r < 0 && ret >= 0) - ret = r; + RET_GATHER(ret, link_stop_engines(link, false)); + RET_GATHER(ret, link_drop_managed_config(link)); return ret; } @@ -1808,22 +1808,17 @@ static int link_carrier_lost_handler(sd_event_source *s, uint64_t usec, void *us static int link_carrier_lost(Link *link) { uint16_t dhcp_mtu; usec_t usec; - int r; assert(link); - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - if (link->iftype == ARPHRD_CAN) /* let's shortcut things for CAN which doesn't need most of what's done below. */ - return 0; + usec = 0; - if (!link->network) - return 0; + else if (!link->network) + usec = 0; - if (link->network->ignore_carrier_loss_set) + else if (link->network->ignore_carrier_loss_set) /* If IgnoreCarrierLoss= is explicitly specified, then use the specified value. */ usec = link->network->ignore_carrier_loss_usec; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 50d284b..84558a5 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -2080,12 +2080,6 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) { assert(link); assert(link->network); - /* Do not start DHCPv6 client if the router lifetime is zero, as the message sent as a signal of - * that the router is e.g. shutting down, revoked, etc,. */ - r = sd_ndisc_router_get_lifetime(rt, NULL); - if (r <= 0) - return r; - switch (link->network->ndisc_start_dhcp6_client) { case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO: return 0; diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 886b4da..c6b3779 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -483,9 +483,7 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule return r; } - if (rule->l3mdev) - r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC); - else if (rule->table < 256) + if (rule->table < 256) r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table); else { r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC); @@ -1784,6 +1782,9 @@ static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) { /* rule->family can be AF_UNSPEC only when Family=both. */ } + if (rule->l3mdev) + rule->table = RT_TABLE_UNSPEC; + return 0; } diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index ec5d396..f46995b 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -821,6 +821,56 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **iface_pairs) { return 0; } +static int remove_macvlan_impl(char **interface_pairs) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r; + + assert(interface_pairs); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + STRV_FOREACH_PAIR(a, b, interface_pairs) { + _cleanup_free_ char *n = NULL; + + n = strdup(*b); + if (!n) + return log_oom(); + + (void) net_shorten_ifname(n, /* check_naming_scheme= */ true); + + r = remove_one_link(rtnl, n); + if (r < 0) + log_warning_errno(r, "Failed to remove macvlan interface %s, ignoring: %m", n); + } + + return 0; +} + +int remove_macvlan(int child_netns_fd, char **interface_pairs) { + _cleanup_close_ int parent_netns_fd = -EBADF; + int r; + + /* In some cases the kernel might pin the macvlan links on the container even after the namespace + * died. Hence, let's better remove them explicitly too. See issue #680. */ + + assert(child_netns_fd >= 0); + + if (strv_isempty(interface_pairs)) + return 0; + + r = netns_fork_and_wait(child_netns_fd, &parent_netns_fd); + if (r < 0) + return r; + if (r == 0) { + r = remove_macvlan_impl(interface_pairs); + _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); + } + + return 0; +} + int setup_ipvlan(const char *machine_name, pid_t pid, char **iface_pairs) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; int r; diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index 840fe15..ed08c98 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -17,6 +17,7 @@ int setup_bridge(const char *veth_name, const char *bridge_name, bool create); int remove_bridge(const char *bridge_name); int setup_macvlan(const char *machine_name, pid_t pid, char **iface_pairs); +int remove_macvlan(int child_netns_fd, char **interface_pairs); int setup_ipvlan(const char *machine_name, pid_t pid, char **iface_pairs); int move_network_interfaces(int netns_fd, char **iface_pairs); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index f8c34b5..7d87116 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -5607,6 +5607,10 @@ static int run_container( r = move_back_network_interfaces(child_netns_fd, arg_network_interfaces); if (r < 0) return r; + + r = remove_macvlan(child_netns_fd, arg_network_macvlan); + if (r < 0) + return r; } r = wait_for_container(TAKE_PID(*pid), &container_status); diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 1716fb3..9a7b73f 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -1925,6 +1925,9 @@ static int event_log_map_components(EventLog *el) { continue; } + if (c->n_variants == 0) + log_notice("Component '%s' has no defined variants.", c->id); + FOREACH_ARRAY(ii, c->variants, c->n_variants) { EventLogComponentVariant *i = *ii; @@ -4060,6 +4063,15 @@ static int event_log_predict_pcrs( component = ASSERT_PTR(el->components[component_index]); + if (component->n_variants == 0) + return event_log_predict_pcrs( + el, + context, + parent_result, + component_index + 1, /* Next component */ + pcr, + path); + FOREACH_ARRAY(ii, component->variants, component->n_variants) { _cleanup_free_ Tpm2PCRPredictionResult *result = NULL; EventLogComponentVariant *variant = *ii; @@ -4118,7 +4130,9 @@ static ssize_t event_log_calculate_component_combinations(EventLog *el) { /* Overflow check */ if (c->n_variants > (size_t) (SSIZE_MAX/count)) return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Too many component combinations."); - + /* If no variant, this will lead to count being 0 and sigfpe */ + if (c->n_variants == 0) + continue; count *= c->n_variants; } diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index d6d2273..847bb6b 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -1148,6 +1148,11 @@ static void resolve_service_all_complete(DnsQuery *query) { if (r < 0) goto finish; + if (isempty(type)) { + r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide valid service", dns_query_string(q)); + goto finish; + } + r = sd_bus_message_append( reply, "ssst", diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index 1a43d0b..8c15120 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -322,6 +322,12 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use return dns_stream_complete(s, -r); } + if (revents & EPOLLERR) { + socklen_t errlen = sizeof(r); + if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &r, &errlen) == 0) + return dns_stream_complete(s, r); + } + if ((revents & EPOLLOUT) && s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size) { diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 92ac075..17a815c 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -633,7 +633,7 @@ static int on_stream_complete(DnsStream *s, int error) { if (ERRNO_IS_DISCONNECT(error) && s->protocol != DNS_PROTOCOL_LLMNR) { log_debug_errno(error, "Connection failure for DNS TCP stream: %m"); - if (s->transactions) { + if (error != ECONNRESET && s->transactions) { DnsTransaction *t; t = s->transactions; @@ -1264,7 +1264,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt } /* These codes probably indicate a transient error. Let's try again. */ - if (IN_SET(t->answer_ede_rcode, DNS_EDE_RCODE_NOT_READY, DNS_EDE_RCODE_NET_ERROR)) { + if (t->answer_ede_rcode == DNS_EDE_RCODE_NOT_READY) { log_debug("Server returned error: %s (%s%s%s), retrying transaction.", FORMAT_DNS_RCODE(DNS_PACKET_RCODE(p)), FORMAT_DNS_EDE_RCODE(t->answer_ede_rcode), diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index 25f85d8..f02ebe3 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -829,6 +829,11 @@ static void resolve_service_all_complete(DnsQuery *query) { if (r < 0) goto finish; + if (isempty(type)) { + r = varlink_error(q->varlink_request, "io.systemd.Resolve.ServiceNotProvided", NULL); + goto finish; + } + r = varlink_replyb(query->varlink_request, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("services", JSON_BUILD_VARIANT(srv)), JSON_BUILD_PAIR_CONDITION(!json_variant_is_blank_object(txt), "txt", JSON_BUILD_VARIANT(txt)), diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index a4e2dae..0d5075e 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -56,8 +56,7 @@ static const BaseFilesystem table[] = { /* aarch64 ELF ABI actually says dynamic loader is in /lib/, but Fedora puts it in /lib64/ anyway and * just symlinks /lib/ld-linux-aarch64.so.1 to ../lib64/ld-linux-aarch64.so.1. For this to work * correctly, /lib64/ must be symlinked to /usr/lib64/. */ - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld-linux-aarch64.so.1" }, # define KNOW_LIB64_DIRS 1 #elif defined(__alpha__) @@ -66,24 +65,20 @@ static const BaseFilesystem table[] = { /* No /lib64 on arm. The linker is /lib/ld-linux-armhf.so.3. */ # define KNOW_LIB64_DIRS 1 #elif defined(__i386__) || defined(__x86_64__) - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld-linux-x86-64.so.2" }, # define KNOW_LIB64_DIRS 1 #elif defined(__ia64__) #elif defined(__loongarch_lp64) # define KNOW_LIB64_DIRS 1 # if defined(__loongarch_double_float) - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld-linux-loongarch-lp64d.so.1" }, # elif defined(__loongarch_single_float) - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld-linux-loongarch-lp64f.so.1" }, # elif defined(__loongarch_soft_float) - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld-linux-loongarch-lp64s.so.1" }, # else # error "Unknown LoongArch ABI" @@ -100,8 +95,7 @@ static const BaseFilesystem table[] = { # endif #elif defined(__powerpc__) # if defined(__PPC64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld64.so.2" }, # define KNOW_LIB64_DIRS 1 # elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ @@ -113,16 +107,14 @@ static const BaseFilesystem table[] = { # if __riscv_xlen == 32 # elif __riscv_xlen == 64 /* Same situation as for aarch64 */ - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld-linux-riscv64-lp64d.so.1" }, # define KNOW_LIB64_DIRS 1 # else # error "Unknown RISC-V ABI" # endif #elif defined(__s390x__) - { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" - "usr/lib64\0" + { "lib64", 0, "usr/lib64\0" "usr/lib\0", "ld-lsb-s390x.so.3" }, # define KNOW_LIB64_DIRS 1 #elif defined(__s390__) diff --git a/src/shared/edit-util.c b/src/shared/edit-util.c index cfb2828..b049603 100644 --- a/src/shared/edit-util.c +++ b/src/shared/edit-util.c @@ -357,7 +357,7 @@ static int strip_edit_temp_file(EditFile *e) { return 1; /* Contents have real changes */ r = write_string_file(e->temp, new_contents, - WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE); + WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE); if (r < 0) return log_error_errno(r, "Failed to strip temporary file '%s': %m", e->temp); diff --git a/src/shared/resize-fs.h b/src/shared/resize-fs.h index b40943c..bacaec7 100644 --- a/src/shared/resize-fs.h +++ b/src/shared/resize-fs.h @@ -7,9 +7,9 @@ int resize_fs(int fd, uint64_t sz, uint64_t *ret_size); -#define BTRFS_MINIMAL_SIZE (256U*1024U*1024U) -#define XFS_MINIMAL_SIZE (300U*1024U*1024U) -#define EXT4_MINIMAL_SIZE (1024U*1024U) +#define BTRFS_MINIMAL_SIZE (256U*U64_MB) +#define XFS_MINIMAL_SIZE (300U*U64_MB) +#define EXT4_MINIMAL_SIZE (32U*U64_MB) uint64_t minimal_size_by_fs_magic(statfs_f_type_t magic); uint64_t minimal_size_by_fs_name(const char *str); diff --git a/src/shared/tests.h b/src/shared/tests.h index 09fdfd6..21f00db 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -227,6 +227,16 @@ static inline int run_test_table(void) { } \ }) +#define ASSERT_FAIL(expr) \ + ({ \ + typeof(expr) _result = (expr); \ + if (_result >= 0) { \ + log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to fail, but it succeeded", \ + PROJECT_FILE, __LINE__, #expr); \ + abort(); \ + } \ + }) + #define ASSERT_ERROR(expr1, expr2) \ ({ \ int _expr1 = (expr1); \ @@ -419,3 +429,30 @@ static inline int run_test_table(void) { abort(); \ } \ }) + + +#define ASSERT_EQ_ID128(expr1, expr2) \ + ({ \ + typeof(expr1) _expr1 = (expr1); \ + typeof(expr2) _expr2 = (expr2); \ + if (!sd_id128_equal(_expr1, _expr2)) { \ + log_error("%s:%i: Assertion failed: \"%s == %s\", but \"%s != %s\"", \ + PROJECT_FILE, __LINE__, \ + #expr1, #expr2, \ + SD_ID128_TO_STRING(_expr1), SD_ID128_TO_STRING(_expr2)); \ + abort(); \ + } \ + }) + +#define ASSERT_NE_ID128(expr1, expr2) \ + ({ \ + typeof(expr1) _expr1 = (expr1); \ + typeof(expr2) _expr2 = (expr2); \ + if (sd_id128_equal(_expr1, _expr2)) { \ + log_error("%s:%i: Assertion failed: \"%s != %s\", but \"%s == %s\"", \ + PROJECT_FILE, __LINE__, \ + #expr1, #expr2, \ + SD_ID128_TO_STRING(_expr1), SD_ID128_TO_STRING(_expr2)); \ + abort(); \ + } \ + }) diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c index d6cdd97..4507000 100644 --- a/src/systemctl/systemctl-logind.c +++ b/src/systemctl/systemctl-logind.c @@ -432,6 +432,14 @@ int help_boot_loader_entry(void) { sd_bus *bus; int r; + /* This is called without checking runtime scope and bus transport like we do in parse_argv(). + * Loading boot entries is only supported by system scope. Let's gracefully adjust them. */ + arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; + if (arg_transport == BUS_TRANSPORT_CAPSULE) { + arg_host = NULL; + arg_transport = BUS_TRANSPORT_LOCAL; + } + r = acquire_bus(BUS_FULL, &bus); if (r < 0) return r; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 0ca76ac..97bdfad 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1073,6 +1073,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot access user instance remotely."); + if (arg_transport == BUS_TRANSPORT_CAPSULE && arg_runtime_scope != RUNTIME_SCOPE_USER) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Cannot access system instance with --capsule=/-C."); + if (arg_wait && arg_no_block) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--wait may not be combined with --no-block."); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 5fe3b0c..7758267 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -1448,9 +1448,15 @@ static int process_item(Context *c, Item *i) { case ADD_USER: { Item *j = NULL; - if (!i->gid_set) + if (!i->gid_set) { j = ordered_hashmap_get(c->groups, i->group_name ?: i->name); + /* If that's not a match, also check if the group name + * matches a user name in the queue. */ + if (!j && i->group_name) + j = ordered_hashmap_get(c->users, i->group_name); + } + if (j && j->todo_group) { /* When a group with the target name is already in queue, * use the information about the group and do not create diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index 4e236bd..5643053 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -7,6 +7,7 @@ #include "log.h" #include "macro.h" #include "mkdir.h" +#include "rm-rf.h" #include "string-util.h" #include "strv.h" #include "tests.h" @@ -393,19 +394,16 @@ TEST(config_parse) { } TEST(config_parse_standard_file_with_dropins_full) { - _cleanup_(rmdir_and_freep) char *root = NULL; + _cleanup_(rm_rf_physical_and_freep) char *root = NULL; _cleanup_close_ int rfd = -EBADF; int r; - assert_se(mkdtemp_malloc(NULL, &root) >= 0); + ASSERT_OK(rfd = mkdtemp_open("/tmp/test-config-parse-XXXXXX", 0, &root)); assert_se(mkdir_p_root(root, "/etc/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); assert_se(mkdir_p_root(root, "/run/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); assert_se(mkdir_p_root(root, "/usr/lib/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); assert_se(mkdir_p_root(root, "/usr/local/lib/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); - rfd = open(root, O_CLOEXEC|O_DIRECTORY); - assert_se(rfd >= 0); - assert_se(write_string_file_at(rfd, "usr/lib/kernel/install.conf", /* this one is ignored */ "A=!!!", WRITE_STRING_FILE_CREATE) == 0); assert_se(write_string_file_at(rfd, "usr/local/lib/kernel/install.conf", diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index d44e043..f2fa51f 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -606,71 +606,67 @@ TEST(open_mkdir_at) { } TEST(openat_report_new) { - _cleanup_free_ char *j = NULL; - _cleanup_(rm_rf_physical_and_freep) char *d = NULL; - _cleanup_close_ int fd = -EBADF; + _cleanup_(rm_rf_physical_and_freep) char *t = NULL; + _cleanup_close_ int tfd = -EBADF, fd = -EBADF; bool b; - assert_se(mkdtemp_malloc(NULL, &d) >= 0); - - j = path_join(d, "test"); - assert_se(j); + ASSERT_OK((tfd = mkdtemp_open(NULL, 0, &t))); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(b); + ASSERT_TRUE(b); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(!b); + ASSERT_FALSE(b); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(!b); + ASSERT_FALSE(b); - assert_se(unlink(j) >= 0); + ASSERT_OK_ERRNO(unlinkat(tfd, "test", 0)); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(b); + ASSERT_TRUE(b); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(!b); + ASSERT_FALSE(b); - assert_se(unlink(j) >= 0); + ASSERT_OK_ERRNO(unlinkat(tfd, "test", 0)); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, NULL); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT, 0666, NULL); + ASSERT_OK(fd); fd = safe_close(fd); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(!b); + ASSERT_FALSE(b); - fd = openat_report_new(AT_FDCWD, j, O_RDWR, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(!b); + ASSERT_FALSE(b); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT|O_EXCL, 0666, &b); - assert_se(fd == -EEXIST); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT|O_EXCL, 0666, &b); + ASSERT_ERROR(fd, EEXIST); - assert_se(unlink(j) >= 0); + ASSERT_OK_ERRNO(unlinkat(tfd, "test", 0)); - fd = openat_report_new(AT_FDCWD, j, O_RDWR, 0666, &b); - assert_se(fd == -ENOENT); + fd = openat_report_new(tfd, "test", O_RDWR, 0666, &b); + ASSERT_ERROR(fd, ENOENT); - fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT|O_EXCL, 0666, &b); - assert_se(fd >= 0); + fd = openat_report_new(tfd, "test", O_RDWR|O_CREAT|O_EXCL, 0666, &b); + ASSERT_OK(fd); fd = safe_close(fd); - assert_se(b); + ASSERT_TRUE(b); } TEST(xopenat_full) { diff --git a/src/test/test-id128.c b/src/test/test-id128.c index 48fdbba..a6ed640 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -28,31 +28,31 @@ TEST(id128) { _cleanup_free_ char *b = NULL; _cleanup_close_ int fd = -EBADF; - assert_se(sd_id128_randomize(&id) == 0); + ASSERT_OK(sd_id128_randomize(&id)); printf("random: %s\n", sd_id128_to_string(id, t)); - assert_se(sd_id128_from_string(t, &id2) == 0); - assert_se(sd_id128_equal(id, id2)); - assert_se(sd_id128_in_set(id, id)); - assert_se(sd_id128_in_set(id, id2)); - assert_se(sd_id128_in_set(id, id2, id)); - assert_se(sd_id128_in_set(id, ID128_WALDI, id)); - assert_se(!sd_id128_in_set(id)); - assert_se(!sd_id128_in_set(id, ID128_WALDI)); - assert_se(!sd_id128_in_set(id, ID128_WALDI, ID128_WALDI)); + ASSERT_OK(sd_id128_from_string(t, &id2) == 0); + ASSERT_EQ_ID128(id, id2); + ASSERT_TRUE(sd_id128_in_set(id, id)); + ASSERT_TRUE(sd_id128_in_set(id, id2)); + ASSERT_TRUE(sd_id128_in_set(id, id2, id)); + ASSERT_TRUE(sd_id128_in_set(id, ID128_WALDI, id)); + ASSERT_FALSE(sd_id128_in_set(id)); + ASSERT_FALSE(sd_id128_in_set(id, ID128_WALDI)); + ASSERT_FALSE(sd_id128_in_set(id, ID128_WALDI, ID128_WALDI)); if (sd_booted() > 0 && sd_id128_get_machine(NULL) >= 0) { - assert_se(sd_id128_get_machine(&id) == 0); + ASSERT_OK(sd_id128_get_machine(&id)); printf("machine: %s\n", sd_id128_to_string(id, t)); - assert_se(sd_id128_get_boot(&id) == 0); + ASSERT_OK(sd_id128_get_boot(&id)); printf("boot: %s\n", sd_id128_to_string(id, t)); } printf("waldi: %s\n", sd_id128_to_string(ID128_WALDI, t)); ASSERT_STREQ(t, STR_WALDI); - assert_se(asprintf(&b, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 32); + ASSERT_EQ(asprintf(&b, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)), 32); printf("waldi2: %s\n", b); ASSERT_STREQ(t, b); @@ -60,137 +60,137 @@ TEST(id128) { ASSERT_STREQ(q, UUID_WALDI); b = mfree(b); - assert_se(asprintf(&b, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 36); + ASSERT_EQ(asprintf(&b, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)), 36); printf("waldi4: %s\n", b); ASSERT_STREQ(q, b); - assert_se(sd_id128_from_string(STR_WALDI, &id) >= 0); - assert_se(sd_id128_equal(id, ID128_WALDI)); - - assert_se(sd_id128_from_string(UUID_WALDI, &id) >= 0); - assert_se(sd_id128_equal(id, ID128_WALDI)); - - assert_se(sd_id128_from_string("", &id) < 0); - assert_se(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f101", &id) < 0); - assert_se(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f10-", &id) < 0); - assert_se(sd_id128_from_string("01020304-0506-0708-090a0b0c0d0e0f10", &id) < 0); - assert_se(sd_id128_from_string("010203040506-0708-090a-0b0c0d0e0f10", &id) < 0); - - assert_se(id128_from_string_nonzero(STR_WALDI, &id) == 0); - assert_se(id128_from_string_nonzero(STR_NULL, &id) == -ENXIO); - assert_se(id128_from_string_nonzero("01020304-0506-0708-090a-0b0c0d0e0f101", &id) < 0); - assert_se(id128_from_string_nonzero("01020304-0506-0708-090a-0b0c0d0e0f10-", &id) < 0); - assert_se(id128_from_string_nonzero("01020304-0506-0708-090a0b0c0d0e0f10", &id) < 0); - assert_se(id128_from_string_nonzero("010203040506-0708-090a-0b0c0d0e0f10", &id) < 0); - - assert_se(id128_is_valid(STR_WALDI)); - assert_se(id128_is_valid(UUID_WALDI)); - assert_se(!id128_is_valid("")); - assert_se(!id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f101")); - assert_se(!id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f10-")); - assert_se(!id128_is_valid("01020304-0506-0708-090a0b0c0d0e0f10")); - assert_se(!id128_is_valid("010203040506-0708-090a-0b0c0d0e0f10")); + ASSERT_OK(sd_id128_from_string(STR_WALDI, &id)); + ASSERT_EQ_ID128(id, ID128_WALDI); + + ASSERT_OK(sd_id128_from_string(UUID_WALDI, &id)); + ASSERT_EQ_ID128(id, ID128_WALDI); + + ASSERT_FAIL(sd_id128_from_string("", &id)); + ASSERT_FAIL(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f101", &id)); + ASSERT_FAIL(sd_id128_from_string("01020304-0506-0708-090a-0b0c0d0e0f10-", &id)); + ASSERT_FAIL(sd_id128_from_string("01020304-0506-0708-090a0b0c0d0e0f10", &id)); + ASSERT_FAIL(sd_id128_from_string("010203040506-0708-090a-0b0c0d0e0f10", &id)); + + ASSERT_OK(id128_from_string_nonzero(STR_WALDI, &id)); + ASSERT_ERROR(id128_from_string_nonzero(STR_NULL, &id), ENXIO); + ASSERT_FAIL(id128_from_string_nonzero("01020304-0506-0708-090a-0b0c0d0e0f101", &id)); + ASSERT_FAIL(id128_from_string_nonzero("01020304-0506-0708-090a-0b0c0d0e0f10-", &id)); + ASSERT_FAIL(id128_from_string_nonzero("01020304-0506-0708-090a0b0c0d0e0f10", &id)); + ASSERT_FAIL(id128_from_string_nonzero("010203040506-0708-090a-0b0c0d0e0f10", &id)); + + ASSERT_TRUE(id128_is_valid(STR_WALDI)); + ASSERT_TRUE(id128_is_valid(UUID_WALDI)); + ASSERT_FALSE(id128_is_valid("")); + ASSERT_FALSE(id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f101")); + ASSERT_FALSE(id128_is_valid("01020304-0506-0708-090a-0b0c0d0e0f10-")); + ASSERT_FALSE(id128_is_valid("01020304-0506-0708-090a0b0c0d0e0f10")); + ASSERT_FALSE(id128_is_valid("010203040506-0708-090a-0b0c0d0e0f10")); fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); + ASSERT_OK(fd); /* First, write as UUID */ - assert_se(sd_id128_randomize(&id) >= 0); - assert_se(id128_write_fd(fd, ID128_FORMAT_UUID, id) >= 0); + ASSERT_OK(sd_id128_randomize(&id)); + ASSERT_OK(id128_write_fd(fd, ID128_FORMAT_UUID, id)); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EUCLEAN); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2), EUCLEAN); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_UUID, &id2)); + ASSERT_EQ_ID128(id, id2); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_ANY, &id2)); + ASSERT_EQ_ID128(id, id2); /* Second, write as plain */ - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK_ERRNO(ftruncate(fd, 0)); - assert_se(sd_id128_randomize(&id) >= 0); - assert_se(id128_write_fd(fd, ID128_FORMAT_PLAIN, id) >= 0); + ASSERT_OK(sd_id128_randomize(&id)); + ASSERT_OK(id128_write_fd(fd, ID128_FORMAT_PLAIN, id)); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EUCLEAN); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET) == 0); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_UUID, &id2), EUCLEAN); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2)); + ASSERT_EQ_ID128(id, id2); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_ANY, &id2)); + ASSERT_EQ_ID128(id, id2); /* Third, write plain without trailing newline */ - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK_ERRNO(ftruncate(fd, 0)); - assert_se(sd_id128_randomize(&id) >= 0); - assert_se(write(fd, sd_id128_to_string(id, t), 32) == 32); + ASSERT_OK(sd_id128_randomize(&id)); + ASSERT_EQ(write(fd, sd_id128_to_string(id, t), 32), 32); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EUCLEAN); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_UUID, &id2), EUCLEAN); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2)); + ASSERT_EQ_ID128(id, id2); /* Fourth, write UUID without trailing newline */ - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK_ERRNO(ftruncate(fd, 0)); - assert_se(sd_id128_randomize(&id) >= 0); - assert_se(write(fd, sd_id128_to_uuid_string(id, q), 36) == 36); + ASSERT_OK(sd_id128_randomize(&id)); + ASSERT_EQ(write(fd, sd_id128_to_uuid_string(id, q), 36), 36); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EUCLEAN); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2), EUCLEAN); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_UUID, &id2)); + ASSERT_EQ_ID128(id, id2); /* Fifth, tests for "uninitialized" */ - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); - assert_se(write(fd, "uninitialized", STRLEN("uninitialized")) == STRLEN("uninitialized")); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG); - - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); - assert_se(write(fd, "uninitialized\n", STRLEN("uninitialized\n")) == STRLEN("uninitialized\n")); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG); - - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); - assert_se(write(fd, "uninitialized\nfoo", STRLEN("uninitialized\nfoo")) == STRLEN("uninitialized\nfoo")); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EUCLEAN); - - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); - assert_se(write(fd, "uninit", STRLEN("uninit")) == STRLEN("uninit")); - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EUCLEAN); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK_ERRNO(ftruncate(fd, 0)); + ASSERT_EQ(write(fd, "uninitialized", STRLEN("uninitialized")), (ssize_t) STRLEN("uninitialized")); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_ANY, NULL), ENOPKG); + + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK_ERRNO(ftruncate(fd, 0)); + ASSERT_EQ(write(fd, "uninitialized\n", STRLEN("uninitialized\n")), (ssize_t) STRLEN("uninitialized\n")); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_ANY, NULL), ENOPKG); + + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK_ERRNO(ftruncate(fd, 0)); + ASSERT_EQ(write(fd, "uninitialized\nfoo", STRLEN("uninitialized\nfoo")), (ssize_t) STRLEN("uninitialized\nfoo")); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_ANY, NULL), EUCLEAN); + + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_OK_ERRNO(ftruncate(fd, 0)); + ASSERT_EQ(write(fd, "uninit", STRLEN("uninit")), (ssize_t) STRLEN("uninit")); + ASSERT_OK_ERRNO(lseek(fd, 0, SEEK_SET)); + ASSERT_ERROR(id128_read_fd(fd, ID128_FORMAT_ANY, NULL), EUCLEAN); /* build/systemd-id128 -a f03daaeb1c334b43a732172944bf772e show 51df0b4bc3b04c9780e299b98ca373b8 */ - assert_se(sd_id128_get_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), - SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0); - assert_se(sd_id128_equal(id, SD_ID128_MAKE(1d,ee,59,54,e7,5c,4d,6f,b9,6c,c6,c0,4c,a1,8a,86))); + ASSERT_OK(sd_id128_get_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), + SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id)); + ASSERT_EQ_ID128(id, SD_ID128_MAKE(1d,ee,59,54,e7,5c,4d,6f,b9,6c,c6,c0,4c,a1,8a,86)); if (sd_booted() > 0 && sd_id128_get_machine(NULL) >= 0) { - assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0); - assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); - assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2) >= 0); - assert_se(!sd_id128_equal(id, id2)); + ASSERT_OK(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id)); + ASSERT_OK(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2)); + ASSERT_EQ_ID128(id, id2); + ASSERT_OK(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2)); + ASSERT_NE_ID128(id, id2); } /* Check return values */ @@ -214,7 +214,7 @@ TEST(sd_id128_get_invocation) { if (r < 0) log_warning_errno(r, "Failed to get invocation ID, ignoring: %m"); else { - assert(!sd_id128_equal(id, appid)); + ASSERT_NE_ID128(id, appid); log_info("Per-App Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid)); } @@ -223,8 +223,8 @@ TEST(sd_id128_get_invocation) { if (r < 0) log_warning_errno(r, "Failed to get invocation ID, ignoring: %m"); else { - assert(!sd_id128_equal(id, appid2)); - assert(!sd_id128_equal(appid, appid2)); + ASSERT_NE_ID128(id, appid2); + ASSERT_NE_ID128(appid, appid2); log_info("Per-App Invocation ID 2: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid2)); } @@ -233,9 +233,9 @@ TEST(sd_id128_get_invocation) { if (r < 0) log_warning_errno(r, "Failed to get invocation ID, ignoring: %m"); else { - assert(!sd_id128_equal(id, appid3)); - assert(sd_id128_equal(appid, appid3)); - assert(!sd_id128_equal(appid2, appid3)); + ASSERT_NE_ID128(id, appid3); + ASSERT_EQ_ID128(appid, appid3); + ASSERT_NE_ID128(appid2, appid3); log_info("Per-App Invocation ID 3: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid3)); } } @@ -256,7 +256,7 @@ TEST(benchmark_sd_id128_get_machine_app_specific) { for (unsigned i = 0; i < iterations; i++) { id.qwords[1] = i; - assert_se(sd_id128_get_machine_app_specific(id, &id2) >= 0); + ASSERT_OK(sd_id128_get_machine_app_specific(id, &id2)); } q = now(CLOCK_MONOTONIC) - t; @@ -271,79 +271,79 @@ TEST(id128_at) { sd_id128_t id, i; tfd = mkdtemp_open(NULL, O_PATH, &t); - assert_se(tfd >= 0); - assert_se(mkdirat(tfd, "etc", 0755) >= 0); - assert_se(symlinkat("etc", tfd, "etc2") >= 0); - assert_se(symlinkat("machine-id", tfd, "etc/hoge-id") >= 0); + ASSERT_OK(tfd); + ASSERT_OK_ERRNO(mkdirat(tfd, "etc", 0755)); + ASSERT_OK_ERRNO(symlinkat("etc", tfd, "etc2")); + ASSERT_OK_ERRNO(symlinkat("machine-id", tfd, "etc/hoge-id")); - assert_se(sd_id128_randomize(&id) == 0); + ASSERT_OK(sd_id128_randomize(&id)); - assert_se(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id) >= 0); + ASSERT_OK(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id)); if (geteuid() == 0) - assert_se(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id) >= 0); + ASSERT_OK(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id)); else - assert_se(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id) == -EACCES); - assert_se(unlinkat(tfd, "etc/machine-id", 0) >= 0); - assert_se(id128_write_at(tfd, "etc2/machine-id", ID128_FORMAT_PLAIN, id) >= 0); - assert_se(unlinkat(tfd, "etc/machine-id", 0) >= 0); - assert_se(id128_write_at(tfd, "etc/hoge-id", ID128_FORMAT_PLAIN, id) >= 0); - assert_se(unlinkat(tfd, "etc/machine-id", 0) >= 0); - assert_se(id128_write_at(tfd, "etc2/hoge-id", ID128_FORMAT_PLAIN, id) >= 0); + ASSERT_ERROR(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id), EACCES); + ASSERT_OK_ERRNO(unlinkat(tfd, "etc/machine-id", 0)); + ASSERT_OK(id128_write_at(tfd, "etc2/machine-id", ID128_FORMAT_PLAIN, id)); + ASSERT_OK_ERRNO(unlinkat(tfd, "etc/machine-id", 0)); + ASSERT_OK(id128_write_at(tfd, "etc/hoge-id", ID128_FORMAT_PLAIN, id)); + ASSERT_OK_ERRNO(unlinkat(tfd, "etc/machine-id", 0)); + ASSERT_OK(id128_write_at(tfd, "etc2/hoge-id", ID128_FORMAT_PLAIN, id)); /* id128_read_at() */ i = SD_ID128_NULL; /* Not necessary in real code, but for testing that the id is really assigned. */ - assert_se(id128_read_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); i = SD_ID128_NULL; - assert_se(id128_read_at(tfd, "etc2/machine-id", ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read_at(tfd, "etc2/machine-id", ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); i = SD_ID128_NULL; - assert_se(id128_read_at(tfd, "etc/hoge-id", ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read_at(tfd, "etc/hoge-id", ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); i = SD_ID128_NULL; - assert_se(id128_read_at(tfd, "etc2/hoge-id", ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read_at(tfd, "etc2/hoge-id", ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); /* id128_read() */ - assert_se(p = path_join(t, "/etc/machine-id")); + ASSERT_NOT_NULL(p = path_join(t, "/etc/machine-id")); i = SD_ID128_NULL; - assert_se(id128_read(p, ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read(p, ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); free(p); - assert_se(p = path_join(t, "/etc2/machine-id")); + ASSERT_NOT_NULL(p = path_join(t, "/etc2/machine-id")); i = SD_ID128_NULL; - assert_se(id128_read(p, ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read(p, ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); free(p); - assert_se(p = path_join(t, "/etc/hoge-id")); + ASSERT_NOT_NULL(p = path_join(t, "/etc/hoge-id")); i = SD_ID128_NULL; - assert_se(id128_read(p, ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read(p, ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); free(p); - assert_se(p = path_join(t, "/etc2/hoge-id")); + ASSERT_NOT_NULL(p = path_join(t, "/etc2/hoge-id")); i = SD_ID128_NULL; - assert_se(id128_read(p, ID128_FORMAT_PLAIN, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_read(p, ID128_FORMAT_PLAIN, &i)); + ASSERT_EQ_ID128(id, i); /* id128_get_machine_at() */ i = SD_ID128_NULL; - assert_se(id128_get_machine_at(tfd, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_get_machine_at(tfd, &i)); + ASSERT_EQ_ID128(id, i); /* id128_get_machine() */ i = SD_ID128_NULL; - assert_se(id128_get_machine(t, &i) >= 0); - assert_se(sd_id128_equal(id, i)); + ASSERT_OK(id128_get_machine(t, &i)); + ASSERT_EQ_ID128(id, i); } TEST(ID128_REFUSE_NULL) { @@ -352,18 +352,18 @@ TEST(ID128_REFUSE_NULL) { sd_id128_t id; tfd = mkdtemp_open(NULL, O_PATH, &t); - assert_se(tfd >= 0); + ASSERT_OK(tfd); - assert_se(id128_write_at(tfd, "zero-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, (sd_id128_t) {}) == -ENOMEDIUM); - assert_se(unlinkat(tfd, "zero-id", 0) >= 0); - assert_se(id128_write_at(tfd, "zero-id", ID128_FORMAT_PLAIN, (sd_id128_t) {}) >= 0); + ASSERT_ERROR(id128_write_at(tfd, "zero-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, (sd_id128_t) {}), ENOMEDIUM); + ASSERT_OK_ERRNO(unlinkat(tfd, "zero-id", 0)); + ASSERT_OK(id128_write_at(tfd, "zero-id", ID128_FORMAT_PLAIN, (sd_id128_t) {})); - assert_se(sd_id128_randomize(&id) == 0); - assert_se(!sd_id128_equal(id, SD_ID128_NULL)); - assert_se(id128_read_at(tfd, "zero-id", ID128_FORMAT_PLAIN, &id) >= 0); - assert_se(sd_id128_equal(id, SD_ID128_NULL)); + ASSERT_OK(sd_id128_randomize(&id)); + ASSERT_NE_ID128(id, SD_ID128_NULL); + ASSERT_OK(id128_read_at(tfd, "zero-id", ID128_FORMAT_PLAIN, &id)); + ASSERT_EQ_ID128(id, SD_ID128_NULL); - assert_se(id128_read_at(tfd, "zero-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, &id) == -ENOMEDIUM); + ASSERT_ERROR(id128_read_at(tfd, "zero-id", ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, &id), ENOMEDIUM); } DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-log.c b/src/test/test-log.c index 97eb5e0..57cab63 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -13,11 +13,6 @@ #include "strv.h" #include "tests.h" -assert_cc(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(EINVAL))); -assert_cc(!IS_SYNTHETIC_ERRNO(EINVAL)); -assert_cc(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(0))); -assert_cc(!IS_SYNTHETIC_ERRNO(0)); - #define X10(x) x x x x x x x x x x #define X100(x) X10(X10(x)) #define X1000(x) X100(X10(x)) @@ -227,6 +222,15 @@ static void test_log_prefix(void) { int main(int argc, char* argv[]) { test_setup_logging(LOG_DEBUG); + ASSERT_TRUE(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(EINVAL))); + ASSERT_TRUE(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(-EINVAL))); + assert_cc(!IS_SYNTHETIC_ERRNO(EINVAL)); + assert_cc(!IS_SYNTHETIC_ERRNO(-EINVAL)); + ASSERT_TRUE(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(0))); + assert_cc(!IS_SYNTHETIC_ERRNO(0)); + ASSERT_EQ(ERRNO_VALUE(EINVAL), EINVAL); + ASSERT_EQ(ERRNO_VALUE(SYNTHETIC_ERRNO(-EINVAL)), EINVAL); + test_assert_return_is_critical(); test_file(); diff --git a/src/test/test-macro.c b/src/test/test-macro.c index 9e2875d..3d5b0cf 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -4,6 +4,7 @@ #include #include "errno-util.h" +#include "id128-util.h" #include "log.h" #include "macro.h" #include "tests.h" @@ -1122,6 +1123,11 @@ TEST(ASSERT) { ASSERT_SIGNAL(ASSERT_OK_ERRNO(-1), SIGABRT); ASSERT_SIGNAL(ASSERT_OK_ERRNO(-ENOANO), SIGABRT); + ASSERT_FAIL(-ENOENT); + ASSERT_FAIL(-EPERM); + ASSERT_SIGNAL(ASSERT_FAIL(0), SIGABRT); + ASSERT_SIGNAL(ASSERT_FAIL(255), SIGABRT); + ASSERT_ERROR(-ENOENT, ENOENT); ASSERT_ERROR(RET_NERRNO(mkdir("/i/will/fail/with/enoent", 666)), ENOENT); ASSERT_SIGNAL(ASSERT_ERROR(0, ENOENT), SIGABRT); @@ -1181,6 +1187,15 @@ TEST(ASSERT) { ASSERT_LT(-1, 1); ASSERT_SIGNAL(ASSERT_LT(0, 0), SIGABRT); ASSERT_SIGNAL(ASSERT_LT(1, -1), SIGABRT); + + ASSERT_EQ_ID128(SD_ID128_NULL, SD_ID128_NULL); + ASSERT_NE_ID128(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), + SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e)); + ASSERT_SIGNAL( + ASSERT_EQ_ID128(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), + SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e)), + SIGABRT); + ASSERT_SIGNAL(ASSERT_NE_ID128(SD_ID128_NULL, SD_ID128_NULL), SIGABRT); } DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/ukify/test/pytest.ini b/src/ukify/test/pytest.ini new file mode 100644 index 0000000..a7c58bb --- /dev/null +++ b/src/ukify/test/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +tmp_path_retention_policy = failed diff --git a/test/README.testsuite b/test/README.testsuite index 22da1cd..6320e94 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -86,6 +86,45 @@ mkosi in the systemd reposistory, so any local modifications to the mkosi configuration (e.g. in `mkosi.local.conf`) are automatically picked up and used by the integration tests as well. +## Iterating on an integration test + +To iterate on an integration test, let's first get a shell in the integration test environment by running +the following: + +```shell +$ meson compile -C build mkosi && SYSTEMD_INTEGRATION_TESTS=1 TEST_SHELL=1 meson test -C build --no-rebuild -i TEST-01-BASIC +``` + +This will get us a shell in the integration test environment after booting the machine without running the +integration test itself. After booting, we can verify the integration test passes by running it manually, +for example with `systemctl start TEST-01-BASIC`. + +Now you can extend the test in whatever way you like to add more coverage of existing features or to add +coverage for a new feature. Once you've finished writing the logic and want to rerun the test, run the +the following on the host: + +```shell +$ mkosi -t none +``` + +This will rebuild the distribution packages without rebuilding the entire integration test image. Next, run +the following in the integration test machine: + +```shell +$ systemctl soft-reboot +$ systemctl start TEST-01-BASIC +``` + +A soft-reboot is required to make sure all the leftover state from the previous run of the test is cleaned +up by soft-rebooting into the btrfs snapshot we made before running the test. After the soft-reboot, +re-running the test will first install the new packages we just built, make a new snapshot and finally run +the test again. You can keep running the loop of `mkosi -t none`, `systemctl soft-reboot` and +`systemctl start ...` until the changes to the integration test are working. + +If you're debugging a failing integration test (running `meson test --interactive` without `TEST_SHELL`), +there's no need to run `systemctl start ...`, running `systemctl soft-reboot` on its own is sufficient to +rerun the test. + ## Running the integration tests the old fashioned way The extended testsuite only works with UID=0. It consists of the subdirectories @@ -181,7 +220,7 @@ sec). `NSPAWN_TIMEOUT=infinity`: Set a timeout for tests under systemd-nspawn (defaults to 1800 sec). -`INTERACTIVE_DEBUG=1`: Configure the machine to be more *user-friendly* for +`TEST_SHELL=1`: Configure the machine to be more *user-friendly* for interactive debugging (e.g. by setting a usable default terminal, suppressing the shutdown after the test, etc.). @@ -198,13 +237,15 @@ or Debian's default kernel path and initrd are used by default.) A script will try to find your qemu binary. If you want to specify a different one with `$QEMU_BIN`. +`TEST_SKIP`: takes a space separated list of tests to skip. + ### Debugging the qemu image -If you want to log in the testsuite virtual machine, use `INTERACTIVE_DEBUG=1` +If you want to log in the testsuite virtual machine, use `TEST_SHELL=1` and log in as root: ```shell -$ sudo make -C test/TEST-01-BASIC INTERACTIVE_DEBUG=1 run +$ sudo make -C test/TEST-01-BASIC TEST_SHELL=1 run ``` The root password is empty. @@ -248,7 +289,12 @@ is available at: https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/ -paths listed at this URL can be appended to the URL to download them. +paths listed at this URL can be appended to the URL to download them. Unfortunately +there are too many results and the web server cannot list them all at once. Fortunately +there is a workaround: copy the last line on the page, and append it to the URL, with +a '?marker=' prefix, and the web server will show the next page of results. For example: + +https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/?marker=noble/amd64/s/systemd-upstream/20240616_211635_5993a@/result.tar The 5 characters at the end of the last directory are not random, but the first 5 characters of a SHA1 hash generated based on the set of parameters given to diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh index a7aa71f..01d0b66 100755 --- a/test/TEST-04-JOURNAL/test.sh +++ b/test/TEST-04-JOURNAL/test.sh @@ -19,7 +19,7 @@ test_append_files() { # Since we nuke the journal repeatedly during this test, let's redirect # stdout/stderr to the console as well to make the test a bit more debug-able. - if ! get_bool "${INTERACTIVE_DEBUG:-}"; then + if ! get_bool "${TEST_SHELL:-}"; then dropin_dir="${workspace:?}/etc/systemd/system/TEST-04-JOURNAL.service.d/" mkdir -p "$dropin_dir" printf '[Service]\nStandardOutput=journal+console\nStandardError=journal+console' >"$dropin_dir/99-stdout.conf" diff --git a/test/TEST-46-HOMED/test.sh b/test/TEST-46-HOMED/test.sh index 06034b7..973b030 100755 --- a/test/TEST-46-HOMED/test.sh +++ b/test/TEST-46-HOMED/test.sh @@ -24,7 +24,7 @@ test_append_files() { inst_binary ssh inst_binary sshd inst_binary ssh-keygen - image_install -o /usr/lib/ssh/sshd-session + image_install -o /usr/lib/ssh/sshd-session /usr/libexec/openssh/sshd-session } do_test "$@" diff --git a/test/TEST-54-CREDS/meson.build b/test/TEST-54-CREDS/meson.build index f725035..99524b9 100644 --- a/test/TEST-54-CREDS/meson.build +++ b/test/TEST-54-CREDS/meson.build @@ -17,15 +17,15 @@ integration_tests += [ files('systemd.extra-unit.my-service.service'), files('systemd.unit-dropin.my-service.service'), files('systemd.unit-dropin.my-service.service~30-named.service'), + 'smbioscredential=magicdata', + 'binarysmbioscredential=magicbinarydata', + 'sysusers.extra="u credtestuser"', + 'tmpfiles.extra="f /tmp/sourcedfromcredential - - - - tmpfilessecret"', + 'fstab.extra="injected /injected tmpfs X-mount.mkdir 0 0"', + 'getty.ttys.container=idontexist', ], 'qemu-args' : integration_test_template['qemu-args'] + [ '-fw_cfg', 'name=opt/io.systemd.credentials/myqemucredential,string=othervalue', - '-smbios', 'type=11,value=io.systemd.credential:smbioscredential=magicdata', - '-smbios', 'type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh', - '-smbios', 'type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK', - '-smbios', 'type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg==', - '-smbios', 'type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg==', - '-smbios', 'type=11,value=io.systemd.credential:getty.ttys.container=idontexist', ], 'firmware' : 'auto', }, diff --git a/test/TEST-74-AUX-UTILS/test.sh b/test/TEST-74-AUX-UTILS/test.sh index d47a0a2..d9ae70c 100755 --- a/test/TEST-74-AUX-UTILS/test.sh +++ b/test/TEST-74-AUX-UTILS/test.sh @@ -31,7 +31,7 @@ test_append_files() { inst_binary ssh inst_binary sshd inst_binary ssh-keygen - image_install -o /usr/lib/ssh/sshd-session + image_install -o /usr/lib/ssh/sshd-session /usr/libexec/openssh/sshd-session inst_binary usermod instmods vmw_vsock_virtio_transport instmods vsock_loopback diff --git a/test/fuzz/fuzz-systemctl-parse-argv/oss-fuzz-70153 b/test/fuzz/fuzz-systemctl-parse-argv/oss-fuzz-70153 new file mode 100644 index 0000000..cb874fa Binary files /dev/null and b/test/fuzz/fuzz-systemctl-parse-argv/oss-fuzz-70153 differ diff --git a/test/integration-test-setup.sh b/test/integration-test-setup.sh new file mode 100755 index 0000000..d7c384a --- /dev/null +++ b/test/integration-test-setup.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +case "$1" in + setup) + if [[ -f "$STATE_DIRECTORY/inprogress" ]]; then + exit 0 + fi + + if [[ -d /snapshot ]]; then + echo "Run systemctl soft-reboot first to make sure the test runs within a pristine rootfs" >&2 + exit 1 + fi + + . /usr/lib/os-release + + if test -n "$(shopt -s nullglob; echo /work/build/*.{rpm,deb,pkg.tar})"; then + case "$ID" in + arch) + pacman --upgrade --needed --noconfirm /work/build/*.pkg.tar + ;; + debian|ubuntu) + apt-get install /work/build/*.deb + ;; + opensuse*) + zypper --non-interactive install --allow-unsigned-rpm /work/build/*.rpm + ;; + centos|fedora) + dnf upgrade --assumeyes --disablerepo="*" /work/build/*.rpm + ;; + *) + echo "Unknown distribution $ID" >&2 + exit 1 + esac + fi + + # TODO: Use a proper flat btrfs subvolume layout once we can create subvolumes without privileged in + # systemd-repart (see https://github.com/systemd/systemd/pull/33498). Until that's possible, we nest + # snapshots within each other. + if command -v btrfs >/dev/null && [[ "$(stat --file-system --format %T /)" == "btrfs" ]]; then + btrfs subvolume snapshot / /snapshot + fi + + touch "$STATE_DIRECTORY/inprogress" + ;; + finalize) + # If we're rebooting, the test does a reboot as part of its execution and we shouldn't remove /inprogress. + if ! [[ "$(systemctl list-jobs)" =~ reboot.target|kexec.target|soft-reboot.target ]]; then + rm -f "$STATE_DIRECTORY/inprogress" + fi + ;; + *) + echo "Unknown verb $1" >&2 + exit 1 +esac diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py index d7a622a..0931043 100755 --- a/test/integration-test-wrapper.py +++ b/test/integration-test-wrapper.py @@ -61,21 +61,35 @@ def main(): print(f"TEST_NO_QEMU=1, skipping {args.name}", file=sys.stderr) exit(77) + if args.name in os.getenv("TEST_SKIP", "").split(): + print(f"Skipping {args.name} due to TEST_SKIP", file=sys.stderr) + exit(77) + keep_journal = os.getenv("TEST_SAVE_JOURNAL", "fail") + shell = bool(int(os.getenv("TEST_SHELL", "0"))) + + if shell and not sys.stderr.isatty(): + print(f"--interactive must be passed to meson test to use TEST_SHELL=1", file=sys.stderr) + exit(1) name = args.name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "") dropin = textwrap.dedent( """\ - [Unit] - SuccessAction=exit - SuccessActionExitStatus=123 - [Service] StandardOutput=journal+console """ ) + if not shell: + dropin += textwrap.dedent( + f""" + [Unit] + SuccessAction=exit + SuccessActionExitStatus=123 + """ + ) + if os.getenv("TEST_MATCH_SUBTEST"): dropin += textwrap.dedent( f""" @@ -92,6 +106,7 @@ def main(): """ ) + journal_file = None if not sys.stderr.isatty(): dropin += textwrap.dedent( """ @@ -102,14 +117,13 @@ def main(): journal_file = (args.meson_build_dir / (f"test/journal/{name}.journal")).absolute() journal_file.unlink(missing_ok=True) - else: + elif not shell: dropin += textwrap.dedent( """ [Unit] Wants=multi-user.target """ ) - journal_file = None cmd = [ args.mkosi, @@ -140,7 +154,7 @@ def main(): ' '.join([ 'systemd.hostname=H', f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/{args.name}.units:/usr/lib/systemd/tests/testdata/units:", - f"systemd.unit={args.unit}", + *([f"systemd.unit={args.unit}"] if not shell else []), 'systemd.mask=systemd-networkd-wait-online.service', *( [ @@ -154,6 +168,7 @@ def main(): ), ]), '--credential', f"journal.storage={'persistent' if sys.stderr.isatty() else args.storage}", + *(['--runtime-build-sources=no'] if not sys.stderr.isatty() else []), 'qemu' if args.vm or os.getuid() != 0 else 'boot', ] @@ -162,8 +177,8 @@ def main(): if journal_file and (keep_journal == "0" or (result.returncode in (args.exit_code, 77) and keep_journal == "fail")): journal_file.unlink(missing_ok=True) - if result.returncode in (args.exit_code, 77): - exit(0 if result.returncode == args.exit_code else 77) + if shell or result.returncode in (args.exit_code, 77): + exit(0 if shell or result.returncode == args.exit_code else 77) if journal_file: ops = [] diff --git a/test/meson.build b/test/meson.build index 173d90c..6acff37 100644 --- a/test/meson.build +++ b/test/meson.build @@ -142,9 +142,11 @@ endif ############################################################ if install_tests - install_data('run-unit-tests.py', - install_mode : 'rwxr-xr-x', - install_dir : testsdir) + foreach script : ['integration-test-setup.sh', 'run-unit-tests.py'] + install_data(script, + install_mode : 'rwxr-xr-x', + install_dir : testsdir) + endforeach endif ############################################################ diff --git a/test/test-functions b/test/test-functions index e219812..04fe20f 100644 --- a/test/test-functions +++ b/test/test-functions @@ -570,7 +570,7 @@ run_qemu() { ${TEST_MATCH_TESTCASE:+"systemd.setenv=TEST_MATCH_TESTCASE=$TEST_MATCH_TESTCASE"} ) - if ! get_bool "$INTERACTIVE_DEBUG" && ! get_bool "$TEST_SKIP_SHUTDOWN"; then + if ! get_bool "$TEST_SHELL" && ! get_bool "$TEST_SKIP_SHUTDOWN"; then kernel_params+=( "panic=1" "softlockup_panic=1" @@ -664,7 +664,7 @@ run_nspawn() { ${TEST_MATCH_TESTCASE:+"systemd.setenv=TEST_MATCH_TESTCASE=$TEST_MATCH_TESTCASE"} ) - if get_bool "$INTERACTIVE_DEBUG"; then + if get_bool "$TEST_SHELL"; then nspawn_options+=("--console=interactive") elif ! get_bool "$TEST_SKIP_SHUTDOWN"; then kernel_params+=("systemd.wants=end.service") @@ -1984,7 +1984,7 @@ install_debug_tools() { dinfo "Install debug tools" image_install -o "${DEBUGTOOLS[@]}" - if get_bool "$INTERACTIVE_DEBUG"; then + if get_bool "$TEST_SHELL"; then # Set default TERM from vt220 to linux, so at least basic key shortcuts work local getty_override="${initdir:?}/etc/systemd/system/serial-getty@.service.d" mkdir -p "$getty_override" diff --git a/test/test-sysusers/test-16.expected-group b/test/test-sysusers/test-16.expected-group new file mode 100644 index 0000000..54918e4 --- /dev/null +++ b/test/test-sysusers/test-16.expected-group @@ -0,0 +1 @@ +foo:x:SYSTEM_UGID_MAX: diff --git a/test/test-sysusers/test-16.expected-passwd b/test/test-sysusers/test-16.expected-passwd new file mode 100644 index 0000000..8823813 --- /dev/null +++ b/test/test-sysusers/test-16.expected-passwd @@ -0,0 +1,2 @@ +foo:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN +bar:x:300:SYSTEM_UGID_MAX::/:NOLOGIN diff --git a/test/test-sysusers/test-16.input b/test/test-sysusers/test-16.input new file mode 100644 index 0000000..2d80d81 --- /dev/null +++ b/test/test-sysusers/test-16.input @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Test fix for https://github.com/systemd/systemd/issues/33547. +# +#Type Name ID +u foo - +u bar 300:foo diff --git a/test/test.service.in b/test/test.service.in index 790c513..48c09ba 100644 --- a/test/test.service.in +++ b/test/test.service.in @@ -7,6 +7,9 @@ Before=getty-pre.target [Service] ExecStartPre=rm -f /failed /testok +ExecStartPre=/usr/lib/systemd/tests/integration-test-setup.sh setup ExecStart=@command@ +ExecStopPost=/usr/lib/systemd/tests/integration-test-setup.sh finalize Type=oneshot MemoryAccounting=@memory-accounting@ +StateDirectory=%N diff --git a/test/units/TEST-04-JOURNAL.bsod.sh b/test/units/TEST-04-JOURNAL.bsod.sh index 83feb89..802d474 100755 --- a/test/units/TEST-04-JOURNAL.bsod.sh +++ b/test/units/TEST-04-JOURNAL.bsod.sh @@ -4,12 +4,12 @@ set -eux set -o pipefail if systemd-detect-virt -cq; then - echo "This test requires a VM, skipping the test" | tee --append /skipped + echo "This test requires a VM, skipping the test" exit 0 fi if [[ ! -x /usr/lib/systemd/systemd-bsod ]]; then - echo "systemd-bsod is not installed, skipping the test" | tee --append /skipped + echo "systemd-bsod is not installed, skipping the test" exit 0 fi diff --git a/test/units/TEST-04-JOURNAL.stopped-socket-activation.sh b/test/units/TEST-04-JOURNAL.stopped-socket-activation.sh new file mode 100755 index 0000000..083f5fa --- /dev/null +++ b/test/units/TEST-04-JOURNAL.stopped-socket-activation.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +systemctl stop systemd-journald.service +systemd-cat date + +# shellcheck disable=SC2016 +timeout 30 bash -xec 'until test "$(systemctl show -p SubState --value systemd-journald.service)" = "running"; do sleep 1; done' diff --git a/test/units/TEST-07-PID1.issue-31752.sh b/test/units/TEST-07-PID1.issue-31752.sh new file mode 100755 index 0000000..89ec07e --- /dev/null +++ b/test/units/TEST-07-PID1.issue-31752.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Make sure NeedDaemonReload= considers newly created drop-ins. +# Issue: https://github.com/systemd/systemd/issues/31752 + +UNIT=test-issue-31752.service + +cleanup() { + rm -rf /run/systemd/system/"$UNIT" /run/systemd/system/"$UNIT".d + systemctl daemon-reload +} + +trap cleanup EXIT + +cat > /run/systemd/system/"$UNIT" < /run/systemd/system/"$UNIT".d/desc.conf <>/skipped + echo "Missing systemd-udev-load-credentials.service" exit 0 fi diff --git a/test/units/TEST-19-CGROUP.delegate.sh b/test/units/TEST-19-CGROUP.delegate.sh index 022515f..7f3a705 100755 --- a/test/units/TEST-19-CGROUP.delegate.sh +++ b/test/units/TEST-19-CGROUP.delegate.sh @@ -5,6 +5,8 @@ set -o pipefail # Test cgroup delegation in the unified hierarchy +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh # shellcheck source=test/units/util.sh . "$(dirname "$0")"/util.sh @@ -13,104 +15,109 @@ if [[ "$(get_cgroup_hierarchy)" != unified ]]; then exit 0 fi -at_exit() { - set +e - userdel -r test +testcase_controllers() { + systemd-run --wait \ + --unit=test-0.service \ + --property="DynamicUser=1" \ + --property="Delegate=" \ + test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ + -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \ + -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control + + systemd-run --wait \ + --unit=test-1.service \ + --property="DynamicUser=1" \ + --property="Delegate=memory pids" \ + grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers + + systemd-run --wait \ + --unit=test-2.service \ + --property="DynamicUser=1" \ + --property="Delegate=memory pids" \ + grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers + + # "io" is not among the controllers enabled by default for all units, verify that + grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers + + # Run a service with "io" enabled, and verify it works + systemd-run --wait \ + --unit=test-3.service \ + --property="IOAccounting=yes" \ + --property="Slice=system-foo-bar-baz.slice" \ + grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers + + # We want to check if "io" is removed again from the controllers + # list. However, PID 1 (rightfully) does this asynchronously. In order + # to force synchronization on this, let's start a short-lived service + # which requires PID 1 to refresh the cgroup tree, so that we can + # verify that this all works. + systemd-run --wait --unit=test-4.service true + + # And now check again, "io" should have vanished + grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers } -systemd-run --wait \ - --unit=test-0.service \ - --property="DynamicUser=1" \ - --property="Delegate=" \ - test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ - -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \ - -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control +testcase_attributes() { + # Test if delegation also works for some of the more recent attrs the kernel might or might not support + for attr in cgroup.threads memory.oom.group memory.reclaim ; do + if grep -q "$attr" /sys/kernel/cgroup/delegate ; then + systemd-run --wait \ + --unit=test-0.service \ + --property="MemoryAccounting=1" \ + --property="DynamicUser=1" \ + --property="Delegate=" \ + test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ + -w /sys/fs/cgroup/system.slice/test-0.service/"$attr" + fi + done +} -# Test if this also works for some of the more recent attrs the kernel might or might not support -for attr in cgroup.threads memory.oom.group memory.reclaim ; do +testcase_scope_unpriv_delegation() { + # Check that unprivileged delegation works for scopes + useradd test + trap "userdel -r test" RETURN + systemd-run --uid=test \ + --property="User=test" \ + --property="Delegate=yes" \ + --slice workload.slice \ + --unit test-workload0.scope\ + --scope \ + test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \ + -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \ + -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control +} - if grep -q "$attr" /sys/kernel/cgroup/delegate ; then +testcase_subgroup() { + # Verify that DelegateSubgroup= affects ownership correctly + unit="test-subgroup-$RANDOM.service" + systemd-run --wait \ + --unit="$unit" \ + --property="DynamicUser=1" \ + --property="Delegate=pids" \ + --property="DelegateSubgroup=foo" \ + test -w "/sys/fs/cgroup/system.slice/$unit" -a \ + -w "/sys/fs/cgroup/system.slice/$unit/foo" + + # Check that for the subgroup also attributes that aren't covered by + # regular (i.e. main cgroup) delegation ownership rules are delegated properly + if test -f /sys/fs/cgroup/cgroup.max.depth; then + unit="test-subgroup-$RANDOM.service" systemd-run --wait \ - --unit=test-0.service \ - --property="MemoryAccounting=1" \ + --unit="$unit" \ --property="DynamicUser=1" \ - --property="Delegate=" \ - test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ - -w /sys/fs/cgroup/system.slice/test-0.service/"$attr" + --property="Delegate=pids" \ + --property="DelegateSubgroup=zzz" \ + test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth" fi -done - -systemd-run --wait \ - --unit=test-1.service \ - --property="DynamicUser=1" \ - --property="Delegate=memory pids" \ - grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers - -systemd-run --wait \ - --unit=test-2.service \ - --property="DynamicUser=1" \ - --property="Delegate=memory pids" \ - grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers - -# "io" is not among the controllers enabled by default for all units, verify that -grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers - -# Run a service with "io" enabled, and verify it works -systemd-run --wait \ - --unit=test-3.service \ - --property="IOAccounting=yes" \ - --property="Slice=system-foo-bar-baz.slice" \ - grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers - -# We want to check if "io" is removed again from the controllers -# list. However, PID 1 (rightfully) does this asynchronously. In order -# to force synchronization on this, let's start a short-lived service -# which requires PID 1 to refresh the cgroup tree, so that we can -# verify that this all works. -systemd-run --wait --unit=test-4.service true - -# And now check again, "io" should have vanished -grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers - -# Check that unprivileged delegation works for scopes -useradd test ||: -systemd-run --uid=test \ - --property="User=test" \ - --property="Delegate=yes" \ - --slice workload.slice \ - --unit test-workload0.scope\ - --scope \ - test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \ - -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \ - -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control - -# Verify that DelegateSubgroup= affects ownership correctly -unit="test-subgroup-$RANDOM.service" -systemd-run --wait \ - --unit="$unit" \ - --property="DynamicUser=1" \ - --property="Delegate=pids" \ - --property="DelegateSubgroup=foo" \ - test -w "/sys/fs/cgroup/system.slice/$unit" -a \ - -w "/sys/fs/cgroup/system.slice/$unit/foo" - -# Check that for the subgroup also attributes that aren't covered by -# regular (i.e. main cgroup) delegation ownership rules are delegated properly -if test -f /sys/fs/cgroup/cgroup.max.depth; then + + # Check that the invoked process itself is also in the subgroup unit="test-subgroup-$RANDOM.service" systemd-run --wait \ --unit="$unit" \ --property="DynamicUser=1" \ --property="Delegate=pids" \ - --property="DelegateSubgroup=zzz" \ - test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth" -fi + --property="DelegateSubgroup=bar" \ + grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup +} -# Check that the invoked process itself is also in the subgroup -unit="test-subgroup-$RANDOM.service" -systemd-run --wait \ - --unit="$unit" \ - --property="DynamicUser=1" \ - --property="Delegate=pids" \ - --property="DelegateSubgroup=bar" \ - grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup +run_testcases diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh index fe410d5..89d6dcd 100755 --- a/test/units/TEST-54-CREDS.sh +++ b/test/units/TEST-54-CREDS.sh @@ -273,8 +273,11 @@ rm -rf /tmp/ts54-creds # Check that globs work as expected mkdir -p /run/credstore echo -n a >/run/credstore/test.creds.first -echo -n b >/run/credstore/test.creds.second +# Make sure that when multiple credentials of the same name are found, the first one is used (/etc/credstore +# is searched before /run/credstore). +echo -n ignored >/run/credstore/test.creds.second mkdir -p /etc/credstore +echo -n b >/etc/credstore/test.creds.second echo -n c >/etc/credstore/test.creds.third systemd-run -p "ImportCredential=test.creds.*" \ --unit=test-54-ImportCredential.service \ diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh index 743baad..f8c22ab 100755 --- a/test/units/TEST-58-REPART.sh +++ b/test/units/TEST-58-REPART.sh @@ -358,14 +358,14 @@ label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD device: $imgs/zzz unit: sectors first-lba: 2048 -last-lba: 6389726 +last-lba: 6422494 $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\"" +$imgs/zzz7 : start= 6291416, size= 131072, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name=\"luks-format-copy\"" if systemd-detect-virt --quiet --container; then echo "Skipping encrypt mount tests in container." @@ -566,8 +566,8 @@ EOF output=$(sfdisk --dump "$imgs/zzz") 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" + assert_in "$imgs/zzz2 : start= 22528, size= 65536, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output" + assert_in "$imgs/zzz3 : start= 88064, size= 65536, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" "$output" if systemd-detect-virt --quiet --container; then echo "Skipping second part of copy blocks tests in container." diff --git a/test/units/TEST-70-TPM2.cryptsetup.sh b/test/units/TEST-70-TPM2.cryptsetup.sh index cb7c8b1..b5dd4df 100755 --- a/test/units/TEST-70-TPM2.cryptsetup.sh +++ b/test/units/TEST-70-TPM2.cryptsetup.sh @@ -210,7 +210,7 @@ Format=ext4 CopyFiles=/tmp/dditest:/ Encrypt=tpm2 EOF - PASSWORD=passphrase systemd-repart --tpm2-device-key=/tmp/srk.pub --definitions=/tmp/dditest --empty=create --size=50M /tmp/dditest.raw --tpm2-pcrs= + PASSWORD=passphrase systemd-repart --tpm2-device-key=/tmp/srk.pub --definitions=/tmp/dditest --empty=create --size=80M /tmp/dditest.raw --tpm2-pcrs= DEVICE="$(systemd-dissect --attach /tmp/dditest.raw)" udevadm wait --settle --timeout=10 "$DEVICE"p1 systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes diff --git a/test/units/TEST-70-TPM2.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh index fd51161..10fa7a9 100755 --- a/test/units/TEST-70-TPM2.pcrlock.sh +++ b/test/units/TEST-70-TPM2.pcrlock.sh @@ -89,6 +89,11 @@ systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2- systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless systemd-cryptsetup detach pcrlock +# Ensure systemd-pcrlock not crashing on empty variant directory +mkdir -p /var/lib/pcrlock.d/123-empty.pcrlock.d +"$SD_PCRLOCK" predict --pcr="$PCRS" +rm -rf /var/lib/pcrlock.d/123-empty.pcrlock.d + # Measure something into PCR 16 (the "debug" PCR), which should make the activation fail "$SD_PCREXTEND" --pcr=16 test70 diff --git a/test/units/TEST-73-LOCALE.sh b/test/units/TEST-73-LOCALE.sh index 06c8c56..0617bd0 100755 --- a/test/units/TEST-73-LOCALE.sh +++ b/test/units/TEST-73-LOCALE.sh @@ -666,14 +666,9 @@ cat >/etc/dbus-1/system.d/systemd-localed-read-only.conf < - - - - - - - - + + + EOF diff --git a/test/units/TEST-74-AUX-UTILS.firstboot.sh b/test/units/TEST-74-AUX-UTILS.firstboot.sh index 7bab009..d9e5f59 100755 --- a/test/units/TEST-74-AUX-UTILS.firstboot.sh +++ b/test/units/TEST-74-AUX-UTILS.firstboot.sh @@ -14,6 +14,7 @@ fi at_exit() { if [[ -n "${ROOT:-}" ]]; then ls -lR "$ROOT" + grep -r . "$ROOT/etc" || : rm -fr "$ROOT" fi @@ -83,15 +84,42 @@ grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id" rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" systemd-firstboot --root="$ROOT" --root-password=foo grep -q "^root:x:0:0:" "$ROOT/etc/passwd" -grep -q "^root:" "$ROOT/etc/shadow" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" echo "foo" >root.passwd systemd-firstboot --root="$ROOT" --root-password-file=root.passwd grep -q "^root:x:0:0:" "$ROOT/etc/passwd" -grep -q "^root:" "$ROOT/etc/shadow" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd -# Set the shell together with the password, as firstboot won't touch -# /etc/passwd if it already exists +# Make sure the root password is set if /etc/passwd and /etc/shadow exist but +# don't have a root entry. +touch "$ROOT/etc/passwd" "$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password=foo +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +# If /etc/passwd and /etc/shadow exist, they will only be updated if the shadow +# password is !unprovisioned. +echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd" +echo "root:!test:::::::" >"$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password=foo +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:!test:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd" +echo "root:!unprovisioned:::::::" >"$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password=foo +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:[^!*]" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-shell=/bin/fooshell +grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" +grep -q "^root:!\*:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" @@ -176,8 +204,9 @@ mkdir -p "$ROOT/bin" touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell" # Temporarily disable pipefail to avoid `echo: write error: Broken pipe set +o pipefail -# We can do only limited testing here, since it's all an interactive stuff, -# so --prompt and --prompt-root-password are skipped on purpose +# We can do only limited testing here, since it's all an interactive stuff, so +# --prompt is skipped on purpose and only limited --prompt-root-password +# testing can be done. echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale grep -q "LANG=foo" "$ROOT$LOCALE_PATH" grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" @@ -193,6 +222,11 @@ echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezon readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname grep -q "foobar" "$ROOT/etc/hostname" +# With no root password provided, a locked account should be created. +systemd-firstboot --root="$ROOT" --prompt-root-password