#!/bin/bash
#
# Description: Stonith Plugin Wrapper
# Version: 2.00
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# Copyright (c) 2009 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#
#######################################################################

#Grobal Variables
command_name=${0##*/}
command_args=("$@")
admins_direction=/var/run/heartbeat/rsctmp/${command_args[1]}_stopped
ha_log_file=/var/log/ha-log
ha_debug_file=/var/log/ha-debug
sfex_stat_path=/usr/lib64/heartbeat/sfex_stat
pluginnum=0 #how many plugins are set in config file.
declare -a seclist #list of section names (in config file).
timer_popd=0 #action timeout ("kill_plugin_if_timed_out") popd(1) or not(0).

#Define default values.
conf_file=${conf_file=/etc/stonith_wrapper.conf}
no_sfex=${no_sfex=FALSE}
index=${index=1}
default_real_plugin="ibmrsa-telnet"
default_start_timeout=20
default_status_timeout=20
default_control_timeout=20
default_sby_wait=15
sby_wait=${sby_wait=${default_sby_wait}}


#
# Output a log message to Heartbeat's log file.
# arg1  : log level
# arg2  : log message
# return: nothing
#
savelog()
{
    typeset level=$1
    shift

    msg=$(echo "$command_name[$$]:";
    date '+%Y/%m/%d_%H:%M:%S'; echo "$level:" "$@")
    echo $msg >> ${ha_debug_file}
    if [[ "$level" != "debug" ]] &&  [[ "$level" != "DEBUG" ]]; then
        echo $msg >> ${ha_log_file}
    fi
}

#
# Kill all processes which are to set timeout,
# and exit with specified status number.
# arg1   : exit status number
# arg2-  : message to output debug log.
# return : nothing
#
kill_and_exit()
{
    typeset status=$1
    #unset all timeout for execute plugin(s).
    timerpids=`jobs -l | awk '{print $2}'`
    savelog debug "kill all timeout-processes. (pids=${timerpids})"
    kill ${timerpids} > /dev/null 2>&1
    shift
    savelog debug "Leave: exitcd=$status : ${command_args[@]} : $@"
    exit $status
}

#
# Check whether a file exists or not.
# When the file exists, it means the administrator judged "STONITH is complete",
# then exit normally.
# If not, continue the following processes.
# arg    : nothing
# return : nothing
#
ok_if_admin_judges_so()
{
    if [[ -f $admins_direction ]]; then
        savelog info "admins decision is accepted."
        rm -f $admins_direction
        kill_and_exit 0 $FUNCNAME $LINENO
    fi
}

#
# Set timeout for executing all plugins.
# When the timeout occurs, kill itself and its children
# by sending SIGKILL to my pgid.
# arg1   : wait time (sec.)
# arg2   : target pgid
# return : nothing (error exit when failure occurs)
#
quit_if_timed_out()
{
    typeset waitsec=$1
    typeset pgid=$2
    exec -a quit_if_timed_out perl -e "sleep $waitsec; kill -9, $pgid;" &
    if [[ $? != 0 ]]; then
        savelog ERROR "exec quit_if_timed_out failed."
        exit 1
    fi
    savelog debug "set timeout for executing all plugins. "\
        "(waittime=${waitsec}sec. pid=$!)"
}

#
# Confirm the target node is dead or alive with ping command.
# arg1  : section name (written on config file) for the plugin
#         which is now STONITHing.
# return: 0 -> the node is dead
#         1 -> the node is alive
ok_if_target_died()
{
    typeset ip_of_target
    typeset vname
    secname="$1"

    eval nodeip_list=\${!${secname}_wrapper_nodeip*}
    for vname in ${nodeip_list}; do
        eval ip_of_target=\$$vname
        if ping -w3 -c1 "$ip_of_target" >/dev/null 2>&1; then
            savelog info "$ip_of_target responded."
            return 1
        else
            savelog info "$ip_of_target do not responded."
        fi
    done

    return 0
}

#
# Confirm the local node is ACT or SBY with check whether specified disk
# device is mounted or not.
# NOTE: This function is not used now.
#       Formerly, when the parameter "no_sfex" is true, call this function
#       in check_if_sby().
#       but now, if "no_sfex" is true, it does not check the local node's
#       status, and does not wait before fence action.
# arg   : nothing
# return: 0 -> the local node is SBY
#         1 -> the local node is ACT
#
check_sby_using_mount()
{
    if grep -q "$device" /proc/mounts; then
        savelog info "this node is active."
        return 1
    else
        savelog info "this node is standby."
        return 0
    fi
}

#
# Confirm the local node is ACT or SBY with sfex_stat command.
# sfex_stat returns...
#         0 -> the local node is ACT
#         1 -> the local node is SBY
# This function should return...
# return: 0 -> the local node is SBY
#         1 -> the local node is ACT, or failure occurs.
#
check_sby_using_sfex()
{
    typeset status

    if   [[ ! -x "${sfex_stat_path}" ]]; then
        savelog ERROR "Unable to use sfex_stat."
        return 1
    fi
    "${sfex_stat_path}" -i "${index}" "${device}" >/dev/null 2>&1
    status=$?
    savelog info "sfex_stat: status=$status"

    if [[ "$status" = 0 ]]; then
        return 1
    else
        return 0
    fi
}

#
# Check whether the local node is SBY or ACT only when the parameter "no_sfex"
# is not true.
# arg   : nothing
# return: 0 -> the local node is SBY
#         1 -> the local node is ACT, or there is no need to check the local
#              node's status.
#
check_if_sby()
{
    if (echo ${no_sfex} | grep -q -i "^true$"); then
        #When node_sfex is true,
        #don't wait even if the local node is SBY node.
        return 1
    fi
    check_sby_using_sfex
    return $?
}

#
# Execute itself as process group leader.
# args  : whole command-line strings
#         (command path and args when this script is executed)
# return: nothing (error exit when failure occurs)
#
become_leader()
{
    exec perl -e 'setpgrp; exec @ARGV' "$@"
    if [[ "$?" != 0 ]]; then
        savelog ERROR "Unable to become leader."
        exit 1
    fi
}

#
# Check the stonith-wrapper process itself is a process group leader or not.
# return: 0 -> I am a process group leader.
#         1 -> I am not a process group leader.
#
is_this_leader()
{
    typeset mypgid
    mypgid=$(ps --no-headers -o '%r' $$)
    mypgid=${mypgid##* }
    if [[ "$mypgid" == $$ ]]; then
        return 0
    fi
    return 1
}

#
# Check validation and check required parameters are specified or not.
# arg   : nothing
# return: nothing (error exit when failure occurs)
#
check_parameters()
{
    #Check validation.
    if [[ -n "${sby_wait}" ]]; then
        if (echo ${sby_wait} | grep -q '[^0-9]') ; then
           savelog WARN "parameter \"sby_wait\" is not digit "\
               "(value=${sby_wait}). "\
               "use default value. (value=${default_sby_wait})"
               sby_wait=${default_sby_wait}
        fi
    fi

    #Check required parameters.
    if (echo ${no_sfex} | grep -q -i "^true$"); then
        #When no_sfex is true,
        #There is no required parameter.
        return
    fi
    if [[ -z "${device}" ]]; then
        savelog ERROR "parameter \"device\" is required "\
        "when \"no_sfex\" is not true."
        exit 1
    fi
}

#
# Check whether the same section name is already specified or not.
# Section name has to be unique in config file.
# Because each plugin's parameter (ex. "wrapper_real_plugin",
# "wrapper_status_timeout", and so on... anyway, parameters which name is
# "wrapper_xxx") is managed with variables which format is
# "<section name>_<parameter name>".
# arg1  : new-commer sectionname
# return: 0 -> the same section name already exists
#         1 -> the same section name does not exist yet
#
does_same_secname_exist()
{
    newcomer="$1"
    i=0
    while [[ 1 ]]
    do
        if [[ ${i} -ge ${pluginnum} ]]; then
            break
        fi
        secname="${seclist[${i}]}"
        if [[ "${newcomer}" = "${secname}" ]]; then
            return 0
        fi
        i=`expr ${i} + 1`
    done
    return 1
}

#
# Parse config file and get real plugins' settings.
# This function's roles are:
#   1) hold section names in section-name-list.
#   2) set parameters for stonith-wrapper("wrapper_xxx") as variables which
#      format is "<section>_<parameter>=<value>"
#   3) hold parameters and its value for real plugin in the list.
#      The format is "parameter=value".
#      The list name is "<section>_real_prmlist".
# arg   : nothing
# return: nothing (error exit when failure occurs)
#
parse_config_file()
{
    linenum=0   #line number in config file.
    pluginnum=0 #how many plugins are set in config file.

    if [[ ! -f ${conf_file} ]]; then
        savelog ERROR "${conf_file} does not exist."
        exit 1
    fi

    while read line; do
        linenum=`expr ${linenum} + 1`
        #Trim.
        line=`echo "${line}" | sed 's/^\s*//' | sed 's/\s*$//'`
        #Ignore empty or comment line.
        if [[ -z "${line}" ]]; then
            continue
        fi
        if (echo "${line}" | grep -q -e '^#' -e '^;'); then
            continue
        fi
        #Get section name.
        if (echo "${line}" | grep -q '^\[.*\]$'); then
            secname=`echo "${line}" | sed 's/^\[//' | sed 's/\]$//'`
            secname=`echo "${secname}" | sed 's/^\s*//' | sed 's/\s*$//'`
            #Section name should not be empty.
            if [[ -z "${secname}" ]]; then
                savelog ERROR "section name is empty. (line:${linenum})"
                exit 1
            fi
            #Only alphabet or numeric characters are allowed
            #to be section name.
            if (echo "${secname}" | grep -q '\W'); then
                savelog ERROR "section name has non-alphanumeric characters. "\
                    "(line:${linenum}, section:${secname})"
                exit 1
            fi
            #Section name should be unique in config file.
            if (does_same_secname_exist "${secname}"); then
                savelog ERROR "section name should be unique in config file. "\
                    "(line:${linenum}, section:${secname})"
                exit 1
            fi
            #Add the section name to section list.
            seclist[${pluginnum}]="${secname}"
            pluginnum=`expr ${pluginnum} + 1`
            set_default_prmvalue "${secname}"

            continue
        fi

        if [[ -z "${secname}" ]]; then
            savelog ERROR "the line for parameter setting appears "\
                "before section. (line:${linenum})"
            exit 1
        fi

        #Get parameter name and value without space and tab characters.
        key=`echo ${line} | awk -F '=' '{print $1}'`
        key=`echo "${key}" | sed 's/^\s*//' | sed 's/\s*$//'`
        value=`echo ${line} | awk -F '=' '{print $2}'`
        value=`echo "${value}" | sed 's/^\s*//' | sed 's/\s*$//'`

        #Check validation.
        #Empty parameter name and value are not allowed.
        if [[ -z "${key}" ]]; then
            savelog WARN "parameter name is empty. "\
                "Ignore the setting. (line:${linenum})"
            continue
        fi
        if [[ -z "${value}" ]]; then
            savelog WARN "parameter value is empty. "\
                "Ignore the setting. (line:${linenum})"
            continue
        fi

        #Get parameter settings.
        if (echo "${key}" | grep -q '^wrapper_'); then
            #parameters for stonith-wrapper.
            #Make format secname_paramname=value
            eval "${secname}_${key}"=\"${value}\"
        else
            #parameters for real plugin.
            #Make format parameter_name=value
            #to add it to parameter list.
            param="${key}=${value}"
            eval real_prm_index=\${#"${secname}"_real_prmlist[*]}
            eval ${secname}_real_prmlist[${real_prm_index}]=\"${param}\"
        fi
    done < ${conf_file}

    if [[ ${pluginnum} -le 0 ]]; then
        savelog ERROR "no section is specified."
        exit 1
    fi
    check_prmvalue_validation
}

#
# For each real plugin's settings, set default value.
# This function is should be called in parse_config_file().
# arg   : nothing
# return: nothing
#
set_default_prmvalue()
{
    secname="$1"

    eval "${secname}_wrapper_real_plugin"=${default_real_plugin}
    eval "${secname}_wrapper_start_timeout"=${default_start_timeout}
    eval "${secname}_wrapper_status_timeout"=${default_status_timeout}
    eval "${secname}_wrapper_control_timeout"=${default_control_timeout}
}

#
# For each real plugin's settings, check parameter value's validation.
# If it founds invalid parameter, set default value.
# This function is should be called in or after parse_config_file().
# arg   : nothing
# return: nothing
#
check_prmvalue_validation()
{
    i=0
    while [[ 1 ]] 
    do
        if [[ ${i} -ge ${pluginnum} ]]; then
            break
        fi
        secname=${seclist[${i}]}

        #Check wrapper_start_timeout. It should be digit.
        eval value=\${${secname}_wrapper_start_timeout}
        if (echo ${value} | grep -q '[^0-9]') ; then
           savelog WARN "parameter \"wrapper_start_timeout\" "\
               "for [${secname}] is not digit (value=${value}). "\
               "use default value. (value=${default_start_timeout})"
           eval "${secname}_wrapper_start_timeout"=${default_start_timeout}
        fi

        #Check wrapper_status_timeout. It should be digit.
        eval value=\${${secname}_wrapper_status_timeout}
        if (echo ${value} | grep -q '[^0-9]') ; then
           savelog WARN "parameter \"wrapper_status_timeout\" "\
               "for [${secname}] is not digit (value=${value}). "\
               "use default value. (value=${default_status_timeout})"
           eval "${secname}_wrapper_status_timeout"=${default_status_timeout}
        fi

        #Check wrapper_control_timeout. It should be digit.
        eval value=\${${secname}_wrapper_control_timeout}
        if (echo ${value} | grep -q '[^0-9]') ; then
           savelog WARN "parameter \"wrapper_control_timeout\" "\
               "for [${secname}] is not digit (value=${value}). "\
               "use default value. (value=${default_control_timeout})"
           eval "${secname}_wrapper_control_timeout"=${default_control_timeout}
        fi

        i=`expr ${i} + 1`
    done
}

#
# Calculate the sum total of timeout value for specified action.
# arg1  : action name [gethosts|status|on|off|reset]
# return: nothing (output sum total of timeout value.)
#
get_action_timeout_value()
{
    total=0
    buffer=10
    timeout_type=""
    action="$1"

    case "${action}" in
        gethosts)
            timeout_type="wrapper_start_timeout"
            default_value=${default_start_timeout}
            ;;
        status)
            timeout_type="wrapper_status_timeout"
            default_value=${default_status_timeout}
            ;;
        on|off|reset)
            timeout_type="wrapper_control_timeout"
            default_value=${default_control_timeout}
            total=`expr ${total} + ${sby_wait}`
            ;;
        *)
            #It does not execute real plugin in other actions,
            #so no need to set timeout in stonith-wrapper.
	    echo "0"
            return
            ;;
    esac

    i=0
    while [[ 1 ]]
    do
        if [[ ${i} -ge ${pluginnum} ]]; then
            break
        fi
        secname="${seclist[${i}]}"
        eval value=\${${secname}_${timeout_type}}
        total=`expr ${total} + ${value}`
        i=`expr ${i} + 1`
    done

    #Add buffer to avoid that timeout for executing a plugin and timeout for
    #executing all plugins occur at the same time.
    total=`expr ${total} + ${buffer}`

    savelog debug "timeout for ${action} action is ${total} sec."
    echo "${total}"
    return
}

