fix: reliably answer delayed DOS batch termination prompts

This commit is contained in:
2026-08-16 18:11:52 +09:00
parent ec48a60e3e
commit cba2c86fbc
+12 -11
View File
@@ -235,11 +235,10 @@ class Host:
reported = started
interrupt_at: float | None = None
interrupted = ""
answered = False
def supervise() -> None:
"""Called every EXEC_POLL_SECONDS while the agent stays silent."""
nonlocal reported, interrupt_at, interrupted, answered
nonlocal reported, interrupt_at, interrupted
now = time.monotonic()
idle = self.guest_idle_seconds()
if now - reported >= 15:
@@ -259,16 +258,18 @@ class Host:
self.monitor("sendkey ctrl-c")
return
waited = now - interrupt_at
if not answered and waited > 4:
# COMMAND.COM asks "Terminate batch file (Y/N/A)?" for .BAT
# targets and sits at that prompt until it is answered.
answered = True
# DOS answers Ctrl+C with "Terminate batch file (Y/N/A)?" and
# waits there. The prompt only appears once COMMAND.COM reaches
# the next batch line, which can be many seconds into a slow
# command, so answer on every poll rather than once: a single
# early 'y' is swallowed by whatever is still running. Send only
# 'y' -- the prompt takes one keystroke, and a trailing Enter
# gets read as "keep going".
self.monitor("sendkey y")
self.monitor("sendkey ret")
# Ctrl+C only lands at a DOS break check. With BREAK=OFF (the
# FreeDOS default) a compute-bound child whose output we
# redirected to a file may never reach one, so the command runs
# to completion regardless. Keep collecting its result rather
# Even answered, Ctrl+C is a request. It lands only at a DOS
# break check, and a DOS/4GW child (wcc386, wmake) runs in
# protected mode where it may never reach one, so the command
# can still run to completion. Keep collecting its result rather
# than abandoning a stream that still owes us one -- give up
# only once the guest has gone quiet too.
if waited > INTERRUPT_GRACE_SECONDS and (idle is None or idle > 5):