#!/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=/var/etc/quagga
STATEDIR=/var/run/quagga
RUNUSER=network
RUNGROUP=$RUNUSER
DAEMONS=${DAEMONS:-"zebra ripd ripngd ospfd ospf6d bgpd"}
DAEMON_FLAGS=-d
WATCHQUAGGA_FLAGS="-d -z -T 60 -r"
WATCHQUAGGA_CMD="$0 watchrestart %s"
if [ ${COMMAND} != "watchrestart" -a -x "${BINDIR}/watchquagga" ]
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

case ${COMMAND}
in
	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
	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}
					WATCHQUAGGA_DAEMONS=${RESULT%watchquagga}
				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 ${RUNUSER}:${RUNGROUP} ${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 ${RUNUSER}:${RUNGROUP} ${CONFDIR}
	[ -d ${STATEDIR} ] || mkdir -p ${STATEDIR} \
	|| die "${ME}: could not create state directory ${STATEDIR} - exiting."
	chown -R ${RUNUSER}:${RUNGROUP} ${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 ${RUNUSER}:${RUNGROUP} ${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} -f ${CONFDIR}/${d}.conf
				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
