#!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.1-or-later set -ex set -o pipefail # shellcheck source=test/units/util.sh . "$(dirname "$0")"/util.sh at_exit() { # Since the soft-reboot drops the enqueued end.service, we won't shutdown # the test VM if the test fails and have to wait for the watchdog to kill # us (which may take quite a long time). Let's just forcibly kill the machine # instead to save CI resources. if [[ $? -ne 0 ]]; then echo >&2 "Test failed, shutting down the machine..." systemctl poweroff -ff fi } trap at_exit EXIT # Because this test tests soft-reboot, we have to get rid of the symlink we put at # /run/nextroot to allow rebooting into the previous snapshot if the test fails for # the duration of the test. However, let's make sure we put the symlink back in place # if the test fails. if [[ -L /run/nextroot ]]; then at_error() { mountpoint -q /run/nextroot && umount -R /run/nextroot rm -rf /run/nextroot ln -sf /snapshot /run/nextroot } trap at_error ERR rm -f /run/nextroot fi systemd-analyze log-level debug export SYSTEMD_LOG_LEVEL=debug if [ -f /run/TEST-82-SOFTREBOOT.touch3 ]; then echo "This is the fourth boot!" systemd-notify --status="Fourth Boot" test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3 rm /run/TEST-82-SOFTREBOOT.touch3 mount rmdir /original-root /run/nextroot # Check that the fdstore entry still exists test "$LISTEN_FDS" -eq 3 read -r x <&5 test "$x" = "oinkoink" # Check that the surviving services are still around test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active" [[ ! -e /run/credentials/TEST-82-SOFTREBOOT-nosurvive.service ]] assert_eq "$(cat /run/credentials/TEST-82-SOFTREBOOT-survive-argv.service/preserve)" "yay" # There may be huge amount of pending messages in sockets. Processing them may cause journal rotation and # removal of old archived journal files. If a journal file is removed during journalctl reading it, # the command may fail. To mitigate such, sync before reading journals. Workaround for #32834. journalctl --sync # Check journals journalctl -o short-monotonic --no-hostname --grep '(will soft-reboot|KILL|corrupt)' assert_eq "$(journalctl -q -o short-monotonic -u systemd-journald.service --grep 'corrupt')" "" # All succeeded, exit cleanly now elif [ -f /run/TEST-82-SOFTREBOOT.touch2 ]; then echo "This is the third boot!" systemd-notify --status="Third Boot" test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2 rm /run/TEST-82-SOFTREBOOT.touch2 # Check that the fdstore entry still exists test "$LISTEN_FDS" -eq 2 read -r x <&4 test "$x" = "miaumiau" # Upload another entry T="/dev/shm/fdstore.$RANDOM" echo "oinkoink" >"$T" systemd-notify --fd=3 --pid=parent 3<"$T" rm "$T" # Check that the surviving services are still around test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active" # Test that we really are in the new overlayfs root fs read -r x "$T" systemd-notify --fd=3 --pid=parent 3<"$T" rm "$T" # Check that the surviving services are still around test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active" test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active" # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs) mkdir -p /run/nextroot /tmp/nextroot-lower /original-root mount -t tmpfs tmpfs /tmp/nextroot-lower echo miep >/tmp/nextroot-lower/lower # Copy os-release away, so that we can manipulate it and check that it is updated in the propagate # directory across soft reboots. Try to cover corner cases by truncating it. mkdir -p /tmp/nextroot-lower/usr/lib grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release (! grep -q MARKER=1 /etc/os-release) mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro # Bind our current root into the target so that we later can return to it mount --bind / /run/nextroot/original-root # Restart the unit that is not supposed to survive systemd-run --collect --service-type=exec --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity # Now ensure there are no naming clashes and a bunch of transient units all succeed for _ in $(seq 1 25); do systemd-run --wait true done # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should # automatically do a softreboot instead of normal reboot. touch /run/TEST-82-SOFTREBOOT.touch2 systemctl --no-block reboot # Now block until the soft-boot killing spree kills us exec sleep infinity else # This is the first boot systemd-notify --status="First Boot" test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0 # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly T="/dev/shm/fdstore.$RANDOM" echo "wuffwuff" >"$T" systemd-notify --fd=3 --pid=parent 3<"$T" rm "$T" survive_sigterm="/dev/shm/survive-sigterm-$RANDOM.sh" cat >"$survive_sigterm" <"$survive_argv" <