#!/bin/sh #---------------------------------------------------------------------------- # /etc/boot.d/base-helper # # Creation: 2004-07-04 jw5 # Last Update: $Id$ #---------------------------------------------------------------------------- : ${resource_lock_timeout:=30} #---------------------------------------------------------------------------- # some useful functions # do_modprobe module params # do_insmod module params # do_insmod_once module params # do_insmod_if_exists module_path module params # module_path is relativ to /lib/modules/kernel_version # do_insmod_if_exists_once module_path module params # begin_script name # end_script name # get_rc_variable variable name file # mk_writable file # unique elements # returns a unique list in $list # set_error # sets an error flag and show an error message # is_error # checks and resets error flag set by set_error # clear_error # clears error flag set by set_error # log_error # show an error message and write it to /bootmsg.txt # translate_ip_net [ []] # translates a config value like IP_NET_1, calls set_error # if an error happens # translate_ip_dev () # translate a symbolic name the corresponding device name; # invokes set_error if an unknown symbolic name is passed # translate_ip_dev_reverse () # reverse lookup for device names, delivers the symbolic name # for a device #---------------------------------------------------------------------------- begin_push () { tos=`/usr/bin/expr $tos + 1` eval "stack_$tos=\"${1}\"" } begin_pop () { eval "ret=\"\$stack_${tos}\"" tos=`/usr/bin/expr $tos - 1` } begin_script () { # disable debugging set +x eval "tmp_debug=\"\$stack_${tos}\"" # push name and debug state of last script begin_push "$SCRIPT" begin_push "$SCRIPT_MSG" begin_push "$debug_active" SCRIPT="$1" SCRIPT_MSG="$2" if echo $1 | grep -q '[[:space:]]'; then log_error "script label contains spaces: '$1'" SCRIPT=$script fi case "x$SCRIPT_MSG" in x) SCRIPT_MSG="executing $SCRIPT ($script)" ;; *) SCRIPT_MSG="$SCRIPT_MSG ($script)" ;; esac /usr/local/bin/colecho "-> $SCRIPT_MSG" gn shell_dbg_opt= eval debug='$'$SCRIPT'_DO_DEBUG' case "$debug" in yes) debug_active='yes' shell_dbg_opt=-x set -x ;; *) case "$tmp_debug" in yes) debug_active=yes shell_dbg_opt=-x set -x ;; esac ;; esac } end_script () { # disable debugging set +x /usr/local/bin/colecho "-> finished $SCRIPT_MSG" gn begin_pop debug_state="$ret" begin_pop SCRIPT_MSG="$ret" begin_pop SCRIPT="$ret" case "$debug_active" in yes) case "$debug_state" in yes) set -x ;; *) debug_active='no' shell_dbg_opt= set +x ;; esac esac } do_log () { : ${base_log_file:=/bootmsg.txt} : ${base_colecho:=colecho} # kristov: disable colours when not called from a boot script (ticket #209) [ "$booting" = "yes" ] || log_col="" case $# in 0) while read line; do $base_colecho "$log_prefix $line" $log_col 1>&2 # kristov: don't log into file when not called from a boot script if [ "$booting" = "yes" ]; then case $log_boot$cons_boot in yesyes) echo "($script) $log_prefix $line" >> $base_log_file ;; esac fi done ;; *) $base_colecho "$log_prefix $*" $log_col 1>&2 # kristov: don't log into file when not called from a boot script if [ "$booting" = "yes" ]; then case $log_boot$cons_boot in yesyes) echo "($script) $log_prefix $*" >> $base_log_file ;; esac fi ;; esac } log_info() { log_boot= log_prefix=" " log_col=$2 : ${log_col:=gn} case "$1" in cy) log_col=$1 ; shift ;; esac case $# in 0) do_log ;; *) do_log "$@" ;; esac return 0 } log_warn() { log_boot=yes log_prefix="WARN:" log_col=rd do_log "$@" return 0 } log_error() { log_boot=yes log_prefix="ERR:" log_col="br x br" do_log "$@" return 1 } begin_probe () { probe='yes' } end_probe () { probe='' } do_insmod () { log_error "invoking insmod (directly or via one of the do_insmod variants), please try to eleminate it or replace it with do_modprobe" /sbin/insmod $* > /tmp/insmod.res.$$ 2>&1 res=$? if [ ! "$probe" -o "$debug_active" = 'yes' ] then case $res in 0) ;; *) log_error "insmod $module $* failed!" log_error < /tmp/insmod.res.$$ ;; esac cat /tmp/insmod.res.$$ fi rm -f /tmp/insmod.res.$$ return $res } do_modprobe () { quiet= if [ "$1" = "-q" ] then quiet=-q shift fi /sbin/modprobe $* > /tmp/insmod.res.$$ 2>&1 res=$? case $res in 0) ;; *) if [ -z "$quiet" ] then log_error "modprobe $* failed!" log_error < /tmp/insmod.res.$$ fi ;; esac [ -z "$quiet" ] && cat /tmp/insmod.res.$$ rm -f /tmp/insmod.res.$$ return $res } do_modprobe_if_exists () { quiet= if [ "$1" = "-q" ] then quiet=-q shift fi local kernel_version=$(uname -r) if ! grep -q "^alias $2 " /lib/modules/$kernel_version/modules.alias then [ -f /lib/modules/$kernel_version/$1/$2.$mod_ext ] || return 0 fi shift do_modprobe $quiet $* } do_insmod_once () { grep -q "^$1 " /proc/modules && return 0 do_insmod $* # returns the result of do_insmod } do_insmod_if_exists () { local kernel_version=$(uname -r) if ! grep -q "^alias $2 " /lib/modules/$kernel_version/modules.alias then [ -f /lib/modules/$kernel_version/$1/$2.$mod_ext ] || return 0 fi shift do_insmod $* } do_insmod_if_exists_once () { grep -q "^$2 " /proc/modules && return 0 do_insmod_if_exists $* } mk_writable () { file=$1 case $file in /opt/*) log_error "mk_writable $file invoked, please remove /opt prefix" file=${file#/opt} ;; esac if [ -e $file ] then if [ -L $file ] then cp $file /tmp/mkwrt.$$ rm -f $file mv /tmp/mkwrt.$$ $file fi if [ ! -w $file ] then chmod +w $file fi fi } get_rc_variable() { local var=$1 local name=$2 local file=$3 # sed expression # s/^${name}='(.*)'/\1/p eval "$var='`sed -n \"s/^${name}='\(.*\)'/\1/p\" $file`'" } unique () { list='' for i in $* do case $list in *$i*) ;; *) list="$list $i" ;; esac done } set_error () { case "x$1" in x) ;; *) log_error "$1" ;; esac base_error='yes' return 1 } is_error () { case "x$base_error" in x) return 1 ;; *) base_error= ; return 0 ;; esac } clear_error () { base_error= } get_value () { local name=$1 eval res=\$$name case "x$res" in x) if ! set | grep -q "$name=" then set_error "unknown variable '$name'" fi ;; esac } # usage: lookup_name # parameter: either a FullQualifiedDomainName or an alias name # returns: nothing # errorcodes: 0 success - successfully resolved name to ip address # 1 error - no matching host record found # # description: Tries to resolve either an FQDN or an alias to an IP address. # Uses /etc/hosts and /etc/hosts.d/hosts* as source. # MAGIC done by this function: # The result will be stored in an global variable "res". lookup_name () { local name="$1" if [ -f /etc/dnsmasq.d/cname.conf ] then local real=`grep "=${name}," /etc/dnsmasq.d/cname.conf | cut -d "=" -f2 | cut -d "," -f2` [ -z "$real" ] || name="$real" fi local ip=`sed -n -e "/[[:space:]]${name}\([[:space:]]\|$\)/s/^\([0-9\.]\+\)[[:space:]].*/\1/p" /etc/hosts.d/hosts*` [ -z "$ip" ] && return 1 set $ip [ "$2" ] && log_error "lookup_name: multiple IPv4 addresses for $name - $ip" res=$1 return 0 } #---------------------------------------------------------------------------- # Translates a net variable like IP_NET_1. Returns 0 on success and # non-zero on failure. Stores the translation result in the variable 'res'. # # If the network is configured via DHCP and an address has been received, the # associated subnet is returned. Otherwise nothing is returned. # # $1 = name of net variable #---------------------------------------------------------------------------- do_translate_ip_net() { get_value $1 is_error && return 1 local net="$res" if [ "$net" = "dhcp" ] then potentially_dynamic=1 get_value $1_DEV is_error && return 1 if [ -f /var/run/dhcpv4.nets ] then res=$(sed -n "/^$res=/s/^$res=//p" /var/run/dhcpv4.nets) else res= fi if [ ! -n "$res" ] then set_error return 1 else return 0 fi else return 0 fi } #---------------------------------------------------------------------------- # Checks if a config value like IP_NET_1 can be translated. Returns 0 on # success and a non-zero value on failure. A translation may be impossible if # e.g. the source value is associated with a DHCP connection which is not # active yet. # # $1 = value to translate #---------------------------------------------------------------------------- can_translate_ip_net() { tin_param="$1" case $tin_param in *.*.*.* | none | default | pppoe | any | IP_NET_*_DEV | IP_ROUTE_*) return 0 ;; dynamic) [ -f /var/run/dynamic.ip ] ;; IP_NET_*_IPADDR) can_translate_ip_net ${tin_param%_IPADDR} ;; IP_NET_*) do_translate_ip_net $tin_param res=$? clear_error return $res ;; @*) lookup_name ${tin_param#@} ;; *) return 1 ;; esac } translate_ip_net () { local tin_param="$1" local var_name=$2 local var=$3 potentially_dynamic= case $tin_param in ip_net_* | ip_route_*) log_error "translate_ip_net: $tin_param ist not support anymore, use the real upper case names" tin_param=`echo $tin_param | tr a-z A-Z` ;; esac case $tin_param in *.*.*.* | none | default | pppoe) res=$tin_param ;; dynamic) potentially_dynamic=1 if [ -f /var/run/dynamic.ip ] then res=$(cat /var/run/dynamic.ip) else set_error "no dynamic IPv4 address available" fi ;; any) res=0.0.0.0/0 ;; IP_NET_*_IPADDR) do_translate_ip_net ${tin_param%_IPADDR} res=${res%/*} ;; IP_NET_*) do_translate_ip_net $tin_param ;; IP_ROUTE_*) get_value $tin_param set $res res=$1 case $res in 0.0.0.0/0) set_error "you can't use a default route entry in a packet filter rule" ;; esac ;; @*) if ! lookup_name ${tin_param#@} then set_error "unable to lookup name $tin_param" fi ;; *) # # what is this case for? # if echo $tin_param | grep -q '^[0-9,]\+$' then res="$tin_param" else set_error fi ;; esac if is_error then if [ "$var_name" ]; then log_error "Unable to translate value '$tin_param' contained in $var_name." else set_error "invalid value '$tin_param' in translate_ip_net ()" fi clear_error return 1 else [ "$var" ] && eval $var="\"$res\"" return 0 fi } #---------------------------------------------------------------------------- # Translates a symbolic device name (e.g. pppoe) into the real interface (e.g. # ppp0). Invokes set_error when the symbolic name is unknown. # # $1 = symbolic network device to translate # $2 = name of variable receiving the translated value # $3 = if nonzero, nonexisting interfaces are ignored (this is e.g. used for # the firewall, as the firewall accepts interface _expressions_ (e.g. # ppp+) and may reference interfaces that will be created later (e.g. # ppp interfaces dynamically created by pppoe_server) #---------------------------------------------------------------------------- translate_ip_dev () { local tid_tif=$1 local tid_var=$2 local tid_ignore=$3 local tid_real_if= if [ ! "$tid_var" ]; then log_error "translate_ip_dev: missing variable name" return fi net_alias_lookup_dev $tid_tif $tid_var TID_ && return case $tid_tif in IP_NET_*_DEV) translate_ip_net $tid_tif && tid_real_if=$res ;; circuit-*) if [ -f /var/run/$tid_tif ]; then read tid_real_if < /var/run/$tid_tif else set_error " Error: unknown circuit $tid_tif" fi ;; pppoe) if [ -f /var/run/pppoe-device ]; then read tid_real_if < /var/run/pppoe-device else set_error " Error: unknown circuit pppoe" fi ;; *) tid_real_if=$tid_tif ;; esac if [ "$tid_real_if" ]; then case $tid_real_if in *+) ;; # ignore wildcard names *) if grep -q "^[[:space:]]*\<$tid_real_if\>" /proc/net/dev then net_alias_add $tid_tif $tid_real_if TID_ elif [ -z "$tid_ignore" ] then log_error "translate_ip_dev '$tid_tif': device '$tid_real_if' not present" fi ;; esac [ "$tid_var" ] && eval $tid_var=$tid_real_if fi } translate_ip_dev_reverse () { net_alias_lookup_name $1 $2 TID_ } wait_for_mdev () { sleep 1 } # Returns all IPv4 addresses currently assigned to the router. The addresses # are returned together with a CIDR-style subnet mask. get_local_ipv4_addresses() { local i res for i in $(seq 1 ${IP_NET_N:-0}) do if do_translate_ip_net IP_NET_${i} then echo $res else clear_error fi done return 0 } # Returns all IPv4 subnets currently assigned to the router. The subnets are # returned in CIDR style. get_local_ipv4_subnets() { local addr for addr in $(get_local_ipv4_addresses) do echo $(netcalc network $addr)/${addr#*/} done return 0 } # Locks a resource. # # $1 = resource to be locked # #2 = name of caller # Returns: 0 on success and non-zero on failure lock_resource() { local pidfile="/var/lock/fli4l-$1.$$" local lockfile="/var/lock/fli4l-$1.lock" local i=0 while ! ln -s "$pidfile" "$lockfile" 2>/dev/null do sleep 1 i=$((i + 1)) if [ $i -eq $resource_lock_timeout ] then pid=$(readlink "$lockfile" | sed 's/.*\.\([0-9]\+\)$/\1/') owner=$(cat /proc/$pid/comm) log_error "$2: failed to lock resource $1 within $resource_lock_timeout seconds: already locked by $owner[$pid]" return 1 fi done return 0 } # Unlocks a queue. # # $1 = resource to be unlocked # #2 = name of caller # Returns: always 0 unlock_resource() { local lockfile="/var/lock/fli4l-$1.lock" rm -f "$lockfile" return 0 } . /etc/boot.d/volume.inc tos=0 SCRIPT='unknown' SCRIPT_MSG='none' base_helper='yes' mod_ext=ko case "`ls -l /proc/$$/fd/1 2> /dev/null`" in */dev/console*) cons_boot=yes ;; esac : ${net_alias:=/etc/boot.d/network_aliases} . $net_alias net_alias_init net_alias_lookup_dev any any TID_ || net_alias_add any any TID_