#! /bin/ksh -p
#
# ident "@(#)scvxinstall.ksh 1.11     04/06/04 SMI"
#
# Copyright 2000-2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#


#####################################################
#
# General comments
#
#####################################################
#
# Scvxinstall provides automatic VxVM installation and root disk
# encapsulation for SunCluster 3.0 and later, hiding the complex steps
# required and protecting the system from user mistakes which can severely
# impact cluster availability.
#
# Among other things, scvxinstall checks for:
#
# - must be run as root
# - all nodes must be run in cluster mode
# - root disk must have slice 2 as the overlap slice (set to entire disk)
# - root disk must have 2 free (unassigned) partitions
# - /global/.devices/node@N must be the only global filesystem on the root disk
# - supported versions of SunOS
#
#####################################################

#####################################################
#
# Global constants which may be set from the environment
#
#####################################################
#
# SC_CDIMAGE_PATHS provides the list of cdimage paths to search in finding
#  the Volume Manager packages.
#
#####################################################


#####################################################
#
# Global Constants
#
#####################################################

# The base directory
typeset -r SC_BASEDIR=/					# Base directory

# The program name
typeset -r PROG=${0##*/}				# Program name
typeset -r ARGS="$*"					# Arguments

# The process ID
typeset -r PID=$$					# Process ID

# SUNWesu is required for use of awk(1)			# SUNWesu
typeset -r SC_ESUPKG=SUNWesu

# Installed VxVM packages
typeset -r SC_VXVM_LIC_PKG_OLD="VRTSlic"	# License package pre-3.5
typeset -r SC_VXVM_LIC_PKG_35="VRTSvlic"	# License package post-3.5
typeset -r SC_VXVM_DEV_PKG="VRTSvmdev"		# Developer package
typeset -r SC_VXVM_REQ_PKGS="VRTSvxvm"		# Required packages
						# Installed packages
typeset -r SC_VXVM_PKGS="${SC_VXVM_LIC_PKG_OLD} ${SC_VXVM_LIC_PKG_35} ${SC_VXVM_REQ_PKGS} ${SC_VXVM_DEV_PKG} VRTSvmman"

# Set the PATH
typeset SC_BINDIRS=${SC_BASEDIR}usr/cluster/bin:${SC_BASEDIR}usr/cluster/lib/sc
typeset SC_VXBINDIRS=${SC_BASEDIR}usr/sbin:${SC_BASEDIR}etc/vx/bin
PATH=${SC_BINDIRS}:${SC_VXBINDIRS}:/bin:/usr/bin:/sbin:/usr/sbin; export PATH

# /etc/name_to_major
typeset -r SC_NAME_TO_MAJOR=${SC_BASEDIR}etc/name_to_major
typeset -r SC_MAX_LOOP_TRIES=5
typeset -r SC_MAJOR_NUM_BUFFER=15
typeset -r SC_MAX_MAJOR_NUMBER=16383			# Maximum major number

# /etc/vfstab
typeset -r SC_VFSTAB=${SC_BASEDIR}etc/vfstab

# Log files
typeset -r SC_LOGDIR=${SC_BASEDIR}var/cluster/logs/install # install logs
typeset -r SC_LOGFILE=${SC_LOGDIR}/${PROG}.log.${PID}	# log file

# Temp files
typeset -r SC_TMPDIR=${SC_BASEDIR}var/cluster/run	# temp directory
typeset -r SC_SCVXDIR=${SC_TMPDIR}/${PROG}		# my temp directory
typeset -r SC_ADMIN=${SC_SCVXDIR}/${PROG}.admin		# pkgadd admin file
typeset -r SC_RSP=${SC_SCVXDIR}/${PROG}.response	# pkgadd response file
typeset -r SC_LOCKFILE=${SC_SCVXDIR}/${PROG}.lock	# lock file
typeset -r SC_ROOTDGFILE=${SC_SCVXDIR}/${PROG}.rootdg	# rootdg file
typeset -r SC_TMPERR=${SC_SCVXDIR}/${PROG}.errs.${PID}	# temp error file
typeset -r SC_TMPLIC=${SC_SCVXDIR}/${PROG}.lics.${PID}	# temp licenses file

# flag files to remember stuff we've already done
typeset -r SC_COMPLETE=${SC_SCVXDIR}/${PROG}.complete
typeset -r SC_RUNRC=${SC_SCVXDIR}/${PROG}.runrc

# scconf error codes
typeset -r SC_SCCONF_EBUSY=12

# cdrom
#
#  There must be an SC_CDIMAGE_OS_ARRAY element for each supported release
#  of SunOS (as given by uname -r).   And, there must be an array element
#  in SC_CDIMAGE_PATHS_ARRAY listing all supported cdimage path components
#  for each OS element.  There is a one-to-one correspondence between
#  the elements in SC_CDIMAGE_OS_ARRAY and SC_CDIMAGE_PATHS_ARRAY.
#
#  In scvx_init(), SC_CDIMAGE_PATHS is set to the correct list of path
#  components for the installed SunOS, if it is not otherwise first set
#  in the caller's environment.
#
typeset -r SC_DFLT_CDIMAGE=/cdrom/cdrom0
typeset SC_CDIMAGE_OS_ARRAY
typeset SC_CDIMAGE_PATHS_ARRAY
set -A SC_CDIMAGE_OS_ARRAY
set -A SC_CDIMAGE_PATHS_ARRAY

SC_CDIMAGE_OS_ARRAY[0]=5.8				# SunOS 5.8
SC_CDIMAGE_PATHS_ARRAY[0]="pkgs Solaris_8/pkgs"
SC_CDIMAGE_OS_ARRAY[1]=5.9				# SunOS 5.9
SC_CDIMAGE_PATHS_ARRAY[1]="pkgs Solaris_9/pkgs"

# VxVM generated files that we check for the existence of
typeset -r SC_VXVM_ENCAP_CONFIGFILE=${SC_BASEDIR}etc/vx/reconfig.d/disks-cap-part		# file that contains the CTD name of the rootdisk to be encapsulated

# Other variables
typeset -r SC_VXVM_LICENSE_FEATURES="VXVM SSA"		# not used
typeset -r SC_VXVM="Volume Manager"
integer -r SC_ABORT_TIMEOUT=20

#####################################################
#
# Global Variables
#
#####################################################
typeset SC_NODES				# list of configured nodes
typeset SC_NODES_PRIVATE			# list of configured nodes (private names)
typeset SC_MYNODENAME				# my node name
typeset SC_MYNODEID				# my nodeid
typeset SC_FIRSTNODE				# "first" node
typeset SC_SUNOS				# SunOS release
typeset SC_CDIMAGE_PATHS			# cdrom image paths per OS
typeset SC_ROOTCTD				# root disk name (cNtNdN)
typeset SC_ROOTDISK				# root disk name (cNtNdNs2)
typeset SC_ROOTRDSK				# root dev (/dev/rdsk/cNtNdNs2)
typeset SC_ROOTARG				# -R arg for pkg commands

typeset SC_PERFORM_ENCAP			# do encap ("true" or "false")
typeset SC_PKG_PATH				# path to packages
typeset SC_LICENSES				# array of license tokens
set -A SC_LICENSES

integer SC_REBOOT_REQUIRED=0			# reboot flag

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

#####################################################
#
# cleanup() [exitstatus]
#
#	exitstatus			status with which to exit
#
#	Cleanup lock files and other temp files.
#
#	If "exitstatus" is given, exit with that status.
#
#	This function either does not return or always returns 0.
#
#####################################################
cleanup()
{
	typeset exitstatus=$1

	# If we do not own the lock file, we are done
	if [[ -f ${SC_LOCKFILE} ]] && [[ "$(cat ${SC_LOCKFILE})" != ${PID} ]]; then
		if [[ -n "${exitstatus}" ]]; then
			exit ${exitstatus}
		fi
		return 0
	fi

	# Cleanup lock file and other temp files
	rm -f ${SC_ADMIN} ${SC_RSP} ${SC_ROOTDGFILE} ${SC_LOCKFILE} ${SC_TMPERR} ${SC_TMPLIC}

	if [[ -n "${exitstatus}" ]]; then
		exit ${exitstatus}
	fi
	return 0
}

#####################################################
#
# verify_isroot()
#
#       Print an error message and return non-zero
#       if the user is not root.
#
#####################################################
verify_isroot()
{
	typeset -r uid=$(expr "$(id)" : 'uid=\([0-9]*\)*')  

        # make sure uid was set
	if [[ -z "${uid}" ]]; then
		printf "$(gettext '%s:  Cannot get uid.')\n" ${PROG} >&2
		return 1
	fi

	# check for root
	if [[ ${uid} -ne 0 ]]; then
		printf "$(gettext '%s:  Must be root.')\n" ${PROG} >&2
		return 1
	fi

	return 0      
}

#####################################################
#
# openlog
#
#	Create the log file.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
openlog()
{
	echo "\n${PROG} ${ARGS}\n" >>${SC_LOGFILE} || return 1

	return 0
}

#####################################################
#
# logmsg()
#
#	Print stdin to stdout and to the install log file.
#	If the install log has not been created, just
#	print to stdout.
#
#####################################################
logmsg()
{
	if [[ ! -f "${SC_LOGFILE}" ]]; then
		cat
	else
		tee -a ${SC_LOGFILE}
	fi
}

#####################################################
#
# logerr()
#
#	Print stdin to stderr and to the install log file.
#	If the install log has not been created, just
#	print to stderr.
#
#####################################################
logerr()
{
	if [[ ! -f "${install_log}" ]]; then
		cat >&2
	else
		tee -a ${install_log} >&2
	fi
}

#####################################################
#
# scvx_get_online_nodecount()
#
#	Print the number of online nodes.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
scvx_get_online_nodecount()
{
	(
		LANG=C; export LANG;
		scstat -n | awk '
			BEGIN {
				i=0
			}

			/^ *Cluster node:/ {
				if ($4 == "Online")
					++i
			}

			END {
				print i
			}
		'
	)

	return 0
}

#####################################################
#
# scvx_get_nodecount()
#
#	Print the number of configured nodes.
#
#	SC_NODES must be set before calling this function.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
scvx_get_nodecount()
{
	if [[ -n "${SC_NODES}" ]]; then
		set -- ${SC_NODES};  echo $#
	fi

	return 0
}

#####################################################
#
# scvx_get_nodelist()
#
#       Print the list of configured nodes.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
scvx_get_nodelist()
{
        (
		LANG=C; export LANG
		scconf -p | sed -n 's/^ *Cluster nodes:[ 	]*\(.*\)/\1/p'
	)

	return 0
}               

#####################################################
#
# scvx_get_priv_nodelist()
#
#       Print the list of private names of configured nodes
#	in the same order as SC_NODES which holds public
#	names.
#
#	SC_NODES must be set before calling this function.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
scvx_get_priv_nodelist()
{
	typeset node
	typeset private_name
	typeset priv_name_list=""

	if [[ -n "${SC_NODES}" ]]; then
		for node in ${SC_NODES}
		do
			private_name=$(scha_cluster_get -O PRIVATELINK_HOSTNAME_NODE ${node})
			status=$?
			if [ $status -ne 0 ]; then
				priv_name_list=""
				break
			else
				priv_name_list=${priv_name_list}"${private_name} "
			fi
		done
	fi
	echo ${priv_name_list}

	return 0
}

#####################################################
#
# verify_ismember
#
#
#       Print an error message and return non-zero
#       if this node is not a member of the cluster.
#
#####################################################
verify_ismember()
{
        if [[ -x /usr/sbin/clinfo ]]; then
                /usr/sbin/clinfo > /dev/null 2>&1
                if [[ $? -eq 0 ]]; then
                        return 0
                fi
        fi

	printf "$(gettext '%s:  This node is not currently in the cluster.')\n" ${PROG} | logerr
        return 1
}

#####################################################
#
# verify_fullmembership
#
#       Print an error message and return non-zero
#       if not all configured nodes are currently members
#	of the cluster.
#
#	This function assumes that verify_ismember() has already
#	been called.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
verify_fullmembership()
{
	integer config_count=$(scvx_get_nodecount)
	integer online_count=$(scvx_get_online_nodecount)
	integer errs=0

	# If verify_ismember() was called, then WE should at least be configed
	if [[ ${config_count} -eq 0 ]]; then
		printf "$(gettext '%s:  Internal error - bad node count.')\n" ${PROG} | logerr
		((errs+=1))
	fi

	# If verify_ismember() was called, then WE should at least be Online
	if [[ ${online_count} -eq 0 ]]; then
		printf "$(gettext '%s:  Internal error - bad membership count.')\n" ${PROG} | logerr
		((errs+=1))
	fi

	if [[ ${errs} -ne 0 ]]; then
		return 1
	fi

	# Make sure that the two counts are identical
	if [[ ${config_count} -ne ${online_count} ]]; then
		printf "$(gettext '%s:  All nodes must be current members of the cluster.')\n" ${PROG} | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# verify_isquorum_init
#
#
#       Print an error message and return non-zero
#       if the cluster quorum is not yet initialized.
#
#####################################################
verify_isquorum_init()
{
	typeset mode
	integer isenabled

	isenabled=$(
		LANG=C; export LANG
		mode="$(scconf -p | sed -n 's/^Cluster install mode:[   ]*\([^ ]*\).*/\1/p')"
                if [[ -n "${mode}" ]] && [[ "${mode}" == "enabled" ]]; then
                        echo 1
                else
                        echo 0
                fi
        )

	if [[ ${isenabled} -eq 1 ]]; then
		printf "$(gettext '%s:  Cluster \"installmode\" is still enabled.')\n" ${PROG} | logerr
		printf "$(gettext '%s:  Please run scsetup(1M) to initialize quorum and disable \"installmode\".')\n" ${PROG} | logerr
		return 1
	fi

        return 0
}

#####################################################
#
# scvx_init()
#
#	Perform general initialization.
#
#	It verifies that SUNWesu is installed.
#
#	It initializes the following global variables:
#		SC_NODES
#		SC_NODES_PRIVATE
#		SC_FIRSTNODE
#		SC_MYNODENAME
#		SC_MYNODEID
#		SC_SUNOS
#		SC_CDIMAGE_PATHS
#		SC_ROOTCTD
#		SC_ROOTDISK
#		SC_ROOTRDSK
#		SC_ROOTARG
#
#	It creates /var/cluster/run/${PROG}, if it is not already there.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
scvx_init()
{
	typeset node
	typeset tmp_array

	typeset special
	integer i

	#
	# Make sure that SUNWesu is installed
	#
	scvx_is_pkg_installed ${SC_ESUPKG}
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  \"%s\" is not installed.')\n" ${PROG} ${ESUPKG} | logerr
		return 1
	fi

	#
	# Set SC_NODES, the list of configured cluster nodes
	#
	SC_NODES="$(scvx_get_nodelist)"
	if [[ -z "${SC_NODES}" ]]; then
		printf "$(gettext '%s:  Unable to establish the list of cluster nodes.')\n" ${PROG} | logerr
		return 1 
	fi

	#
	# Set SC_NODES_PRIVATE, the list of configured cluster nodes--private names
	#
	SC_NODES_PRIVATE="$(scvx_get_priv_nodelist)"
	if [[ -z "${SC_NODES_PRIVATE}" ]]; then
		printf "$(gettext '%s:  Unable to establish the list of cluster node private names.')\n" ${PROG} | logerr
		return 1 
	fi

	#
	# Set SC_FIRSTNODE
	#
	set -A tmp_array ${SC_NODES}
	SC_FIRSTNODE=${tmp_array[0]}

	#
	# Set SC_MYNODENAME, the name of this node
	#
	SC_MYNODENAME=$(uname -n)
	if [[ -z "${SC_MYNODENAME}" ]]; then
		printf "$(gettext '%s:  Unable to determine the name of this node.')\n" ${PROG} | logerr
		return 1
	fi

	# Make sure my nodename is in the list
	let found=0
	for node in ${SC_NODES}
	do
		if [[ ${node} == ${SC_MYNODENAME} ]]; then
			let found=1
			break
		fi
	done
	if [[ ${found} -eq 0 ]]; then
		printf "$(gettext '%s:  Solaris node name does not match cluster node name for this node.')\n" ${PROG} | logerr
		return 1
	fi

	#
	# Get my node ID, SC_MYNODEID
	#
	SC_MYNODEID=$(clinfo -n)
	if [[ -z "${SC_MYNODEID}" ]]; then
		printf "$(gettext '%s:  Unable to determine the node ID for this node.')\n" ${PROG} | logerr
		return 1
	fi

	#
	# Set SC_SUNOS and SC_CDIMAGE_PATHS
	#
	SC_SUNOS=$(uname -r)

	# Make sure we support the SunOS, and get the index
	let i=0
	while [[ -n "${SC_CDIMAGE_OS_ARRAY[i]}" ]]
	do
		if [[ "${SC_CDIMAGE_OS_ARRAY[i]}" == "${SC_SUNOS}" ]]; then
			break
		fi
		((i += 1))
	done
	if [[ -z "${SC_CDIMAGE_OS_ARRAY[i]}" ]]; then
		printf "$(gettext '%s:  This version of %s is not supported.')\n" ${PROG} "Solaris (SunOS ${SC_SUNOS})" | logerr
		return 1
	fi

	# If not already set, set SC_CDIMAGE_PATHS
	if [[ -z "${SC_CDIMAGE_PATHS}" ]]; then
		SC_CDIMAGE_PATHS="${SC_CDIMAGE_PATHS_ARRAY[i]}"
		if [[ -z "${SC_CDIMAGE_PATHS}" ]]; then
			printf "$(gettext '%s:  Internal error - no %s.')\n" ${PROG} "PATHS" | logerr
			return 1
		fi
	fi

	#
	# Set SC_ROOTCTD, SC_ROOTDISK and SC_ROOTRDSK
	#
	special=$(mount -V / | awk '{ print $(NF -1) }')

	# Make sure that this is not a DID device
	if [[ "${special}" == /dev/did/dsk/d*s[0-7] ]];  then
		printf "$(gettext '%s:  The root file system is mounted on a DID device.')\n" ${PROG} | logerr 
		return 1
	fi

	# Also, make sure that this is a regular Solaris disk name
	# The logic to test and use the root device doesn't 
	#  work after encapsulation.  
	scvx_is_encapsulation_done
	if [[ $? -ne 1 ]]; then
	        if [[ "${special}" != /dev/dsk/c*t*d*s[0-7] ]];  then
		      printf "$(gettext '%s:  The root file system is mounted on an unrecognized device.')\n" ${PROG} | logerr
		      return 1
		fi

		# Remove the path and change the slice to s2
		special=$(IFS=/ ; set -- ${special};  shift 3;  echo $1)
		SC_ROOTCTD=$(IFS=s ; set -- ${special};  echo $1)
		SC_ROOTDISK=${SC_ROOTCTD}s2
		SC_ROOTRDSK=/dev/rdsk/${SC_ROOTDISK}
	fi

	#
	# Set SC_ROOTARG
	#
	if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
		SC_ROOTARG="-R ${SC_BASEDIR}"
	else
		SC_ROOTARG=
	fi

	#
	# If necessary, create the temp directory for scvxinstall
	#
	if [[ ! -d "${SC_TMPDIR}" ]]; then
		printf "$(gettext '%s:  Cannot find directory %s.')\n" ${PROG} "${SC_TMPDIR}" | logerr
		return 1
	fi
	if [[ ! -d "${SC_SCVXDIR}" ]]; then
		mkdir -m 0755 ${SC_SCVXDIR}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Unable to create %s.')\n" ${PROG} "${SC_SCVXDIR}" | logerr
			return 1
		fi
		chgrp sys ${SC_SCVXDIR}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Unable to set group for %s.')\n" ${PROG} "${SC_SCVXDIR}" | logerr
			return 1
		fi
	fi

	return 0
}

#####################################################
#
# scvx_setlock()
#
#       Check for the "lockfile".  If it already
#       exists, print an error, and return with non-zero.
#       Otherwise, cleanup any old temp files and create a new
#	lockfile with our pid inside.
#
##################################################### 
scvx_setlock()
{
        # Check for SC_LOCKFILE
        if [[ -f ${SC_LOCKFILE} ]]; then
                printf "$(gettext '%s:  Another instance of this program may already be running.')\n" ${PROG} | logerr
                printf "$(gettext '%s:  If not, remove %s and try again.')\n" ${PROG} "${SC_LOCKFILE}" | logerr
                return 1
        fi

	# Cleanup any old temp files
	cleanup

        # Create lockfile
        echo ${PID} >${SC_LOCKFILE} || return 1

        return 0      
}

#####################################################
#
# show_usage()
#
#	Show scvxinstall usage info
#
#	Return values:
#
#		zero		always returned
#
#####################################################
show_usage()
{
	printf "%s:  %s -s\n" "$(gettext 'usage')"  ${PROG}
	printf "\t%s [-L <license> ...] [-d <cdrom>]\n" ${PROG}
	printf "\t%s -i|-e [-L <license> ...] [-d <cdrom>]\n" ${PROG}
	printf "\t%s -H\n" ${PROG}
	echo

	printf "    $(gettext 'Options:')\n"
	printf "\t    %-20s%s\n" "-s" "$(gettext 'show status')"
	printf "\t    %-20s%s\n" "-i" "$(gettext 'install only')"
	printf "\t    %-20s%s\n" "-e" "$(gettext 'install and encapsulate')"
	printf "\t    %-20s%s\n" "-L <license>" "$(gettext 'license token')"
	printf "\t    %-20s%s\n" "-d <cdrom>" "$(gettext 'cdrom image directory')"
	printf "\t    %-20s%s\n" "-H" "$(gettext 'show this message')"
	echo

	return 0
}

#####################################################
#
# show_status()
#
#	Show status of vxvm installation.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
show_status()
{
	integer vxvm_installed=0
	integer vxio
	typeset feature
	typeset features

	if [[ -f ${SC_COMPLETE} ]]; then
                printf "$(gettext '%s has already been run to completion on this node.')\n" ${PROG} | logmsg
	fi

	# Print status of DMP
	scvx_is_dmp_disabled
	if [[ $? -eq 0 ]]; then
		printf "$(gettext '%s is disabled.')\n" DMP | logmsg
	else
		printf "$(gettext '%s is NOT disabled.')\n" DMP | logmsg
	fi

	# Print status of VxVM package installation
	scvx_is_pkgadd_done
	if [[ $? -eq 0 ]]; then
		printf "$(gettext 'The %s package installation step is complete.')\n" "${SC_VXVM}" | logmsg
		((vxvm_installed+=1))
	else
		printf "$(gettext 'The %s package installation step is NOT complete.')\n" "${SC_VXVM}" | logmsg
	fi

	# Print status of name-to-major changes
	vxio=$(scvx_get_vxio_major_number)
	if [[ ${vxio} -eq 0 ]]; then
		printf "$(gettext 'The %s major number is NOT set.')\n" "vxio" | logmsg
	else
		printf "$(gettext 'The %s major number is set to %d.')\n" "vxio" ${vxio} | logmsg
	fi

	# Print status of license setting
	features="$(scvx_is_licensing_done)"
	for feature in ${features}
	do
		printf "$(gettext 'The \"%s\" %s feature is licensed.')\n" ${feature} "${SC_VXVM}" | logmsg
	done
	if [[ -z "${features}" ]]; then
		printf "$(gettext 'None of the %s features are licensed.')\n" "${SC_VXVM}" | logmsg
	fi

	# Print status of VxVM root disk encapsulation
	scvx_is_encapsulation_done
	if [[ $? -eq 0 || $? -eq 1 ]]; then
		printf "$(gettext 'The %s root disk encapsulation step is completed.')\n" "${SC_VXVM}" | logmsg
	else
		printf "$(gettext 'The %s root disk encapsulation step is NOT completed.')\n" "${SC_VXVM}" | logmsg
	fi

	# Print status of vfstab
	scvx_is_vfstab_update_done
	if [[ $? -eq 0 ]]; then
		printf "$(gettext 'The %s file includes the necessary updates.')\n" /etc/vfstab | logmsg
	else
		printf "$(gettext 'The %s file does NOT include the necessary updates.')\n" /etc/vfstab | logmsg
	fi

	# Print status of reminor state
	scvx_is_reminor_done
	if [[ $? -eq 0 ]]; then
		printf "$(gettext 'The %s %s step is complete.')\n" "rootdg" "reminoring" | logmsg
	else
		printf "$(gettext 'The %s %s step is NOT complete.')\n" "rootdg" "reminoring" | logmsg
	fi

	return 0
}

#####################################################
#
# scvx_get_action()
#
#	Set SC_PERFORM_ENCAP to "true" or "false", based
#	on response from the user.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
scvx_get_action()
{
	typeset answer
	typeset foo

	typeset lyes="$(gettext 'yes')"
	typeset lno="$(gettext 'no')"

	answer=
	echo
	while [[ -z "${answer}" ]]
	do
		printf "$(gettext 'Do you want %s to encapsulate root [no]?  ')\a" "${SC_VXVM}"
		read answer foo

		# Default?
		if [[ -z "${answer}" ]]; then
			answer="${lno}"
		fi

		# White space?
		if [[ -n "${foo}" ]]; then
			answer=
			continue
		fi

		# If English, allow y, Y, yes, YES, n, N, no, NO
		if [[ "${lyes}" == "yes" ]] && [[ "${lno}" == "no" ]]; then
			answer=$(echo ${answer} | nawk '{ print tolower($1) }')
			if [[ "${answer}" == "y" ]]; then
				answer=yes
			elif [[ "${answer}" == "n" ]]; then
				answer=no
			fi
		fi

		# Make sure the answer is yes or no
		if [[ "${answer}" != "${lyes}" ]] &&
		    [[ "${answer}" != "${lno}" ]];  then
			printf "$(gettext 'Please answer \"%s\" or \"%s\".')\n\n\a" "${lyes}" "${lno}"
			answer=
			continue
		fi
	done
	echo

	if [[ "${answer}" == "${lyes}" ]]; then
		SC_PERFORM_ENCAP=true
	else
		SC_PERFORM_ENCAP=false
	fi

	return 0
}

#####################################################
#
# scvx_check_pkgpath() pkg_path
#
#	pkg_path		package path to test
#
#	Check to see if the given "pkg_path" leads to the VxVM
#	packages.  If one or more of the packages are not found,
#	print the list to stdout.
#
#	Return values:
#
#		zero		the VxVM packages are found
#		non-zero	the VxVM packages are not found
#
#####################################################
scvx_check_pkgpath()
{
	typeset pkg_path=${1}

	typeset pkg
	integer errs

	# verify path
	if [[ ! -d "${pkg_path}" ]]; then
		echo ${SC_VXVM_PKGS}
		return 1
	fi

	# verify packages
	let errs=0
	for pkg in ${SC_VXVM_PKGS}
	do
		if [[ ! -d ${pkg_path}/${pkg} ]]; then
			# Skip the license packages as it only appears in 
			# versions 3.2 and later
			if [[ ${pkg} == ${SC_VXVM_LIC_PKG_OLD} ]] || [[ ${pkg} == ${SC_VXVM_LIC_PKG_35} ]]; then
				continue
			fi
			# Skip the dev package as it is removed in 3.5 and
			# possibly, later
			if [[ ${pkg} == ${SC_VXVM_DEV_PKG} ]]; then
				continue
			fi
			echo ${pkg}
			((errs += 1))
		fi
	done

	return ${errs}
}

#####################################################
#
# scvx_construct_pkgpath() pkg_path_base
#
#	pkg_path_base		package path base
#
#	Use the "pkg_path_base" and the list of package paths
#	in SC_CDIMAGE_PATHS to construct a complete package path.
#	If one is found, it is printed on stdout.
#
#	Return values:
#
#		zero		complete package path found
#		non-zero	cannot find VxVM packages
#
#####################################################
scvx_construct_pkgpath()
{
	typeset pkg_path_base=${1}
	typeset cdimage_paths

	# Make sure it is set to something
	if [[ -z "${pkg_path_base}" ]]; then
		return 1
	fi

	# Maybe we don't need to add anything
	scvx_check_pkgpath ${pkg_path_base} >/dev/null
	if [[ $? -eq 0 ]]; then
		echo ${pkg_path_base}
		return 0
	fi

	# Search the list of SC_CDIMAGE_PATHS
	for cdimage_path in ${SC_CDIMAGE_PATHS}
	do
		scvx_check_pkgpath ${pkg_path_base}/${cdimage_path} >/dev/null
		if [[ $? -eq 0 ]]; then
			echo ${pkg_path_base}/${cdimage_path}
			return 0
		fi
	done

	# Not found
	return 1
}

#####################################################
#
# scvx_get_location() [is_interactive]
#
#	is_interactive			if set, prompt user
#
#	If packages are not already installed, this function
#	sets or resets SC_PKG_PATH, upon success.
#	If "is_interactive" is set, the user is prompted.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
scvx_get_location()
{
	typeset is_interactive=${1}

	typeset pkg_path
	typeset cdimage_path
	typeset new_path
	typeset pkg
	typeset dflt
	typeset foo

	# If packages are already installed, we are done
	scvx_is_pkgadd_done
	if [[ $? -eq 0 ]]; then
		return 0
	fi

	#
	# Non-Interactive
	#
	if [[ -z "${is_interactive}" ]] && [[ -z "${SC_PKG_PATH}" ]]; then
		SC_PKG_PATH=${SC_DFLT_CDIMAGE}
	fi

	# If SC_PKG_PATH is set, do not prompt for it
	if [[ -n "${SC_PKG_PATH}" ]]; then

		# Re-construct SC_PKG_PATH
		new_path=$(scvx_construct_pkgpath ${SC_PKG_PATH})
		if [[ -z "${new_path}" ]]; then
			printf "$(gettext '%s:  Cannot find %s packages at %s.')\n" ${PROG} "${SC_VXVM}" ${SC_PKG_PATH}
			return 1
		fi
		SC_PKG_PATH=${new_path}

		# We are done
		return 0
	fi

	#
	# Interactive - prompt for the path
	#

	# Set "dflt" if cdrom0 is loaded with a supported CD
	dflt=
	new_path=$(scvx_construct_pkgpath ${SC_DFLT_CDIMAGE})
	if [[ -n "${new_path}" ]]; then
		dflt=${SC_DFLT_CDIMAGE}
	fi

	pkg_path=
	while [[ -z "${pkg_path}" ]]
	do
		# Prompt, with possible default
		if [[ -n "${dflt}" ]]; then
			printf "$(gettext 'Where is the %s cdrom [%s]?  ')\a" "${SC_VXVM}" ${dflt}
		else
			printf "$(gettext 'Where is the %s cdrom?  ')\a" "${SC_VXVM}"
		fi

		# Get answer
		read pkg_path foo
		if [[ -n "${foo}" ]]; then
			pkg_path=
			continue
		fi

		# If no answer and default, use default
		if [[ -z "${pkg_path}" ]] && [[ -n "${dflt}" ]]; then
			pkg_path=${dflt}
		fi

		# If no answer and no default, beep and repeat
		if [[ -z "${pkg_path}" ]]; then
			continue
		fi

		#
		# See if the packages are there.
		# The user may supply either the cdimage directory
		# OR the packages location directory.
		#
		new_path=$(scvx_construct_pkgpath ${pkg_path})
		if [[ -z "${new_path}" ]]; then
			printf "$(gettext 'Cannot find %s packages at %s.')\n\n\a" "${SC_VXVM}" ${pkg_path}
			pkg_path=
			continue
		fi

		# Done
		pkg_path=${new_path}
	done
	echo

	# Okay, set the PKG_PATH to path-to-packages
	SC_PKG_PATH=${pkg_path}

	return 0
}

#####################################################
#
# scvx_get_ltoken()
#
#	Get license token from the user.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
scvx_get_ltoken()
{
	integer i
	typeset license=

	scvx_is_licensing_done >/dev/null
	if [[ $? -eq 0 ]]; then
		
		echo
		printf "    $(gettext 'One or more %s features are already licensed.')\n" "${SC_VXVM}" | logmsg
		pkginfo -l -q ${SC_VXVM_LIC_PKG_35}
		if [[ $? -eq 0 ]]; then
			printf "    $(gettext 'Use the \"%s\" command to get a report of Veritas license keys ')\n" "vxlicrep" | logmsg
			printf "    $(gettext 'installed.')\n" | logmsg
		fi
		printf "    $(gettext 'If you do not want to supply an additional license key, just')\n" | logmsg
		printf "    $(gettext 'press ENTER.  Otherwise, you may provide one additional key.')\n" | logmsg

		echo
		printf "$(gettext 'Please enter a %s license key [%s]:  ')\a" "${SC_VXVM}" "none"
		read license
		echo

		# Make sure none is "none"
		if [[ -z "${license}" ]] || [[ "$(echo ${license} | nawk '{ print tolower($1) }')" == none ]]; then
			license=none
		fi
	else
		echo
		while [[ -z "${license}" ]]
		do
			printf "$(gettext 'Please enter a %s license key:  ')\a" "${SC_VXVM}"
			read license

			# Don't let them type "none"
			if [[ "$(echo ${license} | nawk '{ print tolower($1) }')" == none ]]; then
				license=
				continue
			fi
		done
		echo
	fi

	# save license key
	let i=0
	while [[ -n "${SC_LICENSES[i]}" ]]
	do
		((i += 1))
	done
	SC_LICENSES[i]="${license}"
}

#####################################################
#
# verify_encap_requirements()
#
#	Verify scvxinstall requirements for encapsulation:
#
#		- root disk has slice 2 and 2 free partitions
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
verify_encap_requirements()
{
	# Print message
	printf "$(gettext 'Verifying encapsulation requirements.')\n" | logmsg

	# verify our root disk requirements

	# get root disk
	# make sure slice 2 exists and starts at cylinder 0
	slice2=$(prtvtoc -h ${SC_ROOTRDSK} | awk '$1 == "2" { print $4 }')
	if [[ -z "${slice2}" ]] || [[ "${slice2}" != 0 ]]; then
		printf "$(gettext '%s:  Root disk must have slice 2 set to the entire disk.')\n" ${PROG} | logerr
		return 1
	fi

	# make sure we have 2 free partitions
	slices="$(echo $(prtvtoc -h ${SC_ROOTRDSK} | wc -l))"
	if [[ $slices -gt 6 ]]; then
		printf "$(gettext '%s:  Root disk must have 2 free slices.')\n" ${PROG} | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# scvx_get_symlink filename
#
#	Print the name of the file pointed to by the sym link.
#
#	Return values:
#
#		zero		always returned
#
#####################################################
scvx_get_symlink()
{
	typeset filename=$1

	if [[ -z "${filename}" ]] || [[ ! -L "${filename}" ]]; then
		return 0
	fi

	ls -l ${filename} | awk '{ print $NF }'

	return 0
}

#####################################################
#
# scvx_is_dmp_disabled()
#
#	Return non-zero if DMP is not fully disabled;  otherwise,
#	return 0.
#
#	Return values:
#
#		zero		DMP is fully disabled
#		non-zero	DMP is not fully disabled
#
#####################################################
scvx_is_dmp_disabled()
{
	if [[ ! -d /dev/vx ]] ||
	    [[ ! -L /dev/vx/dmp ]] ||
	    [[ ! -L /dev/vx/rdmp ]] ||
	    [[ "$(scvx_get_symlink /dev/vx/dmp)" != /dev/dsk ]] ||
	    [[ "$(scvx_get_symlink /dev/vx/rdmp)" != /dev/rdsk ]]; then
		return 1
	fi

	return 0
}

#####################################################
#
# disable_DMP()
#
#	Disable DMP by linking /dev/vx/{r}dmp to /dev/{r}dsk.
#
#	DMP refers to Veritas' own feature called
#	Dynamic MultiPathing.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
disable_DMP()
{
	# See if DMP is already disabled
	scvx_is_dmp_disabled
	if [[ $? -eq 0 ]]; then
		printf "$(gettext '%s is already disabled.')\n" DMP | logmsg
		return 0
	fi

	# Print message
	printf "$(gettext 'Disabling %s.')\n" DMP | logmsg

	# Create /dev/vx, if it is not already there
	if [[ ! -d /dev/vx ]]; then
		mkdir -m 0755 /dev/vx
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Unable to create %s.')\n" ${PROG} /dev/vx | logerr
			return 1
		fi
	fi

	# Create /dev/vx/dmp, if it is not already there
	if [[ ! -r /dev/vx/dmp ]]; then
		ln -s /dev/dsk /dev/vx/dmp
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Unable to create %s.')\n" ${PROG} /dev/vx/dmp | logerr
			return 1
		fi

	# if it is already there, it should be a sym link
	# However versions of VxVM (3.1.1 and later) require DMP to be enabled.
	# Thus, if DMP cannot be disabled, then we will not exit but instead
	# continue the installation.
	else
		if [[ ! -L /dev/vx/dmp ]] ||
	    	    [[ "$(scvx_get_symlink /dev/vx/dmp)" != /dev/dsk ]]; then
			printf "$(gettext '%s:  %s is not set up correctly.')\n" ${PROG} /dev/vx/dmp | logerr
			printf "$(gettext '%s:  Warning: Unable to disable %s, but installation will continue...')\n" ${PROG} DMP | logerr
		fi
	fi

	# Create /dev/vx/rdmp, if it is not already there
	if [[ ! -r /dev/vx/rdmp ]]; then
		ln -s /dev/rdsk /dev/vx/rdmp
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Unable to create %s.')\n" ${PROG} /dev/vx/rdmp | logerr
			return 1
		fi

	# if it is already there, it should be a sym link
	# However versions of VxVM (3.1.1 and later) require DMP to be enabled.
	# Thus, if DMP cannot be disabled, then we will not exit but instead
	# continue the installation.
	else
		if [[ ! -L /dev/vx/rdmp ]] ||
	    	    [[ "$(scvx_get_symlink /dev/vx/rdmp)" != /dev/rdsk ]]; then
			printf "$(gettext '%s:  %s is not set up correctly.')\n" ${PROG} /dev/vx/rdmp | logerr
			printf "$(gettext '%s:  Warning: Unable to disable %s, but installation will continue...')\n" ${PROG} DMP | logerr
		fi
	fi

	return 0
}

#####################################################
#
# scvx_is_pkg_installed() package [partial]
#
#	Check to see if the given "package" is installed.
#	if "partial" is given, just check to see if the
#	packages are partially installed.
#
#	Return zero if the package installed;  otherwise,
#	return non-zero.   If "partial" is given, return zero
#	if the package is ONLY partially installed.
#
#	Return values:
#
#		zero		the package is installed
#		non-zero	the package is not installed
#
#####################################################
scvx_is_pkg_installed()
{
	typeset package=$1
	typeset partial=$2

	# Check argument
	if [[ -z "${package}" ]]; then
		return 1
	fi

	# Partial?
	if [[ -n "${partial}" ]]; then
		partial="-p"
	fi

	# See if the package is installed
	pkginfo -q ${partial} ${SC_ROOTARG} ${package} >/dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		return 1
	fi

	return 0
}

#####################################################
#
# scvx_is_pkgadd_done
#
#	Checks to see if the VxVM package add step completed.
#	Return non-zero if not all of the packages are fully installed.
#
#	Return values:
#
#		zero		All required VxVM pkgs are fully installed
#		non-zero	All required VxVM pkgs are NOT fully installed
#
#####################################################
scvx_is_pkgadd_done()
{
	typeset pkg

	for pkg in ${SC_VXVM_REQ_PKGS}
	do
		# If not installed, return 1
		scvx_is_pkg_installed ${pkg}
		if [[ $? -ne 0 ]]; then
			return 1
		fi

		# If installed, but only partially, return 1
		scvx_is_pkg_installed ${pkg} check_partial
		if [[ $? -eq 0 ]]; then
			return 1
		fi
	done

	return 0
}

#####################################################
#
# setup_pkgadd_stuff()
#
#	Create the admin and response files for auto-pkgadd.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
setup_pkgadd_stuff()
{
	# setup admin file
	cat >${SC_ADMIN} <<END
mail=
instance=quit
partial=quit
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=quit
setuid=nocheck
conflict=nocheck
action=nocheck
basedir=default
END
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Unable to create %s.')\n" ${PROG} ${SC_ADMIN} | logerr
		return 1
	fi

	# setup response file
	cat << EOF >${SC_RSP}
REQ_OS_VERS=${SC_SUNOS}
EOF
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Unable to create %s.')\n" ${PROG} ${SC_RSP} | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# install_pkgs()
#
#	Install VRTS* packages.
#
#	Removes the vxio entry that was added by
#	the package add of VRTSvxvm.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
install_pkgs()
{
	typeset pkg
	typeset cmd

	# See if packages are already fully installed
	scvx_is_pkgadd_done
	if [[ $? -eq 0 ]]; then
		printf "$(gettext 'The %s package installation is already complete.')\n" "${SC_VXVM}" | logmsg
		return 0
	fi

	# Print message
	printf "$(gettext 'Installing packages from %s.')\n" ${SC_PKG_PATH} | logmsg

	# Create the admin and response files
	setup_pkgadd_stuff || return 1

	# Add each of the packages
	for pkg in ${SC_VXVM_PKGS}
	do
		# If already installed, skip it
		scvx_is_pkg_installed ${pkg}
		if [[ $? -eq 0 ]]; then

			# But, first, make sure it is not a partial
			scvx_is_pkg_installed ${pkg} check_partial
			if [[ $? -eq 0 ]]; then
				printf "$(gettext '%s:  %s is found to be only partially installed.')\n" ${PROG} ${pkg} | logerr
				printf "$(gettext '%s:  You must remove %s using pkgrm.')\n" ${PROG} ${pkg} | logerr
				return 1
			fi

			# Skip to the next package
			continue
		fi

		# Check for the Veritas license package VRTSlic being
		# introduced in VxVM3.2 and later versions.  This package must
		# be installed prior to all other VRTS packages.
		if [[ ${pkg} == ${SC_VXVM_LIC_PKG_OLD} && ! -d "${SC_PKG_PATH}/${SC_VXVM_LIC_PKG_OLD}" ]]; then
			# No VRTSlic package found!  Must be a version of 
			# VxVM prior to 3.2.  So skip to the next package
			continue
		fi

		# The above hold true for the new licensing pkg introduced
		# in VxVM 3.5 also
		if [[ ${pkg} == ${SC_VXVM_LIC_PKG_35} && ! -d "${SC_PKG_PATH}/${SC_VXVM_LIC_PKG_35}" ]]; then
			# No VRTSvlic package found!  Must be a version of
			# VxVM prior to 3.5.  So skip to the next package
			continue
		fi

		# Check for the Veritas developer package removed in VxVM3.5
		# and possibly, later versions.
		if [[ ${pkg} == ${SC_VXVM_DEV_PKG} && ! -d "${SC_PKG_PATH}/${SC_VXVM_DEV_PKG}" ]]; then
			# No dev package found!  Must be a version of
			# VxVM later than 3.2.  So skip to the next package
			continue
		fi

		# Add the package
		printf "$(gettext 'Installing %s.')\n" ${pkg} | logmsg
		cmd="pkgadd -a ${SC_ADMIN} -r ${SC_RSP} -d ${SC_PKG_PATH} ${SC_ROOTARG} ${pkg}"
		echo "\n${cmd}" >> ${SC_LOGFILE}
		${cmd} >> ${SC_LOGFILE} 2>&1
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to install %s.')\n" ${PROG} ${pkg} | logerr
			printf "$(gettext '%s:  Check %s for more information.')\n" ${PROG} ${SC_LOGFILE} | logerr
			return 1
		fi
	done

	# Remove the vxio major number entry that was added by
	# package add of VRTSvxvm.
	remove_vxio_entry
	if [[ $? -ne 0 ]]
	then
		return 1
	fi

	# Success
	return 0
}

#####################################################
#
# scvx_get_vxio_major_number
#
#	Return the major number for the vxio entry
#	as defined in the SC_NAME_TO_MAJOR file.
#
#	Return values:
#
#		0		vxio is NOT defined
#		non-zero	vxio is defined
#
#####################################################
scvx_get_vxio_major_number()
{
	typeset major

	# See if we find vxio is set to 
	major=$(awk '/^vxio[ \t]/ {print $2}' ${SC_NAME_TO_MAJOR})
	if [[ -z "${major}" ]]; then
		return 0
	fi
	echo ${major}
}

#####################################################
#
# scvx_reset_vxio_major_num()
#
#	Dynamically negotiate and reset the clusterwide major
#	number for Veritas' vxio driver.  SunCluster requires that
#	the major number for vxio must be equal on all nodes.
#
#	A vxio major number is automatically selected when the VRTSvxvm package
#	is added to the node.  However, this selection is local to that node
#	and might not match other vxio major numbers on other nodes of the
#	cluster.  (e.g. differing vxio major numbers on	different nodes).
#
#	To ensure that all vxio entries match on all nodes, after the package
#	addition, the automatically selected vxio entry is removed from
#	SC_NAME_TO_MAJOR on this node only.  
#	Then the other nodes are consulted to determine a valid 
#	major number for vxio across ALL nodes.
#	
#	This is how the negotiation works:  
#	A clusterwide entry is determined by finding each node's maximum entry 
#	and any vxio entries in the SC_NAME_TO_MAJOR file.  If any
#	node does not yield its maximum major number entry, then we cannot
#	determine the vxio entry and scvxinstall will fail.
#
#	The clusterwide vxio entry can be determined by :
#		1. If all entries in vxio[] are undefined, then this signifies
#		   that no node has yet determined the clusterwide vxio major
#		   number.
#		   Thus this node will be the first node to determine that value
#		       new_major = max(max_entries) + "buffer"
#		2. Else 
#		   Sleep and rediscover each node's maximum entry and vxio
#		   entry in SC_NAME_TO_MAJOR file.  The reason for sleeping
#		   and rediscovery is to protect against the corner case
#		   where another node has not yet removed its vxio major number 
#		   from the VRTSvxvm package addition.
#		   
#		   Analyze the defined vxio entries:
#		   If all vxio entries are equal, then
#			if this vxio entry is valid on this local node, then
#				new_major = max_vxio
#			if this vxio entry is not vaild, then
#				print out an error message and stop
#		   else
#			print out error message stating different vxio major
#			number were seen on different nodes and stop
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
scvx_reset_vxio_major_num()
{
	typeset priv_node
	typeset new_major
	typeset max_node
	typeset used_driver
	integer i=1
	integer j=1
	integer index=0
	integer retry=0
	integer loop=0
	integer max_major=0
	integer max_vxio=-1
	integer error_type

	set -A vxio_max_pair
	set -A vxio
	set -A max
	set -A nodes
	set -A public_names ${SC_NODES}

	while true
	do
		# Initialize
		retry=0
		i=1
		max_major=0
		max_vxio=-1

		# From every node, obtain a pair of integers separated by ','
		#	n,m
		# 		n = vxio major number, if it exists
		#		m = maximum major number defined
		#	NOTE: if vxio does not exist on that node, then n = -1.

		# Use private names for scrconf but use public names for error displays

		for priv_node in ${SC_NODES_PRIVATE}
		do
			nodes[$i]=${public_names[${index}]}
			(( index += 1 ))

			vxio_max_pair[$i]=$(scrconf -d vxio -N ${priv_node} 2>&1)


			if [[ $? -ne 0 ]]
			then
				printf "$(gettext '%s:  Failed to obtain node information from %s.  Trying again...')\n" ${PROG} ${nodes[$i]} | logerr
			else
				(( i += 1 ))
			fi
		done

		# Parse the vxio_max_pair[] array
		j=1
		while [[ $j -le ${#vxio_max_pair[@]} ]]
		do
			if [[ -z ${vxio_max_pair[$j]} ]]
			then
			# One of the nodes did not return a valid pair
				error_type=1
				(( retry += 1 ))
				break
			else
			# Store the pair into two arrays
				vxio[$j]=$(echo ${vxio_max_pair[$j]} | awk -F',' '{print $1}')	
				max[$j]=$(echo ${vxio_max_pair[$j]} | awk -F',' '{print $2}')	
				# Keep track of the maximum vxio number
				if [[ ${vxio[$j]} -ne -1 && ${vxio[$j]} -ne ${max_vxio} ]]
				then
					if [[ ${max_vxio} -eq -1 ]]
					then
						max_vxio=${vxio[$j]}
						max_node=${nodes[$j]}
					else
						# Caution: Different vxio
						# numbers were returned from two
						# different nodes.
						(( retry += 1 ))
						error_type=2
						break
					fi
				fi
				# Keep track of the maximum major number
				if [[ ${max[$j]} -gt ${max_major} ]]
				then
					max_major=${max[$j]}
				fi
			fi
			(( j += 1 ))
		done

		# Did every node respond with a valid vxio,max major number 
		# pair?
		if [[ $retry -eq 0 ]]
		then
		# Yes, every node responded with a valid pair, so proceed
		# with analysis of vxio major numbers.
			break;
		# No, at least one node did not respond with a valid pair.
		elif [[ $loop -ge ${SC_MAX_LOOP_TRIES} ]]
		then
			# Print the appropriate error message to the user
			if [[ ${error_type} -eq 1 ]]
			then
				# Unexpected error.
				# At least one node did not respond with a 
				# valid pair and we have tried multiple times
				# so quit and print an error
				printf "$(gettext '%s:  The value for a common, valid vxio entry cannot be determined because at least one node %s did not respond with valid node information.  If the unresponsive node was busy rebooting, then wait for the reboot to complete and rerun %s.')\n" ${PROG} ${nodes[$j]} ${PROG} | logerr
				return 1
			else
				# Unexpected error.
				# Two nodes have different values for
				# the vxio driver.  We cannot resolve
				# which is the right entry so exit.
				printf "$(gettext '%s:  The value for a common, valid vxio entry cannot be determined because at least two nodes (%s, %s) have different vxio entries (%s, %s) respectively.  Ensure that a common vxio entry is chosen across all nodes of the cluster.')\n" ${PROG} ${nodes[$j]} ${max_node} ${vxio[$j]} ${max_vxio} | logerr
				return 1
			fi
		else
			# Try again as a node might not be far enough in its
			# boot to handle scrconf requests.  So sleep and try
			# again.
			sleep 20 
			(( loop += 1 ))
		fi
	done

	# Analysis of vxio major numbers.
	# (Once we get here, then all nodes have responded with valid pairs.)
	if [[ ${max_vxio} -eq -1 ]]
	then
		# No other node else has figured out the new major number yet.
		# So we'll define a new one on the entire cluster's behalf.
		((new_major=${max_major} + ${SC_MAJOR_NUM_BUFFER}))

		# Ensure that the major number does not exceed the maximum
		if [[ ${new_major} -gt ${SC_MAX_MAJOR_NUMBER} ]]
		then
			printf "$(gettext '%s:  The major number chosen %s exceeds the maximum allowed major number %s.')\n" ${PROG} ${new_major} ${SC_MAX_MAJOR_NUMBER} | logerr
			return 1
		fi
	else
		# See if the newly discovered vxio number is unused on this node
		used_driver=$(grep "[ \t]${max_vxio}$" ${SC_NAME_TO_MAJOR} | awk '{print $1}') 
		if [[ -n ${used_driver} ]]
		then
		# No, some other driver is using it!!
			printf "$(gettext '%s:  The major number %s failed to be assigned to the vxio driver since it is in use by the driver %s on %s.')\n" ${PROG} ${max_vxio} ${used_driver} ${SC_MYNODENAME} | logerr
			return 1
		else
		# The discovered vxio is unused so use it!
			new_major=${max_vxio}
		fi
	fi

	# We have successfully negotiated the vxio major number.
	# Apply it to the SC_NAME_TO_MAJOR file.
	ed -s ${SC_NAME_TO_MAJOR} << EOF >/dev/null 2>&1
\$a
vxio ${new_major}
.
w
q
EOF
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to update %s.\n')\n" ${PROG} ${SC_NAME_TO_MAJOR} | logerr
		return 1
	else
		printf "$(gettext 'Using %s as the vxio major number.')\n" ${new_major} | logmsg
	fi

	# Set the re-boot flag
	((SC_REBOOT_REQUIRED+=1))

	return 0
}

#####################################################
#
# scvx_is_licensing_done
#
#	Checks to see if the VxVM licensing step completed.
#	Prints a list of licensed features.
#
#	Return values:
#
#		zero		one or more features are licensed
#		non-zero	no features are licensed
#
#####################################################
scvx_is_licensing_done()
{
	integer found
	integer	lic_count
	typeset feature
	typeset features
	typeset mode

	# Make sure we have VxVM
	scvx_is_pkgadd_done
	if [[ $? -ne 0 ]]; then
		return 1
	fi

	#
	# Start vxconfigd, if it is not already running;
	# vxconfigd establishes hardware licenses.
	#
	mode="$(
		LANG=C; export LANG
		vxdctl mode | awk '/mode:/ {
			if ($1 == "mode:")
				mode=$2
		}
		END {
		if (NR ==1)
			print mode
		}'
	)"
	if [[ -z "${mode}" ]]; then
		# Cannot get the mode of vxconfigd
		printf "$(gettext '%s:  Unexpected response from %s.')\n" ${PROG} "vxdctl" | logerr
		return 1
	elif [[ "${mode}" == "not-running" ]]; then
		# Start a new vxconfigd in disabled mode
		vxconfigd -m disable >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to start \"%s\".')\n" ${PROG} "vxconfigd" | logerr
			return 1
		fi
	fi

	# For VxVM3.5 and above just check for any licensed feature in the
	# Veritas VM product.
	
	pkginfo -l -q ${SC_VXVM_LIC_PKG_35}
	
	if [[ $? -eq 0 ]]; then
		vxlictest -n "VERITAS Volume Manager" -l >${SC_TMPLIC} 2>&1
		if [[ $? -eq 0 ]]; then
			lic_count=`cat ${SC_TMPLIC} | wc -l`
			if [[ ${lic_count} -ge 1 ]]; then
				echo Veritas_Volume_Manager
				rm -f ${SC_TMPLIC}
				return 0
			fi
		fi
		rm -f ${SC_TMPLIC}
		return 1
	fi

	# Get the list of features which might have valid licenses
	features="$(
		LANG=C; export LANG
		vxlicense -p | nawk '/[Ff]eature/ {
			found_feature=0
			for (i = 0;  i <= NF;  i++) {
				if (tolower($i) == "feature") {
					found_feature=1
				} else if (tolower($i) == "name:" &&
				    found_feature == 1) {
					found_feature=2
				} else if (found_feature == 2) {
					print $i
					break
				} else {
					found_feature=0
				}
			}
		}'     
	)"

	# Now, print the list of any licensed features
	let found=0
	for feature in ${features}
	do
		vxlicense -t ${feature} >/dev/null 2>&1
		if [[ $? -eq 0 ]]; then
			echo ${feature}
			((found += 1))
		fi
	done
	if [[ ${found} -ne 0 ]]; then
		return 0
	fi

	return 1
}

#####################################################
#
# scvx_install_license() [is_interactive]
#
#	Install VxVM usage license.  If "is_interactive" is set
#	and one was not given on the command line, prompt the user for
#	the license.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
scvx_install_license()
{
	is_interactive=${1}

	integer i
	integer found
	integer licensed
	integer status
	integer failed

	typeset feature
	typeset features

	# See if we already have a license
	let found=0
	features="$(scvx_is_licensing_done)"
	for feature in ${features}
	do
		# The list of features can be huge in 3.5 and above, we don't
		# try to dump it
		pkginfo -l -q ${SC_VXVM_LIC_PKG_35}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext 'The \"%s\" %s feature is already licensed.')\n" ${feature} "${SC_VXVM}" | logmsg
		fi
		((found += 1))
	done

	# Non-Interactive
	if [[ -z "${is_interactive}" ]] && [[ -z "${SC_LICENSES[0]}" ]]; then
		SC_LICENSES[0]="none"
	fi

	# If not on the command line, and interactive, get from the user now.
	if [[ -z "${SC_LICENSES[0]}" ]]; then
		scvx_get_ltoken
	fi

	# If "none", we are done, as long as something is licensed.
	if [[ -z "${SC_LICENSES[0]}" ]] || [[ "${SC_LICENSES[0]}" == "none" ]]; then
		# We should not get here if this entered at the prompt
		if [[ ${found} -eq 0 ]]; then
			printf "$(gettext '%s:  A %s license is required, please retry with a valid license.')\n" ${PROG} "${SC_VXVM}" | logerr
			return 1
		fi
		return 0
	fi

	# Print message
	if [[ -z "${SC_LICENSES[1]}" ]]; then
		printf "$(gettext 'Installing %s license.')\n" "${SC_VXVM}" | logmsg
	else
		printf "$(gettext 'Installing %s licenses.')\n" "${SC_VXVM}" | logmsg
	fi

	# install licenses
	let i=0
	let licensed=0
	while [[ -n "${SC_LICENSES[i]}" ]]
	do
		pkginfo -l -q ${SC_VXVM_LIC_PKG_35}
		
		if [[ $? -eq 0 ]]; then
			vxlicinst -k "${SC_LICENSES[i]}" >/dev/null 2>${SC_TMPERR}
		else
			echo "${SC_LICENSES[i]}" | vxlicense -c >/dev/null 2>${SC_TMPERR}
		fi
		
		status=$?
		if [[ -s "${SC_TMPERR}" ]]; then
			logerr <${SC_TMPERR}
		fi
		rm -f ${SC_TMPERR}
		if [[ ${status} -ne 0 ]]; then
			printf "$(gettext '%s:  Bad license key - \"%s\".')\n" ${PROG} "${SC_LICENSES[i]}" | logerr
		else
			((licensed += 1))
		fi
		((i += 1))
	done
	((failed = i - licensed))
	if [[ ${failed} -eq 1 ]]; then
		printf "$(gettext '%s:  Failed to install %s license.')\n" ${PROG} "${SC_VXVM}" | logerr
		return 1
	elif [[ ${failed} -gt 1 ]]; then
		printf "$(gettext '%s:  Failed to install %s licenses.')\n" ${PROG} "${SC_VXVM}" | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# scvx_is_encapsulation_done
#
#	Checks to see if the rootdisk has already been encapsulated or
#	if we completed setup for root disk encapsulation.
#
#	Return values:
#
#		zero		Root disk encapsulation step is complete
#		one		Root disk has already been encapsulated
#		two		Root disk encapsulation step is not complete
#
#####################################################
scvx_is_encapsulation_done()
{
	typeset ctd

	# Make sure we have VxVM
	scvx_is_pkgadd_done
	if [[ $? -ne 0 ]]; then
		return 2
	fi

	# Make sure that the rootdisk has not already been successfully
	# encapsulated.
	scvx_is_rootdisk_encapsulated
	if [[ $? -eq 0 ]]; then
		return 1
	fi

	# Check if the setup for rootdisk encapsulation has been
	# completed.
	if [[ -f ${SC_VXVM_ENCAP_CONFIGFILE} ]]
	then
		ctd="$(
			LANG=C;  export LANG
			cat ${SC_VXVM_ENCAP_CONFIGFILE} | awk '/${SC_ROOTCTD}/ {
				if ($1 == "${SC_ROOTCTD}")
					print $1
			}'
		)"	
		if [[ $ctd == $SC_ROOTCTD ]]
		then
			return 0
		fi
	fi
	return 2
}

#####################################################
#
# scvx_prepare_encapsulation()
#
#	Configure root disk for VxVM encapsulation.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
scvx_prepare_encapsulation()
{
	typeset mode
	typeset ctd
	typeset ret

	# See if the rootdisk has already been encapsulated, or if we are 
	# already set up to encapsulate
	scvx_is_encapsulation_done
	ret=$?
	if [[ $ret -eq 0 ]]; then
		printf "$(gettext 'The %s root disk encapsulation step is already completed.')\n" "${SC_VXVM}" | logmsg
		return 0
	elif [[ $ret -eq 1 ]]; then
		printf "$(gettext '%s:  The rootdisk has already been successfully encapsulated on this node.')\n" ${PROG} | logerr
		return 1
	fi

	# Print message
	printf "$(gettext 'Arranging for %s encapsulation of the root disk.')\n" "${SC_VXVM}" | logmsg

	#
	# Proceed with rootdisk encapsulation preparation.		
	#

	# The vxconfigd daemon may have already been started by the license 
	# check operation.  But, if not, start it now in disabled mode.
	mode="$(
		LANG=C; export LANG
		vxdctl mode | awk '/mode:/ {
			if ($1 == "mode:")
				mode=$2
		}
		END {
		if (NR ==1)
			print mode
		}'
	)"
	if [[ -z "${mode}" ]]; then
		# Cannot get the mode of vxconfigd
		printf "$(gettext '%s:  Unexpected response from %s.')\n" ${PROG} "vxdctl" | logerr
		return 1
	elif [[ "${mode}" == "not-running" ]]; then
		# Start a new vxconfigd in disabled mode
		vxconfigd -m disable >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to start \"%s\".')\n" ${PROG} "vxconfigd" | logerr
			return 1
		else
			# Successfully started the vxconfigd daemon in disabled
			# mode.
			printf "$(gettext 'The vxconfigd daemon has been started and is in disabled mode ...')\n" | logmsg
		fi
	elif [[ "${mode}" != "disabled" ]]; then
		# vxconfigd daemon is started but not in disabled mode 
		printf "$(gettext '%s:  The vxconfigd daemon is in \"%s\" mode but needs to be in \"disable\" mode.')\n" "${PROG}" ${mode} | logerr
		return 1
	else
		# vxconfigd daemon is started and is in disabled mode.
		printf "$(gettext 'The vxconfigd daemon has been started and is in disabled mode...')\n" | logmsg
	fi

	# Re-initialize the volboot file
	# vxconfigd should be running in 'disable' mode for the volboot to 
	# be re-initialzed.  Any existing volboot file is simply rewritten,
	# so it is ok if 'vxdctl init' has been executed beforehand.
	vxdctl init
	if [[ $? -ne 0 ]];  then
		# Failure 'vxdctl init'
		printf "$(gettext '%s:  \"%s\" failed.')\n" ${PROG} "vxdctl init" | logerr
		return 1
	else
		# Success 'vxdctl init'
		printf "$(gettext 'Reinitialized the volboot file...')\n" | logmsg
	fi

	# Test for presence of VxVM 4.0
	vxdg bootdg > /dev/null 2>&1
	if [[ $? -eq 0 ]]; then
		# This means 4.0 packages have been installed, so
		# set bootdg alias
		vxdctl bootdg rootdg
		if [[ $? -ne 0 ]]; then
			# Failure in setting bootdg alias
			printf "$(gettext '%s:  Unable to set %s.')\n" ${PROG} "bootdg alias" | logerr
			return 1
		fi

		# remove install-db file, so that vxencap can run successfully
		rm -f /etc/vx/reconfig.d/state.d/install-db

		# Run vxencap - create disk group called 'rootdg', place root
		# disk in this group and encapsulate it
		vxencap -A -c -g bootdg -f sliced rootdg_${SC_MYNODEID}=${SC_ROOTDISK} >/dev/null 2>&1
	else
		# packages are from version earlier than 4.0, so...
		# Create rootdg.
		# If a rootdg has already been created, then skip this step.
		vxdg list rootdg > /dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			# Create rootdg.	
			vxdg init rootdg
			if [[ $? -ne 0 ]]; then
				# Failure in creating the rootdg for the first time.
				printf "$(gettext '%s:  Failed to create %s using \"%s\".')\n" ${PROG} "rootdg" "vxdg init root" | logerr
				return 1
			else
				# Success in creating the rootdg for the first time.
				printf "$(gettext 'Created the rootdg...')\n" | logmsg
			fi
		else
			# Use the existing rootdg
			printf "$(gettext '%s:  rootdg already exists so %s will attempt to utilize it.')\n" ${PROG} ${PROG} | logmsg
		fi

		# Add the Solaris root disk to rootdg
		# If the rootdisk is already part of rootdg, then skip this step.
		ctd="$(
			LANG=C; export LANG
			vxdctl list | awk '/${SC_ROOTDISK}/ {
				if ($2 == "${SC_ROOTDISK}")
					 print $2
			}'
		)"
		if [[ -z "${ctd}" ]]; then
			# Add the rootdisk to the rootdg.
			vxdctl add disk ${SC_ROOTDISK} type=sliced
			if [[ $? -ne 0 ]]; then
				# Failure in adding rootdisk to rootdg
				printf "$(gettext '%s:  Failed to add root disk \"%s\" to %s.')\n" ${PROG} ${SC_ROOTDISK} "rootdg" | logerr
				return 1
			else
				# Success in adding rootdisk to rootdg
				printf "$(gettext 'Added the rootdisk to the rootdg...')\n" | logmsg
			fi
		else
			# The rootdisk has already been added to the rootdg.
			printf "$(gettext '%s:  The rootdisk \"%s\" already has been added to the rootdg so %s will attempt to utilize it.')\n" ${PROG} ${SC_ROOTDISK} ${PROG} | logmsg 
		fi

		# Run vxencap
		# Since multiple runs of vxencap are not harmful, there is no need to
		# check anything prior to its execution. 
		vxencap rootdisk_${SC_MYNODEID}=${SC_ROOTDISK} >/dev/null 2>&1
	fi

	# check for vxencap success or failure
	if [[ $? -ne 0 ]]; then
		# Failure in vxencap
		printf "$(gettext '%s:  \"%s\" failed for root disk.')\n" ${PROG} "vxencap" | logerr
		return 1
	else
		# Success in vxencap
		printf "$(gettext 'The setup to encapsulate rootdisk is complete...')\n" | logmsg
	fi
  
	# Set the re-boot flag
	((SC_REBOOT_REQUIRED+=1))

	return 0
}

