#!/bin/bash
# This is the glideinWMS factory startup script
# chkconfig: 35 90 30
# description: Starts and stops a glideinWMS factory
# This version is not using the init.d function and modernizing the start/stop for systemd

# SPDX-FileCopyrightText: 2009 Fermi Research Alliance, LLC
# SPDX-License-Identifier: Apache-2.0

# Emulate function library.
success() {
    if [[ "$1" = "-n" ]]; then
        echo -en "\\033[60G[\033[32mOK\033[0m]"
    else
        echo -e "\\033[60G[\033[32mOK\033[0m]"
    fi
    return 0
}

failure() {
    if [[ "$1" = "-n" ]]; then
        echo -en "\\033[60G[\033[31mFAILED\033[0m]"
    else
        echo -e "\\033[60G[\033[31mFAILED\033[0m]"
    fi
    return 1
}

# LSB Exit codes for status:
#0	program is running or service is OK
#1	program is dead and /var/run pid file exists
#2	program is dead and /var/lock lock file exists
#3	program is not running
#4	program or service status is unknown
#5-99	reserved for future LSB use
#100-149	reserved for distribution use
#150-199	reserved for application use
#200-254	reserved
# LSB Exit codes for non status:
#1	generic or unspecified error (current practice)
#2	invalid or excess argument(s)
#3	unimplemented feature (for example, "reload")
#4	user had insufficient privilege
#5	program is not installed
#6	program is not configured
#7	program is not running
#8-99	reserved for future LSB use
#100-149	reserved for distribution use
#150-199	reserved for application use
#200-254	reserved
RET_OK=0
RET_ERROR=1
RET_STATUS_DEADWPIDFILE=1
RET_STATUS_DEADWLOCK=2
RET_STATUS_NOT_RUNNING=3
RET_STATUS_UNKNOWN=4
RET_BAD_SYNTAX=2
RET_NOT_IMPLEMENTED=3
RET_NO_PRIVILEGE=4
RET_NOT_INSTALLED=5
RET_NOT_CONFIGURED=6
RET_NOT_RUNNING=7
RET_NOT_35_READY=150


[ -f /etc/sysconfig/gwms-factory ] && . /etc/sysconfig/gwms-factory

# Variables substituted during the file creation (by cgWCreate.py)
factory_dir=%(factory_dir)s
glideinWMS_dir=%(glideinWMS_dir)s
def_factory_config=%(default_cfg_fpath)s
RPM_INSTALL=%(rpm_install)s

# RPM_INSTALL can be {''|'False'|'True'}
# RPM only if RPM_INSTALL = True
# '' enables autodetection: RPM installation only if this file path is /etc/init.d/gwms-frontend
SCRIPT_NAME="`readlink -f $0`"
#alt: SCRIPT_NAME="$(cd "$(dirname "$0")" 2>/dev/null && pwd)/$(basename "$0")"
if [ -z "$RPM_INSTALL" ]; then
    if [[ "$SCRIPT_NAME" = "/etc/init.d/gwms-factory" ]]; then
        RPM_INSTALL=True
    elif [[ "$SCRIPT_NAME" = "/var/lib/gwms-factory/work-dir/factory_startup" ]] && [[ -e "/etc/gwms-factory/glideinWMS.xml" ]]; then
        RPM_INSTALL=True
    else
        RPM_INSTALL=False
    fi
fi
SCRIPT_NAME="`basename $0`"

if [ "$RPM_INSTALL" = "True" ]; then
    . /etc/rc.d/init.d/functions
    # RPM installation (defaults and binaries)
    LOG_FILE_STARTUP=/dev/null
    DEFAULT_WRITEBACK=no
    BIN_DIR=/usr/sbin/
    TOOLS_DIR=/usr/bin/
    CREATION_DIR=
    FACTORY_START="${BIN_DIR}glideFactory.py"
    FACTORY_STOP="${BIN_DIR}stopFactory.py"
    FACTORY_CHECK="${BIN_DIR}checkFactory.py"
    FACTORY_DOWNTIMES="${BIN_DIR}manageFactoryDowntimes.py"
    # Overriding variables values for RPM installation
    factory_dir=/var/lib/gwms-factory/work-dir
    glideinWMS_dir="$factory_dir"
    def_factory_config=/etc/gwms-factory/glideinWMS.xml
    FACTORY_CONFIG="$def_factory_config"
