ACC SHELL

Path : /etc/sysconfig/network/scripts/
File Upload :
Current File : //etc/sysconfig/network/scripts/functions

#!/bin/bash

# Network interface configuration
#
# Copyright (c) 2002-2006 SuSE Linux AG Nuernberg, Germany.
# All rights reserved.
#
# 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
#
# Authors: Michal Svec <msvec@suse.cz>
#          Mads Martin Joergensen <mmj@suse.de>
#          Marius Tomaschewski <mt@suse.de>
#
# $Id$
#

. /etc/sysconfig/network/scripts/functions.common

NETWORK_RUNFILE="$RUN_FILES_BASE/started"
STAMPFILE_STUB="$RUN_FILES_BASE/new-stamp-"
NETWORKMANAGER_BIN=/usr/sbin/NetworkManager
NM_ONLINE_BIN=/usr/bin/nm-online

#
# to test the next two functions:
#
# for i in {0..32}; do
# 	echo $i: $(pfxlen2mask $i) " ---> " $(mask2pfxlen $(pfxlen2mask $i))
# done

mask2pfxlen() {
	local i octet width=0 mask=(${1//./ })
	test ${#mask[@]} -eq 4 || return 1
	for octet in 0 1 2 3; do
		test "${mask[octet]}" -ge 0 -a \
		     "${mask[octet]}" -le 255 2>/dev/null \
			|| return 1
		for i in 128 192 224 240 248 252 254 255; do
			test ${mask[octet]} -ge $i && ((width++))
		done
	done
	echo $width
	return 0
}
pfxlen2mask() {
	test -n "$1" || return 1
	local o i n=0 adr=() len=$(($1))
	for o in 0 1 2 3; do
		adr[$o]=0
		for i in 128 64 32 16 8 4 2 1; do
			((n++ < len)) && \
			((adr[$o] = ${adr[$o]} + $i))
		done
	done
	echo ${adr[0]}.${adr[1]}.${adr[2]}.${adr[3]}
	return 0
}

add_to_wordlist() {
	local v="${1}"
	local l=(${!v}) ; shift
	local a w
	for a in ${@} ; do
		for w in ${l[@]} ; do
			[ "x$w" = "x$a" ] && \
			continue 2
		done
		l=(${l[@]} $a)
	done
	eval "$v='${l[@]}'"
}

is_iface_available () {
	local IFNAME=${1}
	local IFTYPE=${2:-$INTERFACETYPE}
	test -z "$IFNAME"              &&       return 1
	test -d /sys/class/net/$IFNAME &&       return 0
	test "${SCRIPTNAME%%-*}" = ifdown -a    \
	     "$MODE" = hotplug               && return 0
	: ${IFTYPE:=$(get_iface_type             "$IFNAME")}
	: ${IFTYPE:=$(get_iface_type_from_config "$IFNAME")}
	test "${SCRIPTNAME%%-*}" = ifup      && { \
	    case ${IFTYPE} in
		bond|bridge|vlan|ibchild)       return 0 ;;
		ppp|isdn|dummy|tun|tap)		return 0 ;;
		ipip|ip6tnl|sit|gre|mip6mnha)	return 0 ;;
	    esac
	}
	test -d /sys/class/net/$IFNAME
}

is_iface_up () {
	test -z "$1" && return 1
	test -d /sys/class/net/$1 || return 1
	case "`LC_ALL=POSIX ip link show $1 2>/dev/null`" in
		*$1*UP*) ;;
		*) return 1 ;;
	esac
}

link_ready_check () {
        local c=`cat /sys/class/net/${1}/carrier 2>/dev/null`
        local d=`cat /sys/class/net/${1}/dormant 2>/dev/null`
        local o=`cat /sys/class/net/${1}/operstate 2>/dev/null`

        #debug "link ready ${1}: carrier=$c, dormant=$d, operstate=$o"
        if test -e "/sys/class/net/${1}/operstate" ; then
                # SLE 11 has carrier + operstate + dormant
		test "$d" = "0" || return 3
		test "$c" = "1" || return 2
		test \( "$o" = "up" -o "$o" = "unknown" \) || return 1
        else
                # e.g. SLE 10 does not have operstate/dormant
                test "$c" = "1" || return 1
        fi
	return 0
}

