#! /bin/bash

usage() {
	echo -e $* >&2
	echo >&2
	echo "usage ${0##*/} {netconfig|id|*-id|bus|*-bus} <arg> [<arg>]" >&2
	echo "	netconfig         <directory>:"
	echo "		converts all ifcfg-, ifroute- and ifservices- in this directory"
	echo ""
	echo "	id                <mac address>:"
	echo "	<device type>-id  <mac address>:"
	echo "		returns the interface that matches this mac address"
	echo ""
	echo "	bus               <busname> <device id>:"
	echo "	<device type>-bus <busname> <device id>:"
	echo "		returns the interface that matches this busname and device id"
	exit 1
}

get_iface_from_lladdr() {
	local DEVTYPE=$1 ; shift # we don't use DEVTYPE at the moment
	local LLADDR=$1  ; shift
	local SYSPATH IFACES IFACE N

	test -z "$LLADDR" && return 1

	# 1) Look in new pers.name udev rule file
	IFACES=`sed -n "s/^[^#]\+.*$LLADDR.*NAME=\"\([^\"]*\)\".*$/\1/p" \
		/etc/udev/rules.d/70-persistent-net.rules 2>/dev/null`
	N=0
	for IFACE in $IFACES; do
		: $((N++))
		info_mesg "get_iface_from_lladdr $DEVTYPE $LLADDR 70-per: $IFACE"
	done
	if [ "$N" == 1 ] ; then
		echo $IFACE
		return 0
	fi
	
	# 2a) Check sysfs if there currently is a matching interface.
	# TODO: In this case we should check if the right udev rule exists
	# TODO: and trigger (somehow) a creation of a rule when not?
	N=0
	for SYSPATH in `grep -l "$LLADDR" /sys/class/net/*/address`; do
		# skip wlan helper interfaces
		test "`cat ${SYSPATH/address/type}`" == 801 && continue
		test -L ${SYSPATH/address/device} || continue
		IFACE=${SYSPATH#/sys/class/net/}
		IFACE=${IFACE%/address}
		: $((N++))
		info_mesg "get_iface_from_lladdr $DEVTYPE $LLADDR sysfs: $IFACE"
	done
	if [ "$N" == 1 ] ; then
		echo $IFACE
		return 0
	fi

	# 2b) Check sysfs if there currently is a matching interface.
	#     this is for ifcfg-iucv-id-ROUTER01 on s390...
	N=0
	for SYSPATH in /sys/class/net/*/device; do
		# skip wlan helper interfaces
		test "`cat ${SYSPATH/device/type}`" == 801 && continue
		cd -P $SYSPATH 2>/dev/null || continue
		if test -n "$DEVTYPE" ; then
			cd -P subsystem 2>/dev/null && \
			test "${PWD##*/}" = "$DEVTYPE" || continue
			cd -P $SYSPATH 2>/dev/null || continue
		fi
		test -f user -a "`cat user`" = "$LLADDR" || continue
		IFACE=${SYSPATH#/sys/class/net/}
		IFACE=${IFACE%/device}
		: $((N++))
		info_mesg "get_iface_from_lladdr $DEVTYPE $LLADDR sysfs: $IFACE"
	done
	if [ "$N" == 1 ] ; then
		echo $IFACE
		return 0
	fi
	
	# 3) Look in old pers.name udev rule file
	# This should not happen, because old rule files should already
	# have been converted.
	IFACES=`sed -n "/$LLADDR/s/^.*netiface *\%k *\([^\"]*\)\".*$/\1/p" \
	        /etc/udev/rules.d/30-net_persistent_names.rules 2>/dev/null`
	N=0
	for IFACE in $IFACES; do
		: $((N++))
		info_mesg "get_iface_from_lladdr $DEVTYPE $LLADDR 30-net: $IFACE"
	done
	if [ "$N" == 1 ] ; then
		echo $IFACE
		return 0
	fi

	# 4) Look for PERSISTENT_NAME
	# Ideally we should add a udev rule for this name
	IFACE=`sed -n \
	    "/^[[:space:]]*PERSISTENT_NAME/s/^.*=\([^[:space:]]*\).*$/\1/p" \
	    "/etc/sysconfig/network/ifcfg-*id-$LLADDR" 2>/dev/null`
	info_mesg "get_iface_from_lladdr $DEVTYPE $LLADDR ifcfg-: $IFACE"
	if [ -n "$IFACE" ] ; then
		echo $IFACE
		return 0
	fi
	return 1
}

get_iface_from_businfo()
{
	local DEVTYPE=$1 ; shift
	local BUSNAME=$1 ; shift
	local DEVID=$1   ; shift
	local SYSPATH N
	local IFACES IFACE IF

	test -z "$BUSNAME" -o -z "$DEVID" && return 1

	if test -n "$DEVTYPE" -a -n "$BUSNAME" ; then
		MATCH="\($DEVTYPE\|$BUSNAME\)"
	elif test -n "$BUSNAME" ; then
		MATCH="$BUSNAME"
	fi

	# 1) Look in new pers.name udev rule file
	IFACES=`sed -n \
		"/^[^#]\+.*==\"$MATCH/{/==\"$DEVID\"/s/.*NAME=\"\([^\"]*\)\".*$/\1/p}" \
		/etc/udev/rules.d/70-persistent-net.rules 2>/dev/null`
	N=0
	for IFACE in $IFACES; do
		: $((N++))
		info_mesg "get_iface_from_businfo $DEVTYPE $BUSNAME $DEVID 70-per: $IFACE"
	done
	if [ "$N" == 1 ] ; then
		echo $IFACE
		return 0
	fi

	# 2) Check sysfs if there currently is a matching interface.
	N=0
	for SYSPATH in /sys/class/net/*/device; do
		# skip wlan helper interfaces
		test "`cat ${SYSPATH/device/type}`" == 801 && continue
		cd -P $SYSPATH 2>/dev/null || continue
		test "${PWD##*/}" != "$DEVID" && continue
		cd -P subsystem 2>/dev/null || continue
		if test "${PWD##*/}" != "${BUSNAME}" ; then
		  # ccw devices are in ccwgroup, not in ccw (s390*)
		  test "${PWD##*/}" != "${BUSNAME}group" && continue
		  if test -n "$DEVTYPE" ; then
			cd -P ${SYSPATH}/driver 2>/dev/null || continue
			# ctc iface driver type is ctcm, but qeth
			# and hsi iface driver seems to be qeth...
			test "${PWD##*/}" != "${DEVTYPE}" -a \
			     "${PWD##*/}" != "${DEVTYPE}m" && continue
		  fi
		fi
		IFACE=${SYSPATH#/sys/class/net/}
		IFACE=${IFACE%/device}
		: $((N++))
		info_mesg "get_iface_from_businfo $DEVTYPE $BUSNAME $DEVID sysfs: $IFACE"
	done
	if [ "$N" == 1 ] ; then
		echo $IFACE
		return 0
	fi

	# 3) Look in old pers.name udev rule file
	# This should not happen, because old rule files should already
	# have been converted.
	# --> removed

	# 4) Look for PERSISTENT_NAME
	IFACE=`sed -n \
	    "/^[[:space:]]*PERSISTENT_NAME/s/^.*=\([^[:space:]]*\).*$/\1/p" \
	    "/etc/sysconfig/network/ifcfg-*bus-$BUSNAME-$DEVID" 2>/dev/null`
	info_mesg "get_iface_from_businfo $DEVTYPE $BUSNAME $DEVID ifcfg-: $IFACE"
	if [ -n "$IFACE" ] ; then
		echo $IFACE
		return 0
	fi
}

is_interface() {
	local H=$1
	test -z "$H" && return 1
	test -d /sys/class/net/$H && return 0
	case $H in
		*-*) return 2;;
		eth[0-9]*) return 0;; # add more known names or remove this?
	esac
	test -f /etc/sysconfig/network/ifcfg-$H && return 0
	grep -qs "^PERSISTENT_NAME=$H" /etc/sysconfig/network/ifcfg-* && return 0
	grep -qs "NAME=\"$H\"" /etc/udev/rules.d/70-persistent-net.rules && return 0
	grep -qs "v/rename_netiface *%k *eth0" \
	     /etc/udev/rules.d/30-net_persistent_names.rules && return 0
	return 1
}

