#!/bin/sh # platform ... devuan dash # GPL_3+ cat << 'EEE' > /dev/null /* killtree .... kill process family. bourne-shell script * Copyright (C) 2018,2019 Momi-g * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ EEE # ini=`cat << 'END' # -h bool 0 # -d bool 0 # -s str 'TERM' # -v bool 0 # -c bool 0 # END # ` # buf=`ckopt "$ini"` # eval "$buf" ## ---this code is generated by ckopt # ---optsetting # # -h bool 0 # -d bool 0 # -s str 'TERM' # -v bool 0 # -c bool 0 ckopt_func () { if [ "$1" = "-d" ] ; then eval "opt_${2#?}"'="$3"' # opt_?="$3" return 0 fi ckopt_opt="$2" if [ "$1" = "-e" ] ; then shift 4 while [ $# -gt 0 ] do if [ "$1" != "${1#[#]e}" ] || [ "$1" != "${1#[#]E}" ] ; then eval "${1#[#]?}" if [ "$?" != "0" ] ; then OPTARG="errmsg $ckopt_opt $1" return 1 fi fi shift done return 0 fi if [ "$1" = "-c" ] ; then shift 3 printf '%s\n' "$1" | tr '[:upper:]' '[:lower:]' | awk '$1 ~ /int/ {exit 1}' if [ "$?" = "1" ] ; then shift set -- "dummy" 'printf "%d" "$OPTARG" >/dev/null 2>&1' "$@" fi shift while [ $# -gt 0 ] do eval "$1" if [ "$?" != "0" ] ; then OPTARG="errmsg $ckopt_opt $1" return 1 fi shift done return 0 fi echo "$0: bug. sleep" >/dev/stderr while : do sleep 1000 done exit 1 } # ---dflset OPTIND=1 # fixed value. posix rule. OPTERR=0 ckopt_buf='' ckopt_opt='' ckopt_func -d -h bool 0 ckopt_func -d -d bool 0 ckopt_func -d -s str 'TERM' ckopt_func -d -v bool 0 ckopt_func -d -c bool 0 #---getopts loop. break if all args read or detect '--' while : do if [ 0 -eq "$#" ] || [ "${OPTARG%% *}" = "errmsg" ] ; then break fi check_opt='$'"$OPTIND" eval "check_opt=\"$check_opt\"" if [ "$check_opt" = "--" ] ; then shift $OPTIND; break fi getopts ":h:d:s:v:c:" ckopt_opt "$@" # ":a:bc:f:" etc... if [ "$?" = "1" ] ; then # detect end/normal args. save general args. shift $((OPTIND - 1)) if [ "$#" -eq "0" ] ; then break fi ckopt_buf="$ckopt_buf "`printf '%s' "$1" | sed -e "{ s#'#'\"'\"'#g s/^/'/g s/$/'/g }"` shift OPTIND=1 continue fi # --- your setting if [ "$ckopt_opt" = "h" ] ; then opt_h="$OPTARG" ckopt_func -c -h bool 0 continue fi if [ "$ckopt_opt" = "d" ] ; then opt_d="$OPTARG" ckopt_func -c -d bool 0 continue fi if [ "$ckopt_opt" = "s" ] ; then opt_s="$OPTARG" ckopt_func -c -s str 'TERM' continue fi if [ "$ckopt_opt" = "v" ] ; then opt_v="$OPTARG" ckopt_func -c -v bool 0 continue fi if [ "$ckopt_opt" = "c" ] ; then opt_c="$OPTARG" ckopt_func -c -c bool 0 continue fi # --- your setting end # hit err. ckopt chars... # err detect@silent mode # $?=1 ... detect optend or '--' # ':' ... detect option, but dont have subargs (OPTARG="factor"). # '?' ... detect unsupported option char (OPTARG="factor") or args end (OPTARG="blank"). # OPTARG ... "" is optend. "a/b/c..." is invalid option # OPTIND ... if err, OPTIND indicates next (new) arg pos. OPTARG="errmsg -$OPTARG invalid option / misses subargs" done # --- post process. set not optional args & clean & #eE last cmd. for ii in 1 # dummyjump logic do OPTIND=1 # skip test "${OPTARG%% *}" != "errmsg" || break ckopt_buf="set -- $ckopt_buf"' "$@"' # set -- 'cmd' .. "$@" eval "$ckopt_buf" # run #E cmd. cmd + test $? ... xn ckopt_func -e -h bool 0 ckopt_func -e -d bool 0 ckopt_func -e -s str 'TERM' ckopt_func -e -v bool 0 ckopt_func -e -c bool 0 done if [ "${OPTARG%% *}" = "errmsg" ] ; then printf "$0: opterr. %s\n" "$OPTARG" >/dev/stderr ; while : ; do sleep 1000 ; done ; exit 1 test 1 = 0 fi ## ---generate by ckopt end if [ "$?" != "0" ] ; then echo "$0: args err($OPTARG). see -h. sleep." >/dev/stderr ; while : ; do sleep 1000 ; done exit 1 fi # arg intck for ii do printf '%d' "$ii" >/dev/null 2>&1 if [ "$?" != "0" ] ; then opt_h="1" break fi done if [ "$opt_h" = "1" ] || [ $# -eq 0 ] ; then cat << 'EEE' HowTo (show/kill process family. bourne-shell script) opt: -h, -d(isp, not kill), -s(ignal), -v(erbose), -c(hildren only) ------ ex.) ~$ killtree 1111 -d >>> 0 ppid10 1111 # 1111 -+- 3237 --- 3238 1 ppid1111 3237 # +- 3241 1 ppid1111 3241 2 ppid3237 3238 # generation : parent pid : child pid ex.) ~$ killtree 1111 >>> kill all processes from youngest generation. kill 3238->3237,3241->1111 ex.) ~$ killtree 1111 -s USR1 >>> kill using SIGUSR1. -s opt is based on 'kill' opt. see 'kill -l'. ex.) ~$ killtree 1111 -v #... show report to stdout >>> try: 1111 3237 3238 3241 suc: 1111 3238 rest: 3241 (signal denied etc) lost: 3237 (not found. race condition etc) ex.) ~$ killtree 1111 -c # >>> kill without parent (1111). EEE exit 0 fi #--main_func # func_killtree 1111 TERM cvd >>> pid signal optionchar(-d ...d, -c -v ...cv) func_killtree() { ( kt_buf="" kt_nowpid=$$ kt_tgtpid="$1" # 1234 etc kt_signal="$2" # TERM USR1 etc kt_opts="$3" # dvc -> -c -v -d ... 1 chars kt_pslist=`ps -eo ppid,pid` kt_gen=0 kt_cnt=1 kt_ppids="" kt_add="" kt_result="" kt_failed="" kt_req="" kt_rest="" kt_remove="" kt_lost="" kt_suc="" # skip init pid kt_cpidcmd=`cat << 'EEE' for ii in $kt_buf do echo "$kt_pslist" | awk -v gen=$kt_gen -v ppid=$ii -v skip=$kt_nowpid ' $1 == ppid && $2 != skip {print gen " ppid" $1 " " $2}' done EEE ` # remove this script pid kt_buf=`echo "$kt_pslist" | awk -v pid=$kt_tgtpid '$2 == pid {print $1}'` kt_result="$kt_gen ppid$kt_buf $kt_tgtpid" # 0 ppid1234 1111 gen/ppid/cpid while : do kt_ppids=`echo "$kt_result" | awk -v gen=$kt_gen '$1 == gen {print $NF}' ` kt_gen=$((kt_gen+1)) kt_buf="$kt_ppids" kt_add=`eval "$kt_cpidcmd"` kt_result=`printf '%s\n%s\n' "$kt_result" "$kt_add" | grep -v '^$'` #ck diff kt_buf=`echo "$kt_result" | wc -l` if [ "$kt_buf" = "$kt_cnt" ] ; then break fi kt_cnt="$kt_buf" done # result=`echo "$result" | sort -nr ` kt_buf=`echo "$kt_opts" | tr -dc 'c'` if [ "$kt_buf" != "" ] ; then kt_result=`echo "$kt_result" | grep -v '^0'` fi kt_buf=`echo "$kt_opts" | tr -dc 'd'` if [ "$kt_buf" != "" ] ; then echo "$kt_result" else kt_buf=`echo "$kt_result" | sort -nr | awk '{printf("%s ",$NF)}'` kt_failed="" for kt_ii in $kt_buf do kill -0 $kt_ii >/dev/null 2>&1 if [ "$?" != "0" ] ; then kt_failed="$kt_failed $kt_ii" else kill "-$kt_signal" $kt_ii >/dev/null 2>&1 fi done kt_buf=`echo "$kt_opts" | tr -dc 'v'` if [ "$kt_buf" != "" ] ; then # disp report kt_req=`echo "$kt_result" | awk '{print $NF }' | sort -n` kt_buf=`ps -eo pid` # now running all process kt_rest=`printf '%s\n%s\n' "$kt_buf" "$kt_req" | sort -n | uniq -d` # dup only kt_remove=`printf '%s\n%s\n' "$kt_req" "$kt_rest" | sort -n | uniq -u` # uniq only kt_lost=`printf '%s\n%s\n' "$kt_remove" "$kt_failed" | sort -n | uniq -d ` kt_suc=`printf '%s\n%s\n' "$kt_remove" "$kt_lost" | sort -n | uniq -u` set -- $kt_req echo "try: $*" set -- $kt_suc echo "suc: $*" set -- $kt_rest echo "rest: $*" set -- $kt_lost echo "lost: $*" fi fi #--- clean >> omit. use subshell '()' # kt_buf="" # kt_nowpid="" # kt_tgtpid="" # kt_signal="" # kt_opts="" # # kt_pslist="" # kt_gen="" # kt_cnt="" # kt_ppids="" # kt_add="" # kt_result="" # kt_failed="" # # kt_req="" # kt_rest="" # kt_remove="" # kt_lost="" # kt_suc="" # # kt_cpidcmd="" ) } #--- main optstr="" if [ "$opt_d" = "1" ] ; then optstr="$optstr""d" fi if [ "$opt_v" = "1" ] ; then optstr="$optstr""v" fi if [ "$opt_c" = "1" ] ; then optstr="$optstr""c" fi # func_killtree 1111 TERM cvd >>> pid signal optionchar(-d ...d, -c -v ...cv) for ii do func_killtree "$ii" "$opt_s" "$optstr" done