#!/bin/bash
# BEGIN_ICS_COPYRIGHT8 ****************************************
# 
# Copyright (c) 2015-2017, Intel Corporation
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
#     * Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of Intel Corporation nor the names of its contributors
#       may be used to endorse or promote products derived from this software
#       without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# END_ICS_COPYRIGHT8   ****************************************

# [ICS VERSION STRING: unknown]

PATH=/bin:/sbin:/usr/bin:/usr/sbin

CONFIG_DIR=/etc
CONFIG_FILE=$CONFIG_DIR/opa-fm/opafm.xml	# opafm.info can override
MAX_INSTANCE=7	# largest instance number, for when config file bad

cmd=$1
force=n	# flag to force start even if running
quiet=n	# don't show per manager messages on stop, used by install
[ "$cmd" = "quietstop" ] && quiet=y	# special case for install
shift

usage()
{
	echo "Usage: $0 [start|stop|restart|sweep|status]" >&2
	echo "               [-i instance] [-f] [component|compname|insname] ..." >&2
	echo " start       - start the selected instances/managers" >&2
	echo " stop        - stop the selected instances/managers" >&2
	echo " restart     - restart (eg. stop then start) the selected instances/managers" >&2
	echo " sweep       - force a fabric resweep for the selected instances/managers" >&2
	echo " status      - show status (running/not runing/disabled) for the selected">&2
    echo "               instances/managers" >&2
	echo >&2
	echo " -i instance - an instance to start components for" >&2
	echo "               specified by number (0 to n) or Name" >&2
	echo " -f          - force startup even if appears already started" >&2
	echo " component   - component (sm, or fe) to start for all selected instances" >&2
	echo " compname    - a specific component specified by Name_sm or Name_fe" >&2
	echo " insname     - a specific instance by Name" >&2
	echo >&2
	echo " When -i is used, only the specified components in the instance are started">&2
	echo " -i may be specified more than once to select multiple instances" >&2
	echo " When a compname is specified, that component is started (regardless of -i)" >&2
	echo " When a insname is specified, all components of that instance are started" >&2
	echo " If components are specified, then all components in selected instances are">&2
   	echo " started">&2
	echo " If no arguments are specified, all instances are acted on" >&2
	echo " start, restart and sweep will only act on instances and managers enabled" >&2
	echo " in config file" >&2
	echo " Examples:" >&2
	echo " $0 start -i 0 -i fm1" >&2
	echo "     start all for instance 0,1" >&2
	echo " $0 start -i 0 fm1_sm" >&2
	echo "     start all for instance 0, just sm for instance 1" >&2
	echo " $0 start -i 1 fm0 sm" >&2
	echo "     start sm for instance 1, all for fm0" >&2
	exit 2
}

# just in case no functions script
echo_success() { echo "[  OK  ]"; }
echo_failure() { echo "[FAILED]"; }

my_rc_status_v()
{
	res=$?
	if [ $res -eq 0 ]
	then
		echo_success
	else
		echo_failure
	fi
	echo
	my_rc_status_all=$(($my_rc_status_all || $res))
}

must_be_valid_config()
{
	if [ "$VALID_CONFIG" != "y" ]
	then
		echo "ERROR: Missing or Invalid config file"
		exit 1
	fi
}

allow_invalid_config()
{
	if [ "$VALID_CONFIG" != "y" ]
	then
		echo "Will proceed assuming no more than $(($MAX_INSTANCE+1)) fm instances"
	fi
}

running_manager()
# $1 = instance number
# $2 = manager name in lowercase
{
	pgrep -f "$2 -e ${2}_$1" > /dev/null 2>&1
}

my_rc_status_all=0
my_rc_exit()     { exit $my_rc_status_all; }
my_rc_status()   { my_rc_status_all=$(($my_rc_status_all || $?)); }

temp=$(mktemp "/tmp/ifsfmXXXXX")

# make sure temp file is removed on error.
trap "rm -f $temp" EXIT SIGHUP SIGTERM SIGINT

invalid_config()
{
	local i

	VALID_CONFIG=n
	# set up some default instances to facilitate stop
	init_config
}

test_start()
{
	# $1=parameter name
	# $2=value, must be 0 or 1
	# test value is a valid start value (0 or 1)
	if [ "$2" -eq 0 -o "$2" -eq 1 ]
	then
		return 0
	else
		[ "$quiet" != y ] && echo "Error: $CONFIG_FILE Invalid $1 value: $2" >&2
		invalid_config
		return 1
	fi
}

