#!/bin/sh
#
#pragma ident   "@(#)fdl_enum_probe_disks.m4 1.19     01/12/03 SMI"
#
# Copyright (c) 1997-1999 by Sun Microsystems, Inc.
# All rights reserved.
#
# fdl_enum_probe_disks.shf [-es]
#
# fdl_enum_probe_disks is a fault probe which replaces, and subsumes the
# functions of, fdl_mddb_latch.  Specifically, (as fdl_mddb_latch
# currently does) it creates a "latch file" for each DiskSuite diskset;
# the latch file lists all the disk devices which contain metadevice state
# database replicas (or "mddb replicas").
#
# fdl_enum_probe_disks also performs a new probe, using SCSI inquiry to
# test whether a potential master of a diskset is able to communicate
# with all the disks of the diskset, and logging error messages if it
# cannot.
#
# To accomplish the new disk probe, fdl_enum_probe_disks uses
# other commands as subroutines:
#
#     haconfig -f disks,dualhosted,remotehost=<name>,printboth
#     This uses SCSI inquiry to probe disks on the local node and on the
#     named remote node.  It generates on stdout a table of disk
#     devices-- column 1 contains the device name on the local node,
#     while column 2 contains the name of the same device (matching
#     serial number) on the remote node.  If a device was found on one
#     node but not the other, then a "-" appears in the corresponding
#     column of that row.  Device names are in the format c0t1d2,
#     without a "/dev/rdsk/" prefix or slice number suffix.
#
#     haconfig -f disks
#     Similar to above, but only probes disks on the local node.  This is
#     used with SDS, where we can generate a list of disks in a diskgroup
#     without having to talk to the current master.
#
#     join(1): a Unix command which matches entries in tables.
#
# When called with the -e switch, this program prints to stdout a list of
# all disk devices in HA disksets, one per line, in the form c0t1d2.
# This option is intended for use only by scsirstd.
#
#	Exit codes:
#	0  Command succeeded.
#     127  Command was called improperly or with invalid arguments.
#   other  A called command failed; its exit code is returned.
#
# When the command exits nonzero, error messages are written to stderr (if
# -e switch is present) or logged (if -e is not present).
#
# The -s switch indicates that this program should use the "previous"
# hadfstate file, hadfstate.prev, from the $HA_PRESIDENT node to
# determine the current mastery/maintenance state of logical hosts.  This
# is used (with the -e switch) by scsirstd.c because scsirstd is started
# up in Step3 of cluster reconfiguration, when only the HA_PRESIDENT's
# hadfstate.prev accurately reflects the current ownership of logical hosts.

# Workaround for HA_STDERR being redirected to /dev/null by default.
HA_STDERR=/dev/stderr ; export HA_STDERR

# Non-standard boilerplate, because this script can be called in situations
# where HA_ENV environment has not been set up, or even doesn't exist.
argv0=`basename $0`

# If the HA_ENV environment does not seem to have been set up,
# attempt to source it in using the defaults.

CLUSTERBIN=/opt/SUNWcluster/bin
CLUSTERVAR=/var/opt/SUNWcluster
CLUSTERETC=/etc/opt/SUNWcluster/conf
CLUSTERNAME="`/bin/cat ${CLUSTERETC}/default_clustername`"
NFS_BIN=/opt/SUNWcluster/ha/nfs
ccdfile=${CLUSTERETC}/ccd.database
cdbfile=${CLUSTERETC}/${CLUSTERNAME}.cdb
HA_VAR=/var/opt/SUNWhadf/hadf
MD_BIN=/usr/sbin
pre="SUNWcluster.fdl_enum_probe_disks"

#bug fix 4143849 begin -Naveen Kumar
#check to see if the directory exists, if not create it...
if [ ! -d $HA_VAR ]; then
   mkdir -p $HA_VAR
fi
#bug fix 4143849 end -Naveen Kumar

