fix(configure): re-assert cooked terminal mode every tick during the wait loop#234
Merged
Conversation
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #227 (PR #230): Ctrl+C could still go dead during a configure session. The merged fix forced cooked mode once before blocking on the stop event, but a configure action handled on the HTTP thread can run plugin code that re-flips the TTY into raw mode (ISIG off) while the main thread is already blocked — a one-shot pre-wait fix cannot recover from that, and the operator is stranded again. Reproduced in the field right after using the configure UI.
Fix
run_configure_wizardnow re-asserts cooked mode on a 1-second cadence instead of once: the singlestop_event.wait(timeout_seconds)becomes a deadline-bounded loop that waits inmin(_COOKED_REASSERT_SECONDS, remaining)slices and callsforce_cooked_mode(fd)every tick. Any mid-session raw-mode leak self-heals within one tick, so Ctrl+C reliably delivers SIGINT for the whole session.Properties (review-verified):
stop_event.waitreturns immediately when/api/shutdownor the signal handler sets the event — no full-tick delay.time.monotonic(); the final slice is clamped to the residual;timeout_seconds <= 0exits without spinning.tcgetattr/tcsetattrpair costs microseconds and is OR-only (never clears bits), so an already-cooked terminal is untouched; non-TTY stdin stays a no-op.Verification
Real-pty end-to-end repro: spawn the wizard on a pty, flip the pty to raw (ISIG/ICANON/ECHO cleared) from another thread mid-wait, send
\x03after >1 tick — SIGINT is delivered, the stop handler fires, and the wizard shuts down cleanly.Test plan
tests/test_web_server.py— newtest_reasserts_cooked_mode_during_wait(shrinks the tick to 0.05s, asserts ≥4 force calls over a 0.4s wait; the mock is what is counted, so the loop is exercised identically on Windows); existing one-shot test relaxed to ≥1