processes bug fix
This commit is contained in:
+185
-138
@@ -81,12 +81,17 @@ ppg()
|
||||
|
||||
local pattern="$*"
|
||||
|
||||
# pgrep reads the pattern as an extended regular expression, the fallback
|
||||
# below as a plain substring. Passed through ENVIRON, not awk -v: besides
|
||||
# escape handling, a pattern on awk's own command line would match the awk
|
||||
# process in the ps listing.
|
||||
if command -v pgrep >/dev/null 2>&1; then
|
||||
pgrep -af -- "$pattern"
|
||||
return $?
|
||||
fi
|
||||
|
||||
ps -ef | awk -v pattern="$pattern" '
|
||||
ps -ef | PPG_PATTERN="$pattern" awk '
|
||||
BEGIN { pattern = ENVIRON["PPG_PATTERN"] }
|
||||
NR == 1 {
|
||||
print
|
||||
next
|
||||
@@ -196,11 +201,20 @@ ppn()
|
||||
return 1
|
||||
fi
|
||||
|
||||
# -e: select all processes
|
||||
# -o: specify custom output columns (PID and Command name)
|
||||
# grep -w: ensures exact word matching so 'bash' doesn't match 'dbash'
|
||||
# shellcheck disable=SC2009 # pgrep do not offer the -w switch
|
||||
ps -eo pid,comm | grep -w "$1"
|
||||
# ps prints the PID and the command name (comm, 15 characters at most, no
|
||||
# arguments); awk keeps the header and the lines whose whole name equals
|
||||
# the argument. grep -w was wrong: it also matched inside the PID column,
|
||||
# and in names such as "kde-bash" ("-" is not a word character).
|
||||
ps -eo pid,comm | PPN_NAME="$1" awk '
|
||||
BEGIN { name = ENVIRON["PPN_NAME"] }
|
||||
NR == 1 { print; next }
|
||||
{
|
||||
line = $0
|
||||
sub(/^ *[0-9]+ +/, "", line)
|
||||
if (line == name) { print; found = 1 }
|
||||
}
|
||||
END { exit found ? 0 : 1 }
|
||||
'
|
||||
}
|
||||
export -f ppn
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -259,7 +273,8 @@ gpid()
|
||||
if command -v pgrep >/dev/null 2>&1; then
|
||||
result=$(pgrep -d ' ' -x -- "$proc_name")
|
||||
else
|
||||
result=$(ps -eo pid=,comm= | awk -v proc="$proc_name" '
|
||||
result=$(ps -eo pid=,comm= | GPID_NAME="$proc_name" awk '
|
||||
BEGIN { proc = ENVIRON["GPID_NAME"] }
|
||||
$2 == proc {
|
||||
if (out != "") {
|
||||
out = out " "
|
||||
@@ -288,25 +303,60 @@ export -f gpid
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Internal helper: take the kill-style signal arguments (-9, -KILL, -SIGHUP)
|
||||
# out of an argument list. getopt rejects them as unknown options, so they must
|
||||
# be removed before it runs. They are recognised anywhere before a "--"
|
||||
# (digits, or upper-case names, which cannot clash with the lower-case options
|
||||
# of the callers); everything after "--" is left untouched.
|
||||
# Usage: _proc_split_signals <signals_array> <rest_array> [args...]
|
||||
_proc_split_signals()
|
||||
{
|
||||
local -n _pss_sigs="$1" _pss_rest="$2"
|
||||
shift 2
|
||||
_pss_sigs=()
|
||||
_pss_rest=()
|
||||
local a
|
||||
while [[ $# -gt 0 ]]; do
|
||||
a="$1"
|
||||
shift
|
||||
if [[ $a == "--" ]]; then
|
||||
_pss_rest+=(-- "$@")
|
||||
break
|
||||
fi
|
||||
if [[ $a =~ ^-([0-9]+|SIG[A-Z0-9+-]+|[A-Z][A-Z0-9+-]*)$ ]]; then
|
||||
_pss_sigs+=("$a")
|
||||
else
|
||||
_pss_rest+=("$a")
|
||||
fi
|
||||
done
|
||||
}
|
||||
export -f _proc_split_signals
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Kill all processes owned by the given users (kill user)
|
||||
# Usage: ku <username1 [username2 ...]>
|
||||
# Usage: ku [options] <username1 [username2 ...]>
|
||||
# The users are validated first: nothing is killed if one of them is unknown.
|
||||
# The processes of root and of the current user are never killed (it would take
|
||||
# the session down), except in dry-run mode.
|
||||
ku()
|
||||
{
|
||||
local PARSED
|
||||
local dry_run=0
|
||||
local -a signal_opt=()
|
||||
local -a signal_opt=() _ku_sigs=() _ku_rest=()
|
||||
|
||||
PARSED=$(getopt -o hns: --long help,dry-run,signal: -n 'ku' -- "$@")
|
||||
_proc_split_signals _ku_sigs _ku_rest "$@"
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hns: --long help,dry-run,signal: -n 'ku' -- "${_ku_rest[@]}")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"ku --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local -a _pargs=()
|
||||
readarray -td '' _pargs < <(printf '%s' "$PARSED" | xargs printf '%s\0')
|
||||
set -- "${_pargs[@]}"
|
||||
# Restore the getopt-normalised (shell-quoted) argument list
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
@@ -317,7 +367,9 @@ ku()
|
||||
printf "\t-n, --dry-run\t\tDisplay commands without executing them\n"
|
||||
printf "\t-s, --signal SIG\tSignal to send (overrides KU_DEFAULT_SIGNAL)\n"
|
||||
printf "\t --signal=SIG\tSame as above\n"
|
||||
printf "\t -SIG / -NUM\t\tSignal format compatible with kill\n"
|
||||
printf "\t -SIG / -NUM\t\tSignal format compatible with kill\n\n"
|
||||
printf "Users are given by name or numeric ID. The processes of root and of the\n"
|
||||
printf "current user are never killed (except with --dry-run).\n"
|
||||
return 0
|
||||
;;
|
||||
-n|--dry-run)
|
||||
@@ -332,113 +384,134 @@ ku()
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
signal_opt=("$1")
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Accept kill-style signal forms not handled by getopt, before usernames.
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-[0-9]*|-SIG*|-[[:alpha:]]*)
|
||||
signal_opt=("$1")
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
disp E "Unknown option: $1, use \"ku --help\" to display usage."
|
||||
disp E "Invalid options, use \"ku --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$1" ]]; then
|
||||
# A kill-style signal (-9, -KILL) applies unless -s/--signal was given
|
||||
if [[ ${#signal_opt[@]} -eq 0 && ${#_ku_sigs[@]} -gt 0 ]]; then
|
||||
signal_opt=("${_ku_sigs[-1]}")
|
||||
fi
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
disp E "Usage: ku [options] <username1 [username2 ...]>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local u
|
||||
# First pass: resolve every user to a name (killall only takes names, so
|
||||
# numeric IDs are translated) and check it, before anything is killed.
|
||||
local u uid uname
|
||||
local -a unames=()
|
||||
for u in "$@"; do
|
||||
if ! id "$u" >/dev/null 2>&1; then
|
||||
if ! uid=$(id -u -- "$u" 2>/dev/null); then
|
||||
disp E "User '$u' does not exist."
|
||||
return 1
|
||||
fi
|
||||
uname=$(id -un -- "$u")
|
||||
if (( ! dry_run )) && [[ $uid -eq 0 || $uid -eq $EUID ]]; then
|
||||
disp E "Safety abort: refusing to kill the processes of '$uname' (root or current user)."
|
||||
return 1
|
||||
fi
|
||||
unames+=("$uname")
|
||||
done
|
||||
|
||||
# Second pass: kill. The failure of one user does not skip the others.
|
||||
local rc=0
|
||||
local -a cmd
|
||||
for uname in "${unames[@]}"; do
|
||||
# killall (psmisc) preferred; fall back to pkill (procps-ng).
|
||||
if command -v killall >/dev/null 2>&1; then
|
||||
cmd=(killall)
|
||||
if [[ ${#signal_opt[@]} -gt 0 ]]; then
|
||||
cmd+=("${signal_opt[@]}")
|
||||
elif [[ -n "${KU_DEFAULT_SIGNAL:-}" ]]; then
|
||||
cmd+=("-${KU_DEFAULT_SIGNAL}")
|
||||
fi
|
||||
cmd+=(-u "$uname")
|
||||
elif command -v pkill >/dev/null 2>&1; then
|
||||
cmd=(pkill)
|
||||
# Translate killall's -s SIGNAME form to pkill's -SIGNAME form.
|
||||
if [[ ${#signal_opt[@]} -eq 2 && "${signal_opt[0]}" == "-s" ]]; then
|
||||
cmd+=("-${signal_opt[1]}")
|
||||
elif [[ ${#signal_opt[@]} -gt 0 ]]; then
|
||||
cmd+=("${signal_opt[@]}")
|
||||
elif [[ -n "${KU_DEFAULT_SIGNAL:-}" ]]; then
|
||||
cmd+=("-${KU_DEFAULT_SIGNAL}")
|
||||
fi
|
||||
cmd+=(-u "$uname")
|
||||
else
|
||||
local cmd
|
||||
disp E "ku: neither 'killall' (psmisc) nor 'pkill' (procps) is available."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# killall (psmisc) preferred; fall back to pkill (procps-ng).
|
||||
if command -v killall >/dev/null 2>&1; then
|
||||
cmd=(killall)
|
||||
if [[ ${#signal_opt[@]} -gt 0 ]]; then
|
||||
cmd+=("${signal_opt[@]}")
|
||||
elif [[ -n "${KU_DEFAULT_SIGNAL:-}" ]]; then
|
||||
cmd+=("-${KU_DEFAULT_SIGNAL}")
|
||||
fi
|
||||
cmd+=(-u "$u")
|
||||
elif command -v pkill >/dev/null 2>&1; then
|
||||
cmd=(pkill)
|
||||
# Translate killall's -s SIGNAME form to pkill's -SIGNAME form.
|
||||
if [[ ${#signal_opt[@]} -eq 2 && "${signal_opt[0]}" == "-s" ]]; then
|
||||
cmd+=("-${signal_opt[1]}")
|
||||
elif [[ ${#signal_opt[@]} -gt 0 ]]; then
|
||||
cmd+=("${signal_opt[@]}")
|
||||
elif [[ -n "${KU_DEFAULT_SIGNAL:-}" ]]; then
|
||||
cmd+=("-${KU_DEFAULT_SIGNAL}")
|
||||
fi
|
||||
cmd+=(-u "$u")
|
||||
else
|
||||
disp E "ku: neither 'killall' (psmisc) nor 'pkill' (procps) is available."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if (( dry_run )); then
|
||||
printf "DRY-RUN: "
|
||||
printf "%q " "${cmd[@]}"
|
||||
printf "\n"
|
||||
else
|
||||
"${cmd[@]}"
|
||||
fi
|
||||
if (( dry_run )); then
|
||||
printf "DRY-RUN: "
|
||||
printf "%q " "${cmd[@]}"
|
||||
printf "\n"
|
||||
else
|
||||
"${cmd[@]}" || rc=1
|
||||
fi
|
||||
done
|
||||
return "$rc"
|
||||
}
|
||||
export -f ku
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Internal helper of kt: send a signal to a process and, first, to all its
|
||||
# descendants (depth first, so a parent never outlives the children it could
|
||||
# respawn from). A failure does not stop the others; the status is non-zero if
|
||||
# any kill failed.
|
||||
# Usage: _kt_tree <dry_run 0|1> <pid> [kill_args...]
|
||||
_kt_tree()
|
||||
{
|
||||
local dry="$1" pid="$2" child rc=0
|
||||
shift 2
|
||||
for child in $(pgrep -P "$pid" 2>/dev/null); do
|
||||
_kt_tree "$dry" "$child" "$@" || rc=1
|
||||
done
|
||||
|
||||
if (( dry )); then
|
||||
printf "DRY-RUN: "
|
||||
printf "%q " kill "$@" "$pid"
|
||||
printf "\n"
|
||||
else
|
||||
kill "$@" "$pid" || rc=1
|
||||
fi
|
||||
return "$rc"
|
||||
}
|
||||
export -f _kt_tree
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Kill all children of a process then the process (kill tree)
|
||||
# Usage: kt <pid> [kill_options]
|
||||
# Usage: kt [options] <pid>
|
||||
kt()
|
||||
{
|
||||
local PARSED
|
||||
local dry_run=0
|
||||
local -a pre_kill_opts=()
|
||||
local -a kill_args=() _kt_sigs=() _kt_rest=()
|
||||
|
||||
PARSED=$(getopt -o hns: --long help,dry-run,signal: -n 'kt' -- "$@")
|
||||
_proc_split_signals _kt_sigs _kt_rest "$@"
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hns: --long help,dry-run,signal: -n 'kt' -- "${_kt_rest[@]}")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"kt --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local -a _pargs=()
|
||||
readarray -td '' _pargs < <(printf '%s' "$PARSED" | xargs printf '%s\0')
|
||||
set -- "${_pargs[@]}"
|
||||
# Restore the getopt-normalised (shell-quoted) argument list
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "kt: Kill all children of a process then the process (kill tree).\n\n"
|
||||
printf "Usage: kt [options] <pid> [kill_options]\n\n"
|
||||
printf "Usage: kt [options] <pid>\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-n, --dry-run\t\tDisplay kill commands without executing them\n"
|
||||
@@ -452,79 +525,53 @@ kt()
|
||||
shift
|
||||
;;
|
||||
-s|--signal)
|
||||
pre_kill_opts+=(-s "$2")
|
||||
kill_args=(-s "$2")
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
pre_kill_opts+=("$1")
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Accept kill-style signal forms not handled by getopt, before the PID.
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-[0-9]*|-SIG*|-[[:alpha:]]*)
|
||||
pre_kill_opts+=("$1")
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
disp E "Unknown option: $1, use \"kt --help\" to display usage."
|
||||
disp E "Invalid options, use \"kt --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$1" ]]; then
|
||||
disp E "Usage: kt [options] <pid> [kill_options]"
|
||||
# A kill-style signal (-9, -KILL) applies unless -s/--signal was given
|
||||
if [[ ${#kill_args[@]} -eq 0 && ${#_kt_sigs[@]} -gt 0 ]]; then
|
||||
kill_args=("${_kt_sigs[-1]}")
|
||||
fi
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
disp E "Usage: kt [options] <pid>"
|
||||
return 1
|
||||
fi
|
||||
if [[ $# -gt 1 ]]; then
|
||||
disp E "Unexpected argument: $2. Usage: kt [options] <pid>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local parent_pid="$1"
|
||||
shift
|
||||
|
||||
local -a kill_opts=("${pre_kill_opts[@]}" "$@")
|
||||
|
||||
if [[ ! $parent_pid =~ ^[0-9]+$ ]]; then
|
||||
disp E "Invalid PID: '$parent_pid'."
|
||||
return 1
|
||||
fi
|
||||
if [[ "$parent_pid" == "0" || "$parent_pid" == "1" ]]; then
|
||||
disp E "Safety abort: Refusing to kill PID $parent_pid (system critical)."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local children_pids
|
||||
children_pids=$(pgrep -P "$parent_pid" 2>/dev/null || true)
|
||||
|
||||
local pid
|
||||
for pid in $children_pids; do
|
||||
if (( dry_run )); then
|
||||
kt --dry-run "$pid" "${kill_opts[@]}" || break
|
||||
else
|
||||
kt "$pid" "${kill_opts[@]}" || break
|
||||
fi
|
||||
done
|
||||
|
||||
if (( dry_run )); then
|
||||
local cmd=(kill "${kill_opts[@]}" "$parent_pid")
|
||||
printf "DRY-RUN: "
|
||||
printf "%q " "${cmd[@]}"
|
||||
printf "\n"
|
||||
else
|
||||
kill "${kill_opts[@]}" "$parent_pid"
|
||||
if [[ "$parent_pid" == "$$" || "$parent_pid" == "$BASHPID" ]]; then
|
||||
disp E "Safety abort: Refusing to kill the current shell (PID $parent_pid)."
|
||||
return 1
|
||||
fi
|
||||
if ! command -v pgrep >/dev/null 2>&1; then
|
||||
disp E "kt: 'pgrep' (procps) is required to find the children."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_kt_tree "$dry_run" "$parent_pid" "${kill_args[@]}"
|
||||
}
|
||||
export -f kt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user