#!/bin/sh
set -eu

PRE15_LIB_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/lib" && pwd -P)
PRE15_QEMU_SCRIPT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)/run-qemu.sh
export PRE15_LIB_DIR PRE15_QEMU_SCRIPT
. "$PRE15_LIB_DIR/runner.sh"
. "$PRE15_LIB_DIR/manifest.sh"

pre15_pick_port()
{
	python3 - <<'PY'
import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.bind(("127.0.0.1", 0))
    print(sock.getsockname()[1])
PY
}

pre15_wait_for_ssh()
{
	pre15_ssh_wait=0
	while test "$pre15_ssh_wait" -lt "${PRE15_QEMU_BOOT_TIMEOUT:-180}"; do
		if ! kill -0 "$PRE15_QEMU_PID" 2>/dev/null; then
			pre15_infra_blocked 'QEMU exited before SSH became ready'
		fi
		if pre15_guest_ssh true >/dev/null 2>&1; then
			return 0
		fi
		sleep 1
		pre15_ssh_wait=$((pre15_ssh_wait + 1))
	done
	pre15_infra_blocked 'guest SSH did not become ready before the boot deadline'
}

pre15_qemu_worker()
{
	pre15_qemu_case=$1
	pre15_require_paths
	pre15_qemu_case_script=$(pre15_find_case "$pre15_qemu_case")
	: "${PRE15_QEMU_BASE_IMAGE:?PRE15_QEMU_BASE_IMAGE is required}"
	: "${PRE15_QEMU_SSH_KEY:?PRE15_QEMU_SSH_KEY is required}"
	PRE15_QEMU_SSH_USER=${PRE15_QEMU_SSH_USER:-root}
	PRE15_QEMU_BASE_FORMAT=${PRE15_QEMU_BASE_FORMAT:-qcow2}
	export PRE15_QEMU_SSH_USER PRE15_QEMU_BASE_FORMAT
	for pre15_qemu_tool in qemu-img qemu-system-x86_64 ssh; do
		command -v "$pre15_qemu_tool" >/dev/null 2>&1 || \
			pre15_infra_blocked "missing QEMU runner tool: $pre15_qemu_tool"
	done
	test -f "$PRE15_QEMU_SSH_KEY" || \
		pre15_infra_blocked "SSH key is absent: $PRE15_QEMU_SSH_KEY"
	pre15_own_base_image "$PRE15_QEMU_BASE_IMAGE"
	pre15_qemu_overlay=$PRE15_RUN_DIR/guest-overlay.qcow2
	qemu-img create -f qcow2 -F "$PRE15_QEMU_BASE_FORMAT" \
		-b "$PRE15_QEMU_BASE_IMAGE" "$pre15_qemu_overlay"
	pre15_own_path "$pre15_qemu_overlay" 'QEMU overlay'
	PRE15_QEMU_SSH_PORT=$(pre15_pick_port)
	export PRE15_QEMU_SSH_PORT
	pre15_own_port "$PRE15_QEMU_SSH_PORT" 'QEMU SSH forwarding'
	PRE15_QEMU_CONTROL_PATH=$PRE15_RUN_DIR/ssh-control
	PRE15_QEMU_SERIAL=$PRE15_RUN_DIR/serial.log
	PRE15_QEMU_DEBUG=$PRE15_RUN_DIR/qemu.log
	export PRE15_QEMU_CONTROL_PATH PRE15_QEMU_SERIAL PRE15_QEMU_DEBUG
	pre15_write_argv_json "$PRE15_RUN_DIR/qemu-argv.json" \
		qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 \
		-m "${PRE15_QEMU_MEMORY_MB:-6144}" -smp "${PRE15_QEMU_CPUS:-4}" \
		-drive "file=$pre15_qemu_overlay,if=virtio,format=qcow2" \
		-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:$PRE15_QEMU_SSH_PORT-:22" \
		-device virtio-net-pci,netdev=net0 -display none \
		-serial "file:$PRE15_QEMU_SERIAL" -monitor none
	qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 \
		-m "${PRE15_QEMU_MEMORY_MB:-6144}" -smp "${PRE15_QEMU_CPUS:-4}" \
		-drive "file=$pre15_qemu_overlay,if=virtio,format=qcow2" \
		-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:$PRE15_QEMU_SSH_PORT-:22" \
		-device virtio-net-pci,netdev=net0 -display none \
		-serial "file:$PRE15_QEMU_SERIAL" -monitor none &
	PRE15_QEMU_PID=$!
	export PRE15_QEMU_PID
	pre15_own_pid "$PRE15_QEMU_PID" QEMU
	pre15_wait_for_ssh
	if ssh -MNf -o BatchMode=yes -o StrictHostKeyChecking=no \
	    -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 \
	    -o ControlMaster=yes -o "ControlPath=$PRE15_QEMU_CONTROL_PATH" \
	    -i "$PRE15_QEMU_SSH_KEY" -p "$PRE15_QEMU_SSH_PORT" \
	    "$PRE15_QEMU_SSH_USER@127.0.0.1"; then
		pre15_own_ssh_control "$PRE15_QEMU_CONTROL_PATH" 'QEMU SSH ControlMaster'
	else
		pre15_infra_blocked 'could not establish SSH ControlMaster'
	fi
	pre15_guest_ssh_bounded uname -a > "$PRE15_RUN_DIR/guest-uname.txt"
	pre15_guest_ssh_bounded kldstat > "$PRE15_RUN_DIR/guest-kldstat-before.txt"
	pre15_guest_ssh_bounded dmesg > "$PRE15_RUN_DIR/guest-dmesg-before.txt"
	/bin/sh "$pre15_qemu_case_script"
	pre15_guest_ssh_bounded dmesg > "$PRE15_RUN_DIR/guest-dmesg-after.txt"
}

if test "${1:-}" = --worker; then
	shift
	test "$#" -eq 1 || pre15_fail_usage 'internal QEMU worker requires one case'
	pre15_qemu_worker "$1"
	exit 0
fi

test "$#" -eq 1 || pre15_fail_usage 'usage: run-qemu.sh CASE'
pre15_require_paths
pre15_find_case "$1" >/dev/null
pre15_run_command qemu "$1" "${PRE15_QEMU_TIMEOUT:-1200}" \
	"$PRE15_QEMU_SCRIPT" --worker "$1"