#####################################################
#
# scvx_is_vfstab_update_done
#
#	Checks to see if the vfstab updates are completed.
#
#	Return values:
#
#		zero		vfstab has the necessary updates
#		non-zero	vfstab does not have the necessary updates
#
#####################################################
scvx_is_vfstab_update_done()
{
	typeset globaldevmp=/global/.devices/node@${SC_MYNODEID}

	integer found

	typeset special
	typeset fsckdev
	typeset mountp
	typeset remains

	# Get the two devices
	let found=0
	while read special fsckdev mountp remains
        do
                case ${special} in
                '#'* | '')      # Ignore comments, empty lines
                                continue
                                ;;
                esac

                if [[ "${mountp}" = "${globaldevmp}" ]]; then
			let found=1
			break
                fi
        done < ${SC_VFSTAB}

	# Did we find it?
	if [[ ${found} -eq 0 ]]; then
		return 1
	fi

	#
	# If either of these are still DID devices, then the updates have
	# not yet been completed.   By looking for DID devices, rather
	# than checking for the absence of regular Solaris device names,
	# we still get the same desired results even if VxVM has completely
	# finished the encapsulation process.
	#
	if [[ "${special}" == /dev/did/dsk/d*s[0-7] ]] ||
	    [[ "${fsckdev}" == /dev/did/rdsk/d*s[0-7] ]]; then
		return 1
	fi

	return 0
}

