Skip to content

Commit 975484c

Browse files
fix(tools): detach stdio when /dev/null open fails to avoid pipe-drainer leak
When os.OpenFile(os.DevNull) fails, the original code fell through with the bytes.Buffer pipes generateCommandLine attached, which makes exec.Cmd.Start spawn drainer goroutines that block on the GUI tool's stdout/stderr until it exits, defeating the Process.Release detach. Set Std{in,out,err} to nil in the fallback so the child inherits the parent's stdio with no extra goroutines.
1 parent 0997cac commit 975484c

1 file changed

Lines changed: 8 additions & 1 deletion

File tree

internal/tools/tool/tool.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,19 @@ func (t *tool) Run(args []string) (int, error) {
6565
// Redirect stdio to the null device so exec.Cmd does not spawn pipe
6666
// drainer goroutines. Without this, Start leaves goroutines blocked on
6767
// the child's stdout/stderr until the GUI tool exits, which keeps
68-
// sqlcmd's process tree alive even after Process.Release.
68+
// sqlcmd's process tree alive even after Process.Release. If opening
69+
// the null device fails, fall back to inheriting the parent's stdio
70+
// (also goroutine-free) rather than leaving the bytes.Buffer pipes
71+
// generateCommandLine attached.
6972
if devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0); err == nil {
7073
cmd.Stdin = devNull
7174
cmd.Stdout = devNull
7275
cmd.Stderr = devNull
7376
defer func() { _ = devNull.Close() }()
77+
} else {
78+
cmd.Stdin = nil
79+
cmd.Stdout = nil
80+
cmd.Stderr = nil
7481
}
7582

7683
if err := cmd.Start(); err != nil {

0 commit comments

Comments
 (0)