fix(envd): kill command's whole process tree on signal#2933
fix(envd): kill command's whole process tree on signal#2933mishushakov wants to merge 3 commits into
Conversation
envd's SendSignal only signaled the single process it manages (the command leader), so child processes the command spawned kept running — and consuming resources — after a kill. Non-PTY commands now start in their own process group (Setpgid) and SendSignal delivers the signal to the whole group, terminating the leader together with its children; command timeouts tear down the group too. PTY processes are left untouched. Bumps the envd version. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR SummaryMedium Risk Overview Reviewed by Cursor Bugbot for commit a537272. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Code Review
This pull request ensures that non-PTY commands run in their own process group so that signals and context cancellations terminate the entire process tree instead of just the leader process. A test is added to verify this behavior, and the package version is bumped to 0.6.2. There are no review comments, and I have no feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
❌ 2 Tests Failed:
View the full list of 2 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
Address PR review: signalProcessGroup bypassed Go's ErrProcessDone guard, so a stale signal arriving after the leader was reaped (but before deregistration) could kill(-pid) an unrelated command that recycled the pid as its process group leader. Bail out when cmd.ProcessState is set, mirroring os.Process.Signal. Also make the test's processAlive read /proc/<pid>/stat and treat zombies as not alive, so it doesn't hang when orphans aren't reaped promptly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 847707bb7d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Address PR review: reading cmd.ProcessState in SendSignal raced with cmd.Wait setting it in the reaper goroutine. Replace the guard with a handler-level atomic.Bool set immediately after cmd.Wait reaps the leader, and consult it before sending a process-group signal. The timeout-driven cmd.Cancel path no longer reads ProcessState and is safe because exec stops invoking Cancel once the process exits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
envd's
SendSignalonly signaled the single process it manages (the command leader), so child processes the command spawned kept running — and consuming resources — after akill()(see e2b-dev/E2B#1389, which worked around this SDK-side with apgrepshell walk). Non-PTY commands now start in their own process group (Setpgid) andSendSignaldelivers the signal to the whole group, terminating the leader together with its children; command timeouts tear down the group too, and PTY processes are left untouched. This is the same idiom already used for socat ininternal/port/forward.go, and it's transparently backward-compatible — existing SDKs get whole-tree termination for free and can later drop their workaround. Adds a regression test (TestSendSignal_KillsProcessTree) and bumps the envd version to 0.6.2.🤖 Generated with Claude Code