diff options
Diffstat (limited to 'test')
40 files changed, 1299 insertions, 459 deletions
diff --git a/test/README.testsuite b/test/README.testsuite index 60dc03498b..8cacbb40af 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -202,10 +202,6 @@ systemd-nspawn. `TEST_NO_KVM=1`: Disable qemu KVM auto-detection (may be necessary when you're trying to run the *vanilla* qemu and have both qemu and qemu-kvm installed) -`TEST_NESTED_KVM=1`: Allow tests to run with nested KVM. By default, the -testsuite disables nested KVM if the host machine already runs under KVM. -Setting this variable disables such checks. - `QEMU_MEM=512M`: Configure amount of memory for qemu VMs (defaults to 512M). `QEMU_SMP=1`: Configure number of CPUs for qemu VMs (defaults to 1). diff --git a/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure b/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure index 6e8e3124ba..f29677ce66 100755 --- a/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure +++ b/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure @@ -26,6 +26,6 @@ for bridge in range(1, 26): f"pci-bridge,id=pci_bridge{bridge},bus=pci_bridge{bridge - 1},chassis_nr={64 + bridge},addr=1", ] -config["QemuArgs"] += ["-device", f"virtio-blk-pci,drive=drive0,scsi=off,bus=pci_bridge25,addr=1"] +config["QemuArgs"] += ["-device", f"virtio-blk-pci,drive=drive0,bus=pci_bridge25,addr=1"] json.dump(config, sys.stdout) diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index b5a70cafaf..2fd7bf2bfb 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -464,7 +464,7 @@ testcase_long_sysfs_path() { qemu_opts+=("-device pci-bridge,id=pci_bridge$brid,bus=pci_bridge$((brid-1)),chassis_nr=$((64+brid))") done - qemu_opts+=("-device virtio-blk-pci,drive=drive0,scsi=off,bus=pci_bridge$brid") + qemu_opts+=("-device virtio-blk-pci,drive=drive0,bus=pci_bridge$brid") KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}" QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}" diff --git a/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service new file mode 100644 index 0000000000..a754626828 --- /dev/null +++ b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service @@ -0,0 +1,6 @@ +[Unit] +Description=Testing systemd timers + +[Service] +Type=simple +ExecStart=sh -c 'date +%%s >>/tmp/realtime-test.log ; sleep 5' diff --git a/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer new file mode 100644 index 0000000000..b870b41628 --- /dev/null +++ b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Testing systemd timers + +[Timer] +OnCalendar=*:*:0/5 +AccuracySec=1us +DeferReactivation=true + +[Install] +WantedBy=timers.target diff --git a/test/TEST-74-AUX-UTILS/meson.build b/test/TEST-74-AUX-UTILS/meson.build index 43a733ee64..543eee195f 100644 --- a/test/TEST-74-AUX-UTILS/meson.build +++ b/test/TEST-74-AUX-UTILS/meson.build @@ -4,5 +4,8 @@ integration_tests += [ integration_test_template + { 'name' : fs.name(meson.current_source_dir()), 'storage': 'persistent', + 'vm' : true, }, ] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-74-AUX-UTILS.units'] diff --git a/test/TEST-86-MULTI-PROFILE-UKI/Makefile b/test/TEST-86-MULTI-PROFILE-UKI/Makefile new file mode 100644 index 0000000000..653f16163f --- /dev/null +++ b/test/TEST-86-MULTI-PROFILE-UKI/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +all setup run clean clean-again: + true + +.PHONY: all setup run clean clean-again diff --git a/test/TEST-86-MULTI-PROFILE-UKI/meson.build b/test/TEST-86-MULTI-PROFILE-UKI/meson.build new file mode 100644 index 0000000000..10d5957d8f --- /dev/null +++ b/test/TEST-86-MULTI-PROFILE-UKI/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'storage' : 'persistent', + 'vm' : true, + 'firmware' : 'auto', + 'enabled' : false, + }, +] diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-372994449 b/test/fuzz/fuzz-network-parser/oss-fuzz-372994449 Binary files differnew file mode 100644 index 0000000000..76c3a4903c --- /dev/null +++ b/test/fuzz/fuzz-network-parser/oss-fuzz-372994449 diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service index dbd56ec752..1cb212bcad 100644 --- a/test/fuzz/fuzz-unit-file/directives-all.service +++ b/test/fuzz/fuzz-unit-file/directives-all.service @@ -7,6 +7,7 @@ AllowedCPUs= AllowedMemoryNodes= AllowIsolate= Also= +DeferReactivation= AmbientCapabilities= AssertACPower= AssertArchitecture= diff --git a/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer b/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer index 5bf91b9f4c..5dc269243f 100644 --- a/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer +++ b/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer @@ -33,6 +33,7 @@ Persistent=true AccuracySec=24h RandomizedDelaySec=234234234 FixedRandomDelay=true +DeferReactivation=true Persistent=no Unit=foo.service diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py index a8004b3815..a680ddee53 100755 --- a/test/integration-test-wrapper.py +++ b/test/integration-test-wrapper.py @@ -61,9 +61,10 @@ 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) + for s in os.getenv("TEST_SKIP", "").split(): + if s in args.name: + 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"))) @@ -174,6 +175,14 @@ def main(): result = subprocess.run(cmd) + # On Debian/Ubuntu we get a lot of random QEMU crashes. Retry once, and then skip if it fails again. + if args.vm and result.returncode == 247 and args.exit_code != 247: + journal_file.unlink(missing_ok=True) + result = subprocess.run(cmd) + if args.vm and result.returncode == 247 and args.exit_code != 247: + print(f"Test {args.name} failed due to QEMU crash (error 247), ignoring", file=sys.stderr) + exit(77) + 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) diff --git a/test/meson.build b/test/meson.build index 6acff37508..9d1a069fc9 100644 --- a/test/meson.build +++ b/test/meson.build @@ -376,6 +376,7 @@ foreach dirname : [ 'TEST-83-BTRFS', 'TEST-84-STORAGETM', 'TEST-85-NETWORK', + 'TEST-86-MULTI-PROFILE-UKI', ] subdir(dirname) endforeach diff --git a/test/test-compare-versions.sh b/test/test-compare-versions.sh index c40208be60..e9acd9cbee 100755 --- a/test/test-compare-versions.sh +++ b/test/test-compare-versions.sh @@ -4,32 +4,32 @@ set -e ANALYZE="${1:-systemd-analyze}" -$ANALYZE compare-versions 1 lt 2 -$ANALYZE compare-versions 1 '<' 2 -$ANALYZE compare-versions 1 le 2 -$ANALYZE compare-versions 1 '<=' 2 -$ANALYZE compare-versions 1 ne 2 -$ANALYZE compare-versions 1 '!=' 2 -( ! $ANALYZE compare-versions 1 ge 2 ) -( ! $ANALYZE compare-versions 1 '>=' 2 ) -( ! $ANALYZE compare-versions 1 eq 2 ) -( ! $ANALYZE compare-versions 1 '==' 2 ) -( ! $ANALYZE compare-versions 1 gt 2 ) -( ! $ANALYZE compare-versions 1 '>' 2 ) +"$ANALYZE" compare-versions 1 lt 2 +"$ANALYZE" compare-versions 1 '<' 2 +"$ANALYZE" compare-versions 1 le 2 +"$ANALYZE" compare-versions 1 '<=' 2 +"$ANALYZE" compare-versions 1 ne 2 +"$ANALYZE" compare-versions 1 '!=' 2 +( ! "$ANALYZE" compare-versions 1 ge 2 ) +( ! "$ANALYZE" compare-versions 1 '>=' 2 ) +( ! "$ANALYZE" compare-versions 1 eq 2 ) +( ! "$ANALYZE" compare-versions 1 '==' 2 ) +( ! "$ANALYZE" compare-versions 1 gt 2 ) +( ! "$ANALYZE" compare-versions 1 '>' 2 ) -test "$($ANALYZE compare-versions 1 2)" = '1 < 2' -test "$($ANALYZE compare-versions 2 2)" = '2 == 2' -test "$($ANALYZE compare-versions 2 1)" = '2 > 1' -test "$($ANALYZE compare-versions '' '')" = "'' == ''" +test "$("$ANALYZE" compare-versions 1 2)" = '1 < 2' +test "$("$ANALYZE" compare-versions 2 2)" = '2 == 2' +test "$("$ANALYZE" compare-versions 2 1)" = '2 > 1' +test "$("$ANALYZE" compare-versions '' '')" = "'' == ''" set +e -$ANALYZE compare-versions 1 2; ret1=$? -$ANALYZE compare-versions 2 2; ret2=$? -$ANALYZE compare-versions 2 1; ret3=$? +"$ANALYZE" compare-versions 1 2; ret1=$? +"$ANALYZE" compare-versions 2 2; ret2=$? +"$ANALYZE" compare-versions 2 1; ret3=$? set -e -test $ret1 == 12 -test $ret2 == 0 -test $ret3 == 11 +test "$ret1" == 12 +test "$ret2" == 0 +test "$ret3" == 11 diff --git a/test/test-fstab-generator.sh b/test/test-fstab-generator.sh index af8fa7c226..24a2533f16 100755 --- a/test/test-fstab-generator.sh +++ b/test/test-fstab-generator.sh @@ -44,9 +44,9 @@ test_one() ( fi if [[ "${input##*/}" =~ \.fstab\.input ]]; then - SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=yes root=fstab" SYSTEMD_FSTAB="$input" SYSTEMD_SYSROOT_FSTAB="/dev/null" $generator "$out" "$out" "$out" + SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=yes root=fstab" SYSTEMD_FSTAB="$input" SYSTEMD_SYSROOT_FSTAB="/dev/null" "$generator" "$out" "$out" "$out" else - SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=no $(cat "$input")" $generator "$out" "$out" "$out" + SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=no $(cat "$input")" "$generator" "$out" "$out" "$out" fi # The option x-systemd.growfs creates symlink to system's systemd-growfs@.service in .mount.wants directory. diff --git a/test/test-functions b/test/test-functions index 64a664b69e..8a4cc55626 100644 --- a/test/test-functions +++ b/test/test-functions @@ -84,14 +84,12 @@ add_at_exit_handler() { } # Decide if we can (and want to) run qemu with KVM acceleration. -# Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not, -# check if it's not explicitly disabled (TEST_NO_KVM) and we're not already -# running under KVM. If these conditions are met, enable KVM (and possibly -# nested KVM), otherwise disable it. -if get_bool "${TEST_NESTED_KVM:=}" || (! get_bool "${TEST_NO_KVM:=}" && ! systemd-detect-virt -qv); then - QEMU_KVM=yes -else +# Check if KVM is not explicitly disabled (TEST_NO_KVM), otherwise +# enable KVM (and possibly nested KVM). +if get_bool "${TEST_NO_KVM:=}"; then QEMU_KVM=no +else + QEMU_KVM=yes fi if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then @@ -213,6 +211,7 @@ BASICTOOLS=( ping pkill ps + pwd readlink realpath rev diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index a596537ffa..687fda7b3d 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -8034,6 +8034,38 @@ class NetworkdMTUTests(unittest.TestCase, Utilities): copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf') self.check_mtu('1600', '1550', reset=False) +class NetworkdSysctlTest(unittest.TestCase, Utilities): + + def setUp(self): + setup_common() + + def tearDown(self): + tear_down_common() + + @unittest.skipUnless(compare_kernel_version("6.12"), reason="On kernels <= 6.12, bpf_current_task_under_cgroup() isn't available for program types BPF_PROG_TYPE_CGROUP_SYSCTL") + def check_sysctl_watch(self): + copy_network_unit('12-dummy.network', '12-dummy.netdev', '12-dummy.link') + start_networkd() + + self.wait_online('dummy98:routable') + + # Change managed sysctls + call('sysctl -w net.ipv6.conf.dummy98.accept_ra=1') + call('sysctl -w net.ipv6.conf.dummy98.mtu=1360') + call('sysctl -w net.ipv4.conf.dummy98.promote_secondaries=0') + call('sysctl -w net.ipv6.conf.dummy98.proxy_ndp=1') + + # And unmanaged ones + call('sysctl -w net.ipv6.conf.dummy98.hop_limit=4') + call('sysctl -w net.ipv6.conf.dummy98.max_addresses=10') + + log=read_networkd_log() + self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/accept_ra' from '0' to '1', conflicting with our setting to '0'") + self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/mtu' from '1550' to '1360', conflicting with our setting to '1550'") + self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv4/conf/dummy98/promote_secondaries' from '1' to '0', conflicting with our setting to '1'") + self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/proxy_ndp' from '0' to '1', conflicting with our setting to '0'") + self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/hop_limit'", log) + self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'", log) if __name__ == '__main__': parser = argparse.ArgumentParser() diff --git a/test/test-sysusers.sh.in b/test/test-sysusers.sh.in index 9af253f6c7..fdeae2c898 100755 --- a/test/test-sysusers.sh.in +++ b/test/test-sysusers.sh.in @@ -53,7 +53,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do echo "*** Running $f" prepare_testdir "${f%.input}" cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf" - $SYSUSERS --root="$TESTDIR" + "$SYSUSERS" --root="$TESTDIR" compare "${f%.*}" "" done @@ -62,7 +62,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do echo "*** Running $f on stdin" prepare_testdir "${f%.input}" touch "$TESTDIR/etc/sysusers.d/test.conf" - $SYSUSERS --root="$TESTDIR" - <"$f" + "$SYSUSERS" --root="$TESTDIR" - <"$f" compare "${f%.*}" "on stdin" done @@ -72,9 +72,9 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do prepare_testdir "${f%.input}" touch "$TESTDIR/etc/sysusers.d/test.conf" # this overrides test.conf which is masked on disk - $SYSUSERS --root="$TESTDIR" --replace=/etc/sysusers.d/test.conf - <"$f" + "$SYSUSERS" --root="$TESTDIR" --replace=/etc/sysusers.d/test.conf - <"$f" # this should be ignored - $SYSUSERS --root="$TESTDIR" --replace=/usr/lib/sysusers.d/test.conf - <"$SOURCE/test-1.input" + "$SYSUSERS" --root="$TESTDIR" --replace=/usr/lib/sysusers.d/test.conf - <"$SOURCE/test-1.input" compare "${f%.*}" "on stdin with --replace" done @@ -84,9 +84,9 @@ echo "*** Testing --inline" prepare_testdir "$SOURCE/inline" # copy a random file to make sure it is ignored cp "$f" "$TESTDIR/etc/sysusers.d/confuse.conf" -$SYSUSERS --root="$TESTDIR" --inline \ - "u u1 222 - - /bin/zsh" \ - "g g1 111" +"$SYSUSERS" --root="$TESTDIR" --inline \ + "u u1 222 - - /bin/zsh" \ + "g g1 111" compare "$SOURCE/inline" "(--inline)" @@ -95,19 +95,19 @@ echo "*** Testing --inline with --replace" prepare_testdir "$SOURCE/inline" # copy a random file to make sure it is ignored cp "$f" "$TESTDIR/etc/sysusers.d/confuse.conf" -$SYSUSERS --root="$TESTDIR" \ - --inline \ - --replace=/etc/sysusers.d/confuse.conf \ - "u u1 222 - - /bin/zsh" \ - "g g1 111" +"$SYSUSERS" --root="$TESTDIR" \ + --inline \ + --replace=/etc/sysusers.d/confuse.conf \ + "u u1 222 - - /bin/zsh" \ + "g g1 111" compare "$SOURCE/inline" "(--inline --replace=…)" echo "*** Testing --inline with no /etc" rm -rf "${TESTDIR:?}/etc" -$SYSUSERS --root="$TESTDIR" --inline \ - "u u1 222 - - /bin/zsh" \ - "g g1 111" +"$SYSUSERS" --root="$TESTDIR" --inline \ + "u u1 222 - - /bin/zsh" \ + "g g1 111" compare "$SOURCE/inline" "(--inline)" @@ -136,7 +136,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do echo "*** Running $f (with login.defs)" prepare_testdir "${f%.input}" cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf" - $SYSUSERS --root="$TESTDIR" + "$SYSUSERS" --root="$TESTDIR" # shellcheck disable=SC2050 [ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max @@ -152,7 +152,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do echo "*** Running $f (with login.defs symlinked)" prepare_testdir "${f%.input}" cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf" - $SYSUSERS --root="$TESTDIR" + "$SYSUSERS" --root="$TESTDIR" # shellcheck disable=SC2050 [ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max @@ -166,7 +166,7 @@ for f in $(find "$SOURCE"/unhappy-*.input | sort -V); do echo "*** Running test $f" prepare_testdir "${f%.input}" cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf" - SYSTEMD_LOG_LEVEL=info $SYSUSERS --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err" + SYSTEMD_LOG_LEVEL=info "$SYSUSERS" --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err" if ! diff -u "$TESTDIR/err" "${f%.*}.expected-err"; then echo >&2 "**** Unexpected error output for $f" cat >&2 "$TESTDIR/err" diff --git a/test/test-udev.py b/test/test-udev.py index d9d840eb8c..68c48fd790 100755 --- a/test/test-udev.py +++ b/test/test-udev.py @@ -2313,6 +2313,17 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \"printf %%s 'foo1 foo2' | grep 'foo1 f SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev" KERNEL=="sda6", OPTIONS+="link_priority=10" """), + + Rules.new( + "case insensitive match", + Device( + "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links = ["ok"], + ), + + rules = r""" + KERNEL==i"SDA1", SUBSYSTEMS==i"SCSI", ATTRS{vendor}==i"a?a", SYMLINK+="ok" + """), ] def fork_and_run_udev(action: str, rules: Rules) -> None: diff --git a/test/units/TEST-13-NSPAWN.machinectl.sh b/test/units/TEST-13-NSPAWN.machined.sh index 7ff953bae9..da62b465ea 100755 --- a/test/units/TEST-13-NSPAWN.machinectl.sh +++ b/test/units/TEST-13-NSPAWN.machined.sh @@ -12,7 +12,7 @@ export PAGER= at_exit() { set +e - machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running + machinectl status long-running &>/dev/null && machinectl kill --signal=KILL long-running mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done" [[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT" rm -f /run/systemd/nspawn/*.nspawn @@ -39,6 +39,7 @@ cat >/var/lib/machines/long-running/sbin/init <<\EOF PID=0 +trap "touch /terminate; kill $PID" RTMIN+3 trap "touch /poweroff" RTMIN+4 trap "touch /reboot" INT trap "touch /trap" TRAP @@ -47,12 +48,31 @@ trap 'kill $PID' EXIT # We need to wait for the sleep process asynchronously in order to allow # bash to process signals sleep infinity & + +# notify that the process is ready +touch /ready + PID=$! while :; do wait || : done EOF -machinectl start long-running + +long_running_machine_start() { + # shellcheck disable=SC2015 + machinectl status long-running &>/dev/null && return 0 || true + + # Ensure the service stopped. + systemctl stop systemd-nspawn@long-running.service 2>/dev/null || : + + rm -f /var/lib/machines/long-running/ready + machinectl start long-running + # !!!! DO NOT REMOVE THIS TEST + # The test makes sure that the long-running's init script has enough time to start and registered signal traps + timeout 30 bash -c "until test -e /var/lib/machines/long-running/ready; do sleep .5; done" +} + +long_running_machine_start machinectl machinectl --no-pager --help @@ -98,7 +118,14 @@ timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sl rm -f /var/lib/machines/long-running/reboot machinectl reboot long-running timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done" -# Skip machinectl terminate for now, as it doesn't play well with our "init" +# Test for 'machinectl terminate' +rm -f /var/lib/machines/long-running/terminate +machinectl terminate long-running +timeout 10 bash -c "until test -e /var/lib/machines/long-running/terminate; do sleep .5; done" +timeout 10 bash -c "while machinectl status long-running &>/dev/null; do sleep .5; done" +# Restart container +long_running_machine_start +# Test for 'machinectl kill' rm -f /var/lib/machines/long-running/trap machinectl kill --signal=SIGTRAP --kill-whom=leader long-running timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done" @@ -223,5 +250,69 @@ done (! machinectl read-only container1 foo) (! machinectl read-only container1 -- -1) -varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' -varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":".host"}' +#################### +# varlinkctl tests # +# ################## + +long_running_machine_start + +varlinkctl introspect /run/systemd/machine/io.systemd.Machine io.systemd.Machine +varlinkctl introspect /run/systemd/machine/io.systemd.Machine io.systemd.MachineImage +varlinkctl introspect /run/systemd/machine/io.systemd.MachineImage io.systemd.Machine +varlinkctl introspect /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage + +# test io.systemd.Machine.List +varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' | grep 'long-running' +varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' | grep '.host' +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' + +pid=$(varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' | jq '.leader') +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' >/tmp/expected +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"pid\":$pid}" | diff /tmp/expected - +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"long-running\", \"pid\":$pid}" | diff /tmp/expected - +(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"non-existent\", \"pid\":$pid}") +(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":""}') +(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"ah@??.hmm"}') + +# test io.systemd.Machine.Kill +# sending TRAP signal +rm -f /var/lib/machines/long-running/trap +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Kill '{"name":"long-running", "whom": "leader", "signal": 5}' +timeout 30 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done" + +# test io.systemd.Machine.Terminate +long_running_machine_start +rm -f /var/lib/machines/long-running/terminate +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Terminate '{"name":"long-running"}' +timeout 10 bash -c "until test -e /var/lib/machines/long-running/terminate; do sleep .5; done" +timeout 30 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"long-running\"}'; do sleep 0.5; done" + +# test io.systemd.Machine.Register +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Register '{"name": "registered-container", "class": "container"}' +timeout 30 bash -c "until varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"registered-container\"}'; do sleep 0.5; done" + +# test io.systemd.Machine.Unregister +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Unregister '{"name": "registered-container"}' +timeout 30 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"registered-container\"}'; do sleep 0.5; done" + +# test io.systemd.Machine.List with sshAddress and sshPrivateKeyPath fields +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Register '{"name": "registered-container", "class": "container", "sshAddress": "localhost", "sshPrivateKeyPath": "/non-existent"}' +timeout 30 bash -c "until varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"registered-container\"}'; do sleep 0.5; done" +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"registered-container"}' | jq '.sshAddress' | grep -q 'localhost' +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"registered-container"}' | jq '.sshPrivateKeyPath' | grep -q 'non-existent' +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Unregister '{"name": "registered-container"}' + +# test io.systemd.MachineImage.List +varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}' | grep 'long-running' +varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}' | grep '.host' +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running"}' +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running", "acquireMetadata": true}' | grep 'OSRelease' + +# test io.systemd.MachineImage.Update +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.Update '{"name":"long-running", "newName": "long-running-renamed", "readOnly": true}' +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running-renamed"}' +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running-renamed"}' | jq '.readOnly' | grep 'true' + +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.Update '{"name":"long-running-renamed", "newName": "long-running", "readOnly": false}' +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running"}' +varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running"}' | jq '.readOnly' | grep 'false' diff --git a/test/units/TEST-17-UDEV.11.sh b/test/units/TEST-17-UDEV.11.sh index 42b925f60b..8413d3c189 100755 --- a/test/units/TEST-17-UDEV.11.sh +++ b/test/units/TEST-17-UDEV.11.sh @@ -237,6 +237,8 @@ test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.' test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.' test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='." test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set." +test_syntax_error 'ENV{a}=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator." +test_syntax_error 'ENV{a}+=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator." test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.' test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.' test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.' @@ -275,10 +277,12 @@ test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.' test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.' test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.' test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.' +test_syntax_error 'PROGRAM==i"b"' "Invalid prefix 'i' for PROGRAM." test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.' test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.' test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.' test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.' +test_syntax_error 'IMPORT{file}==i"a", NAME="b"' "Invalid prefix 'i' for IMPORT." test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo' test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.' test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.' diff --git a/test/units/TEST-17-UDEV.database.sh b/test/units/TEST-17-UDEV.database.sh new file mode 100755 index 0000000000..2b66333cad --- /dev/null +++ b/test/units/TEST-17-UDEV.database.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +udevadm control --log-level=debug + +IFNAME=test-udev-aaa +ip link add "$IFNAME" type dummy +IFINDEX=$(ip -json link show "$IFNAME" | jq '.[].ifindex') +udevadm wait --timeout 10 "/sys/class/net/$IFNAME" +# Check if the database file is created. +[[ -e "/run/udev/data/n$IFINDEX" ]] + +ip link del "$IFNAME" +udevadm wait --timeout 10 --removed --settle "/sys/class/net/$IFNAME" +# CHeck if the database file is removed. +[[ ! -e "/run/udev/data/n$IFINDEX" ]] + +udevadm control --log-level=info + +exit 0 diff --git a/test/units/TEST-17-UDEV.diskseq.sh b/test/units/TEST-17-UDEV.diskseq.sh new file mode 100755 index 0000000000..53ee666984 --- /dev/null +++ b/test/units/TEST-17-UDEV.diskseq.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2010 +# shellcheck disable=SC2012 +# shellcheck disable=SC2317 +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# This is a test case for issue #34637. + +at_exit() ( + set +e + + systemctl stop test-diskseq.service || : + rm -f /run/systemd/system/test-diskseq.service + systemctl daemon-reload + + [[ -d "$TMPDIR" ]] && rm -rf "$TMPDIR" + + udevadm control --log-level=info +) + +trap at_exit EXIT + +udevadm control --log-level=debug + +TMPDIR="$(mktemp -d)" +truncate -s 16M "$TMPDIR"/foo.raw +mkfs.ext4 -L foo "$TMPDIR"/foo.raw + +mkdir -p /run/systemd/system/ +cat >/run/systemd/system/test-diskseq.service <<EOF +[Unit] +StartLimitIntervalSec=0 +[Service] +ExecStart=false +Restart=on-failure +MountImages=$TMPDIR/foo.raw:/var +EOF +systemctl daemon-reload + +udevadm settle + +# Check if no lock file exists, if the lock directory exists. +if [[ -d /run/udev/links.lock/ ]]; then + [[ "$(ls /run/udev/links.lock/ | wc -l)" == 0 ]] +fi + +# Save the current number of the directories. +NUM_DISKSEQ=$(ls /run/udev/links/ | grep -c by-diskseq || :) + +systemctl start --no-block test-diskseq.service + +for _ in {0..100}; do + sleep .1 + n=$(ls /run/udev/links/ | grep -c by-diskseq || :) + (( n <= NUM_DISKSEQ + 1 )) +done + +systemctl stop test-diskseq.service || : + +udevadm settle + +# Check if the lock directory exists, but no lock file exists in it. +[[ -d /run/udev/links.lock/ ]] +[[ "$(ls /run/udev/links.lock/ | wc -l)" == 0 ]] + +exit 0 diff --git a/test/units/TEST-19-CGROUP.keyed-properties.sh b/test/units/TEST-19-CGROUP.keyed-properties.sh new file mode 100755 index 0000000000..cadefe26d5 --- /dev/null +++ b/test/units/TEST-19-CGROUP.keyed-properties.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +if [[ "$(get_cgroup_hierarchy)" != unified ]]; then + echo "Skipping $0 as we're not running with the unified cgroup hierarchy" + exit 0 +fi + +testcase_iodevice_dbus () { + # Test that per-device properties are applied in configured order even for different devices (because + # they may resolve to same underlying device in the end + # Note: if device does not exist cgroup attribute write fails but systemd should still track the + # configured properties + systemd-run --unit=test0.service \ + --property="IOAccounting=yes" \ + sleep inf + + systemctl set-property test0.service \ + IOReadBandwidthMax="/dev/sda1 1M" \ + IOReadBandwidthMax="/dev/sda2 2M" \ + IOReadBandwidthMax="/dev/sda3 4M" + + local output + output=$(mktemp) + trap 'rm -f "$output"' RETURN + systemctl show -P IOReadBandwidthMax test0.service >"$output" + diff -u "$output" - <<EOF +/dev/sda1 1000000 +/dev/sda2 2000000 +/dev/sda3 4000000 +EOF + + systemctl stop test0.service +} + +testcase_iodevice_unitfile () { + cat >/run/systemd/system/test1.service <<EOF +[Service] +ExecStart=/usr/bin/sleep inf +IOReadBandwidthMax=/dev/sda1 1M +IOReadBandwidthMax=/dev/sda2 2M +IOReadBandwidthMax=/dev/sda3 4M +EOF + systemctl daemon-reload + + local output + output=$(mktemp) + trap 'rm -f "$output"' RETURN + systemctl show -P IOReadBandwidthMax test1.service >"$output" + diff -u "$output" - <<EOF +/dev/sda1 1000000 +/dev/sda2 2000000 +/dev/sda3 4000000 +EOF + rm -f /run/systemd/system/test1.service +} + +run_testcases diff --git a/test/units/TEST-22-TMPFILES.18.sh b/test/units/TEST-22-TMPFILES.18.sh index 5d24197c81..c81f6bd0ef 100755 --- a/test/units/TEST-22-TMPFILES.18.sh +++ b/test/units/TEST-22-TMPFILES.18.sh @@ -9,26 +9,39 @@ set -o pipefail export SYSTEMD_LOG_LEVEL=debug c=' -d /tmp/somedir -f /tmp/somedir/somefile - - - - baz +d$ /tmp/somedir +f$ /tmp/somedir/somefile - - - - baz +f /tmp/someotherfile - - - - qux ' systemd-tmpfiles --create - <<<"$c" test -f /tmp/somedir/somefile grep -q baz /tmp/somedir/somefile +grep -q qux /tmp/someotherfile systemd-tmpfiles --purge --dry-run - <<<"$c" test -f /tmp/somedir/somefile grep -q baz /tmp/somedir/somefile +grep -q qux /tmp/someotherfile systemd-tmpfiles --purge - <<<"$c" test ! -f /tmp/somedir/somefile test ! -d /tmp/somedir/ +grep -q qux /tmp/someotherfile systemd-tmpfiles --create --purge --dry-run - <<<"$c" test ! -f /tmp/somedir/somefile test ! -d /tmp/somedir/ +grep -q qux /tmp/someotherfile systemd-tmpfiles --create --purge - <<<"$c" test -f /tmp/somedir/somefile grep -q baz /tmp/somedir/somefile +grep -q qux /tmp/someotherfile + +systemd-tmpfiles --purge - <<<"$c" +test ! -f /tmp/somedir/somefile +test ! -d /tmp/somedir/ +grep -q qux /tmp/someotherfile + +rm /tmp/someotherfile diff --git a/test/units/TEST-23-UNIT-FILE-ExtraFileDescriptors-child.sh b/test/units/TEST-23-UNIT-FILE-ExtraFileDescriptors-child.sh new file mode 100755 index 0000000000..8b182955b5 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE-ExtraFileDescriptors-child.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +assert_eq "$LISTEN_FDS" "$1" +assert_eq "$LISTEN_FDNAMES" "$2" + +for ((i = 3; i < 3 + LISTEN_FDS; i++)); do + assert_eq "$(cat /proc/self/fd/"$i")" "${!i}" # Dereference $i to get i'th arg +done diff --git a/test/units/TEST-23-UNIT-FILE.ExtraFileDescriptors.sh b/test/units/TEST-23-UNIT-FILE.ExtraFileDescriptors.sh new file mode 100755 index 0000000000..eaeee635db --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.ExtraFileDescriptors.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +at_exit() { + set +e + + rm -rf /tmp/test-extra-fd/ +} + +trap at_exit EXIT + +mkdir /tmp/test-extra-fd +echo "Hello" > /tmp/test-extra-fd/1.txt +echo "Extra" > /tmp/test-extra-fd/2.txt + +systemd-analyze log-level debug + +# Open files and assign FD to variables +exec {TEST_FD1}</tmp/test-extra-fd/1.txt +exec {TEST_FD2}</tmp/test-extra-fd/2.txt + +TEST_UNIT="test-23-extra-fd.service" + +busctl call \ + org.freedesktop.systemd1 /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager StartTransientUnit \ + "ssa(sv)a(sa(sv))" "$TEST_UNIT" replace 4 \ + ExecStart "a(sasb)" 1 \ + /usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-ExtraFileDescriptors-child.sh \ + 5 /usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-ExtraFileDescriptors-child.sh 2 "test:other" "Hello" "Extra" \ + true \ + RemainAfterExit "b" true \ + Type "s" oneshot \ + ExtraFileDescriptors "a(hs)" 2 \ + "$TEST_FD1" test \ + "$TEST_FD2" other \ + 0 + +cmp -b <(systemctl show -p ExtraFileDescriptorNames "$TEST_UNIT") <<EOF +ExtraFileDescriptorNames=test other +EOF + +# shellcheck disable=SC2016 +timeout 10s bash -xec 'while [[ "$(systemctl show -P SubState test-23-extra-fd.service)" != "exited" ]]; do sleep .5; done' + +assert_eq "$(systemctl show -P Result "$TEST_UNIT")" "success" +assert_eq "$(systemctl show -P ExecMainStatus "$TEST_UNIT")" "0" + +# Verify extra file descriptors stay accessible even after service manager re-executes +systemctl daemon-reexec + +systemctl restart "$TEST_UNIT" + +assert_eq "$(systemctl show -P SubState "$TEST_UNIT")" "exited" +assert_eq "$(systemctl show -P Result "$TEST_UNIT")" "success" +assert_eq "$(systemctl show -P ExecMainStatus "$TEST_UNIT")" "0" + +systemctl stop "$TEST_UNIT" + +systemctl log-level info diff --git a/test/units/TEST-29-PORTABLE.directory.sh b/test/units/TEST-29-PORTABLE.directory.sh new file mode 100755 index 0000000000..5458111ee2 --- /dev/null +++ b/test/units/TEST-29-PORTABLE.directory.sh @@ -0,0 +1,152 @@ +#!/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 +# shellcheck disable=SC2233,SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Arrays cannot be exported, so redefine in each test script +ARGS=() +if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then + # If we're running under sanitizers, we need to use a less restrictive + # profile, otherwise LSan syscall would get blocked by seccomp + ARGS+=(--profile=trusted) +fi + +unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw +unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw + +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0 + +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-foo.service +systemctl is-active minimal-app0-bar.service && exit 1 + +portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0 + +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-bar.service +systemctl is-active minimal-app0-foo.service && exit 1 + +portablectl list | grep -q -F "minimal_1" +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' + +portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0 + +portablectl list | grep -q -F "No images." +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 + +mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc +mount /tmp/app0.raw /tmp/app0 +mount /tmp/app1.raw /tmp/app1 +mount /usr/share/minimal_0.raw /tmp/rootdir + +# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are +# bypassing the sysext logic in portabled here it will otherwise not see the +# extensions additional valid prefix) +grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release + +mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay + +grep . /tmp/overlay/usr/lib/extension-release.d/* +grep . /tmp/overlay/etc/os-release + +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1 + +systemctl is-active app1.service + +portablectl detach --now --runtime overlay app1 + +# Ensure --force works also when symlinking +mkdir -p /run/systemd/system.attached/app1.service.d +cat <<EOF >/run/systemd/system.attached/app1.service +[Unit] +Description=App 1 +EOF +cat <<EOF >/run/systemd/system.attached/app1.service.d/10-profile.conf +[Unit] +Description=App 1 +EOF +cat <<EOF >/run/systemd/system.attached/app1.service.d/20-portable.conf +[Unit] +Description=App 1 +EOF +systemctl daemon-reload + +portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime /tmp/overlay app1 + +systemctl is-active app1.service + +portablectl detach --now --runtime overlay app1 + +umount /tmp/overlay + +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + +systemctl is-active app0.service +systemctl is-active app1.service + +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0 +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2 +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service + +grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf + +grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf + +portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + +# Ensure --clean remove state and other directories belonging to the portable image being detached +test ! -d /var/lib/app0 +test ! -d /run/app0 + +# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned) +portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 +test -d /run/portables/app0 +test -d /run/portables/app1 +test -d /run/portables/rootdir +test -f /run/systemd/system.attached/app0.service +test -f /run/systemd/system.attached/app1.service +test -L /run/systemd/system.attached/app0.service.d/10-profile.conf +test -L /run/systemd/system.attached/app1.service.d/10-profile.conf +portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + +# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce. +# Provides coverage for https://github.com/systemd/systemd/issues/23481 +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 +portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 +# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 +portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 + +# The wrong file should be ignored, given the right one has the xattr set +trap 'rm -rf /var/cache/wrongext' EXIT +mkdir -p /var/cache/wrongext/usr/lib/extension-release.d /var/cache/wrongext/usr/lib/systemd/system/ +echo "[Service]" > /var/cache/wrongext/usr/lib/systemd/system/app0.service +touch /var/cache/wrongext/usr/lib/extension-release.d/extension-release.wrongext_somethingwrong.txt +cp /tmp/rootdir/usr/lib/os-release /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0 +setfattr -n user.extension-release.strict -v "false" /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0 +portablectl "${ARGS[@]}" attach --runtime --extension /var/cache/wrongext /tmp/rootdir app0 +status="$(portablectl is-attached --extension wrongext rootdir)" +[[ "${status}" == "attached-runtime" ]] +portablectl detach --runtime --extension /var/cache/wrongext /tmp/rootdir app0 + +umount /tmp/rootdir +umount /tmp/app0 +umount /tmp/app1 diff --git a/test/units/TEST-29-PORTABLE.image.sh b/test/units/TEST-29-PORTABLE.image.sh new file mode 100755 index 0000000000..376d94c283 --- /dev/null +++ b/test/units/TEST-29-PORTABLE.image.sh @@ -0,0 +1,240 @@ +#!/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 +# shellcheck disable=SC2233,SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Arrays cannot be exported, so redefine in each test script +ARGS=() +if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then + # If we're running under sanitizers, we need to use a less restrictive + # profile, otherwise LSan syscall would get blocked by seccomp + ARGS+=(--profile=trusted) +fi + +portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0 + +portablectl is-attached minimal-app0 +portablectl inspect /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-foo.service +systemctl is-active minimal-app0-bar.service && exit 1 + +portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl is-attached minimal-app0 +portablectl inspect /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-bar.service +systemctl is-active minimal-app0-foo.service && exit 1 + +portablectl list | grep -q -F "minimal_1" +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' + +portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl list | grep -q -F "No images." +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 + +# Ensure we don't regress (again) when using --force + +mkdir -p /run/systemd/system.attached/minimal-app0.service.d/ +cat <<EOF >/run/systemd/system.attached/minimal-app0.service +[Unit] +Description=Minimal App 0 +EOF +cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/10-profile.conf +[Unit] +Description=Minimal App 0 +EOF +cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/20-portable.conf +[Unit] +Description=Minimal App 0 +EOF +systemctl daemon-reload + +portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0 + +portablectl is-attached --force minimal-app0 +portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-foo.service +systemctl is-active minimal-app0-bar.service && exit 1 + +portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl is-attached --force minimal-app0 +portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-bar.service +systemctl is-active minimal-app0-foo.service && exit 1 + +portablectl list | grep -q -F "minimal_1" +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' + +portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl list | grep -q -F "No images." +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 + +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension app0 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf + +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension app0 minimal_1)" +[[ "${status}" == "running-runtime" ]] + +grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf + +portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0 + +# Ensure versioned images are accepted without needing to use --force to override the extension-release +# matching + +cp /tmp/app0.raw /tmp/app0_1.0.raw +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension app0_1 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0 +rm -f /tmp/app0_1.0.raw + +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +# Ensure that adding or removing a version to the image doesn't break reattaching +cp /tmp/app1.raw /tmp/app1_2.raw +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_2 minimal_1)" +[[ "${status}" == "running-runtime" ]] + +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1 minimal_1)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1 +portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 +systemctl daemon-reload +systemctl restart app1.service + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 + +# Ensure vpick works, including reattaching to a new image +mkdir -p /tmp/app1.v/ +cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw +cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)" +[[ "${status}" == "running-runtime" ]] + +rm -f /tmp/app1.v/app1_2.0.raw +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1 +rm -f /tmp/app1.v/app1_1.0.raw + +# Ensure that the combination of read-only images, state directory and dynamic user works, and that +# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while +# after the service is attached before the file appears. +grep -q -F bar "${STATE_DIRECTORY}/app0/foo" +grep -q -F baz "${STATE_DIRECTORY}/app1/foo" + +# Ensure that we can override the check on extension-release.NAME +cp /tmp/app0.raw /tmp/app10.raw +portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)" +[[ "${status}" == "running-runtime" ]] + +portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw" + +# Ensure that we can detach even when an image has been deleted already (stop the unit manually as +# portablectl won't find it) +rm -f /tmp/app10.raw +systemctl stop app0.service +portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 + +# portablectl also accepts confexts +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)" +[[ "${status}" == "running-runtime" ]] + +portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw" + +portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 + +# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned) +portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 +test -f /run/portables/app0.raw +test -f /run/portables/minimal_0.raw +test -f /run/systemd/system.attached/app0.service +test -L /run/systemd/system.attached/app0.service.d/10-profile.conf +portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 + +# Ensure that when two portables share the same base image, removing one doesn't remove the other too + +portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 +portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 + +status="$(portablectl is-attached --extension app0 minimal_0)" +[[ "${status}" == "attached-runtime" ]] +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "attached-runtime" ]] + +(! portablectl detach --runtime /usr/share/minimal_0.raw app) + +status="$(portablectl is-attached --extension app0 minimal_0)" +[[ "${status}" == "attached-runtime" ]] +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "attached-runtime" ]] + +# Ensure 'portablectl list' shows the correct status for both images +portablectl list +portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime" +portablectl list | grep -F "app0" | grep -q -F "attached-runtime" +portablectl list | grep -F "app1" | grep -q -F "attached-runtime" + +portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app + +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "attached-runtime" ]] + +portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app diff --git a/test/units/TEST-29-PORTABLE.sh b/test/units/TEST-29-PORTABLE.sh index 501d492c7d..7e0ba7e38a 100755 --- a/test/units/TEST-29-PORTABLE.sh +++ b/test/units/TEST-29-PORTABLE.sh @@ -5,6 +5,9 @@ set -eux set -o pipefail +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + # shellcheck source=test/units/util.sh . "$(dirname "$0")"/util.sh @@ -18,9 +21,14 @@ DefaultEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30 ManagerEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30 EOF +mkdir -p /run/systemd/system/systemd-portabled.service.d/ +cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf +[Service] +Environment=SYSTEMD_LOG_LEVEL=debug +EOF + systemctl daemon-reexec -export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30 udevadm control --log-level debug @@ -33,6 +41,11 @@ if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then # With the trusted profile DynamicUser is disabled, so the storage is not in private/ STATE_DIRECTORY=/var/lib/ fi +export STATE_DIRECTORY +export SYSTEMD_LOG_LEVEL=debug +export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30 + +# Quick smoke tests systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service' systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service' @@ -40,373 +53,6 @@ systemd-dissect --no-pager /tmp/app0.raw | grep -q '✓ sysext for portable serv systemd-dissect --no-pager /tmp/app1.raw | grep -q '✓ sysext for portable service' systemd-dissect --no-pager /tmp/conf0.raw | grep -q '✓ confext for portable service' -export SYSTEMD_LOG_LEVEL=debug -mkdir -p /run/systemd/system/systemd-portabled.service.d/ -cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf -[Service] -Environment=SYSTEMD_LOG_LEVEL=debug -EOF - -portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0 - -portablectl is-attached minimal-app0 -portablectl inspect /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-foo.service -systemctl is-active minimal-app0-bar.service && exit 1 - -portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl is-attached minimal-app0 -portablectl inspect /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-bar.service -systemctl is-active minimal-app0-foo.service && exit 1 - -portablectl list | grep -q -F "minimal_1" -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' - -portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl list | grep -q -F "No images." -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 - -# Ensure we don't regress (again) when using --force - -mkdir -p /run/systemd/system.attached/minimal-app0.service.d/ -cat <<EOF >/run/systemd/system.attached/minimal-app0.service -[Unit] -Description=Minimal App 0 -EOF -cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/10-profile.conf -[Unit] -Description=Minimal App 0 -EOF -cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/20-portable.conf -[Unit] -Description=Minimal App 0 -EOF -systemctl daemon-reload - -portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0 - -portablectl is-attached --force minimal-app0 -portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-foo.service -systemctl is-active minimal-app0-bar.service && exit 1 - -portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl is-attached --force minimal-app0 -portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-bar.service -systemctl is-active minimal-app0-foo.service && exit 1 - -portablectl list | grep -q -F "minimal_1" -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' - -portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl list | grep -q -F "No images." -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 - -# portablectl also works with directory paths rather than images - -unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw -unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw - -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0 - -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-foo.service -systemctl is-active minimal-app0-bar.service && exit 1 - -portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0 - -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-bar.service -systemctl is-active minimal-app0-foo.service && exit 1 - -portablectl list | grep -q -F "minimal_1" -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' - -portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0 - -portablectl list | grep -q -F "No images." -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 - -portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension app0 minimal_0)" -[[ "${status}" == "running-runtime" ]] - -grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf - -portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension app0 minimal_1)" -[[ "${status}" == "running-runtime" ]] - -grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf - -portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0 - -# Ensure versioned images are accepted without needing to use --force to override the extension-release -# matching - -cp /tmp/app0.raw /tmp/app0_1.0.raw -portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension app0_1 minimal_0)" -[[ "${status}" == "running-runtime" ]] - -portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0 -rm -f /tmp/app0_1.0.raw - -portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 - -systemctl is-active app1.service -status="$(portablectl is-attached --extension app1 minimal_0)" -[[ "${status}" == "running-runtime" ]] - -# Ensure that adding or removing a version to the image doesn't break reattaching -cp /tmp/app1.raw /tmp/app1_2.raw -portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1 - -systemctl is-active app1.service -status="$(portablectl is-attached --extension app1_2 minimal_1)" -[[ "${status}" == "running-runtime" ]] - -portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1 - -systemctl is-active app1.service -status="$(portablectl is-attached --extension app1 minimal_1)" -[[ "${status}" == "running-runtime" ]] - -portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1 -portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 -systemctl daemon-reload -systemctl restart app1.service - -systemctl is-active app1.service -status="$(portablectl is-attached --extension app1 minimal_0)" -[[ "${status}" == "running-runtime" ]] - -portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 - -# Ensure vpick works, including reattaching to a new image -mkdir -p /tmp/app1.v/ -cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw -cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw -portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 - -systemctl is-active app1.service -status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)" -[[ "${status}" == "running-runtime" ]] - -rm -f /tmp/app1.v/app1_2.0.raw -portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 - -systemctl is-active app1.service -status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)" -[[ "${status}" == "running-runtime" ]] - -portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1 -rm -f /tmp/app1.v/app1_1.0.raw - -# Ensure that the combination of read-only images, state directory and dynamic user works, and that -# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while -# after the service is attached before the file appears. -grep -q -F bar "${STATE_DIRECTORY}/app0/foo" -grep -q -F baz "${STATE_DIRECTORY}/app1/foo" - -# Ensure that we can override the check on extension-release.NAME -cp /tmp/app0.raw /tmp/app10.raw -portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)" -[[ "${status}" == "running-runtime" ]] - -portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw" - -# Ensure that we can detach even when an image has been deleted already (stop the unit manually as -# portablectl won't find it) -rm -f /tmp/app10.raw -systemctl stop app0.service -portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 - -# portablectl also accepts confexts -portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)" -[[ "${status}" == "running-runtime" ]] - -portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw" - -portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 - -# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned) -portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 -test -f /run/portables/app0.raw -test -f /run/portables/minimal_0.raw -test -f /run/systemd/system.attached/app0.service -test -L /run/systemd/system.attached/app0.service.d/10-profile.conf -portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 - -# Ensure that when two portables share the same base image, removing one doesn't remove the other too - -portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 -portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 - -status="$(portablectl is-attached --extension app0 minimal_0)" -[[ "${status}" == "attached-runtime" ]] -status="$(portablectl is-attached --extension app1 minimal_0)" -[[ "${status}" == "attached-runtime" ]] - -(! portablectl detach --runtime /usr/share/minimal_0.raw app) - -status="$(portablectl is-attached --extension app0 minimal_0)" -[[ "${status}" == "attached-runtime" ]] -status="$(portablectl is-attached --extension app1 minimal_0)" -[[ "${status}" == "attached-runtime" ]] - -# Ensure 'portablectl list' shows the correct status for both images -portablectl list -portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime" -portablectl list | grep -F "app0" | grep -q -F "attached-runtime" -portablectl list | grep -F "app1" | grep -q -F "attached-runtime" - -portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app - -status="$(portablectl is-attached --extension app1 minimal_0)" -[[ "${status}" == "attached-runtime" ]] - -portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app - -# portablectl also works with directory paths rather than images - -mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc -mount /tmp/app0.raw /tmp/app0 -mount /tmp/app1.raw /tmp/app1 -mount /usr/share/minimal_0.raw /tmp/rootdir - -# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are -# bypassing the sysext logic in portabled here it will otherwise not see the -# extensions additional valid prefix) -grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release - -mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay - -grep . /tmp/overlay/usr/lib/extension-release.d/* -grep . /tmp/overlay/etc/os-release - -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1 - -systemctl is-active app1.service - -portablectl detach --now --runtime overlay app1 - -# Ensure --force works also when symlinking -mkdir -p /run/systemd/system.attached/app1.service.d -cat <<EOF >/run/systemd/system.attached/app1.service -[Unit] -Description=App 1 -EOF -cat <<EOF >/run/systemd/system.attached/app1.service.d/10-profile.conf -[Unit] -Description=App 1 -EOF -cat <<EOF >/run/systemd/system.attached/app1.service.d/20-portable.conf -[Unit] -Description=App 1 -EOF -systemctl daemon-reload - -portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime /tmp/overlay app1 - -systemctl is-active app1.service - -portablectl detach --now --runtime overlay app1 - -umount /tmp/overlay - -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 - -systemctl is-active app0.service -systemctl is-active app1.service - -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0 -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2 -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service - -grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf - -grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf - -portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 - -# Ensure --clean remove state and other directories belonging to the portable image being detached -test ! -d /var/lib/app0 -test ! -d /run/app0 - -# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned) -portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 -test -d /run/portables/app0 -test -d /run/portables/app1 -test -d /run/portables/rootdir -test -f /run/systemd/system.attached/app0.service -test -f /run/systemd/system.attached/app1.service -test -L /run/systemd/system.attached/app0.service.d/10-profile.conf -test -L /run/systemd/system.attached/app1.service.d/10-profile.conf -portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 - -# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce. -# Provides coverage for https://github.com/systemd/systemd/issues/23481 -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 -portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 -# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 -portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 - -# The wrong file should be ignored, given the right one has the xattr set -trap 'rm -rf /var/cache/wrongext' EXIT -mkdir -p /var/cache/wrongext/usr/lib/extension-release.d /var/cache/wrongext/usr/lib/systemd/system/ -echo "[Service]" > /var/cache/wrongext/usr/lib/systemd/system/app0.service -touch /var/cache/wrongext/usr/lib/extension-release.d/extension-release.wrongext_somethingwrong.txt -cp /tmp/rootdir/usr/lib/os-release /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0 -setfattr -n user.extension-release.strict -v "false" /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0 -portablectl "${ARGS[@]}" attach --runtime --extension /var/cache/wrongext /tmp/rootdir app0 -status="$(portablectl is-attached --extension wrongext rootdir)" -[[ "${status}" == "attached-runtime" ]] -portablectl detach --runtime --extension /var/cache/wrongext /tmp/rootdir app0 - -umount /tmp/rootdir -umount /tmp/app0 -umount /tmp/app1 - # Lack of ID field in os-release should be rejected, but it caused a crash in the past instead mkdir -p /tmp/emptyroot/usr/lib mkdir -p /tmp/emptyext/usr/lib/extension-release.d @@ -417,4 +63,8 @@ touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))" test -z "${res}" +: "Run subtests" + +run_subtests + touch /testok diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh index 15f663faf5..6cf1213551 100755 --- a/test/units/TEST-50-DISSECT.dissect.sh +++ b/test/units/TEST-50-DISSECT.dissect.sh @@ -427,14 +427,16 @@ systemctl is-active testservice-50e.service # Check vpick support in ExtensionImages= VBASE="vtest$RANDOM" VDIR="/tmp/$VBASE.v" -mkdir "$VDIR" +EMPTY_VDIR="/tmp/$VBASE-empty.v" +NONEXISTENT_VDIR="/tmp/$VBASE-nonexistent.v" +mkdir "$VDIR" "$EMPTY_VDIR" ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw" ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw" -systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID' +systemd-run -P -p ExtensionImages="$VDIR -$EMPTY_VDIR -$NONEXISTENT_VDIR" bash -c '/opt/script1.sh | grep ID' -rm -rf "$VDIR" +rm -rf "$VDIR" "$EMPTY_VDIR" # ExtensionDirectories will set up an overlay mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test" @@ -502,14 +504,16 @@ systemctl is-active testservice-50f.service # Check vpick support in ExtensionDirectories= VBASE="vtest$RANDOM" VDIR="/tmp/$VBASE.v" -mkdir "$VDIR" +EMPTY_VDIR="/tmp/$VBASE-empty.v" +NONEXISTENT_VDIR="/tmp/$VBASE-nonexistent.v" +mkdir "$VDIR" "$EMPTY_VDIR" ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0" ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1" -systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2" +systemd-run -P --property ExtensionDirectories="$VDIR -$EMPTY_VDIR -$NONEXISTENT_VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2" -rm -rf "$VDIR" +rm -rf "$VDIR" "$EMPTY_VDIR" systemd-dissect --umount "$IMAGE_DIR/app0" systemd-dissect --umount "$IMAGE_DIR/app1" @@ -527,7 +531,7 @@ systemd-sysext unmerge rmdir /etc/extensions/app-nodistro # Similar, but go via varlink -varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}' +varlinkctl call --more /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}' (! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file ) varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}' grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh index 29b789d361..3a4fa654e9 100755 --- a/test/units/TEST-54-CREDS.sh +++ b/test/units/TEST-54-CREDS.sh @@ -43,8 +43,8 @@ CRED_DIR="$(mktemp -d)" ENC_CRED_DIR="$(mktemp -d)" echo foo >"$CRED_DIR/secure-or-weak" echo foo >"$CRED_DIR/insecure" -echo foo | systemd-creds --name="encrypted" encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted" -echo foo | systemd-creds encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted-unnamed" +echo foo | systemd-creds --name="encrypted" encrypt - "$ENC_CRED_DIR/encrypted" +echo foo | systemd-creds encrypt - "$ENC_CRED_DIR/encrypted-unnamed" chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR" chmod -R 0444 "$CRED_DIR/insecure" mkdir /tmp/empty/ diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh index bbef52ea51..0af6f6ad28 100755 --- a/test/units/TEST-58-REPART.sh +++ b/test/units/TEST-58-REPART.sh @@ -29,6 +29,9 @@ if ! systemd-detect-virt --quiet --container; then udevadm control --log-level debug fi +esp_guid=C12A7328-F81F-11D2-BA4B-00A0C93EC93B +xbootldr_guid=BC13C2FF-59E6-4262-A352-B275FD6F7172 + machine="$(uname -m)" if [ "${machine}" = "x86_64" ]; then root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709 @@ -968,6 +971,83 @@ EOF veritysetup dump "${loop}p2" | grep 'Hash block size:' | grep -q '1024' } +testcase_verity_hash_size_from_data_size() { + local defs imgs loop + + if systemd-detect-virt --quiet --container; then + echo "Skipping verity hash size from data size test in container." + return + fi + + defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" + + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + chmod 0755 "$defs" + + echo "*** dm-verity-hash-size-from-data-size ***" + + # create minimized data partition with SizeMaxBytes= + tee "$defs/verity-data.conf" <<EOF +[Partition] +Type=root-${architecture} +CopyFiles=${defs} +Verity=data +VerityMatchKey=root +Minimize=guess +SizeMaxBytes=10G +EOF + + # create hash partition, its size will be derived from SizeMaxBytes= of the data partition + tee "$defs/verity-hash.conf" <<EOF +[Partition] +Type=root-${architecture}-verity +Verity=hash +VerityMatchKey=root +VerityHashBlockSizeBytes=4096 +VerityDataBlockSizeBytes=4096 +EOF + + systemd-repart --offline="$OFFLINE" \ + --definitions="$defs" \ + --seed="$seed" \ + --dry-run=no \ + --empty=create \ + --size=auto \ + --json=pretty \ + "$imgs/verity" + + loop="$(losetup --partscan --show --find "$imgs/verity")" + + # Make sure the loopback device gets cleaned up + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR + + udevadm wait --timeout 60 --settle "${loop:?}p1" "${loop:?}p2" + + output=$(sfdisk -J "$loop") + + # size of the hash partition, as determined by calculate_verity_hash_size() + # for 10GiB data partition and hash / data block size of 4096B + hash_bytes=84557824 + hash_sectors_expected=$((hash_bytes / 512)) + + hash_sectors_actual=$(jq -r ".partitiontable.partitions | map(select(.name == \"root-${architecture}-verity\")) | .[].size" <<<"$output") + + assert_eq "$hash_sectors_expected" "$hash_sectors_actual" + + data_sectors=$(jq -r ".partitiontable.partitions | map(select(.name == \"root-${architecture}\")) | .[].size" <<<"$output") + data_bytes=$((data_sectors * 512)) + data_verity_blocks=$((data_bytes / 4096)) + + # The actual data partition is much smaller than 10GiB, i.e. also smaller than 100MiB + assert_rc 0 test $data_bytes -lt $((100 * 1024 * 1024)) + + # Check that the verity hash tree is created from the actual on-disk data, not the custom size + veritysetup dump "${loop}p2" | grep 'Data blocks:' | grep -q "$data_verity_blocks" +} + testcase_exclude_files() { local defs imgs root output @@ -1325,9 +1405,12 @@ testcase_compression() { # TODO: add btrfs once btrfs-progs v6.11 is available in distributions. for format in squashfs erofs; do - if ! command -v "mkfs.$format" && ! command -v mksquashfs >/dev/null; then - continue - fi + case "$format" in + squashfs) + command -v mksquashfs >/dev/null || continue ;; + *) + command -v "mkfs.$format" || continue ;; + esac [[ "$format" == "squashfs" ]] && compression=zstd [[ "$format" == "erofs" ]] && compression=lz4hc @@ -1390,6 +1473,121 @@ EOF [[ "$(sfdisk -d "$imgs/zzz" | grep -F 'uuid=' | awk '{ print $8 }' | sort -u | wc -l)" == "3" ]] } +testcase_make_symlinks() { + local defs imgs output + + if systemd-detect-virt --quiet --container; then + echo "Skipping MakeSymlinks= test in container." + return + fi + + # For issue #34257 + + defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + chmod 0755 "$defs" + + tee "$defs/root.conf" <<EOF +[Partition] +Type=root +MakeDirectories=/dir +MakeSymlinks=/foo:/bar +MakeSymlinks=/dir/foo:/bar +EOF + + systemd-repart --offline="$OFFLINE" \ + --definitions="$defs" \ + --empty=create \ + --size=1G \ + --dry-run=no \ + --offline="$OFFLINE" \ + --json=pretty \ + "$imgs/zzz" + + systemd-dissect "$imgs/zzz" -M "$imgs/mnt" + assert_eq "$(readlink "$imgs/mnt/foo")" "/bar" + assert_eq "$(readlink "$imgs/mnt/dir/foo")" "/bar" + systemd-dissect -U "$imgs/mnt" +} + +testcase_fallback_partitions() { + local workdir image defs + + workdir="$(mktemp --directory "/tmp/test-repart.fallback.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '${workdir:?}'" RETURN + + image="$workdir/image.img" + defs="$workdir/defs" + mkdir "$defs" + + tee "$defs/10-esp.conf" <<EOF +[Partition] +Type=esp +Format=vfat +SizeMinBytes=10M +EOF + + tee "$defs/20-xbootldr.conf" <<EOF +[Partition] +Type=xbootldr +Format=vfat +SizeMinBytes=100M +SupplementFor=10-esp +EOF + + # Blank disk => big ESP should be created + + systemd-repart --empty=create --size=auto --dry-run=no --definitions="$defs" "$image" + + output=$(sfdisk -d "$image") + assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output" + assert_not_in "${image}2" "$output" + + # Disk with small ESP => ESP grows + + sfdisk "$image" <<EOF +label: gpt +size=10M, type=${esp_guid} +EOF + + systemd-repart --dry-run=no --definitions="$defs" "$image" + + output=$(sfdisk -d "$image") + assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output" + assert_not_in "${image}2" "$output" + + # Disk with small ESP that can't grow => XBOOTLDR created + + truncate -s 150M "$image" + sfdisk "$image" <<EOF +label: gpt +size=10M, type=${esp_guid}, +size=10M, type=${root_guid}, +EOF + + systemd-repart --dry-run=no --definitions="$defs" "$image" + + output=$(sfdisk -d "$image") + assert_in "${image}1 : start= 2048, size= 20480, type=${esp_guid}" "$output" + assert_in "${image}3 : start= 43008, size= 264152, type=${xbootldr_guid}" "$output" + + # Disk with existing XBOOTLDR partition => XBOOTLDR grows, small ESP created + + sfdisk "$image" <<EOF +label: gpt +size=10M, type=${xbootldr_guid}, +EOF + + systemd-repart --dry-run=no --definitions="$defs" "$image" + + output=$(sfdisk -d "$image") + assert_in "${image}1 : start= 2048, size= 204800, type=${xbootldr_guid}" "$output" + assert_in "${image}2 : start= 206848, size= 100312, type=${esp_guid}" "$output" +} + OFFLINE="yes" run_testcases diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index 9fc2de1a21..c00559f6fb 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -172,6 +172,13 @@ systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*' systemd-analyze timestamp now systemd-analyze timestamp -- -1 systemd-analyze timestamp yesterday now tomorrow +systemd-analyze timestamp 'Fri 2012-11-23 23:02:15' +systemd-analyze timestamp 'Fri 2012-11-23 23:02:15 UTC' +systemd-analyze timestamp 'Fri 2012-11-23 23:02:15 CET' +for i in $(timedatectl list-timezones); do + [[ -e "/usr/share/zoneinfo/$i" ]] || continue + systemd-analyze timestamp "Fri 2012-11-23 23:02:15 $i" +done (! systemd-analyze timestamp yesterday never tomorrow) (! systemd-analyze timestamp 1) (! systemd-analyze timestamp '*-2-29 0:0:0') @@ -381,6 +388,29 @@ systemd-analyze verify /tmp/multi-exec-start.service echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service (! systemd-analyze verify /tmp/multi-exec-start.service) +# Prevent regression from #20233 where systemd-analyze will return nonzero exit codes on warnings + +# Unit file with warning "Unknown key name 'foo' in section 'Unit', ignoring" +cat <<EOF >/tmp/testwarnings.service +[Unit] +Foo=Bar + +[Service] +ExecStart=echo hello +EOF + +# yes/no/one should all return nonzero exit status for warnings in unit file +(! systemd-analyze verify --recursive-errors=yes /tmp/testwarnings.service) + +(! systemd-analyze verify --recursive-errors=no /tmp/testwarnings.service) + +(! systemd-analyze verify --recursive-errors=one /tmp/testwarnings.service) + +# zero exit status since no errors and only warnings +systemd-analyze verify /tmp/testwarnings.service + +rm /tmp/testwarnings.service + # Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed # The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in # values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight @@ -965,6 +995,13 @@ systemd-analyze condition --instance=tmp --unit=systemd-growfs@.service systemd-analyze verify --instance=tmp --man=no systemd-growfs@.service systemd-analyze security --instance=tmp systemd-growfs@.service +systemd-analyze has-tpm2 ||: +if systemd-analyze has-tpm2 -q ; then + echo "have tpm2" +else + echo "have no tpm2" +fi + systemd-analyze log-level info touch /testok diff --git a/test/units/TEST-67-INTEGRITY.sh b/test/units/TEST-67-INTEGRITY.sh index a42fd66aa5..eb3082d132 100755 --- a/test/units/TEST-67-INTEGRITY.sh +++ b/test/units/TEST-67-INTEGRITY.sh @@ -52,7 +52,7 @@ fi # Do one iteration with a separate data device, to test those branches separate_data=1 -for algorithm in crc32c crc32 sha1 sha256 +for algorithm in crc32c crc32 xxhash64 sha1 sha256 do if [ "${separate_data}" -eq 1 ]; then data_option="--data-device=${image_dir}/data" diff --git a/test/units/TEST-74-AUX-UTILS.busctl.sh b/test/units/TEST-74-AUX-UTILS.busctl.sh index 3ef94e5cb9..4949f4bac7 100755 --- a/test/units/TEST-74-AUX-UTILS.busctl.sh +++ b/test/units/TEST-74-AUX-UTILS.busctl.sh @@ -46,6 +46,10 @@ busctl call -j \ busctl call --verbose --timeout=60 --expect-reply=yes \ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \ ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount" +# show information passed fd +busctl call --json=pretty \ + org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \ + DumpByFileDescriptor | jq busctl emit /org/freedesktop/login1 org.freedesktop.login1.Manager \ PrepareForSleep b false @@ -53,6 +57,10 @@ busctl emit --auto-start=no --destination=systemd-logind.service \ /org/freedesktop/login1 org.freedesktop.login1.Manager \ PrepareForShutdown b false +systemd-run --quiet --service-type=notify --unit=test-busctl-wait --pty \ + -p ExecStartPost="busctl emit /test org.freedesktop.fake1 TestSignal s success" \ + busctl --timeout=3 wait /test org.freedesktop.fake1 TestSignal | grep -qF 's "success"' + busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \ Version busctl get-property --verbose \ diff --git a/test/units/TEST-74-AUX-UTILS.defer_reactivation.sh b/test/units/TEST-74-AUX-UTILS.defer_reactivation.sh new file mode 100755 index 0000000000..d1b113d1ab --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.defer_reactivation.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +systemctl start realtime-test.timer + +sleep 35 +mindelta=10 + +last= +while read -r time; do + if [ -n "$last" ]; then + delta=$((time - last)) + if [ "$delta" -lt $mindelta ]; then + echo "Timer fired too early: $delta < $mindelta" >/failed + break + fi + fi + last=$time +done </tmp/realtime-test.log + +test ! -s /failed diff --git a/test/units/TEST-74-AUX-UTILS.networkctl.sh b/test/units/TEST-74-AUX-UTILS.networkctl.sh index 0576d6c055..3d402a7182 100755 --- a/test/units/TEST-74-AUX-UTILS.networkctl.sh +++ b/test/units/TEST-74-AUX-UTILS.networkctl.sh @@ -21,6 +21,9 @@ at_exit() { trap at_exit EXIT +systemctl unmask systemd-networkd.service +systemctl start systemd-networkd.service + export NETWORK_NAME="10-networkctl-test-$RANDOM.network" export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev" export LINK_NAME="10-networkctl-test-$RANDOM.link" @@ -75,15 +78,6 @@ cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test.conf" networkctl cat "$NETWORK_NAME" | grep '^# ' | cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf") -networkctl edit --stdin --runtime "$NETDEV_NAME" <<EOF -[NetDev] -Name=test2 -Kind=dummy -EOF - -networkctl cat "$NETDEV_NAME" | grep -v '^# ' | - cmp - <(printf '%s\n' "[NetDev]" "Name=test2" "Kind=dummy") - cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF [Match] OriginalName=test2 @@ -95,13 +89,23 @@ EOF SYSTEMD_LOG_LEVEL=debug EDITOR='true' script -ec 'networkctl edit "$LINK_NAME"' /dev/null cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME" -# Test links -systemctl unmask systemd-networkd -systemctl stop systemd-networkd +# The interface test2 does not exist, hence the below do not work. (! networkctl cat @test2) (! networkctl cat @test2:netdev) +(! networkctl cat @test2:link) +(! networkctl cat @test2:network) + +# create .netdev file at last, otherwise, the .link file will not be applied to the interface. +networkctl edit --stdin --runtime "$NETDEV_NAME" <<EOF +[NetDev] +Name=test2 +Kind=dummy +EOF + +networkctl cat "$NETDEV_NAME" | grep -v '^# ' | + cmp - <(printf '%s\n' "[NetDev]" "Name=test2" "Kind=dummy") -systemctl start systemd-networkd +# wait for the interface being created and configured. SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20 networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME") diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh index 9e0a429e41..5b46e11409 100755 --- a/test/units/TEST-74-AUX-UTILS.run.sh +++ b/test/units/TEST-74-AUX-UTILS.run.sh @@ -246,4 +246,16 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the # Let's chain a couple of run0 calls together, for fun readarray -t cmdline < <(printf "%.0srun0\n" {0..31}) assert_eq "$("${cmdline[@]}" bash -c 'echo $SUDO_USER')" "$USER" + + # Tests for working directory, especially for specifying "/" (see PR #34771). + cd / + assert_eq "$(run0 pwd)" "/" + assert_eq "$(run0 -D /tmp pwd)" "/tmp" + assert_eq "$(run0 --user=testuser pwd)" "/home/testuser" + assert_eq "$(run0 -D /tmp --user=testuser pwd)" "/tmp" + cd /tmp + assert_eq "$(run0 pwd)" "/tmp" + assert_eq "$(run0 -D / pwd)" "/" + assert_eq "$(run0 --user=testuser pwd)" "/home/testuser" + assert_eq "$(run0 -D / --user=testuser pwd)" "/" fi diff --git a/test/units/TEST-86-MULTI-PROFILE-UKI.sh b/test/units/TEST-86-MULTI-PROFILE-UKI.sh new file mode 100755 index 0000000000..042cc59419 --- /dev/null +++ b/test/units/TEST-86-MULTI-PROFILE-UKI.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +bootctl + +CURRENT_UKI=$(bootctl --print-stub-path) + +echo "CURRENT UKI ($CURRENT_UKI):" +ukify inspect "$CURRENT_UKI" +if test -f /run/systemd/stub/profile; then + echo "CURRENT PROFILE:" + cat /run/systemd/stub/profile +fi +echo "CURRENT MEASUREMENT:" +/usr/lib/systemd/systemd-measure --current +if test -f /run/systemd/tpm2-pcr-signature.json; then + echo "CURRENT SIGNATURE:" + jq </run/systemd/tpm2-pcr-signature.json +fi + +echo "CURRENT EVENT LOG + PCRS:" +/usr/lib/systemd/systemd-pcrlock + +if test ! -f /run/systemd/stub/profile; then + openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out /root/pcrsign.private.pem + openssl rsa -pubout -in /root/pcrsign.private.pem -out /root/pcrsign.public.pem + + ukify build --extend="$CURRENT_UKI" --output=/tmp/extended0.efi --profile='ID=profile0 +TITLE="Profile Zero"' --measure-base="$CURRENT_UKI" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512 + + ukify build --extend=/tmp/extended0.efi --output=/tmp/extended1.efi --profile='ID=profile1 +TITLE="Profile One"' --measure-base=/tmp/extended0.efi --cmdline="testprofile1=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512 + + ukify build --extend=/tmp/extended1.efi --output=/tmp/extended2.efi --profile='ID=profile2 +TITLE="Profile Two"' --measure-base=/tmp/extended1.efi --cmdline="testprofile2=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512 + + echo "EXTENDED UKI:" + ukify inspect /tmp/extended2.efi + rm /tmp/extended0.efi /tmp/extended1.efi + mv /tmp/extended2.efi "$CURRENT_UKI" + + # Prepare a disk image, locked to the PCR measurements of the UKI we just generated + truncate -s 32M /root/encrypted.raw + echo -n "geheim" >/root/encrypted.secret + cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom /root/encrypted.raw --key-file=/root/encrypted.secret + systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs= --tpm2-public-key=/root/pcrsign.public.pem --unlock-key-file=/root/encrypted.secret /root/encrypted.raw + rm -f /root/encrypted.secret + + reboot + exit 0 +else + # shellcheck source=/dev/null + . /run/systemd/stub/profile + + # Validate that with the current profile we can fulfill the PCR 11 policy + systemd-cryptsetup attach multiprof /root/encrypted.raw - tpm2-device=auto,headless=1 + systemd-cryptsetup detach multiprof + + if [ "$ID" = "profile0" ]; then + grep -v testprofile /proc/cmdline + echo "default $(basename "$CURRENT_UKI")@profile1" >"$(bootctl -p)/loader/loader.conf" + reboot + exit 0 + elif [ "$ID" = "profile1" ]; then + grep testprofile1=1 /proc/cmdline + echo "default $(basename "$CURRENT_UKI")@profile2" >"$(bootctl -p)/loader/loader.conf" + reboot + exit 0 + elif [ "$ID" = "profile2" ]; then + grep testprofile2=1 /proc/cmdline + rm /root/encrypted.raw + else + exit 1 + fi +fi + +touch /testok |