mirror of
https://github.com/games-on-whales/LXC2Docker.git
synced 2026-10-15 12:52:04 +00:00
[PR #129] [MERGED] feat(exec): honor DetachKeys — detach the stream without killing the process #128
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
games-on-whales/LXC2Docker#128
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/games-on-whales/LXC2Docker/pull/129
Author: @JBailes
Created: 7/1/2026
Status: ✅ Merged
Merged: 7/1/2026
Merged by: @JBailes
Base:
main← Head:fix/exec-detach-keys📝 Commits (2)
bc25831feat(exec): honor DetachKeys — detach the stream without killing the processd30cfeefix(exec): close conn on runner error paths (closeConn is now false)📊 Changes
4 files changed (+606 additions, -66 deletions)
View changed files
📝
internal/api/exec.go(+161 -66)➕
internal/api/exec_detach.go(+154 -0)➕
internal/api/exec_detach_runner_test.go(+121 -0)➕
internal/api/exec_detach_test.go(+170 -0)📄 Description
Design-approved deferred item (the most complex).
POST /exec/{id}/startstored + echoedDetachKeysbut never acted on it — a client couldn't detach an exec (ctrl-p ctrl-q) and had to drop the connection.What it does
parseDetachKeys:DetachKeysstring → byte sequence (ctrl-a..z,ctrl-@,ctrl-[/\/]/^/_, single chars). Defaultctrl-p,ctrl-q. Malformed keys fall back to the default (Docker is lenient; the client validated at create).escapeReader: wraps the client stdin; forwards non-matching bytes, withholds a partial match across reads, swallows the full sequence and returnserrDetachsoio.Copystops.switchableWriter(→io.Discard) that swallows write errors so the single drain goroutine keeps the pty/pipes drained (the process never blocks on a full buffer); the client connection is closed;cmd.Waitruns in the background and the exec record is finished viaonExitonly when the process actually exits (Runningstays true until then).runExecTTY) keeps the pty master open (closing it would SIGHUP the shell). Non-TTY (runExecMux) closesstdinWon detach soos/exec's stdin-copy goroutine ends andcmd.Waitcan complete — the client's input stream is gone (Docker's non-TTY detach behaves the same). (This was a real bug I caught via a-racerunner test: leavingstdinWopen hangscmd.Wait.)Wolf safety
escapeReaderis a pure pass-through until the exact escape bytes match, so only streams that actually contain the sequence are affected — every other byte stream is byte-for-byte unchanged. Wolf's fake-udev exec sends no such bytes, so it's unaffected. The 101/200 preamble and the #98 fix are untouched.Tests
exec_detach_test.go—parseDetachKeys(all mappings + errors);escapeReader(passthrough, one-read match, split-across-reads, broken partial, double-start, empty keys);switchableWriter(swallow + swap).exec_detach_runner_test.go— the teardown proof: detach returnstrue, the process is still alive afterward, andonExitfires only once the process exits; plus the normal-exit path. Race-clean under-race.go test ./...,go vet,gofmt,go buildclean. Merge only on verified-green CI.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.