#!/bin/bash
#
# init fragment for O2CB.
#
# chkconfig:	2345 24 20
# description:	Load O2CB cluster services at system boot.
#

# Source function library
. /etc/rc.d/init.d/functions

# Try get config
[ -f /etc/sysconfig/o2cb ] && . /etc/sysconfig/o2cb

CLUSTERCONF=/etc/ocfs2/cluster.conf

# 
# This is a tricky bit of eval work.  There are many steps involved in
# O2CB startup/shutdown, so we collect them here.  Each line is a line
# of shell code that needs to be run.  The code is run via eval as
# follows:
#
# start/load:
#    Eval of the exact lines, in order.  So, the first call is
#    "eval load_module configfs".
#
# status:
#    Eval of the lines with "check_" prepended, in order.  So the first
#    call is "eval check_load_module configfs".
#
# stop/unload:
#    Eval of the lines with "un" prepened, in reverse order.  So the
#    *last* call is "eval unload_module configfs".
# 
# To provide an action, create a set of shell functions or commands
# "foo", "check_foo", and "unfoo".  Then add "foo arguments" to the
# appropriate place.  Be careful, eval requires quoting to be right.
#
LOAD_ACTIONS=("load_module configfs"
		"mount_fs configfs /sys/kernel/config"
		"load_module ocfs2_nodemanager"
		"load_module ocfs2_dlm"
		"load_module ocfs2_dlmfs"
		"mount_fs ocfs2_dlmfs /dlm")


#
# if_fail()
#
# Evaluates return codes.  If 0, prints "OK", if 1, prints "Failed"
# and exits.  If 2, status is "already done" and nothing is printed.
# The rest of the functions in here all honor this convention.
#
if_fail()
{
    RC="$1"
    REASON="$2"
    if [ "$RC" = "0" ]
    then
        echo "OK"
        return
    elif [ "$RC" = "2" ]
    then
        return
    fi
    echo "Failed"
    if [ -n "${REASON}" ]
    then
        echo "${REASON}" >&2
    fi
    exit 1
}

#
# load_module()
# Load a module
#
load_module()
{
	_modprobe single $1
}

#
# check_heartbeat()
#
# 0 is hb not active, 1 is error, 2 is hb active
#
check_heartbeat()
{
    if [ "$#" -lt "1" -o -z "$1" ]; then
        echo "check_heartbeat(): Requires an argument" >&2
        return 1
    fi
    CLUSTER="$1"

    RC=0
    if [ -d "/sys/kernel/config/cluster/${CLUSTER}/heartbeat/" ]
    then
        ls -1 "/sys/kernel/config/cluster/${CLUSTER}/heartbeat/" | while read HBUUID
        do
            if [ -d "/sys/kernel/config/cluster/${CLUSTER}/heartbeat/${HBUUID}" ]
            then
                return 2;
            fi
        done
        if [ $? = 2 ]
        then
            RC=2
        fi
    fi

    return $RC
}

#
# clean_heartbeat()
# Removes the inactive heartbeat regions
# 
clean_heartbeat() {
    if [ "$#" -lt "1" -o -z "$1" ]; then
        echo "clean_heartbeat(): Requires an argument" >&2
        return 1
    fi
    CLUSTER="$1"

    echo -n "Cleaning heartbeat on ${CLUSTER}: "

    if [ ! -d "/sys/kernel/config/cluster/${CLUSTER}/heartbeat/" ]
    then
        echo "OK"
        return
    fi

    ls -1 "/sys/kernel/config/cluster/${CLUSTER}/heartbeat/" | while read HBUUID
    do
        if [ ! -d "/sys/kernel/config/cluster/${CLUSTER}/heartbeat/${HBUUID}" ]
        then
            continue
        fi

        OUTPUT="`ocfs2_hb_ctl -I -u ${HBUUID} 2>&1`"
        if [ $? != 0 ]
        then
            echo "Failed"
            echo "${OUTPUT}" >&2
            exit 1
        fi

        REF="`echo ${OUTPUT} | awk '/refs/ {print $2; exit;}' 2>&1`"
        if [ $REF != 0 ]
        then
           echo "Failed"
           echo "At least one heartbeat region still active" >&2
           exit 1
        else
           OUTPUT="`ocfs2_hb_ctl -K -u ${HBUUID} 2>&1`"
        fi
    done
    if [ $? = 1 ]
    then
        exit 1
    fi
    echo "OK"
}

