#!/bin/sh
#
#pragma ident   "@(#)have_maj_util.m4 1.12     01/11/01 SMI"
#
# Copyright (c) 1993-1999 by Sun Microsystems, Inc.
# All rights reserved.
#
#

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

MD_BIN=/usr/sbin
NFS_BIN=/opt/SUNWcluster/ha/nfs

is_member() {
	if [ $# -ne 2 ]; then
	        logerr "1100" "is_member: called with wrong number of \
arguments should be 2 but is $#"
		return 1
	fi
	for ISM_X in $2 ; do
		if [ "$1" = "$ISM_X" ]; then
			return 0
		fi
	done
	return 1
}

cleanup()
{
	rm -f $TMPOUT $TMPOUT2 $TMPERR 2>/dev/null 1>&2
	rm -f $MS_TMPOUT $MS_TMPOUT2 $MS_TMPERR 2>/dev/null 1>&2
	rm -f $HA_TMP/enum_disks.*.$$ 2>/dev/null 1>&2 
}

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


logerr()
{
	echo "$*"
	log_info "${pre}.$1" "$2"
}

errlogecho()
{
	log_info "${pre}.have_maj.$1", "$2"
}

log_err_file()
{
	echo "`date` ------" >> $HA_TMP/logerr
	cat $1 >> $HA_TMP/logerr
}

# mediator_stat -	mediator_stat sds_disksetname [CHECK]
#
# Run medstat to determine if all mediator databases are ok.
#
# Without the CHECK argument, this function always returns 0.
# Error messages are logged for any problems found in medstat output.
#
# With the CHECK argument, we are checking to see if the loss of
# one mediator would still leave us with a quorum (either >50% of
# mediators, or the local mediator is golden).  Returns:
#   0: we have a mediator quorum that would survive loss of one mediator
#   1: we have a mediator quorum that would not survive the loss of 
#       one mediator
#   2: we do not have a mediator quorum
#  >2: an error occurred which prevented testing of the mediator quorum.
#
# Note that medstat exits with a 2 when mediators are NOT configured
# for the specified diskset.
#
mediator_stat()
{
    MS_DS=$1
    if [ -n "$2" ]; then
	MS_CHECK=1
    else
	MS_CHECK=0
    fi
    export MS_DS MS_CHECK MYHOSTNAME REMOTEOWNER

    MS_TMPOUT=$HA_TMP/${argv0}.ms.${MS_DS}.out.$$
    MS_TMPOUT2=$HA_TMP/${argv0}.ms.${MS_DS}.out2.$$
    MS_TMPERR=$HA_TMP/${argv0}.ms.${MS_DS}.err.$$

# XXXX
# For the following program, we just assume it runs as 
# ENVIRON["MS_CHECK"] = 0 in HA2.0 

    medstat -s $MS_DS >$MS_TMPOUT 2>$MS_TMPERR
    RC=$?
    if [ $RC -ne 2 ]; then
	nawk '
/^Mediator[ 	][ 	]*Status[ 	][ 	]*Golden$/	{ next }
	{
	    if ($2 == "Bad") {
		printf "Attention required - medstat shows bad mediator data on host %s for diskset %s\n", $1, $MS_DS
	    }
	    if ($2 == "Fatal") {
		printf "Attention required - medstat finds a fatal error in probing mediator data on host %s for diskset %s!\n", $1, $MS_DS
	    }
	    if ($2 == "Ok") {
#		if (ENVIRON["MS_CHECK"] == 1 && $3 == "Yes" &&
#		  $1 == ENVIRON["MYHOSTNAME"]) {
#		    golden = 1	# my mediator is golden
#		    exit
#		}
		goodcount += 1
	    } else {
#		if (ENVIRON["MS_CHECK"] == 1 && ENVIRON["REMOTEOWNER"] == $1) {
#		    sibmedbad = 1  # victim host mediator is already bad
#		    exit
#		}
		badcount += 1
	    }
	}
	END	{
	    if (golden == 1 || sibmedbad == 1)
		exit 0
	    if (goodcount -1 <= badcount +1)
		exit 77      # assumes nawk does not exit 77
	    else
		exit 0
	}' <$MS_TMPOUT >$MS_TMPOUT2
	case $? in
	0)  # quorum would survive loss of 1 mediator or victim host mediator
	    # is already bad (hence its death would not cause loss of quorum)
	    RSTATUS=0
	    ;;
	77) # quorum would not survive loss of 1 mediator
	    RSTATUS=1
	    ;;
	*)  # nawk failed-- stderr will show error
	    RSTATUS=3
	    ;;
	esac
	if [ $MS_CHECK -ne 1 ]; then
	    if [ $RC -ne 0 ] && [ ! -s $MS_TMPOUT2 ]; then
		echo "Attention required - medstat failed for diskset $MS_DS"\
