From: Lennart Poettering Date: Thu, 28 May 2026 10:20:59 +0000 (+0200) Subject: test: verify bootctl link-auto and io.systemd.BootControl.LinkAuto X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9ac1b6c12433acc531cbced384553472476e2271;p=thirdparty%2Fsystemd.git test: verify bootctl link-auto and io.systemd.BootControl.LinkAuto Add a TEST-87 testcase exercising "bootctl link-auto" and the equivalent io.systemd.BootControl.LinkAuto() Varlink method: a UKI plus extras are staged below the search directories and we assert the kernel and sidecar resources are linked into $BOOT. Covered: plain kernel.efi + extras.d/, versioned kernel.efi.v/ and extras .v/ resolved via vpick, directory priority (/etc wins over /run), the no-op case when nothing is staged, and the Varlink method including its empty reply when there is nothing to link. --- diff --git a/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh b/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh index 2b095c1fbcb..0b19acd0173 100755 --- a/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh +++ b/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh @@ -710,4 +710,162 @@ EOF '{"id":"foo*.conf"}') } +cleanup_link_auto() { + rm -rf /run/systemd/uki /etc/systemd/uki + if [[ -n "${LINK_WORKDIR:-}" ]]; then + rm -rf "$LINK_WORKDIR" + unset LINK_WORKDIR + fi + restore_esp +} + +testcase_bootctl_link_auto() { + if ! command -v ukify >/dev/null; then + echo "ukify not found, skipping." + return 0 + fi + + backup_esp + LINK_WORKDIR="$(mktemp --directory /tmp/test-bootctl-link-auto.XXXXXXXXXX)" + trap cleanup_link_auto RETURN ERR + + # Ensure loader/entries directory is present + bootctl install --make-entry-directory=yes + + local ESP + ESP="$(bootctl --print-esp-path)" + + cat >"$LINK_WORKDIR/os-release" <<'EOF' +ID=testos +NAME="Test OS" +PRETTY_NAME="Test OS" +EOF + echo "fake-kernel" >"$LINK_WORKDIR/vmlinuz" + echo "fake-initrd" >"$LINK_WORKDIR/initrd" + + # Two distinct UKIs, so we can tell which one was picked up. + ukify build \ + --linux "$LINK_WORKDIR/vmlinuz" \ + --initrd "$LINK_WORKDIR/initrd" \ + --os-release "@$LINK_WORKDIR/os-release" \ + --uname "1.2.3-testkernel" \ + --cmdline "quiet uki=a" \ + --output "$LINK_WORKDIR/uki_a.efi" + ukify build \ + --linux "$LINK_WORKDIR/vmlinuz" \ + --initrd "$LINK_WORKDIR/initrd" \ + --os-release "@$LINK_WORKDIR/os-release" \ + --uname "1.2.3-testkernel" \ + --cmdline "quiet uki=b" \ + --output "$LINK_WORKDIR/uki_b.efi" + + local TOKEN="systemdtest" + local BOOTCTL=(bootctl "--entry-token=literal:$TOKEN") + local ENTRY="$ESP/loader/entries/${TOKEN}-commit_1.conf" + + # --- Test 1: link-auto picks up kernel.efi + extras.d/ from /run/systemd/uki/ --- + rm -rf /run/systemd/uki + mkdir -p /run/systemd/uki/extras.d + cp "$LINK_WORKDIR/uki_a.efi" /run/systemd/uki/kernel.efi + echo "sysext-data" >/run/systemd/uki/extras.d/hello.sysext.raw + echo "confext-data" >/run/systemd/uki/extras.d/hello.confext.raw + echo "cred-data" >/run/systemd/uki/extras.d/hello.cred + + "${BOOTCTL[@]}" link-auto + + test -f "$ENTRY" + test -f "$ESP/$TOKEN/kernel.efi" + cmp "$LINK_WORKDIR/uki_a.efi" "$ESP/$TOKEN/kernel.efi" + test -f "$ESP/$TOKEN/hello.sysext.raw" + test -f "$ESP/$TOKEN/hello.confext.raw" + test -f "$ESP/$TOKEN/hello.cred" + grep "^uki /${TOKEN}/kernel.efi\$" "$ENTRY" >/dev/null + grep "^extra /${TOKEN}/hello.sysext.raw\$" "$ENTRY" >/dev/null + grep "^extra /${TOKEN}/hello.confext.raw\$" "$ENTRY" >/dev/null + grep "^extra /${TOKEN}/hello.cred\$" "$ENTRY" >/dev/null + + "${BOOTCTL[@]}" unlink "${TOKEN}-commit_1.conf" + test ! -e "$ENTRY" + test ! -e "$ESP/$TOKEN/kernel.efi" + + # --- Test 2: versioned kernel.efi.v/ and extras .v/ are resolved via vpick --- + rm -rf /run/systemd/uki + mkdir -p /run/systemd/uki/kernel.efi.v /run/systemd/uki/extras.d/hello.sysext.raw.v + cp "$LINK_WORKDIR/uki_a.efi" /run/systemd/uki/kernel.efi.v/kernel_1.0.efi + cp "$LINK_WORKDIR/uki_a.efi" /run/systemd/uki/kernel.efi.v/kernel_2.0.efi + echo "sysext-1" >/run/systemd/uki/extras.d/hello.sysext.raw.v/hello_1.0.sysext.raw + echo "sysext-2" >/run/systemd/uki/extras.d/hello.sysext.raw.v/hello_2.0.sysext.raw + + "${BOOTCTL[@]}" link-auto + + test -f "$ENTRY" + # vpick must select the newest version + test -f "$ESP/$TOKEN/kernel_2.0.efi" + test ! -e "$ESP/$TOKEN/kernel_1.0.efi" + test -f "$ESP/$TOKEN/hello_2.0.sysext.raw" + test ! -e "$ESP/$TOKEN/hello_1.0.sysext.raw" + grep "^uki /${TOKEN}/kernel_2.0.efi\$" "$ENTRY" >/dev/null + grep "^extra /${TOKEN}/hello_2.0.sysext.raw\$" "$ENTRY" >/dev/null + + "${BOOTCTL[@]}" unlink "${TOKEN}-commit_1.conf" + + # --- Test 3: priority — /etc/systemd/uki/ wins over /run/systemd/uki/ --- + rm -rf /run/systemd/uki /etc/systemd/uki + mkdir -p /run/systemd/uki/extras.d /etc/systemd/uki/extras.d + cp "$LINK_WORKDIR/uki_b.efi" /run/systemd/uki/kernel.efi + cp "$LINK_WORKDIR/uki_a.efi" /etc/systemd/uki/kernel.efi + echo "run-cred" >/run/systemd/uki/extras.d/hello.cred + echo "etc-cred" >/etc/systemd/uki/extras.d/hello.cred + + "${BOOTCTL[@]}" link-auto + + test -f "$ESP/$TOKEN/kernel.efi" + # The /etc copy (uki_a) must win over the /run copy (uki_b) + cmp "$LINK_WORKDIR/uki_a.efi" "$ESP/$TOKEN/kernel.efi" + cmp <(echo "etc-cred") "$ESP/$TOKEN/hello.cred" + + "${BOOTCTL[@]}" unlink "${TOKEN}-commit_1.conf" + rm -rf /etc/systemd/uki + + # --- Test 4: with nothing staged, link-auto is a successful no-op --- + rm -rf /run/systemd/uki + "${BOOTCTL[@]}" link-auto + # No entries referencing our token should remain. + local leftover + leftover="$(find "$ESP/loader/entries/" -name "*$TOKEN*" -print -quit 2>/dev/null)" + test -z "$leftover" + + # === Varlink coverage: io.systemd.BootControl.LinkAuto === + local BOOTCTL_BIN vreply vid vtoken + BOOTCTL_BIN="$(type -p bootctl)" + + # --- Test 5: LinkAuto discovers kernel.efi + extras --- + rm -rf /run/systemd/uki + mkdir -p /run/systemd/uki/extras.d + cp "$LINK_WORKDIR/uki_a.efi" /run/systemd/uki/kernel.efi + echo "cred-data" >/run/systemd/uki/extras.d/hello.cred + + vreply="$(varlinkctl call --json=short "$BOOTCTL_BIN" io.systemd.BootControl.LinkAuto '{}')" + vid="$(echo "$vreply" | jq -r '.ids[0]')" + test -n "$vid" + test "$vid" != "null" + vtoken="${vid%%-commit_*}" + test -n "$vtoken" + + test -f "$ESP/loader/entries/$vid" + test -f "$ESP/$vtoken/kernel.efi" + test -f "$ESP/$vtoken/hello.cred" + grep "^uki /$vtoken/kernel.efi\$" "$ESP/loader/entries/$vid" >/dev/null + + varlinkctl call --quiet "$BOOTCTL_BIN" io.systemd.BootControl.Unlink "{\"id\":\"$vid\"}" + test ! -e "$ESP/loader/entries/$vid" + test ! -e "$ESP/$vtoken/kernel.efi" + test ! -e "$ESP/$vtoken/hello.cred" + + # --- Test 6: LinkAuto with nothing staged returns an empty id list --- + rm -rf /run/systemd/uki + vreply="$(varlinkctl call --json=short "$BOOTCTL_BIN" io.systemd.BootControl.LinkAuto '{}')" + assert_eq "$(echo "$vreply" | jq -r '.ids | length')" "0" +} + run_testcases