From 31681cb4e7e4a2f8718cb6f0e3aafec83f6a9e3b Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 12 Aug 2026 09:32:21 +0200 Subject: [PATCH] Add context cancellation to bypass.Submit bypass.Submit now takes a context and returns early when cancelled. This allows the stop command to properly cancel pending bypass requests that haven't been approved or denied yet. The full context chain is now: 1. stop command -> Interrupt -> cancel actCtx 2. actCtx cancellation -> closes CallTool fid 3. toolsrv proc/new sees closed fid -> propagates ctx cancellation 4. ExecuteTool -> executeBypassDirect -> bypass.Submit 5. bypass.Submit returns ctx.Err() when context is cancelled --- cmd/toolsrv/internal/bypass/bypass.go | 28 ++++++++++++++++++++++----- cmd/toolsrv/internal/exec/exec.go | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cmd/toolsrv/internal/bypass/bypass.go b/cmd/toolsrv/internal/bypass/bypass.go index 7e88547..9dcc0e3 100644 --- a/cmd/toolsrv/internal/bypass/bypass.go +++ b/cmd/toolsrv/internal/bypass/bypass.go @@ -4,6 +4,7 @@ package bypass import ( + "context" "crypto/rand" "encoding/hex" "errors" @@ -29,10 +30,10 @@ var ( requests = make(map[string]*Request) ) -// Submit submits a bypass request and blocks until resolved. +// Submit submits a bypass request and blocks until resolved or context cancelled. // Returns (approved, error). If denied, approved is false and error is nil. -// If there's an error (e.g., timeout, connection lost), error is non-nil. -func Submit(cmd, cwd string, env map[string]string) (bool, error) { +// If context is cancelled, returns (false, ctx.Err()). +func Submit(ctx context.Context, cmd, cwd string, env map[string]string) (bool, error) { req := &Request{ ID: nextID(), Cmd: cmd, @@ -45,8 +46,25 @@ func Submit(cmd, cwd string, env map[string]string) (bool, error) { requests[req.ID] = req mu.Unlock() - pending <- req - <-req.done + // Send to pending channel (may block if channel is full) + select { + case pending <- req: + case <-ctx.Done(): + mu.Lock() + delete(requests, req.ID) + mu.Unlock() + return false, ctx.Err() + } + + // Wait for resolution or context cancellation + select { + case <-req.done: + case <-ctx.Done(): + mu.Lock() + delete(requests, req.ID) + mu.Unlock() + return false, ctx.Err() + } mu.Lock() delete(requests, req.ID) diff --git a/cmd/toolsrv/internal/exec/exec.go b/cmd/toolsrv/internal/exec/exec.go index e2fb2fd..f8b6eee 100644 --- a/cmd/toolsrv/internal/exec/exec.go +++ b/cmd/toolsrv/internal/exec/exec.go @@ -240,7 +240,7 @@ func executeBypassDirect(ctx context.Context, cmd, cwd string, envExtra map[stri } // Submit bypass request and wait for approval - approved, err := bypass.Submit(cmd, cwd, envMap) + approved, err := bypass.Submit(ctx, cmd, cwd, envMap) if err != nil { return "", fmt.Errorf("bypass request failed: %w", err) }