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))
|
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 {
|
if len(toolResult.Content) == 0 {
|
||||||
t.Fatal("toolsrv.ToolResult.Content is empty")
|
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)
|
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.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 {
|
cmd.Cancel = func() error {
|
||||||
if cmd.Process != nil {
|
if cmd.Process != nil {
|
||||||
syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
|
syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,28 @@ func (st *State) ListProcs() []int {
|
||||||
return pids
|
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.
|
// DismissProc removes a process from the list.
|
||||||
func (st *State) DismissProc(pid int) bool {
|
func (st *State) DismissProc(pid int) bool {
|
||||||
st.procMu.Lock()
|
st.procMu.Lock()
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,9 @@ func main() {
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Kill all procs on shutdown
|
||||||
|
defer srv.Fs.KillAll()
|
||||||
|
|
||||||
// Start idle timeout monitor
|
// Start idle timeout monitor
|
||||||
if *idleTimeout > 0 {
|
if *idleTimeout > 0 {
|
||||||
go idleMonitor(runCtx, cancel, *idleTimeout)
|
go idleMonitor(runCtx, cancel, *idleTimeout)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,11 @@ fi
|
||||||
# Execute the command. The sandbox (landlock) is already applied by toolsrv.
|
# Execute the command. The sandbox (landlock) is already applied by toolsrv.
|
||||||
# We use eval to handle pipes, redirects, and compound commands.
|
# We use eval to handle pipes, redirects, and compound commands.
|
||||||
# Output streams directly to stdout/stderr for real-time capture.
|
# 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=$?
|
rc=$?
|
||||||
|
set -e
|
||||||
echo "exit: $rc"
|
echo "exit: $rc"
|
||||||
exit $rc
|
exit $rc
|
||||||
Loading…
Reference in New Issue