#####################################################
#
# update_vfstab()
#
#	Update /etc/vfstab to use /dev/{r}dsk/cXtXdX name for
#	/global/.devices instead of /dev/did/{r}dsk so VxVM
#	can recognize it is on the root disk.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
update_vfstab()
{
	typeset globaldevmp=/global/.devices/node@${SC_MYNODEID}

	integer i
	typeset foo

	typeset special
	typeset gspecial
	typeset fsckdev
	typeset gfsckdev
	typeset mountp
	typeset gmountp
	typeset remains
	typeset gremains
	typeset globals
	set -A globals

	typeset dev
	typeset rdev
	typeset disk
	typeset slice

	typeset new_dev
	typeset new_rdev

	typeset vfstabline

	scvx_is_vfstab_update_done
	if [[ $? -eq 0 ]]; then
		printf "$(gettext 'The %s file already includes the necessary updates.')\n" /etc/vfstab | logmsg
		return 0
	fi

	# Print message
	printf "$(gettext 'Updating %s entry in %s.')\n" /global/.devices /etc/vfstab | logmsg

	#
	# Get the two devices we need to change.
	# Also, stash the device names of all global devices other than
	# the one for "globaldevmp"
	#
	let i=0
	set -A globals
	gspecial=
	gfsckdev=
	while read special fsckdev mountp remains
        do
                case ${special} in
                '#'* | '')      # Ignore comments, empty lines
                                continue
                                ;;
                esac

                if [[ "${mountp}" = "${globaldevmp}" ]]; then
			if [[ -n "${gspecial}" ]]; then
				printf "$(gettext '%s:  More than one entry found in %s for %s.')\n" ${PROG} ${SC_VFSTAB} ${globaldevmp} | logerr
				return 1
			fi
			gspecial="${special}"
			gfsckdev="${fsckdev}"
			gmountp="${mountp}"
			gremains="${remains}"
			continue
                fi

		if [[ "${remains}" == *global* ]]; then
			foo=
			if [[ "${special}" == /dev/dsk/c*t*d*s[0-7] ]]; then
				foo=$(IFS=/ ; set -- ${special};  shift 3;  echo $1)
			elif [[ "${special}" == /dev/did/dsk/d*s[0-7] ]]; then
				foo=$(IFS=/ ; set -- ${special};  shift 4;  echo $1)
			elif [[ "${special}" == /dev/global/dsk/d*s[0-7] ]]; then
				foo=$(IFS=/ ; set -- ${special};  shift 4;  echo $1)
			fi
			if [[ -n "${foo}" ]]; then
				globals[i]=$(IFS=s ; set -- ${foo}; echo $1)
				((i += 1))
			fi
		fi
        done < ${SC_VFSTAB}

	# Make sure we found what we were looking for
	if [[ -z "${gspecial}" ]]; then
		printf "$(gettext '%s:  %s does not have an entry for %s.')\n" ${PROG} ${SC_VFSTAB} ${globaldevmp} | logerr
		return 1
	fi

	#
	# Make sure that these are not DID devices
	#
	# This should never fail, since the test for checking
	# whether or not this step is complete in
	# scvx_is_vfstab_update_done() is to see if
	# DID devices are still being used.
	#
	if [[ "${gspecial}" != /dev/did/dsk/d*s[0-7] ]] ||
	    [[ "${gfsckdev}" != /dev/did/rdsk/d*s[0-7] ]]; then
		printf "$(gettext '%s:  DID devices are still in use for %s.')\n" ${PROG} ${globaldevmp} | logerr
		return 1
	fi

	# Verify that the DID devices are the same
	dev=$(IFS=/ ; set -- ${gspecial};  shift 4;  echo $1)
	rdev=$(IFS=/ ; set -- ${gfsckdev};  shift 4;  echo $1)
	if [[ "${dev}" != "${rdev}" ]]; then
		printf "$(gettext '%s:  Unexpected device names for %s.')\n" ${PROG} ${globaldevmp} | logerr
		return 1
	fi

	# Get the disk and slice number
	disk=$(IFS=s ; set -- ${dev}; echo $1)
	slice=$(IFS=s ; set -- ${dev}; echo $2)

	#
	# Get the Solaris device names
	#
	# We have to echo the output from scdidadm to get rid of the
	# extra tab;  scdidadm ends each line with a tab!
	#
	new_rdev=$(echo $(scdidadm -l -o path ${disk}))
	if [[ $? -ne 0 ]] || [[ -z "${new_rdev}" ]]; then
		printf "$(gettext '%s:  DID device %s is unknown.')\n" ${PROG} ${disk} | logerr
		return 1
	fi
	if [[ ${new_rdev} != /dev/rdsk/c*t*d* ]];  then
		printf "$(gettext '%s:  %s is mounted on an unrecognized device.')\n" ${PROG} ${globaldevmp} | logerr
		return 1
	fi
	if [[ ${new_rdev} != *s[0-7] ]]; then
		new_rdev=${new_rdev}s${slice}
	fi
	new_dev=$(echo ${new_rdev} | sed 's#rdsk#dsk#')
	if [[ -z "${new_dev}" ]]; then
		printf "$(gettext '%s:  Unexpected sed(1) error.')\n" ${PROG} | logerr
		return 1
	fi

	#
	# Make sure that neither the Solaris device name for root nor the
	# DID name for root is used in any other global device name.
	#
	for foo in ${globals[*]}
	do
		if [[ /dev/dsk/${foo}s${slice} == ${new_dev} ]] ||
		    [[ ${foo} == ${disk} ]]; then
			printf "$(gettext '%s:  Unsupported global device (%s) mount from the root disk.')\n" ${PROG} ${foo} | logerr
			return 1
		fi
	done

	# Construct our new vfstab line
	vfstabline="${new_dev}	${new_rdev}	${gmountp}	${gremains}"

	# Edit vfstab, by commenting out the original line and adding the new 
	foo=$(echo ${gspecial} | sed 's#/#\\/#g')
	ed -s ${SC_VFSTAB} << EOF >/dev/null 2>&1
