#!/bin/sh
#/
#/ \file
#/
#/ \brief       enable/disable swapping facility
#/
#/ \author      Arthur
#/
#/ \date        November 20, 2012
#/
#/ \version     0.2
#/
#/ \todo
#/
#/ * When activating swap, is a check needed to see whether swap is
#/   already active?
#/


################################################################################
#                                                                              #
#                              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="swapping facility"

START_SETTLE=0
RESTART_PAUSE=1
STOP_SETTLE=0

FSTAB_FILE="/etc/fstab"
SWAPINFO_FILE="/proc/swaps"


################################################################################
#                                                                              #
#                               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] <ACTION>"

    exit 1
}


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


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

    exit 1
}


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


#/
#/ Activate swapping facility. All devices in the system's \c fstab file
#/ that are marked for swap usage will be activated. Non-existing swap
#/ devices defined in the \c fstab are silently ignored. Also, swap space
#/ will be re-initialized to match the kernel's page size, if necessary.
#/
#/ \returns
#/
#/ Return code 0 if activation succeeded, otherwise 1.
#/
start()
{
    assert start "$START_SETTLE -ge 0"

    defined=`list_defined_swap_devs`
    dev=

    $ECHO_N "Activating swap..."
    if test -n "$defined"
    then
        for dev in $defined
        do
            # only swap device or swap file suitable for swapping:
            if test -b "$dev" -o -f "$dev"
            then
                $DRY_RUN /sbin/swapon --fixpgsz $dev 2>/dev/null
                if test $? -eq 0
                then
                    sleep $START_SETTLE
                    $ECHO_N " $dev"
                fi
            fi
        done
        $ECHO
    else
        $ECHO "<none>"
    fi
}


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


#/
#/ De-activate swapping facility. Active swap devices are retrieved from the
#/ <tt>/proc</tt> filesystem and are de-activated.
#/
#/ \returns
#/
#/ Return code 0 if de-activation succeeded, otherwise 1.
#/
stop()
{
    assert  stop "$STOP_SETTLE -ge 0"

    active=`list_active_swap_devs`
    dev=

    # disable all active swap devices, if there are any:
    if test -n "$active"
    then
        $ECHO_N "De-activating swap..."
        for dev in $active
        do
            $DRY_RUN /sbin/swapoff $dev 2>/dev/null
            if test $? -eq 0
            then
                sleep $STOP_SETTLE
                $ECHO_N " $dev"
            fi
        done
        $ECHO
    else
        $ECHO "Swapping facility not active."
    fi
}


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


#/
#/ Shutdown and restart swapping facility.
#/
#/ \returns
#/
#/ Return code 0 if restart sequence succeeded, otherwise code 1.
#/
restart()
{
    assert restart "$RESTART_PAUSE -ge 0"

    stop  &&  sleep $RESTART_PAUSE  &&  start
    if test $? -eq 0
    then
        return 0
    else
        return 1
    fi
}


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


#/
#/ Display whether the swapping facility is currently active and if so, which
#/ devices are being used. Also, verify whether the active swap devices
#/ correspond to the predefined swap devices in the \c fstab file.
#/
#/ \returns
#/
#/ Always return code 0.
#/
status()
{
    defined=        # swap devices defined in "fstab" file
    active=         # swap devices currently active
    all=            # "defined" and "active" combined (w/ duplicates)
    undefined=      # active swap, but not defined in "fstab" file
    inactive=       # defined in "fstab" file, but not active swap
    dev=

    #
    # Output an alphabetically sorted list of active swap devices, if any.
    #
    active=`list_active_swap_devs`
    if test -n "$active"
    then
        active=`sort_list $active`
        $ECHO "Swapping facility active on: $active"

        #
        # Check for discrepancy between active swap and "fstab" file.
        # We substract ("lexicographically") device filenames from the
        # "all" list and this gives us a clue whether certain devices
        # were undefined or have become in-active.
        #
        defined=`list_defined_swap_devs`
        all="$defined $active"
        undefined=`$ECHO $all $defined | fmt -w1 | sort | uniq -u | tr '\n' ' '`
        inactive=`$ECHO $all $active | fmt -w1 | sort | uniq -u | tr '\n' ' '`

        if test -n "$undefined"
        then
            $ECHO "Undefined, but active swap on: $undefined"
        fi

        if test -n "$inactive"
        then
            $ECHO "Defined, but in-active swap on: $inactive"
        fi
    else
        $ECHO "Swapping facility not active."
    fi

    return 0
}


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


#/
#/ Return list of devices marked for swap usage. The list of devices is
#/ is echo'ed to stdout and is whitespace-separated. If no devices are marked
#/ for swap usage, an empty string is echo'ed.
#/
list_defined_swap_devs()
{
    if test -r "$FSTAB_FILE"
    then
        # extract swap device files from fstab file:
        defined=`cat $FSTAB_FILE | \
             sed -ne 's@^\(/.*\)[ \t]\+[A-Za-z0-9]\+[ \t]\+swap[ \t]\+.*@\1@p'`

        $ECHO $defined
    else
        error "\"fstab\" file not found: $FSTAB_FILE"
    fi
}


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


#/
#/ Return list of devices actively being used for swap. The list of devices is
#/ is echo'ed to stdout and is whitespace-separated. If no devices were active,
#/ an empty string is echo'ed.
#/
list_active_swap_devs()
{
    active=         # active swap devices

    if test -r "$SWAPINFO_FILE"
    then
        # retrieve swap device files from /proc filesystem:
        active=`cat $SWAPINFO_FILE  | \
                sed -ne 's@^\(/[^ \t]\+\)[ \t]\+.*@\1@p'`

        $ECHO $active
    else
        error "swap info file not found: $SWAPINFO_FILE"
    fi
}


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


################################################################################
#                                                                              #
#                         S T A R T  O F  S C R I P T                          #
#                                                                              #
#                                                                              #
# Operation:                                                                   #
#                                                                              #
# o Process command line arguments.                                            #
# o Call appropriate function for specified ACTION.                            #
#                                                                              #
#                                                                              #
################################################################################

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

ACTION=""                           # action to perform...


#
# 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 command line ACTION.
# Only one ACTION argument is allowed.
#
if test $# -eq 1
then
    case "$1" in
    start)   start   ;;
    stop)    stop    ;;
    restart) restart ;;
    status)  status  ;;
    *)
        fatal "unknown action: $1"
        ;;
    esac
elif test $# -eq 0
then
    fatal "missing action"
else
    fatal "too many actions: $*"
fi
