#!/bin/sh

#
# Find the Emulab control network interface.
#
# When called the first time (an invocation on any interface when
# /var/emulab/boot/controlif does not exist) we run DHCP and find the
# control network.
#
# On all invocations, we check the contents of that file against the
# interface we were called with and return "cnet" if we are the control
# net.  Otherwise we just return the physical interface name.
#

static_widearea_config()
{
	local iface=$1

	if [ -e /etc/emulab/isrem -a -e /etc/emulab/waconfig ]; then
		echo "Found Emulab widearea config info..."
		. /etc/emulab/waconfig
	else
		return 1
	fi

	# XXX check WA_MAC?
	if [ "$WA_BOOTMETHOD" != static ]; then
		return 1
	fi
	
	cnetif=`/sbin/ifconfig -a | \
		sed -n 's/^\([^ ]*\) *Link encap:Ethernet *HWaddr '$WA_MAC' *$/\1/ip'`

	if [ -z "$cnetif" ]; then
		echo "Could not find iface with MAC $WA_MAC; trying DHCP!"
		return 1
	elif [ -z "$WA_HOSTNAME"   -o -z "$WA_DOMAIN" -o \
	       -z "$WA_IP_ADDR"    -o -z "$WA_IP_NETMASK" -o \
	       -z "$WA_IP_GATEWAY" -o -z "$WA_IP_DNS1" ]; then
		echo "Missing static IP config vars; trying DHCP!"
		return 1
	fi

	# Make sure ifup passed us the correct interface.  If not,
	# exit successfully so that the DHCP client isn't run
	if [ "$cnetif" != "$iface" ]; then
		return 0
	fi

	# We're going ahead with the static config
	echo "Statically configuring control net on $cnetif ..."
	/sbin/ifconfig "$cnetif" inet "$WA_IP_ADDR" netmask "$WA_IP_NETMASK" up
	/sbin/route add default gateway "$WA_IP_GATEWAY"
		
        # setup resolv.conf
	echo "search $WA_DOMAIN" > /etc/resolv.conf
	echo "nameserver $WA_IP_DNS1" >> /etc/resolv.conf
	if [ -n "$WA_IP_DNS2" ]; then
		echo "nameserver $WA_IP_DNS2" >> /etc/resolv.conf
	fi

        # set hostname
	hosts_str="$WA_HOSTNAME"
	if echo "$WA_HOSTNAME" | grep -q \\.; then
		hostname "$WA_HOSTNAME"
	else
		hostname "${WA_HOSTNAME}.${WA_DOMAIN}"
		hosts_str="${WA_HOSTNAME}.${WA_DOMAIN} ${hosts_str}"
	fi

        # setup hosts file
	echo "$WA_IP_ADDR ${hosts_str}" >> /etc/hosts

        # setup a few necessary emulab files...
	echo "$cnetif" > $BOOTDIR/controlif
	if [ -e "/etc/emulab/bossnode" ]; then
		bossnode=`cat /etc/emulab/bossnode`
		i=0
		while [ $i -lt 6 ]; do
			bossip=`host -t A "$bossnode"`
			[ $? -eq 0 ] && break
			i=`expr $i + 1`
			sleep 5
		done
		
		echo `echo "$bossip" | sed -n -e 's/.*has address\s*\(.*\)/\1/p'` \
			> $BOOTDIR/bossip
	fi
	echo "$WA_HOSTNAME" > $BOOTDIR/realname
	echo "$WA_IP_GATEWAY" > $BOOTDIR/routerip
	echo "$WA_IP_ADDR" > $BOOTDIR/myip
	echo "$WA_IP_NETMASK" > $BOOTDIR/mynetmask

	return 0
}

export LANG=C
iface="$1"

cnetfile="/var/run/cnet"
cnetif=''

# We only need the lock if the file doesn't exist.
# XXX keep this lightweight as it can be called many times from
# ifquery in the udev path.
if [ -f $cnetfile ]; then
    cnetif=`cat $cnetfile`
    if [ "$cnetif" = $iface ]; then
	echo cnet
    else
	echo $iface
    fi
    exit 0
fi

lockdir=/var/lock/findcnet

. /etc/emulab/paths.sh

#
# NB: if we're being invoked by a udevd trigger, skip.  We want to be
# invoked by ifup.  This happens with systemd on Ubuntu 16 because a
# couple of the udevd rules (/lib/udev/bridge-network-interface and
# /lib/udev/vlan-network-interface call ifquery without the
# --no-mappings argument).
#
if [ -n "$SUBSYSTEM" ]; then
    echo "Skipping systemd udevd triggered action '$ACTION' subsystem '$SUBSYSTEM' device '$INTERFACE' ..." >>$LOGDIR/dhclient.log
    echo $iface
    exit 0
elif [ -n "$SYSTEMD_ALIAS" ]; then
    echo "Skipping systemd udevd triggered SYSTEMD_ALIAS '$SYSTEMD_ALIAS' ..." >>$LOGDIR/dhclient.log
    echo $iface
    exit 0
fi

while ! mkdir $lockdir 2> /dev/null; do
	sleep 1
done

echo "`date`: $iface: starting" >>$LOGDIR/dhclient.log

#
# We use /var/run to store the current idea of the cnet interface
# because we want it to go away on reboot.
#

if [ -f $cnetfile ]; then
    cnetif=`cat $cnetfile`
    echo "`date`: $iface: $cnetfile already had $cnetif" >>$LOGDIR/dhclient.log
