#!/bin/bash # vmctl.sh — drive a nested VM's console on demo-hp from DooPlex (rehearsal 2026-10-20). # The installer keeps the HUNGARIAN keyboard (as a household would), so characters are sent as the # scancodes a Hungarian-layout keyboard produces: y<->z swapped, '-' on the US '/' key, '0' on the # US '`' key, '@' = AltGr+V. Only the characters below are supported — anything else is refused. # # vmctl.sh shot console screenshot (QEMU screendump → PNG on DooPlex) # vmctl.sh key ... send raw keys (e.g. ret, tab, right, alt-n) # vmctl.sh type type text with the Hungarian mapping # vmctl.sh typevar type the value of an env var (use inside `sec run`; never echoed) set -euo pipefail VMID=$1; CMD=$2; shift 2 HOST=${VMHOST:-demo-hp} map_char() { local c=$1 case "$c" in [a-x]) echo "$c" ;; y) echo "z" ;; z) echo "y" ;; [A-X]) echo "shift-${c,,}" ;; Y) echo "shift-z" ;; Z) echo "shift-y" ;; [1-9]) echo "$c" ;; 0) echo "grave_accent" ;; .) echo "dot" ;; ,) echo "comma" ;; -) echo "slash" ;; _) echo "shift-slash" ;; @) echo "alt_r-v" ;; " ") echo "spc" ;; *) echo "UNSUPPORTED" ;; esac } send_text() { local s=$1 keys=() k i for (( i=0; i<${#s}; i++ )); do k=$(map_char "${s:$i:1}") if [ "$k" = UNSUPPORTED ]; then echo "vmctl: unsupported character at position $i" >&2; exit 2; fi keys+=("$k") done # one ssh, one qm sendkey per key (qm sendkey takes one key combo) printf '%s\n' "${keys[@]}" | ssh "$HOST" "while read -r k; do LC_ALL=C qm sendkey $VMID \"\$k\" >/dev/null 2>&1 || exit 3; sleep 0.08; done" } case "$CMD" in shot) out=$1 ssh "$HOST" "rm -f /tmp/vmshot-$VMID.ppm; echo 'screendump /tmp/vmshot-$VMID.ppm' | LC_ALL=C qm monitor $VMID >/dev/null 2>&1; sleep 1; cat /tmp/vmshot-$VMID.ppm" > "$out.ppm" convert "$out.ppm" "$out"; rm -f "$out.ppm"; echo "shot: $out" ;; key) for k in "$@"; do ssh "$HOST" "LC_ALL=C qm sendkey $VMID '$k'"; sleep 0.3; done ;; type) send_text "$1" ;; typevar) v=${!1:-}; [ -n "$v" ] || { echo "vmctl: env $1 empty" >&2; exit 2; } send_text "$v" ;; *) echo "usage: see header" >&2; exit 1 ;; esac