#! /bin/bash
#
PATH="/sbin:/bin"

declare -i MAXLOOPS=40          #
declare -i LOOPDELAY=500000     # useconds
declare -i MAXIFNUM=1024        #
RULE_FILE=/etc/udev/rules.d/30-net_persistent_names.rules
USED_IFACE_FILE_STUB=/tmp/used_interface_names
USED_IFACE_FILE="`mktemp $USED_IFACE_FILE_STUB.XXXXXXXX`"

STAMPFILE=/tmp/dummy_stamp
trap 'rm -f $USED_IFACE_FILE
      echo renamed > $STAMPFILE
      echo INTERFACE=$INTERFACE
      echo DEVPATH=${DEVPATH%/*}/$INTERFACE
      echo RENAMED=yes' EXIT

. /etc/sysconfig/network/config
. /etc/sysconfig/network/scripts/functions

if [ -r /sys/class/net/$1/ifindex ] ; then
	STAMPFILE=$STAMPFILE_STUB`cat /sys/class/net/$1/ifindex`
else
	STAMPFILE=${STAMPFILE_STUB}no_iface
fi
echo virgin > $STAMPFILE

if [ "$FORCE_PERSISTENT_NAMES" != yes ] ; then
	info_mesg "No persistent names wanted"
	exit 0
fi

#exec 2>>/tmp/rename_netiface.$1-$2.$SEQNUM.log
#set -x

error_exit() {
	local RET
	declare -i RET=$1
	if [ $RET -ne "$1" ] ; then
		RET=255
	else
		shift
	fi
	err_mesg "$@"
	exit $RET
}

usage() {
	mesg "usage: $0 <oldname> [<newname>]" >&2
	error_exit $*
}

check_if_name_is_free() {
	grep -qs "^[^#].*rename_netiface %k $NEWNAME\"" $RULE_FILE && return 1
	grep -vs "$MAC_ADDRESS" $USED_IFACE_FILE_STUB* \
	    | grep -q "^$NEWNAME" && return 1
	echo $NEWNAME $MAC_ADDRESS >> $USED_IFACE_FILE
	usleep $((RANDOM%600+700))000
	grep -vs "$MAC_ADDRESS" $USED_IFACE_FILE_STUB* \
            | grep -q "^$NEWNAME" && return 1
	return 0
}

# Return 0 only if rule was successfully written (if needed of course)
write_rule() {
	test "$WRITE_RULE" == yes || return 0
	check_if_name_is_free || return 1
	if [ "$NOMAC_HACK_APPLIED" == yes ] ; then
		echo "SUBSYSTEM==\"net\", ACTION==\"add\"," \
		     "ENV{ADDRESS}==\"$MAC_ADDRESS\"," \
		     "IMPORT=\"/sbin/rename_netiface %k $NEWNAME\"" \
		     >> $RULE_FILE
	else
		echo "SUBSYSTEM==\"net\", ACTION==\"add\"," \
		     "SYSFS{address}==\"$MAC_ADDRESS\"," \
		     "IMPORT=\"/sbin/rename_netiface %k $NEWNAME\"" \
		     >> $RULE_FILE
	fi
	/sbin/udevcontrol reload_rules || return 1
	info_mesg "New rule for $MAC_ADDRESS added."
}

test $# -gt 2 && usage 1 "too many arguments: $*"
OLDNAME=$1
NEWNAME=$2
test -z "$OLDNAME" && usage 2 "too little arguments"
ls /sys/class/net/$OLDNAME 2>/dev/null 1>&2 \
    || error_exit 3 "oldname $OLDNAME does not exist"
NAMEBASE=${OLDNAME%%[0-9]*}
case "$NAMEBASE" in
	eth|ath|wlan|ra)
		: go on
		;;
	*)
		exit 0
		;;
esac
declare -i IFNUM=${OLDNAME##$NAMEBASE}


if [ -z "$NEWNAME" ] ; then
	# This routine looks for a network interface name that is still not
	# used as persistent name. At first it tries the name the interface
	# currently has. If this name is already occupied, then increase the
	# number and try again.
	# To check if a name is occupied we have to look in
	# /etc/udev/rules.d/60-net_*.rules and in /tmp/used_interface_names*.
	# The latter serves as temporary registration file to avoid race
	# conditions. It will be removed when the script exits.

	NEWNAME=$OLDNAME
	if ls /sys/class/net/$OLDNAME 2>/dev/null 1>&2; then
		MAC_ADDRESS=`cat /sys/class/net/$OLDNAME/address`
	else
		MAC_ADDRESS=$2
	fi
	if [ -z "$MAC_ADDRESS" ] ; then
		error_exit 4 "no mac address for $OLDNAME"
	fi
	if [ "${MAC_ADDRESS//0/}" == ":::::" ] ; then
		# Workaround for some drivers which don't request their
		# firmware before beeing set up and don't have a mac address
		# before firmware was loaded.
		ip link set up dev $OLDNAME
		ip link set down dev $OLDNAME
		# Do we have to wait some time?
		for i in 0 1 2 3 4 5 6 7 8 9; do
			MAC_ADDRESS=`cat /sys/class/net/$OLDNAME/address`
			info_mesg "waiting for a usefull mac address: $MAC_ADDRESS"
			test "${MAC_ADDRESS//0/}" != ":::::" && break
			sleep 1
		done
	fi
	if [ "${MAC_ADDRESS//0/}" == ":::::" ] ; then
		error_exit 5 "all zero mac address for $OLDNAME"
	fi
	
	while ! check_if_name_is_free; do
		while [ $IFNUM -lt $MAXIFNUM ] ; do
			: $((IFNUM++))
			NEWNAME=$NAMEBASE$IFNUM
			ls /sys/class/net/$NEWNAME 2>/dev/null 1>&2 && continue
			continue 2
		done
		error_exit 6 "could not get a free persistent interfacename" \
		         "for $OLDNAME ($MAC_ADDRESS)"
	done
	WRITE_RULE=yes
	info_mesg "$OLDNAME: new persistent name for $MAC_ADDRESS is $NEWNAME"
fi
if [ -z "$NEWNAME" ] ; then
	error_exit 7 "cannot get a valid new interface name"
fi

# Simply try to rename directly, because it will work in most cases
if nameif -r "$NEWNAME" "$OLDNAME" 2>/dev/null 1>&2; then
	write_rule || error_exit 8 "Name $NEWNAME for $MAC_ADDRESS is NOT" \
	                       "persistent"
	info_mesg "$OLDNAME -> $NEWNAME: immediate success"
	INTERFACE=$NEWNAME
	exit 0
fi

# Generate a temporary interface name
TMPNAME="${NAMEBASE}xx${IFNUM}"
ls /sys/class/net/$TMPNAME 2>/dev/null 1>&2 \
    && error_exit 10 "temporary interface name $TMPNAME does exist"

# Rename it to the temporary name.
# Then try several times to rename it to new name
nameif -r "$TMPNAME" "$OLDNAME" 2>/dev/null 1>&2 \
    || error_exit 11 "cannot rename interface to temporary name $TMPNAME"
echo looping > $STAMPFILE
declare -i LOOPS=0
while [ $MAXLOOPS -gt $LOOPS ] ; do
	: $((LOOPS++))
	if nameif -r "$NEWNAME" "$TMPNAME" 2>/dev/null 1>&2; then
		write_rule || error_exit 12 "Name $NEWNAME for $MAC_ADDRESS is" \
		                       "NOT persistent"
		info_mesg "$OLDNAME -> $NEWNAME: success after $LOOPS loops"
		INTERFACE=$NEWNAME
		exit 0
	fi
	usleep $LOOPDELAY
done

if nameif -r "$OLDNAME" "$TMPNAME" 2>/dev/null 1>&2; then
	info_mesg "$OLDNAME -> $NEWNAME: NO success after $LOOPS loops." \
	          "Interface again named $OLDNAME"
else
	info_mesg "$OLDNAME -> $NEWNAME: NO success after $LOOPS loops." \
	          "Interface now named $TMPNAME, because $OLDNAME was no" \
	          "longer available"
	INTERFACE=$TMPNAME
fi
exit 0






