#!/bin/bash
# =====================================================================
# Presses the virtual power button, from the unit's ExecStop=.
#
# WHY THIS EXISTS
#
# Stopping the unit used to SIGTERM qemu, which from inside the machine
# is indistinguishable from pulling the plug out of the wall. For a
# session that is fine - the overlay is thrown away either side of it -
# but a MAINTENANCE boot is the operator editing the base image that
# every future session runs from, and yanking power on that is how a
# frozen image acquires a dirty filesystem, or a half-written registry.
#
# ExecStop= runs while the main process is still alive, so this sends
# `system_powerdown` over QMP and returns. systemd then waits for qemu
# to exit by itself, up to TimeoutStopSec - and if the guest ignores the
# button, or has already hung, the SIGTERM still happens afterwards. So
# this is a courtesy with a deadline, not a new way to get stuck.
#
# Deliberately never fails: a stop path that can error is a stop path
# that leaves the unit in `failed` and the next start blocked.
# =====================================================================
set -u

SESSION="${1:-${OWUIVM_SESSION:-}}"
[[ "$SESSION" =~ ^[0-9a-f]{32}$ ]] || exit 0
SOCK="/run/owuivm/$SESSION/qmp.sock"
[ -S "$SOCK" ] || exit 0

/usr/bin/python3 - "$SOCK" <<'PY' 2>/dev/null || true
import json, socket, sys, time
path = sys.argv[1]
try:
    s = socket.socket(socket.AF_UNIX); s.settimeout(5); s.connect(path)
    f = s.makefile("rwb")
    f.readline()                                    # greeting
    f.write(b'{"execute":"qmp_capabilities"}\n'); f.flush(); f.readline()
    f.write(b'{"execute":"system_powerdown"}\n'); f.flush(); f.readline()
    # Give the guest a moment to notice the button before systemd starts
    # counting; an OS that has begun shutting down will not be helped by
    # being asked twice.
    deadline = time.monotonic() + 90
    while time.monotonic() < deadline:
        f.write(b'{"execute":"query-status"}\n'); f.flush()
        line = f.readline()
        if not line:
            break                                   # qemu has gone: done
        try:
            if not (json.loads(line).get("return") or {}).get("running", True):
                break
        except ValueError:
            pass
        time.sleep(1)
except Exception:
    pass
PY
exit 0