/^${foo}[ 	]/s/^/#/
\$a
${vfstabline}
.
w
q
EOF
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to update %s.')\n" ${PROG} ${SC_VFSTAB} | logerr
		return 1
	fi

	# Set the re-boot flag
	((SC_REBOOT_REQUIRED+=1))

	return 0
}

#####################################################
#
# scvx_is_reminor_done
#
#	Checks to see if reminoring has been completed.
#	Note that this is performed by the RC script.
#
#	Return values:
#
#		zero		reminoring is complete
#		non-zero	reminoring is not complete
#
#####################################################
scvx_is_reminor_done()
{
    #  if special devices directories haven't been created, then 
    #  reminoring can't have been done
    if [[ ! -d /dev/vx/rdsk ]]; then
            return 1
    fi

    if [[ ! -d /dev/vx/dsk ]]; then
            return 1
    fi

    #  Compute the reminored minor number
    node=`/usr/sbin/clinfo -n`
    new_minor=`/usr/bin/expr $node \* 50`
    new_major=$(scvx_get_vxio_major_number)

    if [[ ${new_major} -eq 0 ]]
    then
        return 1
    fi

    major_minor=`printf "%d,%3d" ${new_major} ${new_minor}`

    # See if the correct minor number has been set
    ls -l /dev/vx/dsk | grep "${major_minor}" > /dev/null
    if [[ $? -eq 1 ]]; then
          return 1
    fi

    # See if the correct minor number has been set
    ls -l /dev/vx/rdsk | grep "${major_minor}" > /dev/null
    return $?
}

