108 lines
2.7 KiB
Bash
108 lines
2.7 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
process_identity_token() {
|
|
local pid="$1"
|
|
ps -o lstart= -p "$pid" 2>/dev/null | xargs || true
|
|
}
|
|
|
|
same_process_identity() {
|
|
local pid="$1"
|
|
local expected_token="$2"
|
|
local current_token
|
|
|
|
current_token="$(process_identity_token "$pid")"
|
|
[[ -n "$current_token" && "$current_token" == "$expected_token" ]]
|
|
}
|
|
|
|
snapshot_process_tree() {
|
|
local pid="$1"
|
|
local token
|
|
local child
|
|
local child_snapshot
|
|
local descendants=""
|
|
|
|
[[ "$pid" =~ ^[0-9]+$ ]] || return 0
|
|
token="$(process_identity_token "$pid")"
|
|
[[ -n "$token" ]] || return 0
|
|
|
|
while IFS= read -r child; do
|
|
if [[ -n "$child" ]]; then
|
|
child_snapshot="$(snapshot_process_tree "$child")"
|
|
if [[ -n "$child_snapshot" ]]; then
|
|
descendants="${descendants}${child_snapshot}"$'\n'
|
|
fi
|
|
fi
|
|
done < <(pgrep -P "$pid" 2>/dev/null || true)
|
|
|
|
same_process_identity "$pid" "$token" || return 0
|
|
printf '%s' "$descendants"
|
|
printf '%s|%s\n' "$pid" "$token"
|
|
}
|
|
|
|
signal_process_snapshot() {
|
|
local snapshot="$1"
|
|
local signal="${2:-TERM}"
|
|
local pid
|
|
local token
|
|
|
|
[[ -n "$snapshot" ]] || return 0
|
|
while IFS='|' read -r pid token; do
|
|
if [[ -n "$pid" ]] && same_process_identity "$pid" "$token"; then
|
|
kill "-$signal" "$pid" >/dev/null 2>&1 || true
|
|
fi
|
|
done <<<"$snapshot"
|
|
}
|
|
|
|
process_snapshot_alive() {
|
|
local snapshot="$1"
|
|
local pid
|
|
local token
|
|
|
|
[[ -n "$snapshot" ]] || return 1
|
|
while IFS='|' read -r pid token; do
|
|
if [[ -n "$pid" ]] && same_process_identity "$pid" "$token"; then
|
|
return 0
|
|
fi
|
|
done <<<"$snapshot"
|
|
return 1
|
|
}
|
|
|
|
stop_process_tree() {
|
|
local root_pid="$1"
|
|
local wait_loops="${PROCESS_TREE_TERM_WAIT_LOOPS:-20}"
|
|
local snapshot
|
|
local descendants
|
|
local current
|
|
local all_snapshots
|
|
|
|
[[ "$root_pid" =~ ^[0-9]+$ ]] || return 0
|
|
snapshot="$(snapshot_process_tree "$root_pid")"
|
|
[[ -n "$snapshot" ]] || return 0
|
|
all_snapshots="$snapshot"
|
|
|
|
# Stop the first child snapshot before the wrapper so it cannot orphan the
|
|
# already-known Java/Vite process when the wrapper exits.
|
|
descendants="$(printf '%s\n' "$snapshot" | sed '$d')"
|
|
signal_process_snapshot "$descendants" TERM
|
|
|
|
current="$(snapshot_process_tree "$root_pid")"
|
|
if [[ -n "$current" ]]; then
|
|
all_snapshots="${all_snapshots}"$'\n'"${current}"
|
|
signal_process_snapshot "$current" TERM
|
|
fi
|
|
|
|
for _ in $(seq 1 "$wait_loops"); do
|
|
current="$(snapshot_process_tree "$root_pid")"
|
|
if [[ -n "$current" ]]; then
|
|
all_snapshots="${all_snapshots}"$'\n'"${current}"
|
|
signal_process_snapshot "$current" TERM
|
|
fi
|
|
if ! process_snapshot_alive "$all_snapshots"; then
|
|
return 0
|
|
fi
|
|
sleep 0.2
|
|
done
|
|
|
|
signal_process_snapshot "$all_snapshots" KILL
|
|
}
|