>$MS_TMPOUT2
	    fi
	    if [ -s $MS_TMPOUT2 ]; then
		# append non-blank lines to MS_TMPOUT2
		grep '.' $MS_TMPERR >>$MS_TMPOUT2
		log_err_file $MS_TMPOUT2
	    fi
	fi
    fi

    # Note, CHECK option should never be called when mediators not in use
    if [ $MS_CHECK -eq 1 ]; then
	case $RC in
	0)  # mediator quorum exists; RSTATUS was set above
	    ;;
	1)  # no current mediator quorum
	    RSTATUS=2
	    ;;
	*)  # medstat failed in some other way
	    cat $MS_TMPERR >&2
	    RSTATUS=3
	    ;;
	esac
    else
	RSTATUS=0
    fi

    rm -f $MS_TMPOUT $MS_TMPOUT2 $MS_TMPERR

    return $RSTATUS
}

have_maj()
{

	LOCALSW=0
	TAKESW=0
while getopts :lt c
do
    	case $c in
    	l)  LOCALSW=1
		;;
	t)  TAKESW=1
		;;
    	\?) errlogecho "1010" "Source code error: called with unknown \
switch -$OPTARG"
		return 1
		;;
    	esac
done
shift `expr $OPTIND - 1`
if [ -n "$1" ]; then
	MULTI_DS=$1
else
    	errlogecho "1020" "Source code error: requires disksetname argument"
    	cleanup
    	return 1
fi
METADBFILE=""
if [ -n "$2" ]; then
    	if [ $LOCALSW -eq 1 ]; then
	errlogecho "3010" "Source code error: called with both -l switch and \
file argument"
	cleanup
	return 1
    else
        METADBFILE=$2
    fi
fi
if [ -n "$3" ]; then
    	errlogecho "3020" "Source code error: called with too many arguments"
    	cleanup
    	return 1
fi

LHOST=""
# Set LHOST to logical host of the diskset.
LHOST=`disktype -h ${MULTI_DS} 2>$TMPERR`
# echo "LHOST=$LHOST"
RC=$?
if [ $RC -ne 0 ] || [ -z "$LHOST" ]; then
    	errlogecho "4010" "unable to extract logical hostname corresponding to \
diskset name $MULTI_DS (will use the diskset name itself): `cat $TMPERR`"
	return 1
fi

REMOTEOWNER=""
MYHOSTNAME="`uname -n`"
if [ $LOCALSW -eq 0 ]; then
    # Note, when called by fdl_consider_takeover, the local switch is not set
    #  so we will take this code path and set REMOTEOWNER.  This may be used
    #  later by mediator_stat() when checking for 50% case.
    # Set REMOTEOWNER to the owner of the diskset.
    	REMOTEOWNER="`haget -f master -h $LHOST 2>$TMPERR`"
    	RC=$?
    	if [ $RC -ne 0 ]; then
		errlogecho "4020" "unable to determine current master \
of logical host $LHOST, exiting non-zero: `cat $TMPERR`"
		cleanup
		return 1
    	fi
fi

#### Fix start for bugid: 4176230 ####
# Per hactl man page, hactl command with -p can be executed on any
# physical host even if it is not a potential master.

# This fix checks to see if this host is a potential master, if it is,
# continue processing as before otherwise checks to see if a potential
# master exists for the logical hosts. Here the check for accessibilty
# of disksets for the next master is not done as the data services
# already does a rigorous check on this.

POTENTIAL_MASTERS=""
NEW_MASTER=""

POTENTIAL_MASTERS="`haget -f physical_hosts -h $LHOST | grep -v $REMOTEOWNER`"

# Check if this physical host can master.
NEW_MASTER="`echo $POTENTIAL_MASTERS | grep $MYHOSTNAME`"

if [ "$NEW_MASTER" != "$MYHOSTNAME" ]; then
        # This host is not eligible to takeover. Need not check for
        # access to disksets by this host.
        # Access to disksets by potential master is done by the
        # data services. Now just return success.

        cleanup
        return 0