else
    # TAR installation  (defaults and binaries)
    LOG_FILE_STARTUP=/dev/null
    DEFAULT_WRITEBACK=yes
    BIN_DIR="$glideinWMS_dir/factory/"
    CREATION_DIR="$glideinWMS_dir/creation/"
    FACTORY_START="${BIN_DIR}glideFactory.py"
    FACTORY_STOP="${BIN_DIR}stopFactory.py"
    FACTORY_CHECK="${BIN_DIR}checkFactory.py"
    FACTORY_DOWNTIMES="${BIN_DIR}manageFactoryDowntimes.py"
    FACTORY_CONFIG="$factory_dir/glideinWMS.xml"
fi

check_installed () {
    # check to see if the program is installed (look for FACTORY_START and FACTORY_CHECK)
    if [ -x "$FACTORY_START" -a -x "$FACTORY_CHECK" ]; then
        return 0
    fi
    echo -n "The Factory is not installed correctly"
    failure
    exit $RET_NOT_INSTALLED
}

check_configured () {
    # check if some files created during the configuration (upgrade/reconfig) exist
    if [ -x "$factory_dir/glidein_startup.sh" ]; then
        if [ -r "$factory_dir/glidein.descript" ]; then
            return 0
        fi
        echo -n "The Factory is not configured correctly: try to run reconfig"
    else
        echo -n "The Factory is not configured correctly: try to run upgrade"
    fi
    failure
    exit $RET_NOT_CONFIGURED
}


export HOME="/var/lib/gwms-factory"
export X509_CERT_DIR="/etc/grid-security/certificates"
#export FACTORY_USER=gfactory
FACTORY_USER=gfactory


if [ -r "${factory_dir}/glidein.descript" ]; then
    factory_name="`awk '/^FactoryName /{print $2}' ${factory_dir}/glidein.descript`"
    glidein_name="`awk '/^GlideinName /{print $2}' ${factory_dir}/glidein.descript`"
    force_name_option="-force_name \"$glidein_name\""
else
    factory_name="NEW_INSTALL"
    glidein_name="NEW_INSTALL"
    force_name_option=
fi
id_str="$glidein_name@${factory_name}"


get_user() {
    # logname returns the original user efater a "su" instead I need the current one
    if [ ! -z "$USER" ]; then
        echo $USER
    else
        # whoami is the current user, logname the original
        whoami
    fi
}

# Check that factory user is the owner of the config file
THIS_USER="$(get_user)"
THIS_ID=$(id -u $THIS_USER)
# should check actually the config file in the working directory
# Double percent %% needed for template substitution
FILE_OWNER=$(stat -c%%u "$def_factory_config")
if [ $THIS_ID -eq 0 ]; then
    if [ $(id -u $FACTORY_USER) -ne $FILE_OWNER ]; then
        echo "The Factory user ($FACTORY_USER) must own the configuration file ($def_factory_config).
Use the correct user or edit FACTORY_USER in $0"
        failure
        exit $RET_NO_PRIVILEGE
    fi
else
    if [ $THIS_ID -ne $FILE_OWNER ]; then
        echo "The user invoking this script ($THIS_ID) must be root or must own the configuration file ($def_factory_config)"
        failure
        exit $RET_NO_PRIVILEGE
    fi
fi

# -s /bin/sh necessary because frontend could have /bin/nologin as default
# BSD system: SU_COMMAND="eval sudo -u "
SU_COMMAND="/bin/su -s /bin/sh"
if [ -x "/sbin/runuser" ]; then
    # For SELinux we need to use 'runuser' not 'su'
    SU_COMMAND="/sbin/runuser -s /bin/sh"      # /sbin/runuser
fi

invoke_as_factory() {
    # parameters are the user and the command to invoke
    # using external variables $FACTORY_USER, $SU_COMMAND and $FILE_OWNER
    local user_to_use=$FACTORY_USER
    local user_id=$(id -u $(get_user))
    if [ $user_id -eq 0 ]; then
        # It's root, change user
        $SU_COMMAND $user_to_use -c "$@"
        return $?
    elif [ $user_id = $FILE_OWNER ]; then
        # It's the owner, invoke directly, both su and runuser would ask for password
        eval "$@"
        return $?
    else
        # someone else, raise error
        echo "ERROR: you must be root or the owner of ${def_factory_config}."
        #return 4
        exit 4
    fi
}