#
# unload_module()
# Unload a module
#
# 0 is success, 1 is error, 2 is not loaded
# 
unload_module() {
    if [ "$#" -lt "1" -o -z "$1" ]; then
        echo "unload_module(): Requires an argument" >&2
        return 1
    fi
    MODNAME="$1"

    MODOUT="`awk '$1 ~ /^'$MODNAME'$/{print $1,$3;exit}' < /proc/modules 2>/dev/null`"
    if [ -z "$MODOUT" ]
    then
        return 2
    fi
    case "$MODOUT" in
    $MODNAME\ 0)
        ;;
    $MODNAME\ *)
        return 2
        ;;
    *)
        echo -n "Invalid module parsing! "
        return 1
        ;;
    esac

    echo "Unloading module \"$MODNAME\": "
    modprobe -rs "$MODNAME"
    if [ "$?" != 0 ]; then
        echo "Unable to unload module \"$MODNAME\"" >&2
        return 1
    fi

    return 0
}

#
# check_load_module()
#
# 0 is not loaded, 1 is error, 2 is already loaded
#
check_load_module() {
    if [ "$#" -lt "1" -o -z "$1" ]
    then
        echo "check_load_module(): Requires an argument" >&2
        return 1
    fi
    MODNAME="$1"

    echo -n "Module \"$MODNAME\": "
    MODOUT="`awk '$1 ~ /^'$MODNAME'$/{print $1,$3;exit}' < /proc/modules 2>/dev/null`"
    if [ -z "$MODOUT" ]
    then
        echo "Not loaded"
        return 0
    fi
    echo "Loaded"
    return 2
}


#
# mount_fs()
# Mount a filesystem.
#
# 0 is success, 1 is error, 2 is already mounted
#
mount_fs() {
    TYPE=$1
    FULL_MOUNT="$2"
    FULL_MOUNTSEARCH="`echo "$FULL_MOUNT" | sed -e 's/\//\\\\\//g'`"
    MOUNTOUT="`awk '$2 ~ /^'$FULL_MOUNTSEARCH'$/{print $2; exit}' < /proc/mounts 2>/dev/null`"

    if [ -n "$MOUNTOUT" ]
    then
        return 2
    fi

	run_cmd "Mounting ${TYPE} filesystems" mount -t ${TYPE} ${TYPE} $FULL_MOUNT
    return 0
}

#
# check_mount_fs()
#
# 0 is not mounted, 1 is error, 2 is already mounted
#
check_mount_fs() {
    TYPE="$1"
    FULL_MOUNT="$2"
    FULL_MOUNTSEARCH="`echo "$FULL_MOUNT" | sed -e 's/\//\\\\\//g'`"
    MOUNTOUT="`awk '$2 ~ /^'$FULL_MOUNTSEARCH'$/{print $2; exit}' < /proc/mounts 2>/dev/null`"

    echo -n "Filesystem \"$TYPE\": "
    if [ -n "$MOUNTOUT" ]
    then
        echo "Mounted"
        return 2
    fi
    echo "Not mounted"
    return 0
}

#
# unmount_fs()
# Unmount a filesystem
#
# 0 is success, 1 is error, 2 is not mounted
#
unmount_fs() {
    TYPE=$1
    FULL_MOUNT=$2
    FULL_MOUNTSEARCH="`echo "$FULL_MOUNT" | sed -e 's/\//\\\\\//g'`"
    MOUNTOUT="`awk '$2 ~ /^'$FULL_MOUNTSEARCH'$/{print $2; exit}' < /proc/mounts 2>/dev/null`"

    if [ -z "$MOUNTOUT" ]
    then
        return 2
    fi

    run_cmd "Unmounting ${TYPE} filesystem" umount $FULL_MOUNT
}