fi

# Since this host is eligible to takeover,
# hactl processing will continue as before.

#### Fix End for bugid: 4176230 ####

if [ $LOCALSW -eq 0 ] && [ -z "$METADBFILE" ]; then
    	if [ -z "$REMOTEOWNER" ]; then
		log_info "${pre}.1100" \
	 	"called without -l switch and without the filename" \
		"argument but $LHOST has no current master, so $argv0 "
		"check is giving up"
		cleanup
		return 89
    	fi
    	if [ "$REMOTEOWNER" = "$MYHOSTNAME" ]; then
		log_info "${pre}.1101" \
		"called without -l switch but this host owns the diskset, "\
		"so $argv0 check is giving up"
		cleanup
		return 89
    	fi

    	# Check cluster membership of caller and current master
     	CLUST_MEMBERS="`haget -f current_memeber_hosts 2>$TMPERR`"
    	RC=$?
    	if [ $RC -eq 5 ]; then
		errlogecho "4030" "called from a host which is not a \
cluster member; exiting non-zero"
		cleanup
		return 1
    	fi
    	if [ $RC -eq 0 ]; then
		# Get rid of any extra white-space:
		CLUST_MEMBERS="`echo $CLUST_MEMBERS`"
		is_member $REMOTEOWNER "$CLUST_MEMBERS"
		if [ $? -ne 0 ]; then
	    	errlogecho "4040" "Host $REMOTEOWNER (current owner of $MULTI_DS)\
 is not a current cluster member; exiting non-zero"
	    	cleanup
	    	return 1
	fi
    else
	ERRMESSAGE="haget -f current_member_hosts failed but $argv0\
proceeding: `cat $TMPERR`"
	echo "Warning: $ERRMESSAGE" 1>&2
	log_info "${pre}.1002" \
		"$ERRMESSAGE"
    fi
fi

SHORTRSHTIMEOUT=60
TIMEOUTEC=99
TIMEOUT=300

LISTFILENAME=""

# Check all the disksets. 
for DS in `echo $MULTI_DS | tr ',' ' '` ; do

# Check if metadb file exists...
if [ ! -f "$METADBFILE" ] || [ ! -s "$METADBFILE" ]; then
    if [ $LOCALSW -eq 1 ]; then
	hatimerun -e -t $TIMEOUTEC $TIMEOUT \
	    $MD_BIN/metadb -s $DS </dev/null >$TMPOUT 2>$TMPERR
	RC=$?
	if [ $RC -eq $TIMEOUTEC ]; then
	    errlogecho "4050" "metadb -s $DS  timed out"
	    cleanup
	    return 1
	elif [ $RC -ne 0 ]; then
	    if grep -i "must be owner" $TMPERR >/dev/null 2>&1 ; then
		log_info "${pre}.1102" \
		 "this host does not own diskset $DS, so $argv0 check is "
		 "giving up"
		cleanup
		return 89
	    fi
	    errlogecho "4060" "metadb -s $DS failed: `cat $TMPERR`"
	    cleanup
	    return 1
	fi
	# Local metadb command succeeded
        LISTFILENAME=$TMPOUT
    else
	RHOST=""
	REMOTEHOSTSPRIVATE="`haget -f private_links -h $REMOTEOWNER 2>$TMPERR`"
	RC=$?
	if [ $RC -ne 0 ] || [ -z "$REMOTEHOSTSPRIVATE" ]; then
	    errlogecho "4070" "unable to determine private links of host \
`$REMOTEOWNER, exiting non-zero: `cat $TMPERR`"
	    cleanup
	    return 1
	fi
	for RH in $REMOTEHOSTSPRIVATE ; do
	    ping $RH > /dev/null 2>&1
	    if [ $? -eq 0 ]; then
		RHOST=$RH
		break
	    fi
	done
	if [ -z "$RHOST" ]; then
	    errlogecho "4080" "Cannot communicate with host $REMOTEOWNER on \
private links"
	    cleanup
	    return 1
	fi

	hatimerun -t $SHORTRSHTIMEOUT rsh -n $RHOST /bin/true >/dev/null 2>&1
	if [ $? -ne 0 ]; then
	    errlogecho "4090" "rsh of /bin/true to host $REMOTEOWNER timed-out"
	    cleanup
	    return 88
	fi

	hatimerun -e $TIMEOUTEC -t $TIMEOUT fdl_rshstatus $RHOST \
	    $MD_BIN/metadb -s $DS </dev/null >$TMPOUT 2>$TMPERR
	RC=$?
	if [ $RC -eq $TIMEOUTEC ]; then
	    errlogecho "4100" "rsh of metadb -s $DS to host $REMOTEOWNER \
timed out"
	    cleanup
	    return 1
	elif [ $RC -ne 0 ]; then
	    if grep -i "must be owner" $TMPERR >/dev/null 2>&1 ; then
		log_info "${pre}.1104" \
	 	"sibling host did not own diskset $DS, so $argv0 check "
		"is giving up"
		cleanup
		return 89
	    fi
	    errlogecho "4110" "rsh of metadb -s $DS to host $REMOTEOWNER \
failed: `cat $TMPERR`"
	    cleanup
	    return 1
	fi
        # Remote metadb succeeded.
        LISTFILENAME=$TMPOUT
    fi
