#!/usr/bin/openrc-run

. /opt/etc/functions.openrc

CONNECT_WAIT_TIME=${CONNECT_WAIT_TIME-"10"}
CARRIER_WAIT_TIME=${CARRIER_WAIT_TIME-"3"}
DHCP_POLICY=${DHCP_POLICY-"probe"}
DHCP_RETRIES=${DHCP_RETRIES-"3"}

extra_commands="init clean"

depend()
{
	before localmount mounta
	provide net ${SVCNAME}
}

INTERFACE_LIST=""
NUM_CONNECTED=0
CONNECTED_IFACES=""

update_interface_list() {
	[ -n "${INTERFACE_LIST}" ] && return 0
	INTERFACE_LIST=`ip link | grep ^[1-9] | grep -v LOOPBACK | awk '{print $2}' | sed 's/://g'`
}

ip_devs_cmd() {
	for IFACE in ${INTERFACE_LIST}; do
		ip $1 dev ${IFACE}
	done
}

iface_clean_ips()
{
	local IFACE=$1
	local ADDR
	while [ 0 ]; do
		ADDR=`ip addr show dev ${IFACE} | grep 'inet ' `
		[ $? = 0 ] || break
		ADDR=`echo ${ADDR} | awk '{print $2;}'`
		einfo "${ME}: removing inet4 address ${ADDR} from ${IFACE}"
		ip addr del ${ADDR} dev ${IFACE}
	done
	while [ 0 ]; do
		ADDR=`ip addr show dev ${IFACE} | grep 'inet6 ' `
		[ $? = 0 ] || break
		ADDR=`echo ${ADDR} | awk '{print $2;}'`
		einfo "${ME}: removing inet6 address ${ADDR} from ${IFACE}"
		ip addr del ${ADDR} dev ${IFACE}
	done
}

clean_all_ips() {
	local IFACE
	einfo "${ME}: removing all addresses from all interfaces..."
	for IFACE in ${INTERFACE_LIST}; do
		iface_clean_ips ${IFACE}
	done
}

# FIXME: sure_kill()
clean()
{
	local _timeout=5
	update_interface_list
	update_me
	if pgrep dhcpcd > /dev/null 2>&1; then
		einfo "${ME}: killing dhcpcd ..."
		killall dhcpcd
	fi
	while pgrep dhcpcd > /dev/null 2>&1; do
		einfo "${ME}: still killing dhcpcd ..."
		killall dhcpcd
		if [ ${_timeout} -eq 0 ]; then
			einfo "${ME}: Sending SIGKILL to dhcpcd ..."
			killall -s KILL dhcpcd
			break
		fi
		sleep 1
		_timeout=$((${_timeout}-1))
		update_me
	done
	clean_all_ips
	einfo "${ME}: setting all interfaces DOWN..."
	ip_devs_cmd "link set down"
	NUM_CONNECTED=0
	CONNECTED_IFACES=""
}

wait_for_carrier()
{
	local IFACE
	NUM_CONNECTED=0
	CONNECTED_IFACES=""
	einfo "${ME}: wfc(): setting all eth ifs UP..."
	ip_devs_cmd "link set up"
	local _count=${CARRIER_WAIT_TIME}
	while [ ${_count} -gt 0 ]; do
		sleep 1
		for IFACE in ${INTERFACE_LIST}; do
			# If 'NO-CARRIER' is present in iface's status, continue
			ip link show dev ${IFACE} | head -n 1 | grep 'NO-CARRIER' \
				> /dev/null 2>&1 && continue
			# If this iface is already marked as connected one, continue
			echo ${CONNECTED_IFACES} | grep ${IFACE} \
				> /dev/null 2>&1 && continue
			# Mark interface as connected
			einfo "${ME}: wfc(): carrier detected on ${IFACE}"
			CONNECTED_IFACES="${CONNECTED_IFACES} ${IFACE}"
			NUM_CONNECTED=$((${NUM_CONNECTED}+1))
		done
		_count=$((${_count}-1))
	done
}