#####################################################
#
# scvx_reboot()
#
#	This function attains its re-boot token, then
#	re-boots.
#
#	Return values:
#
#		zero		re-boot is not required
#		non-zero	failure
#
#####################################################
scvx_reboot()
{
	integer printflg=0
	integer status

	# First, let's see if we need to do a reboot
	if [[ ${SC_REBOOT_REQUIRED} -eq 0 ]]; then
		printf "$(gettext 'Skipping re-boot.\n')\n" | logmsg
		return 0
	fi

	# Get the boot token
	while true
	do
		scrconf -b
                case $? in
		0)			# We got the boot token
			break
			;;
			
		${SC_SCCONF_EBUSY})	# Somebody else is booting
			if [[ ${printflg} -eq 0 ]]; then
				printf "$(gettext 'Waiting for all nodes to re-join the cluster ...')\n" | logmsg
				((printflg+=1))
			fi
			sleep 5
			;;

		*)			# Error
			printf "$(gettext '%s:  Failed to get permission to re-boot.')\n" ${PROG} | logerr
			return 1
			;;
		esac
	done

	# Print message
	echo | logmsg
	printf "$(gettext 'This node will be re-booted in %d seconds.')\n" ${SC_ABORT_TIMEOUT} | logmsg
	printf "$(gettext 'Type %s to abort ')" "Ctrl-C" | logmsg

	# Give them a chance to hit Ctrl-C
	let i=0
	while [[ $i -lt ${SC_ABORT_TIMEOUT} ]]
	do
		echo ".\c" | logmsg
		sleep 1
		((i += 1))
	done
	echo | logmsg

	# Cleanup
	cleanup

	# Re-boot in non-cluster mode
	reboot -- -x

	# We are done
	exit 0
}