ipv6_addr_dad_check()
{
	local iface="$1" word i
	local nodad=1 tentative=1 dadfailed=1
	test -f "/sys/class/net/$iface/ifindex" || return 1
	while read -a word ; do
		test "${word[0]}" != "inet6" && continue
		for((i=2; i<${#word[@]}; ++i)) ; do
			case ${word[$i]} in
			nodad)		nodad=0     ;;
			tentative)	tentative=0 ;;
			dadfailed)	dadfailed=0 ;;
			flags)  ((i++))
				rx='^[[:xdigit:]]+$'
				[[ "${word[$i]}" =~ $rx ]] || continue
				hx="0x${word[$i]}"
				test $(( $hx & 0x02 )) -ne 0 && nodad=0
				test $(( $hx & 0x08 )) -ne 0 && dadfailed=0
				test $(( $hx & 0x40 )) -ne 0 && tentative=0
			;;
			esac
		done
		#debug "ipv6 dad $iface: nodad=$nodad, dadfailed=$dadfailed, tentative=$tentative"
		test $nodad     -eq 0 && continue
		test $dadfailed -eq 0 && return 2
		test $tentative -eq 0 && return 3
	done < <(LC_ALL=C ip -6 addr show ${iface:+dev "$iface"} 2>/dev/null)
	return $R_SUCCESS
}

link_ready_wait ()
{
	local iface=$1
	local -i wsecs=${2:-0}
	local -i uwait=25000
	local -i loops=$(((wsecs * 1000000) / $uwait))
	local -i loop=0 ret=0

	link_ready_check "$iface" ; ret=$?
	while ((ret != 0 && loop++ < loops)) ; do
		usleep $uwait
		link_ready_check "$iface" ; ret=$?
	done
	return $ret
}

ipv6_addr_dad_wait()
{
	local iface=$1
	local -i wsecs=${2:-0}
	local -i uwait=25000
	local -i loops=$(((wsecs * 1000000) / $uwait))
	local -i loop=0 ret=0

	ipv6_addr_dad_check "$iface" ; ret=$?
	while ((ret == 3 && loop++ < loops)) ; do
		usleep $uwait
		ipv6_addr_dad_check "$iface" ; ret=$?
	done
	return $ret
}

get_ethtool_drv_info () {
	test -n "$1" || return 1
	local ethtool="/sbin/ethtool"
	if [ ! -x $ethtool ] ; then
		[ -x /usr${ethtool} ] && ethtool="/usr${ethtool}"  || return 1
	fi
	local key val
	$ethtool -i "$1" 2>/dev/null |
	while read key val ; do
		case "$key" in
			driver:)           printf 'ETHTOOL_DRV_NAME=%q\n'       "$val" ;;
			version:)          printf 'ETHTOOL_DRV_VERSION=%q\n'    "$val" ;;
			firmware-version:) printf 'ETHTOOL_DRV_FW_VERSION=%q\n' "$val" ;;
			bus-info:)         printf 'ETHTOOL_DRV_BUS_INFO=%q\n'   "$val" ;;
		esac
	done 2>/dev/null # bash printf required for %q format
}

get_iface_type () {
	local IF=$1 TYPE
	test -n "$IF" || return 1
	test -d /sys/class/net/$IF || return 2
	case "`cat /sys/class/net/$IF/type`" in
		    1)
		     	TYPE=eth
		     	# Ethernet, may also be wireless, ...
		     	if test -d /sys/class/net/$IF/wireless -o \
		     	        -L /sys/class/net/$IF/phy80211 ; then
		     	    TYPE=wlan
		     	elif test -d /sys/class/net/$IF/bridge ; then
		     	    TYPE=bridge
		     	elif test -f /proc/net/vlan/$IF ; then
		     	    TYPE=vlan
		     	elif test -d /sys/class/net/$IF/bonding ; then
		     	    TYPE=bond
		     	elif test -f /sys/class/net/$IF/tun_flags ; then
		     	    TYPE=tap
		     	elif test -d /sys/devices/virtual/net/$IF ; then
		     		case $IF in
		     		  (dummy*) TYPE=dummy ;;
		     		esac
		     	fi
		     	;;
		   24)	TYPE=eth ;; # firewire ;; # IEEE 1394 IPv4 - RFC 2734
		   32)	# InfiniBand
		      	case $IF in
		      	*.*) TYPE=ibchild ;; *) TYPE=ib ;;
		      	esac
		      	;;
		  512)	TYPE=ppp ;;
		  768)	TYPE=ipip ;; # IPIP tunnel
		  769)	TYPE=ip6tnl ;; # IP6IP6 tunnel
		  772)	TYPE=lo ;;
		  776)	TYPE=sit ;; # sit0 device - IPv6-in-IPv4
		  778)	TYPE=gre ;; # GRE over IP
		  783)	TYPE=irda ;; # Linux-IrDA
		  801)	TYPE=wlan_aux ;;
		65534)	TYPE=tun ;;
	esac
	# The following case statement still has to be replaced by something
	# which does not rely on the interface names.
	case $IF in
		ippp*|isdn*) TYPE=isdn;;
		mip6mnha*)   TYPE=mip6mnha;;
	esac
	test -n "$TYPE" && echo $TYPE && return 0
	return 3
}

