#!/bin/ksh
#
# @(#)pdbconf.sh 1.32 97/04/18 SMI
#
#	Copyright (c) 1994 Sun Microsystems, Inc.
#
# pdbconf - SPARCcluster PDB configuration editor script.
#

Myname=`basename $0`

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

cdbpath="/etc/opt/SUNWcluster/conf"
ssacmd=/usr/sbin/ssaadm

tmppath=${Tmppath=/usr/tmp}
sedfile=${tmpfile=${tmppath}/sed.tmp$$}
touch $sedfile 
trap "echo \"$Myname: ***interrupt***\" 1>&2; /bin/rm -rf $sedfile; exit 1"\
	      1 2 3 15

######################################################################################

function get_hostslot {
# If host is set then we want to query the $CLUSTNAME.cdb file and get
# the slot location for this host.
if [[ -n "$host" ]]; then
	for i in 0 1
	do
		if cdbmatch cluster.node.$i.hostname $cdbfile | /bin/grep $host >/dev/null 2>&1
		then
			hostslot=$i
			echo 2>&1 "Host $host is node number $hostslot."
			break;
		fi
	done
fi
}

######################################################################################

function get_newhost
{
if [[ -n "$newhost" ]]; then
	set `echo $newhost | sed -e "s/,/ /g"`
	echo "/^cluster.node.0.hostname/s|\(.*\):.*$|\1: $1|" >>$sedfile
	echo "/^cluster.node.1.hostname/s|\(.*\):.*$|\1: $2|" >>$sedfile
fi
}

######################################################################################

