#!/bin/ksh
#
#	Copyright 06/03/98 Sun Microsystems, Inc.  All Rights Reserved.
#
# @(#)hainformix.sh	1.20 98/06/03 SMI
#
# hainformix - administrative command for HA-DBMS Informix
#	(see hainformix_support(4) )
#	     verifies and updates the HA-DBMS Informix configuration files
#
#	This utility depends on the existence of the hadfconfig file.
#
# usage: hainformix [ -s ] operation [ onconfig [ data ...] informixserver ]
#	-s:        silent mode
#	operation: one of list, insert, delete, update, start, stop
#	onconfig:  name of onconfig (not required for list operation)
#	data ...:  set of data fields for insert or update operation
#	informixserver: Name of informixserver as specified in the DBSERVERNAME
#			 of the ONCONFIG file
#

# remember our name
argv0=`basename $0`

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

# HA-DBMS file locations
HA_SUPPORT=${HA_FILES}/hainformix_support
HA_DATABASES="${HA_FILES}/hainformix_databases"
DS="informix"
PREFIX="SUNWcluster.ha.${DS}"

# Include dbms utilities file.
. ${_basedir}/${_productdir}/bin/dbms_utilities

# locations
HA_BIN=${_basedir}/${_productdir}/bin
FM_PROGS=${_basedir}/${_productdir}/ha/informix
CT_PROGS=${_basedir}/${_productdir}/ha/informix
ETC_CLUSTER=${_basedir_etc}/etc/opt/${_productdir}
PATH=$HA_BIN:$FM_PROGS:$CT_PROGS:/usr/bin:/usr/sbin
export PATH


# silent mode flag
silent=""

# Informix locations
INFTAB=/var/opt/informix/inftab

if [ -f ${ETC_CLUSTER}/conf/default_clustername ]; then
        HA_CLUSTER=`cat ${ETC_CLUSTER}/conf/default_clustername`
        #echo "Assuming a default cluster name of ${HA_CLUSTER}"
else
        echo "Cannot determine cluster name."
        exit 1
fi

TRAPSIGNALS="1 2 3 15"

#
# not_positive_numeric() -	not_positive_numeric string
#
#	Returns 1 if the string is numeric and positive, else returns 0
#
not_positive_numeric() {
	[ -z "$1" -o "`expr 0$1 : '.*'`" != "`expr 0$1 : '[0-9]*'`" -o \
		`expr 0$1 \<= 0` != "0" ]
}


#
# not_non_neg_numeric() -	not_non_neg_numeric string
#
#	Returns 1 if the string is numeric and positive or 0, else returns 0
#
not_non_neg_numeric() {
	[ -z "$1" -o "`expr 0$1 : '.*'`" != "`expr 0$1 : '[0-9]*'`" -o \
		`expr 0$1 \< 0` != "0" ]
}


# check_inftab_line onconfig - find entry in $INFTAB, return in inftab_line
check_inftab_line() {
	inftab_line=`grep "^[	 ]*$1:" $INFTAB`
	if [ "$inftab_line" = "" ] ; then
                errlog "${PREFIX}.hainformix.4000" \
			"onconfig \"$1\" not in $INFTAB"
                return 1
	else
		return 0
        fi
}

		
###############################################################################
# hainformix_check databases_file support_file - sanity check of various things
#    databases_file is name of hainformix_databases(4) file
#    support_file is name of hainformix_support(4) file
#    This function will report all errors found, and always returns 0
hainformix_check() {

 	hadatabases_check $1
	return 0
}

parse_line() {
        db_line=$1
        case "$db_line" in
                '#'*) continue ;;
        esac
 
        # fields are separated by TABs, cut them apart
 
        mode=`echo "$db_line" | cut -s -f 1`
        onconfig=`echo "$db_line" | cut -s -f 2`
        logical_host=`echo "$db_line" | cut -s -f 3`
        poll_cycle=`echo "$db_line" | cut -s -f 4`
        connect_cycle=`echo "$db_line" | cut -s -f 5`
        timeout=`echo "$db_line" | cut -s -f 6`
        restart_delay=`echo "$db_line" | cut -s -f 7`
        dbname=`echo "$db_line" | cut -s -f 8`
        informixserver=`echo "$db_line" | cut -s -f 9`
        extra=`echo "$db_line" | cut -s -f 10`
 
}