get_iface_type_from_config () {
	local VARIABLE VALUE TYPE IF=$1
	local INTERFACETYPE WIRELESS WIRELESS_MODE BONDING_MASTER ETHERDEVICE
	local BRIDGE TUNNEL PPPMODE MODEM_DEVICE ENCAP
	test -z "$IF" && return 3
	test -r ifcfg-$IF || return 2
	# If variable (1st word) has the right value (2nd word) then this
	# interface is of this type (3rd word). If variable can have any
	# value except empty, then use "_not_empty_" as value.
	# INTERFACETYPE gets special handling and has variable type
	while read VARIABLE VALUE TYPE; do
		eval $VARIABLE=
		get_variable $VARIABLE $IF
		if [ "$VALUE" == _not_empty_ ] ; then
			test -n "${!VARIABLE}" && break
		else
			test "${!VARIABLE}" == "$VALUE" && break
		fi
	done <<- EOL
		INTERFACETYPE _not_empty_
		WIRELESS yes wlan
		WIRELESS_MODE _not_empty_ wlan
		BONDING_MASTER yes bond
		ETHERDEVICE _not_empty_ vlan
		BRIDGE yes bridge
		TUNNEL tap tap
		TUNNEL tun tun
		TUNNEL sit sit
		TUNNEL gre gre
		TUNNEL ipip ipip
		PPPMODE pppoe ppp
		PPPMODE pppoatm ppp
		PPPMODE capi-adsl ppp
		PPPMODE pptp ppp
		MODEM_DEVICE _not_empty_ ppp
		ENCAP syncppp isdn
		ENCAP rawip isdn
	EOL
	case "$IF" in
		ib*.*) INTERFACETYPE=ibchild ;;
		ib*)   INTERFACETYPE=ib      ;;
	esac

	# bnc#458412: map obsolete (s390) eth type overrides
	case "$INTERFACETYPE" in
		(qeth|hsi|lcs|ctc|iucv|tr) INTERFACETYPE=eth ;;
	esac

	test -n "$INTERFACETYPE" && echo $INTERFACETYPE && return
	if [ -z "$TYPE" ] ; then
		case $IF in
			lo)          TYPE=lo;;
			dummy*)      TYPE=dummy;;
			ib*)         TYPE=ib;;
			ip6tnl*)     TYPE=ip6tnl;;
			mip6mnha*)   TYPE=mip6mnha;;
		esac
	fi
	# There is a config file, but it has no special type: This must be ethernet
	test -n "$TYPE" && echo $TYPE || echo eth
}

#
# supported_on_localfs($INTERFACE [, $INTERFACETYPE])
#
# Returns 0 (true) when we support to start the interface
# (configuration) on local-fs only, or 1 (false) when
# also remote-fs is required (e.g. because of utilities
# in /usr that may be on NFS) or 2 (false) on errors.
#
supported_on_localfs() {
	local IFNAME=$1
	local IFTYPE=${2:-$INTERFACETYPE}

	test "x$IFNAME" = x && return 2
	test "x$IFTYPE" = x && IFTYPE=$(get_iface_type             $IFNAME)
	test "x$IFTYPE" = x && IFTYPE=$(get_iface_type_from_config $IFNAME)

	# Currently it is sufficient to check the type only.
	local result=1	# remote-fs requirement as default
	case $IFTYPE in
		"")                     result=2 ;;
		lo|dummy)               result=0 ;;
		eth|tr|ib)              result=0 ;;
		ibchild|tap)            result=0 ;;
		vlan|bridge|bond)       result=0 ;;
	esac
	# tunnels in principle too, but it may be not possible
	# to resolve the base / slave interface correctly ...
	#tun|sit|gre|ipip)              result=0 ;;
	return $result
}

