Skip to content

fix: run interactive pty in raw mode so stdout is not truncated - #845

Open
SomSamantray wants to merge 6 commits into
apple:mainfrom
SomSamantray:fix/pty-raw-mode-interactive-stdout
Open

fix: run interactive pty in raw mode so stdout is not truncated#845
SomSamantray wants to merge 6 commits into
apple:mainfrom
SomSamantray:fix/pty-raw-mode-interactive-stdout

Conversation

@SomSamantray

@SomSamantray SomSamantray commented Aug 19, 2026

Copy link
Copy Markdown

Interactive containers (container run --interactive on apple/container) truncated stdout at 1024 bytes when output went to a terminal or file: the guest pty slave was left in canonical mode, whose line discipline buffers large writes, so a process writing more than 1024 bytes and exiting lost the buffered tail.

The pty slave is now put into raw mode before it is wired onto the container's stdio, using the existing Terminal.setraw() recipe (cfmakeraw with OPOST/ONLCR preserved so CRLF output translation is kept). Putting the slave fully into raw mode also clears ISIG, and @stephenlclarke pointed out in review that this would stop Ctrl-C from generating SIGINT for the container's own process. Terminal.setraw() now takes an opt-in preserveSignalGeneration parameter (default false, so every other caller — the host-side terminals in cctl and the examples — keeps its current behavior); only the guest pty slave in vmexec enables it, so Ctrl-C still interrupts the running process.

Regression tests cover both paths: default setraw() clears ICANON and ISIG while preserving OPOST/ONLCR; with preserveSignalGeneration: true, ICANON still clears but ISIG stays set.

Related: apple/container#1148

Unapplied review findings

  • P2 (advisory) — Sources/ContainerizationOS/Terminal.swift:150 — Restoring ISIG on the guest pty slave re-arms SIGQUIT (Ctrl-) and SIGTSTP (Ctrl-Z) generation, not just SIGINT (Ctrl-C).
    cfmakeraw clears the whole ISIG bit, so re-setting it (rather than selectively re-enabling only the INTR special character) restores all three signal-generating characters at once. This matches this repo's pre-truncation-fix behavior exactly (a freshly allocated pty slave has ISIG on by default), so it isn't a new regression relative to main — but it's worth an explicit call on whether that's the intended scope (matching runc's own console contract, which does the same) or whether only SIGINT should be restored (via _POSIX_VDISABLE on VQUIT/VSUSP). No code change proposed pending that call.

The pty slave allocated for interactive containers was left in canonical
mode, whose line discipline buffers output and causes large writes to be
truncated at 1024 bytes (reported in apple/container#1148). A process
writing more than 1024 bytes to stdout and exiting loses the buffered
tail because the canonical-mode tty holds it waiting for a line
terminator.

Configure the slave with the existing Terminal.setraw() recipe
(cfmakeraw plus OPOST preserved) before dup3'ing it onto stdio, so
output passes through without truncation while newline-to-CRLF
translation is kept.

Add a regression test asserting the termios flags: a fresh pty slave is
canonical, and setraw() clears ICANON while preserving OPOST.
Unify the ICANON and OPOST assertions through one flag-membership
helper operating on a tcflag_t field, instead of re-inlining the same
bit-mask idiom with different shapes.
cfmakeraw clears OPOST but leaves ONLCR set; re-adding OPOST in
Terminal.setraw() restores newline-to-CRLF output translation. Lock both
flags so the raw-mode recipe's output behavior is pinned by the test.
@stephenlclarke

Copy link
Copy Markdown
Contributor

One terminal behaviour needs preserving before this lands: cfmakeraw clears ISIG, which makes Ctrl-C an ordinary input byte instead of generating SIGINT.

I’ve put a focused follow-up on upstream/pty-preserve-isig, based directly on this PR’s current head:
stephenlclarke@2ae716f

It adds an opt-in preserveSignalGeneration argument to Terminal.setraw() and uses it for vmexec. Existing callers retain the current raw-mode behaviour. All three focused terminal tests pass.

Please feel free to cherry-pick the commit or take the branch.

stephenlclarke and others added 2 commits September 6, 2026 07:50
Guards the ICANON/OPOST/ONLCR assertions already in this test with an
ISIG check, so a future change that flips the preserveSignalGeneration
default would fail this test instead of passing silently.
@SomSamantray

Copy link
Copy Markdown
Author

I've put a focused follow-up on upstream/pty-preserve-isig, based directly on this PR's current head... Please feel free to cherry-pick the commit or take the branch.

Cherry-picked as 625e774 (on top of your commit at f4ccfb7) — thanks for the fast, well-scoped fix. Also added one more regression assertion: the default (non-vmexec) path of setraw() now has an explicit test that ISIG stays cleared, so a future change that flips the default wouldn't pass silently.

One open question from that same review pass, noted in the PR description under "Unapplied review findings": restoring ISIG re-arms SIGQUIT/SIGTSTP as well as SIGINT, matching this repo's pre-truncation-fix default — happy to narrow that to SIGINT-only if that's the preferred scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants