diff --git a/demo/qemu-args.sh b/demo/qemu-args.sh index cedd1c0b..19474994 100644 --- a/demo/qemu-args.sh +++ b/demo/qemu-args.sh @@ -3,8 +3,10 @@ # A session resumes a machine saved by make-snapshot.sh, and QEMU can resume a saved machine only # into the same machine: the same board, CPU, memory, firmware and devices. Both scripts build the # command line here, so they cannot drift apart. It is the machine `make -f kernel/Makefile -# ARCH=amd64 qemu` boots (q35, TCG, 1 GiB, OVMF, the ISO as a CD), less what a visitor must not -# have: no network device, no Artemis disk, no display, and QEMU's seccomp sandbox on. +# ARCH=amd64 qemu` boots (q35, TCG, OVMF, the ISO as a CD) -- in 384 MiB, not its 1 GiB: v4 boots +# to Zuse's prompt from 256 MiB, refuses at 128 ("no memory for the block chain"), measured +# 2026-11-07 (#116) -- less what a visitor must not have: no network device, no Artemis disk, no +# display, and QEMU's seccomp sandbox on. # # demo_qemu_args -> fills the array QEMU_ARGS # @@ -18,7 +20,7 @@ demo_qemu_args() { QEMU_ARGS=( -machine q35,accel=tcg -cpu qemu64,+rdrand - -m 1024 + -m 384 # v4 boots from 256 MiB; 384 leaves room (#116) -smp 1 -drive "if=pflash,format=raw,readonly=on,file=$OVMF_CODE" -drive "if=pflash,format=raw,file=$vars" diff --git a/demo/starshipos-demo.service b/demo/starshipos-demo.service index 2285a281..08ca318a 100644 --- a/demo/starshipos-demo.service +++ b/demo/starshipos-demo.service @@ -13,8 +13,13 @@ User=starshipos-demo Group=starshipos-demo Environment=DEMO_STATE=/var/lib/starshipos-demo Environment=DEMO_SESSION_LIMIT=1800 -# -m: sessions at once -- each a QEMU of about 1 GiB and most of a core (issue #116 measures it) -ExecStart=/usr/bin/ttyd --port 7681 --interface lo --writable --max-clients 3 \ +Environment=DEMO_MAX_GUESTS=2 +# the guests' slot locks (start-session.sh): /run/starshipos-demo, made for this user, gone at stop +RuntimeDirectory=starshipos-demo +# --max-clients: the guests and a few more, so a visitor over the limit reaches the busy message +# instead of a refused connection; still a cap on open connections +# each guest a QEMU of 384 MiB and most of a core (demo/qemu-args.sh; #116) +ExecStart=/usr/bin/ttyd --port 7681 --interface lo --writable --max-clients 6 \ --client-option titleFixed=StarshipOS --client-option disableLeaveAlert=true \ /opt/starshipos-demo/demo/start-session.sh Restart=on-failure diff --git a/demo/start-session.sh b/demo/start-session.sh index 09d4286f..b42340d8 100755 --- a/demo/start-session.sh +++ b/demo/start-session.sh @@ -12,8 +12,29 @@ here=$(cd "$(dirname "$0")" && pwd) . "$here/qemu-args.sh" dir=${1:-${DEMO_STATE:-/var/lib/starshipos-demo}} limit=${DEMO_SESSION_LIMIT:-1800} # seconds a session may last +guests=${DEMO_MAX_GUESTS:-2} # machines at once: 384 MiB each (qemu-args.sh, #116) +slots=${DEMO_SLOTS:-/run/starshipos-demo} # a lock file per machine [ -f "$dir/state" ] || { echo "The demo is not set up yet: no saved machine in $dir." >&2; exit 1; } + +# One of $guests slots, or a busy message (Captain Bob, 2026-11-07: "Limit guests and have a Busy, +# try back in a few minutes"). A slot is a lock this script holds for the session's life (flock -n: +# two visitors cannot both take the last one); it goes when the script does, however it ends. The +# machine's processes are started without the lock's descriptor ({slot}>&-), so it is this +# script's alone. +mkdir -p "$slots" +slot= +for i in $(seq 1 "$guests"); do + exec {fd}>"$slots/slot-$i.lock" + if flock -n "$fd"; then slot=$fd; break; fi + exec {fd}>&- +done +if [ -z "$slot" ]; then + printf '\r\nEvery StarshipOS machine is busy right now (%d at once on this small server).\r\n' "$guests" + printf 'Try back in a few minutes.\r\n' + sleep 20 + exit 0 +fi tmp=$(mktemp -d "${TMPDIR:-/tmp}/starshipos-session.XXXXXX") trap 'rm -rf "$tmp"' EXIT cp "$dir/vars.fd" "$tmp/vars.fd" @@ -28,13 +49,13 @@ printf 'Try: 3 4 + . : SQUARE DUP * ; 7 SQUARE .\r\n\r\n' qemu-system-x86_64 "${QEMU_ARGS[@]}" \ -chardev "socket,id=con,path=$tmp/con.sock,server=on,wait=off" -serial chardev:con \ -qmp "unix:$tmp/qmp.sock,server=on,wait=off" -monitor none \ - -incoming "file:$dir/state" &- & qemu=$! -( sleep "$limit"; kill "$qemu" 2>/dev/null ) & +( sleep "$limit"; kill "$qemu" 2>/dev/null ) {slot}>&- & timer=$! # The bridge is a tracked child too: bash runs a trap only once its foreground command returns, # so a hang-up would otherwise wait on the bridge. `wait` returns to the trap at once. -python3 -I "$here/console.py" "$tmp/qmp.sock" "$tmp/con.sock" 0<&0 & +python3 -I "$here/console.py" "$tmp/qmp.sock" "$tmp/con.sock" 0<&0 {slot}>&- & bridge=$! trap 'kill "$bridge" "$qemu" "$timer" 2>/dev/null; rm -rf "$tmp"' EXIT trap 'exit 0' HUP TERM INT