# Environment file not there; set up defaults
PATH=${NFS_BIN}:${HA_BIN}:${FM_BIN}:${CT_BIN}:/usr/bin:/usr/sbin:/etc:${PATH}
export PATH

HA_LOCALHOST=${HA_LOCALHOST:=`uname -n`}
export HA_LOCALHOST

# include have_maj_util library
. have_maj_util
if [ $? -ne 0 ]; then
    puterr "1100" "$argv0  Cannot find HA utilities library!"
    exit 127
fi

puterr()
{
    # Write error message to stderr (for -e switch) or syslog it.
    if [ $EFLAG -eq 1 ]; then
	echo "${argv0}: ERROR: $*" >&2
    else
	log_info "${pre}.$1" "$2"
    fi
}


putwarn()
{
    # Write warning message to stderr (for -e switch) or syslog it.
    if [ $EFLAG -eq 1 ]; then
	echo "${argv0}: WARNING: $*" >&2
    else
	log_info "${pre}.$1" "$2"
    fi
}


tryhaget()
{
    haget $* 2>$enum_tmperr
    HAGET_RC=$?
    if [ $HAGET_RC -ne 0 ]; then
	puterr "3001" "haget $* failed: `cat $enum_tmperr`"
	cleanup
	exit $HAGET_RC
    fi
    return 0
}

# lhost_mast() will find out all the loghosts which can be mastered 
# by the phyhost. It includes all the loghosts which are 
# currently mastered plus those are not currently mastered. 
lhost_mast()
{
        if [ $# -ne 1 ]; then
                puterr "lhost_mast.1000" "usage: lhosts_master phys-host"
                echo "usage: lhosts_master phys-host" 1>&2
                return 1
        fi
        PHOST=$1
        LHOST=""
        lghost_r="`${CLUSTERBIN}/scccd -f ${ccdfile} ${CLUSTERNAME} \
	   LOGHOST query lname \*`"
        for row in ${lghost_r}; do
                echo $row | /usr/bin/egrep -e $PHOST 1>/dev/null 2>/dev/null
                if [ $? -eq 0 ]; then
                        lhost="`echo $row | awk -F: '{ print $2 }'`"
                fi
                if [ ! -n "$LHOST" ]; then
                        LHOST="$lhost"
                else
                        LHOST="$lhost $LHOST"
                fi
        done
        echo $LHOST
}

getplink()
{
	PRIVLINK=""
	nodename=$1
	numnodes=`enmatch cluster.number.nodes` 
	numip=`enmatch cluster.number.nets`
	myid=0
	while [ $myid -lt $numnodes ]; do
		if [ "`enmatch cluster.node.$myid.hostname`" = ${nodename} ]
		then
			break;
		fi
		myid=`expr $myid + 1`;
	done
	if [ $myid -ge $numnodes ]; then 
		puterr "getplink.4010" "${CLUSTERNAME} node ${nodename} \
 		    is not defined as part of this cluster in \
		    ${CLUSTERNAME}.cdb file"			  
		exit 1
	fi
	addrid=0
	while [ $addrid -lt $numip ]; do   
		priv_addr="`enmatch cluster.node.${myid}.phost.${addrid}`"
		RC=$?
		if [ $? -ne 0 ]; then
			# no more private ip 
			break;
		fi
		# do ping to make sure it works, then return
		if ping $priv_addr >/dev/null 2>&1; then
			PRIVLINK=$priv_addr
			return 0
		fi
		addrid=`expr $addrid + 1`
	done
	if [ $addrid -ge $numip ]; then
		puterr "getplink.4020" "${CLUSTERNAME} node ${nodename} \
		   is not defined with any private ip address"
		exit 1
	fi
}
		
enmatch()
{
	${CLUSTERBIN}/cdbmatch $* ${cdbfile}
}
	
#
# Main program.
#

HA_TMP=`enmatch env.tmpdir`
if [ ! -d ${HA_TMP} ]; then
	mkdir -p ${HA_TMP}
fi

EFLAG=0
SFLAG=0
while getopts :es c
do
    case $c in
    e)	EFLAG=1
	;;
    s)	SFLAG=1
	;;
    \?)	puterr "1001" "called with unknown switch -$OPTARG"
	exit 127
	;;
    esac
