#!/bin/sh
#
# This file is part of the FreeWRT project. FreeWRT is copyrighted
# material, please see the LICENCE file in the top-level directory
# or at http://www.freewrt.org/licence for details.
#
# Christian Fischer <spaetzle@freewrt.org>
#

built_in() {
	local what=$1
	local alias=$(alias $what)
	unalias $what
	alias $(echo $alias|sed 's/'\''//g')
	shift
	$what $@
}

config() {
	[ x"$1" != x"0" ]
}

__ifupdown() {
	local environ=$(cat $ENVFILE)

	IFUPDOWN_ENV=$(echo -n $IFUPDOWN_ENV|sed 's/\(MDENT=\)[^ ]*[[:space:]]*//g';\
		echo " PARENT_IFACE_TYPE=$IFACE_TYPE"; \
		echo " PARENT_IFACE=$IFACE"; \
		echo " MDENT=$MDENT") \
		busybox "$@"

	retval=$?
	echo $environ > $ENVFILE
	return $retval
}

__exit() {
	[ "$1" != "0" ] && echo "H_ERR=1" >> $ENVFILE
	exit $1
}

iface_exists() {
	grep -q "${1:-$IFACE}:" /proc/net/dev
}

is_up() {
	local iface=${1:-$IFACE}

	if iface_exists $iface
	then
		ip a|grep ${iface}:[[:space:]]|grep -q UP && return

		### ifupdown netlink bug workaround ###
		# suppress "ip: RTNETLINK answers: File exists" message in the case of
		# iface is down but an ip address is assigned
		ip a|grep ${iface}$|grep -q inet && ip addr flush dev $iface >/dev/null 2>&1
		### end workaround ###
	fi
	return 1
}

main_env_update() {
	local t_mdent lastmatch=""

	# get submode from calling hook script
	SUBMODE=$(dirname $0|sed '{ s/\(.*\/\)*//; s/\.d$//}')

	# create env file
	if ! [ -f "$ENVFILE" ]
	then
		[ -d $(dirname $ENVFILE) ] || mkdir -p $(dirname $ENVFILE)
		echo "MDENT=0" >> $ENVFILE
		echo "LASTLOG=0" >> $ENVFILE

		if is_up
		then
			IFACE_STATE="up"
		else
			IFACE_STATE="down"
		fi
		echo "IFACE_STATE=$IFACE_STATE" >> $ENVFILE

		if [ "$IFACE" != "lo" ]
		then
			# search for existing lib files end evaluate iface type by using the appropriate
			# function from lib file
			for lib in ${LIBDIR}/iftypes/* ${LIBDIR_OVERLAY}/iftypes/*
			do
				if [ -f $lib ]
				then
					if (. $lib; iface_type 2>&-)
					then
						if [ "$lastmatch" != "" ]
						then
							if [ "$(basename $lib)" = "$(basename $lastmatch)" ]
							then
								mprint -s "Warning: $lib overlays $lastmatch"
							else
								mprint -s "Error: iface type $lib overlays $lastmatch"
							fi
						fi
						lastmatch=$lib
					fi
				fi
			done
		fi

		if [ "$lastmatch" != "" ]
		then
			IFACE_TYPE=$(basename $lastmatch)
		else
			# if iface type isn't evaluated type "iface" is default
			IFACE_TYPE="iface"
		fi

		# update env
		echo "IFACE_TYPE=${IFACE_TYPE}" >> $ENVFILE
	fi

	. $ENVFILE
	t_mdent=$MDENT

	# expand $IFUPDOWN_ENV
	for var in $IFUPDOWN_ENV; do
		eval $var
	done

	[ $t_mdent -gt $MDENT ] && MDENT=$t_mdent
	main_pos_update
}

main_pos_update() {
	if [ "${MDENT:-0}" = "0" ]
	then
		STARTCOL=""
	else
		STARTCOL="[$(expr ${MDENT:-0} \* ${TAB})C"
	fi
	sed 's/MDENT=[0-9]*/'MDENT=$MDENT'/' -i $ENVFILE
}

main_preup() {
	config $RT_PREUP_PRINTING_OFF || mstart

	if ! config $RT_PREUP_IFUP_CHECK_OFF && is_up
	then
		if ! config $RT_PREUP_PRINTING_OFF
		then
			mup
			mstate 1
			minfo "Interface already up"
		fi
		exit 1
	fi

	if_preup 2>&-
	retval=$?

	if [ $retval != 0 ]
	then
		if [ $retval != 127 ]
		then
			fail_preup 2>&- || exit 1
		fi
	fi

	config $RT_PREUP_PRINTING_OFF || mup
}

main_up() {
	if ! config $RT_UP_IFUP_CHECK_OFF
	then
		is_up
		retval=$?

		if [ $retval != 0 ]
		then

			if ! config $RT_UP_PRINTING_OFF
			then
				mstate $retval
				[ "$METHOD" = "manual" ] && minfo "Method manual is set"
			fi
			exit 1
		else
			config $RT_UP_PRINTING_OFF || mstate $retval
		fi
	fi

	if_up 2>&-
	retval=$?

	if [ $retval != 0 ]
	then
		if [ $retval != 127 ]
		then
			fail_up 2>&- || exit 1
		fi
	fi

	if [ -d /tmp/ifupdown/pcode/${IFACE} ]
	then
		for dir in /tmp/ifupdown/pcode/${IFACE}/*
		do
			if [ -f ${dir}/postup ]
			then
				. ${dir}/postup
			fi
		done
	fi
}

main_down() {
	config $RT_DOWN_PRINTING_OFF || mstop

	if [ -d /tmp/ifupdown/pcode/${IFACE} ]
	then
		for dir in /tmp/ifupdown/pcode/${IFACE}/*
		do
			if [ -f ${dir}/predown ]
			then
				. ${dir}/predown
			fi
		done
	fi

	if_down 2>&-
	retval=$?

	if [ $retval != 0 ]
	then
		if [ $retval != 127 ]
		then
			fail_down 2>&- || exit 1
		fi
	fi

	config $RT_DOWN_PRINTING_OFF || mdown
}

main_postdown() {
	if ! config $RT_POSTDOWN_IFDOWN_CHECK_OFF
	then
		if [ $IFACE_STATE = "down" ]
		then
			if ! config $RT_POSTDOWN_PRINTING_OFF
			then
				mdown
				mstate 1
				minfo "Interface already down"
			fi
		else
			if is_up
			then
				if ! config $RT_POSTDOWN_PRINTING_OFF
				then
					mstate 1
					[ "$METHOD" = "manual" ] && minfo "Method manual is set"
				fi
				return 1
			else
				config $RT_POSTDOWN_PRINTING_OFF || mstate 0
			fi
		fi
	fi

	if_postdown 2>&-
	retval=$?

	if [ $retval != 0 ]
	then
		if [ $retval != 127 ]
		then
			fail_postdown 2>&- || exit 1
		fi
	fi

	# pcode cleanup
	rm -rf /tmp/ifupdown/pcode/*/$IFACE 2>&- 1>&-
}

main_exec_dirhooks() {
	for hook in ${LIBDIR_OVERLAY}/${1}/${2} ${LIBDIR}/${1}/${2}
	do
		if [ -f $hook ]
		then
			. $hook
			eval $3 2>&-
			retval=$?
			[ $retval != 0 -a $retval != 127 ] && exit 1
			return
		fi
	done
}

main_exec_plugins() {
	for plugin in ${LIBDIR_OVERLAY}/plugins/* ${LIBDIR}/plugins/*; do
		[ -f $plugin ] || continue
		. $plugin
		eval "${plugin##*/}_${1}" 2>&-
		retval=$?
		[ $retval != 0 -a $retval != 127 ] && exit 1
	done
}