else
    LISTFILENAME=$METADBFILE
fi


# metadb succeeded.  Construct the diskset list.
awk '{print $NF}' $LISTFILENAME | \
    egrep '/dev/did/|c[0-9][0-9]*t[0-9][0-9]*d[0-9][0-9]*' | \
    sed '1,$s:/dsk/:/rdsk/:' >$TMPOUT2

LCNT="`wc -l <$TMPOUT2`"
if [ $LCNT -eq 0 ]; then
    errlogecho "4120" "Could not construct list of mddb replicas: \
computed empty list"
    return 1
fi

# Save the metadb file, so it could be used next time instead of
# recreating it.
if [ ! -f "$METADBFILE" ] || [ ! -s "$METADBFILE" ]; then
	$NFS_BIN/renamefile $LISTFILENAME $METADBFILE
fi
    
#
# The majmddb utility determines whether or not this host is able to
# access either a majority or half of the disk names passed to
# it on stdin.
#
# Without the -s option, exit codes are as follows:
#
#	0	- Able to access a majority.
#	75	- Not able to access a majority, but able to access half.
#	other	- error
#
# WARNING:	Do *NOT* use -s option to majmddb here, as the -s option
#		surpresses exit code 75.
#
majmddb -v <$TMPOUT2 >/dev/null 2>&1
RC=$?
if [ $RC -eq 0 ]; then
    log_info "${pre}.1105" \
    	"Accessed majority of diskset $DS replicas"
elif [ $RC -eq 75 ]; then
    log_info "${pre}.1106" \
	"Accessed half of diskset $DS replicas"
    ${MD_BIN}/metaset -s $DS 2>/dev/null | grep "^Mediator Host" >/dev/null 2>&1
    if [ $? -eq 0 ]; then
    	log_info "${pre}.1107" \
	"Mediators in use for diskset $DS"
	mediator_stat $DS CHECK 2>$TMPERR
	RC=$?
	if [ $TAKESW -eq 1 ]; then
	    case $RC in
	    0|2)
		;;
	    1)	errlogecho "4130" "Able to access half of diskset $DS \
replicas, but mediator quorum would not survive loss of one mediator"
		cleanup
		return 1
		;;
	    *)	errlogecho "4140" "mediator_stat -s $DS CHECK failed"
		log_err_file $TMPERR
		cleanup
		return 1
		;;
	    esac
	fi
	if [ $RC -eq 0 ]; then
    	    log_info "${pre}.1108" \
	    "Accessed majority of diskset $DS mediators"
	else
            errlogecho "3150" "medstat returned non-zero for diskset $DS"
            log_err_file $TMPERR
            errlogecho "3160" "Failed to access both half of the replicas and" 
		"an acceptable mediators status for diskset $DS"
            cleanup
            return 1
	fi
    else
	errlogecho "3170""Able to access half of diskset $DS replicas, \
but mediators are not in use"
	cleanup
	return 1
    fi
else
    errlogecho "3180" "Failed to access a majority of diskset $DS replicas"
    cleanup
    return 1
fi

#
# Run medstat to determine if all mediator databases are ok.
# We get here only if we have already determined that an attempt
# to take ownership would be likely to succeed.
#
# This is launched and run in background, since medstat can have
# a very long timeout associated with it.
#
mediator_stat $DS &

    done  # "For DS in ..."

return 0

}