#
# ask_dhcp_server(polycy[,iface])
# Issues dhcp request for one or several interfaces.
# Relies on global variables NUM_CONNECTED and CONNECTED_IFACES
# Valid policies:
#	'exact' - issue dhcp request only for specified iface
#	'single' - issue request if only one iface is connected
#	'probe' - issue request on the first connected iface
#	'all' - issue requests on all connected ifaces
#

ask_dhcp_server()
{
	if [ ${NUM_CONNECTED} -eq 0 ]; then
		eerror "${ME}: all interfaces are disconnected."
		return 1
	fi
	case "$1" in
		"exact")
			if [ -z "$2" ]; then
				eerror "${ME}: policy=$1: interface name is not specified."
				return 1
			fi
			if !(echo ${CONNECTED_IFACES} | grep $2 2>/dev/null); then
				eerror "${ME}: policy=$1: requested interface $2 is disconnected."
				return 1
			fi
			dhcpcd ${DHCPCD_ARGS} $2
			return $?
			;;
		"probe")
			IFACE=`echo ${CONNECTED_IFACES} | awk '{print $1;}'`
			if [ ${NUM_CONNECTED} -gt 1 ]; then
				ewarn "${ME}: policy=$1: multiple interfaces are connected, using ${IFACE}."
				ewarn "${ME}: policy=$1: consider changing conf.d/${SVCNAME}::DHCP_POLICY to \"exact <if>\"."
			fi
			dhcpcd ${DHCPCD_ARGS} ${IFACE}
			return $?
			;;
		"single")
			if [ ${NUM_CONNECTED} -gt 1 ]; then
				eerror "${ME}: policy=$1: multiple interfaces are connected, change conf.d/${SVCNAME}::DHCP_POLICY to \"exact <if>\"."
				return 1
			fi
			dhcpcd ${DHCPCD_ARGS} ${CONNECTED_IFACES}
			return $?
			;;
		"all")
			local _rv=0
			for IFACE in ${CONNECTED_IFACES}; do
				dhcpcd ${DHCPCD_ARGS} ${IFACE} || _rv=1
			done
			return ${_rv}
			;;
		*)
			eerror "${ME}: unknown policy \"$1\""
			return 1
			;;
	esac
}

init()
{
	local _rv=1
	local _count=${CONNECT_WAIT_TIME}
	update_interface_list
	wait_for_carrier
	while [ ${_count} -gt 0 ]; do
		if [ ${NUM_CONNECTED} -gt 0 ]; then
			_rv=0
			break
		fi
		sleep 1
		wait_for_carrier
		_count=$((${_count}-1))
	done
	echo "DEBUG: NUM_CONN=${NUM_CONNECTED}, CONN_IFS: \"${CONNECTED_IFACES}\", RV=${_rv}"
	if [ ${_rv} -eq 0 ]; then
		_count=${DHCP_RETRIES}
		while true; do
			if ask_dhcp_server ${DHCP_POLICY} ${POLICY_ARGS}; then
				_rv=0
				break
			else
				_rv=1
				[ ${_count} -eq 0 ] && break
				_count=$((${_count}-1))
				# FIXME
				sleep 4
			fi
		done
	fi
	return ${_rv}
}

start()
{
	local _rv
	ebegin "Starting ${SVCNAME}..."
	eindent
	init
	_rv=$?
	eoutdent
	eend ${_rv}
	return ${_rv}
}	

restart() {
	clean
	init
}

reload() {
	restart
}

just_reset() {
	if pgrep dhcpcd > /dev/null 2>&1; then
		update_me
		einfo "${ME}: killing dhcpcd ..."
		killall dhcpcd
	fi
}

stop() {
	ebegin "Resetting service ${SVCNAME} to stopped state..."
	just_reset
	eend 0
	return 0
}