############################################################
# remove_vxio_entry()
#
#	Removes all vxio entry in the SC_NAME_TO_MAJOR file
#	file.  This vxio entry was automatically added
#	when the VRTSvxvm package was added.
#
#	Returns
#		zero		success
#		non-zero	failure
#
############################################################
remove_vxio_entry()
{
	# Simply remove any and all vxio entries.
	ed -s ${SC_NAME_TO_MAJOR} << EOF >/dev/null 2>&1
g/^vxio[ 	]/c
.
w
q
EOF
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to update %s.\n')\n" ${PROG} ${SC_NAME_TO_MAJOR} | logerr
		return 1
	fi

	return 0
}

#####################################################
# install_vxvm()
# 
#	Install the Veritas packages on the node and
#	determine a valid vxio major number to use.
#	A valid entry for the vxio driver must be 
#	the same across all nodes.  Of course, the
#	entry cannot be used by any other driver either.
#
#	If all the Veritas packages are already installed
#	and the vxio entry is defined in /etc/name_to_major
#	both before this function runs, then that vxio entry
#	will be kept and attempted to be used.  This allows
#	the user to circumvent the renumbering of vxio.
#	Otherwise, the Veritas packages are installed and 
#	a clusterwide vxio major number will be determined.
#
#	Return values:
#		zero		success
#		non-zero	failure
#
#####################################################
install_vxvm()
{
	integer return_code=0
	integer current_vxio

	# Install the VRTS packages
	install_pkgs

	if [[ $? -ne 0 ]]
	then
		return_code=1
	else
		# If a vxio entry exists on this node, then assume that the
		# user purposely set it to that value.  So we won't reset it.
		# However, we will check to make sure that it is valid.
		current_vxio=$(scvx_get_vxio_major_number)
		if [[ ${current_vxio} -ne 0 ]]
		then
			# A vxio is defined already so validate it
			# If there are error, then scvx_validate_driver()
			# will output the appropriate error message.
			scvx_validate_driver "vxio" ${current_vxio}
			if [[ $? -eq 0 ]]
			then
				printf "$(gettext 'Discovered and using the predefined vxio number %s...')\n" ${current_vxio} | logmsg
			else
				return 1
			fi
		else
			# Obtain the clusterwide vxio major number
			printf "$(gettext 'Obtaining the clusterwide vxio number...')\n" | logmsg
			# Pause here to let other nodes complete the package
			# installation and have the automatically selected 
			# vxio major number removed.
			sleep 20
			scvx_reset_vxio_major_num
			if [[ $? -ne 0 ]]
			then
				return_code=1
			fi
		fi
	fi
	return $return_code	
}