done

shift `expr $OPTIND - 1`
if [ -n "$1" ]; then
    puterr "1001" "called with improper command line arguments: $*"
    exit 127
fi

# XXXX
# Because fdl_enum_probe_disks is bundled with NFS probing, so it 
# is always activated when cluster is up. No cluster test is 
# necessary here
  

enum_tmperr=$HA_TMP/enum_disks.err.$$
enum_tmp1=$HA_TMP/enum_disks.1.$$
enum_did=$HA_TMP/enum_disks.did.$$
enum_tmp2=$HA_TMP/enum_disks.2.$$
enum_tmp3=$HA_TMP/enum_disks.3.$$
enum_probe1=$HA_TMP/enum_disks.probe1.$$
enum_disks=$HA_TMP/enum_disks.disks.$$
enum_tmpstate=$HA_TMP/enum_disks.hadfstate.$$
cleanup

establish_cleanup_handler

# Set LHOSTS to a list of all logical hosts which the local node can master
LHOSTS="`lhost_mast ${HA_LOCALHOST}`"
DIDDID=0
DIDPROBE1=0
TIMEOUT=600

for LH in $LHOSTS ; do
    # If LH is in maintenance mode, skip it.
    if [ "`tryhaget -f is_maint -h $LH`" -eq 1 ]; then
	continue
    fi

    # Set PH to the current master of LH (may be null).
    PH="`tryhaget -f master -h $LH`"
    
    # Set DS to the name of LH's diskset
    DS_ROW="`scccd -f ${ccdfile} ${CLUSTERNAME} LOGHOST query lname ${LH}`" 
    if [ $? -ne 0 ]; then
	puterr "4010" "Couldn't perform scccd LOGHOST for loghost $LH"
	exit 1
    fi
    MULTI_DS="`echo $DS_ROW | awk -F: '{print $4}'`"  
    if [ $? -ne 0 ]; then
	puterr "4020" "Couldn't perform awk for $DS_ROW"
	exit 1
    fi

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

    DSTYPE="`disktype -t ${DSNAME}`"

    REALFILE=$HA_VAR/metadb.$DSNAME
    NEWFILE=${REALFILE}.new.$$

    case $DSTYPE in
    sds )
	# Perform the former fdl_mddb_latch functions:
	if [ $EFLAG -ne 1 ]; then
	    if [ "$PH" = "$HA_LOCALHOST" ]; then
		${MD_BIN}/metadb -s $DSNAME >$NEWFILE 2>$enum_tmperr
		if [ $? -ne 0 ]; then
		    puterr "3002" "metadb -s $DSNAME failed: \ 