help_usage() {
    if [ "$RPM_INSTALL" = "True" ]; then
        # upgrade does not make sense for RPM because the RPM upgrade will take care of it
    #             echo $"Usage: factory_startup {reconfig xml <update_default_cfg> <writeback yes|no>}"
        echo "Usage: $SCRIPT_NAME [-i] {start|stop|restart|status|info|reconfig|down|up|infosysdown|statusdown}
    Without '-i' the RPM version invocation will remain in the foreground also for start
    $SCRIPT_NAME reconfig [NEW_XML_CONFIG_FILE] [-fix_rrd] [update_default_cfg] [{yes|no}] [-comment COMMENT] [-force_delete]
    $SCRIPT_NAME {down|up} -entry factory|entries|ENTRY_NAME [-delay DELAY] [-frontend SEC_NAME] [-security SEC_CLASS|All] [-comment COMMENT]
    $SCRIPT_NAME infosysdown [-delay DELAY] [-frontend SEC_NAME] [-security SEC_CLASS|All] [-comment COMMENT]
    $SCRIPT_NAME statusdown -entry factory|entries|ENTRY_NAME [-delay DELAY]
"
    else
        echo "Usage: $SCRIPT_NAME [-i] {start|stop|restart|status|info|reconfig|down|up|infosysdown|statusdown}
    Without '-i' the RPM version invocation will remain in the foreground also for start
    $SCRIPT_NAME upgrade [NEW_XML_CONFIG_FILE]
    $SCRIPT_NAME reconfig [NEW_XML_CONFIG_FILE] [-fix_rrd] [update_default_cfg] [{yes|no}] [-comment COMMENT] [-force_delete]
    $SCRIPT_NAME {down|up} -entry factory|entries|ENTRY_NAME [-delay DELAY] [-frontend SEC_NAME|All] [-security SEC_CLASS|All] [-comment COMMENT]
    $SCRIPT_NAME infosysdown [-delay DELAY] [-frontend SEC_NAME] [-security SEC_CLASS|All] [-comment COMMENT]
    $SCRIPT_NAME statusdown -entry factory|entries|ENTRY_NAME [-delay DELAY]
"
#                 echo $"Usage: factory_startup {reconfig xml <update_default_cfg> <writeback yes|no>}"
#    $SCRIPT_NAME ress+bdii entries [-delay DELAY] [-frontend SEC_NAME] [-security SEC_CLASS|All] [-comment COMMENT]
# RPM: echo $"Usage: factory_startup $1 -entry 'factory'|'entries'|entry_name [-delay delay] [-frontend sec_name|'All'] [-security sec_class|'All'] [-comment comment]"

    fi
}

start() {
    check_installed
    pushd $factory_dir 1>/dev/null
    check_configured
    echo -n "Starting GlideinWMS Factory $id_str: "

    if "$FACTORY_CHECK" "$factory_dir"  2>/dev/null 1>&2 </dev/null; then
        # already running, so nothing to be done
        echo -n "Already running "
        success
        popd 1>/dev/null
        return
    fi

    if [[ "$RPM_INSTALL" = "True" && -z "$MANUAL_INVOKE" ]]; then
        # Foreground invocation
        # forcing daemon to continue, the factory will check the pidfile
        # daemon --user $FACTORY_USER --force --pidfile="$factory_dir/lock/glideinWMS.lock" -2 "${FACTORY_START} $factory_dir 2>/dev/null 1>&2 </dev/null &"
        "${FACTORY_START}" "$factory_dir"
    else
        # Background invocation as factory user
        invoke_as_factory "nice -2 \"${FACTORY_START}\" \"$factory_dir\" 2>/dev/null 1>&2 </dev/null &"

        for i in {1..15}; do  # Use seq if a variable is needed: $(seq 1 $END)
            # Returns 0 if running
            if "$FACTORY_CHECK" "$factory_dir" >/dev/null 2>&1 </dev/null; then
                break
            fi
            sleep 1
        done
        "$FACTORY_CHECK" "$factory_dir"  2>/dev/null 1>&2 </dev/null && success || failure
        RETVAL=$?
    fi

    popd 1>/dev/null
    return $RETVAL
}

stop() {
    check_installed
    echo -n "Shutting down GlideinWMS Factory $id_str: "

    if ! "$FACTORY_CHECK" "$factory_dir" 2>/dev/null 1>&2 </dev/null; then
        # not running, so nothing to be done
        echo -n "Not running. "
        success
        return
    fi

    if ! invoke_as_factory "\"$FACTORY_STOP\" -f \"$factory_dir\" 2>/dev/null 1>&2 </dev/null"; then
        echo -n "Invocation failed "
        failure
        return 1
    fi

    # check that it has actually stopped
    # can take some time
    for i in {1..30}; do  # Use seq if a variable is needed: $(seq 1 $END)
        # Returns 0 if running, 1 if not
        if ! "$FACTORY_CHECK" "$factory_dir" >/dev/null 2>&1 </dev/null; then
            # RETVAL=0
            break
        fi
        sleep 1
    done

    "$FACTORY_CHECK" "$factory_dir"  2>/dev/null 1>&2 </dev/null && failure || success
    RETVAL=$?
    return $RETVAL
}

