#! /usr/bin/ksh
#
# ident	"@(#)ucmm_reconf.sh	1.9	03/04/17 SMI"
#
# Copyright 1997-2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# set some flags (NOTE: they are not set implicitly in subroutines)
#
# set -x # print commands as executed
# set -e # execute ERR trap on command error
# set -u # treat unset variables as an error
# set -f # disable file name generation

# defined for i18n
TEXTDOMAIN=TEXT_DOMAIN; export TEXTDOMAIN
TEXTDOMAINDIR=MESSAGE_DIR; export TEXTDOMAINDIR

#
# should be used only the reconf_framework
#
pre="SUNWscucm.ucmm_reconf"
RECONF_ROOT=/usr/cluster/lib/ucmm
export RECONF_DIR=${RECONF_ROOT}/reconf.d
export RECONF_SCRIPTS=/usr/cluster/lib/ucmm
reconf_error_file=${RECONF_ROOT}/.ucmm_reconf_error
CLUSTM=/usr/cluster/lib/ucmm/clustm
RECONF_REQUIRED_ERR=205
INCLUDE=.
${INCLUDE} ${RECONF_SCRIPTS}/ucmm_reconf.common

######################################################################
# init - Initialize the Environment for execution of reconf programs.#
######################################################################
function init
{

	#
	# set require path/common directories for execution	
	#
	numnodes_key=cluster.number.nodes;
	clustername_key=cluster.cluster_name;
	modnotconfig="";
	cdbfile=/dev/null;

	#
	# set it in a array to once so that allocation is reduced while checking
	#
	set -A MODULES "${modnotconfig}";

	# All subsequent variables that are defined are automatically exported.
	set -a

        LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/cluster/lib
	CLUSTERSCBIN=/usr/cluster/lib/sc
	CLUSTERBIN=/usr/cluster/lib/ucmm
	CLUSTERVAR=/var/cluster/ucmm
	PATH=/usr/sbin:/usr/bin/:/usr/cluster/bin:${CLUSTERSCBIN}:${CLUSTERBIN}:/usr/ccs/bin/:/bin/:/sbin
	NUMNODES=$(enmatch ${numnodes_key});
	CLUSTNAME=$(enmatch ${clustername_key});

	STEP_VERSION_FILE=${CLUSTERVAR}/step_version_lock

	set +a

	#
	# create temprorary file locations
	#
	logfile=${CLUSTERVAR}/ucmm_reconf.log
	rotate_log "$logfile" 100000

	tmpdir=${CLUSTERVAR}; 

	if [ ! -d $tmpdir ]; then 
		mkdir -p $tmpdir; 
	fi

	export_step_version

	exec 1>> $logfile
	exec 2>> $logfile

	validate_env_vars

	log_info "${pre}" "Step: %s CURRNODES=%s  STEP_VERSION=%s" \
		"${CURRSTEP}" "${CURRNODES}" "${STEP_VERSION}"

}

#############################################################
# rotate_log()
#
#############################################################
rotate_log()
{
	typeset file_size=0
	typeset logfile="$1"
	typeset log_size="${2:-100000}"

	[ ! -f "$logfile" ] && return

	file_size=$(/bin/ls -l "$logfile" | /bin/awk '{print $5}')

	[ $file_size -lt $log_size ] && return

	[ -f ${logfile}.1 ] &&  /bin/rm -f "${logfile}.1"
	[ -f ${logfile}.0 ] && /bin/mv ${logfile}.0 ${plogfile}.1
	/bin/mv "${logfile}" "${logfile}.0"
	/bin/touch "${logfile}"
}

#####################################################################
# validate_env_vars:                                                #
#  This function verifies that the required env variables are set by#
#  clustd, Otherwise it will get it from clustm.                    #
#####################################################################
function validate_env_vars
{

	set -a

        CURRNODES=$(${CLUSTM} getcurrmembers $CLUSTNAME);
        LOCALNODEID=$(${CLUSTM} getlocalnodeid $CLUSTNAME);
	SEQNUM=$(${CLUSTM} getseqnum $CLUSTNAME);
        ALLNODES=$(${CLUSTM} getallnodes ${CLUSTNAME});

	set +a

}
#############################################################
# enmatch : lookup a value in the sc configuration file    #
#############################################################
enmatch() {

	${CLUSTERBIN}/cfgmatch $* ${cdbfile} || \
       	(log_info "${pre}.enmatch" "cfgmatch $* failed"; \
                return 1)

}


##########################################################
# Usage:  Echo the correct usage of the program.         #
##########################################################
_usage() {

	#
        # The following are not public entrypoints
	#
	lmsg="\
	%s [cmmstart|cmmstop|cmmabort]\n\
	%s [cmmstep0-9|cmmreturn]\n\
	The [cmmstart|cmmstepN|cmmstop|cmmabort|cmmreturn]\n\
	commands are invoked by the cluster membership\n\
	monitor during cluster state transitions"
	printf "${lmsg}\n" "${prog}" "${prog}" "${prog}" "${prog}"
        exit 2

}