main_exec_inlinehooks() {
	case $SUBMODE in
		if-pre-up)
			[ "${IF_PRE_UP:-""}" != "" ] && eval $IF_PRE_UP
		;;
		if-up)
			[ "${IF_UP:-""}" != "" ] && eval $IF_UP
		;;
		if-down)
			[ "${IF_DOWN:-""}" != "" ] && eval $IF_DOWN
		;;
		if-post-down)
			[ "${IF_POST_DOWN:-""}" != "" ] && eval $IF_POST_DOWN
		;;
	esac
}

### main entry point ###

CFG_PRINTING_OFF=0
CFG_SYSLOG_OFF=0
CFG_FANCY=0
CFG_DEBUG=0

if ! [ -f /etc/conf.d/ifupdown ]
then
	logger -t ifupdown "/etc/conf.d/ifupdown not found"
	exit 1
fi
. /etc/conf.d/ifupdown

config ${CFG_DEBUG:=""} && set -x

RT_PREUP_PRINTING_OFF=0
RT_UP_PRINTING_OFF=0
RT_DOWN_PRINTING_OFF=0
RT_POSTDOWN_PRINTING_OFF=0
RT_PREUP_IFUP_CHECK_OFF=0
RT_UP_IFUP_CHECK_OFF=0
RT_POSTDOWN_IFDOWN_CHECK_OFF=0
RT_BB_NOEXEC=0
readonly ENVFILE="/tmp/ifupdown/env"
readonly LIBDIR="/lib/network"
readonly LIBDIR_OVERLAY="/etc/network/lib"
IFACE_TYPE="none"
H_ERR=0

alias exit="__exit"
alias ifup="__ifupdown ifup"
alias ifdown="__ifupdown ifdown"
[ -z "${KSH_VERSION:-""}" ] || alias which='whence -p'

if ! [ -f ${LIBDIR}/sh/mfunctions.sh ]
then
	logger -t ifupdown "${LIBDIR}/sh/mfunctions.sh not found"
	exit 1
fi
. ${LIBDIR}/sh/mfunctions.sh

main_env_update

config $H_ERR && built_in exit 1

if [ "$IFACE_TYPE" != "iface" ]
then
	if [ -f "${LIBDIR_OVERLAY}/iftypes/${IFACE_TYPE}" ]
	then
		. ${LIBDIR_OVERLAY}/iftypes/${IFACE_TYPE}
	elif [ -f "${LIBDIR}/iftypes/${IFACE_TYPE}" ]
	then
		. ${LIBDIR}/iftypes/$IFACE_TYPE
	else
		mprint -s "Error: libfile $IFACE_TYPE not found"
	fi
fi

case $SUBMODE in
	if-pre-up)
		main_preup
		main_exec_dirhooks methods $METHOD method_preup
		main_exec_plugins preup

		# bypass execution of further busybox ifupdown.c code
		config $RT_BB_NOEXEC && built_in exit 1
	;;
	if-up)
		main_exec_plugins up
		main_exec_dirhooks methods $METHOD method_up
		main_up
	;;
	if-down)
		main_down
		main_exec_dirhooks methods $METHOD method_down
		main_exec_plugins down

		# bypass execution of further busybox ifupdown.c code
		config $RT_BB_NOEXEC && built_in exit 1
	;;
	if-post-down)
		main_exec_plugins postdown
		main_exec_dirhooks methods $METHOD method_postdown
		main_postdown
	;;
esac

exit 0

# vim:ts=4
