From 866d327f0bad3a0730801a22654bac45e02ee14b Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 20 Jul 2026 23:40:56 -0500 Subject: fix(test): harden four VM-framework paths from the S5 audit init_vm_paths suffixed the disk and NVRAM by FS_PROFILE but left PID_FILE, MONITOR_SOCK, and SERIAL_LOG shared. A concurrent btrfs and zfs run read each other's PID file, so the second run's stop logic could kill the first run's VM. All runtime paths now carry the suffix. kill_qemu sent kill -9 and returned without waiting, so the force-kill fallback could run qemu-img snapshot against a qcow2 the dying qemu still held locked (the restore failed silently behind || true, leaving the base image dirty). It now reaps or polls the process to death before returning. debug-vm.sh hardcoded the btrfs base-disk name, so FS_PROFILE=zfs booted the wrong base or fatalled. It now takes DISK_PATH from init_vm_paths. Both runners reported a completion-marker grep as "ArchSetup Exit Code," but the installer runs detached with set -e off, so its real exit status was never captured -- a run that errored mid-way and still reached its last line reported exit 0. The flag is now ARCHSETUP_COMPLETED with the report labeled honestly; Testinfra stays the pass/fail authority. --- scripts/testing/lib/vm-utils.sh | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'scripts/testing/lib/vm-utils.sh') diff --git a/scripts/testing/lib/vm-utils.sh b/scripts/testing/lib/vm-utils.sh index b85e773..b6686e9 100755 --- a/scripts/testing/lib/vm-utils.sh +++ b/scripts/testing/lib/vm-utils.sh @@ -66,9 +66,12 @@ init_vm_paths() { # let a zfs run's ZFSBootMenu entries clobber the btrfs GRUB entry, leaving # the btrfs base unbootable (no removable ESP fallback to recover from). OVMF_VARS="$VM_IMAGES_DIR/OVMF_VARS${img_suffix}.fd" - PID_FILE="$VM_IMAGES_DIR/qemu.pid" - MONITOR_SOCK="$VM_IMAGES_DIR/qemu-monitor.sock" - SERIAL_LOG="$VM_IMAGES_DIR/qemu-serial.log" + # Runtime paths carry the same suffix: a concurrent btrfs and zfs run + # sharing one PID file let the second run's stop logic kill the first + # run's VM, and both truncated the same serial log. + PID_FILE="$VM_IMAGES_DIR/qemu${img_suffix}.pid" + MONITOR_SOCK="$VM_IMAGES_DIR/qemu-monitor${img_suffix}.sock" + SERIAL_LOG="$VM_IMAGES_DIR/qemu-serial${img_suffix}.log" mkdir -p "$VM_IMAGES_DIR" } @@ -287,6 +290,18 @@ kill_qemu() { pid=$(cat "$PID_FILE" 2>/dev/null) if [ -n "$pid" ]; then kill -9 "$pid" 2>/dev/null || true + # Wait for the process to actually die before returning: the + # force-kill fallback is followed by restore_snapshot, and + # qemu-img refuses a qcow2 the dying qemu still holds locked. + # `wait` reaps it when it's our child; otherwise poll until the + # /proc entry is gone or a zombie (fds released either way). + wait "$pid" 2>/dev/null || true + local i state + for i in $(seq 1 50); do + state=$(awk '{print $3}' "/proc/$pid/stat" 2>/dev/null) || state="" + [ -z "$state" ] || [ "$state" = "Z" ] && break + sleep 0.1 + done fi fi _cleanup_qemu_files -- cgit v1.2.3