#!/bin/sh
# A simple wrapper around udhcpc so that we are able to dynamically specify
# which options it should ask for.

# note: ifupdown does not allow VERBOSITY through. can be set in OPTS_FILE.
VERBOSITY="${VERBOSITY:-0}"

. ${CIRROS_SHLIB:=/lib/cirros/shlib} ||
	{ echo "failed to read ${CIRROS_SHLIB}" 1>&2; exit 1; }

set -f

# ensure PATH has /sbin in it.
path_has /sbin || PATH="$PATH:/sbin"

OPTS_FILE="/etc/default/udhcpc"
RESOLV_CONF="/etc/resolv.conf"

apply_static_routes() {
	# routes are pairs of network and gateway
	# 169.254.169.254/32 10.65.0.128
	local net="" router="" err=0
	while [ $# -ne 0 ]; do
		net="$1"
		router="$2"
		[ -n "$net" ] || continue
		debug 1 "adding net $net with router $router"
		route add -net "$net" gw "$router" || {
			error "WARN: failed: route add -net \"$net\" gw \"$router\""
			err=$(($err+1));
		}
		shift 2 || {
			error "apply_static_routes: failed shift 2. odd number of args?"
			return 1;
		}
	done
	return $err
}


readconfig() {
	[ ! -f "$OPTS_FILE" ] || . "$OPTS_FILE" ||
		{ error "failed to read $OPTS_FILE"; return 1; }
	# these are expected to be set.
	OPTIONS=${OPTIONS:-staticroutes mtu}
	TIMEOUT=${TIMEOUT:-60}
	MTU=${MTU:-1500}
}

up() {
	[ $# -ge 1 ] || { error "$0 up: must provide interface"; return 1; }
	local iface="$1" opts="" hostname=""

	readconfig || return
	hostname=$(hostname 2>/dev/null)

	# Gather all options and start udhcpc.
	for opt in $OPTIONS; do
		opts="-O $opt $opts"
	done

	debug 1 "Starting udhcpc on $iface, asking for options {$OPTIONS}"
	udhcpc -p "/var/run/udhcpc.${iface}.pid" -R -n \
		${TIMEOUT:+-T "${TIMEOUT}"} -i "$iface" -s "$0" \
		$opts ${hostname:+-x "hostname:${hostname}"}
}

down() {
	local iface="$1" pidfile="" pid=""
	[ $# -ge 1 ] || { error "$0 down: must provide interface"; return 1; }
	pidfile="/var/run/udhcpc.$iface.pid"
	[ -f "$pidfile" ] ||
		{ error "$iface: no pidfile"; return 1; }
	read pid < "$pidfile" ||
		{ error "failed to read pid from '$pidfile' for '$iface'"; return 1; }
	kill $pid >/dev/null
	return
}

renew_bound() {
	local flags="" mode="$1" i=""
	shift;
	[ -n "$interface" ] ||
		{ error "$0 $mode: 'interface' not provided"; return 1; }

	[ -n "$ip" ] ||
		{ error "$0 $mode: 'ip' not set in environment"; return 1; }

	readconfig || return

	[ -n "$broadcast" ] && flags="${flags}broadcast $broadcast "
	[ -n "$subnet" ] && flags="${flags}netmask $subnet "
	flags="${flags}mtu ${mtu:-${MTU}} "
	flags=${flags% }

	debug 1 "ifconfig $interface $ip $flags"
	ifconfig $interface $ip $flags || {
		error "$0 $mode: failed: 'ifconfig $interface $ip $flags'";
		return 1;
	}

	if [ -n "$router" ] ; then
		local out="" ret=""
		debug 2 "deleting routers"
		while :; do
			out=$(route del default gw 0.0.0.0 dev $interface 2>&1)
			ret=$?
			[ $ret -eq 0 ] && break
			case "$out" in
				*SIOCDELRT*[Nn]o\ such\ process*) break;;
			esac
			error "deleting routes failed: $out" 1>&2
		done

		for i in $router; do
			debug 1 "route add default gw $i dev $interface"
			route add default gw $i dev $interface
		done
	fi

	local msg="configuring $RESOLV_CONF for $interface. domain=$domain"
	msg="$msg nameservers: $dns"
	debug 1 "$msg"
	{
		[ -n "$domain" ] && echo "search $domain"
		for i in $dns ; do
			echo nameserver $i
		done
	} > "$RESOLV_CONF"

	apply_static_routes $staticroutes
}

case "$1" in
	up)
		shift
		up "$@"
		;;
	down)
		shift;
		down "$@"
		;;
	deconfig)
		ifconfig $interface 0.0.0.0
		;;
	renew|bound)
		renew_bound "$@";
		;;
	*)
		error "Usage: $0 <up|down>"
		exit 1
		;;
esac

exit
# vi: ts=4 noexpandtab syntax=sh