get_iface_from_hwdesc() {
	local H=$1
	test -z "$H" && return 1
	if is_interface $H; then
		echo $H
		return 0
	fi
	case $H in
		*-id-*)   get_iface_from_lladdr  "${H%-id*}"  "${H#*id-}" ;;
		*id-*)    get_iface_from_lladdr  ""           "${H#*id-}" ;;
		*-bus-*)  get_iface_from_businfo "${H%-bus*}" `(IFS=-; echo ${H#*bus-})` ;;
		*bus-*)   get_iface_from_businfo ""           `(IFS=-; echo ${H#*bus-})` ;;
		*interface-*) echo ${H#*interface-} ;;
		*devpath-*|*vpid-*|*type-*) ;;
		*) echo $H ;;
	esac
}

convert_netconfig() {
	cd ${1:-Xx-HAMMERNID-xX} || usage "Cannot change into directory '$1'"
	RCF=revert_conversion_from_`date +"%y%m%d.%H%M"`
	for F in ifcfg-* ifroute-* ifservices-*; do
		CFGTYPE=${F%%-*}
		CFGNAME=${F#*-}
		DEVTYPE=
		IFACE=
		info_mesg "convert_all_files: $F"
		case $CFGNAME in
			id-*)	IFACE=$(get_iface_from_lladdr ""         ${F#*id-}) ;;
			*-id-*) DEVTYPE=${CFGNAME%%-id-*}
				IFACE=$(get_iface_from_lladdr "$DEVTYPE" ${F#*id-}) ;;
			bus-*)	IFACE=$(get_iface_from_businfo "" `(IFS=-; echo ${F#*bus-})`) ;;
			*-bus-*)DEVTYPE=${CFGNAME%%-bus-*}
				IFACE=$(get_iface_from_businfo "$DEVTYPE" `(IFS=-; echo ${F#*bus-})`) ;;
			*)      continue ;;
		esac
		if [ -n "$IFACE" -a ! -e "$CFGTYPE-$IFACE" ] ; then
			MSG="`mv -v $F $CFGTYPE-$IFACE 2>&1`"
			echo "converting filename: $MSG"
			echo "mv -v $CFGTYPE-$IFACE $F" >> $RCF
		else
			echo -n "# could not convert '$F': "
			test -z "$IFACE" && echo -n "no interface found"
			test -e "$CFGTYPE-$IFACE" && echo -n "file '$CFGTYPE-$IFACE' already exists"
			echo
		fi
	done
	if [ -f "$RCF" ] ; then
		cat <<- EOT >> $RCF
		
		# This file helps you to revert the automatic conversion and logs
		# If conversion was succesful you may remove this file.
		# For the reason and the details about this conversion see:
		# /usr/share/doc/packages/sysconfig/README.no_more_hardwaredescriptions
		EOT
	fi
}

