#!/bin/sh
#/
#/ \file
#/
#/ \brief       run command script for specified runlevel
#/
#/ \author      Arthur
#/
#/ \date        November 20, 2012
#/
#/ \version     0.2
#/


################################################################################
#                                                                              #
#                              C O N S T A N T S                               #
#                                                                              #
################################################################################


RC_NAME=`basename $0 2>/dev/null`
RC_DIR=`dirname \`readlink -qe $0\` 2>/dev/null`
RC_VER="0.2"
RC_ID="switch runlevel"

RUNLEVELS="sS01234567"          # valid runlevels for this script

RC_BASEDIR="/sbin"              # base directory for the `rc?.d' directories


################################################################################
#                                                                              #
#                               I N C L U D E S                                #
#                                                                              #
################################################################################


. $RC_DIR/init.d/include/rc_utils


################################################################################
#                                                                              #
#                              F U N C T I O N S                               #
#                                                                              #
################################################################################


#-------------------------------------------------------------------------------


#/
#/ Output script usage and exit.
#/
#/ \returns
#/
#/ Always exit code 1.
#/
usage()
{
    $ECHO "Usage: $RC_NAME [-n][-q][-v][-h][-V] <sS01234567>"

    exit 1
}


#-------------------------------------------------------------------------------


#/
#/ Output script version and exit.
#/
#/ \returns
#/
#/ Always exit code 1.
#/
version()
{
    $ECHO "$RC_NAME $RC_VER"

    exit 1
}


#-------------------------------------------------------------------------------


#/
#/ Check whether specified runlevel is valid. Valid runlevels are enumerated
#/ in <tt>RUNLEVELS</tt>.
#/
#/ \param[in] $1  Runlevel value to validate.
#/
#/ \returns
#/
#/ Return code 0 if specified runlevel is valid, otherwise 1.
#/
validate_runlevel()
{
    if test "`expr length "$1" 2>/dev/null`" = "1"
    then
        if test "`expr index "$1" "$RUNLEVELS" 2>/dev/null`" = "1"
        then
            return 0
        fi
    fi

    return 1
}


#-------------------------------------------------------------------------------


#/
#/ Switch runlevel by executing command scripts in \c rc directory
#/ associated with runlevel. The name of the \c rc sub-directory has the
#/ following naming scheme:
#/
#/     rc<runlevel>.d
#/
#/ When locating the command scripts, the \c rc sub-directory is prefixed by
#/ the path specified in the constant <tt>RC_BASEDIR</tt>. Command scripts must
#/ have the appropriate execute bit(s) set otherwise they'll be ignored.
#/ The "K" scripts, for de-activating services or facilities, are run first.
#/ Next, the "S" scripts are run which activate the services or facilities
#/ for the given runlevel.
#/
#/ \param[in] $1  Runlevel the system will switch to. The value must be a valid
#/                runlevel as recognized by this script (see \c RUNLEVELS).
#/
#/ \returns
#/
#/ Return code 0 if all command scripts for the specified runlevel executed
#/ successfully, otherwise 1.
#/
transit_runlevel()
{
    assert transit_runlevel "-n '$1'"

    rl=$1           # runlevel parameter
    rc_path=""      # "rc" directory for given runlevel (full path)
    K_list=""       # list of "K" scripts in "rc" directory
    S_list=""       # list of "S" scripts in "rc" directory
    script=""
    action=""

    rc_path=$RC_BASEDIR/rc${rl}.d
    if test -d "$rc_path"
    then
        #
        # Determine sorted list of "K" and "S" scripts in "rc" directory.
        # The `ls' command performs alphabetical sorting on the list while
        # a `sort' command is used to do a numerical sort of the sequence
        # number (i.e. character 2 and 3 of the filename).
        #
        K_list=`ls $rc_path/K[0-9][0-9]* 2>/dev/null | sort --key=1.2n,1.3n`
        S_list=`ls $rc_path/S[0-9][0-9]* 2>/dev/null | sort --key=1.2n,1.3n`

        #
        # Execute command scripts. The "K" scripts go first, then the
        # "S" scripts. The scripts must be executable otherwise they'll
        # be ignored.
        #
        for script in $K_list $S_list
        do
            if test -x "$script"
            then
                case "`basename $script`" in
                K*) action="stop"  ;;
                S*) action="start" ;;
                *)  action=""      ;;
                esac

                if test "$VERBOSE" = "1"
                then
                    $ECHO "$RC_NAME: executing $script"
                fi
                $DRY_RUN eval $script $action
            else
                if test "$VERBOSE" = "1"
                then
                    error "$script: command script not executable"
                fi
            fi
        done
    else
        error "$rc_path: directory not found"
    fi
}


#-------------------------------------------------------------------------------


################################################################################
#                                                                              #
#                         S T A R T  O F  S C R I P T                          #
#                                                                              #
#                                                                              #
# Operation:                                                                   #
#                                                                              #
# o Process command line arguments.                                            #
# o Verify requested runlevel.                                                 #
# o Run command scripts for requested runlevel.                                #
#                                                                              #
#                                                                              #
################################################################################

QUIET=0
VERBOSE=0               # verbose flag (0 or 1)
DRY_RUN=                # default is to actually execute commands
RUNLEVEL_ARG=


#
# Handle command line OPTIONS.
#
while test $# -gt 0
do
    case "$1" in
    -n|--dry-run)
        DRY_RUN="$ECHO -e \n> "     # ensure we begin on a new line
        ;;
    -q|--quiet)
        QUIET=1
        exec >/dev/null             # mute stdout
        ;;
    -v|--verbose)
        VERBOSE=1
        ECHO_N=$ECHO                # don't inhibit newline;
                                    # this prevents clobbering of text
        ;;
    -h|--help)
        usage
        ;;
    -V|--version)
        version
        ;;
    -*)
        fatal "unknown option: $1"
        ;;
    *)                              # first non-OPTIONS argument
        break
        ;;
    esac
    shift
done

#
# Handle influential environment variables.
#
if test "$RC_VERBOSE" = "0" -o "$RC_VERBOSE" = "1"
then
    VERBOSE=$RC_VERBOSE
fi


#
# Handle runlevel argument. Output an error message and
# the script's usage if argument missing or incorrect.
#
if test $# -eq 1
then
    RUNLEVEL_ARG=$1
    if validate_runlevel $RUNLEVEL_ARG
    then
        #
        # Runlevel "s" (lower case) and runlevel "S" are the same. Internally
        # we only use "S", so translate "s" to "S" if necessary.
        #
        if test "$RUNLEVEL_ARG" = "s"
        then
            RUNLEVEL_ARG=S
        fi

        #
        # Runlevel argument is OK.
        #
        if test "$VERBOSE" = "1"
        then
            $ECHO "$RC_NAME: runlevel validated: $RUNLEVEL_ARG"
        fi

        #
        # Switch runlevel.
        #
        transit_runlevel $RUNLEVEL_ARG
    else
        fatal "invalid runlevel: $RUNLEVEL_ARG"
    fi
elif test $# -eq 0
then
    fatal "missing runlevel argument"
else
    fatal "too many runlevel arguments: $*"
fi