load() {
    for i in $(seq 0 $((${#LOAD_ACTIONS[*]} - 1)) ); do
        eval ${LOAD_ACTIONS[i]}
    done
}

load_status() {
    for i in $(seq 0 $((${#LOAD_ACTIONS[*]} - 1)) ); do
        eval "check_${LOAD_ACTIONS[i]}"
    done
    return "$?"
}

online() {
    CLUSTER="${1:-${O2CB_BOOTCLUSTER}}"
    if [ -z "$CLUSTER" ]
    then
        echo "Cluster not known"
        return
    fi

    check_online $CLUSTER
    if [ $? = 2 ]
    then
        echo "Cluster ${CLUSTER} already online"
        return
    fi

    if ! [ -f ${CLUSTERCONF} ]
    then
        echo -n "Checking cluster configuration : "
        if_fail 1
    fi

    echo -n "Starting cluster ${CLUSTER}: "
    OUTPUT="`o2cb_ctl -H -n "${CLUSTER}" -t cluster -a online=yes 2>&1`"
    if [ $? = 0 ]
    then
        O2CB_HEARTBEAT_THRESHOLD_FILE_OLD=/proc/fs/ocfs2_nodemanager/hb_dead_threshold
        O2CB_HEARTBEAT_THRESHOLD_FILE=/sys/kernel/config/cluster/${CLUSTER}/heartbeat/dead_threshold
        if [ -n "$O2CB_HEARTBEAT_THRESHOLD" ]; then
            if [ -f "$O2CB_HEARTBEAT_THRESHOLD_FILE" ]; then
                echo "$O2CB_HEARTBEAT_THRESHOLD" > "$O2CB_HEARTBEAT_THRESHOLD_FILE"
            elif [ -f "$O2CB_HEARTBEAT_THRESHOLD_FILE_OLD" ]; then 
                echo "$O2CB_HEARTBEAT_THRESHOLD" > "$O2CB_HEARTBEAT_THRESHOLD_FILE_OLD"
            else
                echo "WARNING: Unable to set heartbeat dead threshold" >&2
            fi
        fi

        echo "OK"
        return
    else
        echo "Failed"
        echo "$OUTPUT"
    fi

    echo -n "Stopping cluster ${CLUSTER}: "
    OUTPUT="`o2cb_ctl -H -n "${CLUSTER}" -t cluster -a online=no 2>&1`"
    if_fail "$?" "$OUTPUT"
}

#
# check_online()
#
# 0 is not online, 1 is error, 2 is online
#
check_online() {
    if [ "$#" -lt "1" -o -z "$1" ]
    then
        echo "check_online(): Requires an argument" >&2
        return 1
    fi
    CLUSTER="$1"

    RC=0
    if [ -d "/sys/kernel/config/cluster/${CLUSTER}/node/" ]
    then
        ls -1 "/sys/kernel/config/cluster/${CLUSTER}/node/" | while read NODE
        do
            LOCAL="`cat \"/sys/kernel/config/cluster/${CLUSTER}/node/${NODE}/local\"`"
            if [ $LOCAL = 1 ]
            then
                return 2
            fi
        done
        if [ $? = 2 ]
        then
            RC=2
        fi
    fi
    return $RC
}

offline() {
    CLUSTER="${1:-${O2CB_BOOTCLUSTER}}"
    if [ -z "$CLUSTER" ]
    then
        return
    fi

    if [ ! -e "/sys/kernel/config/cluster/${CLUSTER}" ]
    then
        return
    fi

    clean_heartbeat $CLUSTER

    echo -n "Stopping cluster ${CLUSTER}: "
    check_heartbeat $CLUSTER
    if [ $? != 0 ]
    then
        echo "Failed"
        echo "Unable to stop cluster as heartbeat region still active" >&2
    fi

    OUTPUT="`o2cb_ctl -H -n "${CLUSTER}" -t cluster -a online=no 2>&1`"
    if_fail "$?" "$OUTPUT"

    unload_module ocfs2
    if_fail "$?"
}

start() {
    if [ "$O2CB_ENABLED" != "true" ]
    then
        exit 0
    fi

    load
    online "$1"
}

unload() {
    if [ -d /sys/kernel/config/cluster/ ]; then
        ls -1 /sys/kernel/config/cluster/ | while read CLUSTER
        do
            echo "Unable to unload modules as Cluster ${CLUSTER} is still online" >&2
            exit 1
        done
        if [ $? = 1 ]
        then
            exit 1
        fi
    fi

    for i in $(seq $((${#LOAD_ACTIONS[*]} - 1)) -1 0); do
        eval "un${LOAD_ACTIONS[i]}"
    done
}

stop() {
    offline "$1"
    unload
}

status() {
    load_status
    if [ $? != 2 ]
    then
        return 0;
    fi

    CLUSTER="${1:-${O2CB_BOOTCLUSTER}}"
    if [ -z "$CLUSTER" ]
    then
        return 1;
    fi

    echo -n "Checking cluster $CLUSTER: "
    check_online $CLUSTER
    if [ $? = 2 ]
    then
       echo "Online"
    else
       echo "Offline"
       return 0;
    fi

    echo -n "Checking heartbeat: "
    check_heartbeat $CLUSTER
    if [ $? = 2 ]
    then
        echo "Active"
    else
        echo "Not active"
        return 0;
    fi

    return

    echo -n "Checking if O2CB is loaded: "
    RC=0
    for MODSPEC in $MODULE_LIST
    do
        MODULE_NAME="`echo $MODSPEC | cut -d: -f1`"
        FSTYPE="`echo $MODSPEC | cut -d: -f2`"
        MOUNT_POINT="`echo $MODSPEC | cut -d: -f3`"

        if grep "^${MODULE_NAME} " /proc/modules >/dev/null 2>&1
        then
            echo -n "${MODULE_NAME} "
        else
            RC=1
            break
        fi
    done
    if_fail "$RC"

    echo -n "Checking O2CB mount points: "
    for MODSPEC in $MODULE_LIST
    do
        MODULE_NAME="`echo $MODSPEC | cut -d: -f1`"
        FSTYPE="`echo $MODSPEC | cut -d: -f2`"
        MOUNT_POINT="`echo $MODSPEC | cut -d: -f3`"

        if [ -z "$FSTYPE" -o -z "$MOUNT_POINT" ]
        then
            continue
        fi

        FULL_MOUNT="${O2CB_MANAGER}/${MOUNT_POINT}"
        FULL_MOUNTSEARCH="`echo "$FULL_MOUNT" | sed -e 's/\//\\\\\//g'`"
        if grep "^${FSTYPE} ${FULL_MOUNTSEARCH} ${FSTYPE}" /proc/mounts >/dev/null 2>&1
        then
            echo -n "${MOUNT_POINT} "
        else
            RC=1
            break
        fi
    done
    if_fail "$RC"
}



case "$1" in
    start)
        start "$2"
        ;;

    status)
        status "$2"
        ;;

    stop)
        stop "$2"
        ;;

    restart)
        stop "$2"
        start "$2"
        ;;

    force-reload)
        stop "$2"
        start "$2"
        ;;

    load)
        load
        ;;

    online)
        load
        online "$2"
        ;;

    offline)
        offline "$2"
        ;;

    unload)
        offline "$2"
        unload
        ;;

    *)
        msg_usage "$0 {start|stop|restart|force-reload|load|unload|online|offline|status}"
        exit 3
        ;;
esac

exit 0