`cat $enum_tmperr`"
		else
		    renamefile $NEWFILE $REALFILE 2>$enum_tmperr
		    if [ $? -ne 0 ]; then
			puterr "3003" "File rename of $NEWFILE to \
$REALFILE failed: `cat $enum_tmperr`"
		    fi
		fi
		continue  # Done with this logical host
	    elif [ -n "$PH" ]; then	# remote master
		OK=0
		getplink $PH
		RC=$?
		case $RC in
		0)  OK=1
		    ;;
		1)  puterr "3004" "host2plink called incorrectly"
		    ;;
		2)  puterr "3005" "Host $PH is not in the HA configuration"
		    ;;
		3|4)  puterr "3006" "unable to find working private link to $PH"
		    ;;
		esac

		if [ $OK -eq 1 ]; then
		    hatimerun -t ${TIMEOUT} ${CLUSTERBIN}/fdl_rshstatus \
			${PRIVLINK} ${MD_BIN}/metadb -s $DSNAME >$NEWFILE \
			2>$enum_tmperr
		    if [ $? -ne 0 ]; then
			puterr "3008" "rsh to host $PRIVLINK of metadb \
-s $DSNAME failed or timed-out: `cat $enum_tmperr`"
			OK=0
		    else
			OK=1
		    fi
		fi

		if [ $OK -eq 1 ]; then
		    renamefile $NEWFILE $REALFILE 2>$enum_tmperr
		    if [ $? -ne 0 ]; then
			puterr "3003" "File rename of $NEWFILE to \
$REALFILE failed: `cat $enum_tmperr`"
			OK=0
		    else
			OK=1
		    fi
		fi

		if [ $OK -eq 1 ]; then
		
		TMPOUT=$HA_TMP/${argv0}.out.$$
		TMPOUT2=$HA_TMP/${argv0}.out2.$$
		TMPERR=$HA_TMP/${argv0}.err.$$

		    have_maj $DSNAME $REALFILE >/dev/null 2>&1
		    if [ $? -ne 0 ]; then
			cat >$enum_tmperr <<LONGMSG
Cannot access a majority of diskset $DSNAME replicas.  This
  host does not currently master the $DSNAME diskset, however, this
  error implies that this host would not be able to take over
  mastery of the diskset $DSNAME if its current master were to die.
LONGMSG
			log_err_file $enum_tmperr
			rm -f $enum_tmperr
		    fi
		fi
	    fi	# (else no node currently masters LH; do nothing.)
	fi  # EFLAG -ne 1
	# End of former fdl_mddb_latch functions

	# metaset into $enum_tmp1
	${MD_BIN}/metaset -s $DSNAME >$enum_tmp1 2>$enum_tmperr
	if [ $? -ne 0 ]; then
	    puterr "3012" "metaset -s $DSNAME failed: `cat $enum_tmperr`"
	    continue
	fi

	# filter metaset output into $enum_tmp2

nawk '
        /d[0-9]+/ {
        print "/dev/did/rdsk/"$1
            }
            ' <$enum_tmp1 >$enum_tmp2 2>$enum_tmperr


	if [ $? -ne 0 ]; then
	    puterr "3014" "nawk failed on output of metaset -s $DSNAME: \
`cat $enum_tmperr`"
	    continue
	fi

	# sort device list into $enum_tmp1
	sort -b <$enum_tmp2 >$enum_tmp1 2>$enum_tmperr
	if [ $? -ne 0 ]; then
	    puterr "3016" "sort failed on metaset device list: \
`cat $enum_tmperr`"
	    continue
	fi

	# if DID is used, convert did device list to c0t1d2 format
	if grep -l '/dev/did/' $enum_tmp1 >/dev/null 2>&1 ; then
	    if [ $DIDDID -ne 1 ]; then
		didadm -l >$enum_tmp3 2>$enum_tmperr
		RC=$?
		if [ $RC -ne 0 ]; then
		    puterr "3018" "didadm -l failed (exiting): \
`cat $enum_tmperr`"
		    cleanup
		    exit $RC
		fi
		sed -e 's+^[^:]*:/dev/rdsk/++' <$enum_tmp3 >$enum_tmp2 \
		  2>$enum_tmperr
		RC=$?
		if [ $RC -ne 0 ]; then
		    puterr "3020" "sed failed on output of didadm -l (exiting):\
`cat $enum_tmperr`"
		    cleanup
		    exit $RC
		fi
		sort -b -k 2 <$enum_tmp2 >$enum_did
		RC=$?
		if [ $RC -ne 0 ]; then
		    puterr "3022" "sort failed on didadm maplist (exiting):\
`cat $enum_tmperr`"
		    cleanup
		    exit $RC
		fi
		DIDDID=1
	    fi

	    # Join $enum_tmp1 to $enum_did to produce a file $enum_tmp2
	    # with lines of the form:	<diskname> <didname>
	    join -j2 2 -o 2.1,0 $enum_tmp1 $enum_did >$enum_tmp2 2>$enum_tmperr
	    RC=$?
	    if [ $RC -ne 0 ]; then
		puterr "3024" "join failed on didadm devices (exiting): \
`cat $enum_tmperr`"
		cleanup
		exit $RC
	    fi

	    # sort did device list back into $enum_tmp1
	    sort -b -k 1 <$enum_tmp2 >$enum_tmp1 2>$enum_tmperr
	    RC=$?
	    if [ $RC -ne 0 ]; then
		puterr "3026" "sort failed on did device list (exiting): 