init_config()
{
	local i

	# set up some default instances to facilitate stop
	i=0
	while [ $i -le $MAX_INSTANCE ]
	do
		INSTANCES_NAME[$i]="fm$i"
		INSTANCES_EXIST[$i]=0
		INSTANCES_SM[$i]=0
		INSTANCES_FE[$i]=0
		i=$(($i + 1))
	done
}

# examine config file for Start tags
# build arrays of instance names and SM/FE start values
# outputs arrays indicating final enabled/disabled Start status for
# each manager in each instance:
# INSTANCES_NAME, INSTANCES_SM, INSTANCES_FE
get_config()
{
	local COMMON_SM_START COMMON_FE_START
	local INSTANCES_START
	local i IFS EXTRACT_CMD

	VALID_CONFIG=y

	# start values from <Common> section
	COMMON_SM_START=1	# Common.Sm.Start
	COMMON_FE_START=1	# Common.Fe.Start

	INSTANCES_START=	# Fm.Shared.Start

	# these arrays are the final output of this function
	# arrays of information about each instance from <Fm> section
	INSTANCES_NAME=		# Fm.Shared.Name
	INSTANCES_EXIST=	# was instance found in config file
	INSTANCES_SM=		# final Start enable for Sm in instance
	INSTANCES_FE=		# final Start enable for Fe in instance
	# manager start requires Fm.Shared.Start
	# Common.x.Start is overall setting, then Fm.X.Start can override

	init_config

	IFS_FM_BASE=/usr/lib/opa-fm
	if [ "$CONFIG_FILE" != $CONFIG_DIR/opafm.xml ]
	then
		Xopt="-X $CONFIG_FILE"
	fi

	$IFS_FM_BASE/bin/config_check -c $CONFIG_FILE
	if [ $? != 0 ]
	then
		[ "$quiet" != y ] && echo "Error: $CONFIG_FILE Invalid" >&2
		invalid_config
		return
	fi

	i=-1
	export IFS=\;
	EXTRACT_CMD="$IFS_FM_BASE/bin/opaxmlextract -H -e Common.Sm.Start -e Common.Fe.Start -e Fm.Shared.Name -e Fm.Shared.Start -e Fm.Sm.Start -e Fm.Fe.Start"
	if [ "$quiet" != y ]
	then
		eval $EXTRACT_CMD < $CONFIG_FILE > $temp
	else
		eval $EXTRACT_CMD < $CONFIG_FILE > $temp 2>/dev/null
	fi
	if [ $? != 0 ]
	then
		[ "$quiet" != y ] && echo "Error: $CONFIG_FILE Invalid" >&2
		invalid_config
		return
	fi
	#cat $temp
	while read com_sm_start com_fe_start ins_name ins_start sm_start fe_start
	do
		if [ ! -z "$com_sm_start" ]
		then
			# Common.Sm section in XML
			if ! test_start Common.Sm.Start "$com_sm_start"; then return; fi
			COMMON_SM_START="$com_sm_start"
		elif [ ! -z "$com_fe_start" ]
		then
			# Common.Fe section in XML
			if ! test_start Common.Fe.Start "$com_fe_start"; then return; fi
			COMMON_FE_START="$com_fe_start"
		elif [ ! -z "$ins_name" ]
		then
			i=$(($i + 1))
			# Fm.Shared section in XML
			INSTANCES_NAME[$i]="$ins_name"
			INSTANCES_EXIST[$i]=1
			if [ -z "$ins_start" ]
			then
				ins_start=0
			fi
			if ! test_start "$ins_name Start" "$ins_start"; then return; fi
			INSTANCES_START[$i]=$ins_start
			INSTANCES_SM[$i]=$((${INSTANCES_START[$i]} && $COMMON_SM_START))
			INSTANCES_FE[$i]=$((${INSTANCES_START[$i]} && $COMMON_FE_START))
		elif [ ! -z "$sm_start" ]
		then
			if ! test_start "$ins_name Sm.Start" "$sm_start"; then return; fi
			INSTANCES_SM[$i]=$((${INSTANCES_START[$i]} && $sm_start))
		elif [ ! -z "$fe_start" ]
		then
			if ! test_start "$ins_name Fe.Start" "$fe_start"; then return; fi
			INSTANCES_FE[$i]=$((${INSTANCES_START[$i]} && $fe_start))
		else
			# unexpected case
			[ "$quiet" != y ] && echo "unexpected"
		fi
	done < $temp
	rm -f $temp
	i=$(($i + 1))
	while [ $i -le $MAX_INSTANCE ]
	do
		INSTANCES_NAME[$i]="fm$i"
		INSTANCES_EXIST[$i]=0
		INSTANCES_SM[$i]=0
		INSTANCES_FE[$i]=0
		i=$(($i + 1))
	done
}

