DF-5561 / serial_writer.py
#!/usr/bin/env python3 # DF-5561 โ persistent writer for the guest's socket-backed serial console. # # Key details: # * The QEMU chardev socket stays connected across FIFO EOFs (a disconnect # would drop bytes still buffered in the emulated 16550). # * Lines are sent in <=8-byte chunks with pacing: the guest's UART RX FIFO # is 16 bytes and ddb polls cngetc without flow control. # Usage: # echo 'print db_active' > /tmp/opencode/df-5561-ser.in import os, socket, threading, time SOCK = "/tmp/opencode/df-5561-ser.sock" FIFO = "/tmp/opencode/df-5561-ser.in" DRAIN = "/tmp/opencode/df-5561-drain.log" # socket-side copy of guest output def connect(): while True: try: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(SOCK) return s except (FileNotFoundError, ConnectionRefusedError) as e: print(f"connect failed: {e}; retrying...", flush=True) time.sleep(2) def drainer(s): # MUST continuously recv(): QEMU applies backpressure to the guest UART # (poll-mode cnputc spins forever) once the chardev client buffer fills. with open(DRAIN, "ab", buffering=0) as d: while True: try: b = s.recv(65536) except OSError: return if not b: return d.write(b) def send_line(s, line): data = (line if line.endswith("\n") else line + "\n").encode() for i in range(0, len(data), 8): s.sendall(data[i:i+8]) time.sleep(0.15) def main(): try: os.remove(FIFO) except FileNotFoundError: pass os.mkfifo(FIFO) print(f"serial writer: {FIFO} -> {SOCK} (pid {os.getpid()})", flush=True) s = connect() print("connected to serial socket", flush=True) threading.Thread(target=drainer, args=(s,), daemon=True).start() while True: with open(FIFO, "r") as f: for line in f: line = line.rstrip("\n") if not line: continue try: print(f">>> {line}", flush=True) send_line(s, line) except OSError as e: print(f"send failed: {e}; reconnecting", flush=True) s = connect() send_line(s, line) # FIFO EOF (all writers closed) โ keep socket, reopen FIFO. print("[fifo eof โ keeping socket]", flush=True) if __name__ == "__main__": main() |