hadb_validate_entry() {

	have_error=0

	parse_line "$1"

	# first make sure we have enough values here
	if [ "$informixserver" = "" ] ; then
		log "not enough fields in line:"
		log "line $line_no --> $db_line"
		have_error=1
		continue
	fi

	case "$mode" in
		on|off) ;;
		*) log "First column (mode) has invalid value $mode:"
		   log "line $line_no --> $db_line"
		   have_error=1 ;;
	esac

        associated_hosts=`hareg -q ${DS} -H | tr '\012' ' '`
        is_member "$logical_host" "$associated_hosts"
        if [ $? -ne 0 ]; then
                log "Logical Host \"$logical_host\" is not associated with service \"${DS}\" or not configured."
                log "Valid logical hostnames: \"$associated_hosts\"."
		log "line $line_no --> $db_line"
                have_error=1
        fi

	if not_positive_numeric $poll_cycle ; then
		log "Poll cycle time \"$poll_cycle\" should be a numeric value greater than 0:"
		log "line $line_no --> $db_line"
		have_error=1
	fi

	if not_non_neg_numeric $connect_cycle ; then
		log "Connect cycle count \"$connect_cycle\" should be a numeric value greater than or equal to 0:"
		log "line $line_no --> $db_line"
		have_error=1
	fi

	if not_non_neg_numeric $timeout ; then
		log "Time out \"$timeout\" should be a numeric value greater than or equal to 0:"
		log "line $line_no --> $db_line"
		have_error=1
	fi
         
	if not_non_neg_numeric $restart_delay ; then
		log "Restart delay \"$restart_delay\" should be a numeric value greater than or equal to 0:"
		log "line $line_no --> $db_line"
		have_error=1
	fi

	if [ ! "$dbname" ] ; then
		log "Database login name should not be null"
		log "line $line_no --> $db_line"
	fi

	if [ "$extra" != "" ] ; then
		log "too many values:"
		log "line $line_no --> $db_line"
		have_error=1
	fi

	return $have_error
}
	

# hadatabases_check filename - sanity check of hainformix_databases file
# always returns 0
hadatabases_check() {
	log "Checking contents of file $BASEFILE..."
	line_no=0
	get_all ${DS} |\
	while read db_line ; do
		line_no=`expr $line_no + 1`
		case "$db_line" in
			'#'*) continue ;;
		esac

		hadb_validate_entry "$db_line"
         
	done
	log " "
	return 0
}

##############################################################################
# hasupport_check filename - checks the contents of a hainformix_support(4) file
#	and report all problems. Always returns 0
hasupport_check() {
	log "Checking contents of file $1..."
	line_no=0
	cat $1 | \
	while read line ; do
		line_no=`expr $line_no + 1`
		# skip comment lines
		case "$line" in
			'#'*) continue ;;
		esac
		# fields are separated by TABs, cut them apart
		version=`echo "$line" | cut -s -f 1`
		monitor=`echo "$line" | cut -s -f 2`
		action_file=`echo "$line" | cut -s -f 3`
		extra=`echo "$line" | cut -s -f 4`

		# first make sure we have enough values here
		if [ "$action_file" = "" ] ; then
			log "not enough fields (should be three) in line:"
			log "line $line_no --> $line"
			continue
		fi

		if [ ! -x "${HA_BIN}/${monitor}" ] ; then
			log "fault monitor ${HA_BIN}/${monitor} does not exist or is not executable:"
		   	log "line $line_no --> $line" 
		fi
		
		full_action_file="${HA_FILES}/${action_file}"
		if [ ! -r "$full_action_file" ] ; then
			if [ -r ${full_action_file}.tmpl ] ; then
				log "creating ${full_action_file} from template file ${full_action_file}.tmpl because of reference in"
				cp ${full_action_file}.tmpl ${full_action_file}
				chmod +w ${full_action_file}
			else
				log "file ${full_action_file} does not exist or is not readable! Referenced by"
				log "template file ${full_action_file}.tmpl does not exist or is not readable"
				fi
			log "line $line_no --> $line"
		fi

		if [ "$extra" != "" ] ; then
			log "too many values:"
			log "line $line_no --> $line"
		fi
	
         
	done
	log " "
	return 0
}

#
# hainformix_list filename - list the contents of the data file, skipping
#	comment lines
hainformix_list() {
	get_all ${DS}
}


