Killing a process tree, killing a PID and all children

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

  • stu4331 says:

    *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’
    }

Leave a Reply

Your email address will not be published. Required fields are marked *