diff --git a/doc/todo.md b/doc/todo.md index 7981a94..0e67793 100755 --- a/doc/todo.md +++ b/doc/todo.md @@ -71,7 +71,7 @@ version-bump. **[easy]** ### processes -- [ ] **`ku` dry-run flag** — add `-n` / `--dry-run` to print what would be +- [X] **`ku` dry-run flag** — add `-n` / `--dry-run` to print what would be killed without acting. **[easy]** ### pwd diff --git a/profile.d/processes.sh b/profile.d/processes.sh index ee04d1d..75ec278 100644 --- a/profile.d/processes.sh +++ b/profile.d/processes.sh @@ -190,23 +190,88 @@ export -f gpid # Usage: ku ku() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - printf "ku: Kill all processes owned by the given users.\n\n" - printf "Usage: ku \n\n" - printf "Options:\n" - printf "\t-h, --help\t\tDisplay this help screen\n" - return 0 - fi + local dry_run=0 + local -a signal_opt=() + + while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + printf "ku: Kill all processes owned by the given users.\n\n" + printf "Usage: ku [options] \n\n" + printf "Options:\n" + printf "\t-h, --help\t\tDisplay this help screen\n" + 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\tSignal format compatible with kill\n" + return 0 + ;; + -n|--dry-run) + dry_run=1 + shift + ;; + -s|--signal) + if [[ -z "${2:-}" || "$2" == -* ]]; then + disp E "--signal requires a value." + return 1 + fi + signal_opt=(-s "$2") + shift 2 + ;; + --signal=*) + if [[ -z "${1#*=}" ]]; then + disp E "--signal requires a value." + return 1 + fi + signal_opt=(-s "${1#*=}") + shift + ;; + -[0-9]*|-SIG*|-[[:alpha:]]*) + signal_opt=("$1") + shift + ;; + --) + shift + break + ;; + -*) + disp E "Unknown option: $1, use \"ku --help\" to display usage." + return 1 + ;; + *) + break + ;; + esac + done + if [[ -z "$1" ]]; then - disp E "Usage: ku " + disp E "Usage: ku [options] " return 1 fi + + local u for u in "$@"; do if ! id "$u" >/dev/null 2>&1; then disp E "User '$u' does not exist." return 1 else - killall ${KU_DEFAULT_SIGNAL:+-${KU_DEFAULT_SIGNAL}} -u "$u" + local 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") + + if (( dry_run )); then + printf "DRY-RUN: " + printf "%q " "${cmd[@]}" + printf "\n" + else + "${cmd[@]}" + fi fi done } @@ -219,32 +284,91 @@ export -f ku # Usage: kt [kill_options] kt() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - printf "kt: Kill all children of a process then the process (kill tree).\n\n" - printf "Usage: kt [kill_options]\n\n" - printf "Options:\n" - printf "\t-h, --help\t\tDisplay this help screen\n" - return 0 - fi + local dry_run=0 + local -a pre_kill_opts=() + + while [[ $# -gt 0 ]]; 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] [kill_options]\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" + printf "\t-s, --signal SIG\tSignal to send to process tree\n" + printf "\t --signal=SIG\tSame as above\n" + printf "\t -SIG / -NUM\tSignal format compatible with kill\n" + return 0 + ;; + -n|--dry-run) + dry_run=1 + shift + ;; + -s|--signal) + if [[ -z "${2:-}" || "$2" == -* ]]; then + disp E "--signal requires a value." + return 1 + fi + pre_kill_opts+=(-s "$2") + shift 2 + ;; + --signal=*) + if [[ -z "${1#*=}" ]]; then + disp E "--signal requires a value." + return 1 + fi + pre_kill_opts+=(-s "${1#*=}") + shift + ;; + -[0-9]*|-SIG*|-[[:alpha:]]*) + pre_kill_opts+=("$1") + shift + ;; + --) + shift + break + ;; + *) + break + ;; + esac + done + if [[ -z "$1" ]]; then - disp E "Usage: kt " + disp E "Usage: kt [options] [kill_options]" return 1 fi local parent_pid="$1" shift + + local -a kill_opts=("${pre_kill_opts[@]}" "$@") + 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") + children_pids=$(pgrep -P "$parent_pid" 2>/dev/null || true) + local pid for pid in $children_pids; do - kt "$pid" "$@" || break + if (( dry_run )); then + kt --dry-run "$pid" "${kill_opts[@]}" || break + else + kt "$pid" "${kill_opts[@]}" || break + fi done - kill "$@" "$parent_pid" + + 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" + fi } export -f kt # ------------------------------------------------------------------------------