#!/bin/sh
# Check the link status of an ethernet interface
# This script should be installed in /etc/network/if-pre-up.d/
#
# You can use this script to solve bug #120382
# ('ifup should (optionally) check for link before configuring the interface.')
# if you configure ABORT_NO_LINK to 'yes' in /etc/default/network-test
# since this will make the script abort if the interface does not have
# any link.
#
# Note that if you set ABORT_NO_LINK to 'yes' and the Ethernet interface
# does not have a link, the script will abort and ifupdown will *not*
# mark the interface as configured.
#
# It can also be used as a standalone script by setting up
# its environment:
#    IFACE=eth0  check-network-cable
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# You can also find a copy of the GNU General Public License at
# http://www.gnu.org/licenses/licenses.html#TOCLGPL
#

# Read system default file
rc=/etc/default/network-test
[ ! -r $rc ] || . $rc

# Defaults
ETHTOOL=/sbin/ethtool

alt_et=/usr/sbin/ethtool
[ -x $ETHTOOL ] || [ ! -x $alt_et ] || ETHTOOL=$alt_et
MIITOOL=/sbin/mii-tool
DO_SYSLOG=${DO_SYSLOG:-yes}
ABORT_NO_LINK=i${ABORT_NO_LINK:-no}
VERBOSITY=${VERBOSITY:-0}

if [ "$DO_SYSLOG" = yes ]; then
	OUTPUT="logger -i -p daemon.err -s"
else
	OUTPUT=echo
fi

# Set our locale environment, just in case any of the tools get translated
LC_ALL=C
export LC_ALL

check_status_miitool() {
	local status=0

	if $MIITOOL "$IFACE" 2>&1 | grep -q "no link"; then
		status=1
	fi
	return $status
}

check_status_ethtool() {
	local status=0
	local LINK=$($ETHTOOL "$IFACE" 2>&1 | grep "Link detected" || :)

	# If ethtool fails to print out the link line we break off
	# notice that ethtool cannot get the link status out of all
	# possible network interfaces
	[ "$LINK" ] || return 1
	if ! echo $LINK | grep -q "Link detected: yes"; then
		status=1
	fi
	return $status
}

check_status_iplink() {
	local status=0
        local info=""

	[ -x /sbin/ip ] || return 0
	info=$(/sbin/ip link show "$IFACE" 2>&1 | grep "$IFACE:")
	if echo $info | grep -q NO-CARRIER ||
	   echo $info | grep -q "state DOWN" ||
	   echo $info | grep -q "state LOWERLAYERDOWN"; then
		status=1
	fi
	return $status
}

check_status() {
	local status=0 myid=$(id -u)

	ifconfig "$IFACE" 2>/dev/null 1>&2 || {
		$OUTPUT "ERROR: Interface $IFACE does not seem to be present" \
			"in the system"
		# FIXME: would that be return status 0 or 1?
		return
	}
	# Use ethtool if installed (preferable to mii-tool)
	# If none are installed (or not running as root) we will test using
	# 'ip link show'
	if [ -x $ETHTOOL ] && [ $myid -eq 0 ]; then
		check_status_ethtool || status=$?
	elif [ -x $MIITOOL ] && [ $myid -eq 0 ]; then
		check_status_miitool || status=$?
	else
		check_status_iplink || status=$?
	fi
	[ $status -eq 0 ] ||
		$OUTPUT "WARNING: Initialising interface $IFACE which does" \
			"not have link"
	return $status
}

check_bond_status() {
	local status=1 slaves slave_iface

	slaves="/sys/class/net/$IFACE/bonding/slaves"

	[ -e $slaves ] || return 0
	while read slave_iface; do
		# Use ":" command to silence slaves.
		OUTPUT=: IFACE=$slave_iface check_status || status=$?
		# One functional slave will suffice
		[ $status -ne 0 ] || return 0
	done <$slaves
	$OUTPUT "WARNING: Initialising bond $IFACE which does not have link" \
		"on any slave"
	return $status
}

[ "$IFACE" ] || {
    $OUTPUT "ERROR: Variable IFACE not set in environment"
    exit 1
}

# Check our IFACE name, if it does not start with eth, bail out
case $IFACE in
	eth*)
		check_status || [ "$ABORT_NO_LINK" != yes ] || exit 1
		;;
	bond*)
		check_bond_status || [ "$ABORT_NO_LINK" != yes ] || exit 1
		;;
esac