# -i options processed into a temp
# matches to instance name or instance_process added immediately to startup
# sm, fe added to targets list
# after processing all args, add to startup list all targets for all -i instances
# [-i instance_num] [-i instance_name] process_name ...
# start -i 0 sm fe
#	 just start sm and fe for instance 0
# start -i 0 -i 1 sm
# 	just start sm for instance 0,1
# start -i 0 -i 1
# 	start all for instance 0,1
# start -i 0 fm1_sm
# 	start all for instance 0, just sm for instance 1
# start -i 1 fm0 sm
#	start sm for instance 1, all for fm0
# outputs arrays indicating which managers are selected:
#	SEL_SM, SEL_FE
parse_args()
{
	local SM_SEL FE_SEL INSTANCES_SEL
	local ALL_SEL ALL_INSTANCES i param found list

	ALL_INSTANCES=y	# are all instances selected
	INSTANCES_SEL=	# instances selected via -i or name

	ALL_SEL=y		# are all managers selected
	SM_SEL=n		# sm manager explicitly selected
	FE_SEL=n		# fe manager explicitly selected

	# these arrays are the final output of this function
	SEL_SM=			# array of which Sm instances are selected
	SEL_FE=			# array of which Fe instances are selected

	OPTIND=1
	while getopts fi: param
	do
		case $param in
		f)	force=y;;
		i)
			ALL_INSTANCES=n
			if [ $OPTARG -ge 0 ] 2>/dev/null
			then
				INSTANCES_SEL=(${INSTANCES_SEL[@]} $OPTARG)
			else
				i=0
				found=0
				while [ $i -lt ${#INSTANCES_NAME[@]} ]
				do
					if [ "$OPTARG" = "${INSTANCES_NAME[$i]}" ]
					then
						INSTANCES_SEL=(${INSTANCES_SEL[@]} $i)
						found=1
					fi
					i=$(($i + 1))
				done
				if [ $found != 1 ]
				then
					echo "Ignoring unknown instance/target: $OPTARG" >&2
				fi
			fi
			;;
		?) 	usage;;
		esac
	done
	shift $(($OPTIND - 1))
	while [ ! -z "$1" ]
	do
		if [ "$1" = "sm" ]
		then
			SM_SEL=y
			ALL_SEL=n
		elif [ "$1" = "fe" ]
		then
			FE_SEL=y
			ALL_SEL=n
		else
			i=0
			found=0
			while [ $i -lt ${#INSTANCES_NAME[@]} ]
			do
				if [ "$1" = "${INSTANCES_NAME[$i]}" ]
				then
					INSTANCES_SEL=(${INSTANCES_SEL[@]} $i)
					SEL_SM[$i]=1
					SEL_FE[$i]=1
					found=1
				elif [ "$1" = "${INSTANCES_NAME[$i]}"_sm ]
				then
					SEL_SM[$i]=1
					found=1
				elif [ "$1" = "${INSTANCES_NAME[$i]}"_fe ]
				then
					SEL_FE[$i]=1
					found=1
				fi
				i=$(($i + 1))
			done
			if [ $found != 1 ]
			then
				echo "Ignoring unknown instance/target: $1" >&2
			else
				ALL_INSTANCES=n
			fi
		fi
		shift
	done

	# now combine -i and other args
	if [ "$VALID_CONFIG" != "y" ]
	then
		# so stop can work even if invalid config
		list="$(seq 0 $MAX_INSTANCE)"
	elif [ "$ALL_INSTANCES" = y ]
	then
		#echo "All Instances"
		list="$(seq 0 $(( ${#INSTANCES_NAME[@]} -1)) )"
	else
		list="${INSTANCES_SEL[*]}"
		#echo "Just Instances: $list"
	fi
	set -- $list
	for i in "$@"
	do
		#echo "setup $i"
		#set -x
		if [ "$VALID_CONFIG" != "y" ]
		then
			# assume it exists and is enabled
			INSTANCES_EXIST[$i]=1
			INSTANCES_SM[$i]=1
			INSTANCES_FE[$i]=1
		fi
		[ "$SM_SEL" = y -o "$ALL_SEL" = y ] && SEL_SM[$i]=1
		[ "$FE_SEL" = y -o "$ALL_SEL" = y ] && SEL_FE[$i]=1
		#set +x
	done
}

# start a specific manager instance
start_manager()
# $1 = instance number
# $2 = manager name in lowercase
{
	local upcase pid res

	upcase=$(echo $2|tr a-z A-Z)
	echo -n "Starting $upcase $1: ${INSTANCES_NAME[$1]}_$2: "
	if running_manager $1 $2 && [ "$force" = "n" ]
	then
		# already running
		echo "Already running"
		res=1; false
		my_rc_status
	else
 		#$IFS_FM_BASE/runtime/$2 -D -e ${2}_$1 $Xopt &
 		#systemctl start opa$2@$1
		if [ "$2" = "sm" ]; then
		    # start up the SM
		    echo "start 1 $1" > /var/run/opafmd
		else
		    # start up the FE
		    echo "start 2 $1" > /var/run/opafmd
		fi
		res=$?; #pid=$!
		[ $res -eq 0 ] || false
		my_rc_status_v
	fi
	if [ $res -eq 0 ]
	then
		touch /var/lock/subsys/opafm
	fi
	return $res
}

# stop a specific manager instance
stop_manager()
# $1 = instance number
# $2 = manager name in lowercase
# $3 = is manager start enabled
{
	local upcase res i was_running exists

	upcase=$(echo $2|tr a-z A-Z)
	exists=${INSTANCES_EXIST[$1]}
	was_running=0
	running_manager $1 $2 && was_running=1
	if [ $was_running = 1 ]
	then
		if [ "$quiet" != y ]
		then
			echo -n "Stopping $upcase $1: ${INSTANCES_NAME[$1]}_$2: "
		fi
		#killproc "$2 -D -e ${2}_$1"
		res=0
		#pkill -f "$2 -D -e ${2}_$1"
		#systemctl stop opa$2@$1
		if [ "$2" = "sm" ]; then
		    # start up the SM
		    echo "stop 1 $1" > /var/run/opafmd
		else
		    # start up the FE
		    echo "stop 2 $1" > /var/run/opafmd
		fi
		# give it up to a minute to exit
		i=0
		while [ $i -lt 60 ]
		do
			if ! running_manager $1 $2
			then
				break
			fi
			sleep 1
			i=$(($i + 1))
		done
		if [ $i -ge 60 ]
		then
			# still running, kill it hard
			pkill -9 -f "$2 -e ${2}_$1"
			sleep 1
		fi
		res=0; running_manager $1 $2 && res=1
		if [ "$quiet" != y ]
		then
			[ $res -eq 0 ] || false
			my_rc_status_v
		else
			[ $res -eq 0 ] || false
			my_rc_status
		fi
	else
		if [ "$exists" = 1 -a "$3" = 1  -a "$quiet" != y ]
		then
			echo -n "Stopping $upcase $1: ${INSTANCES_NAME[$1]}_$2: "
			echo "Not Running"
			res=0
			#my_rc_status_v
			my_rc_status
		else
			res=0
			my_rc_status
		fi
	fi
	return $res
}

# force resweep of a specific manager instance
# should only be called for managers which are enabled and support sweep
sweep_manager()
# $1 = instance number
# $2 = manager name in lowercase
{
	local upcase pid res

	upcase=$(echo $2|tr a-z A-Z)
	echo -n "Force Sweep $upcase $1: ${INSTANCES_NAME[$1]}_$2: "
	if running_manager $1 $2
	then
 		$IFS_FM_BASE/bin/fm_cmd -i $1 ${2}ForceSweep >/dev/null
		res=$?
		[ $res -eq 0 ] || false
		my_rc_status_v
	else
		echo "Not Running"
		res=1; false
		my_rc_status
	fi
	return $res
}

# used to report error when exclusively PM/FE resweep requested
sweep_na()
# $1 = instance number
# $2 = manager name in lowercase
{
	local upcase

	upcase=$(echo $2|tr a-z A-Z)
	echo -n "Force Sweep $upcase $1: ${INSTANCES_NAME[$1]}_$2: "
	echo "NA for $upcase"; false
	#my_rc_status_v
	my_rc_status
	return 1
}

# check a specific manager instance
status_manager()
# $1 = instance number
# $2 = manager name in lowercase
# $3 = is manager start enabled
{
	local upcase res

	upcase=$(echo $2|tr a-z A-Z)
	if [ ${INSTANCES_EXIST[$1]} = 1 ]
	then
		echo -n "Checking $upcase $1: ${INSTANCES_NAME[$1]}_$2: "
		if running_manager $1 $2
		then
			echo "Running"
			res=0
		elif [ "$3" = 1 ]
		then
			echo "Not Running"
			res=1; false
		else
			echo "Disabled"
			res=0
		fi
	fi
	my_rc_status
	return $res
}

# start all processes which are enabled and selected via command line
start()
{
	local ret i

	echo "Starting IFS Fabric Manager"

	modprobe ib_uverbs

	ret=0
	i=0
	if ! pgrep -f "opafmd -D" > /dev/null 2>&1
	then
		echo "OPA Fabric Manager service not running"
		echo "Please start opafm service and try again."
		exit 1
	fi
	while [ $i -lt ${#INSTANCES_NAME[@]} ]
	do
		if [ 1 = "${INSTANCES_SM[$i]}" -a 1 = "${SEL_SM[$i]}" ]
		then
			start_manager $i sm
			ret=$(($ret || $?))
			sleep 1
		fi
		if [ 1 = "${INSTANCES_FE[$i]}" -a 1 = "${SEL_FE[$i]}" ]
		then
			start_manager $i fe
			ret=$(($ret || $?))
			sleep 1
		fi
		i=$(($i + 1))
	done
	return $ret
}

# stop all processes which are selected via command line
stop()
{
	local ret i

	if [ "$quiet" = y ]
	then
		echo "Stopping all instances of IFS Fabric Manager"
	else
		echo "Stopping IFS Fabric Manager"
	fi
	ret=0
	i=0
	while [ $i -lt ${#INSTANCES_NAME[@]} ]
	do
		if [ 1 = "${SEL_FE[$i]}" ]
		then
			stop_manager $i fe "${INSTANCES_FE[$i]}"
			ret=$(($ret || $?))
		fi
		if [ 1 = "${SEL_SM[$i]}" ]
		then
			stop_manager $i sm "${INSTANCES_SM[$i]}"
			ret=$(($ret || $?))
		fi
		i=$(($i + 1))
	done
	if ! pgrep -f "sm -e sm_" > /dev/null 2>&1 \
		&& ! pgrep -f "fe -e fe_" > /dev/null 2>&1
	then
		rm -f /var/lock/subsys/opafm
	fi
	return $ret
}

# force sweep for all managers which are enabled and selected via command line
sweep()
{
	local ret i swept

	echo "Forcing IFS Fabric Manager Sweep"

	ret=0
	i=0
	while [ $i -lt ${#INSTANCES_NAME[@]} ]
	do
		swept=0
		if [ 1 = "${INSTANCES_SM[$i]}" -a 1 = "${SEL_SM[$i]}" ]
		then
			sweep_manager $i sm
			ret=$(($ret || $?))
			swept=1
		fi
		if [ 1 = "${INSTANCES_FE[$i]}" -a 1 = "${SEL_FE[$i]}" ]
		then
			[ $swept = 0 ] && sweep_na $i fe
		fi
		i=$(($i + 1))
	done
	return $ret
}

# check all processes which are enabled and selected via command line
status()
{
	local ret i

	echo "Checking IFS Fabric Manager"
	ret=0
	i=0
	while [ $i -lt ${#INSTANCES_NAME[@]} ]
	do
		if [ 1 = "${SEL_SM[$i]}" ]
		then
			status_manager $i sm "${INSTANCES_SM[$i]}"
			ret=$(($ret || $?))
		fi
		if [ 1 = "${SEL_FE[$i]}" ]
		then
			status_manager $i fe "${INSTANCES_FE[$i]}"
			ret=$(($ret || $?))
		fi
		i=$(($i + 1))
	done
	return $ret
}

get_config

parse_args "$@"

case "$cmd" in
	start)
		must_be_valid_config
		start;;
	quietstop|stop)
		allow_invalid_config
		stop;;
	restart)
		must_be_valid_config
		stop; start;;
	sweep)
		must_be_valid_config
		sweep;;
	status)
		status;;
	*)
		echo "$0: invalid command \"$cmd\"" >&2
		usage;;
esac
