#!/bin/sh
#/
#/ \file
#/
#/ \brief       final shutdown sequence of system
#/
#/ \author      Arthur
#/
#/ \date        January 31, 2013
#/
#/ \version     0.2
#/
#/ \remarks
#/
#/ This script must only rely on utilities found in /bin and /sbin.
#/


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


RC_NAME=`ls $0 2>/dev/null | sed -e 's@.*/\+\([^ \t/]\+\)@\1@'`
RC_DIR=/sbin/init.d
RC_VER="0.2"
RC_ID="system halt"

SIG_QUIT=3
SIG_KILL=9

IGNORE_SIG="INT QUIT TSTP"              # signal names/numbers to ignore

REBOOT_DELAY=2                          # time to wait before re-init system [s]
SOFTOFF_DELAY=3                         # time to wait before power-off [s]
HARDOFF_DELAY=3                         # time to wait before killing mains [s]

POWEROFF_FLAG=/etc/killpower


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


. $RC_DIR/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]"

    exit 1
}

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


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

    exit 1
}


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


#/
#/ Flush buffers for all disk devices.
#/
#/ \returns
#/
#/ Return code 0 if service has been stopped; code 1 in case the service
#/ was not running or any other error condition.
#/
#/ \todo
#/
#/ * Code is very specific to \c unimatrix, may need a more generic impl.
#/
flush_buffers()
{
    mylex_dir=""

    $ECHO "Flushing disk buffers..."

    #
    # It's said `sync' waits executing until any pending syncing is done,
    # so if the second `sync' exits, the buffers have been flushed.
    #
    $DRY_RUN sync
    $DRY_RUN sync

    #
    # Flush cache of Mylex controllers.
    #
    for mylex_dir in `ls /proc/rd/c?`
    do
        if test -f "$mylex_dir/user_command"
        then
            $ECHO flush-cache > $mylex_dir/user_command
        fi
    done
}


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


#/
#/ Make a log about shutdown. Input an entry in the system logs that the
#/ system was shutdown. This function is most likely called just prior to
#/ the filesystems being unmount (or mounted read-only).
#/
#/ \param[in] $1  Log message (optional).
#/
#/ \returns
#/
#/ Return code 0 if shutdown has been logged, otherwise code 1.
#/
log_system_shutdown()
{
    msg="$1"

    $DRY_RUN halt -w
}


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


#/
#/ Unmount all local filesystems except root filesystem. In case a filesystem
#/ cannot be unmounted, the function attempts to remount the filesystem
#/ read-only. This is not considered to be an error.
#/
#/ \returns
#/
#/ Return code 0 if all local filesystems were unmounted; code 1 in case one
#/ or more filesystems could not be unmounted.
#/
unmount_local_filesystems()
{
    $ECHO "Unmounting local filesystems..."

    $DRY_RUN umount -ar

    return $?
}


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


#/
#/ Terminate all processes except thyself.
#/
terminate_them_others()
{
    assert terminate_them_others "-n '$SIG_QUIT'"
    assert terminate_them_others "-n '$SIG_KILL'"

    $ECHO "Signalling processes QUIT..."
    $DRY_RUN killall5 -$SIG_QUIT

    sleep 3

    $ECHO "Signalling processes KILL..."
    $DRY_RUN killall5 -$SIG_KILL
}


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


#/ 
#/ Check whether UPS detected power failure.
#/  
#/ \returns
#/ 
#/ Return code 0 if line power is currently failing, otherwise 1.
#/  
is_line_power_failing()
{
    #
    # The power-off flag is represented
    # by the presence of a certain file.
    #
    test -f "$POWEROFF_FLAG"
    return $?
}


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


################################################################################
#                                                                              #
#                         S T A R T  O F  S C R I P T                          #
#                                                                              #
#                                                                              #
# Operation:                                                                   #
#                                                                              #
# o Process command line arguments.                                            #
# o Determine halt mode.                                                       #
# o Execute final system shutdown procedures.                                  #
# o Put system in requested halt mode.                                         #
#                                                                              #
################################################################################

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

