#!/bin/sh
#
# quagga	Starts/stops quagga daemons and watchquagga.
#		Create a daemon.conf file to have that routing daemon
#		started/stopped automagically when using this script
#		without any daemon names as args.
#		If watchquagga is available, it will also be
#		started/stopped if the script is called without
#		any daemon names.
#

ME=$(basename $0)

usage() {
	echo "Usage: ${ME} {start|stop|restart} [daemon ...]"
	exit 2
}

if [ -z "$1" ]
then
	usage
else
	COMMAND=$1
fi
shift
ARG_DAEMONS=$*
BINDIR=/usr/sbin
CONFDIR=/etc/quagga
STATEDIR=/var/run/quagga
DAEMONS="zebra ripd ripngd ospfd ospf6d bgpd"
DAEMON_FLAGS=-d
WATCHQUAGGA_FLAGS="-d -z -T 60 -R"
WATCHQUAGGA_CMD="$0 watchrestart"
if [ ${COMMAND} != "watchrestart" ]
then
	DAEMONS="${DAEMONS} watchquagga"
fi
DAEMONS_STARTSEQ=${DAEMONS}

reverse()
{
	local revlist r
        revlist=
        for r
	do
                revlist="$r $revlist"
        done
        echo $revlist
}

DAEMONS_STOPSEQ=$(reverse ${DAEMONS_STARTSEQ})

#pidof() {
#	ps ax | awk 'match($5, "(^|/)'"$1"'$") > 0 { printf " %s", $1 }'
#}

quit() {
	echo "${ME}: $1"
	exit 0
}

die() {
	echo "${ME}: $1"
	exit 1
}

is_in() {
	local i
	for i in $2
	do
		[ "$1" = "$i" ] && return 0
	done
	return 1
}

select_subset() {
	local unknown i j
	unknown=
	RESULT=
	for i in $1
	do
		is_in $i "$2" || unknown="$unknown $i"
	done
	if [ -n "$unknown" ]
	then
		RESULT=$unknown
		return 1
	else
		for j in $2
		do
			is_in $j "$1" && RESULT="$RESULT $j"
		done
		return 0
	fi
}

# check command
. /etc/rc.conf

case ${COMMAND} in
autostart|start|stop|restart)
	;;
watchrestart)
	if [ -n "$ARG_DAEMONS" ]
	then
		echo "${ME}: watchrestart mode is only for use by watchquagga"
		exit 2
	fi
	;;
*)
	usage
	;;
esac

# select daemons to start

case ${COMMAND} in
autostart)
	test x"$quagga" = x"NO" && exit 0
	exec $0 start
	;;
start|restart|watchrestart)
	START_DAEMONS=
	for d in ${DAEMONS_STARTSEQ}
	do
		[ -x "${BINDIR}/${d}" -a -f "${CONFDIR}/${d}.conf" ] \
		&& START_DAEMONS="${START_DAEMONS}${d} "
	done
	WATCHQUAGGA_DAEMONS=${START_DAEMONS}
	if is_in watchquagga "${DAEMONS_STARTSEQ}"
	then
		START_DAEMONS="${START_DAEMONS} watchquagga"
	fi
	if [ -n "${ARG_DAEMONS}" ]
	then
		if select_subset "${ARG_DAEMONS}" "${DAEMONS}"
		then
			if select_subset "${ARG_DAEMONS}" "${START_DAEMONS}"
			then
				START_DAEMONS=${RESULT}
			else
				die "these daemons are not startable:${RESULT}."
			fi
		else
			die "unknown daemons:${RESULT}; choose from: ${DAEMONS}."
		fi
	fi
	;;
esac

# select daemons to stop

case ${COMMAND} in
stop|restart|watchrestart)
	STOP_DAEMONS=${DAEMONS_STOPSEQ}
	if [ -n "${ARG_DAEMONS}" ]
	then
		if select_subset "${ARG_DAEMONS}" "${STOP_DAEMONS}"
		then
			STOP_DAEMONS=${RESULT}
		else
			die "unknown daemons:${RESULT}; choose from: ${DAEMONS}."
		fi
	fi
	stop_daemons=
	for d in ${STOP_DAEMONS}
	do
		pidfile=${STATEDIR}/${d}.pid
		if [ -f "${pidfile}" -o -n "$(pidof ${d})" ]
		then
			stop_daemons="${stop_daemons}${d} "
		elif [ -n "${ARG_DAEMONS}" ]
		then
			echo "${ME}: found no ${d} process running."
		fi
	done
	STOP_DAEMONS=${stop_daemons}
	;;