elif static_widearea_config $iface; then
    #
    # Make sure that static_widearea_config matched $iface to the target WA_MAC.
    #
    if [ "$cnetif" = "$iface" ]; then
	echo $iface > $cnetfile
	cnetif=`cat $cnetfile`

        # Emit this upstart event to allow boot to continue, even
        # if we couldn't get a dhcp lease.
        # Otherwise, if this is systemd, we have a special job that
        # is waiting for the /var/run/cnet file to appear.
	if [ -f /sbin/initctl ]; then
	    /sbin/initctl emit -n 'emulab-findcnet-done'
	fi
    else
        #
        # If not, don't fail; there was widearea config info; but also don't
	# succeed in the final check at the end of the script.  This $iface is
	# not the control net.
        #
	cnetif=''
    fi
else
    #
    # Find a list of candidate interfaces and run dhclient on them all.
    #
    # Also wait until either the previous control net device is up, or
    # until at least one is up (if this is first boot of the image and
    # there is not yet a previous control net device), or a max amount
    # of time.
    #
    MAXUPWAITTIME=6
    LEASES="/var/lib/dhcp/dhclient.leases"
    prevcnetdevs=
    if [ -f $LEASES ]; then
	prevcnetdevs=`cat $LEASES | sed -n -r -e 's/^[ \t]*interface[ \t]*"([^"]*)".*$/\1/p' | uniq | xargs`
	echo "`date`: $iface: findcnet found '$prevcnetdevs' possible previous control net devices from old DHCP leases" >>$LOGDIR/dhclient.log
    fi
    _iflist=`ifconfig -a | grep -E '^(eth|en|sl)' | awk '{ print $1 }' | xargs`
    lpc=0
    while [ $lpc -lt $MAXUPWAITTIME ]; do
	_iflist=`ifconfig -a | grep -E '^(eth|en|sl)' | awk '{ print $1 }' | xargs`
	echo "`date`: $iface: findcnet checking $lpc for up devices: $_iflist" >>$LOGDIR/dhclient.log
	downifs=0
	upifs=0
	upifaces=
	foundprevcnet=0
	for _if in $_iflist ; do
	    ip link show "$_if" | grep -q LOWER_UP
	    if [ $? -eq 0 ] ; then
		upifs=`expr $upifs + 1`
		upifaces="$upifaces $_if"
		for _previf in $prevcnetdevs ; do
		    if [ "$_previf" = "$_if" ]; then
			echo "`date`: $iface: known cnet device $_if is up; stopping waiting" >>$LOGDIR/dhclient.log
			foundprevcnet=1
			break
		    fi
		done
		if [ $foundprevcnet -eq 1 ]; then
		    break
		fi
	    else
		#ifconfig $_if up
		downifs=`expr $downifs + 1`
	    fi
	done
	if [ $foundprevcnet -eq 1 ]; then
	    break
	fi
	if [ -z "$prevcnetdevs" -a $upifs -gt 0 ]; then
	    echo "`date`: $iface: at least one iface is up ($upifaces) (no previous cnet yet, must be first boot); stopping waiting" >>$LOGDIR/dhclient.log
	    break
	fi
	if [ $downifs -eq 0 ]; then
	    echo "`date`: $iface: all '$_iflist' are up; stopping waiting" >>$LOGDIR/dhclient.log
	    break
	fi
	
	lpc=`expr $lpc + 1`
	sleep 1
    done

    # Mark that we are running dhclient.
    touch /var/run/cnet-dhlient-running

    #
    # If dhclient returns success, then it has configured the first interface
    # and gone into background mode.  At that point we don't care about it any
    # more and just kill it.  We also shutdown all the other interfaces (which
    # dhclient will leave "up").
    #
    echo "`date`: $iface: findcnet running dhclient on: $_iflist"
    if [ -x /sbin/dhclient ] && /sbin/dhclient -q $_iflist ; then
        killall dhclient
	rm -f /var/run/dhclient.pid
        echo "`date`: $iface: findcnet dhclient returned"
	[ -f $BOOTDIR/controlif ] && cp $BOOTDIR/controlif $cnetfile
    	cnetif=`cat $cnetfile`
	for _if in $_iflist; do
	    [ $_if = $cnetif ] && continue
	    echo "`date`: taking $_if down"
	    ifconfig $_if down
	done
    fi

    # We are done with dhclient.
    rm -f /var/run/cnet-dhlient-running
    
    # Emit this upstart event to allow boot to continue, even
    # if we couldn't get a dhcp lease.
    # Otherwise, if this is systemd, we have a special job that
    # is waiting for the /var/run/cnet file to appear.
    if [ -f /sbin/initctl ]; then
	/sbin/initctl emit -n 'emulab-findcnet-done'
    fi
fi >>$LOGDIR/dhclient.log 2>&1

#
# In case ifup/ifdown try to feed us anything
#
while read foo bar; do
    echo "`date`: $iface: findcnet got \"$foo $bar\" from caller"
done >>$LOGDIR/dhclient.log 2>&1

if [ -z "$cnetif" ]; then
    echo "`date`: $iface: findcnet got empty $cnetfile" >>$LOGDIR/dhclient.log
fi

echo "`date`: $iface: cnet is $cnetif" >>$LOGDIR/dhclient.log

if [ "$cnetif" = $iface ]; then
    echo cnet
else
    echo $iface
fi

rm -rf $lockdir
exit 0