#
# Kill all children of stonith-wrapper.
# The difference with quit_if_timed_out() is
# this function kills child-processes only, does not kill stonith-wrapper
# itself.
# arg   : nothing
# return: nothing
#
kill_all_children()
{
    timer_popd=1
    action_timeout_pid=`jobs -l | awk '$0 ~ /quit_if_timed_out/ {print $2}'`
    childrenpids=`ps --no-headers -o pid,pgid | awk -v pgid=$$ -v except=${action_timeout_pid} 'BEGIN{childlist=""}{if ($2 == pgid && $1 != pgid && $1 != except) {childlist=childlist" "$1}}END{print childlist}'`
    savelog WARN "timeout for a plugin's action popd. "\
        "kill all child proccesses. (pids =${childrenpids})"
    kill -9 ${childrenpids} > /dev/null 2>&1
    wait ${childrenpids} > /dev/null 2>&1
}

#
# Send SIGUSR1 after sleep to kill all child-processes when the timeout of
# a plugin's action occurs.
# When it receives SIGUSR1, kill_all_children() will be called.
# arg1   : wait time (sec.)
# return : nothing (error exit when failure occurs)
#
kill_plugin_if_timed_out()
{
    typeset waitsec=$1
    #After sending SIGUSR1, sleep for a while to be killed and be waited
    #by main process (in kill_all_children()).
    exec -a kill_plugin_if_timed_out perl -e "sleep $waitsec; kill SIGUSR1, $$;sleep 10" &
    if [[ $? != 0 ]]; then
        savelog ERROR "exec $FUNCNAME failed."
        exit 1
    fi
    savelog debug "set timeout for executing one plugin. "\
        "(waittime=${waitsec}sec. pid=$!)"
}