esac

# stop daemons

for d in $STOP_DAEMONS
do
	echo -n "${ME}: Stopping ${d} ... "
	pidfile=${STATEDIR}/${d}.pid
	if [ -f "${pidfile}" ]
	then
		file_pid=$(cat ${pidfile})
		if [ -z "${file_pid}" ]
		then
			echo -n "no pid file entry found ... "
		fi
	else
		file_pid=
		echo -n "no pid file found ... "
	fi
	proc_pid=$(pidof ${d})
	if [ -z "${proc_pid}" ]
	then
		echo -n "found no ${d} process running ... "
	else
		count=0
		notinpidfile=
		for p in ${proc_pid}
		do
			count=$((${count}+1))
			if kill ${p}
			then
				echo -n "killed ${p} ... "
			else
				echo -n "failed to kill ${p} ... "
			fi
			[ "${p}" = "${file_pid}" ] \
			|| notinpidfile="${notinpidfile} ${p}"
		done
		[ ${count} -le 1 ] \
		|| echo -n "WARNING: ${count} ${d} processes were found running ... "
		for n in ${notinpidfile}
		do
			echo -n "WARNING: process ${n} was not in pid file ... "
		done
	fi
	count=0
	survivors=$(pidof ${d})
	while [ -n "${survivors}" ]
	do
		sleep 1
		count=$((${count}+1))
		survivors=$(pidof ${d})
		[ -z "${survivors}" -o ${count} -gt 5 ] && break
		for p in ${survivors}
		do
			sleep 1
			echo -n "${p} "
			kill ${p}
		done
	done
	survivors=$(pidof ${d})
	[ -n "${survivors}" ] && \
	if kill -KILL ${survivors}
	then
		echo -n "KILLed ${survivors} ... "
	else
		echo -n "failed to KILL ${survivors} ... "
	fi
	sleep 1
	survivors=$(pidof ${d})
	if [ -z "${survivors}" ]
	then
		echo -n "done."
		if [ -f "${pidfile}" ]
		then
			rm -f ${pidfile} \
			|| echo -n " Failed to remove pidfile."
		fi
	else
		echo -n "failed to stop ${survivors} - giving up."
		if [ "${survivors}" != "${file_pid}" ]
		then
			if echo "${survivors}" > ${pidfile}
			then
				chown quagga:quagga ${pidfile}
				echo -n " Wrote ${survivors} to pidfile."
			else
				echo -n " Failed to write ${survivors} to pidfile."
			fi
		fi
	fi
	echo
done

# start daemons

if [ -n "$START_DAEMONS" ]
then
	[ -d ${CONFDIR} ] \
	|| quit "${ME}: no config directory ${CONFDIR} - exiting."
	chown -R quagga:quagga ${CONFDIR}
	[ -d ${STATEDIR} ] || mkdir -p ${STATEDIR} \
	|| die "${ME}: could not create state directory ${STATEDIR} - exiting."
	chown -R quagga:quagga ${STATEDIR}

	for d in $START_DAEMONS
	do
		echo -n "${ME}: Starting ${d} ... "
		proc_pid=$(pidof ${d})
		pidfile=${STATEDIR}/${d}.pid
		file_pid=
		if [ -f "${pidfile}" ]
		then
			file_pid=$(cat ${pidfile})
			if [ -n "${file_pid}" ]
			then
				echo -n "found old pid file entry ${file_pid} ... "
			fi
		fi
		if [ -n "${proc_pid}" ]
		then
			echo -n "found ${d} running (${proc_pid}) - skipping ${d}."
			if [ "${proc_pid}" != "${file_pid}" ]
			then
				if echo "${proc_pid}" > ${pidfile}
				then
					chown quagga:quagga ${pidfile}
					echo -n " Wrote ${proc_pid} to pidfile."
				else
					echo -n " Failed to write ${proc_pid} to pidfile."
				fi
			fi
		elif rm -f "${pidfile}"
		then
			if [ "${d}" = "watchquagga" ]
			then
				$("${BINDIR}/${d}" \
					${WATCHQUAGGA_FLAGS} \
					"${WATCHQUAGGA_CMD}" \
					${WATCHQUAGGA_DAEMONS})
				status=$?
			else
				$("${BINDIR}/${d}" ${DAEMON_FLAGS})
				status=$?
			fi
			if [ $status -eq 0 ]
			then
				echo -n "done."
			else
				echo -n "failed."
			fi
		else
			echo -n " failed to remove pidfile."
		fi
		echo
	done
fi
