# ARCH picks the qemu binary, the machine type and the display device.
# Nothing here needs /dev/kvm: another architecture is emulated.

"$VMM" create ar1 64M >/dev/null 2>&1
FW=/usr/share/qemu/edk2-aarch64-code.fd
[ -f "$FW" ] || FW=/usr/share/AAVMF/AAVMF_CODE.fd

cfg ar1 <<EOF
ARCH=riscv64
EOF
refuses 'ARCH must be' "an unsupported ARCH is refused" dryrun ar1

cfg ar1 <<EOF
ARCH=aarch64
EOF
refuses 'requires FIRMWARE' "aarch64 without firmware is refused" dryrun ar1

cfg ar1 <<EOF
FIRMWARE=firmware.fd
EOF
refuses 'FIRMWARE must be an absolute path' "a relative FIRMWARE is refused" dryrun ar1

# The host's own architecture is accelerated. -cpu host means nothing
# anywhere else, and asking for it under emulation is an error.
: > "$VMMDIR/ar1/config"
outputs 'accel=kvm' "the host architecture gets kvm" dryrun ar1
outputs "-cpu 'host'" "the host architecture gets the host cpu" dryrun ar1

# A host with no /dev/kvm can still run its own architecture, slowly.
cfg ar1 <<EOF
HWACCEL=no
EOF
outputs 'accel=tcg' "HWACCEL=no emulates the host architecture" dryrun ar1
outputs "-cpu 'max'" "HWACCEL=no drops -cpu host, which needs kvm" dryrun ar1

cfg ar1 <<EOF
HWACCEL=maybe
EOF
refuses 'HWACCEL must be' "HWACCEL takes yes or no" dryrun ar1

cfg ar1 <<EOF
ARCH=aarch64
FIRMWARE="$FW"
GRAPHICS=vnc
EOF
outputs 'qemu-system-aarch64' "ARCH picks the qemu binary" dryrun ar1
outputs "machine 'virt" "aarch64 gets the virt machine" dryrun ar1
outputs 'accel=tcg' "a foreign architecture is emulated" dryrun ar1
outputs "-cpu 'max'" "an emulated guest does not ask for the host cpu" dryrun ar1
outputs "bios '$FW'" "the firmware reaches the argv" dryrun ar1
outputs 'virtio-gpu-pci' "aarch64 gets a display device that is not VGA" dryrun ar1

# ARCH names a binary that may not be installed. Saying so is instant;
# leaving it to setsid costs both monitor timeouts first.
NOQEMU=$WORK/noqemu-bin
mkdir -p "$NOQEMU"
for b in flock uname od awk cat tr; do
	ln -sf "$(command -v "$b")" "$NOQEMU/$b"
done
: > "$VMMDIR/ar1/config"
out=$(PATH=$NOQEMU "$VMM" start ar1 2>&1) && got=0 || got=$?
if [ "$got" != 0 ] && printf '%s' "$out" | grep -q 'qemu-system-.* is not on PATH'; then
	ok "a qemu that is not installed is named at once"
else
	notok "a qemu that is not installed is named at once" "exit $got: $out"
fi

# Emulated end to end: the firmware really runs and really talks to the
# serial console, which is the whole point of passing -bios.
if [ -f "$FW" ] && command -v qemu-system-aarch64 >/dev/null 2>&1; then
	cfg ar1 <<EOF
ARCH=aarch64
FIRMWARE="$FW"
MEM=512
CPUS=1
EOF
	exits 0 "an emulated aarch64 guest starts" start ar1
	outputs 'running' "the emulated guest answers its monitor" \
		monitor ar1 info status
	sleep 2
	holds "the emulated firmware writes to the serial console" \
		test -s "$VMMDIR/ar1/console.log"
	"$VMM" kill ar1 >/dev/null 2>&1 || :
else
	skip 3 "no qemu-system-aarch64 or no aarch64 firmware"
fi

# Failure after spawn must reap only that child and remove every runtime
# object it may have created.
if command -v "qemu-system-$(uname -m)" >/dev/null 2>&1; then
	"$VMM" create ar2 64M >/dev/null 2>&1
	printf 'not a qcow2 image\n' > "$VMMDIR/ar2/disk.qcow2"
	cfg ar2 <<EOF
HWACCEL=no
MEM=256
CPUS=1
EOF
	exits 1 "a qemu startup failure is reported" start ar2
	holds "a failed start leaves no pidfile" test ! -e "$VMMDIR/ar2/pid"
	holds "a failed start leaves no input FIFO" test ! -e "$VMMDIR/ar2/qmp.in"
	holds "a failed start leaves no output FIFO" test ! -e "$VMMDIR/ar2/qmp.out"
	holds "a failed start releases its lock" \
		flock -n "$VMMDIR/ar2" true
else
	skip 5 "no qemu for the host architecture"
fi

# A qemu that never opens the monitor does not die of its own accord, and
# it has written no pidfile to be found by. It is still vmm's own child.
# The stand-in does not exec, so it keeps the argv this looks for.
FAKEBIN=$WORK/fake-qemu-bin
FAKE=$FAKEBIN/qemu-system-$(uname -m)
mkdir -p "$FAKEBIN"
cat > "$FAKE" <<'EOF'
#!/bin/sh
sleep 5
EOF
chmod +x "$FAKE"
"$VMM" create ar3 64M >/dev/null 2>&1
out=$(PATH=$FAKEBIN:$PATH MONITOR_TIMEOUT=1 "$VMM" start ar3 2>&1) && got=0 || got=$?
if [ "$got" = 1 ]; then
	ok "a start whose monitor never answers fails"
else
	notok "a start whose monitor never answers fails" "exit $got: $out"
fi
left=
for cmdline in /proc/[0-9]*/cmdline; do
	line=
	IFS= read -r line 2>/dev/null < "$cmdline" || :
	case $line in
	*"-pidfile$VMMDIR/ar3/pid")
		p=${cmdline#/proc/}
		left="$left ${p%/cmdline}"
		;;
	esac
done
if [ -z "$left" ]; then
	ok "and kills the child it spawned"
else
	notok "and kills the child it spawned" "still running:$left"
	# shellcheck disable=SC2086  # a list of pids this loop collected
	kill -9 $left 2>/dev/null || :
fi