get_hwaddress () {
	cat /sys/class/net/$1/address >/dev/null
}

# This will echo the first address listed for the given interface.
get_ipv4address () {
	test -z "$1" && return 1
	local a b c
	while read a b c; do
		if [ "$a" = inet ] ; then
			break
		fi
	done < <(LC_ALL=POSIX ip -4 address list "$1" 2>/dev/null)
	test -z "$b" && return 1
	echo ${b%%/*}
}

convert_ipv4address_to_6to4 () {
	printf "2002:%02x%02x:%02x%02x::1\n" $(IFS=.; echo $1)
}

convert_6to4_to_ipv4address () {
	ADDR=$1
	LIST=($(IFS=:; echo $ADDR))
	if [ ${#LIST[@]} -ne 4 -o "${LIST[0]}" != "2002" ] ; then
		echo $ADDR
	fi
	NORM_1=`printf "%04x" 0x${LIST[1]} 2>/dev/null`
	NORM_2=`printf "%04x" 0x${LIST[2]} 2>/dev/null`

	printf "::%u.%u.%u.%u" \
		0x${NORM_1:0:2} 0x${NORM_1:2:2} \
		0x${NORM_2:0:2} 0x${NORM_2:2:2}
}

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

get_startmode () {
	local STARTMODE
	get_variable STARTMODE $1
	echo  "$STARTMODE"
}

get_slaves () {
	local ret=1
	local v vv
	case $1 in
		ib*.*) echo -n "${1%%\.*} " ; ret=0 ;;
	esac
	for v in BONDING_SLAVE ETHERDEVICE TUNNEL_DEVICE \
	         TUNNEL_LOCAL_INTERFACE BRIDGE_PORTS; do
		get_variable $v $1
		for vv in `eval echo \$\{\!$v\*\}`; do
			if [ -n "${!vv}" ] ; then
				echo -n "${!vv} "
				ret=0
			fi
			unset $vv
		done
		test $ret = 0 && return 0
	done
	return 1
}

get_ifplugd_priority () {
	local IFPLUGD_PRIORITY=0
	declare -i IFPLUGD_PRIORITY
	get_variable IFPLUGD_PRIORITY $1
	echo "$IFPLUGD_PRIORITY"
}

# We have to write status files per interface or per configuration for at least
# these reasons:
# 1) remember the used configuration if getcfg cannot get it after the device
#    has been unplugged  --> OBSOLETE: no more getcfg
# 2) store ifup options while restarting the network (e.g. the choosen provider)
# 3) pass status information to smpppd to allow kinternet to show them to the
#    user.
# 4) control running ifup/down processes (ifdown has to stop a running ifup)
# To handle this cached information, there are the *_cached_config_data
# functions.

# write_cached_config_data <type> <data> <name> [PFX=<prefix>]
# needs at least 3 arguments
# - the type of data to write: config, options, state, ...
# - the data itself
# - the configuration or interface name
# - the file prefix is optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints nothing
# You have to commit changes after writing with commit_cached_config_data()
write_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX FILE TMPFILE MODFILE
	test -n "$4" && eval $4
	: ${PFX:=if-}
	FILE=$RUN_FILES_BASE/$PFX$3
	MODFILE=$RUN_FILES_BASE/tmp/$PFX$3.$$                  # MODFILE
	TMPFILE=$RUN_FILES_BASE/tmp/$PFX$3.$$.tmp              # MODFILE
	test -f $MODFILE || cp $FILE $MODFILE 2>/dev/null
	FILE=$MODFILE                                       # MODFILE
	touch $FILE
	while IFS== read a b; do
		case $a in
			$1) ;;
			 *) echo "$a=$b" ;;
		esac
	done < <(cat $FILE) > $TMPFILE
	if [ -n "$2" ] ; then
		echo "$1=$2" >> $TMPFILE
	fi
	if [ -f $TMPFILE ] ; then
		mv $TMPFILE $FILE
	fi
}

# INTERFACE=`read_cached_config_data <type> <name> [PFX=<prefix>]`
# needs at least 2 arguments
# - the type of data to read: config, options, state, ...
# - the configuration or interface name
# - the file prefix is optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints the wanted data
read_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX
	test -n "$3" && eval $3
	: ${PFX:=if-}
	if [ -r "$RUN_FILES_BASE/$PFX$2" ] ; then
		while IFS== read a b; do
			case $a in
				$1) echo "$b" ;;
				 *) ;;
			esac
		done < $RUN_FILES_BASE/$PFX$2
	fi
}

# delete_from_cached_config_data <type> [<data> [<name>]] [PFX=<prefix>]
# Deletes an entry "$1=$2" from all config data cache files.
# If there is a third argument, we delete it only from this configuration. All
# handled files that are empty after modification will be deleted.
# If $2 is empty then remove line $1=* from this ($3) or all configuration.
# If $1 is '*' it will remove all entries.
#
# !!! WIP !!!
# It currently works only on one file and 2nd and 3rd argument are mandatory
# !!! WIP !!!
#
# needs at least 1 argument
# - the type of data to delete: config, options, state, ...
# - optional the data itself
# - optional the configuration or interface name
# - the file prefix is also optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints nothing
# You have to commit changes after deleting with commit_cached_config_data()
delete_from_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local TYPE DATA PFX FILE TMPFILE MODFILE NAME
	TYPE=$1; shift
	if [ "$1" = "${1#PFX}" ] ; then
		DATA=$1; shift
	fi
	if [ "$1" = "${1#PFX}" ] ; then
		NAME=$1; shift
	fi
	test -n "$1" && eval $1
	: ${PFX:=if-}
	FILE=$RUN_FILES_BASE/$PFX$NAME                 # MODFILE
	MODFILE=$RUN_FILES_BASE/tmp/$PFX$NAME.$$          # MODFILE
	TMPFILE=$RUN_FILES_BASE/tmp/$PFX$NAME.$$.tmp      # MODFILE
	test -f $MODFILE || cp $FILE $MODFILE 2>/dev/null
	FILE=$MODFILE                                       # MODFILE
	touch $FILE
		if [ -s "$FILE" ] ; then
			while IFS== read a b; do
				case $a in
					$TYPE)
						if [ "$b" != "$DATA" -a -n "$DATA" ] ; then
							echo "$a=$b" 
						fi
						;;
					 *) echo "$a=$b" ;;
				esac
			done < <(cat $FILE) > $TMPFILE
		fi
		if [ -f $TMPFILE ] ; then
			mv $TMPFILE $FILE
		fi
		if [ ! -s $FILE ] ; then
			rm -Rf $FILE
		fi
#	done   MODFILE
}

# HWDESC NIX < <(grep_cached_config_data <type> <data> [PFX=<prefix>])
# needs 2 arguments:
# - the type of data to grep for: config, options, state, ...
# - the data itself
# - the file prefix is optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints all matching configuration names in a single line
grep_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX
	test -n "$3" && eval $3
	: ${PFX:=if-}
	local restore_nullglob="$(shopt -p nullglob)"
	shopt -s nullglob
	for f in $RUN_FILES_BASE/$PFX*; do
		while IFS== read a b; do
			case $a in
				$1)
					if [ "$b" = "$2" ] ; then
						echo -n "${f#$RUN_FILES_BASE/$PFX} " 
					fi
					;;
			esac
		done < $f
	done
	eval $restore_nullglob
	echo
}

# Writing and deleting cached config data is always done in temporary files. To
# make this changes visible in the right file you must commit the changes. This
# helps to make file changes atomic.
commit_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX FILE MODFILE
	test -n "$2" && eval $2
	: ${PFX:=if-}
	FILE=$RUN_FILES_BASE/$PFX$1
	MODFILE=$RUN_FILES_BASE/tmp/$PFX$1.$$
	if [ -f $MODFILE ] ; then
		mv $MODFILE $FILE
	else
		rm -f $FILE
	fi
}

is_connected () {
	case `read_cached_config_data status $1` in
		connected) return 0 ;;
		connecting) return 0 ;; # might be wrong, test for link to
	esac
	return 1
}

has_link () {
	case `read_cached_config_data link $1` in
		yes) return 0 ;;
	esac
	return 1
}

# This function looks for interfaces which depend on the given interface. It
# prints a list with all depending interfaces. It returns 0 if there are
# depending interfaces and !=0 if not.
# Currently it checks only for vlan and optionally bonding slave interfaces.
# FIXME: Add other types of interfaces that depend on others.
get_depending_ifaces() {
	local VLAN_PATH BOND_PATH DEP_VLANS DEP_BONDS BASE_IFACE i
	local -a DEP_IFACES=()
	VLAN_PATH="/proc/net/vlan"
	BOND_PATH=""
	while [ $# -gt 0 ]; do
		case $1 in
		--with-bonding-slaves)
			BOND_PATH="/proc/net/bonding"
			shift
		;;
		-*)	shift ;;
		*)	break ;;
		esac
	done
	BASE_IFACE="$1"

	if [ -z "$BASE_IFACE" ]; then
		return 1
	fi

	if [ -d "$VLAN_PATH" ]; then
		DEP_VLANS=`cd "$VLAN_PATH"
			grep -lws "Device: *$BASE_IFACE" *`
		DEP_IFACES+=($DEP_VLANS)
	fi

	if [ -n "$BOND_PATH" -a -d "$BOND_PATH" ]; then
		DEP_BONDS=`cd "$BOND_PATH"
			grep -s '^Slave Interface:' $BASE_IFACE |
			while IFS=':' read text iface ; do echo -n "$iface" ; done`
		DEP_IFACES+=($DEP_BONDS)
	fi

	case $BASE_IFACE in
		(ib*.*)	;;
		(ib*)	# the infiniband children -- is there a better way?
			for i in `ls -1 /sys/class/net/ 2>/dev/null` ; do
				test -d /sys/class/net/$i || continue
				case $i in (${BASE_IFACE}.*)
					DEP_IFACES+=($i)
				;;
				esac
		     	done
		;;
	esac

	if [ ${#DEP_IFACES[*]} -gt 0 ]; then
		echo "${DEP_IFACES[*]}"
		return 0
	else
		return 1
	fi
}

resolve_iface_startorder()
{
    #
    # resolve_iface_startorder <name of the result variable>
    #                          <list of interfaces to resolve>
    #                          [ <skip list> [filter fuction] ]
    #
    # This function creates a start ordered list of virtual interfaces
    # as bondings, vlans and bridges known by the get_slaves() function.
    # It reads the base or "slave" interfaces of each virtual interface
    # recursively and inserts the slaves into the result list before the
    # virtual interface itself. Further it detects interface usage loops
    # like: interface "a" needs "b" and "b" needs "a" and skips them.
    #
    # The optional skip list allows to stop the recursion insertion at
    # e.g. already started / existing physical slave interfaces.
    # Addding interfaces to resolve to this skip list causes resolving
    # of the recursive list of slave interfaces without the requested
    # interfaces themself (resolve_iface_startorder slaves "br0" "br0").
    #
    # The optional filter function allows to filter out interfaces as
    # well as all depending interfaces. For example a vlan interface
    # will be filtered out, when the underlying interface gets filtered
    # out. This happens also, when the function does not cause to filter
    # out the vlan interface directly. Main purpose is to filter out
    # interfaces, that are not supported without a mounted remotefs.
    #
    # $1:  the name of the result start ordered list variable to set
    # $2:  list of virtual interfaces to resolve the start order for
    # $3:  optional list of interfaces to skip/stop reading
    # $4:  optional (white list) filter function returning true for
    #      acceptable interfaces and false to filter out an interface.
    #
    # returns 0 on success,
    #         1 on empty result list name,
    #         [2 on interface usage loop error; disabled]
    #
    local NAME="$1"
    local TODO="$2"
    local SKIP="$3"
    local FUNC="$4"
    local LIST=()

    _resolve_iface_startorder() {
	local  todo="$1"
	local  skip="$2"
	local  func="$3"
	local guard="$4"
	local level="$5"
	local slaves iface i ret
	local result=0
	for iface in ${todo} ; do
		for i in ${guard} ; do
			if [ "x${i}" = "x${iface}" ] ; then
				err_mesg "Interface dependency loop " \
				         "detected: ${guard} ${iface}"
				# guard non-empty in level > 0
				return 2
			fi
		done

		if [ "x$func" != x ] && ! $func "$iface" &>/dev/null ; then
			[ $level -eq 0 ] && continue || return 3
		fi

		slaves=(`get_slaves $iface 2>/dev/null`)
		if [ $? = 0 -a ${#slaves[@]} -gt 0 ] ; then
			ret=0
			_resolve_iface_startorder \
				"${slaves[*]}"    \
				"${skip}"         \
				"${func}"         \
				"${guard} $iface" \
                                $(($level+1))     \
				|| ret=$?
			if [ $ret -ne 0 ] ; then
				if [ $level -eq 0 ] ; then
					#result=$ret
					continue
				else
					return $ret
				fi
			fi
		fi

		for i in ${LIST[@]} ${skip} ; do
			[ "x$i" = "x$iface" ] && continue 2
		done

		LIST=(${LIST[@]} $iface)
	done
	return $result
    }

    [ "x$NAME" = x ] && return 1

    _resolve_iface_startorder "$TODO" "$SKIP" "$FUNC" "" 0 || return $?

    eval "$NAME='${LIST[@]}'"
}

nm_running () {
	local MSG RET
	test -x "$NETWORKMANAGER_BIN" || return
	MSG=`checkproc $NETWORKMANAGER_BIN 2>&1`
	RET=$?
	info_mesg "$MSG"
	return $RET
}

netcontrol_running() {
	test -f $NETWORK_RUNFILE
}

# returns 0 if ifup is currently working on this interface
ifup_on_iface() {
	ps axhww | grep -qs "[i]fup.* $INTERFACE\>"
}

# returns 0 if there is a dhcp client running on this interface
# prints pids of all dhcp clients on this interface
# prints nothing if called with option '-q'
# Usually it should not happen that more then one dhcpcd is running on one
# interface, but it may happen. So better safe than sorry!
dhcpc4_on_iface() {
	local pid retval=1
	[ "x$DHCLIENT"  != x -a "x$INTERFACE" != x ] || return $retval
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?$DHCLIENT\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "x$1" == "x-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
dhcpc6_on_iface() {
	local pid retval=1
	[ "x$DHCLIENT6"  != x -a "x$INTERFACE" != x ] || return $retval
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?$DHCLIENT6\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "x$1" == "x-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
dhcpc_on_iface() {
	local pid retval=1 pattern
	[ "x$DHCLIENT"  != x ] && pattern=$DHCLIENT || pattern=""
	[ "x$DHCLIENT6" != x ] && pattern="${pattern:+$pattern|}$DHCLIENT6"
	[ "x$pattern" != x -a "x$INTERFACE" != x ] || return $retval
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?($pattern)\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "$1" == "-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
any_dhcpc_on_iface()
{
	local pid retval=1 pattern=""
	[ "x$INTERFACE" != x ] || return $retval
	# just search for all known dhcp clients
	pattern="dhcpcd|dhclient|dhcp6c|dhclient6"
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?($pattern)\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "$1" == "-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
dhcp_interfaces() {
	local old_if=$INTERFACE
	for INTERFACE in `ls -1 /sys/class/net`; do
		if test -d "/sys/class/net/$INTERFACE" ; then
			any_dhcpc_on_iface -q && echo "$INTERFACE"
		fi
	done
	INTERFACE=$old_if
	return 0
}

# Parse iSCSI Boot Firmare Table settings of given interface
# and provide as adequate ifcfg/netcontrol scripts settings.
parse_ifname_ibft_settings()
{
	case $1 in ""|.|..|*/*) return 1 ;; esac
	test -d "/sys/class/net/$1" || return 1

	local IF="$1" sysfs_ibft="/sys/firmware/ibft"
	#test -d "$sysfs_ibft" || modprobe -qs iscsi_ibft 2>/dev/null
	test -d "$sysfs_ibft" || return 1

	local edir ibft_e_origin
	for edir in $(ls -1d "$sysfs_ibft/ethernet"* 2>/dev/null) ;
	do
		if test -d "$edir/device/net/$IF" ; then
			# Check another flags (flags bit 1, FW Boot Selected)?
			# IpPrefixOrigin: Other,Manual,WellKnown,Dhcp,RouterAdv
			ibft_e_origin=`cat "$edir/origin" 2>/dev/null`
			case $ibft_e_origin in
			3)	# IpPrefixOriginDdhcp
				echo "BOOTPROTO='dhcp'"
			;;
			*)	# ... everything else ('static' ?!)
				echo "BOOTPROTO='none'"
			;;
			esac
			return 0
		fi
	done
	return 1
}


ACC SHELL 2018