function validate_interfaces
{
if [[ $# -gt 2 ]]; then
	if [[ "$2" = "$4" ]]; then
		echo 2>&1 "$Myname: Interfaces specified must be unique!"
		exit 3
	fi
fi
}

######################################################################################

function validate_controllers
{
typeset error=0
for i in $*
do
	if $(echo $i | /bin/grep -v '^[0-9A-Fa-f]\{12\}' >/dev/null 2>&1)
	then
		echo 2>&1 "$Myname: Illegal serial number: $i"
		error=1
	fi
done
return ${error}
}

######################################################################################

function create_ssa_link
{
wwn=$1
ssafile=$2

cdbfile=$cdbpath/$CLUSTNAME.cdb
ssadevdir=$(cdbmatch ssa.devdir ${cdbfile})
sn=$(print $wwn | sed 's/^\(....\)0*/SUNW,pln@[ab]0*\1,/')
realssapath=$(/bin/grep -i $sn ${ssafile} 2>/dev/null)
cd ${ssadevdir}
/bin/rm -rf ${wwn}
ln -s ${realssapath} ${wwn}
}

######################################################################################

function create_disk_link
{
diskid=$1
devaddr=$2

cdbfile=$cdbpath/$CLUSTNAME.cdb
ssadevdir=$(cdbmatch ssa.devdir ${cdbfile})
cd ${ssadevdir}
/bin/rm -f ${diskid}
ln -s /dev/rdsk/${devaddr} ${diskid}
}

######################################################################################

function print_root_warning
{
cdbfile=$cdbpath/$CLUSTNAME.cdb
print $*
print "This is an invalid selection of the quorum device"
print "Select another quorum device for both the nodes"
print "WARNING: The $cdbfile file may be now inconsistent on the two nodes"
}

######################################################################################

# This function finds the root device of the system, whether it is encapsulated by
# the volume manager or not. It sets two variables which are used by other functions:
# rootdev = address of the root disk of the form cXtYdZs2
# rootc = controller target number on which the root disk is present, basically, the
#         the string 'cX' from the device address

function find_root_device
{
rootdev=$(cat /etc/vfstab | egrep '[	]/[	]' | cut -f2 -d'	')
print "${rootdev}" | egrep '/dev/rdsk/c[0-9]+t[0-9]+d[0-9]s[0-9]' > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
	rootdev=$(print ${rootdev} | sed -e 's|/dev/rdsk/||')
	rootdev=$(print ${rootdev} | sed -e 's/s[0-9]/s2/')
	rootc=${rootdev%%t*}
else
	 print "${rootdev}" | egrep '/dev/vx/rdsk/rootvol' > /dev/null 2>&1
	 if [[ $? -eq 0 ]]; then
		rootvol=$(print ${rootdev} | sed -e 's|/dev/vx/rdsk/||')
		plex=$(/usr/sbin/vxprint | grep rootvol | grep pl | head -1 | awk '{print $2}')
		dm=$(/usr/sbin/vxprint | grep ${plex} | grep sd | awk '{print $2}' | sed -e 's/-[A-Z0-9][0-9]//g')
		rootdev=$(/usr/sbin/vxprint | grep ${dm} | grep dm | awk '{print $3}')
		rootc=${rootdev%%t*}
	fi
fi
}

######################################################################################
#
# This function is called from change_quorum_dev and takes two arguments - the new
# and the old quorum device, respectively. It releases the reservation on the old
# quorum device only if it is successfully able to reserve the new quorum device.

function reserve_release_qdev
{
typeset newqdev
typeset oldqdev

newqdev=$1
oldqdev=$2

pdbssa q_reserve ${newqdev}
if [[ "$?" -ne 0 ]]; then
	print
	print "Unable to reserve new quorum device: ${newqdev}"
	print "No changes have been made to the system."
	exit 1
fi

# Release the reservation on the old quorum device
pdbssa release ${oldqdev}
}

######################################################################################
function change_quorum_dev
{
typeset found=0
cdbfile=$cdbpath/$CLUSTNAME.cdb
oldquorumdev=$(cdbmatch ctlreserve.node.quorumdev ${cdbfile})

# If oldquorumdev is an SSA controller, and this node happens to be the only node
# in the cluster, then we need to release the reservation on oldquorumdev and reserve
# the new quorum device. To this effect, we set a flag and get the current membership.

let only_node_in_cluster=0
curr_members=$(get_node_status | grep membership | sed -e 's/membership: //')
if [[ "${curr_members}" = @(0|1) ]]; then
	let only_node_in_cluster=1
fi

curr_members=$(get_node_status | grep membership | sed -e 's/membership: //')

# Figure out the disk on which the root partition is present. This is
# validate the choice of the quorum device.

find_root_device

# save all the controller paths in the system

ssafile=/var/tmp/pdbconf.$$
findssa ${ssafile} > /dev/null 2>&1

# First, let's check to see whether the argument is of the form c#

if [[ "$1" = c+([0-9]) ]]; then

	if [[ "$1" = "${rootc}" ]]; then
		print_root_warning "Root device ${rootdev} of host $(uname -n) is on controller $1"
		/bin/rm -f ${ssafile}
		exit 1
	fi

	wwn=$(${ssacmd} inq $1 | grep 'Serial Num' | awk '{print $3}')

	if [[ -n "${wwn}" ]]; then
		# Validate wwn for syntax
		if validate_controllers ${wwn}
		then
			newquorumdev=${wwn}
			create_ssa_link ${wwn} ${ssafile}
			if (( only_node_in_cluster == 1 )); then
				reserve_release_qdev ${newquorumdev} ${oldquorumdev}
			fi
		else
			print "Invalid controller: $1"
			/bin/rm -f ${ssafile}
			exit 1
		fi
	else
		print "Invalid controller: $1"
		/bin/rm -f ${ssafile}
		exit 1
	fi

elif [[ "$1" = c+([0-9])t+([0-9])d+([0-9])s2 ]]; then

	# quorum device is a disk

	if [[ "$1" = "${rootdev}" ]]; then
		print_root_warning "Disk $1 is the root disk of host $(uname -n)"
		/bin/rm -f ${ssafile}
		exit 1
	fi

	ctlr=$(print $1 | sed -e 's/t[0-9][0-9]*d[0-9]s2//')
	wwn=$(${ssacmd} inq ${ctlr} 2>/dev/null | grep 'Serial Num' | awk '{print $3}')
	if [[ -f ${ssacmd} ]]; then
		diskid=$(${ssacmd} inq /dev/rdsk/$1 | grep 'Serial Num' | sed -e 's/Serial Number[ ]*//')
	else
		diskid=$(/opt/SUNWcluster/bin/pdbssa inquiry $1)
	fi

	if [[ -n "${wwn}" && -n "${diskid}" ]]; then
	  
		newquorumdev="${wwn}.${diskid}"
		create_ssa_link ${wwn} ${ssafile}
		if (( only_node_in_cluster == 1 )); then
			reserve_release_qdev ${newquorumdev} ${oldquorumdev}
		fi	  

	elif [[ -z "${wwn}" && -n "${diskid}" ]]; then
	  
		newquorumdev="${diskid}"
		create_disk_link ${diskid} $1
		if (( only_node_in_cluster == 1 )); then
			reserve_release_qdev ${newquorumdev} ${oldquorumdev}
		fi
		
	else
		print "Invalid disk: $1"
		/bin/rm -f ${ssafile}
		exit 1
	fi
else
	# argument could be a wwn - validate the syntax

	if validate_controllers $1
	then	
		# make sure that the indicated WWN is actually present
		# on the system

		cd /dev/rdsk
		ctls=$(/bin/ls | sed 's/t.*$//' | uniq)
		for c in ${ctls}
		do
			wwn=$(${ssacmd} inq $c 2>/dev/null | grep 'Serial Num' | awk '{print $3}')
			if [[ "${wwn}" = "$1" ]]; then
				found=1
				if [[ "$c" = "$rootc" ]]; then
					print_root_warning "Root device ${rootdev} of host $(uname -n) is on controller $1"
					/bin/rm -f ${ssafile}
					exit 1
				fi
				
				newquorumdev=$1
				create_ssa_link ${wwn} ${ssafile}
				if (( only_node_in_cluster == 1 )); then
					reserve_release_qdev ${newquorumdev} ${oldquorumdev}
				fi
				break
			fi
		done
		if [[ "${found}" -eq 0 ]]; then
			print "Invalid controller: $1"
			/bin/rm -f ${ssafile}
			exit 1
		fi
	else
		print "Invalid controller: $1"
		/bin/rm -f ${ssafile}
		exit 1
	fi
fi
/bin/rm -f ${ssafile}
echo "/ctlreserve.node.quorumdev/s|\(.*\):.*$|\1: $newquorumdev|"\
>> $sedfile
}

######################################################################################

function get_interfaces
{
newinterfaces=$*
if [[ -n "$newinterfaces" ]]; then
	print "newinterfaces = $newinterfaces"
	validate_interfaces $newinterfaces
	set $newinterfaces
	while [ $# -gt 0 ]
	do
		cif=$(cdbmatch cluster.node.${hostslot}.if.$1 $cdbfile 2>&1| cut -d" " -f1)
		echo "/cluster.node.${hostslot}.if.$1/s|$cif|$2|" >> $sedfile
		echo "/cluster.node.${hostslot}.haiface.$1/s|$cif|$2|">> $sedfile
		echo "/cluster.node.${hostslot}.ifpath.$1/s|$cif|$2|">> $sedfile
		shift 2
	done
fi
}

######################################################################################

function get_cluster_name
{
if [[ -z "$CLUSTNAME" ]];then
	echo 2>&1 "$Myname: No cluster name specified!"
	exit 1
else
	cdbfile=$cdbpath/$CLUSTNAME.cdb
	# Ok now lets see if this is legal.
	echo 2>&1 "$cdbfile"
	if cdbmatch cluster.cluster_name $cdbfile 2>/dev/null \
		| /bin/grep $CLUSTNAME >/dev/null 2>&1
	then
		:
	else
		echo 2>&1 "$Myname: No Such or Illegal cluster name: $CLUSTNAME!"
		exit 2
	fi
fi
}

######################################################################################

function get_udlm_cfile
{
if [[ -n "$newudlmfile" ]]; then
	if [[ -n "$nudlmcfile" ]]; then
		if print "${nudlmcfile}" | grep "^/" > /dev/null 2>&1; then
			echo "/^udlm.oracle.config.file/s|:.*|: $nudlmcfile|" >> $sedfile
		else
			echo 2>&1 "\"${nudlmcfile}\": filename must be an absolute pathname."
			exit 2
		fi
	else
		echo "/^udlm.oracle.config.file/s|:.*|:|" >> $sedfile
	fi
fi
}

######################################################################################

function get_newnetaddr
{
if [[ -n "$nodeid" ]]; then
	if [[ "$nodeid" = "0" ]]; then
		echo "/^cluster.node.0.physaddr/s|:.*|: $newnetaddr|" >> $sedfile
	elif [[ "$nodeid" = "1" ]]; then
		echo "/^cluster.node.1.physaddr/s|:.*|: $newnetaddr|" >> $sedfile
	else
		echo 2>&1 "$nodeid: Illegal nodeid. Should be 0 or 1"
	fi
fi
}

######################################################################################
  
function display_config
{
 get_cluster_name
 host0=`cdbmatch cluster.node.0.hostname $cdbfile`
 host1=`cdbmatch cluster.node.1.hostname $cdbfile`
 h0if0=`cdbmatch cluster.node.0.if.0 $cdbfile | cut -d" " -f1`
 h0if1=`cdbmatch cluster.node.0.if.1 $cdbfile | cut -d" " -f1`
 h1if0=`cdbmatch cluster.node.1.if.0 $cdbfile | cut -d" " -f1`
 h1if1=`cdbmatch cluster.node.1.if.1 $cdbfile | cut -d" " -f1`
 h0ctl0=`cdbmatch ctlreserve.node.ctllist $cdbfile`
 h0qrm0=`cdbmatch ctlreserve.node.quorumdev $cdbfile`
 h0shd0=`cdbmatch ctlreserve.node.shdisklist $cdbfile`
 oracdb=`cdbmatch udlm.oracle.config.file $cdbfile`
 echo 2>&1 "
Current Configuration for Cluster $CLUSTNAME:

  Hosts in cluster: $host0 $host1

  Private Network Interfaces for 
      $host0: $h0if0 $h0if1
      $host1: $h1if0 $h1if1
  
  Quorum Device:
      $h0qrm0
"
 ORA=0
 pdbapps=$(cdbmatch cluster.pdbapps $cdbfile)
 ora=$(appmatch ${pdbapps} ${ORA})
 if [ $ora -eq 1 ]; then
   echo 2>&1 "
  Oracle DLM configuration file:
      $oracdb
"
 fi
}

######################################################################################

function change_cluster_diskgroups
{
CVM=3
VxVM=4
SDS=5
cdbfile=$cdbpath/$CLUSTNAME.cdb
host=$1
get_hostslot
shift
diskgroups=$*

pdbapps=$(cdbmatch cluster.pdbapps $cdbfile)
vm=$(appmatch ${pdbapps} ${CVM})
if [[ "${vm}" = "1" ]]; then
	vm=cvm
else
	vm=$(appmatch ${pdbapps} ${VxVM})
	if [[ "${vm}" = "1" ]]; then
		vm=vxvm
	fi
fi

# deport the diskgroup if present in this node

for d in $diskgroups
do
	if [[ "${vm}" = "cvm" || "${vm}" = "vxvm" ]]; then
		result=$(/usr/sbin/vxdg list ${d} > /dev/null 2>&1)
		if [[ "$?" -eq 0 ]]; then
			if print "${result}" | grep shared > /dev/null 2>&1; then
				print "Illegal argument: ${d} is a shared diskgroup"
				exit 1
			fi
			/usr/sbin/vxdg deport ${d} > /dev/null 2>&1
			if [[ "$?" -ne 0 ]]; then
				print "Deport of diskgroup ${d} failed"
				exit 1
			fi
		fi
	else
		print Operations of Volume Manager ${vm} are not supported
		exit 1
	fi
done
			
echo "/vm.node.${hostslot}.cdg/s|\(.*\):.*$|\1: $diskgroups|"\
>> $sedfile
}

######################################################################################

function usage
{
 echo 2>&1 "Usage: 

$Myname clustername -h <new hostname1> <new hostname2>

$Myname clustername -i <hostname> <if0> <if1>

$Myname clustername -g <hostname> <cdg1> <cdg2> ...

$Myname clustername -p

$Myname clustername -u

$Myname clustername -v

$Myname clustername -U [absolute path of the config file for Oracle Unix DLM]

$Myname clustername -N <0|1> <ethernet address of host>

$Myname clustername -q <quorum-device>
"
}

######################################################################################

   if [ $# -ge 2 ]; then
     CLUSTNAME=$1
     shift;
   else
     usage
     exit 2
   fi
   while getopts pdguvqUN:i:h: c
   do
	case $c in
	   U) shift
              newudlmfile=NUDLM
              nudlmcfile=$1 
	      ;;
	 u|v) echo 2>&1 "$Myname: Option not implemented yet."
	      exit 0;;
	   p) shift
	      display_config
	      exit 0
	      ;;
	   g) shift
	      if [ $# -lt 1 ]; then
		echo 2>&1 "$Myname: Missing hostname argument. \
Should be:\n$Myname clustername -g hostname [dg1 dg2 ...]"
		exit 2
	      else
	        change_cluster_diskgroups $*
	      fi
		;;
	   h) shift; 		# get rid of the -h parameter!
	      if [ $# -ne 2 ]; then
		echo 2>&1 "$Myname: Illegal Hostname format. \
Should be:\n$Myname clustername -h newhost1 newhost2"
	        exit 2
	      else
		newhost=$1,$2
		shift 2
	      fi
	      ;;

           N) shift;
	      if [ $# -ne 2 ]; then
		echo 2>&1 "$Myname: Illegal ethernet address format. \
Should be:\n$Myname clustername -N nodeid new-ethernet-address"
                exit 2
              else
                newnetaddr=$2
                nodeid=$1
                shift 2
              fi
              ;;
	   i) shift
	      if [ $# -ne 3 ]; then
		echo 2>&1 "$Myname: Illegal interface format. \
Should be:\n$Myname clustername -i hostname ifX ifY"
		exit 2
	      else
		host=$1
		newinterfaces="0 "$2" 1 "$3
		shift 3
	      fi
		;;
	   q) shift
	      if [ $# -ne 1 ]; then
		echo 2>&1 "$Myname: Illegal number of arguments. \
Should be:\n$Myname clustername -q quorum-device"
		exit 2
	      else
		quorum_dev=$1
		change_quorum_dev $quorum_dev
		shift 1
	      fi
		;;
	  *) echo $*
	esac
   done


CLUSTNAME=${CLUSTNAME:=ops}

get_cluster_name

get_hostslot

get_newhost 

get_udlm_cfile $newudlmfile $nudlmcfile

get_newnetaddr

get_interfaces $newinterfaces

cp $cdbfile ${cdbfile}.o
sed -f $sedfile ${cdbfile}.o > $cdbfile

# Find and restart the inetd:
mond_pid=`/usr/bin/ps -ed | /usr/bin/grep mond | /usr/bin/awk '{print $1}'`
if [ -n "$mond_pid" ]; then
	kill $mond_pid
fi

inet_pid=`/usr/bin/ps -ed | /usr/bin/grep inetd | /usr/bin/awk '{print $1}'`
if [ -n "$inet_pid" ]; then
	kill -HUP $inet_pid
else
	echo 2>&1 "inetd is not running! Something is wrong!!"
fi
