#! /bin/sh

# This is the file "r2d2" which starts and stops services for the different
# runlevels of the SysV init scheme.
#
# Author:       Winfried Trmper <winni@xpilot.org>
# Version:      0.5.8
#
# Unlike traditional implementations it avoids the messy scheme with
# expressing the setup through the filesystem, 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).
#

CFGFILE="/etc/rc.d/Runlevel.cfg"
BAKCFG="/etc/rc.d/Runlevel.bak"   # link to /var/backup/Runlevel.cfg ?
LOCKFILE="/var/lock/runlevel.lck"

# Since version 0.5.8 r2d2 features a searchpath for scripts.
# This has the potential to unify the configuration file for
# all distributions and the related Linux-documentation.
INITD_PATH="/etc/rc.d"

trap ":" INT QUIT TSTP
[ -n "$1" ] && RUNLEVEL="$1"
[ -z "$PREVLEVEL" ] && PREVLEVEL="N"

  # wait for any lock to vanish (but not when booting)
i=0
while [ -f "$LOCKFILE" -a "$PREVLEVEL" != "N" ]
do
    [ ! -O "$LOCKFILE" ] && break  # security-hack
    read pid < "$LOCKFILE"
    if ! kill -0 "$pid" &> /dev/null
    then
	echo "$0: found stale lockfile '$LOCKFILE'. Ignoring it." >&2
#	/bin/rm -f "$LOCKFILE"
        break
    fi
    if [ "$i" -gt "9" ]
    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.
  # Keep in mind that it is not the purpose of _this_ script to make backup
  # copies, we're in read-only mode ..

if [ ! -f "$CFGFILE" ]
then
    echo "Missing configuration file '$CFGFILE' using fallback config."
    if [ ! -f "$BAKCFG" ]
    then
	echo "No configuration file at all. You're in serious trouble now. Aborting."
	exit 1
    fi
    CFGFILE="$BAKCFG"  #else
fi


function element() {
    local element list IFS

    element="$1"
    case "$element" in
        boot   | B) 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
}


  # CMDLIST ensures scripts are killed in reverse order
CMDLIST="set middle=here"



while read  OFF_LEVELS  ON_LEVELS  CMD  EXTRA_OPTIONS
do
    case "$OFF_LEVELS" in
	\#* | "") continue ;;
    esac
    found=""
    case "$CMD" in
	/*)
	    [ -x "$CMD" ] && found="t"
	    ;;
	*)
	    old_IFS="$IFS"
	    IFS=":"
	    for prefix in $INITD_PATH
	    do
		if [ -x "$prefix/$CMD" ]
		then
		    CMD="$prefix/$CMD"
		    found="t"
		    break
		fi
	    done
	    IFS="$old_IFS"
	    ;;
    esac
    if [ -z "$found" ]
    then
	echo "r2d2: $CMD not executable or not found in $INITD_PATH."
	echo "      Ignoring entry. Edit $CFGFILE to fix this."
	continue
    fi

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


      # start only if CMD was not switched on in previous runlevel
    if element "$RUNLEVEL" in "$ON_LEVELS" \
    && ! element "$PREVLEVEL" in "$ON_LEVELS"
    then
	CMDLIST="$CMDLIST; $CMD start"
	continue
    fi

     # no stopping needed while booting
    [ "$PREVLEVEL" = "N" ] && continue

      # stop only if CMD was not switched off in previous runlevel
    if element "$RUNLEVEL" in "$OFF_LEVELS" \
    && ! element "$PREVLEVEL" in "$OFF_LEVELS"
    then
	  # Command is inserted at the beginning to implement
          # "stopping the last service first"
	CMDLIST="$CMD stop; $CMDLIST"
    fi

done < $CFGFILE

# Execute the commands collected above
eval $CMDLIST