#
# Unset timeout for a plugin's action which set in kill_plugin_if_timed_out().
# arg1   : plugin name
# return : nothing
#
unset_kill_plugin_timer()
{
    plugin_name="$1"
    timerpid=`jobs -l | awk '$0 ~ /kill_plugin_if_timed_out/ {print $2}'`
    if [[ -n "${timerpid}" ]]; then
        savelog debug "unset timer for ${plugin_name}. (pid=${timerpid})"
        kill -9 ${timerpid} > /dev/null 2>&1
        wait ${timerpid} > /dev/null 2>&1
    fi
}

#
# Execute one real plugin.
# This function's roles are:
#  1) set timeout for the plugin's action.
#  2) execute a real plugin with parameters which are specified in config file.
#  3) wait until the real plugin returns.
#  4) return the real plugin's return code.
# arg1  : section name (written on config file) for the plugin to execute
# arg2- : all arguments which is specified when stonith-wrapper is executed
# return: real plugin's return code
#
exec_one_real_plugin()
{
    secname="$1"
    shift
    action="$1"
    timeout_value=0
    eval plugin_name=\${${secname}_wrapper_real_plugin}

    #Get timeout value and set timer.
    case "${action}" in
        gethosts)
            eval timeout_value=\${${secname}_wrapper_start_timeout}
            ;;
        status)
            eval timeout_value=\${${secname}_wrapper_status_timeout}
            ;;
        on|off|reset)
            eval timeout_value=\${${secname}_wrapper_control_timeout}
            ;;
        *)
            #It does not execute real plugin in other actions.
            return 0
            ;;
    esac
    kill_plugin_if_timed_out ${timeout_value}

    #Get the real plugin's parameters.
    #To realize this, change IFS to '\n' temporarily.
    #Otherwise, when a parameter's value has space,
    #env command misunderstands "oh, there are two or more strings."
    eval real_prmnum=\${#"${secname}"_real_prmlist[*]}
    parameters=""
    cIFS=$IFS
    IFS='
'
    j=0
    while [[ 1 ]]
    do
        if [[ ${j} -ge ${real_prmnum} ]]; then
            break
        fi
        eval tmp=\${${secname}_real_prmlist[${j}]}
        parameters="${parameters}
${tmp}"
        j=`expr ${j} + 1`
    done

    #Now, execute the real plugin!
    #Execute as background to allow main process receive SIGNAL.
    savelog debug "execute [env ${parameters} ${0%/*}/${plugin_name} $@]."
    (env ${parameters} ${0%/*}/${plugin_name} "$@")&
    IFS=${cIFS}
    wait $! > /dev/null 2>&1
    rc=$?
    savelog debug "${action} action with ${plugin_name} is complete. (rc=${rc})"

    return ${rc}
}

#
# Execute all real plugins in the list even if some of them is failed.
# arg1  : all arguments which is specified when stonith-wrapper is executed
# return: 0 -> all real plugins succeeded
#         1 -> one or more real plugins failed
#
exec_real_plugins_all()
{
    action="$1"
    errcnt=0
    i=0
    while [[ 1 ]]
    do
        timer_popd=0
        if [[ ${i} -ge ${pluginnum} ]]; then
            break
        fi
        secname=${seclist[${i}]}
        eval plugin_name=\${${secname}_wrapper_real_plugin}
        exec_one_real_plugin "${secname}" "$@"
        rc=$?
        unset_kill_plugin_timer "${plugin_name}"

        if [[ ${timer_popd} != 0 ]]; then
            savelog ERROR "${plugin_name} failed to do ${action} action. "\
                "(Timed Out)"
            errcnt=`expr ${errcnt} + 1`
        elif [[ ${rc} != 0 ]]; then
            savelog ERROR "${plugin_name} failed to do ${action} action. "\
                "(rc=${rc})"
            errcnt=`expr ${errcnt} + 1`
        fi
        i=`expr ${i} + 1`
    done
    if [[ ${errcnt} -gt 0 ]]; then
        return 1
    fi
    return 0
}

#
# Execute real plugins in the list until it succeeds to do the action.
# arg1  : all arguments which is specified when stonith-wrapper is executed
# return: 0 -> a real plugin succeeded
#         1 -> all real plugins failed
#
exec_real_plugins_till_it_succeed()
{
    action="$1"
    errcnt=0
    i=0
    while [[ 1 ]]
    do
        timer_popd=0
        if [[ ${i} -ge ${pluginnum} ]]; then
            break
        fi
        secname=${seclist[${i}]}
        eval plugin_name=\${${secname}_wrapper_real_plugin}
        exec_one_real_plugin "${secname}" "$@"
        rc=$?

        #Check whether the real plugin succeeds in fencing or not.
        #There are 2 criteria for judging.
        #1)In the case which real plugin's return code is 0,
        #  it means "fencing is complete successfully".
        #2)In the case which real plugin's return code is _not_ 0, then
        #  - If the parameter named "wrapper_nodeip*" are specified for the
        #    plugin, it judges whether fencing is complete or not based on
        #    "nodeips are dead or alive".
        #    When all IP address are dead, it means "fencing succeeded."
        #    When even if one IP address is alive, it means "fencing is failed."
        #  - If the plugin doesn't have any "wrapper_nodeip*", then
        #    it just judges "fencing is failed" from return code.
        if [[ ${rc} = 0 ]]; then
            #action succeeded.
            unset_kill_plugin_timer "${plugin_name}"
            return 0
        fi
        if [[ ${timer_popd} != 0 ]]; then
            savelog ERROR "${plugin_name} failed to do ${action} action. "\
                "(Timed Out)"
        else
            eval nodeip_list=\${!${secname}_wrapper_nodeip*}
            if [[ -n "${nodeip_list}" ]]; then
                savelog info "waiting for the target to be dead."
                while [[ 1 ]];
                do
                    if [[ ${timer_popd} != 0 ]]; then
                        #Wait until timeout occurs, but nodeip* is alive.
                        #It means "fencing is failed!"
                        savelog ERROR "${plugin_name} failed to do "\
                            "${action} action. (nodeip is alive)"
                         break
                    fi
                    ok_if_target_died "${secname}"
                    died=$?
                    if [[ ${died} = 0 ]]; then
                        #All nodeip* are dead.
                        #It means "fencing succeeded!"
                        savelog info "all nodeips are dead."
                        unset_kill_plugin_timer "${plugin_name}"
                        return 0
                    fi
                    sleep 1
                done
            else
                savelog ERROR "${plugin_name} failed to do ${action} action. "\
                    "(rc=${rc})"
            fi
        fi
        unset_kill_plugin_timer "${plugin_name}"
        i=`expr ${i} + 1`
    done
    return 1
}

#
# Output all configurations.
# arg   : nothing
# return: nothing
#
output_all_config()
{
    i=0
    while [[ 1 ]]
    do
        if [[ ${i} -ge ${pluginnum} ]]; then
            break
        fi
        secname=${seclist[${i}]}
        eval plugin_name=\${${secname}_wrapper_real_plugin}
        eval start_timeout=\${${secname}_wrapper_start_timeout}
        eval status_timeout=\${${secname}_wrapper_status_timeout}
        eval control_timeout=\${${secname}_wrapper_control_timeout}
        eval real_prmnum=\${#"${secname}"_real_prmlist[*]}
        real_params=""
        if [[ ${real_prmnum} -gt 0 ]]; then
            #Output all parameters for real plugin.
            eval real_params=\${"${secname}"_real_prmlist[@]}
        fi
        nodeips=""
        eval nodeip_list=\${!${secname}_wrapper_nodeip*}
        if [[ -n "$nodeip_list" ]]; then
            for vname in ${nodeip_list}; do
                eval ip=\$$vname
                nodeips="${nodeips} ${ip}"
            done
        fi
        nodeips=`echo "${nodeips}" | sed 's/^\s*//' | sed 's/\s*$//'`
        savelog debug "plugin[${i}] -> type: ${plugin_name}, "\
            "start_timeout(s): ${start_timeout}, "\
            "status_timeout(s): ${status_timeout}, "\
            "control_timeout(s): ${control_timeout}, "\
            "nodeip: [${nodeips}], "\
            "parameters: [${real_params}]"

        i=`expr ${i} + 1`
    done
}

####################
# Main function
####################

trap 'kill_all_children' SIGUSR1

if ! is_this_leader; then
    become_leader $0 "$@"
fi

savelog debug "Enter: $@"


case "$1" in
    gethosts)
        #Parse settings.
        check_parameters
        parse_config_file
        output_all_config

        #Set action timeout.
        action_timeout=`get_action_timeout_value "$1"`
        quit_if_timed_out ${action_timeout} $$

        #Execute all real_plugins.
        exec_real_plugins_all "$@"
        kill_and_exit $?
        ;;
    on)
        #Parse settings.
        check_parameters
        parse_config_file
        output_all_config

        #Set action timeout.
        action_timeout=`get_action_timeout_value "$1"`
        quit_if_timed_out ${action_timeout} $$

        #Execute real_plugins until STONITH succeeds.
        exec_real_plugins_till_it_succeed "$@"
        kill_and_exit $?
        ;;
    off|reset)
        #Parse settings.
        check_parameters
        parse_config_file
        output_all_config

        ok_if_admin_judges_so

        #Set action timeout.
        action_timeout=`get_action_timeout_value "$1"`
        quit_if_timed_out ${action_timeout} $$

        #Wait for a while if the local node is SBY.
        if check_if_sby; then
            savelog info "sby_wait($sby_wait)"
            sleep $sby_wait
        fi

        #Execute real_plugins until STONITH succeeds.
        exec_real_plugins_till_it_succeed "$@"
        rc=$?
        if [[ ${rc} = 0 ]]; then
            kill_and_exit 0
        fi

        #Wait admin's judgement until action timeout occurs.
        savelog info "waiting administrator's judgement. "\
        "(targetfile=${admins_direction})"
        while true; do
            ok_if_admin_judges_so
            sleep 1
        done
        ;;
    status)
        #Parse settings.
        check_parameters
        parse_config_file
        output_all_config

        #Set action timeout.
        action_timeout=`get_action_timeout_value "$1"`
        quit_if_timed_out ${action_timeout} $$

        #Execute all real_plugins.
        exec_real_plugins_all "$@"
        kill_and_exit 0
        ;;
    getconfignames)
        #No required paramter.
        exit 0
        ;;
    getinfo-devid)
        echo "External STONITH plugin for wrapping plugins"
        exit 0
        ;;
    getinfo-devname)
        echo "External STONITH plugin for wrapping one or more external STONITH plugins"
        exit 0
        ;;
    getinfo-devdescr)
        echo "wrapper for external STONITH plugins."
        echo "To realize the following funcs."
        echo " - set fence timeout for each plugins."
        echo " - kill all child-processes when an operation timeout occurs."
        echo " - avoid fencing each other when Sprit-Brain occurs."
        echo " - realize escalation."
        echo "   (if the plugin with higher priority is failed,"
        echo "    execute the lower one)"
        echo " - confirm that fencing is complete or not with ping command."
        echo "   It's effective when STONITH device's power source is common"
        echo "   with its host machine."
        echo " - allow administrator to break infinity loop which occurs"
        echo "   because all STONITH plugins are failed."
        exit 0
        ;;
    getinfo-devurl)
        echo "http://moin.linux-ha.org/lha/ja/StonithWrapper_ja"
        exit 0
        ;;
    getinfo-xml)
        cat << XML