# hainformix_insert filename onconfig_name logical_host poll_time connect_cyle timeout \
#	restart_delay dbname informixserver
# insert a record into filename 
#  
hainformix_insert() {
	onconfig_name=$1
	shift
        if [ $# -ne 7 ] ; then
		log "parameters required for insert are: onconfig host probe_cycle_time connect_cycle_count time_out restart_delay dbname informixserver"
		usage
	fi

	# make sure line for $onconfig_name doesn't already exists in file
	line=$(get_instance "${DS}" "${onconfig_name}")
	if [ "$line" != "" ] ; then
		log "onconfig \"$onconfig_name\" already in $BASEFILE:"
		abort "${PREFIX}.hainformix.4000"  "--> $line"
	fi	

	# build the line for the new entry
	new_line="off	$onconfig_name	$1	$2	$3	$4	$5	$6	$7"

	parse_line "$new_line"
	get_hosts_and_links "$logical_host"
	hadb_validate_entry "$new_line"
	if [ $? -ne 0 ]; then
		abort "${PREFIX}.hainformix.4010" \
		"new hainformix onconfig line failed sanity check"
	fi

	check_inftab_line $onconfig_name
	if [ $? -ne 0 ]; then
		abort "${PREFIX}.hainformix.4020" \
		"inftab file failed sanity check"
	fi

	add_unique_ha_instance "${DS}" "$new_line" 
        [ $? -ne 0 ] && abort "${PREFIX}.hainformix.4330" \
                "Could not add entry to CCD for $onconfig_name. Retry the operation."

	}


# hainformix_delete filename onconfig_name
# delete a record for $onconfig_name from $filename
hainformix_delete() {
	onconfig_name=$1 ; shift
	inst_mode=$1

	line=$(get_instance "${DS}" "${onconfig_name}" )
	if [ "$line" = "" ] ; then
		abort "${PREFIX}.hainformix.4030" \
		"no entry for onconfig $1 in $BASEFILE - can't delete"
	fi

        # reject if fault monitor still running, only if not stopping
        if [[ "$inst_mode" != "stop" ]]; then
		mode=`echo "$line" | cut -s -f 1`
		if [ "$mode" = "on" ] ; then
			abort "${PREFIX}.hainformix.4040" \
			"cannot delete entry; stop $1 fault monitor first"
		fi
	fi

	# delete all entries with $1 in the second column from the file
	remove ${DS} onconfig $onconfig_name
	[ $? -ne 0 ] && abort "${PREFIX}.hainformix.4050" \
			"$onconfig_name could not be modified! Retry the operation."
}


# update an existing record in the data file ###################################
hainformix_update() {
	onconfig_name=$1; shift
        if [ $# -ne 7 ] ; then
		log "parameters required for update are: onconfig host probe_cycle_time connect_cycle_count time_out restart_delay dbname informixserver ]"
		usage
	fi
	line=$(get_instance "${DS}" "${onconfig_name}")
	if [ "$line" = "" ] ; then
		abort "${PREFIX}.hainformix.4300" \
	"no entry for onconfig $onconfig_name in $BASEFILE - can't update"
	fi
	
	old_mode=`echo "$line" | cut -s -f 1`
        if [ "$old_mode" = "on" ] ; then
		abort "${PREFIX}.hainformix.4040" \
			"cannot update entry; stop $onconfig_name fault monitor first"
        fi

	new_line="$old_mode	$onconfig_name	$1	$2	$3	$4	$5	$6	$7"

	hadb_validate_entry "$new_line"
	if [ $? -ne 0 ]; then
		abort "${PREFIX}.hainformix.4310" \
		"new hainformix onconfig line failed sanity check"
	fi

	check_inftab_line $onconfig_name
	if [ $? -ne 0 ]; then
		abort "${PREFIX}.hainformix.4320" \
			"inftab file failed sanity check"
	fi


	# delete the line with $onconfig_name in the second column from the
	# file, and then insert $new_line in its place (replacing the line)
        hainformix_delete $onconfig_name ""
        add_unique_ha_instance "${DS}" "$new_line"
	[ $? -ne 0 ] && abort "${PREFIX}.hainformix.4330" \
			"$filename could not be modified!"
	return 0
}


# hainformix_start_stop_run filename onconfig_name ( start | stop )
# starts/stops fault monitoring for $onconfig
# does not update $filename - see function hainformix_start_stop
# Note - remote fault monitor is not started if Informix is installed on
#	 logical host.
hainformix_start_stop_run() {

	typeset return_value
	typeset -i i
 
	lhost=$(get_instance "${DS}" "$1" | awk '{print $3}')
	if [[ -z "$lhost" ]] ; then
		abort "${PREFIX}.hainformix.4400" \
		"no entry for onconfig $1 in HA INFORMIX CCD - can't $2"
	fi
	set -A hostarray $(get_logical_links "${lhost}")

        # Set no delay since we are manually starting fmon
        HA_FM_DBMSPROBE_DELAY=0; export HA_FM_DBMSPROBE_DELAY

	# call start/stop locally
	#log "$2 fault monitor for $1 on localhost..."
	${HA_BIN}/ha_dbms_call localhost $1 $2 informix
	return_value=$?
        # call start remotely only if the local ha_dbms_call succeeded.
        (($return_value == 0)) && \
        if (( ${#hostarray[*]} > 0 )) ; then
                i=0
                while (( $i < ${#hostarray[*]} ))
                do
                        #log "$2 fault monitor for $1 on ${hostarray[i]} ... "
                        (( i=i+1 ))
                        ${HA_BIN}/ha_dbms_call ${hostarray[i]} $1 $2 informix
                        (( i=i+1 ))
                        if [ "$?" != 0 ]; then
                                log "Failed to $2 fault monitor for $1 on ${hostarray[i]}"
                        fi
                done
        else
                log "No $2 of fault monitor for $1 on remote host."
        fi

	return $return_value
}

# hainformix_start_stop filename onconfig ( start | stop )
# changes the on/off status for $onconfig in $filename
# does not actually start or stop monitoring - see hainformix_start_stop_run
hainformix_start_stop() {
	# find the onconfig
	line=$(get_instance_dynamic "${DS}" "${1}")
	if [ "$line" = "" ] ; then
		abort "${PREFIX}.hainformix.4200" \
		"no entry for onconfig $1 in $BASEFILE - can't $2"
	fi
	mode=`echo "$line" | cut -s -f 1`
	if [ "$2" = "start" ] ; then
		if [ "$mode" != "off" ] ; then
			abort "${PREFIX}.hainformix.4210" \
			"onconfig $1 is not stopped in $BASEFILE!"
		fi

	        # check the entry for the specific onconfig 
                hadb_validate_entry "$line"
                if [ $? -ne 0 ]; then
                       abort "${PREFIX}.hainformix.4220" \
			"hainformix onconfig ccd failed sanity check"
                fi
		check_inftab_line $1 
		if [ $? -ne 0 ]; then
			abort "${PREFIX}.hainformix.4230" \
			"inftab file failed sanity check"
		fi
	else
		if [ "$mode" != "on" ] ; then
			abort "${PREFIX}.hainformix.4240" \
			"onconfig $1 is not started in $BASEFILE!"
		fi
	fi
	
	# update the entry in the file - find the old entry, and reply
	# off with on and vice versa
        if [ "$2" = "start" ] ; then
                new_line=`echo "$line" | sed 's/^off/on/'`
        else
                new_line=`echo "$line" | sed 's/^on/off/'`
        fi
        hainformix_delete $1 $2
        add_unique_ha_instance "${DS}" "$new_line"
        if [ $? -ne 0 ] ; then
                abort "${PREFIX}.hainformix.4250" \
			"onconfig could not be modified!"
        fi
	return 0
}

get_hosts_and_links() {

logical_host_name="$1"
 
set -A nativehosts $(haget -f mastered | tr '\012' ' ')
set -A foreignhosts $(haget -f not_mastered | tr '\012' ' ')
 
for i in ${nativehosts[*]}
do
	if [[ "$i" == "$logical_host_name" ]]; then
		HA_NATIVEHOST="$i"
		break
	fi
done
for i in ${foreignhosts[*]}
do
	if [[ "$i" == "$logical_host_name" ]]; then
		HA_FOREIGNHOST="$i"
		break
	fi
done

if [[ -n "$HA_NATIVEHOST" ]]; then
        res=`haget -f physical_hosts -h $HA_NATIVEHOST`
elif [[ -n "$HA_FOREIGNHOST" ]]; then
        res=`haget -f physical_hosts -h $HA_FOREIGNHOST`
else
        abort "${PREFIX}.hainformix.4140" "Cannot determine host members."
fi

HA_NATIVEHOST="${nativehosts}"
HA_FOREIGNHOST="${foreignhosts}"
ASYMLOGICALHOST="${nativehosts} ${foreignhosts}"

}

# ignore signals
catchsig() {
	echo "Caught signal; continuing."
}


#
# get_yes_or_no - prompt user for "y" or "n", and read answer. repeat if
#	answer is not "y" or "n". Returns the choice.
#	In silent mode, just returns "y"
#
get_yes_or_no() {
	if [ "$silent" = "y" ] ; then
		resp="y"
	else
		read resp
		while [ "$resp" != "y" -a "$resp" != "n" ]
		do
			echo
			echo '    Please respond "y" or "n" \c'
			read resp
		done
	fi
}


# log ... - if not silent, echo all parameters
log() {
	[ "$silent" != "y" ] && echo "$*"
}
# infolog ... - if not silent, echo all parameters
infolog() {
        typeset prefix
 
        prefix=$1
        shift
        [ "$silent" != "y" ] && log_info $prefix "$*"
}

# errlog - echo all parameters
errlog() {
        typeset prefix
 
        prefix=$1
        shift
        log_error $prefix "$*"
}

# abort ... - if not silent, echo all parameters. Then exit with status of 1
abort() {
        typeset prefix
 
        prefix=$1
        shift
#        [ "$silent" != "y" ] && print $prefix "$*"
        [ "$silent" != "y" ] && print "$*"
        exit 1
}

# usage - print command usage ################################################
usage() {
	log "usage: $argv0 [ -s ] operation [ onconfig [ data ...] informixserver ]"
	log "  -s:        silent mode"
        log "  operation: one of list, insert, delete, update, start, stop"
	log "  onconfig:  name of onconfig (not required for list operation)"
        log "  data ...:  set of data fields for insert or update operation"
        log "  informixserver:  Name of informixserver as specified in the DBSERVERNAME parameter of ONCONFIG file"
	exit 2
}


# check who we are - if we are not root, exit immediately
res=`id`
[ $? -ne 0 ] && abort "${PREFIX}.hainformix.4060" "Cannot execute id command"
[ `expr "$res" : "uid=0(root)"` != 11 ] && abort "${PREFIX}.hainformix.4070" \
	"$argv0 must be executed as root."

# check for -s parameter
while getopts s c
do
	case $c in
		s) silent="y" ;;
		\?) usage ;;
	esac
done
shift `expr $OPTIND - 1`

export HA_DATABASES


# make sure we have a command
if [ $# -lt 1 ] ; then
	log "missing operation"
	usage
fi
command="$1"
shift
case "$command" in 
#	check|list)			 will_modify=0 ;;
	list)				 will_modify=0 ;;
	start|stop|insert|delete|update) will_modify=1 ;;
	*)				 log "invalid operation"
					 usage ;;
esac

# make sure HA is up
if [ $will_modify -eq 1 ]; then
        clustm getstate $HA_CLUSTER >/dev/null 2>&1
	if [ $? -ne 0 ] ; then
        	abort "${PREFIX}.hainformix.4100" \
		"Cluster is not up; Run scadmin {startcluster|startnode} first"
        fi

        CMSTATE="`clustm getstate $HA_CLUSTER 2> /dev/null`"
        #make sure the host is not in reconfiguring
        if [ "$CMSTATE" != "end" ]; then
                abort "${PREFIX}.4110" \
"This Sun Cluster is currently reconfiguring: hainformix command not allowed" 
	fi
fi


# make sure Informix service is registered and the state is on
if [ $will_modify -eq 1 ]; then
	reg=`hareg | grep informix`
else
	reg=''
fi
if [ -z "$reg" -a $will_modify -eq 1 ] ;  then
	errlog "${PREFIX}.hainformix.4115" \
	"Sun Clusters HA-DBMS for INFORMIX service is not registered."
	abort "${PREFIX}.hainformix.4120" \
		"You may run \"hareg -s -r informix\" to register it."
fi

reg_state=`echo "$reg" | cut -f2 -s`
if [ "$reg_state" != "on" -a $will_modify -eq 1 ] ; then
	errlog "${PREFIX}.hainformix.4125" \
		"The state of Solstice HA-DBMS for INFORMIX service is off."
	abort "${PREFIX}.hainformix.4130" \
		"You may run \"hareg -y informix\" to turn the service on."
fi


# make sure we can read ${HA_DATABASES} - hainformix_check relies on this test
if [ "$command" != "list" -a "$command" != "check" ] ; then
	if [ $# -lt 1 ] ; then
		log "onconfig name missing"
		usage
	fi
	onconfig=$1
	shift

fi

parms=$*

#BASEFILE=`basename ${HA_DATABASES}`
BASEFILE="CCD"
export BASEFILE


HA_LOCALHOST=`uname -n`
trap "catchsig" $TRAPSIGNALS

# run the appropriate function
case "$command" in
	list)       hainformix_list  ;;
	insert)     hainformix_insert $onconfig $parms ;;
	delete)	    hainformix_delete $onconfig $command ;;
	update)     hainformix_update $onconfig $parms ;;
	start|stop) hainformix_start_stop $onconfig $command
		    hainformix_start_stop_run $onconfig $command ;;
	*)	    usage ;; # really already checked further up
esac

exit 0
