toolsrv: kill all procs on shutdown, add Pdeathsig
- KillAll(): sends SIGKILL to all running proc groups on clean shutdown - Pdeathsig: SIGKILL ensures child procs die if toolsrv crashes - Shell tool: use subshell + set +e for streaming without exit propagation - Fix integration test to match new error format This is the connection-based ownership model: toolsrv death = proc death. The session owns the toolsrv process, so session death cascades to all procs.
This commit is contained in:
parent
0d1eeaa46b
commit
97c208fa39
|
|
@ -368,11 +368,14 @@ func TestIntegration_ToolExecutionError(t *testing.T) {
|
|||
t.Fatalf("failed to parse toolsrv.ToolResult: %v (raw: %s)", err, string(result))
|
||||
}
|
||||
|
||||
// Should have exit code in output
|
||||
// Should be an error result with exit code info
|
||||
if len(toolResult.Content) == 0 {
|
||||
t.Fatal("toolsrv.ToolResult.Content is empty")
|
||||
}
|
||||
if !strings.Contains(toolResult.Content[0].Text, "exit: 42") {
|
||||
if !toolResult.IsError {
|
||||
t.Error("expected IsError=true for non-zero exit")
|
||||
}
|
||||
if !strings.Contains(toolResult.Content[0].Text, "exit status 42") && !strings.Contains(toolResult.Content[0].Text, "exit: 42") {
|
||||
t.Errorf("output doesn't contain exit code: %q", toolResult.Content[0].Text)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ func executeSandboxed(ctx context.Context, toolPath, stdinData, cwd string, envE
|
|||
cmd.Env = append(cmd.Env, k+"="+v)
|
||||
}
|
||||
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pdeathsig: syscall.SIGKILL}
|
||||
cmd.Cancel = func() error {
|
||||
if cmd.Process != nil {
|
||||
syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
|
||||
|
|
|
|||
|
|
@ -336,6 +336,28 @@ func (st *State) ListProcs() []int {
|
|||
return pids
|
||||
}
|
||||
|
||||
// KillAll sends SIGKILL to all running procs and cancels their contexts.
|
||||
// Called on shutdown to ensure no orphaned processes.
|
||||
func (st *State) KillAll() {
|
||||
st.procMu.Lock()
|
||||
procs := make([]*Proc, 0, len(st.procs))
|
||||
for _, p := range st.procs {
|
||||
procs = append(procs, p)
|
||||
}
|
||||
st.procMu.Unlock()
|
||||
|
||||
for _, p := range procs {
|
||||
p.mu.Lock()
|
||||
proc := p.proc
|
||||
exited := p.Exited
|
||||
p.mu.Unlock()
|
||||
if !exited && proc != nil {
|
||||
syscall.Kill(-proc.Pid, syscall.SIGKILL)
|
||||
}
|
||||
p.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
// DismissProc removes a process from the list.
|
||||
func (st *State) DismissProc(pid int) bool {
|
||||
st.procMu.Lock()
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@ func main() {
|
|||
cancel()
|
||||
}()
|
||||
|
||||
// Kill all procs on shutdown
|
||||
defer srv.Fs.KillAll()
|
||||
|
||||
// Start idle timeout monitor
|
||||
if *idleTimeout > 0 {
|
||||
go idleMonitor(runCtx, cancel, *idleTimeout)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ fi
|
|||
# Execute the command. The sandbox (landlock) is already applied by toolsrv.
|
||||
# We use eval to handle pipes, redirects, and compound commands.
|
||||
# Output streams directly to stdout/stderr for real-time capture.
|
||||
eval "$cmd" 2>&1
|
||||
# Run in a subshell so 'exit N' doesn't kill this script.
|
||||
# Disable errexit for the subshell so we can capture the exit code.
|
||||
set +e
|
||||
(eval "$cmd") 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
echo "exit: $rc"
|
||||
exit $rc
|
||||
Loading…
Reference in New Issue