#!/bin/ksh
#
#pragma ident "@(#)dbms_utilities	1.40 01/03/27 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.
#

Myname=`basename $0`



# Get the BASEDIR and PRODUCTDIR settings from the installed pkgs
_basedir_etc=`pkgparam SUNWsccf BASEDIR 2>/dev/null`
_basedir=`pkgparam SUNWsc BASEDIR 2>/dev/null`
_productdir=`pkgparam SUNWsc PRODUCTDIR 2>/dev/null`
_basedir_etc=${_basedir_etc:=""}
_basedir=${_basedir:=""}
_productdir=${_productdir:="SUNWcluster"}

cdbpath="${_basedir_etc}/etc/opt/${_productdir}/conf"
CLUSTNAME=$(cat ${cdbpath}/default_clustername)
cdbfile=${cdbpath}/${CLUSTNAME}.cdb

PATH="/opt/SUNWcluster/bin:/usr/5bin:/usr/bin/X11:/usr/local/bin:\
/usr/bsd:/usr/ucb:$PATH"
export PATH
 
PDBCCD="scccd"
CCD_RETRY_COUNT=30
ccdfilename=$(ccdadm ${CLUSTNAME} -w)

localhostname=$(uname -n)
MY_INST_NAME=""
MY_LOGICAL_HOST=""

# "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:DomainName:PostOffice:ProgramDir:MailboxDir:MailUserName:MTAUID:MTAGID:NewUserForms:mh_finger:LHLIBMATCH"
NSMAIL_PROBE_FORMAT="name:prog:interval:timeout:takeover"

HAORACLEDB_FORMAT="monitoring:instancename:logical_host:probe:conn_probe:timeout:restart:db_login:pfile:listener"

HASYBASEDB_FORMAT="monitoring:server:logical_host:probe:conn_probe:timeout:restart:db_login:runfile:bserver:brunfile"

HAINFORMIXDB_FORMAT="monitoring:onconfig:logical_host:probe:conn_probe:timeout:restart:db_name:informixserver"

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=""

##############################################################################
# lookup a value in the configuration file
enmatch() {
        cdbmatch $* ${cdbfile} || \
                (print "$pre.4001" "cdbmatch $* ${cdbfile} failed" 1>&2; return 1)
}
 
# lookup a value in ccd statiction file
enccdmatch() {
        ccdmatch $* ${ccdfile} || \
                (log_error "$pre.4703" "ccdmatch $* ${ccdfile} failed" 1>&2; return 1)
}

##############################################################################
#
# 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
{
  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
{
  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*}
}
##############################################################################
# is_node_cluster_member
#
# input: node id
# output: true if nodeid is a cluster member
#         false otherwise.
 
function is_node_cluster_member
{
nodeid="$1"
 
get_curr_members
 
# exit if this node is not in the cluster membership
 
if [[ "${curr_members}" != *${nodeid}* ]]; then
  return 1
fi
 
return 0
 
}
##############################################################################
# 
# function check_if_valid
#
#	This function checks if its is valid to do a CCD operation
#
##############################################################################
function check_if_valid
{

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

}
#####################################################
#
#  disk_mounted  instance_name
#
# This is a temp routine that will be phased out
# (or just changed) once the admin filesystem framework
# issues have been worked out.
#####################################################

disk_mounted()
{
        if [[ -n "$1" ]]; then
                db_instance=$(get_instance "${ds}" "$1")
# if db_instance is null then $1 is not a db instance. In that
# case we will assume that the paramter passed is already
# a logical host. If its not a logical host that is ok
# because the disk_lh_mounted routine will not fail. It
# will simply return a null, just as if the parameter were
# a legal logical host (disk group really) but not mounted.
                if [[ -n "$db_instance" ]]; then
                        read_ha_databases $1
                        if [[ -n "$logical_host" ]]; then
                                disk_lh_mounted $logical_host
                        fi
                else
                        disk_lh_mounted $1
                fi
        fi
}

#####################################################
#
#  disk_lh_mounted logical_host
#       This function returns the disk group this
#       logical host currently has mounted
#
disk_lh_mounted()
{
typeset mount_points 
typeset admin_fs
 
        admin_fs=$(haget -f pathprefix -h $1)
        mount_points=$(mount -p | awk '{print $3}' | grep "^$admin_fs$")
        echo $mount_points
		
#typeset adisk 

#        disk_group=$(${PDBCCD} -f ${ccdfilename} -r 5 -w 5 ${CLUSTNAME} LOGHOST  \
#		query lname $1 | awk -F: '{print $4}' | tr ',' ' ')
#	for adisk in $disk_group
#	do
#		vxdg -q list | grep "^$adisk " | awk '{print $1}'
#	done

}

