From 99401670ee36b8f88359f5d8e93d2a9b67978ead Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Tue, 20 Oct 2026 22:49:11 -0700 Subject: [PATCH] Audit of 9af1463 --- internal/agents/bash.go | 8 ++------ internal/agents/usage.go | 9 ++------- internal/antigravity/session.go | 11 +++++------ internal/claude/session.go | 12 +++++------- internal/codex/session.go | 14 ++++++-------- internal/git/git.go | 9 +++------ internal/procgroup/procgroup.go | 30 ++++++++++++++++++++++++++++++ 7 files changed, 53 insertions(+), 40 deletions(-) create mode 100644 internal/procgroup/procgroup.go diff --git a/internal/agents/bash.go b/internal/agents/bash.go index d764088..49648c1 100644 --- a/internal/agents/bash.go +++ b/internal/agents/bash.go @@ -8,10 +8,10 @@ import ( "os" "os/exec" "strings" - "syscall" "time" "g.fc.run/ai/byre/internal/mcp" + "g.fc.run/ai/byre/internal/procgroup" ) const bashKeep = 64 << 10 @@ -68,13 +68,9 @@ func (m *Manager) bash(ctx context.Context, l *live, raw json.RawMessage) (strin cctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - cmd := exec.CommandContext(cctx, shell, "-l", "-c", args.Command) + cmd := procgroup.CommandContext(cctx, shell, "-l", "-c", args.Command) cmd.Dir = l.dir cmd.Env = append(os.Environ(), portEnv(task)...) - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - cmd.Cancel = func() error { - return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) - } cmd.WaitDelay = 5 * time.Second out := &tailWriter{} cmd.Stdout = out diff --git a/internal/agents/usage.go b/internal/agents/usage.go index 1033a26..52de091 100644 --- a/internal/agents/usage.go +++ b/internal/agents/usage.go @@ -8,13 +8,12 @@ import ( "log/slog" "maps" "os" - "os/exec" "slices" "strings" - "syscall" "time" "g.fc.run/ai/byre/internal/api" + "g.fc.run/ai/byre/internal/procgroup" ) const usageTimeout = 10 * time.Second @@ -127,11 +126,7 @@ func runUsageCommand(cmdline string) (api.Usage, error) { } ctx, cancel := context.WithTimeout(context.Background(), usageTimeout) defer cancel() - cmd := exec.CommandContext(ctx, shell, "-l", "-c", cmdline) - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - cmd.Cancel = func() error { - return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) - } + cmd := procgroup.CommandContext(ctx, shell, "-l", "-c", cmdline) cmd.WaitDelay = time.Second out, err := cmd.Output() if err != nil { diff --git a/internal/antigravity/session.go b/internal/antigravity/session.go index b97cfb6..2819f0f 100644 --- a/internal/antigravity/session.go +++ b/internal/antigravity/session.go @@ -13,7 +13,6 @@ import ( "net" "net/http" "os" - "os/exec" "path/filepath" "strings" "sync" @@ -22,6 +21,7 @@ import ( "time" "g.fc.run/ai/byre/internal/mcp" + "g.fc.run/ai/byre/internal/procgroup" ) const maxRuleBytes = 24000 @@ -115,7 +115,7 @@ func (e Event) Result() (Result, error) { } type proc struct { - cmd *exec.Cmd + cmd *procgroup.Cmd stdin io.WriteCloser done chan struct{} expected bool @@ -324,7 +324,7 @@ func (s *Session) spawnLocked() error { } args = append(args, "--print", "") - cmd := exec.Command("agy", args...) + cmd := procgroup.Command("agy", args...) cmd.Dir = s.opts.Cwd cmd.Stderr = os.Stderr cmd.Env = append(os.Environ(), @@ -334,7 +334,6 @@ func (s *Session) spawnLocked() error { "GIT_COMMITTER_EMAIL="+s.opts.GitEmail, ) cmd.Env = append(cmd.Env, s.opts.Env...) - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} stdin, err := cmd.StdinPipe() if err != nil { @@ -429,7 +428,7 @@ func (s *Session) restartLocked() error { select { case <-p.done: case <-time.After(10 * time.Second): - syscall.Kill(-p.cmd.Process.Pid, syscall.SIGKILL) + p.cmd.Kill() <-p.done } return s.spawnLocked() @@ -568,7 +567,7 @@ func (s *Session) Kill() { s.mu.Unlock() if p != nil { - syscall.Kill(-p.cmd.Process.Pid, syscall.SIGKILL) + p.cmd.Kill() <-p.done } s.die() diff --git a/internal/claude/session.go b/internal/claude/session.go index 8ccd880..5728ade 100644 --- a/internal/claude/session.go +++ b/internal/claude/session.go @@ -13,13 +13,12 @@ import ( "io" "log/slog" "os" - "os/exec" "sync" "sync/atomic" - "syscall" "time" "g.fc.run/ai/byre/internal/mcp" + "g.fc.run/ai/byre/internal/procgroup" ) type HookFunc func(ctx context.Context, input json.RawMessage) (map[string]any, error) @@ -194,7 +193,7 @@ func (e Event) Result() (Result, error) { } type Session struct { - cmd *exec.Cmd + cmd *procgroup.Cmd stdin io.WriteCloser events chan Event done chan struct{} @@ -255,11 +254,10 @@ func Start(ctx context.Context, opts Options) (*Session, error) { return nil, err } - s.cmd = exec.Command("claude", args...) + s.cmd = procgroup.Command("claude", args...) s.cmd.Dir = opts.Cwd s.cmd.Stderr = os.Stderr s.cmd.Env = buildEnv(opts) - s.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} s.stdin, err = s.cmd.StdinPipe() if err != nil { @@ -514,7 +512,7 @@ func (s *Session) Close() error { select { case <-s.done: case <-time.After(5 * time.Second): - syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL) + s.cmd.Kill() <-s.done } @@ -522,7 +520,7 @@ func (s *Session) Close() error { } func (s *Session) Kill() { - syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL) + s.cmd.Kill() <-s.done s.cmd.Wait() } diff --git a/internal/codex/session.go b/internal/codex/session.go index 633ca91..dc21994 100644 --- a/internal/codex/session.go +++ b/internal/codex/session.go @@ -11,15 +11,14 @@ import ( "io" "log/slog" "os" - "os/exec" "regexp" "strconv" "sync" "sync/atomic" - "syscall" "time" "g.fc.run/ai/byre/internal/mcp" + "g.fc.run/ai/byre/internal/procgroup" ) type Options struct { @@ -90,7 +89,7 @@ type rpcResult struct { } type Session struct { - cmd *exec.Cmd + cmd *procgroup.Cmd stdin *os.File events chan Event done chan struct{} @@ -156,11 +155,10 @@ func ReadRateLimits(ctx context.Context, cwd string, env []string) (RateLimitSna } func spawn(opts Options) (*Session, error) { - cmd := exec.Command("codex", "app-server", "-c", "check_for_update_on_startup=false") + cmd := procgroup.Command("codex", "app-server", "-c", "check_for_update_on_startup=false") cmd.Dir = opts.Cwd cmd.Stderr = os.Stderr cmd.Env = buildEnv(opts) - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} stdinR, stdin, err := os.Pipe() if err != nil { @@ -538,7 +536,7 @@ func (s *Session) Close() error { select { case <-s.done: case <-time.After(5 * time.Second): - syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL) + s.cmd.Kill() <-s.done } @@ -546,7 +544,7 @@ func (s *Session) Close() error { } func (s *Session) Kill() { - syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL) + s.cmd.Kill() <-s.done s.cmd.Wait() } @@ -579,7 +577,7 @@ func (s *Session) write(m message, deadline time.Time) error { n, err := s.stdin.Write(append(b, '\n')) if n > 0 && errors.Is(err, os.ErrDeadlineExceeded) { slog.Error("codex stopped reading its stdin mid-line; killing it", "pid", s.cmd.Process.Pid, "written", n, "len", len(b)+1) - syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL) + s.cmd.Kill() } return err } diff --git a/internal/git/git.go b/internal/git/git.go index e27171d..fc4c7c8 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -11,8 +11,9 @@ import ( "slices" "strconv" "strings" - "syscall" "time" + + "g.fc.run/ai/byre/internal/procgroup" ) type RepoInfo struct { @@ -435,14 +436,10 @@ func runNetwork(ctx context.Context, dir string, args ...string) (string, string } func runCodeInput(ctx context.Context, dir string, env []string, stdin io.Reader, args ...string) (string, string, int, error) { - cmd := exec.CommandContext(ctx, "git", args...) + cmd := procgroup.CommandContext(ctx, "git", args...) cmd.Dir = dir cmd.Env = append(os.Environ(), env...) cmd.Stdin = stdin - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - cmd.Cancel = func() error { - return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) - } cmd.WaitDelay = 5 * time.Second stderr := &strings.Builder{} cmd.Stderr = stderr diff --git a/internal/procgroup/procgroup.go b/internal/procgroup/procgroup.go new file mode 100644 index 0000000..ba6248b --- /dev/null +++ b/internal/procgroup/procgroup.go @@ -0,0 +1,30 @@ +package procgroup + +import ( + "context" + "os/exec" + "syscall" +) + +type Cmd struct { + *exec.Cmd +} + +func Command(name string, args ...string) *Cmd { + return group(exec.Command(name, args...)) +} + +func CommandContext(ctx context.Context, name string, args ...string) *Cmd { + cmd := group(exec.CommandContext(ctx, name, args...)) + cmd.Cancel = cmd.Kill + return cmd +} + +func group(cmd *exec.Cmd) *Cmd { + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + return &Cmd{Cmd: cmd} +} + +func (cmd *Cmd) Kill() error { + return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) +}