restart() {
    # stop successful or already stopped
    if ! stop; then
        # Waiting 30 sec for the shutdown to complete
        echo "Waiting for the GlideinWMS Factory to shutdown" 2>&1
        for i in {1..60}; do  # Use seq if a variable is needed: $(seq 1 $END)
            # Returns 0 if running, 1 if not
            if ! "$FACTORY_CHECK" "$factory_dir" >/dev/null 2>&1 </dev/null; then
                RETVAL=0
                break
            fi
            sleep 1
            echo -n "."
        done
        echo
        if [[ $RETVAL -ne 0 ]]; then
            echo "Failed to shutdown the GlidinWMS Factory. Please check."
            exit $RETVAL
        else
            echo "Shutdown complete"
        fi
    fi
    start
}

reconfig() {
    # RETVAL contains the exit code
    #  0 all OK
    #  6 bad or missing configuration (even if running w/ old configuration)
    #  1 generic error (also start failed)
    check_installed
    shift

    #"$FACTORY_CHECK" "$factory_dir" >/dev/null 2>&1 </dev/null
    #notrun=$?
    if ! "$FACTORY_CHECK" "$factory_dir" >/dev/null 2>&1 </dev/null; then

	    if [[ -f "$1" ]]; then
            echo "Using factory config file arg: $1"
            cfg_loc=$1
            shift
	    else
            echo "Using default factory config file: $def_factory_config"
            cfg_loc=$def_factory_config
	    fi

	    local update_def_cfg_option=
	    local update_def_cfg=no
	    local writeback=$DEFAULT_WRITEBACK
	    local force_delete=""
	    local fix_rrd=""
	    local comment_option=""

	    while (( "$#" )); do
            var=$1
            case "$var" in
		        yes | no) writeback="$var"
                    ;;
		        "-force_delete") force_delete="-force_delete"
                    ;;
		        "-fix_rrd") fix_rrd="-fix_rrd"
                    ;;
		        update_default_cfg) update_def_cfg=yes
                    if [ "$RPM_INSTALL" = "True" ]; then
			            echo "update_def_cfg is not a valid option for RPM installations"
			            help_usage
			            exit 2
                    fi
                    ;;
		        "-comment") comment_option="-comment \\\"$2\\\""
                    shift
                    ;;
		        *)  echo "Unknown argument passed: $var"
                    # echo $"Usage: factory_startup {reconfig xml <update_default_cfg> <writeback yes|no>}"
                    help_usage
                    exit 1
                    ;;
            esac
            shift
	    done
	    if [[ -n "$GLIDEIN_WRITEBACK" ]]; then
            writeback="$GLIDEIN_WRITEBACK"
	    fi

	    echo "Reconfiguring the factory... "
	    pushd $factory_dir 1>/dev/null
	    if [[ "$RPM_INSTALL" = "True" ]]; then
            extra_options=
	    else
            extra_options=" $force_name_option -update_def_cfg $update_def_cfg"
	    fi
	    invoke_as_factory "\"${CREATION_DIR}reconfig_glidein\" -update_scripts no -xml \"$cfg_loc\" -writeback $writeback $fix_rrd $force_delete $comment_option"

	    reconfig_failed=$?
	    popd 1>/dev/null
        echo -n "Factory reconfiguration... "
	    if [[ "$reconfig_failed" -eq 0 ]]; then
	        success
	        RETVAL=0
        else
            failure
            # To return the correct exit code (RET_NOT_CONFIGURED instead of 1)
            RETVAL=$RET_NOT_CONFIGURED
        fi
	    return $RETVAL
    else
        # Factory running, we can proceed only without options
        if [[ $# -eq 0 ]]; then
            echo 'Factory is running. Invoking reconfig command without stopping the service'
            killpid=`awk '/PID/ {print $2}' $factory_dir/lock/glideinWMS.lock`
            kill -1 $killpid
            return
        else
            echo 'Aborting. As Factory is currently running, you can only reload the configuration (default reconfig).'
            echo 'You have to stop the service manually, invoke reconfig with arguments and then start the service again.'
            return 1
        fi
    fi
}

upgrade() {
    # see reconfig for return values and logic explanation
    check_installed
    shift

    if ! "$FACTORY_CHECK" "$factory_dir" >/dev/null 2>&1 </dev/null; then

        local cfg_loc
        if [ -f "$1" ]; then
           echo "Using factory config file arg: $1"
           cfg_loc=$1
           shift
        else
           echo "Using default factory config file: $def_factory_config"
           cfg_loc=$def_factory_config
        fi

        local writeback=$DEFAULT_WRITEBACK
        local force_delete=""
        local fix_rrd=""
        for var in "$@"
        do
            case "$var" in
            yes | no) writeback="$var"
                ;;
            "-force_delete") force_delete="-force_delete"
                ;;
            "-fix_rrd") fix_rrd="-fix_rrd"
                ;;
            *)  echo "Invalid option: $var"
                help_usage
                exit 2
                ;;
            esac
        done

        pushd $factory_dir 1>/dev/null
        if [ "$RPM_INSTALL" = "True" ]; then
            extra_options=
        else # options used only in the tarball version (before the merge)
