From 4ac3b2135a5ed881b9c95e7a031e665daa3366b3 Mon Sep 17 00:00:00 2001 From: Ollie Agent Date: Mon, 17 Aug 2026 17:45:29 +0200 Subject: [PATCH] Reap toolsrv processes on shutdown --- cmd/olliesrv/internal/toolclient/spawn.go | 40 +++++++++++++++++------ 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/cmd/olliesrv/internal/toolclient/spawn.go b/cmd/olliesrv/internal/toolclient/spawn.go index 5651137..d8dc829 100644 --- a/cmd/olliesrv/internal/toolclient/spawn.go +++ b/cmd/olliesrv/internal/toolclient/spawn.go @@ -6,6 +6,7 @@ import ( "context" "crypto/rand" "encoding/hex" + "errors" "fmt" "io" "net" @@ -27,6 +28,8 @@ type Process struct { Secret string Info ProcessInfo cancel context.CancelFunc + waitOnce sync.Once + waitErr error } // ProcessInfo contains metadata about the toolsrv process. @@ -94,7 +97,9 @@ func Spawn(ctx context.Context, cwd string, opts ...Option) (*Process, error) { // Wait for socket to be ready if err := waitForSocket(socketPath, 5*time.Second); err != nil { cancel() - cmd.Process.Kill() + _ = cmd.Process.Kill() + _ = cmd.Wait() + _ = os.Remove(socketPath) return nil, fmt.Errorf("wait for socket: %w", err) } @@ -115,10 +120,15 @@ func (p *Process) Kill() error { if p.cancel != nil { p.cancel() } + var err error if p.Cmd != nil && p.Cmd.Process != nil { - return p.Cmd.Process.Kill() + err = p.Cmd.Process.Kill() } - return nil + waitErr := p.Wait() + if err != nil && !errors.Is(err, os.ErrProcessDone) { + return err + } + return waitErr } // Close terminates the process (alias for Kill). @@ -128,10 +138,16 @@ func (p *Process) Close() error { // Wait waits for the process to exit. func (p *Process) Wait() error { - if p.Cmd != nil { - return p.Cmd.Wait() + if p.Cmd == nil { + return nil } - return nil + p.waitOnce.Do(func() { + p.waitErr = p.Cmd.Wait() + if p.SocketPath != "" { + _ = os.Remove(p.SocketPath) + } + }) + return p.waitErr } // RemoteConfig configures remote toolsrv spawning. @@ -219,13 +235,15 @@ exec "$CACHE_DIR/bin/toolsrv" %s // Send tarball of config dir over stdin if err := writeTarball(stdin, cfgDir); err != nil { cancel() - cmd.Process.Kill() + _ = cmd.Process.Kill() + _ = cmd.Wait() return nil, fmt.Errorf("send tarball: %w", err) } // Send bootstrap script if _, err := stdin.Write([]byte(bootstrap)); err != nil { cancel() - cmd.Process.Kill() + _ = cmd.Process.Kill() + _ = cmd.Wait() return nil, fmt.Errorf("send bootstrap: %w", err) } stdin.Close() @@ -248,14 +266,16 @@ exec "$CACHE_DIR/bin/toolsrv" %s case <-readyCh: case <-time.After(30 * time.Second): cancel() - cmd.Process.Kill() + _ = cmd.Process.Kill() + _ = cmd.Wait() return nil, fmt.Errorf("remote toolsrv startup timeout") } // Wait for local socket forwarding to be ready if err := waitForSocket(localSock, 5*time.Second); err != nil { cancel() - cmd.Process.Kill() + _ = cmd.Process.Kill() + _ = cmd.Wait() return nil, fmt.Errorf("wait for forwarded socket: %w", err) }