###################################################################
# Reconfiguration FrameWork for executing reconf programs         #
###################################################################
function check_execution_error
{
        case $2 in

           0)   # success

		lmsg="%s %s %s completed successfully in %s"
		printf "${lmsg}\n" "$(/bin/date)" "${pre}" "$1" "${CURRSTEP}"
                return 0;;
 
           200)   # status ignore
		scds_syslog -p error -t ${pre} -m \
			"%s error status ignored in step %s" \
			"$1" ${CURRSTEP};
                echo $1 >> ${reconf_error_file} 2>/dev/null;
                return 0;;

           ${RECONF_REQUIRED_ERR})
		scds_syslog -p error -t ${pre} -m \
			"%s requests reconfiguration in step %s" \
			"$1" ${CURRSTEP};
		if [[ "${CURRSTEP}" = "cmmabort" ]] ; then
			scds_syslog -p error -t ${pre} -m \
				"%s exited with error %s in step %s" \
				"$1" "${RECONF_REQUIRED_ERR}" ${CURRSTEP};
			sleep 1
			exit 1
		elif [[ "${CURRSTEP}" != "cmmstart" && \
			"${CURRSTEP}" != "cmmreturn" ]] ; then
                   # Reconfigure Status
                   # Means that after the step completes, send
                   # 205 to cmm so that it can reconfigure.
		    reconf_required=1
		fi
		return 0;;
 
           *)   # fail all other cases.
		scds_syslog -p error -t ${pre} -m \
			"%s exited with error %s in step %s" \
			"$1" "$2" ${CURRSTEP};
		# to make sure that it is logged in syslog.
		sleep 1
                exit 1;;
        esac
 
}

##############################################################
#  Check if the module needs to be executed.                 #
#  return 1 if module needs to be  executed.                 #
#  return 0 if module is not configured to be executed OR    #
#           is in reconfiguration error file                 #
##############################################################
function check_module_configured
{

	typeset i
	typeset -i found

	set +e

	modname=$1
	if [ -z ${modname} ]; then
		return 0
	fi

	i=0
	found=1
	while [ ! -z ${MODULES[i]:-""} ]; do
	   if [ ${MODULES[i]} = ${modname} ]; then
		# name found in not configured list.
		return 0;
	   fi
	   i=i+1
	done

	if [ -f ${reconf_error_file} ]; then
		/usr/bin/grep ${modname} ${reconf_error_file}  2>&1 >/dev/null
		if [ $? -eq 0 ]; then
			found=0
		fi
	fi

	return ${found};
}