#####################################################
#
# scvx_validate_driver()
#
#	This function determines if the inputted major
#	number is a valid entry for the inputted driver.
#	The major number must match every other node's 
#	major number of that particular driver.  If any 
#	errors are encountered, then an error message
#	is outputted and logged.
#
#	Return values:
#		zero		major number is valid
#		non-zero	major number is invalid
#
#####################################################
scvx_validate_driver()
{
	typeset driver=$1
	integer cand_major=$2
	typeset node_public
	typeset node_private
	typeset driver_max_pair
	typeset major
	set -A public_names ${SC_NODES}
	integer index=0

	# Check to make sure that the the major number < SC_MAX_MAJOR_NUMBER

	if [[ ${cand_major} -gt ${SC_MAX_MAJOR_NUMBER} ]]
	then
		printf "$(gettext 'The major number %s exceeds the maximum major number allowed.')\n" ${cand_major} | logerr
		return 1
	fi

	# From every node, obtain a pair of integers separated by ','
	#	n,m
	# 		n = driver major number, if it exists
	#		m = maximum major number defined
	#	NOTE: if driver does not exist on that node, then n = -1.

	# Use private names for scrconf but use public names for error displays

	for node_private in ${SC_NODES_PRIVATE}
	do
		node_public=${public_names[index]}
		(( index += 1 ))

		driver_max_pair=$(scrconf -d ${driver} -N ${node_private} 2>&1)
		if [[ $? -ne 0 ]]
		then
			printf "$(gettext 'Cannot validate %s for %s.  Failed to obtain node information from %s.')\n" ${cand_major} ${driver} ${node_public} | logerr
			return 1
		fi
		# Check the candidate with the defined driver on another
		# node.
		major=$(echo ${driver_max_pair} | awk -F',' '{print $1}')	
		if [[ ${major} -ne -1 && ${major} -ne ${cand_major} ]]
		then
			# Invalid candidate
			printf "$(gettext 'The defined major number %s for %s does not match the major number %s found on %s.')\n" ${cand_major} ${driver} ${major} ${node_public} | logerr
			return 1
		fi
	done
}

