#!/bin/ksh
#
#pragma ident   "@(#)ds_utilities.sh 1.13     01/03/28 SMI" 
#
#   Copyright 11/25/96 Sun Microsystems, Inc.  All Rights Reserved.
#   Copyright 5/20/97 Sun Microsystems, Inc.  All Rights Reserved.
#
#
# Library of shell routines for High Availability scripts.
# This is included at the top of EACH cluster monitor script.
#
#set -x
Myname=`basename $0`

PATH="/opt/SUNWcluster/bin:/usr/5bin:/usr/local/bin:\
/usr/bsd:/usr/ucb:./:$PATH"
export PATH

cdbpath="/etc/opt/SUNWcluster/conf"

CLUSTNAME=$(cat ${cdbpath}/default_clustername)
cdbfile=${cdbpath}/${CLUSTNAME}.cdb
localhostname=$(uname -n)

# "intance keys" table
set +A instKeyTable "NAME" "LOGICAL_HOST" "BASE_DIR" "START" "STOP" "ABORT" \
         "START_NET" "STOP_NET" "ABORT_NET" "STOP_TIMEOUT" "RETRY" \
         "RETRY_TIMES" "RETRY_INTERVAL" "CONF_DIR" "CONF_FILE" "PORT"

# "probe keys" table
set +A probeKeyTable "PROG" "INTERVAL" "TIMEOUT" "CALLBACK" "TAKEOVER" \
         "LOCAL" "REMOTE"

# "instance key functions" table
set +A instKeyFunctionsTable "check_name" "check_logical_host" \
		"check_base_dir" "check_exe_file" "check_exe_file" "check_exe_file" \
		"check_exe_file" "check_exe_file" "check_exe_file" "check_number" \
		"check_yesno" "check_number" "check_number" "check_dir" \
		"check_read_file" "check_number"

# "probe key functions" table
set +A probeKeyFunctionsTable "check_exe_file" "check_number" "check_number" \
		"check_exe_file" "check_yesno" "check_yesno" "check_yesno"

NSHTTP_FORMAT="name:logical_host:base_dir:retry:port"
NSHTTP_PROBE_FORMAT="name:prog:interval:timeout:takeover"

NSNEWS_FORMAT="name:logical_host:base_dir:retry:port"
NSNEWS_PROBE_FORMAT="name:prog:interval:timeout:takeover"

DNS_FORMAT="name:logical_host:start:retry:conf_dir"
DNS_PROBE_FORMAT="name:prog:interval:timeout:takeover"

NSMAIL_FORMAT="name:logical_host:retry_times:retry_interval:conf_file"
NSMAIL_CONF_FORMAT="name:VariableName:Value"
NSMAIL_PROBE_FORMAT="name:prog:interval:timeout:takeover"

DFLT_STOP_TIMEOUT="15"
DFLT_RETRY="n"
DFLT_RETRY_TIMES="0"
DFLT_RETRY_INTERVAL="0"
DFLT_REMOTE="y"
DFLT_LOCAL="n"
DFLT_TAKEOVER="y"

typeset -u CURRENT_SERVICE
CURRENT_SERVICE=""
MY_INST_NAME=""
MY_LOGICAL_HOST=""

ccdfilename=$(ccdadm ${CLUSTNAME} -w)



##############################################################################
#
# function get_hostid hostname
#
# This function takes the hostname as an argument and returns the unique id
# assigned to the host in the static cluster configuration.
#
##############################################################################
function get_hostid
{
#set -x
  typeset i
  typeset numofnodes
 
  if [[ -n "$1" ]]; then
    numofnodes=$(cdbmatch cluster.number.nodes ${cdbfile})
    let i=0
    while (( i < ${numofnodes} )); do
      hostname=$(cdbmatch cluster.node.$i.hostname ${cdbfile})
      if [[ "${hostname}" = "$1" ]]; then
    print $i
    break
      else
    let i=i+1
      fi
    done
  fi   
}
##############################################################################
#
# function get_curr_members
#
#	This function uses the "get_node_status" script in /opt/SUNWcluster/bin
#	to determine the current members of the clusters and sets them in the
#	global variable curr_members
# 
##############################################################################
function get_curr_members
{
#set -x
  typeset node_status

  #print "Checking node status..."
  node_status=$(get_node_status)
  node_status=$(print ${node_status})
  
  curr_members=${node_status##*membership: }
  curr_members=${curr_members%% interconnect*}
}

##############################################################################
# 
# function check_if_valid
#
#	This function checks if its is valid to do a CCD operation
#
##############################################################################
function check_if_valid
{
#set -x
numnodes=$(cdbmatch cluster.number.nodes $cdbfile)

# Get current cluster membership.

get_curr_members

# get the nodeid of the localhost

localnodeid=$(get_hostid "${localhostname}")

# exit if this node is not in the cluster membership

if [[ "${curr_members}" != *${localnodeid}* ]]; then
	print
	print "This node ${localhostname} is currently not a member"
	print "of the ${CLUSTNAME} cluster. Exiting ..."
	print
	exit 1
fi

}
##############################################################################
#
# function get_val_for_key format_row value_row key
#
#	This function "extracts" the value corresponding to the given key
#	from within the value_row. The key should be one of the valid fields
#	in the format_row. Both format_row and value_row are in CCD syntax,
#	ie., the fields are separated by a :
#
##############################################################################
function get_val_for_key
{

typeset -u format_row
typeset value_row
typeset value
typeset -u key
typeset num
typeset i v
typeset keyTable
typeset valueTable

format_row=$1
value_row=$2
key=$3
value=""

set -A keyTable 
format_row="`echo ${format_row} | tr ':' ' '`"
set -A valueTable 
value_row="`echo ${value_row} | tr ':' ' '`"

let i=0
for f in ${format_row}; do
	keyTable[i]=${f}
	let i=i+1
done

let i=0
for v in ${value_row}; do
	valueTable[i]=${v}
	let i=i+1
done

num=${#keyTable[*]}
let i=0  
while (( i < ${num} )); do
    if [[ "${key}" = ${keyTable[i]} ]]; then
        value=${valueTable[i]}
        break
    fi   
    let i=i+1
done 
 
print ${value}
 
}

#####################################################
#
#   get_format service
#
#       service - the name of the service whose corresponding CCD
#                 format is to be returned.
#
#####################################################
function get_format
{

#set -x
typeset format
typeset service

service=$1
format=""

    case ${service} in
        NSHTTP)
            format=${NSHTTP_FORMAT}
            ;;
        NSNEWS)
            format=${NSNEWS_FORMAT}
            ;;
        NSMAIL)
            format=${NSMAIL_FORMAT}
            ;;
        NSMAIL3_CONF)
            format=${NSMAIL_CONF_FORMAT}
            ;;
        NSMAIL4_CONF)
            format=${NSMAIL_CONF_FORMAT}
            ;;
        DNS)
            format=${DNS_FORMAT}
            ;;
        NSHTTP_PROBE)
            format=${NSHTTP_PROBE_FORMAT}
            ;;
        NSNEWS_PROBE)
            format=${NSNEWS_PROBE_FORMAT}
            ;;
        NSMAIL_PROBE)
            format=${NSMAIL_PROBE_FORMAT}
            ;;
        DNS_PROBE)
            format=${DNS_PROBE_FORMAT}
            ;;
        *)
            ;;
    esac

