#!/bin/bash
# This software is licensed under the Gnu GPL version 2, see http://www.fsf.org/
# Fuer dieses Script gibt es eine OpenPGP-Signatur (ID 0xECCB5814) auf der Downloadseite (killtree.sig)

version=1.0.2

# Dieses Script wird so aufgerufen wie kill, killt aber nicht nur die uebergebenen PIDs, 
# sondern auch den ganzen Baum der Kindprozesse.

# CHANGELOG
# version 1.0.1, 2006-10-07, Hauke Laging, software@hauke-laging.de

usage () {
	echo Usage: >&2
	echo "'$0' [--show|--showonly] [-<signal>] pid ..." >&2
	echo "'$0' --help" >&2
	echo "'$0' --version" >&2
}

info () {
	usage
	cat <<EOT

This script calls kill for the argument PIDs and all child processes
thus sending the signal to a whole process tree.

This script accepts normal PIDs only (i.e. no negative ones for 
addressing process groups).
EOT
}

if [ "$1" = --help ]
	then
	info
	exit 0
fi

if [ "$1" = --version ]
	then
	echo "killtree version ${version}"
	echo "This software is licensed under the Gnu GPL version 2, see http://www.fsf.org/"
	exit 0
fi

type mktemp &>/dev/null || { echo "Program 'mktemp' is not available; aborting."; exit 1; }

ps_file=$(mktemp /tmp/ps.XXXXXX)
/bin/ps -eo pid,ppid | awk 'NR==1 {next;}; {print $1 " " $2}' > "$ps_file"

# PID-Arrays initialisieren

counter_wrpp=0
while read pid ppid
	do
	pids[$counter_wrpp]=$pid
	ppids[$counter_wrpp]=$ppid
	((counter_wrpp++))
done < "$ps_file"
rm "$ps_file"

show=no
showonly=no
if [ "$1" = --show -o "$1" = --showonly ]
	then
	show=yes
	if [ "$1" = --showonly ]
		then
		showonly=yes
	fi
	shift
fi
if [ "$1" != "${1#-}" ]
	then
	signal="$1"
	shift
else
	signal=
fi
if [ $# -eq 0 ]
	then
	echo "Argument missing; aborting" >&2
	usage
	exit 2
fi
kill_pids=()

for((counter_mainargs=1;counter_mainargs<=$#;counter_mainargs++))
	do
	root_pid=${!counter_mainargs}
	if [[ ! "$root_pid" =~ '^[1-9][0-9]+$' ]]
		then
		echo "Illegal argument '${root_pid}'; aborting" >&2
		exit 1 # bei kill sollte man bei fehlerhaftem Aufruf lieber beenden als ueberspringen...
	fi
	kill_pids=(${kill_pids[@]} $root_pid)
	search_pids=($root_pid)
	pids_found=yes
	while [ "$pids_found" = yes ]
		do
		ppid_matches=()
		for search_pid in ${search_pids[@]}
			do
			for((counter_pidarray=0;counter_pidarray<counter_wrpp;counter_pidarray++))
				do
				if [ $search_pid -eq  ${ppids[$counter_pidarray]} ]
					then
					tmp_i=${#kill_pids[@]}
					kill_pids[$tmp_i]=${pids[$counter_pidarray]}
					tmp_i=${#ppid_matches[@]}
					ppid_matches[$tmp_i]=${pids[$counter_pidarray]}
				fi
			done
		done
		search_pids=(${ppid_matches[*]})
		if [ ${#ppid_matches[*]} -gt 0 ]
			then
			pids_found=yes
		else
			pids_found=no
		fi
	done
done
count_kill=${#kill_pids[@]}

echo "Going to call 'kill ${signal}' for ${count_kill} PIDs."
if [ "$show" = yes ]
	then
	echo ${kill_pids[@]}
	if [ "$showonly" = yes ]
		then
		echo "Option --showonly given; exiting without sending signals." >&2
		exit 0
	fi
fi
count_success=0
count_failure=0
# Den Baum von der Wurzel zu den Blaettern abarbeiten, um zu vermeiden, dass Prozesse ihre beendeten Kinder neu starten, 
#	bevor sie selber beendet werden.
for((counter_kill=0;counter_kill<count_kill;counter_kill++))
	do
	if [ -n "$signal" ]
		then
		kill "$signal" ${kill_pids[counter_kill]} &>/dev/null
	else
		kill ${kill_pids[counter_kill]} &>/dev/null
	fi
	if [ $? -eq 0 ]
		then
		((count_success++))
	else
		((count_failure++))
	fi
done
echo "${count_success} processes received the signal."
echo "${count_failure} errors occurred."