########################################################################
#								       #
# Execute Reconfiguration Programs:                                    #
#                                                                      #
# Parameter 1: Directory From which we have to execute the             #
#              Reconfiguration Programs.                               #
# Variables  :                                                         #
#   reconf_prog:                                                       #
#        Reconfiguring Programs for StepN                              #
#   current_reconf_prog:                                               #
#        Reconfiguration Programs for Current executionsequence        #
#   cur_seqno,seqno: Execution sequence Numbers.                       #
#   count: count is the number of programs for concurrent execution.   #
#                                                                      #
########################################################################
function execute_reconf
{
	integer i
	integer count=0

	typeset reconfdir=$1
	typeset step_number=${2:-"?"}
	set +e

	#
	# check for the existence of the script file.
	#
	if [ ! -d ${reconfdir} ]; then
		#echo "No actions for this step"
		return 0
	fi
	#
	# Get  the complete listing og the actions to be 
	# to be executed for step N
	cd $reconfdir   
	CURDIR=${PWD}
	set -A reconf_prog $(/bin/ls ??_* 2>/dev/null)

	if [ ${#reconf_prog[*]} -eq 0 ]; then
		#echo "No actions for this step"
		return 0
	fi

	log_info "${pre}" \
		"ucmm reconfiguration step ${step_number} started"
	
	reconf_required=0

	# set the index to start of the array
	i=0
	while [ ! -z  ${reconf_prog[i]:-""} ]
	do
		prog=${reconf_prog[i]}
		#
		# extract sequence number and module name.
		#
		component=${prog#*_}
		cur_seqno=${prog%%_*}
		count=1
		current_reconf_prog=${reconf_prog[i]}
		i=i+1
		check_module_configured ${component}
		if [ $? -eq 0 ]; then
			continue;
		fi
		# collect the items with the same sequence number.
		while [ ! -z ${reconf_prog[i]:-""} ]
        	do
			prog=${reconf_prog[i]}
			seqno=${prog%%_*}
			component=${prog#*_}
			if [ ${cur_seqno} -eq ${seqno} ]
			then
				# check for error in reconf_error_file.
				check_module_configured ${component}
				if [ $? -ne 0 ]; then
					current_reconf_prog="${current_reconf_prog} ${reconf_prog[$i]}"
					count=count+1
				fi
			else
				# No more reconf programs with same seqno.
				break;
			fi
			i=i+1
		done

		# Now execute the reconf programs.
		if [ ${count} -eq 1 ] 
		then
			component=${current_reconf_prog#*_}
			lmsg="%s %s %s started in %s"
			printf "${lmsg}\n" "$(/bin/date)" "${pre}" \
				"${component}" "${CURRSTEP}"
			eval ${CURDIR}/${current_reconf_prog}
			error=$?
			check_execution_error ${component} ${error}
		else
			# Multiple reconfiguration programs to be 
			# executed.
			set -A execute_items ${current_reconf_prog}
			integer execute=0
			while [ ! -z ${execute_items[execute]:-""} ]
			do 
				prog=${execute_items[execute]}
				component=${prog#*_}
				# execute the items
				/bin/rm -rf ${tmpdir}/${component}.${CURRSTEP}
				lmsg="%s %s %s started in %s"
				printf "${lmsg}\n" "$(/bin/date)" "${pre}" \
					"${component}" "${CURRSTEP}"
				(eval ${CURDIR}/${prog} ||  \
					echo $? > ${tmpdir}/${component}.${CURRSTEP}) &
				execute=execute+1
			done   
			# wait for the result
			wait
			# check for the results using the log file.
			execute=0
			while [ ! -z ${execute_items[execute]:-""} ]
			do 
				prog=${execute_items[execute]}
				component=${prog#*_}
				error=0
				if [ -f ${tmpdir}/${component}.${CURRSTEP} ]
				then
					error=$(cat ${tmpdir}/${component}.${CURRSTEP})
				fi
				check_execution_error ${component} ${error}
				execute=execute+1
			done
		fi
		# 
		# if reconfiguration was required by the component
		# then do exeuctue any more components in the step
		# and exit with RECONF_REQUIRED_ERR;
		#
		if [ ${reconf_required} -eq 1 ]; then
			log_info ${pre} \
				"ucmm reconfiguration step ${step_number} completed"
			exit ${RECONF_REQUIRED_ERR};
		fi

	done
	
	log_info "${pre}" \
		"ucmm reconfiguration step ${step_number} completed"
	set -e
}

############################################################
#  Handle All CMM-Transactions requested by clustd         #
############################################################

########################################################################
# called from cluster membership monitor "stop" transition
########################################################################
function cmmstop_cmd
{
	export CURRSTEP=cmmstop

	/bin/rm -rf ${reconf_error_file}

	# Handle RcK.d Trasactions
	execute_reconf ${RECONF_DIR}/rcK.d/ stop
}

########################################################################
# called from cluster membership monitor "abort" transition
########################################################################
function cmmabort_cmd
{
	export CURRSTEP=cmmabort

	/bin/rm -rf ${reconf_error_file}

	# Handle RcA.d Trasactions
	execute_reconf ${RECONF_DIR}/rcA.d/ abort
}

function cmmreturn_cmd
{
	# Handle rcR.d
	execute_reconf ${RECONF_DIR}/rcR.d/ return

}

function cmmstart_cmd
{
	/bin/rm -rf ${reconf_error_file}

	# Handle rcS.d transactions
	execute_reconf ${RECONF_DIR}/rcS.d/ start
}

function cmmstep1_cmd
{

	set -A nodes $(echo ${CURRNODES})
	integer i=0
	names=""
	while [ ! -z ${nodes[i]:-""} ]
	do
		thisname=$(enmatch cluster.node.${nodes[i]}.hostname)
		names="$names ${thisname}"
		i=i+1
	done

	execute_reconf ${RECONF_DIR}/rc1.d/ step1
}

function common_cmmstep
{

	step=$1
	CMMPREFIX=cmmstep
	#
	# Extract "number"
	#
	step_number=${step##$CMMPREFIX}
	#
	# Extract the number of Steps for CDB
	#
	reconfdir=${RECONF_DIR}/rc${step_number}.d/
	execute_reconf ${reconfdir} ${step_number}

}

export_step_version()
{
	# Default step version is version B
	#
	STEP_VERSION="B"


	if [ -f "${STEP_VERSION_FILE}" ]; then
		# Step version file exists
		# Set STEP version to version A
		STEP_VERSION="A"
	fi

	export STEP_VERSION
}

#typeset -ft $(typeset +f)
#
#  End of All Functions 
#
# get program options
prog=$0
cmd=$1

export CURRSTEP=$cmd

set -e
init $*

#
# Dispatch the call.
#
# file descriptor #3 is the original stdout if you need to send
# messages to the interactive user.
#
case ${cmd} in
 
        # async commands - not interactive
	cmmstop)	cmmstop_cmd	3>&1 ;;

	cmmabort)	cmmabort_cmd	3>&1 ;;

	cmmstart | cmmreturn | cmmstep1) \
		eval ${cmd}_cmd  3>&1 ;;

	cmmstep[2-9]| cmmstep[0-9][0-9])
		common_cmmstep $cmd 3>&1 ;;

	*)	_usage ;;
esac
exit 0