get_variable () {
	local line
	while read line; do
		eval $line
	done < <(grep "^[[:space:]]*$1" $2 2>/dev/null)
}

# known variables to be converted
KVTBC="BONDING_SLAVE BRIDGE_PORTS DEVICE ETHERDEVICE"
KVTBC="$KVTBC TUNNEL_LOCAL_INTERFACE _nm_name"

convert_vars_in_file() {
	local F=$1 V VV IFACE
	test -n "$F" || return 1
	test -r "$F" || return 2
	shift
	for V in ${*:-$KVTBC}; do
		get_variable $V $F
		for VV in `eval echo \$\{\!$V\*\}`; do
			if [ -n "${!VV}" ] ; then
				IFACE=
				for H in ${!VV} ; do
					IFACE="${IFACE:+$IFACE }`get_iface_from_hwdesc $H`"
				done
				echo "$F: $VV: ${!VV} --> $IFACE"
				test -z "$IFACE" && continue
				#sed -n "s/^\([[:space:]]*${VV}=\).*$/\1'$IFACE'/p" $F
				sed -i "s/^\([[:space:]]*${VV}=\).*$/\1'$IFACE'/" $F
			fi
		done
	done
}

. /etc/sysconfig/network/scripts/functions.common
shopt -s nullglob

one=$1 ; shift
case $one in
	netconfig) convert_netconfig $1 ;;
	*-id)      t=${one%-id}  ;
	           get_iface_from_lladdr  "$t" $@ ;;
	id)        get_iface_from_lladdr  ""   $@ ;;
	bus)       get_iface_from_businfo ""   $@ ;;
	*-bus)     t=${one%-bus} ;
	           get_iface_from_businfo "$t" $@ ;;
	*)
		if [ -r "$one" ] ; then
			convert_vars_in_file $one $*
		else
			usage
		fi ;;
esac
