Here are a couple of useful functions I wrote to kill a process tree. It’s useful when dealing with tcpserver, which refuses to disconnect a client for as long as a process it started, or a child of that process, still runs. It’s also good for tackling runaway unintentional fork bombs, rogue daemons and other mishaps.
The following script provides two functions:
| sf_killtree |
sends SIGSTOP to a process, kills all its children recursively, kills the process, sends SIGCONT and waits for it, to avoid “Terminated:” messages |
|---|---|
| sf_killchildren |
kills all children of the current script without killing the script itself |
Script:
function sf_killtree
{
local ppid=$1 sig=${2:-"TERM"}
if ! kill -0 "$ppid"; then
builtin echo "killtree: ($ppid) - No such process" >&2
return 1
fi
(( ppid == $$ )) || kill -STOP "$ppid" 2>/dev/null || :
local pids=( $({ pgrep -P $ppid || ps -o pid= --ppid $ppid || :; } 2>/dev/null ) )
if [[ -n "${pids[@]:-}" ]]; then
for pid in "${pids[@]}"; do
sf_killtree "$pid" "$sig" 2>/dev/null || :
done
fi
kill "-${sig}" "$ppid" 2>/dev/null || :
(( ppid == $$ )) || kill -CONT "$ppid" 2>/dev/null || :
wait "$ppid" 2>/dev/null || :
}
function sf_killchildren
{
local _term=$(builtin trap -p TERM)
builtin trap : TERM
sf_killtree $$ TERM;
builtin trap - TERM
builtin eval "$_term"
}
Addendum
Please also note the following from the KILL (2) man page:
“If pid equals 0, then sig is sent to every process in the process group of the calling process.”
While this does kill the script itself, unlike sf_killchildren, it can be a useful thing to end a script with, or to put in an EXIT trap.
1 Comment
*shell_version
function Stop-ProcessTree {
param(
[Parameter(Mandatory=$true)]
[int]$ParentId,
[string]$Signal = ‘Stop’ # ‘Stop’ = terminate, ‘Suspend’ = pause, ‘Continue’ = resume
)
# Get all child processes recursively
$children = Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $ParentId }
foreach ($child in $children) {
Stop-ProcessTree -ParentId $child.ProcessId -Signal $Signal
}
try {
if ($Signal -eq ‘Suspend’) {
Suspend-Process -Id $ParentId -ErrorAction SilentlyContinue
} elseif ($Signal -eq ‘Continue’) {
Resume-Process -Id $ParentId -ErrorAction SilentlyContinue
} else {
Stop-Process -Id $ParentId -Force -ErrorAction SilentlyContinue
}
} catch {}
}
function Stop-MyChildren {
$myPid = $PID
Stop-ProcessTree -ParentId $myPid -Signal ‘Stop’
}