#!/bin/bash # make-snapshot.sh -- boot the demo machine once, to its prompt, and save it: every session # resumes from here, at the prompt in seconds instead of after boot and POST (issue #113). # # demo/make-snapshot.sh [identity.img] # # gets the image, the identity drive if any, the firmware's variables as the boot # left them (vars.fd) and the machine's state (state). One QEMU, in the foreground. set -euo pipefail here=$(cd "$(dirname "$0")" && pwd) . "$here/qemu-args.sh" iso=${1:?usage: demo/make-snapshot.sh [identity.img]} dir=$(realpath -m "${2:?usage: demo/make-snapshot.sh [identity.img]}") ident=${3:-} prompt=${DEMO_PROMPT:-ok> } # what the console prints when it is ready boot_limit=${DEMO_BOOT_LIMIT:-600} # seconds a boot may take, POST included rm -rf "$dir"; mkdir -p "$dir" cp "$iso" "$dir/StarshipOS.iso" [ -z "$ident" ] || cp "$ident" "$dir/identity.img" cp /usr/share/OVMF/OVMF_VARS_4M.fd "$dir/vars.fd" demo_qemu_args "$dir" "$dir/vars.fd" log=$dir/boot.log qmp=$dir/qmp.sock qemu-system-x86_64 "${QEMU_ARGS[@]}" -serial "file:$log" -qmp "unix:$qmp,server=on,wait=off" & pid=$! trap 'kill $pid 2>/dev/null || true' EXIT qmp() { # one QMP command, after the greeting and capabilities; prints QEMU's replies python3 - "$qmp" "$1" <<'PY' import json, socket, sys, time s = socket.socket(socket.AF_UNIX); s.connect(sys.argv[1]); f = s.makefile("rw") f.readline(); f.write(json.dumps({"execute": "qmp_capabilities"}) + "\n"); f.flush(); f.readline() f.write(sys.argv[2] + "\n"); f.flush(); print(f.readline().strip()) PY } echo "demo: booting $iso to '$prompt' (at most ${boot_limit}s)" t0=$(date +%s) until [ -S "$qmp" ] && grep -aqF -- "$prompt" "$log" 2>/dev/null; do kill -0 $pid 2>/dev/null || { echo "demo: QEMU exited during the boot" >&2; tail -5 "$log" >&2; exit 1; } [ $(( $(date +%s) - t0 )) -lt "$boot_limit" ] || { echo "demo: no prompt in ${boot_limit}s" >&2; tail -5 "$log" >&2; exit 1; } sleep 1 done echo "demo: at the prompt after $(( $(date +%s) - t0 ))s; saving" sleep 2 # let the console finish the line # Saved running, not stopped first: QEMU saves the run state too, and a machine saved stopped # resumes stopped -- every session would be a frozen screen. Migration stops it at the end itself. qmp "{\"execute\":\"migrate\",\"arguments\":{\"uri\":\"file:$dir/state\"}}" >/dev/null until qmp '{"execute":"query-migrate"}' | grep -q '"status": "completed"'; do qmp '{"execute":"query-migrate"}' | grep -q '"status": "failed"' && { echo "demo: saving failed" >&2; exit 1; } sleep 1 done qmp '{"execute":"quit"}' >/dev/null || true wait $pid 2>/dev/null || true trap - EXIT rm -f "$qmp" echo "demo: saved $(du -h "$dir/state" | cut -f1) to $dir/state"