#!/bin/sh
# Written by Jarett Stevens
# iftool
# script to set ethernet interface speed/duplex in Solaris (see usage below)
# now available in the 80-column version!
# script blatantly does not support speeds other than 10/100/1000...
#
# 2008/10/16 - Initial revision

# for the impatient
trap cleanup 1 2 15

# Some quick defaults and definitions
XPGEGREP=/usr/xpg4/bin/egrep         #because the xpg4 egrep can be -quiet
AUTONEG=unchanged
DUPLEX=none
SPEED=none
TEMP=`mktemp`

exit_usage() {
	cat <<- EOF >&2

	iftool 
	iftool <interface> -a on
	iftool <interface> -s 10|100|1000 -d half|full [-a on|off]

	  -a   Autonegotiate
	  -s   Speed
	  -d   Duplex

	Setting "-a on" only will enable all speed/duplex combinations 
	for link autonegotiation.  Speed and duplex must be set together.
	Script must be executed as root.

	EOF

	cleanup
	exit 1
}

exitmsg() {
	printf "${1}\n" >&2
	cleanup
	exit 2
}

cleanup() {
	rm -f ${TEMP}
}

# we have to be root
test "`/usr/xpg4/bin/id -u`" = "0" || exitmsg "script must be executed as root"

# having no arguments, display the interface info and skip the rest
# (sorry, Solaris 10+ only)
if test ${#} -eq 0 
	then
	test `uname -r` -ge "5.10" && /sbin/dladm show-dev
	cleanup 
	exit 0
fi

# Set the interface
INTERFACE="${1}"

shift

# just to make sure the first argument wasnt an option..
echo "${INTERFACE}" | ${XPGEGREP} -q -e '[^a-zA-Z0-9:]' && exit_usage

while getopts a:d:s: THIS_OPT
	do
	case ${THIS_OPT} in
		a)	AUTONEG="${OPTARG}" ;;
		d)	DUPLEX="${OPTARG}" ;;
		s)	SPEED="${OPTARG}" ;;
		*)	exit_usage ;;
	esac
done

# $INTERFACE:X fixed because we are nice enough to accept that
INTERFACE=`echo "${INTERFACE}" | sed -e 's/:.*$//'`

# sanitize... 
AUTONEG=`echo "${AUTONEG}" | tr '[A-Z]' '[a-z]'`
DUPLEX=`echo "${DUPLEX}" | tr '[A-Z]' '[a-z]'`

# more input validation
test "${AUTONEG}" = "on" -o "${AUTONEG}" = "off" -o\
	"${AUTONEG}" = "unchanged" || exit_usage

test "${DUPLEX}" = "full" -o "${DUPLEX}" = "half" -o\
	"${DUPLEX}" = "none" || exit_usage

test "${SPEED}" = "10" -o "${SPEED}" -eq "100" -o\
	"${SPEED}" -eq "1000" -o "${SPEED}" = "none" || exit_usage

# you must specify speed and duplex if you specify either
test "${SPEED}" != "none" -a "${DUPLEX}" = "none" && exit_usage
test "${DUPLEX}" != "none" -a "${SPEED}" = "none" && exit_usage

# the interface name should begin with a letter and end with a number
echo "${INTERFACE}" | ${XPGEGREP} -q -e '^[a-z].*[0-9]$' || \
	exitmsg "Invalid interface: ${INTERFACE}"

# see if the interface has a "clone" /dev entry or individual
if test -L /dev/${INTERFACE}
	then
	INSTANCE=none
	DEVICE="/dev/${INTERFACE}"
else
	INSTANCE=`echo "${INTERFACE}" | sed -e 's/^[a-z].*[a-z]//'`
	DEVICE="/dev/`echo \"${INTERFACE}\" | sed -e 's/[0-9]*$//'`"

	#failing the above, verify that THIS device actually exists..
	test -L ${DEVICE} || exitmsg "Invalid interface: ${INTERFACE}"
fi

# final verification that the interface/device exists (driver attached)
${XPGEGREP} -q -e `echo "${INTERFACE}" | \
	sed -e 's/[0-9]*$//'` /etc/path_to_inst || \
	exitmsg "Invalid interface: ${INTERFACE}"

# a quick translation for later
DUPLEX=`echo "${DUPLEX}" | sed -e 's/half/hdx/' -e 's/full/fdx/'`

# set the device instance if necessary
test "${INSTANCE}" = "none" || \
	/usr/sbin/ndd -set ${DEVICE} instance ${INSTANCE}

# get the interface writable speed/duplex parameters and put them in $TEMP
/usr/sbin/ndd -get ${DEVICE} \? | \
	${XPGEGREP} -e '^adv_[10]+[A-Za-z0-9]+_cap.*write' | \
	cut -d" " -f1 > ${TEMP}

# special case 1
# just turn on autoneg ("-a on" alone) - so we will also enable 
# every speed/duplex
if test "${AUTONEG}" = "on" -a "${SPEED}" = "none" -a "${DUPLEX}" = "none"
	then
	while read ADV_CAP
		do
		/usr/sbin/ndd -set ${DEVICE} ${ADV_CAP} 1
	done < ${TEMP}
else
	# does this interface support the speed we were given
	${XPGEGREP} -q -e "^adv_${SPEED}[hf]dx_cap" ${TEMP} || \
		exitmsg "Invalid speed \"${SPEED}\" for ${INTERFACE}"

	while read ADV_CAP
		do
		echo "${ADV_CAP}" | \
			${XPGEGREP} -q -e "^adv_${SPEED}${DUPLEX}_cap" && \
			/usr/sbin/ndd -set ${DEVICE} ${ADV_CAP} 1 || \
			/usr/sbin/ndd -set ${DEVICE} ${ADV_CAP} 0
	done < ${TEMP}
fi

# we don't actually bother the autoneg setting unless it is set explicitly
test "${AUTONEG}" = "on" && /usr/sbin/ndd -set ${DEVICE} adv_autoneg_cap 1
test "${AUTONEG}" = "off" && /usr/sbin/ndd -set ${DEVICE} adv_autoneg_cap 0

# cleanup
cleanup

# explicit
exit 0