`cat $enum_tmperr`"
		cleanup
		exit $RC
	    fi
	fi

	# Now $enum_tmp1 contains the sorted list of disk devices, of the
	# form:  c0t1d2
	# If DID is used, column 2 gives the full did (rdsk) pathname:
	#	c0t1d2 /dev/did/rdsk/d29

	if [ $EFLAG -eq 1 ]; then
	    if [ -s $enum_disks ]; then
		cat $enum_tmp1 >>$enum_disks 2>$enum_tmperr
		if [ $? -ne 0 ]; then
		    puterr "3028" "File catenation of $enum_tmp1 onto \
$enum_disks failed: `cat $enum_tmperr`"
		    continue
		fi
	    else
		renamefile $enum_tmp1 $enum_disks 2>$enum_tmperr
		if [ $? -ne 0 ]; then
		    puterr "3003" "File rename of $enum_tmp1 to \
$enum_disks failed: `cat $enum_tmperr`"
		    continue
		fi
	    fi
	else  # -e switch is not present:
	    if [ $DIDPROBE1 -ne 1 ]; then
		haconfig -f disks >$enum_tmp3 2>$enum_tmperr
		RC=$?
		if [ $RC -ne 0 ]; then
		    puterr "3030" "haconfig -f disks failed (exiting): \
`cat $enum_tmperr`"
		    cleanup
		    exit $RC
		fi
		sort -b <$enum_tmp3 >$enum_probe1
		RC=$?
		if [ $RC -ne 0 ]; then
		    puterr "3032" "sort failed on haconfig disklist \
(exiting): `cat $enum_tmperr`"
		    cleanup
		    exit $RC
		fi
		DIDPROBE1=1
	    fi
	    join -v 1 -j1 1 $enum_tmp1 $enum_probe1 >$enum_disks 2>$enum_tmperr
	    RC=$?
	    if [ $RC -ne 0 ]; then
		puterr "3034" "join failed on diskset $DSNAME disklist: \
`cat $enum_tmperr`"
		continue
	    fi
	    exec <$enum_disks
	    while read baddisk baddid ; do
		if [ -n "$baddid" ]; then
		    baddid=" ($baddid)"
		fi
		puterr "3036" "Cannot access disk ${baddisk}${baddid} in \
diskset $DSNAME, possibly due to a bad cable or controller."
	    done

	    if [ -s $enum_disks ]; then
		cat >$enum_tmperr <<LONGMSG
Unable to access one or more disks in diskset $DSNAME in
logical host $LH.  This host does not currently master $LH,
but this error implies an impaired ability to take over
this logical host if the current master were to die.
LONGMSG
		log_err_file $enum_tmperr
	    fi
	fi
	;;  # end "diskset type SDS"

    vxvm )
	# 
	# XXXX 
	# In SC2.2, fdl_enum_probe_disks is only used to probe SDS diskset.
	# It doesn't do anything to VXVM
	#
	;;  

    esac  # "case DSTYPE"

    done  # "For DSNAME in ..."
done  # "For LH in $LHOSTS"

if [ $EFLAG -eq 1 ] && [ -s $enum_disks ]; then
    cat $enum_disks
fi

cleanup
exit 0
