diff options
Diffstat (limited to 'test/units/TEST-82-SOFTREBOOT.sh')
-rwxr-xr-x | test/units/TEST-82-SOFTREBOOT.sh | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/test/units/TEST-82-SOFTREBOOT.sh b/test/units/TEST-82-SOFTREBOOT.sh new file mode 100755 index 0000000..f86bc92 --- /dev/null +++ b/test/units/TEST-82-SOFTREBOOT.sh @@ -0,0 +1,268 @@ +#!/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 + +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 </lower + test "$x" = "miep" + cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release + grep -q MARKER=1 /etc/os-release + + # Switch back to the original root, away from the overlayfs + mount --bind /original-root /run/nextroot + mount + + # Restart the unit that is not supposed to survive + systemd-run --collect --service-type=exec --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity + + # Now issue the soft reboot. We should be right back soon. + touch /run/TEST-82-SOFTREBOOT.touch3 + systemctl --no-block soft-reboot + + # Now block until the soft-boot killing spree kills us + exec sleep infinity + +elif [ -f /run/TEST-82-SOFTREBOOT.touch ]; then + echo "This is the second boot!" + systemd-notify --status="Second Boot" + + test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 1 + + # Clean up what we created earlier + rm /run/TEST-82-SOFTREBOOT.touch + + # Check that the fdstore entry still exists + test "$LISTEN_FDS" -eq 1 + read -r x <&3 + test "$x" = "wuffwuff" + + # Check that we got a PrepareForShutdownWithMetadata signal with the right type + cat /run/TEST-82-SOFTREBOOT.signal + test "$(jq -r '.payload.data[1].type.data' </run/TEST-82-SOFTREBOOT.signal)" = "soft-reboot" + + # Check that the system credentials survived the soft reboot. + test "$(systemd-creds cat --system kernelcmdlinecred)" = "uff" + + # Upload another entry + T="/dev/shm/fdstore.$RANDOM" + echo "miaumiau" >"$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" <<EOF +#!/bin/bash +trap "" TERM +systemd-notify --ready +rm "$survive_sigterm" +exec sleep infinity +EOF + chmod +x "$survive_sigterm" + + survive_argv="/dev/shm/survive-argv-$RANDOM.sh" + cat >"$survive_argv" <<EOF +#!/bin/bash +systemd-notify --ready +rm "$survive_argv" +exec -a @sleep sleep infinity +EOF + chmod +x "$survive_argv" + # This sets DefaultDependencies=no so that they remain running until the very end, and + # IgnoreOnIsolate=yes so that they aren't stopped via the "testsuite.target" isolation we do on next boot, + # and will be killed by the final sigterm/sigkill spree. + systemd-run --collect --service-type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=TEST-82-SOFTREBOOT-nosurvive-sigterm.service "$survive_sigterm" + systemd-run --collect --service-type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes -p SetCredential=gone:hoge --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity + + # Ensure that the unit doesn't get deactivated by dependencies on the source file. Given it's a verity + # image that is already open, even if the tmpfs with the image goes away, the file will be pinned by the + # kernel and will keep working. + cp /usr/share/minimal_0.* /tmp/ + + # Configure these transient units to survive the soft reboot - they will not conflict with shutdown.target + # and it will be ignored on the isolate that happens in the next boot. The first will use argv[0][0] = + # '@', and the second will use SurviveFinalKillSignal=yes. Both should survive. + # By writing to stdout, which is connected to the journal, we also ensure logging doesn't break across + # soft reboots due to journald being temporarily stopped. + systemd-run --service-type=notify --unit=TEST-82-SOFTREBOOT-survive-argv.service \ + --property SurviveFinalKillSignal=no \ + --property IgnoreOnIsolate=yes \ + --property DefaultDependencies=no \ + --property After=basic.target \ + --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \ + --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \ + --property SetCredential=preserve:yay \ + "$survive_argv" + # shellcheck disable=SC2016 + systemd-run --service-type=exec --unit=TEST-82-SOFTREBOOT-survive.service \ + --property TemporaryFileSystem="/run /tmp /var" \ + --property RootImage=/tmp/minimal_0.raw \ + --property BindReadOnlyPaths=/dev/log \ + --property BindReadOnlyPaths=/run/systemd/journal/socket \ + --property BindReadOnlyPaths=/run/systemd/journal/stdout \ + --property SurviveFinalKillSignal=yes \ + --property IgnoreOnIsolate=yes \ + --property DefaultDependencies=no \ + --property After=basic.target \ + --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \ + --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \ + bash -c 'count=0; while echo "$count"; do count=$[$count +1]; sleep 1; done' + + # Check that we can set up an inhibitor, and that busctl monitor sees the + # PrepareForShutdownWithMetadata signal and that it says 'soft-reboot'. + systemd-run --unit busctl.service --service-type=exec --property StandardOutput=file:/run/TEST-82-SOFTREBOOT.signal \ + busctl monitor --json=pretty --match 'sender=org.freedesktop.login1,path=/org/freedesktop/login1,interface=org.freedesktop.login1.Manager,member=PrepareForShutdownWithMetadata,type=signal' + systemd-run --unit inhibit.service --service-type=exec \ + systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \ + sleep infinity + + # Enqueue a bunch of failing units to try and trigger the transient name clash that happens due to D-Bus + # being restarted and the "unique" bus IDs not being unique across restarts + for _ in $(seq 1 25); do + # Use --wait to ensure we connect to the system bus instead of the private bus (otherwise a UUID is + # used instead of the bus ID) + systemd-run --wait false || true + done + + # Now issue the soft reboot. We should be right back soon. + touch /run/TEST-82-SOFTREBOOT.touch + systemctl --no-block --check-inhibitors=yes soft-reboot + + # Now block until the soft-boot killing spree kills us + exec sleep infinity +fi + +systemd-analyze log-level info + +touch /testok +systemctl --no-block exit 123 |