#####################################################
#
#   get_diskgroups mastered not_mastered
#
#       mastered - logical host that this physical host
#                  is currently the master of. This is
#                  the info returned from
#                  haget -f mastered.
#
#       not_mastered - logical host that this physical host
#                      is not currently the master of. This

#                      is the same info as returned from:
#                      haget -f not_mastered.
#
#  output: none directly, but will set the following
#          variables:
#
#       HA_REMOTEHOST
#       HA_ALL_METASETS
#       HA_METASETSERVE
#       HA_NO_METASETSERVE
#       HA_NATIVE_DISKSET
#       HA_FOREIGN_DISKSET
#
function get_diskgroups
{
        service=LOGHOST
        service_ds=LOGHOST_DS
        mastered=$1
        not_mastered=$2
 
        diskgroups=""
        no_diskgroups=""
 
 
        localhost=$(uname -n)
 
# This gives me all the diskgroups for the logical hosts
# mastered on this localhost and are connected with a
# data service of dataservice.
        n=""
        for mhost in $mastered
        do
 
                for n in $(${PDBCCD} -f ${ccdfilename} ${CLUSTNAME} ${service} \
                  query lname "${mhost}" | awk -F: '{print $4}')
                do
                        diskgroups="$diskgroups $n"
                done
        done
 
# This gives me all the diskgroups for the logical hosts
# that are not mastered on this localhost and are connected with a
# data service of dataservice.
        n=""
        for mhost in $not_mastered
        do
                for n in $(${PDBCCD} -f ${ccdfilename} ${CLUSTNAME} ${service} \
                  query lname "${mhost}" | awk -F: '{print $4}')
                do
                        no_diskgroups="$no_diskgroups $n"
                done
        done
 
        all_diskgroups="${diskgroups} ${no_diskgroups}"
 
        imported_dgs=$(vxdg -q list | grep -v rootdg | awk '{print $1}')
 
        if [[ -z "$imported_dgs" ]];then
                deported_dgs="$all_diskgroups"
        else
                left=${all_diskgroups%$imported_dgs*}
                right=${all_diskgroups#*$imported_dgs}
                deported_dgs="${left} ${right}"
        fi
 
        hosts_in_ds=$(${PDBCCD} -f ${ccdfilename} ${CLUSTNAME} ${service} \
          query lname "${mastered}" | awk -F: '{print $3}')
 
        SAVEIFS="$IFS"
        IFS=','$IFS
        set -- $hosts_in_ds >/dev/null
        if [[ $# -ge 2 ]]; then
# A case that we really don't know what to do with. Legally
# this host list can only contain two hosts at most. This is because
# a disk can only be mastered on two hosts currently. The problem
# is that when setting up the environment, scconf will not CHECK to
# see that the physical host list only contains two hosts. So it
# is possible for the user to add three or four or .... In this
# case I am only going to deal with the first two hosts.
                if [[ "$1" = "$localhost" ]]; then
                        remotehost="$2"
                elif [[ "$2" = "$localhost" ]]; then
                        remotehost="$1"
                else
# Well lets see... if the localhost is not in the list then we
# cannot determine the remote host, ie. this node is not one of
# the two legal hosts that can master this disk groups in question.
# In most (and likely all) cases this would be because of a setup
# error and not something that will just 'happen'. I don't believe
# if we have gotten far enough to call this routine that this
# condition can ever happen.
 
                        remotehost=""
                fi
        else
# if there is only one host in this list then this is an error.
                remotehost=""
        fi
        IFS="$SAVEIFS"
 
        HA_REMOTEHOST="$remotehost"
        HA_METASETSERVE=$(echo ${imported_dgs})
        HA_NO_METASETSERVE=$(echo ${deported_dgs})
        HA_NATIVE_DISKSET=$(echo ${diskgroups})
        HA_FOREIGN_DISKSET=$(echo ${no_diskgroups})
        HA_ALL_METASETS=$(echo ${all_diskgroups})
 
 
}

##############################################################################
#
# function get_private_links logical_host
#
# return an array of the following: privlink1, privlink2, remotehost up/down,
#    ha remotehost up/down, status of privlink1, status if privlink2.
##############################################################################
function get_private_links
{
if [[ $# -lt 1 ]]; then
	abort "No valid parameters passed to get_private_links."
fi
logical_host="$1"; shift;
my_node=`uname -n`

if [[ -n "$logical_host" ]]; then
        res=`haget -f physical_hosts -h $logical_host`
else
        abort "Cannot determine host members."
fi
set -- $res > /dev/null
# We only deal with the first two nodes
# since we are in a two-node mode right now.
if [ $# -ge 2 ]; then
 if [ "$1" = "$my_node" ]; then
        second_node=$2
 elif [ "$2" = "$my_node" ]; then
        second_node=$1
 else
        abort "Bad HOSTNAME line in hadfconfig."
 fi
 else
	abort "Phyiscal host list for logical host $logical_host invalid."
fi

# Get the remote nodes ip address from the cdb file.
localhostid=`get_hostid $my_node`
remhostid=`get_hostid $second_node`
PRIV1=`enmatch cluster.node.${remhostid}.phost.0`
PRIV2=`enmatch cluster.node.${remhostid}.phost.1`

res=`/usr/sbin/ping $PRIV1 5`
priv1res=$?
res=`/usr/sbin/ping $PRIV2 5`
priv2res=$?

remhost_haup="false"
remhostup="false"
if [ $priv1res -eq 0 -o $priv2res -eq 0 ]; then
        # we know the other machine is up
        remhostup="true"
        is_node_cluster_member $remhostid
        isremotemember=$?
        is_node_cluster_member $localhostid
        islocalmember=$?
        if ((  $isremotemember == 0  )) && (( $islocalmember == 0 )); then
                HA_MEMBERSHIP="BOTH"
        elif (( $islocalmember == 0 )); then
                HA_MEMBERSHIP="JUSTME"
        else
                HA_MEMBERSHIP=""
        fi
 
        #check if both hosts have HA running
        if [ "$HA_MEMBERSHIP" = "BOTH" ] ; then
                remhost_haup="true"
        fi
fi

print "$PRIV1 $PRIV2 $remhostup $remhost_haup $priv1res $priv2res"

}

##############################################################################
#
# function get_logical_links logical_host
#
# return an array of the following: logicallink1,logicallinkn...
##############################################################################
function get_logical_links
{
if [[ $# -lt 1 ]]; then
	abort "4500" "No valid parameters passed to get_logical_links."
fi
logical_host="$1"; shift;
my_node=`uname -n`

if [[ -n "$logical_host" ]]; then
        res=$(haget -f physical_hosts -h $logical_host | tr '\012' ' ')
else
        abort "4140" "Cannot determine host members."
fi

# if the physical hosts list does NOT contain this host (ie.
# this host is not part of the logical host group), then we
# don't need to cut anything out of 'res'. Otherwise yes.

if print "$res" | /bin/grep "$my_node" >/dev/null 2>&1
then
#
# NOTE: need to subtract out 'my_node'....
	left=${res%$my_node*}
	right=${res#*$my_node}
	hostlist="${left} ${right}"
else
	hostlist="${res}"
fi
#
for hostname in ${hostlist[*]}
do

  hostid=$(get_hostid $hostname)
  is_node_cluster_member $hostid
  isremotemember=$?
  if ((  $isremotemember == 0  )); then
  	loglink=$(enmatch cluster.node.${hostid}.hahost)
	links="${links} ${hostname} ${loglink}"
  fi
 
done

print "${links}"

}

##############################################################################
#
# 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
{

typeset format
typeset service

service=$1
format=""

    case ${service} in
        NSHTTP)
            format=${NSHTTP_FORMAT}
            ;;
        NSNEWS)
            format=${NSNEWS_FORMAT}
            ;;
        NSMAIL)
            format=${NSMAIL_FORMAT}
            ;;
        NSMAIL_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}
            ;;
        HAORACLE)
            format=${HAORACLEDB_FORMAT}
            ;;
        HASYBASE)
            format=${HASYBASEDB_FORMAT}
            ;;
        HAINFORMIX)
            format=${HAINFORMIXDB_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
{

# get the row(s) from the ccd. Strip off the leading
# format string since that is of little value here.
rows=$(get $1 ""|sed 's/.[^:]*.\(.*\)/\1/')

#print "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
{
ds="$1"
instance="$2"

set -A retarray $(get_dbms_service "$ds")

service=${retarray[0]}

rows=$(${PDBCCD} -f ${ccdfilename} ${CLUSTNAME} ${service} query monitoring "${instance}")

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

print "${rows}"

}
#######################################################
# function get_dbms_service <dataservice>
#
#######################################################
function get_dbms_service
{

ds="$1"

case ${ds} in
	"oracle") 
		service="HAORACLE"
		colume_value="instancename";;
	"sybase") 
		service="HASYBASE"
		colume_value="server";;
	"informix") 
		service="HAINFORMIX"
		colume_value="onconfig";;
esac

print "${service} ${colume_value}"

}
#######################################################
# function get_instance <dataservice> <key_value>
#
#######################################################
function get_instance
{
 
typeset rows
typeset i
ds=$1; shift
key_value=$1

set -A retarray $(get_dbms_service "$ds")

service=${retarray[0]}
colume_value=${retarray[1]}

sedcmd="s/^${service}://"
 
retry=3
rows=""

while [ $retry -gt 0  ]; do
        rows=$(${PDBCCD} -f ${ccdfilename} ${CLUSTNAME} ${service} query ${colume_value} "${key_value}")
        if [[ -n "${rows}" ]]; then
                break
        fi
        sleep 1
        retry=`expr $retry - 1`
done

rows="`echo "${rows}" | sed -e "$sedcmd" | tr ':' '\11' `"

print "${rows}"
 
}
#######################################################
# function get_instance_dynamic <dataservice> <key_value>
#
#######################################################
function get_instance_dynamic
{

typeset rows
typeset i
ds=$1; shift
key_value=$1

set -A retarray $(get_dbms_service "$ds")

service=${retarray[0]}
colume_value=${retarray[1]}

sedcmd="s/^${service}://"


rows=$(${PDBCCD} -r ${CCD_RETRY_COUNT} -w 5 ${CLUSTNAME} ${service} query ${colume_value} "${key_value}")
rows="`echo "${rows}" | sed -e "$sedcmd" | tr ':' '\11' `"

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
{
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=$(${PDBCCD} -f ${ccdfilename} ${CLUSTNAME} "LOGHOST_DS" query ${LOGHOST_DS_FMT} "${lname}:${service}")
 
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
{

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
{

typeset -u service
typeset key
typeset value

ds=$1
key=$2
value=$3

set -A retarray $(get_dbms_service "$ds")

service=${retarray[0]}

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

${PDBCCD} -r ${CCD_RETRY_COUNT} -w 5 ${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

}
##############################################################################
function add_unique_ha_instance
{

ds="$1" 
shift

set -A retarray $(get_dbms_service "$ds")

service=${retarray[0]}
colume_value=${retarray[1]}
 

key_value=$(echo "$1" | awk '{print $2}')

entry=$(echo "$1" | tr '\11' ':')
 
format=$(get_format "$service")

rows=$(${PDBCCD} -r ${CCD_RETRY_COUNT} -w 5 ${CLUSTNAME} ${service} query "${colume_value}" "${key_value}")
if [[ -n "${rows}" ]]; then
        print
        print "Error: The specified instance ${key_value} of ${service}"
        print "already exists in the Cluster Configuration Database."
        print "No changes have been made to the configuration."
        print
        return 1
fi

${PDBCCD} -r ${CCD_RETRY_COUNT} -w 5 ${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_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
{
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=$(${PDBCCD} -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

${PDBCCD} -r ${CCD_RETRY_COUNT} -w 5 ${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
{

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

${PDBCCD} -r ${CCD_RETRY_COUNT} -w 5 ${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
{  

    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
{

	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
{

	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
{

    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
{

    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
{

	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
{ 
 
    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
{

	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
{

    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
{

	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 
{

	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
{

	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
{
 
    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
{
 
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
#set -x
    # 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
#set -x
    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
}

log_dbms()
{
    print -r $(date) ":" $@ >> $HA_VAR/hadbms.log
}

logalert()
{
    loglevel alert "$*"
}

logerr()
{
	typeset prefix

	prefix="${PREFIX}.${1}"
	shift
	
        log_error $prefix "$(get_logical_host):$(get_inst_name): $*"
        log_dbms $prefix "$(get_logical_host):$(get_inst_name): $*"
}

logwarning()
{
	typeset prefix
 
        prefix="${PREFIX}.${1}"
        shift
 
        log_warning $prefix "$(get_logical_host):$(get_inst_name): $*"
        log_dbms $prefix "$(get_logical_host):$(get_inst_name): $*"

}

logwarn()
{
        typeset prefix
 
        prefix="${PREFIX}.${1}"
        shift

        log_warning $prefix "$(get_logical_host):$(get_inst_name): $*"
        log_dbms $prefix "$(get_logical_host):$(get_inst_name): $*"
}

lognotice()
{

        typeset prefix
 
        prefix="${PREFIX}.${1}"
        shift
        log_info $prefix "$(get_logical_host):$(get_inst_name): $*"
        log_dbms $prefix "$(get_logical_host):$(get_inst_name): $*"
}

logdeb()
{
        typeset prefix
 
        prefix="${PREFIX}.${1}"
        shift
 
        log_debug $prefix "$(get_logical_host):$(get_inst_name): $*"
        log_dbms $prefix "$(get_logical_host):$(get_inst_name): $*"
}

logprint()
{
	print "$*"
	log_dbms $@
}

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()
{
#set -xv
	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 ()
{
#set -x
	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 ()
{

	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

		# Apply action to the bundle
		ha_svc_bundle $1
	done
}


#
# 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
}

##############################################################################
#
# function get_hostname hostid
#
# This function takes the hostname as an argument and returns the unique id
# assigned to the host in the static cluster configuration.
#
##############################################################################
function get_hostname
{
  typeset i
  typeset hostname

      hostname=$(cdbmatch cluster.node.$1.hostname ${cdbfile})
    print $hostname
}

#
# need_to_run_probe logical_host localhost
#
# return 0 if yes, need to run
#       >= 1 if no, do not run (gives detailed explaination)
#      1 - I am not immediate backup
#      2 - No current master
#      3 - No logical host specified
#      4 - no valid phycical hosts
#	
#
	
function need_to_run_probe
{

typeset master
typeset mynode="$2"
typeset backup=
typeset logical_host=$1
typeset a_host

if [[ -n "$logical_host}" ]]; then
	master="`haget -f master -h ${logical_host}`"
   	if [[ -z ${master} ]]; then
       		 return 2
   	fi
else
   return 3
fi

[ "$master" == "$mynode" ] && return 0

VALID_PHY_HOSTS=$(haget -f physical_hosts -h $logical_host | tr '\012' ' ' )
if [[ -z ${VALID_PHY_HOSTS} ]]; then
    return 4
fi

get_curr_members
node_list=${curr_members}

backup=
typeset next_one=0

for j in $node_list; do
   a_host=$(get_hostname $j)
   is_member "$a_host" "$VALID_PHY_HOSTS" || continue
   : ${backup:="$a_host"}
   if [ $next_one -eq 1 ]; then
      backup="$a_host"
      break
   fi

   [ "$a_host" == "$master" ] &&  next_one=1

done
: ${backup:=""}
   
#print "backup will be=$backup"

[ "$backup" == "$mynode" ] && return 0

return 1

}

##############################################################################
#
#  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 ()
{
 MY_INST_NAME=$1
 #set_logical_host $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()
{
 MY_LOGICAL_HOST=$1
}
##############################################################################
#
#  get_inst_name
#
#       This function returns instance name from global variable
#
##############################################################################
get_inst_name()
{
 print ${MY_INST_NAME}
}
##############################################################################
#
#  get_logical_host
#
#       This function returns logical host name from global variable
#
##############################################################################
get_logical_host()
{
print ${MY_LOGICAL_HOST}
}

##############################################################################
#
#  get_dbms_version [-p] <host> <instance> <informix|oracle|sybase> 
#
#       This function returns version number of RDBMS instance 
#       if -p option is used then private link is used to query the information
#             if host specified is logical host then IP address of 
#		current master on private links is used.
#		if first link is not up then second link is tried.
#
#       return codes:
#		 0 - OK
#		-1 - invalid options
#		-2 - host is not a logical host nor physical host
#		-3 - No current master for this LH
#		 1,2 - returned by ha_dbms_call
##############################################################################
get_dbms_version()
{
#set -x

typeset opt
typeset use_private=0

while getopts :p opt
do
  case $opt in
     p) use_private=1;;
     *) return -1;;
  esac
done

shift `expr $OPTIND - 1`

[ $# -ne 3 ] && return -1

typeset version
typeset rc
typeset host="$1"
typeset instance="$2"
typeset type="$3"

if [ ${use_private} -eq 0 ]; then
   version=$(ha_dbms_call ${host} ${instance} version ${type})
   rc=$? 
   print -r ${version}
   return $rc
fi
     

typeset allhosts=$(haget -f all_physical_hosts | tr '\012' ' ')
is_member "${host}" "${allhosts}"
if [ $? -ne 0 ] ; then
	# Check assume that this is logical host  and find out
	# its master
	host=$(haget -f master -h ${host})
	if [ $? -ne 0 ]; then
		return  -2
	fi
	[ -z "$host" ] && return -3
fi
typeset priv_links=$(haget -f private_links -h ${host})
typeset ip

for ip in ${priv_links}; do
   version=$(ha_dbms_call ${host} ${instance} version ${type})
   rc=$? 
   if [ $rc -eq 0 ]; then
   	print -r ${version}
   	return $rc
   fi
done
return $rc

}
update_status()
{
  log_dbms "Status update"  $@
  ha_dbms_call $@  &
}