MODE=                               # what to do w/ system after shutdown
HARDOFF_TIME=                       # time main power is cut (if applicable)

trap "" $IGNORE_SIG                 # prevent interruption of the script


#
# 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


#
# Determine what to do after shutdown. This is encoded in the script name.
# There are four modes. Three are encoded in the script name and one is
# determined differently.
#
# Script name encode:
#
#   - halt      turn off system
#   - reboot    restart system
#   - srm       return to SRM console
#
# Non-encoded:
#
#   - softoff   soft-power off system ("standby")
#   - hardoff   hard-power off system ("cut mains")
#
name_parts=`split_SK_script_name $RC_NAME`
if test -n "$name_parts"
then
    mode_part=`$ECHO $name_parts | cut -d' ' -f3 2>/dev/null`
    case "$mode_part" in
    halt)    MODE=halt    ;;
    reboot)  MODE=reboot  ;;
    srm)     MODE=srm     ;;
    *)
        #
        # Don't consider an unknown mode to be fatal. Instead, fallback to
        # "srm" mode so the shutdown procedure can continue and the system
        # won't end up in limbo state.
        #
        error "unknown mode: $mode_part"
        MODE=srm
        error "switching to mode: $MODE"
        ;;
    esac
else
    #
    # Supply a fallback to "srm" mode for the same reason as mentioned above.
    #
    error "could not determine halt mode"
    MODE=srm
    error "switching to mode: $MODE"
fi
#
# There are some external factors that may change the requested MODE.
#
# The INIT_HALT environment variable could be set through the `-H' or
# `-P' flag of the `shutdown' command.
#
if test "$MODE" = "halt"
then
    case "$INIT_HALT" in
    HALT)       MODE=halt     ;;
    POWERDOWN)  MODE=softoff  ;;    # man page `shutdown' says POWEROFF??
    *)
        #
        # Other cases not supported, stick with "halt" mode.
        #
        ;;
    esac
fi
#
# Or, maybe line power is failing in which case main power
# to the system will be cut.
#
is_line_power_failing
if test $? -eq 0
then
    MODE=hardoff
    HARDOFF_TIME=`cat $POWEROFF_FLAG`
    $ECHO "Main power is failing, switching to mode: $MODE"
fi



#
# Initiate the final shutdown procedures.
#

terminate_them_others

log_system_shutdown

flush_buffers
unmount_local_filesystems

shutdown_mark=`date 2>/dev/null`

$ECHO
$ECHO "*** SYSTEM SHUTDOWN COMPLETE at $shutdown_mark ***"
$ECHO


#
# Put system in the requested MODE.
#
case "$MODE" in
softoff)
    MSG="Powering off system in $SOFTOFF_DELAY second(s)..."
    CMD="sleep $SOFTOFF_DELAY; poweroff -n"
    ;;
halt)
    MSG="System halted."
    CMD="exit 0"
    ;;
reboot)
    MSG="Rebooting system in $REBOOT_DELAY second(s)..."
    CMD="sleep $REBOOT_DELAY; reboot -n"
    ;;
srm)
    MSG="Returning to SRM console..."
    CMD="halt -n"
    ;;
hardoff)
    MSG="Cutting main power at $HARDOFF_TIME"
    CMD="sleep 1d"
    ;;
*)
    #
    # Again, we treat a fatality as an error and fallback to
    # "srm" mode because the system is one step from being
    # shutdown and there's no point in turning back.
    #
    error "unsupported mode: $MODE"
    MODE=srm
    error "switching to mode: $MODE"
    MSG="Returning to SRM console..."
    CMD="halt -n"
    ;;
esac

$ECHO $MSG
$DRY_RUN eval $CMD


#
# Script should not get past here, but if it does...
#
$ECHO
$ECHO "*** SHUTDOWN FAILURE ***"
$ECHO
$ECHO "Unable to shutdown system to mode \"$MODE\"."
$ECHO "Notify operator."
exit 1