# fix_rrd is now used all the time just like frontend upgrade()
#            extra_options=" $force_delete $fix_rrd"
            extra_options=" $force_delete"
        fi
        invoke_as_factory "\"${CREATION_DIR}reconfig_glidein\" $force_name_option -writeback yes -update_scripts yes -xml \"$cfg_loc\" -fix_rrd $extra_options"

        reconfig_failed=$?
        popd 1>/dev/null
        echo -n "Upgrading the factory"
        if [[ $reconfig_failed -eq 0 ]]; then
            success
            RETVAL=0
        else
            failure
            RETVAL=$RET_NOT_CONFIGURED
        fi
        return $RETVAL
    else
        echo 'Aborting. upgrade can not be processed because the Factory is running.'
        echo 'You have to stop the service manually, invoke upgrade (with arguments if any) and then start the service again.'
        return 1
    fi
}

downtime() {
    if [[ -z "$3" ]]; then
        #echo $"Usage: factory_startup $1 -entry 'factory'|'entries'|entry_name [-delay delay] [-frontend sec_name] [-security sec_class|\'All\'] [-comment comment]"
        #RPM: echo $"Usage: factory_startup $1 -entry 'factory'|'entries'|entry_name [-delay delay] [-frontend sec_name|'All'] [-security sec_class|'All'] [-comment comment]"
        help_usage
        exit 2
    fi

    if [ "$1" == "down" ]; then
        echo -n "Setting downtime..."
    elif [ "$1" == "up" ]; then
        echo -n "Removing downtime..."
    else
        echo -n "Infosys-based downtime management."
    fi

    "$FACTORY_DOWNTIMES" -cmd $1 -dir "$factory_dir" "$@" </dev/null && success || failure
    RETVAL=$?
    return $RETVAL
}


systemd_reload(){

    if ps $1 | grep glideFactory > /dev/null 2>&1 ; then
       echo "Reload of GWMS Factory Service triggers factory reconfig, PID is $1"
       /bin/kill -HUP $1
    elif ps $1 | grep reconfig_glidein > /dev/null 2>&1 ; then
       echo "a previous Reload of GWMS Factory Service has not completed. No factory reconfig will be performed"
    else
       echo "Cannot detect a running factory. Will not reload or reconfig"
    fi

}

MANUAL_INVOKE=
if [[ "$1" = "-i" ]]; then
    MANUAL_INVOKE=true
    shift
fi

case $1 in
    start)
        start $2
        ;;
    stop)
        stop
        ;;
    force-reload|restart)
        restart
        ;;
    status)
        "$FACTORY_CHECK" "$factory_dir"
        RETVAL=$?
        #TODO: Should it print something?
        ;;
    info)
        shift
        "${CREATION_DIR}info_glidein" "$@" "$FACTORY_CONFIG"
        RETVAL=$?
        ;;
    reconfig)
        reconfig "$@"
        ;;
    upgrade)
        #if [ "$RPM_INSTALL" = "True" ]; then
        #    echo "upgrade is not a valid action for RPM installations"
        #    help_usage
        #    exit 2
        #fi
        upgrade "$@"
        ;;
    down)
        downtime down "$@"
        ;;
    up)
        downtime up "$@"
        ;;
    infosysdown)
        downtime ress+bdii entries "$@"
        ;;
    statusdown)
        if [ -z "$2" ]; then
            #echo $"Usage: factory_startup $1 -entry 'factory'|'entries'|entry_name [-delay delay]"
            help_usage
            exit 1
        fi
        "$FACTORY_DOWNTIMES" -cmd check -dir "$factory_dir" "$@"
        RETVAL=$?
        ;;
    reload)
        if [ "$2" != "" ]; then
            systemd_reload $2
        else
           echo "MUST SUPPLY A PID WITH THIS ARGUMENT"
        fi
        ;;
    *)
        help_usage
        #echo $"Usage: factory_startup {start|stop|restart|status|info|reconfig|down|up|infosysdown|statusdown}"
        exit $RET_NOT_IMPLEMENTED
esac

exit $RETVAL
