#!/sbin/runscript
# Copyright (c) 2004-2005 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header$

# Contributed by Roy Marples (uberlord@gentoo.org)
# Many thanks to Aron Griffis (agriffis@gentoo.org)
# for help, ideas and patches

#NB: Config is in /etc/conf.d/net

if [[ -n ${NET_DEBUG} ]]; then
	set -x
	devnull=/dev/stderr
else
	devnull=/dev/null
fi

# For pcmcia users. note that pcmcia must be added to the same
# runlevel as the net.* script that needs it.
depend() {
	use coldplug hotplug pcmcia usb isdn wlan

	# Load any custom depend functions for the given interface
	# For example, br0 may need eth0 and eth1
	local iface=${myservice##*.}
	[[ $( type -t depend_${iface} ) == "function" ]] && depend_${iface}
}

# Define where our modules are
MODULES_DIR=/lib/rcscripts/net.modules.d

# Load some functions shared between ourselves and our dhcp helpers
source ${MODULES_DIR}/helpers.d/functions

# Make some wrappers to fudge after/before/need/use depend flags.
# These are callbacks so mods[i] will be set.
after() {
	local x="$*"
	[[ $# -gt 1 ]] && x=$( echo -e "${x// /\n}" | sort | xargs )
	eval "${MODULE}_after() { echo \"$x\"; }"
}
before() {
	local x="$*"
	[[ $# -gt 1 ]] && x=$( echo -e "${x// /\n}" | sort | xargs )
	eval "${MODULE}_before() { echo \"$x\"; }"
}
need() {
	local x="$*"
	[[ $# -gt 1 ]] && x=$( echo -e "${x// /\n}" | sort | xargs )
	eval "${MODULE}_need() { echo \"$x\"; }"
}
installed() {
	local x="$*"
	[[ $# -gt 1 ]] && x=$( echo -e "${x// /\n}" | sort | xargs )
	# We deliberately misspell this as _installed will probably be used
	# at some point
	eval "${MODULE}_instlled() { echo \"$x\"; }"
}

sort() {
	LC_ALL=C /bin/sort "$@"
}

# void module_class_wrap(char *module, char *provides)
# Setup class wrappers: interface_up -> iproute2_up, for example
module_class_wrap() {
	local module=${1} provides=${2} x 

	# Don't wrap functions if another module already has
	[[ $( type -t ${provides}_provides ) == function ]] && return

	for x in $( typeset -f | grep -o ^${module}_'[^ ]*' ); do
		eval "${provides}${x#${module}}() { ${x} \"\$@\"; }"
	done
}

# bool module_load_minimum(char *module)
#
# Does the minimum checking on a module - even when forcing
module_load_minimum() {
	local f=${1} MODULE=${1##*/}

	if [[ ! -f ${f} ]]; then
		eerror "${f} does not exist"
		return 1
	fi

	if ! source ${f} ; then
		eerror "${MODULE} failed a sanity check"
		return 1
	fi

	for f in check_installed provides check_depends depend; do
		[[ $( type -t ${MODULE}_${f} ) == function ]] && continue
		eerror "${MODULE} does not support the required function ${f}"
		return 1
	done

	# Wrap our before/after/need/use functions
	${MODULE}_depend

	return 0
}

# bool modules_load_auto()
#
# Load and check each module for sanity
# If the module is not installed, the functions are to be removed
modules_load_auto() {
	local e f i j beforefuncs afterfuncs diffs unload

	# Populate the MODULES array
	MODULES=( $( find ${MODULES_DIR}/ -maxdepth 1 -type f ! -name ".*" | sort ) )

	# Each of these sources into the global namespace, so it's
	# important that module functions and variables are prefixed with
	# the module name, for example iproute2_

	beforefuncs="$( typeset -F )"
	j=${#MODULES[@]}
	loaded_interface=false
	for (( i=0; i<j; i++ )); do

		unload=false
		module_load_minimum ${MODULES[i]} || unload=true
		MODULES[i]=${MODULES[i]##*/}

		! ${unload} && ${MODULES[i]}_check_installed false || unload=true

		if [[ ${MODULES[i]} == interface ]]; then
			eerror "interface is a reserved name - cannot load a module called interface"
			return 1
		fi

		afterfuncs="$( typeset -F )"
		if ${unload} ; then
			unset MODULES[i]
			unsetfuncs="${unsetfuncs} $( diff -U0 <( echo "${beforefuncs}" ) <( echo "${afterfuncs}" ) 2>/dev/null \
				| awk 'BEGIN { ORS = " "; } /+declare -f/ { print $3; }' )"
		fi
		beforefuncs="${afterfuncs}"

	done
	MODULES=( "${MODULES[@]}" )

	return 0
}


# bool modules_check_installed(void)
#
# Ensure that all modules have the required modules loaded
# This enables us to remove modules from the MODULES array
# Whilst other modules can still explicitly call them
# One example of this is essidnet which configures network
# settings for the specific ESSID connected to as the user
# may be using a daemon to configure wireless instead of our
# iwconfig module
modules_check_installed() {
	local i j missingdeps nmods=${#MODULES[@]}

	for (( i=0; i<nmods; i++ )); do
		[[ $( type -t ${MODULES[i]}_instlled ) != function ]] && continue
		for j in $( ${MODULES[i]}_instlled ); do
			missingdeps=true
			if [[ $( type -t ${j}_check_installed ) == function ]]; then
				${j}_check_installed && missingdeps=false
			fi
			${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break
		done
	done

	MODULES=( "${MODULES[@]}" )
	PROVIDES=( "${PROVIDES[@]}" )
}

# bool modules_check_user(void)
modules_check_user() {
	local -a umods
	local i j k l npref nmods=${#MODULES[@]}

	# Has the interface got any specific modules?
	eval umods=( \"\$\{modules_${iface}\[@\]\}\" )

	# Global setting follows interface-specific setting
	umods=( "${umods[@]}" "${modules[@]}" )

	# Add our preferred modules
	npref=3
	umods=( "${umods[@]}" "ifconfig" "dhcpcd" "iwconfig" )

	# First we strip any modules that conflict from user settings
	# So if the user specifies pump then we don't use dhcpcd
	for (( i=0; i<${#umods[@]}; i++ )); do
		# Some users will inevitably put "dhcp" in their modules
		# list.  To keep users from screwing up their system this
		# way, ignore this setting so that the default dhcp
		# module will be used.
		[[ ${umods[i]} == dhcp ]] && continue

		# We remove any modules we explicitly don't want
		if [[ ${umods[i]} == !* ]]; then
			for (( j=0; j<nmods; j++ )); do
				[[ -z ${MODULES[j]} ]] && continue
				if [[ ${umods[i]:1} == ${MODULES[j]} || ${umods[i]:1} == ${PROVIDES[j]} ]]; then
					# We may need to setup a class wrapper for it even though
					# we don't use it directly
					${MODULES[j]}_check_installed && module_class_wrap ${MODULES[j]}
					unset MODULES[j]
					unset PROVIDES[j]
					break
				fi
			done
			continue
		fi

		if [[ $( type -t ${umods[i]}_provides ) != function ]]; then
			# If the module is one of our preferred modules, then
			# ignore this error; whatever is available will be
			# used instead.
			(( i < ${#umods[@]} - npref )) || continue
			eerror "${umods[i]} is not a valid module (missing provides)"
			return 1
		fi

		# Ensure that the user specified module has its package installed
		if (( i < ${#umods[@]} - npref )); then
			# $1==true causes check_installed to report its own errors
			${umods[i]}_check_installed true || return 1
		else
			# If this happens on a preferred modules, ignore it;
			# whatever is available will be used instead.
			${umods[i]}_check_installed false || continue
		fi

		mod=$( ${umods[i]}_provides )
		for (( j=0; j<nmods; j++ )); do
			[[ -z ${MODULES[j]} ]] && continue
			if [[ ${PROVIDES[j]} == ${mod} && ${umods[i]} != ${MODULES[j]} ]]; then
				# We don't have a match - now ensure that we still provide an alternative.
				# This is to handle our preferred modules.
				for (( l=0; l<nmods; l++ )); do
					[[ ${l} -eq ${j} || -z ${MODULES[l]} ]] && continue
					if [[ ${PROVIDES[l]} == ${mod} ]]; then
						unset MODULES[j]
						unset PROVIDES[j]
						break
					fi
				done
			fi
		done
	done

	# Then we strip conflicting modules.
	# We only need to do this for 3rd party modules that conflict with
	# our own modules and the preferred list AND the user modules
	# list doesn't specify a preference.
	for (( i=0; i<nmods-1; i++ )); do
		[[ -z ${MODULES[i]} ]] && continue			
		for (( j=i+1; j<nmods; j++)); do
			[[ -z ${MODULES[j]} ]] && continue
			[[ ${PROVIDES[i]} == ${PROVIDES[j]} ]] && unset MODULES[j] && unset PROVIDES[j]
		done
	done

	MODULES=( "${MODULES[@]}" )
	PROVIDES=( "${PROVIDES[@]}" )
	return 0
}

# void modules_sort(void)

modules_sort() {
	local -a modnums sort_history modafter modbefore
	local i j k p changed_something nmods=${#MODULES[@]}

	# Sort our modules
	# We do this by assigning numbers to each module
	# We also assign modbefore and modafter so we don't
	# shell out as much because it's expensive on CPU.
	modnums=()
	for (( i=0; i<nmods; i++ )); do
		modnums[i]=${i}
		[[ $( type -t ${MODULES[i]}_after ) == function ]] && modafter[i]=$( ${MODULES[i]}_after )
		[[ $( type -t ${MODULES[i]}_before ) == function ]] && modbefore[i]=$( ${MODULES[i]}_before )
	done
	
	# Then we swap numbers based on and after/before flags
	# until we don't swap anymore. The sort_history array prevents
	# the possibility of an infinite loop
	sort_history[0]="${modnums[*]}"
	for (( k=1; 1; k++ )); do
		changed_something=false
		for (( i=0; i<nmods; i++ )); do
			for p in ${modafter[i]}; do
				for (( j=0; j<nmods; j++ )); do
					[[ ${p} != ${MODULES[j]} && ${p} != ${PROVIDES[j]} ]] && continue
					if [[ ${modnums[i]} -lt ${modnums[j]} ]]; then
						tmp=${modnums[i]}
						modnums[i]=${modnums[j]}
						modnums[j]=${tmp}
						changed_something=true
					fi
				done
			done
			for p in ${modbefore[i]}; do
				for (( j=0; j<nmods; j++ )); do
					[[ ${p} != ${MODULES[j]} && ${p} != ${PROVIDES[j]} ]] && continue
					if [[ ${modnums[i]} -gt ${modnums[j]} ]]; then
						tmp=${modnums[i]}
						modnums[i]=${modnums[j]}
						modnums[j]=${tmp}
						changed_something=true
					fi
				done
			done
		done
		${changed_something} || break

		# Make sure we aren't repeating a previous state
		# First time through, k=1, k/2=0
		sort_history[k]="${modnums[*]}"
		if [[ ${sort_history[k]} == ${sort_history[k/2]} ]]; then
			eerror "Detected an infinite loop sorting modules; blundering ahead"
			break
		fi
	done

	# Finally we sort our modules in number order
	um=""
	for (( i=0; i<nmods; i++ )); do
		um="${um}${modnums[i]} ${MODULES[i]} ${PROVIDES[i]}\n"
	done

	p=( $( echo -e "${um}" | sort -n | awk '{print $2,$3}' ) )
	MODULES=()
	PROVIDES=()
	j=0
	for (( i=0; i<${#p[@]}; i+=2 )); do
		MODULES[j]=${p[i]}
		PROVIDES[j]=${p[i+1]}
		(( j++ ))
	done
}

# bool modules_check_depends(bool showprovides)
modules_check_depends() {
	local showprovides=${1:-false} nmods=${#MODULES[@]} i j needmod
	local missingdeps p  interface=false

	for (( i=0; i<nmods; i++ )); do
		if [[ $( type -t ${MODULES[i]}_need ) == function ]]; then
			for needmod in $( ${MODULES[i]}_need ); do
				missingdeps=true
				for (( j=0; j<nmods; j++ )); do
					if [[ ${needmod} == ${MODULES[j]} || ${needmod} == ${PROVIDES[j]} ]]; then
						missingdeps=false
						break
					fi
				done
				if ${missingdeps} ; then
					eerror "${MODULES[i]} needs ${needmod} (dependency failure)"
					return 1
				fi
			done
		fi

		${MODULES[i]}_check_depends || return 1
		[[ ${PROVIDES[i]} == interface ]] && interface=true

		if ${showprovides} ; then
			[[ ${PROVIDES[i]} != ${MODULES[i]} ]] && veinfo "${MODULES[i]} provides ${PROVIDES[i]}"
		fi
	done

	if ! ${interface} ; then
		eerror "no interface module has been loaded"
		return 1
	fi

	return 0
}

# bool modules_load(char *iface, bool starting)
#
# Loads the defined handler and modules for the interface
# Returns 0 on success, otherwise 1
modules_load()  {
	local iface=${1} starting=${2:-true} mod p=false i j unsetfuncs
	local -a x
	local RC_INDENTATION=${RC_INDENTATION}	# so it will reset after function
	local -a PROVIDES

	if [[ ${iface} == lo ]]; then
		# We force lo to only use these modules for a major speed boost
		modules_force=( "iproute2" "ifconfig" )
	else
		eval x=( \"\$\{modules_force_${iface}\[@\]\}\" )
		[[ -n ${x} ]] && modules_force=( "${x[@]}" )
		if [[ -n ${modules_force} ]]; then
			ewarn "WARNING: You are forcing modules!"
			ewarn "Do not complain or file bugs if things start breaking"
			report=true
		fi
		veinfo "Loading networking modules for ${iface}"
	fi
	eindent

	if [[ -z ${modules_force} ]]; then
		modules_load_auto || return 1
	else
		j=${#modules_force[@]}
		for (( i=0; i<j; i++ )); do
			module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1
			${modules_force[i]}_check_installed || unset modules_force[i]
		done
		MODULES=( "${modules_force[@]}" )
	fi

	# We now buffer the _provides functions for a big speed boost
	j=${#MODULES[@]}
	for (( i=0; i<j; i++ )); do
		PROVIDES[i]=$( ${MODULES[i]}_provides )
	done

	if [[ -z ${modules_force[@]} ]]; then
		if ${starting}; then
			modules_check_user || return 1
		fi

		# We unset unloaded module functions here as so we can error
		# with a message informing the user to emerge the correct
		# package if it's in their modules
		[[ -n ${unsetfuncs} ]] && unset ${unsetfuncs}

		modules_sort
	fi

	# Setup class wrappers: interface_up -> iproute2_up, for example
	j=${#MODULES[@]}
	for (( i=0; i<j; i++ )); do
		module_class_wrap ${MODULES[i]} ${PROVIDES[i]}
	done

	modules_check_installed || return 1

	[[ ${iface} != lo ]] && veinfo "modules: ${MODULES[@]}"
	eindent

	[[ ${iface} != lo && ${starting} == true ]] && p=true
	modules_check_depends ${p} || return 1
	return 0
}

# bool iface_start(char *interface)
#
# iface_start is called from start.  It's expected to start the base
# interface (for example "eth0"), aliases (for example "eth0:1") and to start
# VLAN interfaces (for example eth0.0, eth0.1).  VLAN setup is accomplished by
# calling itself recursively.
iface_start() {
	local iface=${1} mod config_counter=-1 x warn=false config_worked=false
	local RC_INDENTATION=${RC_INDENTATION}	# so it will reset after function
	local -a config fallback fallback_route conf
	local ifvar=$( interface_variable ${iface} )

	# pre Start any modules with
	for mod in ${MODULES[@]}; do
		if [[ $( type -t ${mod}_pre_start ) == function ]]; then
			${mod}_pre_start ${iface} || { eend 1; return 1; }
		fi
	done

	# New style config - one variable fits all
	eval config=( \"\$\{config_${ifvar}\[@\]\}\" )
	eval fallback=( \"\$\{fallback_${ifvar}\[@\]\}\" )
	eval fallback_route=( \"\$\{fallback_route_${ifvar}\[@\]\}\" )

	# We must support old configs
	if [[ -z ${config} ]]; then
		interface_get_old_config ${iface} || return 1
	fi

	# Handle "noop" correctly
	if [[ ${config[0]} == "noop" ]]; then
		if interface_is_up ${iface} true ; then
			einfo "Keeping current configuration for ${iface}"
			eend 0
			return 0
		fi

		# Remove noop from the config var
		config=( "${config[@]:1}" )
	fi

	# Provide a default of DHCP if no configuration is set
	if [[ -z ${config} ]]; then
		if [[ $( type -t dhcp_start ) == function ]]; then
			config=( "dhcp" )
			warn=true
		else
			eerror "Cannot default to dhcp as there is no dhcp module loaded"
			eerror "No configuration for ${iface}"
			return 1
		fi
	fi

	einfo "Bringing up ${iface}"
	eindent
	for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do
		# Handle null and noop correctly
		if [[ ${config[config_counter]} == "null" \
		|| ${config[config_counter]} == "noop" ]]; then
			eend 0
			config_worked=true
			continue
		fi

		if ${warn}; then
			ewarn "Configuration not set for ${iface} - assuming dhcp"
			warn=false
		fi

		# We convert it to an array - this has the added
		# bonus of trimming spaces!
		conf=( ${config[config_counter]} )
		einfo "${conf[0]}"

		# Do we have a function for our config?
		if [[ $( type -t ${conf[0]}_start ) == function ]]; then
			# Check that the module is valid
			x=false
			for mod in ${MODULES[@]}; do
				if [[ $( ${mod}_provides ) == ${conf[0]} ]]; then
					x=true
					break
				fi
			done

			if ! ${x}; then
				[[ $( type -t ${conf[0]}_check_installed == function ) ]] && ${conf[0]}_check_installed true
				eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)"
			else
				eindent
				${conf[0]}_start ${iface} ; x=$?
				eoutdent
				[[ ${x} == 0 ]] && config_worked=true && continue
			fi
		# We need to test to see if it's an IP address or a function
		# We do this by testing if the 1st character is a digit
		elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]]; then
			x=0
#			if [[ $(type -t address_exists ) == function ]]; then
#				if address_exists ${iface} ${conf[0]} ; then
#					eerror "${conf[0]%%/*} already taken on ${iface}"
#					x=1
#				fi
#			fi
			[[ ${x} == 0 ]] && interface_add_address ${iface} ${conf[@]} ; x=$?
			eend ${x} && config_worked=true && continue
		else
			eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)"
		fi

		if [[ -n ${fallback[config_counter]} ]]; then
			einfo "Trying fallback configuration"
			config[config_counter]=${fallback[config_counter]}
			fallback[config_counter]=''

			# Do we have a fallback route?
			if [[ -n ${fallback_route[config_counter]} ]]; then
				eval "routes_${ifvar}=( "\"\$\{fallback_route\[${config_counter}\]\[@\]\}\"" )"
				fallback_route[config_counter]=''
			fi

			(( config_counter-- )) # since the loop will increment it
			continue
		fi
	done
	eoutdent

	# We return failure if no configuration parameters worked
	${config_worked} || return 1

	# Start any modules with _post_start
	for mod in ${MODULES[@]}; do
		if [[ function == $( type -t ${mod}_post_start ) ]]; then
			${mod}_post_start ${iface} || return 1
		fi
	done

	return 0
}

# bool iface_stop(char *interface)
#
# iface_stop: bring down an interface.  Don't trust information in
# /etc/conf.d/net since the configuration might have changed since
# iface_start ran.  Instead query for current configuration and bring
# down the interface.
#
# However, we are currently reliant on handler and modules specified
# in /etc/conf.d/net
iface_stop() {
	local iface=${1} i aliases need_begin=false mod
	local RC_INDENTATION=${RC_INDENTATION}	# so it will reset after function

	# pre Stop any modules
	for mod in ${MODULES[@]}; do
		[[ $( type -t ${mod}_pre_stop ) == function ]] && ${mod}_pre_stop ${iface}
	done

	einfo "Bringing down ${iface}"
	eindent

	# Collect list of aliases for this interface.
	# List will be in reverse order.
	aliases=$( interface_get_aliases_rev ${iface} )

	# Stop aliases before primary interface.
	# Note this must be done in reverse order, since ifconfig eth0:1 
	# will remove eth0:2, etc.  It might be sufficient to simply remove 
	# the base interface but we're being safe here.
	for i in ${aliases} ${iface}; do
		# Stop all our modules
		for mod in ${MODULES[@]}; do
			[[ $( type -t ${mod}_stop ) == function ]] && ${mod}_stop ${i}
		done

		# A module may have removed the interface
		interface_exists ${iface} || { eend 0; continue; }

		# Delete all the addresses for this alias
		interface_del_addresses ${i}

		# Do final shut down of this alias
		ebegin "Shutting down ${i}"
		interface_iface_stop ${i}
		eend $?
	done

	# post Stop any modules
	for mod in ${MODULES[@]}; do
		# We have already taken down the interface, so no need to error
		[[ $( type -t ${mod}_post_stop ) == function ]] && ${mod}_post_stop ${iface}
	done

	return 0
}

# bool run_start(char *iface)
#
# Brings up ${IFACE}.  Calls preup, iface_start, then postup.
# Returns 0 (success) unless preup or iface_start returns 1 (failure).
# Ignores the return value from postup.
# We cannot check that the device exists ourselves as modules like
# tuntap make create it.
run_start() {
	local iface=${1} ifvar x

	if [[ ${iface} == lo ]]; then
		ebegin "Bringing up ${iface}"
		interface_loopback_create
		eend $?
		return $?
	fi

	# We may not have a loaded module for ${iface}
	# Some users may have "alias natsemi eth0" in /etc/modules.d/foo
	# so we can work with this
	# However, if they do the same with eth1 and try to start it
	# but eth0 has not been loaded then the module gets loaded as
	# eth0.
	# Not much we can do about this :(
	# Also, we cannot error here as some modules - such as bridge
	# create interfaces
	if ! interface_exists ${iface} ; then
		/sbin/modprobe ${iface} &>/dev/null
	fi

	# Setup variables for pre/post to use
	ifvar=$( interface_variable ${iface} )
	local IFACE=${iface} IFVAR=${ifvar}

	# Call user-defined preup function if it exists
	if [[ $( type -t preup ) == function ]]; then
		einfo "Running preup function"
		eindent
		preup ${iface} ; x=$?
		eoutdent
		if [[ ${x} != 0 ]]; then
			eerror "preup ${iface} failed"
			return 1
		fi
	fi

	# Don't let preup modify us
	iface=${1}
	local IFACE=${iface} IFVAR=${ifvar} config

	# If config is set to noop and the interface is up with an address
	# then we don't start it
	eval config=( \"\$\{config_${ifvar}\[@\]\}\" )
	if [[ ${config[0]} == "noop" ]] && interface_is_up ${iface} true ; then
		einfo "Keeping current configuration for ${iface}"
		eend 0
	else
		# Remove noop from the config var
		[[ ${config[0]} == "noop" ]] && eval "config_${ifvar}=( "\"\$\{config\[@\]:1\}\"" )"

		# There may be existing ip address info - so we strip it
		interface_del_addresses ${iface}

		# Start the interface
		if ! iface_start ${iface} ; then
			interface_exists ${iface} && interface_down ${iface}
			eend 1
			return 1
		fi
	fi

	# Call user-defined postup function if it exists
	if [[ $( type -t postup ) == function ]]; then
		einfo "Running postup function"
		eindent
		postup ${iface}
		eoutdent
	fi

	return 0
}

# bool run_stop(char *iface) {
#
# Brings down ${iface}.  If predown call returns non-zero, then
# stop returns non-zero to indicate failure bringing down device.
# In all other cases stop returns 0 to indicate success.
run_stop() {
	local iface=${1} ifvar x

	# Don't run any special shutdown functions for lo
	if [[ ${iface} == lo ]]; then
		ebegin "Shutting down ${iface}"
		interface_iface_stop ${iface}
		eend $?
		return 0
	fi

	# Setup variables for pre/post to use
	ifvar=$( interface_variable ${iface} )
	local IFACE=${iface} IFVAR=${ifvar}

	# Call user-defined predown function if it exists
	if [[ $( type -t predown ) == function ]]; then
		einfo "Running predown function"
		eindent
		predown ${iface} ; x=$?
		eoutdent
		if [[ ${x} != 0 ]]; then
			eend 1 "predown ${iface} failed"
			return 1
		fi
	elif is_net_fs /; then
		eerror "root filesystem is network mounted -- can't stop ${iface}"
		return 1
	fi

	# Don't let predown modify us
	iface=${1}
	local IFACE=${iface} IFVAR=${ifvar}

	iface_stop ${iface} || return 1  # always succeeds, btw

	# Call user-defined postdown function if it exists
	if [[ $( type -t postdown ) == function ]]; then
		einfo "Running postdown function"
		eindent
		postdown ${iface}
		eoutdent
	fi

	return 0
}

# bool run(char *iface, char *cmd)
#
# Main start/stop entry point
# We load modules here and remove any functions that they
# added as we may be called inside the same shell scope for another interface
run() {
	local iface=${1} cmd=${2} r=1 RC_INDENTATION=${RC_INDENTATION}	# so it will reset after function
	local before starting=true
	local -a MODULES mods

	# We need to override the exit function as runscript.sh now checks
	# for it. We need it so we can mark the service as inactive ourselves.
	unset -f exit

	eindent

	unset_functions=${unset_functions:-false}
	${unset_functions} && before="$( typeset -F )"
	[[ ${cmd} == "stop" ]] && starting=false
	if modules_load ${iface} ${starting} ; then
		if [[ ${cmd} == "stop" ]]; then
			# Reverse the module list for stopping
			mods=( "${MODULES[@]}" )
			for ((i = 0; i < ${#mods[@]}; i++)); do
				MODULES[i]=${mods[((${#mods[@]} - i - 1))]}
			done

			run_stop ${iface} && r=0
		else
			run_start ${iface} && r=0
		fi
	fi

	# We need to remove added functions before we quit as other init
	# scripts may be launching afterwards
	${unset_functions} && \
	unset $( diff -U0 <( echo "${before}" ) <( echo "$( typeset -F )" ) 2>/dev/null \
		| awk 'BEGIN { ORS = " "; } /+declare -f/ { print $3; }' ) 2>/dev/null

	return ${r}
}

# bool start(void)
#
# Start entry point so that we only have one function
# which localises variables and unsets functions
start() {
	einfo "Starting ${IFACE}"
	run ${IFACE} start
	return $?
}

# bool stop(void)
#
# Stop entry point so that we only have one function
# which localises variables and unsets functions
stop() {
	einfo "Stopping ${IFACE}"
	run ${IFACE} stop
	return $?
}

# bool restart(void)
#
# Restart entry point
# We do this so as we only have to remove functions when stopping
restart() {
	local unset_functions=true
	service_started "${myservice}" && svc_stop
	unset_functions=false
	svc_start
	return $?
}

# vim:ts=4