print ${format}

}

##############################################################################
#
#   get_all service
#
#	This function returns all CCD rows of a given servive
#
#       service - the name of the service whose corresponding CCD
#                 entries are to be returned.
#
##############################################################################
function get_all
{
#set -x
typeset rows
rows=$(get $1 "")

#echo "get_all: ${rows}"

print ${rows}

}

##############################################################################
#   get service instance
# 
#   This function returns the CCD row of the given instance of the
#		 given servive
# 
#       service - the name of the service whose corresponding CCD
#                 entry is to be returned. 
#		instance - the "name" of the instance which is to be extracted.
# 
##############################################################################
function get
{
#set -x

typeset rows
typeset i
service=$1
instance=$2

if [[ -z ${ccdfilename} ]]; then
	return
fi

rows=$(scccd -f ${ccdfilename} ${CLUSTNAME} ${service} query name "${instance}")

#print "get ${service} ${instance} ${rows}"

print ${rows}

}
##############################################################################
#   get_ds_lname service lname 
#
#   This function returns the LOGHOST_DS CCD row for the given data 
#	service/logical host name pair
#	(This function will be used by hainetconfig before it calls connect()
#	which does:
#	  scconf <clustername> -s <dsname> <lname>
#	to link a logical host to a data service).
#
#       service - the name of the data service
#       lname - the "logical host name" to which the service is currently
#				linked to in the CCD
#
##############################################################################
function get_ds_lname
{
#set -x
typeset -l service
typeset lname 
typeset rows
typeset i

service=$1
lname=$2
 
LOGHOST_DS_FMT="lname:dsname"

if [[ -z ${ccdfilename} ]]; then 
    return 
fi 
 
rows=$(scccd -f ${ccdfilename} ${CLUSTNAME} "LOGHOST_DS" query ${LOGHOST_DS_FMT} "${lname}:${service}")
 
#print "get ${service} ${instance} ${rows}"
 
print ${rows}
 
}
##############################################################################
#   connect service lname
#
#   This function connects the given data service with the given logical
#	host name using:
#     scconf <clustername> -s <dsname> <lname>
#
#       service - the name of the data service
#       lname - the "logical host name" to which the service is currently
#               linked to in the CCD
#
##############################################################################
function connect
{
#set -x

typeset -l service 
typeset lname
 
service=$1
lname=$2

scconf ${CLUSTNAME} -s ${service} ${lname}
if [[ "$?" -ne 0 ]]; then
    print
    print "Unable to connect data service ${service} with logical host"
	print "name ${lname} using scconf in the Cluster Configuration Database."
    print "Check system console log for errors."
    print
    return 1
fi
}
##############################################################################
#   remove service key value
#
#   This function removes the specified service entry from the CCD
#
#       service - the name of the service whose corresponding CCD
#                 entry is to be removed.
#		key - the "column-list" parameter in the CCD format corresponding
#			  to the service
#		value - the value for the "key" that should be remvoved from the CCD
#
##############################################################################
function remove
{
#set -x

typeset -u service
typeset key
typeset value

service=$1
key=$2
value=$3

#print "${service} ${key} ${value}"


scccd ${CLUSTNAME} ${service} remove ${key} ${value}
if [[ "$?" -ne 0 ]]; then
    print
    print "Unable to remove ${value} from the Cluster Configuration Database."
    print "Check system console log for errors."
    print
    return 1
fi


}
##############################################################################
#   add_unique service format entry
#
#   This function adds the specified unique service entry into the CCD
#
#       service - the name of the service whose corresponding CCD
#                 entry is to be added.
#       format - the "column-list" CCD format corresponding to the service
#       entry - the value for the "format" that should be added to the CCD
#
#	Note: This functions first checks to see if an entry with the same
#		  "instance name" is already present in the CCD. IF present, it
#		  simply returns. Thus, this function will add only unique entries
#		  into the CCD.
#
##############################################################################
function add_unique
{
#set -x
typeset service format entry instance_name

service=$1
format=$2
entry=$3

instance_name=${entry#*:}
instance_name=${instance_name%%:*}


if [[ -z ${ccdfilename} ]]; then 
    return 
fi 
 
rows=$(scccd -f ${ccdfilename} ${CLUSTNAME} ${service} query instance_name ${instance_name})
if [[ -n "${rows}" ]]; then
	print
	print "Error: The specified instance ${instance_name} of ${service}"
	print "already exists in the Cluster Configuration Database."
	print "No changes have been made to the configuration."
	print
	return 1
fi

scccd ${CLUSTNAME} ${service} add ${format} ${entry}
if [[ "$?" -ne 0 ]]; then
	print
	print "Unable to add ${entry} to the Cluster Configuration Database."
	print "Check system console log for errors."
	print
	return 1
fi


}

##############################################################################
#   add service format entry
#
#   This function adds the specified service entry into the CCD
#
#       service - the name of the service whose corresponding CCD
#                 entry is to be added.
#       format - the "column-list" CCD format corresponding to the service
#       entry - the value for the "format" that should be added to the CCD
#
##############################################################################
function add
{
#set -x

typeset service format entry
 
service=$1
format=$2
entry=$3 
 
scccd ${CLUSTNAME} ${service} add ${format} ${entry}
if [[ "$?" -ne 0 ]]; then
    print
    print "Unable to add ${entry} to the Cluster Configuration Database."
    print "Check system console log for errors."
    print
    return 1
fi

}

###########################################################
#
# check_name workfile Name
#   Checks if an instance name is unique within the given workfile
#
#   Return 0 if unique
#   Return !=0 if not unique
#
###########################################################
function check_name
{  
#set -x

    typeset -u service
    typeset inst_name
	typeset sname
	typeset value
	typeset iname
	typeset file
	typeset result

	let result=0
    service=${CURRENT_SERVICE}
	file=$1
    inst_name=$2

    while read sname iname value ; do
        if [[ "${sname}" = "${service}" \
				&& "${iname}" = "${inst_name}" ]]; then
					print "Instance ${inst_name} already exists"
                	let result=1
					break
        fi
    done < ${file}

	return ${result}
}

###########################################################
#
# check_logical_host host
#   Checks if host is a valid logical host in the configuration
#
#   Return 0 if valid
#   Return 1 if not valid
#
###########################################################
function check_logical_host
{
#set -x

	typeset lhost

	lhost=$1

	lhostlist=`haget -f all_logical_hosts`
	for lh in ${lhostlist}; do
		if [[ "${lh}" = "${lhost}" ]]; then
			return 0
		fi
	done

	print "${lhost}: Non-existent logical host"
	return 1
}

###########################################################
#
# check_file file 
#   Checks if file exists
#
#   Return 0 if valid
#   Return 1 if not valid
#
###########################################################
function check_file
{
#set -x

	typeset file

	file=$1

	if [[ -f ${file} ]]; then
		return 0
	else
		print
		print "\t${file}: No such file or directory."
		print
		return 1
	fi

}

###########################################################
#
# check_exe_file file
#   Checks if file exists and is an executable file
#
#   Return 0 if valid
#   Return 1 if not valid
#
###########################################################
function check_exe_file
{
#set -x

    typeset file

    file=$1

	check_file ${file}

	if [[ $? -ne 0 ]]; then
		return 1
	fi

    if [[ -x ${file} ]]; then
        return 0
    else
		print
        print "\t${file}: is not executable"
		print
        return 1
    fi

}

###########################################################
#
# check_read_file file
#   Checks if file exists and is readable
#
#   Return 0 if valid
#   Return 1 if not valid
#
###########################################################
function check_read_file
{
#set -x

    typeset file

    file=$1

    check_file ${file}

    if [[ $? -ne 0 ]]; then
        return 1
    fi

    if [[ -r ${file} ]]; then
        return 0
    else
		print
        print "\t${file}: is not readable"
		print
        return 1
    fi
 
}

###########################################################
#
# check_pathname pathname
#   Checks given pathname is valid
#
#   Return 0 if valid
#   Return 1 if not valid 
# 
########################################################### 
function check_pathname
{
#set -x

	typeset pname

	pname=$1

	if [[ ${#pname} -gt 1024 ]]; then
		print
		print "\t${pname}: Pathname length exceeds system maximum"
		print
		return 1
	else
		return 0
	fi
}

########################################################### 
#
# check_dir dir 
#   Checks if dir is a directory
# 
#   Return 0 if valid 
#   Return 1 if not valid 
# 
########################################################### 
function check_dir
{ 
 
#set -x
    typeset dir
 
    dir=$1 
 
    check_pathname ${dir} 
 
    if [[ $? -ne 0 ]]; then 
        return 1 
    fi 
 
    if [[ -d ${dir} ]]; then 
        return 0 
    else
		print
        print "\t${dir}: is not a directory" 
		print
        return 1 
    fi 
  
}

###########################################################
#
# check_base_dir base_dir
#   Checks if base_dir is a valid entry for the BASE_DIR key
#
#   Return 0 if valid
#   Return 1 if not valid
#
###########################################################
function check_base_dir
{
#set -x

	typeset base_dir
	typeset -L1 first_char

	base_dir=$1
	first_char=$1

	if [[ ${first_char} != "/" ]]; then
		print
		print "\tAbsolute pathname required"
		print
		return 1
	fi

	check_dir ${base_dir}
	result=$?
	return ${result}

}

###########################################################
#
# check_ysesno val 
#   Checks if val is either 'y' or 'n'
#
#   Return 0 if valid
#   Return 1 if not valid
#
###########################################################
function check_yesno
{
#set -x

    typeset -L1 first_char

    first_char=$1

    if [[ ${first_char} != "y" && ${first_char} != "n" ]]; then
		print
        print "\tYes(y) or No(n) indication required."
		print
        return 1
    fi
 
    return 0
 
}

########################################################### 
# 
# check_number num 
#   Checks if num is a valid integer number
# 
#   Return 0 if valid 
#   Return 1 if not valid 
# 
########################################################### 
function check_number
{
#set -x

	typeset num
	typeset result

	num=$1

	result=${num##*([0-9])}

	if [[ -z ${result} ]]; then
		return 0
	else
		print
		print "\t${num} is not a valid integer"
		print
		return 1
	fi

}

###########################################################
#
# validate <attrName> <value> 
#
# validates the given value to be of the same "type" as
# governed by the "attrName"
#
#   Return 0 if valid
#   Return 1 if not valid
#
###########################################################
function validate 
{
#set -x

	typeset num
	typeset i
	typeset name
	typeset value
	typeset function

	let i=0
	name=$1
	value=$2
	function=""

	# Locate "name" in the "instance keys" table
	num=${#instKeyTable[*]}

	while (( i < ${num} )); do
	if [[ "${name}" = ${instKeyTable[i]} ]]; then
		function=${instKeyFunctionsTable[i]}
		break
	fi
	let i=i+1
	done

	if [[ -z ${function} ]]; then
		# search in the "probe key functions table"

		let i=0
    	num=${#probeKeyTable[*]}
 
    	while (( i < ${num} )); do
    	if [[ "${name}" = ${probeKeyTable[i]} ]]; then
        	function=${probeKeyFunctionsTable[i]}
        	break
    	fi
    	let i=i+1
    	done
	fi

	if [[ -z ${function} ]]; then
		print "Error: Unable to locate validation method for ${name}"
		return 1
	fi

	${function} ${value}
	result=$?
	return ${result}

}
##############################################################################
#   source_instance formatrow valuerow instance_num
#
#   This function converts the instance's parameters into shell variables.
#
#		formatrow - this is the "format sequence" as it would appear in
#					the CCD. The fields are separated by a colon.
#		valuerow - this is a list of values corresponding to the formatrows.
#					Again, fields are separated by a colon.
#		instance_num - the number of the instance. This will determine
#					the name of the shell variable.
#
#		Examples:
#					INST_1_STOP_TIMEOUT
#					INST_1_RETRY
#					INST_2_RETRY_INTERVAL
#
##############################################################################
function source_instance
{
#set -x

	typeset formatrow
	typeset valuerow
	typeset -u val
	typeset found_retry
	typeset found_retry_times
	typeset found_retry_interval
	typeset found_stop_timeout
	typeset instance_num

	formatrow=$1
	valuerow=$2
	instance_num=$3

	let found_retry=0
	let found_retry_times=0
	let found_retry_interval=0
	let found_stop_timeout=0

	formatlist="`echo ${formatrow} | tr ':' ' '`"
	valuerow="`echo ${valuerow} | tr ':' ' '`"
	set -A valueList ${valuerow}

	#print "formatlist = ${formatlist}"
	#print "valuerow = ${valuerow}"

	let j=0
	for val in ${formatlist}; do
		if [[ "${val}" = "RETRY" && -n "${valueList[j]}" ]]; then
			let found_retry=1
		elif [[ "${val}" = "RETRY_TIMES" && -n "${valueList[j]}" ]]; then 
            let found_retry_times=1
		elif [[ "${val}" = "RETRY_INTERVAL" && -n "${valueList[j]}" ]]; then 
            let found_retry_interval=1
		elif [[ "${val}" = "STOP_TIMEOUT" && -n "${valueList[j]}" ]]; then 
            let found_stop_timeout=1
		fi

		#echo "_INST_${instance_num}_${val}=\"${valueList[j]}\""
		echo "_INST_${instance_num}_${val}=\"${valueList[j]}\"" >> ${tmp_env}
		let j=j+1
	done

	# output default instance keys & values

	if (( found_retry == 0 )); then
		#echo "_INST_${instance_num}_RETRY=\"${DFLT_RETRY}\""
		echo "_INST_${instance_num}_RETRY=\"${DFLT_RETRY}\"" >> ${tmp_env}
	fi

	if (( found_retry_times == 0 )); then
		#echo "_INST_${instance_num}_RETRY_TIMES=\"${DFLT_RETRY_TIMES}\""
		echo "_INST_${instance_num}_RETRY_TIMES=\"${DFLT_RETRY_TIMES}\"" \
				>> ${tmp_env}
	fi

	if (( found_retry_interval == 0 )); then
		#echo "_INST_${instance_num}_RETRY_INTERVAL=\"${DFLT_RETRY_INTERVAL}\""
		echo "_INST_${instance_num}_RETRY_INTERVAL=\"${DFLT_RETRY_INTERVAL}\"" \
				>> ${tmp_env}
	fi

	if (( found_stop_timeout == 0 )); then
		#echo "_INST_${instance_num}_STOP_TIMEOUT=\"${DFLT_STOP_TIMEOUT}\""
		echo "_INST_${instance_num}_STOP_TIMEOUT=\"${DFLT_STOP_TIMEOUT}\"" \
				>> ${tmp_env}
	fi

	return 0
}

##############################################################################
#   source_probe formatrow valuerow instance_num probe_num
#
#   This function converts the instance's probe parameters into shell variables.
#
#       formatrow - this is the "format sequence" as it would appear in
#                   the CCD. The fields are separated by a colon.
#       valuerow - this is a list of values corresponding to the formatrows.
#                   Again, fields are separated by a colon.
#       instance_num - the number of the instance. This will determine
#                   the name of the shell variable.
#       probe_num - the number of the instance's probe. This will determine
#                   the name of the shell variable.
#
#		Examples:	
#					INST_1_PROBE_1_PROG
#					INST_1_PROBE_2_PROG
#					INST_2_PROBE_3_PROG
#
##############################################################################
function source_probe
{
#set -x
 
    typeset formatrow
    typeset valuerow
    typeset -u val
	typeset instance
	typeset probe 
	typeset j
	typeset found_takeover
	typeset found_remote
	typeset found_local

    formatrow=$1 
    valuerow=$2
	instance=$3
    probe=$4

	let found_takeover=0
	let found_remote=0
	let found_local=0
 
    formatlist="`echo ${formatrow} | tr ':' ' '`"
    valuerow="`echo ${valuerow} | tr ':' ' '`"
    set -A valueList ${valuerow}
 
    #print "formatlist = ${formatlist}"
    #print "valuerow = ${valuerow}"
 
    let j=0
    for val in ${formatlist}; do

		if [[ "${val}" = "TAKEOVER" && -n "${valueList[j]}" ]]; then
			let found_takeover=1
		elif [[ "${val}" = "REMOTE" && -n "${valueList[j]}" ]]; then 
			let found_remote=1
		elif [[ "${val}" = "LOCAL" && -n "${valueList[j]}" ]]; then 
			let found_local=1
		fi

        #echo "_INST_${instance}_PROBE_${probe}_${val}=\"${valueList[j]}\""
        echo "_INST_${instance}_PROBE_${probe}_${val}=\"${valueList[j]}\"" \
				>> ${tmp_env}
        let j=j+1
    done

	# output default probe keys & values

	if (( found_takeover == 0 )); then
		echo "_INST_${instance}_PROBE_${probe}_TAKEOVER=\"${DFLT_TAKEOVER}\"" \
			>> ${tmp_env}
		#echo "_INST_${instance}_PROBE_${probe}_TAKEOVER=\"${DFLT_TAKEOVER}\""
	fi

	if (( found_remote == 0 )); then
		echo "_INST_${instance}_PROBE_${probe}_REMOTE=\"${DFLT_REMOTE}\"" \
				>> ${tmp_env}
		#echo "_INST_${instance}_PROBE_${probe}_REMOTE=\"${DFLT_REMOTE}\""
	fi

	if (( found_local == 0 )); then
		echo "_INST_${instance}_PROBE_${probe}_LOCAL=\"${DFLT_LOCAL}\"" \
				>> ${tmp_env}
		#echo "_INST_${instance}_PROBE_${probe}_LOCAL=\"${DFLT_LOCAL}\""
	fi

    return 0
}

##############################################################################
#   source_env service
#
#   This function converts the service's instance and probe parameters
#	into shell variables
#
#		service - the name of the service whose parameters are to be
#				  converted into shell variables. This name should match the
#				  format name in CCD.
#       Examples:
#
#				source_env NSHTTP
#				source_env NSMAIL
#				source_env NSNEWS
##############################################################################
function source_env
{
#set -x
 
typeset rows
typeset i
typeset j
typeset tmp_env
typeset proberows
typeset instance_list
typeset num
typeset inst_key_list
typeset probe_key_list
typeset format probe_format
typeset -u service
service=$1
tmp_env=/var/opt/SUNWcluster/run/svc_env.$$
touch ${tmp_env} 

let i=1
format=$(get_format ${service})
probe_format=$(get_format ${service}_PROBE)
rows=$(get_all ${service})
instance_list=""

  if [[ -n "${rows}" ]]; then
    for r in ${rows}; do
        instance_name=${r#*:}
        instance_name=${instance_name%%:*}
		instance_list="${instance_list}${instance_name} "
		r=${r#*:}
		source_instance ${format} ${r} ${i}
		let j=1
		proberows=$(get ${service}_PROBE ${instance_name})
		for pr in ${proberows}; do
			pr=${pr#*:}
			source_probe ${probe_format} ${pr} ${i} ${j}
			let j=j+1
		done
		let j=j-1
		echo "_INST_${i}_PROBECOUNT=${j}" >> ${tmp_env}
		#echo "_INST_${i}_PROBECOUNT=${j}"
		let i=i+1
	done
	instance_list=${instance_list% }
	echo "_INST_LIST=\"${instance_list}\"" >> ${tmp_env}
	#echo "_INST_LIST=\"${instance_list}\""
  fi

	# Output the "instance keywords"

	let i=0
	num=${#instKeyTable[*]}
	inst_key_list=""
	while (( i < ${num} )); do
		inst_key_list="${inst_key_list}${instKeyTable[i]} "
    let i=i+1
	done
	inst_key_list=${inst_key_list% }
	echo "_INSTANCE_KWDS=\"${inst_key_list}\"" >> ${tmp_env}
	#echo "_INSTANCE_KWDS=\"${inst_key_list}\""

    # Output the "probe keywords"
 
    let i=0 
    num=${#probeKeyTable[*]} 
    probe_key_list="" 
    while (( i < ${num} )); do   
        probe_key_list="${probe_key_list}${probeKeyTable[i]} "
    let i=i+1 
    done
	probe_key_list=${probe_key_list% }
    echo "_PROBE_KWDS=\"${probe_key_list}\"" >> ${tmp_env}
    #echo "_PROBE_KWDS=\"${probe_key_list}\""

	. ${tmp_env}
	rm -f ${tmp_env} > /dev/null 2>&1
}
#
#	Following subroutines are taken from the Solstice ds_utilities.sh
#	with some modifications
#
loglevel()
{
# XXX
    # Usage: loglevel level msg ...
    # Logs a message.  The level is in the syslog sense, and is passed
    # to the logger command.  
    LOGGER=logger
    UTIL_ha_slogtag=${HA_SLOGTAG:=SUNWcluster}
    UTIL_facility=${HA_SLOGFACILITY:=local7}
    UTIL_LEVEL=$1
    UTIL_LEVELUP="`echo $UTIL_LEVEL | tr "[a-z]" "[A-Z]"`"
    if [ "$UTIL_LEVELUP" = "ERR" ]; then
	UTIL_LEVELUP="ERROR"
    fi
    shift
    UTIL_msg="`echo \"$*\" | sed s/%/%%/g | tr '\012' ' '`"
    UTIL_msg="${UTIL_LEVELUP}: `basename $0`: $UTIL_msg"
    # The logger(1) program is written with a buffer size of 120 for
    # the non-switch arguments.  We used to be friendly to it by
    # truncating our message, roughly:
    #    MAXLOGGERARGS=110
    #    UTIL_msg="`echo $UTIL_msg | fdl_headc $MAXLOGGERARGS`"
    # However, truncating loses too much information.  Instead,
    # we now use fold(1).  Strangely, logger(1) uses a larger buffer
    # of size 200 when taking input from a file, so we use 180
    # for the fold width.  We also use sed to add a continuation
    # indicator to lines other than the first.
    UTIL_foldwidth=180
    echo "$UTIL_msg" | fold -w $UTIL_foldwidth | sed -e '2,$ s/^/'${UTIL_LEVELUP}': CONT:  /' | \
	$LOGGER -p ${UTIL_facility}.${UTIL_LEVEL} -t $UTIL_ha_slogtag
    if [ $? -ne 0 ]; then
	echo "$UTIL_ha_slogtag: $UTIL_msg" >/dev/console
	if [ $? -ne 0 ]; then
	    return 1
	fi
    fi
# XXX
    return 0
}

#
# log_err_file filename     Copies the entire contents of filename to
#			    syslog as an "err" using our facility and tag.
log_err_file()
{
    if [ -z "$1" ]; then
        logerr "Source code error: log_err_file called with no argument"
	return 0
    fi
    if [ ! -s $1 ]; then
	return 0
    fi
    LOGGER=logger
    UTIL_ha_slogtag=${HA_SLOGTAG:=SUNWcluster}
    UTIL_facility=${HA_SLOGFACILITY:=local7}
    sed -e 's/^/ERROR: /' < $1 | \
	$LOGGER -p ${UTIL_facility}.err -t $UTIL_ha_slogtag
    if [ $? -ne 0 ]; then
	cat $1 > /dev/console
	if [ $? -ne 0 ]; then
	    return 1
	fi
    fi
    return 0
}

logalert()
{
    loglevel alert "$*"
}

logerr()
{
	typeset prefix

    prefix=$1
    shift
    log_error $prefix "$(get_logical_host):$(get_inst_name): $*"
}

logwarning()
{
	typeset prefix

    prefix=$1
    shift 
    log_warning $prefix "$(get_logical_host):$(get_inst_name): $*"
}

logwarn()
{
	typeset prefix

    prefix=$1
    shift 
    log_warning $prefix "$(get_logical_host):$(get_inst_name): $*"
}

lognotice()
{
	typeset prefix

	prefix=$1
	shift
	log_info $prefix "$(get_logical_host):$(get_inst_name): $*"
}

logdeb()
{
	typeset prefix

	prefix=$1
	shift
	log_debug $prefix "$(get_logical_host):$(get_inst_name): $*"
}



runerr()
{
    # Usage: runerr cmd args ...
    # Runs cmd with args, redirecting stderr to its own file.
    # If cmd exits zero, just return 0.
    # If cmd exits non-zero, we log an error, including the contents of
    # stderr, and exit 1 from the surrounding script. 
    UTIL_RUNERR_TMPERR=/var/opt/SUNWcluster/run/fdl_runerr.$$
    $* 2>$UTIL_RUNERR_TMPERR
    RC=$?
    if [ $RC -eq 0 ]; then
	rm -f $UTIL_RUNERR_TMPERR
	return 0;
    fi
    logerr "$* exitted non-zero ${RC}, stderr was: `cat $UTIL_RUNERR_TMPERR`"
    rm -f $UTIL_RUNERR_TMPERR
    exit 1
}



#
# list_priv_siblings
#
# Output a list of the private-link network names of our sibling hosts.
#
list_priv_siblings()
{
	myname=`uname -n`
	allhosts=`haget -f all_physical_hosts`
	if [ $? -eq 0 ]; then
		for i in $allhosts; do
			if [ "$myname" = "$i" ]; then
				continue
			fi
			plinks=`haget -f private_links -h $i`
		done
		set $plinks
		priv=$1
	else
		# HA not running, ask for private link name.
		false
		until [ $? -eq 0 ]; do
			echo_n "Please enter a private link name for the sibling server: "
			read priv
			check_reply $priv 
		done
	fi

	echo $priv
}


#
# is_member()
# Usage: is_member element "$SET"
# The second argument should be quoted, as shown.
# Returns 0 for true, 1 for false, ala Unix programs.
#
is_member() {
	for ISM_X in $2 ; do
		if [ "$1" = "$ISM_X" ]; then
			return 0
		fi
	done
	return 1
}


#
# is_subset()
# Usage: is_subset "$FOO" "$BAR"
# The arguments should be quoted, as shown.
# The arguments may be empty lists provided that they are quoted.
# Returns 0 for true, 1 for false, ala Unix programs.
#
is_subset() {
	for ISS_X in $1 ; do
		is_member $ISS_X "$2"
		if [ $? -ne 0 ]; then
			return 1
		fi
	done
	return 0
}


#
# sets_equal()
# Usage:  sets_equal "$FOO" "$BAR"
# The arguments should be quoted, e.g., as shown.
# The arguments may be empty lists provided that they are quoted.
# Returns 0 for true, 1 for false, ala Unix programs.
#
sets_equal() {
	is_subset "$1" "$2"
	if [ $? -ne 0 ]; then
		return 1
	fi
	is_subset "$2" "$1"
	if [ $? -ne 0 ]; then
		return 1
	fi
	return 0
}


#
# set_diff()
# Usage: set_diff "$FOO" "$BAR"
# Computes the set difference: result = FOO - BAR
# The arguments should be quoted, as shown on the Usage line.
#
set_diff() {
	SETDIFF_RESULT=""
	SETDIFF_FIRST=1
	for SETDIFF_X in $1 ; do
		is_member $SETDIFF_X "$2"
		if [ $? -ne 0 ]; then
			if [ $SETDIFF_FIRST -eq 1 ]; then
				SETDIFF_RESULT="$SETDIFF_X"
				SETDIFF_FIRST=0
			else				
				SETDIFF_RESULT="$SETDIFF_RESULT $SETDIFF_X"
			fi
		fi
	done
	echo "$SETDIFF_RESULT"
	return 0
}


#
# cleanstring() -	cleanstring string
#
#	Print the string after cleaning it up for use as a parameter name.
#
cleanstring() {
	echo $* | sed 's/[^a-zA-Z0-9_]/_/g'
}

#
# count_items() -	count_items [items]
#
#	Print the number of items, or arguments
#
count_items() {
	echo $#
}

#
# is_numeric() -	is_numeric string
#
#	Returns zero is the string is numeric
#
is_numeric() {
	if [ -z "$1" ]; then
		return 1
	fi
	[ "`expr $1 : '.*'`" = "`expr $1 : '[0-9]*'`" ]
	return
}

#
# select_item item-# word-list
#
# Select word item-# from word-list.  Numbering begins from 1.
# item-# beyond end of list returns blank.  item-# 0 returns "sh".
# Negative item-# returns undefined results.  Use ksh to handle
# >1-digit item-#'s.
#
select_item()
{
	item=$1
	shift
	ksh -c "set - $* ; eval echo \${$item}"
}

#
# echo_n string
#
# (Conveniently) echo the string without an ending newline.
#
echo_n ()
{
	eval "/usr/bin/echo '$* \c'"
}

#
# y_or_n word
#
# Indicate affirmative (return 0) or negative (return 1) answer.
# XXX - no i18n.
#
y_or_n()
{
	[ "$1" = "" -o `expr "$1" : [yY].*` -gt 0 ] && return 0

	return 1
}

#
# check_reply string
#
# Prompt the user to verify the previously-entered string.
#
check_reply ()
{
	echo "You entered:"
	echo $*
	echo_n "Is this correct (y/n) [y]? "
	read ans
	y_or_n $ans

	return $?
}

#
# getpids() -		getpids command [command_arg]
#
#	Print all pids for "command", optionally qualified with a "command_arg".#
getpids() 
{
        UTIL_command=`basename $1`			  # Binary basename
        UTIL_cmdarg=${2:+"`echo $2 | sed 's-/-\\\/-g'`"}  # Pre-escape /'s

	# Find instances of binary name plain or preceded by a pathname,
	# optionally followed immediately by the command-arg word.
        /usr/proc/bin/pflags /proc/* 2>/dev/null | \
                nawk 'BEGIN { FS=":" } \
/^[0-9]*:	'${UTIL_command}${UTIL_cmdarg:+" $UTIL_cmdarg"}'( |$)/ \
        { print $1 ; next } \
/^[0-9]*:	.*( |\/)'${UTIL_command}${UTIL_cmdarg:+" $UTIL_cmdarg"}'( |$)/ \
        { print $1 }'
 
        return 0
}

#
# establish_cleanup_handler()
#   Establishes trap handlers for calling a cleanup() function.
#
establish_cleanup_handler()
{
    ECH_TRAPSIGNALS="1 2 3 15"
    trap "cleanup ; trap 0 ; exit 1" $ECH_TRAPSIGNALS
}


#
# prog_not_exist_err progname 
#
#   Tests whether progname exists in our path and if not issues
# an error message and returns 1.  Otherwise, returns 0.
#
prog_not_exist_err()
{
    PNE_PATH="`echo $PATH | tr ':' ' '`"
    for PNE_CAND in $PNE_PATH ; do
	if [ -x ${PNE_CAND}/$1 ]; then
	    return 0
	fi
    done
    # This handles case where argument is a full path name:
    if [ -x /$1 ]; then
	return 0
    fi
    logerr "Program $1 does not exist in PATH. Possibly a data service did not get installed properly"
    return 1
}    

# get_config_param <instance_name> <keyword>
#
# The routine assumes that the service instance and probe parameters
# were laready read into (from CCD) and the _INST_* variables were set
# (typically the do_service script will call source_env().)
#
# The routine prints to the standard output the value of the requested
# keyword.
#
# If _INST_LIST is empty or the keyword was not set in the config file,
# it will silently print an empty string.
#

get_config_param ()
{

	#set -x
	inst_name=$1
	inst_key=$2
	val=
	n=0

	for i in $_INST_LIST; do
		n=`expr $n + 1`
		if [ "$i" = "$inst_name" ]; then
			eval "val=\$_INST_${n}_${inst_key}"
			break
		fi
	done

	echo $val
}

# generic_svc action mastered-hosts non-mastered-hosts timeout [ no_idemp ]
#
# Called directly by a top level method.
# action can be: 'start', 'stop', 'start_net', stop_net', 'fm_init',
# 'fm_start', 'fm_stop', 'fm_check_this_host_ok', corresponding to each
# data service method.
# The mastered-hosts, non-mastered-hosts, and timeout args are
# pass-through from the framework.
# no_idemp is an optional flag that when set to 0 means that idempotency
# check is required before calling bundle_do_svc, and 1 means don't do
# the idempotency check (leting bundle_do_svc deal with that).
# no_idemp defaults to 0.
#

generic_svc ()
{
	# First, make global lists of master and non-mastered hosts
	MASTERED_LOGICAL_HOSTS="$2"
	NOT_MASTERED_LOGICAL_HOSTS="$3"

	# Replace comma with space to form an sh word list
	MASTERED_LOGICAL_HOSTS="`echo $MASTERED_LOGICAL_HOSTS | tr ',' ' '`"
	NOT_MASTERED_LOGICAL_HOSTS="`echo $NOT_MASTERED_LOGICAL_HOSTS | tr ',' ' '`"

	# Save timeout
	METHOD_TIMEOUT="$4"

	# get the no_idemp flag. If not set, default to 0
	NO_IDEMP=$5
	[ -z "$NO_IDEMP" ] && NO_IDEMP=0

	## echo "_INST_LIST = $_INST_LIST"

	# Check whether to start this service here
	n=0
	for i in $_INST_LIST; do
		n=`expr $n + 1`

		# Set variables used per-instance lower down
		_INST_NAME=$i
		for keyword in $_INSTANCE_KWDS; do

			varname=_INST_${n}_${keyword}
			#
			# this line of code sets a variable _INST_<KEYWORD> to the
			# content of _INST_<n>_<KEYWORD> only if _INST_<n>_<KEYWORD>
			# was set by the parser. This is done so unset keywords
			# in the config file will not get a corresponding variable,
			# so it will be possible in the config file to set
			# a keyword to null string
			#
			eval "[ \"\${$varname-UnsetVar}\" = \"UnsetVar\" ] || \
				_INST_${keyword}=\$$varname "

		done

		# Set the count of probes for this instance; default 0
		# (PROBECOUNT is not a keyword).
		eval '_INST_PROBECOUNT=${_INST_'$n'_PROBECOUNT:=0}'
		pcount=$_INST_PROBECOUNT
		while [ $pcount -ne 0 ] ; do
			#
			# for each PROBE block, set only the variables that
			# where defined in the config file (and created
			# by the parser)
			#
			for keyword in $_PROBE_KWDS; do
			
				varname=_INST_${n}_PROBE_${pcount}_${keyword}
				eval "[ \"\${$varname-UnsetVar}\" = \"UnsetVar\" ] || \
					_INST_PROBE_${keyword}_${pcount}=\$$varname "

			done

			pcount=`expr $pcount - 1`
		done
			
		# Build private variable defns
		eval priv_vars='"$_INST_'$n'_PRIVATE_VARS"'
		for j in $priv_vars; do
			eval _INST_PRIV_$j='"$_INST_'$n'_PRIV_'$j'"'
		done

		set_inst_name ${_INST_NAME}

		# Apply action to the bundle
		ha_svc_bundle $1 &
	done
	wait
}


#
# ha_svc_bundle action
# 
# For each service in the bundle, execute action as directed by $1.
#
ha_svc_bundle ()
{
	# XXX Future: For bundles with defined PARTs, iteration over the parts
	# goes here; make instance name from $_INST_INSTANCE and PART name.  
	# See Aug 1996 @Home prototype.

	# Check idempotence here, if required
	case "$1" in
	  start | start_net)

		# Pause here to check mastery before checking service
		is_member $_INST_LOGICAL_HOST "$MASTERED_LOGICAL_HOSTS" || return 0

		if [ $NO_IDEMP -eq 0 ]; then
			# If service is running, do nothing
			ha_svc_not_running $_INST_NAME || return 0
		fi
		;;

      fm_start)
 
        if [ $NO_IDEMP -eq 0 ]; then
            # If probe is running, do nothing
            ha_svc_not_running $_INST_NAME.probe || return 0
        fi
        ;;
 
	  stop | stop_net | abort | abort_net)
		# Pause here to check mastery before checking service
		is_member $_INST_LOGICAL_HOST "$NOT_MASTERED_LOGICAL_HOSTS" || return 0

		if [ $NO_IDEMP -eq 0 ]; then
			# If service not running, do nothing
			ha_svc_not_running $_INST_NAME && return 0
		fi
		;;

      fm_stop)

        if [ $NO_IDEMP -eq 0 ]; then
            # If probe not running, do nothing 
            ha_svc_not_running $_INST_NAME.probe && return 0
        fi
        ;;
	esac

	# Execute bundle_do_svc in a subshell, so any variable name 
	# collisions do not corrupt our internal variables.
	( bundle_do_svc $1 )
}


#
# ha_svc_not_running instance_name
#   - return 0 if instance specified by $1 is not running,
#     otherwise return 1.
#	Inverted logic derived from UNIX command exit codes.
#
ha_svc_not_running ()
{

	if [ -z "$1" ]; then
		logerr "Internal error in ha_svc_not_running"
		exit 1
	fi
	pmfadm -l $1 >/dev/null 2>&1
	if [ $? -eq 0 ]; then
		return 1
	else
		return 0
	fi
}
 

#
# ha_svc_report action command status
#
# Test the given status from the attempt to start/stop command.
# Report any errors.  
#
ha_svc_report ()
{
	action_status=0
	action=$1
	cmd=$2
	actstat=$3
	# get stderr output from commands and log if appropriate
	if [ $actstat -ne 0 ]; then
		logerr "Error: $cmd didn't $action successfully."
		err=`cat $errfile`
		rm -f $errfile 2>/dev/null
		lognotice $err
		action_status=1
	fi

	return $action_status
}


#
# set_owner pathname
# 
# Set the variable "prog_owner" with the owner of pathname, otherwise leave
# unset.
#
set_owner()
{
	if [ -x "$1" ]; then
		prog_owner=`ls -l "$1" | awk '{ print \$3 }'`

		# Bogus command or numeric (unknown) owner id?  
		if [ -z "$prog_owner" ] || 
		   expr "$prog_owner" : '[0-9]*' > /dev/null 2>&1; then
			unset prog_owner
			return 1
		fi
		
		return 0
	fi
	return 1
}

# 
# `start_as cmdline`
#
# Output the command line that will execute $0 under its owner's uid.  
# Do this by noting the owner of the program and su-ing to that owner 
# to execute the command.  Leave effective command path in $abs_cmd.
#
# Note: call this routine only for single commands; |, &, and ;
# command lines may not have the desired effect.  Caller should 
# evaluate the output of this command within `` in the calling script.
#
start_as()
{
	case "$1" in
	  /*|*/*)
		abs_cmd="$1"
		set_owner "$abs_cmd"
		;;
	  *)
		for i in `echo $PATH | sed 's/:/ /g'`; do
			abs_cmd="$i/$1"
			set_owner "$abs_cmd" && break
		done
		;;
	esac

	shift
#	echo "$abs_cmd $*"
	echo "/bin/su - '$prog_owner' -c '$abs_cmd $*'"
	return 0
}

#
# need_to_run_probe logical_host localhost
#
# return 0 if yes, need to run
#        1 if no, do not run
#
function need_to_run_probe
{
#set -x

        typeset localnodeid
        typeset precedingnodeid
        typeset num
        typeset found
        typeset myposition masterposition
        typeset master_phyhost
        typeset localhost
        typeset result

        logical_host=$1
        localhost=$2

        master_phyhost="`haget -f master -h ${logical_host}`"
        if [[ -z ${master_phyhost} ]]; then
                return 1
        fi

        VALID_PHY_HOSTS="`haget -f physical_hosts -h ${logical_host}`"
        if [[ -z ${VALID_PHY_HOSTS} ]]; then
                return 1
        fi

        set -A hostsList ${VALID_PHY_HOSTS}

        num=${#hostsList[*]}
        let found=0
        let i=0
        let myposition=-1

        while (( i < ${num} )); do
            if [[ "${master_phyhost}" = "${hostsList[i]}" ]]; then
                let masterposition=$i
            fi
 
            if [[ "${localhost}" = "${hostsList[i]}" ]]; then
                let found=1
                let myposition=$i
            fi
            let i=i+1
        done
 
        if [[ $found -ne 1 ]]; then
            return 1
        fi
 
        # Get current cluster membership.
        get_curr_members
 
        # get the nodeid of the localhost
        localnodeid=$(get_hostid "${localhost}")
 
        # dont have to run probe if this node is not in the cluster membership
        if [[ "${curr_members}" != *${localnodeid}* ]]; then
            return 1
        fi

		# If this node is the current master, we have to run the probe
		if [[ "${master_phyhost}" = "${localhost}" ]]; then
			return 0
		fi
 
        # Now starting from the current Master, traverse right to find
        # the next available (ie., runnung in the cluster) member node
        let i=masterposition
        let found=0
 
        bkupNode=""
        while (( i < ${num}-1 )); do
            nodeid=$(get_hostid ${hostsList[i+1]})
            if [[ -n "${nodeid}" && \
                    "${curr_members}" = *${nodeid}* ]]; then
                # found one - then is the backup node
                bkupNode=${hostsList[i+1]}
                break
            fi
            let i=i+1
        done
 
        # if not found, lets start from the first (ie., 0) and traverse
        # right towards the current master. The first found available
        # node shall be the backup node.
 
        if [[ -z ${bkupNode} ]]; then
            let i=0
            while (( i < ${masterposition} )); do
                nodeid=$(get_hostid ${hostsList[i]})
                if [[ -n "${nodeid}" && \
                    "${curr_members}" = *${nodeid}* ]]; then
                    bkupNode=${hostsList[i]}
                    break
                fi
            	let i=i+1
            done
        fi
 
        if [[ "${bkupNode}" = "${localhost}" ]]; then
            return 0
        else
            return 1
        fi
}


##############################################################################
#
#  set_inst_name  <instance name>
#
#       This function sets instance name in global variable
#       use 'get_inst_name' to obtain the value of instance
#
##############################################################################
set_inst_name ()
{
#set -x
 MY_INST_NAME=$1
 set_logical_host `get_config_param ${MY_INST_NAME} LOGICAL_HOST`
}

##############################################################################
#
#  set_logical_host  <logical host name>
#
#       This function sets value in global variable
#       use get_my_logical_host to obtain the value of logical host
#
##############################################################################
set_logical_host()
{
#set -x
 MY_LOGICAL_HOST=$1
}
##############################################################################
#
#  get_inst_name
#
#       This function returns instance name from global variable
#
##############################################################################
get_inst_name()
{
#set -x
 print ${MY_INST_NAME}
}
##############################################################################
#
#  get_logical_host
#
#       This function returns logical host name from global variable
#
##############################################################################
get_logical_host()
{
#set -x
print ${MY_LOGICAL_HOST}
}
