#!/bin/bash
# This is the file "rc" which starts and stops services for the different
# runlevels of the SysV init.
#
# Author:       Winfried Trmper <winni@xpilot.org>
# Version:      0.2
#
# Misc fixes by Tom Lees <tom@lpsg.demon.co.uk>.
# Misc improvements and code rewrite by Martin Schulze <joey@debian.org>
#
# Unlike traditional implementations it avoids the messy scheme with
# expressing the setup through links but reads a central config file
# instead. From a technical point of view both methods are almost
# equivalent.
#
# To be compatible with the common configuration scheme in the Linux-world,
# every script has two states: "on" or "off". The effect of this is that
# once it is switched on, it is never started again when the runlevel changes
# (it is only executed to switch it off if necessary).
#

# The following section is taken from the original rc with slight
# modifications.

# Set onlcr to avoid staircase effect.
stty onlcr 0>&1

debug=0
if [ "$1" = "-d" ]
then
    debug=1
    shift
fi

# Is this done because RUNLEVEL and PREVLEVEL could be read-only?
#
# Now find out what the current and what the previous runlevel are.
runlevel=$RUNLEVEL
  # Get first argument. Set new runlevel to this argument.
[ "$1" != "" ] && runlevel="$1"
  # Is this necessary?
prevlevel=${PREVLEVEL:="N"}
  # Is this necessary?
#export runlevel previous

CFGFILE="/etc/runlevel.conf"
BAKCFG="/etc/runlevel.fallback"
LOCKFILE="/var/lock/runlevel.lock"
TMPFILE="/etc/runlevel.tmp"

true=0
false=1

valid_min_seq=1
valid_max_seq=99

[ $debug -eq 1 ] && echo "rc: $prevlevel -> $runlevel"
  # wait for any lock to vanish (but only when not booting)
i=0
while [ -f "$LOCKFILE" -a "$previous" != "N" ]
do
    read pid < "$LOCKFILE"
    if ! kill -0 $pid &> /dev/null
    then
	echo "$0: found stale lockfile '$LOCKFILE'. Ignoring it." >&2
# restriction on built-in functions ...
#        rm -f "$LOCKFILE"
        break
    fi
    if [ "$i" -gt "10" ]
    then
        echo "Process no. '$pid' is locking the configuration database. Terminating." >&2
        exit 1
    fi
    sleep 2
    let i+=1
done


  # This script is vital so we better keep an old copy of the configuration
  # file as fallsave-configuration. This does not handle a broken config
  # file, though.
if [ ! -f "$CFGFILE" ]
then
    echo "Missing configuration file '$CFGFILE' using fallback config."

      # This could be a link to /var/backup/runlevel.conf.
    if [ ! -f "$BAKCFG" ]
    then
	echo "No configuration file at all. You're in serious trouble now. Aborting."
	exit 1
    fi
    CFGFILE="$BAKCFG"
fi

function is_valid_sequence() {
    if [ $# -ne 1 ]
    then
	return $false
    fi

    case $1 in
    [0-9]|[0-9][0-9]) ;;
    *) return $false ;;
    esac

    if [ $1 -ge $valid_min_seq -a $1 -le $valid_max_seq ]
    then
	return $true
    fi
    return $false
}

function element() {
    local element list IFS


    element="$1"
    case "$element" in
	reboot | R) element=0 ;;
	single | S) element=1 ;;
	halt   | H) element=6 ;;
    esac
	
    [ "$2" = "in" ] && shift
    list="$2"
    [ "$list" = "-" ] && return 1
    [ "$list" = "*" ] && return 0

    IFS=","
    set -- $list
    case $element in
	"$1" | "$2" | "$3" | "$4" | "$5" | "$6" | "$7" | "$8" | "$9")
	    return 0
    esac
    return 1
}

function pushlevel
{
    local IFS list newcmd newlevels i
    newcmd=$1;shift
    newlevels=$1; shift

    for i in *$
    do
	cmd=${i%:*}
	if [ "$cmd" = "$newcmd" ]
	then
	    echo -n "$i,$newlevels "
	else
	    echo -n "$i "
	fi
    done
}


  # CMDLIST ensures scripts are killed in reversed order
CMDLIST="set centerline=here"
STARTCMD=""
STOPCMD=""
  # Experimental: To tell the scripts they are not called manually.
  # (should be unset in init.d-scripts)
CALL_FROM_RC="yes"

[ $debug -eq 1 ] && echo "Reading configuration file $CFGFILE."

case $runlevel in
0|6)	start=stop; stop=stop;;
*)	start=start; stop=stop;;
esac

while read  SORT_NO  OFF_LEVELS  ON_LEVELS  CMD  OPTIONS
do
    case "$SORT_NO" in
	"#*"|""|"#") continue ;;
    esac
    [ ! -f "$CMD" ] && continue
    is_valid_sequence "$SORT_NO" || continue

    # currently OPTIONS is completely ignored ... we _could_ pass them to the
    # init-script after "start" or "stop".

    [ "$ON_LEVELS" != "-" ] && GEN_STARTLIST=`pushlevel $CMD $ON_LEVELS $GEN_STARTLIST`
    [ "$OFF_LEVELS" != "-" ] && GEN_STOPLIST="$GEN_STOPLIST$CMD:$OFF_LEVELS "

    element "$runlevel" in "$ON_LEVELS" && STARTLIST="$STARTLIST$CMD "
    element "$runlevel" in "$OFF_LEVELS" && STOPLIST="$STOPLIST$CMD "

done < $CFGFILE


# First, run the KILL scripts.
for CMD in $STOPLIST
do
    if [ "$prevlevel" != "N" ]
    then
	case "$CMD" in
	*.sh)	CMDLIST="$CMDLIST; . $CMD $stop" ;;
	*)	[ -x "$CMD" ] && CMDLIST="$CMDLIST; $CMD $stop" ;;
	esac
    fi
done

# Then look at the start scripts
for comb in $STARTLIST
do
    CMD=${comb%:*}
    if [ "$prevlevel" != "N" ]
    then
	level=${comb#*:}
	if element "$prevlevel" in "$level"
	then
	    continue
	fi
    fi

    case "$CMD" in
    *.sh)	CMDLIST="$CMDLIST; . $CMD $start" ;;
    *)	[ -x "$CMD" ] && CMDLIST="$CMDLIST; $CMD $start" ;;
    esac
done

# Execute the commands collected above
if [ $debug -eq 1 ]
then
    echo "$CMDLIST"
else
    sh -c "$CMDLIST"
fi