<parameters>
<parameter name="sby_wait" unique="1" required="0">
<content type="integer" />
<shortdesc lang="en">
Wait sec. for standby node
</shortdesc>
<longdesc lang="en">
time (sec.) to wait before fencing the target node when the local node is standby.
To avoid fencing each other when Split-Brain occurs.
Default is 15.
</longdesc>
</parameter>

<parameter name="no_sfex" unique="1" required="0">
<content type="string" />
<shortdesc lang="en">
Don't check the local is active/standby.
</shortdesc>
<longdesc lang="en">
If it is "true", stonith-wrapper does not check the local node's status ([active|standby]).
If it is not "true" or not set, stonith-wrapper checks that with sfex_stat command before fencing, and wait for a while when it is standby.
Default is "FALSE".
</longdesc>
</parameter>

<parameter name="device" unique="1" required="0">
<content type="string" />
<shortdesc lang="en">
device for sfex's control data
</shortdesc>
<longdesc lang="en">
Block device path that stores exclusive control data.
When the parameter "no_sfex" is not true, this parameter is required.
</longdesc>
</parameter>

<parameter name="index" unique="1" required="0">
<content type="integer" />
<shortdesc lang="en">
block device index for sfex
</shortdesc>
<longdesc lang="en">
Position in block device where exclusive control data for sfex is stored.
1 or more is specified.
Default is 1.
</longdesc>
</parameter>

</parameters>
XML
        exit 0
        ;;
    verify)
        #Check validation of config file.
        parse_config_file
        output_all_config
        exit 0
        ;;
    *)
        exit 1
        ;;
esac

# vim: set ai ts=8 sw=4: