openrc/sh/net.sh

664 lines
15 KiB
Bash
Executable File

#!/sbin/runscript
# Copyright 1999-2007 Gentoo Foundation
# Copyright 2007 Roy Marples
# All rights reserved
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
MODULESDIR="${RC_LIBDIR}/net"
MODULESLIST="${RC_SVCDIR}/nettree"
_config_vars="config routes"
[ -z "${IN_BACKGROUND}" ] && IN_BACKGROUND=false
description="Configures network interfaces."
# Handy var so we don't have to embed new lines everywhere for array splitting
__IFS="
"
depend() {
local IFACE=${SVCNAME#*.}
local IFVAR=$(echo -n "${IFACE}" | sed -e 's/[^[:alnum:]]/_/g')
need localmount
after bootmisc
provide net
case "${IFACE}" in
lo|lo0) ;;
*)
after net.lo net.lo0
local prov=
eval prov=\$RC_NEED_${IFVAR}
[ -n "${prov}" ] && need ${prov}
eval prov=\$RC_USE_${IFVAR}
[ -n "${prov}" ] && use ${prov}
eval prov=\$RC_BEFORE_${IFVAR}
[ -n "${prov}" ] && before ${prov}
eval prov=\$RC_AFTER_${IFVAR}
[ -n "${prov}" ] && after ${prov}
eval prov=\$RC_PROVIDE_${IFVAR}
[ -n "${prov}" ] && provide ${prov}
;;
esac
}
_shell_var() {
echo -n "$1" | sed -e 's/[^[:alnum:]]/_/g'
}
# Support bash arrays - sigh
_get_array() {
local _a=
if [ -n "${BASH}" ] ; then
case "$(declare -p "$1" 2>/dev/null)" in
"declare -a "*)
eval "set -- \"\${$1[@]}\""
for _a in "$@"; do
printf "%s\n" "${_a}"
done
return 0
;;
esac
fi
eval _a=\$$1
printf "%s" "${_a}"
printf "\n"
[ -n "${_a}" ]
}
# Flatten bash arrays to simple strings
_flatten_array() {
if [ -n "${BASH}" ] ; then
case "$(declare -p "$1" 2>/dev/null)" in
"declare -a "*)
eval "set -- \"\${$1[@]}\""
for x in "$@"; do
printf "'%s' " "$(printf "$x" | sed "s:':'\\\'':g")"
done
return 0
;;
esac
fi
eval _a=\$$1
printf "%s" "${_a}"
printf "\n"
[ -n "${_a}" ]
}
_wait_for_carrier() {
local timeout= efunc=einfon
_has_carrier && return 0
eval timeout=\$carrier_timeout_${IFVAR}
timeout=${timeout:-${carrier_timeout:-5}}
# Incase users don't want this nice feature ...
[ ${timeout} -le 0 ] && return 0
[ "${RC_PARALLEL}" = "yes" ] && efunc=einfo
${efunc} "Waiting for carrier (${timeout} seconds) "
while [ ${timeout} -gt 0 ] ; do
sleep 1
if _has_carrier ; then
[ "${efunc}" = "einfon" ] && echo
eend 0
return 0
fi
timeout=$((${timeout} - 1))
[ "${efunc}" = "einfon" ] && printf "."
done
[ "${efunc}" = "einfon" ] && echo
eend 1
return 1
}
_netmask2cidr() {
local i= len=0
local IFS=.
for i in $1; do
while [ ${i} != "0" ] ; do
len=$((${len} + ${i} % 2))
i=$((${i} >> 1))
done
done
echo "${len}"
}
_configure_variables() {
local var= v= t=
for var in ${_config_vars} ; do
local v=
for t in "$@" ; do
eval v=\$${var}_${t}
if [ -n "${v}" ] ; then
eval ${var}_${IFVAR}=\$${var}_${t}
continue 2
fi
done
done
}
_show_address() {
einfo "received address $(_get_inet_address "${IFACE}")"
}
# Basically sorts our modules into order and saves the list
_gen_module_list() {
local x= f= force=$1
if ! ${force} && [ -s "${MODULESLIST}" -a "${MODULESLIST}" -nt "${MODULESDIR}" ] ; then
local update=false
for x in "${MODULESDIR}"/* ; do
[ -e "${x}" ] || continue
if [ "${x}" -nt "${MODULESLIST}" ] ; then
update=true
break
fi
done
${update} || return 0
fi
einfo "Caching network module dependencies"
# Run in a subshell to protect the main script
(
after() {
eval ${MODULE}_after="\"\${${MODULE}_after}\${${MODULE}_after:+ }$*\""
}
before() {
local mod=${MODULE}
local MODULE=
for MODULE in "$@" ; do
after "${mod}"
done
}
program() {
if [ "$1" = "start" -o "$1" = "stop" ] ; then
local s="$1"
shift
eval ${MODULE}_program_${s}="\"\${${MODULE}_program_${s}}\${${MODULE}_program_${s}:+ }$*\""
else
eval ${MODULE}_program="\"\${${MODULE}_program}\${${MODULE}_program:+ }$*\""
fi
}
provide() {
eval ${MODULE}_provide="\"\${${MODULE}_provide}\${${MODULE}_provide:+ }$*\""
local x
for x in $* ; do
eval ${x}_providedby="\"\${${MODULE}_providedby}\${${MODULE}_providedby:+ }${MODULE}\""
done
}
for MODULE in "${MODULESDIR}"/* ; do
sh -n "${MODULE}" || continue
. "${MODULE}" || continue
MODULE=${MODULE#${MODULESDIR}/}
MODULE=${MODULE%.sh}
eval ${MODULE}_depend
MODULES="${MODULES} ${MODULE}"
done
VISITED=
SORTED=
visit() {
case " ${VISITED} " in
*" $1 "*) return ;;
esac
VISITED="${VISITED} $1"
eval AFTER=\$${1}_after
for MODULE in ${AFTER} ; do
eval PROVIDEDBY=\$${MODULE}_providedby
if [ -n "${PROVIDEDBY}" ] ; then
for MODULE in ${PROVIDEDBY} ; do
visit "${MODULE}"
done
else
visit "${MODULE}"
fi
done
eval PROVIDE=\$${1}_provide
for MODULE in ${PROVIDE} ; do
visit "${MODULE}"
done
eval PROVIDEDBY=\$${1}_providedby
[ -z "${PROVIDEDBY}" ] && SORTED="${SORTED} $1"
}
for MODULE in ${MODULES} ; do
visit "${MODULE}"
done
printf "" > "${MODULESLIST}"
i=0
for MODULE in ${SORTED} ; do
eval PROGRAM=\$${MODULE}_program
eval PROGRAM_START=\$${MODULE}_program_start
eval PROGRAM_STOP=\$${MODULE}_program_stop
eval PROVIDE=\$${MODULE}_provide
echo "module_${i}='${MODULE}'" >> "${MODULESLIST}"
echo "module_${i}_program='${PROGRAM}'" >> "${MODULESLIST}"
echo "module_${i}_program_start='${PROGRAM_START}'" >> "${MODULESLIST}"
echo "module_${i}_program_stop='${PROGRAM_STOP}'" >> "${MODULESLIST}"
echo "module_${i}_provide='${PROVIDE}'" >> "${MODULESLIST}"
i=$((${i} + 1))
done
echo "module_${i}=" >> "${MODULESLIST}"
)
return 0
}
_load_modules() {
local starting=$1 mymods=
# Ensure our list is up to date
_gen_module_list false
if ! . "${MODULESLIST}" ; then
_gen_module_list true
. "${MODULESLIST}"
fi
MODULES=
if [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] ; then
eval mymods=\$modules_${IFVAR}
[ -z "${mymods}" ] && mymods=${modules}
fi
local i=-1 x= mod= f= provides=
while true ; do
i=$((${i} + 1))
eval mod=\$module_${i}
[ -z "${mod}" ] && break
[ -e "${MODULESDIR}/${mod}.sh" ] || continue
eval set -- \$module_${i}_program
if [ -n "$1" ] ; then
x=
for x in "$@" ; do
[ -x "${x}" ] && break
done
[ -x "${x}" ] || continue
fi
if ${starting} ; then
eval set -- \$module_${i}_program_start
else
eval set -- \$module_${i}_program_stop
fi
if [ -n "$1" ] ; then
x=
for x in "$@" ; do
[ -x "${x}" ] && break
done
[ -x "${x}" ] || continue
fi
eval provides=\$module_${i}_provide
if ${starting} ; then
case " ${mymods} " in
*" !${mod} "*) continue ;;
*" !${provides} "*) [ -n "${provides}" ] && continue ;;
esac
fi
MODULES="${MODULES}${MODULES:+ }${mod}"
# Now load and wrap our functions
if ! . "${MODULESDIR}/${mod}.sh" ; then
eend 1 "${SVCNAME}: error loading module \`${mod}'"
exit 1
fi
[ -z "${provides}" ] && continue
# Wrap our provides
local f=
for f in pre_start start post_start ; do
eval "${provides}_${f}() { type ${mod}_${f} >/dev/null 2>/dev/null || return 0; ${mod}_${f} \"\$@\"; }"
done
eval module_${mod}_provides="${provides}"
eval module_${provides}_providedby="${mod}"
done
# Wrap our preferred modules
for mod in ${mymods} ; do
case " ${MODULES} " in
*" ${mod} "*)
eval x=\$module_${mod}_provides
[ -z "${x}" ] && continue
for f in pre_start start post_start ; do
eval "${x}_${f}() { type ${mod}_${f} >/dev/null 2>/dev/null || return 0; ${mod}_${f} \"\$@\"; }"
done
eval module_${x}_providedby="${mod}"
;;
esac
done
# Finally remove any duplicated provides from our list if we're starting
# Otherwise reverse the list
local LIST="${MODULES}" p=
MODULES=
if ${starting} ; then
for mod in ${LIST} ; do
eval x=\$module_${mod}_provides
if [ -n "${x}" ] ; then
eval p=\$module_${x}_providedby
[ "${mod}" != "${p}" ] && continue
fi
MODULES="${MODULES}${MODULES:+ }${mod}"
done
else
for mod in ${LIST} ; do
MODULES="${mod}${MODULES:+ }${MODULES}"
done
fi
veinfo "Loaded modules: ${MODULES}"
}
_load_config() {
local config="$(_get_array "config_${IFVAR}")"
local fallback="$(_get_array fallback_${IFVAR})"
if [ "${IFACE}" = "lo" -o "${IFACE}" = "lo0" ] ; then
[ "${config}" != "null" ] && config="127.0.0.1/8
${config}"
else
if [ -z "${config}" ] ; then
ewarn "No configuration specified; defaulting to DHCP"
config="dhcp"
fi
fi
# We store our config in an array like vars
# so modules can influence it
config_index=0
local IFS="$__IFS"
for cmd in ${config}; do
eval config_${config_index}="'${cmd}'"
config_index=$((${config_index} + 1))
done
# Terminate the list
eval config_${config_index}=
config_index=0
for cmd in ${fallback}; do
eval fallback_${config_index}="'${cmd}'"
config_index=$((${config_index} + 1))
done
# Terminate the list
eval fallback_${config_index}=
# Don't set to zero, so any net modules don't have to do anything extra
config_index=-1
}
start() {
local IFACE=${SVCNAME#*.} oneworked=false module=
local IFVAR=$(_shell_var "${IFACE}") cmd= metric=0 our_metric=$metric
einfo "Bringing up interface ${IFACE}"
eindent
if [ -z "${MODULES}" ] ; then
local MODULES=
_load_modules true
fi
# We up the iface twice if we have a preup to ensure it's up if
# available in preup and afterwards incase the user inadvertently
# brings it down
if type preup >/dev/null 2>/dev/null ; then
_up 2>/dev/null
ebegin "Running preup"
eindent
preup || return 1
eoutdent
fi
_up 2>/dev/null
for module in ${MODULES} ; do
if type "${module}_pre_start" >/dev/null 2>/dev/null ; then
if ! ${module}_pre_start ; then
eend 1
exit 1
fi
fi
done
if ! _exists ; then
eerror "ERROR: interface ${IFACE} does not exist"
eerror "Ensure that you have loaded the correct kernel module for your hardware"
return 1
fi
if ! _wait_for_carrier ; then
if service_started devd ; then
ewarn "no carrier, but devd will start us when we have one"
mark_service_inactive "${SVCNAME}"
else
eerror "no carrier"
fi
return 1
fi
local config= config_index=
_load_config
config_index=0
if [ -n "${our_metric}" ] ; then
metric=${our_metric}
elif [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] ; then
metric=$((${metric} + $(_ifindex)))
fi
while true ; do
eval config=\$config_${config_index}
[ -z "${config}" ] && break
set -- "${config}"
ebegin $1
eindent
case "$1" in
noop)
if [ -n "$(_get_inet_address)" ] ; then
oneworked=true
break
fi
;;
null) : ;;
[0-9]*|*:*) _add_address ${config} ;;
*)
if type "${config}_start" >/dev/null 2>/dev/null ; then
"${config}"_start
else
eerror "nothing provides \`${config}'"
fi
;;
esac
if eend $? ; then
oneworked=true
else
eval config=\$fallback_${config_index}
if [ -n "${config}" ] ; then
eoutdent
ewarn "Trying fallback configuration ${config}"
eindent
eval config_${config_index}=\$config
unset fallback_${config_index}
config_index=$((${config_index} - 1))
fi
fi
eoutdent
config_index=$((${config_index} + 1))
done
if ! ${oneworked} ; then
if type failup >/dev/null 2>/dev/null ; then
ebegin "Running failup"
eindent
failup
eoutdent
fi
return 1
fi
local hidefirstroute=false first=true
local routes="$(_get_array "routes_${IFVAR}")"
if [ "${IFACE}" = "lo" -o "${IFACE}" = "lo0" ] ; then
if [ "${config_0}" != "null" ]; then
routes="127.0.0.0/8 via 127.0.0.1
${routes}"
hidefirstroute=true
fi
fi
local IFS="$__IFS"
for cmd in ${routes}; do
unset IFS
if ${first}; then
first=false
einfo "Adding routes"
fi
eindent
ebegin ${cmd}
# Work out if we're a host or a net if not told
case "${cmd}" in
*" -net "*|*" -host "*) ;;
*" netmask "*) cmd="-net ${cmd}" ;;
*)
case "${cmd%% *}" in
*.*.*.*/32) cmd="-host ${cmd}" ;;
*.*.*.*/*|0.0.0.0|default) cmd="-net ${cmd}" ;;
*) cmd="-host ${cmd}" ;;
esac
;;
esac
if ${hidefirstroute} ; then
_add_route ${cmd} >/dev/null 2>/dev/null
hidefirstroute=false
else
_add_route ${cmd} >/dev/null
fi
eend $?
eoutdent
done
unset IFS
for module in ${MODULES} ; do
if type "${module}_post_start" >/dev/null 2>/dev/null ; then
if ! ${module}_post_start ; then
eend 1
exit 1
fi
fi
done
if type postup >/dev/null 2>/dev/null ; then
ebegin "Running postup"
eindent
postup
eoutdent
fi
return 0
}
stop() {
local IFACE=${SVCNAME#*.} module=
local IFVAR=$(_shell_var "${IFACE}") opts=
einfo "Bringing down interface ${IFACE}"
eindent
if [ -z "${MODULES}" ] ; then
local MODULES=
_load_modules false
fi
if type predown >/dev/null 2>/dev/null ; then
ebegin "Running predown"
eindent
predown || return 1
eoutdent
else
if is_net_fs /; then
eerror "root filesystem is network mounted -- can't stop ${IFACE}"
return 1
fi
fi
for module in ${MODULES} ; do
if type "${module}_pre_stop" >/dev/null 2>/dev/null ; then
if ! ${module}_pre_stop ; then
eend 1
exit 1
fi
fi
done
for module in ${MODULES} ; do
if type "${module}_stop" >/dev/null 2>/dev/null ; then
${module}_stop
fi
done
# Only delete addresses for non PPP interfaces
if ! type is_ppp >/dev/null 2>/dev/null || ! is_ppp ; then
_delete_addresses "${IFACE}"
fi
for module in ${MODULES} ; do
if type "${module}_post_stop" >/dev/null 2>/dev/null ; then
${module}_post_stop
fi
done
[ "${IN_BACKGROUND}" != "true" ] && \
[ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] && \
_down 2>/dev/null
[ -x /sbin/resolvconf ] && resolvconf -d "${IFACE}"
if type postdown >/dev/null 2>/dev/null ; then
ebegin "Running postdown"
eindent
postdown
eoutdent
fi
return 0
}
# vim: set ts=4 sw=4 :