#####################################################
#
# scvx_doit() [is_interactive]
#
#	is_interactive			interactive
#
#	This function performs the main function for
#	VxVM installation and encapsulation tasks.
#
#	If "is_interactive" is given, it means that
#	this is being run interactively;   it is okay
#	to prompt for information.   Otherwise,
#	prompting is not allowd.
#
#	Return values:
#
#		zero		success
#		non-zero	failure
#
#####################################################
scvx_doit()
{
	typeset is_interactive=${1}

	integer i
	typeset new_path

	# has scvxinstall already installed VxVM?
	if [[ -f ${SC_COMPLETE} ]]; then
                printf "$(gettext '%s:  %s has already been run to completion on this node.')\n" ${PROG} ${PROG} | logerr
		printf "$(gettext '%s:  The presence of \"%s\" indicates prior completion.')\n" ${PROG} ${SC_COMPLETE} | logerr

		echo | logerr
                printf "$(gettext '%s:  Removal of the completion file is considered risky.')\n" ${PROG} | logerr
                printf "$(gettext '%s:  Attempts to re-run this program could result in unrecoverable damage.')\n" ${PROG} | logerr

		return 1
	fi

	# If interactive, see if we need to encapsulate (set SC_PERFORM_ENCAP)
	if [[ -n "${is_interactive}" ]]; then
		scvx_get_action
	fi

	# Get location of packages, if packages are not yet installed
	scvx_get_location ${is_interactive} || return 1

	# Disable DMP, if it is not already disabled
	disable_DMP || return 1

	# Add the packages, and negotiate a clusterwide
	# vxio major number.
	install_vxvm || return 1

	# Installation is complete
	printf "$(gettext '%s installation is complete.')\n" "${SC_VXVM}" | logmsg

	# Start vxconfigd and install license, if necessary
	scvx_install_license ${is_interactive} || return 1

	# if we're not encapsulating the root disk, cleanup and then we're done
	if [[ "${SC_PERFORM_ENCAP}" != true ]]; then
		vxdg bootdg > /dev/null 2>&1
		if [[ $? -eq 0 ]]; then
			# 4.0 or later packages are installed so we can...
			# remove install-db file
			rm -f /etc/vx/reconfig.d/state.d/install-db
			# re-initialize the volboot file
			vxdctl init
			if [[ $? -ne 0 ]]; then
				# Failed to re-initialize volboot file
				printf "$(gettext '%s:  Unable to re-initialize %s.')\n" ${PROG} "volboot file" | logerr
				return 1
			fi
			# enable config daemon
			vxdctl enable
			if [[ $? -ne 0 ]]; then
				# Failed to enable config daemon
				printf "$(gettext '%s:  Unable to enable %s.')\n" ${PROG} "config daemon" | logerr
				return 1
			fi
		fi	
		if [[ ${SC_REBOOT_REQUIRED} -ne 0 ]]; then
			printf "\n$(gettext 'Please remember to re-boot this node before configuring VxVM.')\n\n\a" | logmsg
		fi
		return 0
	fi
	
	
	# Check requirements for encapsulation
	verify_encap_requirements || return 1

	# Starting the encapsulation process ...
	echo | logmsg
	printf "$(gettext 'The %s root disk encapsulation step will begin in %d seconds.')\n" "${SC_VXVM}" ${SC_ABORT_TIMEOUT} | logmsg
	printf "$(gettext 'Type %s to abort ')" "Ctrl-C" | logmsg

	# Give them a chance to hit Ctrl-C
	let i=0
	while [[ $i -lt ${SC_ABORT_TIMEOUT} ]]
	do
		echo ".\c" | logmsg
		sleep 1
		((i += 1))
	done
	echo | logmsg

	# Prepare for encapsulation, if necessary
	scvx_prepare_encapsulation || return 1
	
	# Update vfstab, if necessary
	update_vfstab || return 1

	# Arrange for the RC script to run
	echo ${SC_LOGFILE} > ${SC_RUNRC} || return 1

	# Reboot, when we get the token
	scvx_reboot || return 1

	# We should never get here
	return 0
}

#####################################################
#
# scvx_is_rootdisk_encapsulated()
#
#	This function checks to see if the rootdisk has
#	already been encapsulated.  (This prevents this
#	script from being run again and corrupting the
#	node installation.)
#
#	Return values:
#
#		zero		the rootdisk is currently encapsulated
#		non-zero	the rootdisk is NOT currently encapsulated.
#
#####################################################
scvx_is_rootdisk_encapsulated()
{
	typeset device
	# Has the rootdisk already been encapsulated?
	# The way to tell is to see if / is mounted from 
	# a Veritas volume (/dev/vx/...)
	# If it has, then there is nothing to be done.
	device=$(mount -V / | awk '/\/dev\/vx/ {print $(NF-1)}')
	if [[ -n $device ]]
	then
		return 0
	fi
	return 1
}

#####################################################
#
# Main
#
#####################################################

integer lindex
integer sflg=0
integer Hflg=0
integer iflg=0
integer eflg=0
integer Lflg=0
integer dflg=0

typeset is_interactive=
typeset c

typeset mode

umask 022

# Parse command line options
let lindex=0
set -A SC_LICENSES
while getopts :sieHL:d: c
do
	case ${c} in
	s)	# Show status
		if [[ -n "${mode}" ]]; then
			show_usage >&2
			cleanup 1
		fi
		((sflg += 1))
		mode=$c
		;;

	H)	# Show help
		if [[ -n "${mode}" ]]; then
			show_usage >&2
			cleanup 1
		fi
		((Hflg += 1))
		mode=$c
		;;

	i)	# Set install mode
		if [[ -n "${mode}" ]]; then
			show_usage >&2
			cleanup 1
		fi
		SC_PERFORM_ENCAP=false
		((iflg += 1))
		mode=$c
		;;

	e)	# Set encapsulate mode
		if [[ -n "${mode}" ]]; then
			show_usage >&2
			cleanup 1
		fi
		SC_PERFORM_ENCAP=true
		((eflg += 1))
		mode=$c
		;;

	L)	# License key
		((Lflg += 1))
		license="${OPTARG}"

		# Make sure none is "none"
		if [[ "$(echo ${license} | nawk '{ print tolower($1) }')" == none ]]; then
			license=none
			let lindex=0
			set -A SC_LICENSES
		fi

		# Add it to the array of licenses
		if [[ "${SC_LICENSES[0]}" != "none" ]]; then
			SC_LICENSES[lindex]="${license}"
			((lindex += 1))
		fi
		;;

	d)	# Path
		if [[ ${dflg} -gt 0 ]]; then
			show_usage >&2
			cleanup 1
		fi
		SC_PKG_PATH="${OPTARG}"
		;;

	*)	# Unknown option
		if [[ ${c} == \? ]]; then
			printf "$(gettext '%s:  Unknown option, -%c.')\n" ${PROG} ${OPTARG}
		fi
		show_usage >&2
		cleanup 1
		;;
	esac
done

# Make sure there are no extra arguments
shift $((OPTIND - 1))
if [[ $# -ne 0 ]]; then
	show_usage >&2
	cleanup 1
fi

# Default mode is interactive
if [[ -z "${mode}" ]]; then
	mode=interactive
	is_interactive=1
else
	is_interactive=
fi

# Make sure that -l and -d were only given with legal modes
if [[ ${Lflg} -gt 0 ]] || [[ ${dflg} -gt 0 ]]; then
	if [[ ${iflg} -eq 0 ]] &&
	    [[ ${eflg} -eq 0 ]] &&
	    [[ -z "${is_interactive}" ]]; then
		show_usage >&2
		cleanup 1
	fi
fi

# Do it
case ${mode} in

# Show status
s)
	# Make sure we are root
	verify_isroot || cleanup 1

	# Make sure we are in the cluster
	verify_ismember || cleanup 1

	# Initialize global variables
	scvx_init || cleanup 1

	# Show status
	show_status

	# Done
	cleanup 0
	;;

# Show help
H)
	# Just print the usage message to stdout
	show_usage

	# Done
	cleanup 0
	;;

# Everything else
i|e|interactive)

	# Make sure we are root
	verify_isroot || cleanup 1

	# Use a log file for this path
	openlog || cleanup 1

	# Make sure we are in the cluster
	verify_ismember || cleanup 1

	# Make sure that the rootdisk is not already encapsulated
	scvx_is_rootdisk_encapsulated && { printf "$(gettext 'The rootdisk has been encapsulated on this node.')\n" ; cleanup 1 ; }

	# Initialize global variables
	scvx_init || cleanup 1

	# Arrange for cleanup on SIGHUP or SIGINT
	trap 'cleanup 10' HUP INT 

	# Create the lock file
	scvx_setlock || cleanup 1

	# Make sure we start off with full cluster membership
	verify_fullmembership || cleanup 1

	# Make sure that quorum has been initialized
	verify_isquorum_init || cleanup 1

	# VxVM installation
	scvx_doit ${is_interactive} || cleanup 1

	# Done
	cleanup 0
	;;

# Internal error
*)
	# Print error
	printf "$(gettext '%s:  Internal error - unknown mode.')\n" ${PROG}

	# Done
	cleanup 1
esac

# We should never get here
exit 0
