summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/README.testsuite54
-rwxr-xr-xtest/TEST-04-JOURNAL/test.sh2
-rwxr-xr-xtest/TEST-46-HOMED/test.sh2
-rw-r--r--test/TEST-54-CREDS/meson.build12
-rwxr-xr-xtest/TEST-74-AUX-UTILS/test.sh2
-rw-r--r--test/fuzz/fuzz-systemctl-parse-argv/oss-fuzz-70153bin0 -> 67 bytes
-rwxr-xr-xtest/integration-test-setup.sh57
-rwxr-xr-xtest/integration-test-wrapper.py33
-rw-r--r--test/meson.build8
-rw-r--r--test/test-functions6
-rw-r--r--test/test-sysusers/test-16.expected-group1
-rw-r--r--test/test-sysusers/test-16.expected-passwd2
-rw-r--r--test/test-sysusers/test-16.input7
-rw-r--r--test/test.service.in3
-rwxr-xr-xtest/units/TEST-04-JOURNAL.bsod.sh4
-rwxr-xr-xtest/units/TEST-04-JOURNAL.stopped-socket-activation.sh10
-rwxr-xr-xtest/units/TEST-07-PID1.issue-31752.sh44
-rwxr-xr-xtest/units/TEST-09-REBOOT.sh4
-rwxr-xr-xtest/units/TEST-17-UDEV.credentials.sh2
-rwxr-xr-xtest/units/TEST-19-CGROUP.delegate.sh183
-rwxr-xr-xtest/units/TEST-54-CREDS.sh5
-rwxr-xr-xtest/units/TEST-58-REPART.sh8
-rwxr-xr-xtest/units/TEST-70-TPM2.cryptsetup.sh2
-rwxr-xr-xtest/units/TEST-70-TPM2.pcrlock.sh5
-rwxr-xr-xtest/units/TEST-73-LOCALE.sh11
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.firstboot.sh79
-rwxr-xr-xtest/units/TEST-82-SOFTREBOOT.sh15
27 files changed, 421 insertions, 140 deletions
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
--- /dev/null
+++ b/test/fuzz/fuzz-systemctl-parse-argv/oss-fuzz-70153
Binary files 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" <<EOF
+[Service]
+ExecStart=/usr/bin/true
+RemainAfterExit=yes
+EOF
+
+systemctl daemon-reload
+systemctl start "$UNIT"
+assert_eq "$(systemctl show -P NeedDaemonReload "$UNIT")" no
+
+mkdir /run/systemd/system/"$UNIT".d
+cat > /run/systemd/system/"$UNIT".d/desc.conf <<EOF
+[Unit]
+Description=Test NeedDaemonReload status after creating drop-in
+EOF
+
+assert_eq "$(systemctl show -P NeedDaemonReload "$UNIT")" yes
+
+rm /run/systemd/system/"$UNIT".d/desc.conf
+
+assert_eq "$(systemctl show -P NeedDaemonReload "$UNIT")" no
diff --git a/test/units/TEST-09-REBOOT.sh b/test/units/TEST-09-REBOOT.sh
index 85630b6..014ea31 100755
--- a/test/units/TEST-09-REBOOT.sh
+++ b/test/units/TEST-09-REBOOT.sh
@@ -17,7 +17,11 @@ systemd-cat journalctl --list-boots
run_subtests
if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then
+ SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT=1
+ export SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT
systemctl_final reboot
+ # Now block until the reboot killing spree kills us.
+ exec sleep infinity
elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then
assert_not_reached
fi
diff --git a/test/units/TEST-17-UDEV.credentials.sh b/test/units/TEST-17-UDEV.credentials.sh
index 42d3883..2552dc6 100755
--- a/test/units/TEST-17-UDEV.credentials.sh
+++ b/test/units/TEST-17-UDEV.credentials.sh
@@ -5,7 +5,7 @@ set -eux
set -o pipefail
if [[ $(systemctl is-enabled systemd-udev-load-credentials.service) == not-found ]]; then
- echo "Missing systemd-udev-load-credentials.service" >>/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
"https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
- <allow send_member="SetLocale"/>
- <allow send_member="SetVConsoleKeyboard"/>
- <allow send_member="SetX11Keyboard"/>
- </policy>
- <policy context="default">
- <allow send_member="SetLocale"/>
- <allow send_member="SetVConsoleKeyboard"/>
- <allow send_member="SetX11Keyboard"/>
+ <allow send_destination="org.freedesktop.locale1" send_interface="org.freedesktop.locale1" send_member="SetLocale"/>
+ <allow send_destination="org.freedesktop.locale1" send_interface="org.freedesktop.locale1" send_member="SetVConsoleKeyboard"/>
+ <allow send_destination="org.freedesktop.locale1" send_interface="org.freedesktop.locale1" send_member="SetX11Keyboard"/>
</policy>
</busconfig>
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 </dev/null
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:!\*:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
# Existing files should not get overwritten
@@ -204,15 +238,46 @@ grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
# Re-enable pipefail
set -o pipefail
+# --prompt-* options with credentials. Unfortunately, with --root the
+# --systemd.firstboot kernel command line option is ignored, so that can't be
+# --tested.
+rm -fr "$ROOT"
+mkdir -p "$ROOT/bin"
+touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
+systemd-run --wait --pipe --service-type=exec \
+ -p SetCredential=firstboot.locale:foo \
+ -p SetCredential=firstboot.locale-messages:bar \
+ -p SetCredential=firstboot.keymap:foo \
+ -p SetCredential=firstboot.timezone:Europe/Berlin \
+ -p SetCredential=passwd.hashed-password.root:"$ROOT_HASHED_PASSWORD1" \
+ -p SetCredential=passwd.shell.root:/bin/fooshell \
+ systemd-firstboot \
+ --root="$ROOT" \
+ --prompt-locale \
+ --prompt-keymap \
+ --prompt-timezone \
+ --prompt-root-password \
+ --prompt-root-shell \
+ </dev/null
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
+grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
+readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
+grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
+
# Assorted tests
rm -fr "$ROOT"
mkdir "$ROOT"
systemd-firstboot --root="$ROOT" --setup-machine-id
grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id"
+rm -fv "$ROOT/etc/machine-id"
systemd-firstboot --root="$ROOT" --delete-root-password
-diff <(echo) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
+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-shell=/bin/nonexistentshell)
(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid)
diff --git a/test/units/TEST-82-SOFTREBOOT.sh b/test/units/TEST-82-SOFTREBOOT.sh
index f86bc92..9f3d406 100755
--- a/test/units/TEST-82-SOFTREBOOT.sh
+++ b/test/units/TEST-82-SOFTREBOOT.sh
@@ -19,6 +19,21 @@ at_exit() {
trap at_exit EXIT
+# Because this test tests soft-reboot, we have to get rid of the symlink we put at
+# /run/nextroot to allow rebooting into the previous snapshot if the test fails for
+# the duration of the test. However, let's make sure we put the symlink back in place
+# if the test fails.
+if [[ -L /run/nextroot ]]; then
+ at_error() {
+ mountpoint -q /run/nextroot && umount -R /run/nextroot
+ rm -rf /run/nextroot
+ ln -sf /snapshot /run/nextroot
+ }
+
+ trap at_error ERR
+ rm -f /run/nextroot
+fi
+
systemd-analyze log-level debug
export SYSTEMD_LOG_LEVEL=debug