Follow-up to #333 / #334 (remote session export, merged as 4715a30).
Defect
SessionTransplant.bounded() wraps a remote session fetch in a job-control group with a ten-minute watchdog and arms trap 'kill -TERM -- -$gc_p -$gc_w 2>/dev/null; exit 143' INT TERM HUP so that a signal to the shell takes the pipeline's and the watchdog's process groups with it. That trap never fires in practice:
- Foundation
Process spawns each /bin/sh in its own process group (measured: pgid == pid), so Ctrl-C in a Terminal reaches only the CLI's group, never the shell running the fetch.
- Nothing in the codebase sends TERM to that shell either:
runShell has no cancellation path, so an app-side cancellation (the export effect being torn down) leaves it running too.
Measured on the loopback ssh rig under a real pty, with the ssh mux master frozen to stall a fetch: the CLI died of SIGINT at 3s while 3 sh, 2 tar, 2 sleep 600 and the pipeline subshells survived to completion or to the 600s watchdog. The trap itself works when the shell is TERMed directly (measured: TERM to the shell leaves no ssh, tar, subshell or sleep behind), so the gap is only that no signal ever reaches it.
Impact: an interrupted or cancelled export leaks the fetch processes for up to ten minutes per loop. They exit on their own at the deadline; nothing is left permanently.
Fix idea
- A liveness guard in the watchdog instead of one long sleep: loop in short steps and
kill -0 $PPID each step, killing both groups (-$gc_p -$gc_w) the moment the parent is gone. That covers every way the parent can die, including SIGKILL, without depending on a signal reaching the shell.
process.terminate() on Task cancellation in runShell, so an app-side cancellation reaches the shell and the existing trap does its job.
- A SIGINT handler in the CLI's export path that terminates the in-flight fetches before exiting.
Test: the existing pipelineIsBoundedAndKillsItsOwnProcessGroupOnTheDeadline asserts the trap text; add one asserting the kill -0 $PPID guard, and re-measure on the rig with the mux master frozen: after Ctrl-C to the CLI, pgrep -f 'sleep 600' and the tar/ssh children should be gone within one guard step.
Reported by the RemoteExportVerify loop during the #334 review; measurements are theirs, reproduced with a forkpty harness (children need SIG_DFL dispositions).
Follow-up to #333 / #334 (remote session export, merged as 4715a30).
Defect
SessionTransplant.bounded()wraps a remote session fetch in a job-control group with a ten-minute watchdog and armstrap 'kill -TERM -- -$gc_p -$gc_w 2>/dev/null; exit 143' INT TERM HUPso that a signal to the shell takes the pipeline's and the watchdog's process groups with it. That trap never fires in practice:Processspawns each/bin/shin its own process group (measured: pgid == pid), so Ctrl-C in a Terminal reaches only the CLI's group, never the shell running the fetch.runShellhas no cancellation path, so an app-side cancellation (the export effect being torn down) leaves it running too.Measured on the loopback ssh rig under a real pty, with the ssh mux master frozen to stall a fetch: the CLI died of SIGINT at 3s while 3
sh, 2tar, 2sleep 600and the pipeline subshells survived to completion or to the 600s watchdog. The trap itself works when the shell is TERMed directly (measured: TERM to the shell leaves no ssh, tar, subshell or sleep behind), so the gap is only that no signal ever reaches it.Impact: an interrupted or cancelled export leaks the fetch processes for up to ten minutes per loop. They exit on their own at the deadline; nothing is left permanently.
Fix idea
kill -0 $PPIDeach step, killing both groups (-$gc_p -$gc_w) the moment the parent is gone. That covers every way the parent can die, including SIGKILL, without depending on a signal reaching the shell.process.terminate()on Task cancellation inrunShell, so an app-side cancellation reaches the shell and the existing trap does its job.Test: the existing
pipelineIsBoundedAndKillsItsOwnProcessGroupOnTheDeadlineasserts the trap text; add one asserting thekill -0 $PPIDguard, and re-measure on the rig with the mux master frozen: after Ctrl-C to the CLI,pgrep -f 'sleep 600'and the tar/ssh children should be gone within one guard step.Reported by the RemoteExportVerify loop during the #334 review; measurements are theirs, reproduced with a forkpty harness (children need SIG_DFL dispositions).