Files
LithosAnanke/demo/console.py
T
Claude 3bd81097e1 chore(license): an SPDX header on every source file that had none (LicenseRef-Starship-1.0) [skip ci]
The header scan (Captain Bob, 2026-11-10): 788 source files -- 351 with the
SL-1.0 header, 12 with the old name (docs/working/experiments, kept by
ruling), 31 under someone else's licence (tools/kconfig, Linux's, GPL-2.0:
kept), and 394 with none. Ruled: the SPDX form; capsules (.4th, .f18) and
proofs (.thy) left for a ruling of their own (mkcapsule's 64x16 block format,
content-addressed capsules).

253 files now begin
    SPDX-License-Identifier: LicenseRef-Starship-1.0
    Copyright (c) 2023-2026 Robert A. James
as a /* */ block (C, headers, assembly, linker scripts) or # lines (scripts,
Makefiles, Kconfig; below a shebang or a coding line); tools/mkcapsule.c,
which had the copyright line, gets the SPDX line only.

Not touched: docs/working/ (archive and old campaign material, as the
ruling kept the experiments); v4/system/post_cases.c (generated by
v4/tools/mkpost.py, "do not edit" -- the generator has the header).

Comments only: the kernel builds on amd64, aarch64, riscv64; make -C v4
test-32: 39 programs, 0 failures; every changed script parses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-10 04:12:49 +00:00

63 lines
2.4 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-License-Identifier: LicenseRef-Starship-1.0
# Copyright (c) 2023-2026 Robert A. James
"""console.py -- the visitor's terminal joined to the demo machine's serial console, once the
machine is running (issue #112).
console.py <qmp.sock> <console.sock>
A resumed machine takes a moment to load. Anything typed meanwhile would reach a serial port
whose state is about to be replaced, and be lost half-way through a line ("3 4 + ." arriving as
"4 + ."). So: wait until QEMU says the machine is running, throw away what was typed before,
say so, and only then pass bytes both ways. Ends when either side closes.
"""
import json, os, select, socket, sys, termios, time, tty
qmp_path, con_path = sys.argv[1], sys.argv[2]
def connect(path, limit=30):
end = time.time() + limit
while True:
try:
s = socket.socket(socket.AF_UNIX); s.connect(path); return s
except OSError:
if time.time() > end: raise
time.sleep(0.1)
qmp = connect(qmp_path).makefile("rw")
qmp.readline()
qmp.write(json.dumps({"execute": "qmp_capabilities"}) + "\n"); qmp.flush(); qmp.readline()
end = time.time() + 60
while True: # until the machine runs
qmp.write(json.dumps({"execute": "query-status"}) + "\n"); qmp.flush()
while True:
r = json.loads(qmp.readline())
if "return" in r or "error" in r: break # skip events
if r.get("return", {}).get("status") == "running": break
if time.time() > end: sys.exit("The machine did not start.")
time.sleep(0.1)
con = connect(con_path)
stdin, stdout = sys.stdin.fileno(), sys.stdout.fileno()
saved = None
if os.isatty(stdin):
termios.tcflush(stdin, termios.TCIFLUSH) # what was typed while the machine was loading
saved = termios.tcgetattr(stdin)
tty.setraw(stdin) # the machine echoes and edits the line itself
os.write(stdout, b"Ready -- type a line and press Enter.\r\n")
try:
while True:
r, _, _ = select.select([stdin, con], [], [])
if stdin in r:
data = os.read(stdin, 1024)
if not data: break
con.sendall(data)
if con in r:
data = con.recv(4096)
if not data: break
os.write(stdout, data)
except OSError:
pass # the visitor's terminal went away
finally:
if saved: termios.tcsetattr(stdin, termios.TCSADRAIN, saved)