#! /bin/ksh
#
# ident	"@(#)scinstall_common.ksh	1.97	06/03/27 SMI"
#
# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#####################################################
#
# setfile() filename
#
#	Set the file mode, ownership, and group of
#	the given read-only root "filename".
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
setfile()
{
	typeset -r filename=$1
	typeset mode=0444
	if [[ $# -ne 1 ]]; then
		if [[ $# -eq 2 ]]; then
		    mode=$2
		else
		    printf "$(gettext '%s:  Internal error - bad call to setfile()')\n" ${PROG} >&2
		    return 1
		fi
	fi

	# set the file mode, owner, group
	chmod ${mode} ${filename} || return 1
	chown root ${filename} || return 1
	chgrp sys  ${filename} || return 1

	return 0
}

#####################################################
#
# duplicate() [args ...]
#
#	args - a list of 0-n arguments
#
#	This function returns non-zero if any two
#	arguments in the list match.   That is,
#	if duplicate args are found.
#
#####################################################
duplicate()
{
	typeset arglist;  set -A arglist $*

	integer i=0
	integer j

	while [[ -n "${arglist[i]}" ]]
	do
		((j = i + 1))
		while [[ -n "${arglist[j]}" ]]
		do
			if [[ "${arglist[i]}" == "${arglist[j]}" ]]; then
				return 1
			fi
			((j += 1))
		done
		((i += 1))
	done

	return 0
}

#####################################################
#
# check_opts() given_opts legal_opts required_opts
#
#	given_opts	- comma separated list of given subopts
#	legal_opts	- comma separated list of legal subopts
#	required_opts	- comma separated list of required subopts
#
#	This function returns non-zero if there are "given_opts"
#	which are not in the "legal_opts" list, or if there
#	are "required_opts" which are not in the "given_opts" list.
#
#	Suboption checking is incomplete at this stage, since we cannot
#	check properties until the property lists are installed.
#	scrconf(1M) will do a more complete job, once we install it.
#	And, after the pkgadds, we go through our options and let
#	scrconf(1M) re-check for usage.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
check_opts()
{
	typeset given_opts;  set -A given_opts $(IFS=, ; echo $1)
	typeset legal_opts;  set -A legal_opts $(IFS=, ; echo $2)
	typeset required_opts;  set -A required_opts $(IFS=, ; echo $3)

	integer i
	integer j

	typeset buffer
	typeset opt

	if [[ $# -ne 3 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to check_opts()')\n" ${PROG} >&2
		return 1
	fi

	# strip values from the given opts
	let i=0
	buffer=
	while [[ -n "${given_opts[i]}" ]]
	do
		opt=$(expr "${given_opts[i]}" : '\(.*\)=.*')
		if [[ -z "${opt}" ]]; then
			opt=${given_opts[i]}
		fi
		buffer="${buffer} ${opt}"
		((i += 1))
	done
	set -A given_opts ${buffer}

	# Make sure that all of the given options are legal
	if [[ -n "${legal_opts}" ]]; then
		let i=0
		while [[ -n "${given_opts[i]}" ]]
		do
			let j=0
			while [[ -n "${legal_opts[j]}" ]]
			do
				if [[ "${given_opts[i]}" = "${legal_opts[j]}" ]]; then
					break
				fi
				((j += 1))
			done
			if [[ -z "${legal_opts[j]}" ]]; then
				return 1
			fi
			((i += 1))
		done
	fi

	# Make sure that all of the required options are given
	if [[ -z "${given_opts}" ]] && [[ -n "${required_opts}" ]]; then
		return 1
	fi

	let i=0
	while [[ -n "${required_opts[i]}" ]]
	do
		let j=0
		while [[ -n "${given_opts[j]}" ]]
		do
			if [[ "${required_opts[i]}" = "${given_opts[j]}" ]]; then
				break
			fi
			((j += 1))
		done
		if [[ -z "${given_opts[j]}" ]]; then
			return 1
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# check_optslist() given_optslist opt_type legal_opts required_opts
#
#	given_optslist	- full list of options & suboptions for given opt_type
#	opt_type	- option letter
#	legal_opts	- comma separated list of legal subopts
#	required_opts	- comma separated list of required subopts
#
#	This function returns non-zero if there are suboptions in
#	the"given_optslist" which are not in the "legal_opts" list,
#	or if there are "required_opts" which are not in the
#	"given_optslist" list.
#
#	Suboption checking is incomplete at this stage, and we cannot
#	check properties until the property lists are installed.
#	scrconf(1M) will do a more complete job, once we install it.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
check_optslist()
{
	typeset -r given_optslist="$1"
	typeset -r opt_type="$2"
	typeset -r legal_opts"$3"
	typeset -r required_opts="$4"

	typeset c

	if [[ $# -ne 4 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to check_optslist()')\n" ${PROG} >&2
		return 1
	fi

	set - ${given_optslist}
	OPTIND=1
	while getopts ${opt_type}: c 2>/dev/null
	do
		case ${c} in
		${opt_type})
			check_opts "${OPTARG}" "${legal_opts}" "${required_opts}" || return 1
			;;

		*)
			return 1
			;;
		esac
	done

	return 0
}

#####################################################
#
# check_cable_opts() "cable_opts" "installnode" "adapter_opts"
#
#	cable_opts	- comma separated list of cable options
#	installnode	- the name of the node being installed
#
#	This function returns non-zero if any of the cable options
#	are illegal.
#
#	The following things are checked:
#
#		- each -m option must have exactly two endpoints
#		- at least one nodename for an adapter in a pair of
#			adapter endpoints must be "NULL" or equal
#			to the name of this node
#		- each cable must connect this node to the cluster
#		- each adapter may be given only once
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
check_cable_opts()
{
	typeset -r cable_opts="$1"
	typeset -r installnode="$2"
	typeset -r adapter_opts="$3"

	typeset c
	typeset subopts
	typeset value
	typeset nodenames
	typeset foo
	typeset bar
	typeset adapter
	typeset adapters
	typeset myadapters
	typeset expanded_adapters
	typeset phys_adapters
	typeset expanded_adp
	typeset vlans
	typeset vlan_array
	typeset adp_array
	typeset adp_drv
	typeset adp_inst
	typeset vlanned_instance

	integer i
	integer j
	integer countme
	integer found
	integer adp_count

	# If no cable options, there is nothing to do
	if [[ -z "${cable_opts}" ]]; then
		return 0
	fi

	# Turn adapter_opts into list of adapters
	adapters="$(print_subopt_values "${adapter_opts}" "name")"
	vlans="$(print_optional_subopt_values "${adapter_opts}" "vlanid")"
	set -A adp_array ${adapters}
	set -A vlan_array ${vlans}

	let adp_count=0
	for adp in ${adapters}
	do
		((adp_count += 1))
	done

	let j=0
	expanded_adapters=
	phys_adapters=
	while [[ ${j} -lt ${adp_count} ]]
	do
		# Use the VLAN MULTIPLIER to derive the expanded (vlanid
		# incorporated) adapter names and the physical adapter names.
		adp=${adp_array[j]}
		adp_drv=$(expr ${adp} : '\([a-z0-9]*[a-z]\)')
		adp_inst=$(expr ${adp} : '[a-z0-9]*[a-z]\([0-9]*\)')
		if [[ ${adp_inst} -lt ${SC_VLAN_MULTIPLIER} ]]; then
			if [[ ${vlan_array[j]} -ne 0 ]]; then
				vlanned_instance=$(expr ${vlan_array[j]} \* ${SC_VLAN_MULTIPLIER} + ${adp_inst})
				expanded_adp=${adp_drv}${vlanned_instance}
				expanded_adapters="${expanded_adapters} ${expanded_adp}"
			else
				expanded_adapters="${expanded_adapters} ${adp}"
			fi
			phys_adapters="${phys_adapters} ${adp}"
		else
			expanded_adapters="${expanded_adapters} ${adp}"
			phys_instance=$(expr ${adp_inst} % ${SC_VLAN_MULTIPLIER} )
			phys_adp=${adp_drv}${phys_instance}
			phys_adapters="${phys_adapters} ${phys_adp}"
		fi
		((j += 1))
	done

	#
	# For each opt,
	#
	myadapters=
	OPTIND=1
	while getopts m: c ${cable_opts} 2>/dev/null
	do
		if [[ "${c}" != "m" ]]; then
			printf "$(gettext '%s:  Internal error in check_cable_opts()')\n" "${PROG}" >&2
			return 1
		fi

		# for each subopt ...
		set -A subopts $(IFS=, ; echo ${OPTARG})
		let i=0
		let countme=0
		while [[ -n "${subopts[i]}" ]]
		do
			# get value (endpoint=<value>)
			value=$(expr "${subopts[i]}" : 'endpoint=\(.*\)')
			if [[ -z "${value}" ]]; then
				printf "$(gettext '%s:  Unrecognized suboption given with -m')\n" "${PROG}" | logerr
				return 1
			fi

			# get nodename, if there is one ([nodename]:adapter)
			nodenames[i]="-"
			if [[ "${value}" = *:* ]]; then

				# Set the nodename portion
				nodenames[i]=$(expr "${value}" : '\(.*\)\:.*')
				if [[ -z "${nodenames[i]}" ]]; then
					nodenames[i]=${installnode}
				fi

				# Is it my adapter?
				if [[ "${nodenames[i]}" = "${installnode}" ]]; then
					((countme += 1))
					adapter=$(expr "${value}" : '.*:\(.*\)')
					if [[ "${adapter}" != *@* ]]; then
						adapter="${adapter}@0"
					fi
					myadapters="${myadapters} ${adapter}"
					bar=$(expr "${adapter}" : '\([^@]*\).*')
					let found=0
					for foo in ${adapters}
					do
						if [[ "${foo}" = "${bar}" ]]; then
							let found=1
							break
						fi
					done
					if [[ ${found} -eq 0 ]]; then
						for foo in ${expanded_adapters}
						do
							if [[ "${foo}" = "${bar}" ]]; then
								let found=1
								break
							fi
						done
					fi
					if [[ ${found} -eq 0 ]]; then
						for foo in ${phys_adapters}
						do
							if [[ "${foo}" = "${bar}" ]]; then
								let found=1
								break
							fi
						done
					fi
					if [[ ${found} -ne 1 ]]; then
						printf "$(gettext '%s:  Cable endpoint must match one of the adapters')\n" "${PROG}" | logerr
						return 1
					fi
				fi
			fi
			((i += 1))
		done

		# there must be two endpoints
		if [[ ${i} -ne 2 ]]; then
			printf "$(gettext '%s:  There must be two endpoints per cable')\n" "${PROG}" | logerr
			return 1
		fi

		# One of the nodenames must be me
		if [[ ${countme} -lt 1 ]]; then
			printf "$(gettext '%s:  At least one end of each cable must attach to this node')\n" "${PROG}" | logerr
			return 1
		fi

		# And, only one
		if [[ ${countme} -gt 1 ]]; then
			printf "$(gettext '%s:  At least one end of each cable must attach to this node')\n" "${PROG}" | logerr
			return 1
		fi
	done

	# Make sure that there are no duplicate adapters
	duplicate ${myadapters}
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  One or more of the adapters for this node is cabled more than once')\n" "${PROG}" | logerr
		return 1
	fi

	# Do the same check with the entire list of physical adapters
	duplicate ${phys_adapters}
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  One or more physical adapters are used more than once for this node')\n" "${PROG}" | logerr
		return 1
	fi
	return 0
}

#####################################################
#
# print_subopt_values() suboptlist subopt
#
#	suboptlist		comma separated list of "suboptions"
#	subopt			name of the suboption
#
#	Print the values of the given "subopt", as found
#	in the "suboptlist".  If option letters are included,
#	they are skipped
#
#	This function always returns zero.
#
#####################################################
print_subopt_values()
{
	typeset -r suboptlist="$(IFS=, ; echo $1)"
	typeset -r subopt=$2

	typeset foo

	# Check arg
	if [[ $# -ne 2 ]]; then
		return 0
	fi

	for foo in ${suboptlist}
	do
		# Strip out any option letters
		if [[ "${foo}" = -* ]]; then
			continue
		fi

		if [[ "${foo}" = *=* ]]; then
			foo="$(expr "${foo}" : ${subopt}'=\(.*\)')"
		fi
		if [[ -n "${foo}" ]]; then
			echo ${foo}
		fi
	done

	return 0
}

#####################################################
#
# print_optional_subopt_values() suboptlist subopt
#
#	suboptlist		comma separated list of "suboptions"
#	subopt			name of the suboption
#
#	Print the values of the given "subopt", as found
#	in the "suboptlist".  This routine should be used only
#	when option letters are included, like 
#	"-A <adp_opts> -A <adp_opts>" #	etc.
#
#	The only difference with print_subopt_values() is that
#	in case the suboption is optional and thus missing, a
#	"0" is used to fill in the value.
#
#	This function always returns zero.
#
#####################################################
print_optional_subopt_values()
{
	typeset -r suboptlist="$(IFS=, ; echo $1)"
	typeset -r subopt=$2

	integer new_option

	typeset foo

	# Check arg
	if [[ $# -ne 2 ]]; then
		return 0
	fi

	let new_option=0
	for foo in ${suboptlist}
	do
		# Strip out option letters
		if [[ "${foo}" = -* ]]; then
			if [[ ${new_option} -eq 1 ]]; then
				echo 0
			fi
			new_option=1

			continue
		fi

		if [[ "${foo}" = *=* ]]; then
			foo="$(expr "${foo}" : ${subopt}'=\(.*\)')"
		fi
		if [[ -n "${foo}" ]]; then
			echo ${foo}
			new_option=0
		fi
	done
	if [[ ${new_option} -eq 1 ]]; then
                echo 0
        fi

	return 0
}

#####################################################
#
# new_separator <new_separator> "<space_separated_list>"
#
#	Replace the spaces in the "space_separated_list" with
#	the "new_separator", and print the results.
#
#	This function always returns zero.
#
#####################################################
new_separator()
{
	typeset new_separator=${1};  shift
	typeset space_separated_list="${*}"

	typeset item
	typeset newlist=

	for item in ${space_separated_list}
	do
		if [[ -z "${newlist}" ]]; then
			newlist=${item}
		else
			newlist=${newlist}:${item}
		fi
	done

	echo ${newlist}
}

#####################################################
#
# setlock()
#
#	Check for the "lockfile".  If it already
#	exists, print an error, and return with non-zero.
#	Otherwise, create a lockfile with our pid inside.
#
#####################################################
setlock()
{
	# If we already set our lock, return
	if [[ ${SC_LOCK_ISSET} -eq 1 ]]; then
		return 0
	fi

	# Check for lockfile
	if [[ -f ${lockfile} ]]; then
		printf "$(gettext '%s:  Another instance of this program may already be running')\n" "${PROG}" >&2
		printf "$(gettext '%s:  If not, remove %s and try again')\n" "${PROG}" "${lockfile}" >&2
		return 1
	fi

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

	# Set the lock flag
	SC_LOCK_ISSET=1

	return 0
}

#####################################################
#
# is_os_okay
#
#	Return ${SC_FALSE} if the OS cannot support cluster software
#	Return ${SC_TRUE} if the OS might have support for cluster software
#
#####################################################
is_os_okay()
{
	if [[ -x /usr/sbin/clinfo ]]; then
		return ${SC_TRUE}
	fi

	return ${SC_FALSE}
}

#####################################################
#
# is_cluster_member()
#
#	Return ${SC_FALSE} if this node is NOT a member of the cluster
#	Return ${SC_TRUE} if this node is a member of the cluster
#
#####################################################
is_cluster_member()
{
	if [[ -x /usr/sbin/clinfo ]]; then
		/usr/sbin/clinfo > /dev/null 2>&1
		if [[ $? -eq 0 ]]; then
			return ${SC_TRUE}
		fi
	fi

	return ${SC_FALSE}
}

#####################################################
#
# verify_d_option() [cdimagebasedir]
#
#	If "cdimagebasedir" is not set or does not include .cdtoc,
#	print an error message, and return non-zero.
#
#####################################################
verify_d_option()
{
	typeset -r cdimagebasedir=$1

	typeset foo
	integer found

	if [[ -z "${cdimagebasedir}" ]]; then
		printf "$(gettext '%s:  Please use -d to specify the location of the CDROM')\n" "${PROG}" | logerr
		return 1
	fi

	# Search for .cdtoc in cdimagebasedir or its children
	let found=0
	for foo in ${cdimagebasedir} ${cdimagebasedir}/*
	do
		if [[ -f "${foo}/${SC_CDTOC}" ]]; then
			let found=1
			break
		fi
	done
	if [[ ${found} -eq 0 ]]; then
		printf "$(gettext '%s:  Cannot find the \"%s\" file')\n" "${PROG}" "${SC_CDTOC}" | logerr
		printf "$(gettext '%s:  \"%s\" does not appear to be a %s CD or CD image')\n" "${PROG}" "${cdimagebasedir}" "Sun Cluster" | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# verify_G_option() [global]
#
#	verify -G option.   If there is a failure, print message
#	and return non-zero.
#
#	If global_fs is not given, the default is assumed.
#
#####################################################
verify_G_option()
{
	typeset -r global=$1

	# Print message ...
	printf "\n" | logmsg
	printf "$(gettext 'Checking device to use for global devices file system ... ')" | logmsg

	# Check device or filesystem
	if [[ -n "${global}" ]] && [[ -c "${global}" ]];  then
		is_globalcspecial_okay ${global} || return 1
	else
		is_globalfs_okay "${global}" || return 1
	fi
		
	# ... done message
	printf "%s\n" ${SC_DONE} | logmsg

	return 0
}

#####################################################
#
# admin()
#
#	Creates "adminfile" for pkgadd and pkgrm.
#
#	Returns non-zero on error.
#
#####################################################
admin()
{
	# Create file
	cat >${adminfile} <<END
basedir=default
mail=
runlevel=quit
conflict=nocheck
setuid=nocheck
action=nocheck
partial=quit
instance=unique
idepend=quit
rdepend=nocheck
space=quit
END

	return $?
}

#####################################################
#
# get_oe_from_os() os
#
#	os			SunOS version as returned by uname -r
#
#	Search the SC_NAMES_OS_VERS array for the first matching
#	"sunos" and return the mapped Solaris name from SC_NAMES_OE_VERS.
#	Refer to comments in the definitions found in scinstall.ksh for
#	more information.
#
#	When a match is found, print it.
#
#	This function always returns zero.
#
#####################################################
get_oe_from_os()
{
	typeset -r os=$1

	integer i

	# Check arg
	if [[ -z "${os}" ]]; then
		return 0
	fi

	# Search for a match
	let i=0
	while [[ -n "${SC_NAMES_OS_VERS[i]}" ]]
	do
		if [[ "${SC_NAMES_OS_VERS[i]}" == "${os}" ]]; then
			echo ${SC_NAMES_OE_VERS[i]}
			return 0
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# get_oe_alt_from_os() os
#
#	os			SunOS version as returned by uname -r
#
#	Search the SC_NAMES_OS_VERS array for the first matching
#	"sunos" and return the mapped Solaris name from SC_NAMES_OE_ALT_VERS.
#	Refer to comments in the definitions found in scinstall.ksh for
#	more information.
#
#	When a match is found, print it.
#
#	This function always returns zero.
#
#####################################################
get_oe_alt_from_os()
{
	typeset -r os=$1

	integer i

	# Check arg
	if [[ -z "${os}" ]]; then
		return 0
	fi

	# Search for a match
	let i=0
	while [[ -n "${SC_NAMES_OS_VERS[i]}" ]]
	do
		if [[ "${SC_NAMES_OS_VERS[i]}" == "${os}" ]]; then
			echo ${SC_NAMES_OE_ALT_VERS[i]}
			return 0
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# get_os_from_oe() oe
#
#	oe			Operating environ, as found in SC_NAMES_OE_VERS
#
#	Search the SC_NAMES_OS_VERS array for the first matching
#	"oe" and return the mapped SunOS name from SC_NAMES_OS_VERS.
#	Refer to comments in the definitions found in scinstall.ksh for
#	more information.
#
#	When a match is found, print it.
#
#	This function always returns zero.
#
#####################################################
get_os_from_oe()
{
	typeset -r oe=$1

	integer i

	# Check arg
	if [[ -z "${oe}" ]]; then
		return 0
	fi

	# Search for a match
	let i=0
	while [[ -n "${SC_NAMES_OE_VERS[i]}" ]]
	do
		if [[ "${SC_NAMES_OE_VERS[i]}" == "${oe}" ]] ||
		     [[ "${SC_NAMES_OE_ALT_VERS[i]}" == "${oe}" ]]; then
			echo ${SC_NAMES_OS_VERS[i]}
			return 0
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# get_name_from_oe() oe
#
#	oe			Operating environ, as found in SC_NAMES_OE_VERS
#
#	Search the SC_NAMES_OS_VERS array for the first matching
#	"oe" and return the mapped Solaris name from SC_NAMES_SOLARIS.
#	Refer to comments in the definitions found in scinstall.ksh for
#	more information.
#
#	When a match is found, print it.
#
#	This function always returns zero.
#
#####################################################
get_name_from_oe()
{
	typeset -r oe=$1

	integer i

	# Check arg
	if [[ -z "${oe}" ]]; then
		return 0
	fi

	# Search for a match
	let i=0
	while [[ -n "${SC_NAMES_OE_VERS[i]}" ]]
	do
		if [[ "${SC_NAMES_OE_VERS[i]}" == "${oe}" ]]; then
			echo ${SC_NAMES_SOLARIS[i]}
			return 0
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# get_oe_from_path() path topdir
#
#	path			directory path
#	topdir			expected top level directory
#
#	Look for the <operating_environment> component in the
#	given "path".   The top level directory, or "topdir"
#	is expcted to be either "Tools" or "Packages";   if the
#	top level directory does not match the given "topdir",
#	this function returns immediately.  Below "Tools|Packages",
#	the next directory up should be the <operating_environment>
#	directory, if it exists.   We assume that there is no
#	<isa> directory on the CD.   Refer to PSARC/1999/518 and
#	PSARC/1996/133 for more information regarding the
#	CD layout.
#
#	If the directory below "Tools|Packages" matches one of the OEs in
#	SC_NAMES_OE_VERS, print the name of that oe.
#
#	This function always returns zero.
#
#####################################################
get_oe_from_path()
{
	typeset -r path=$1
	typeset -r topdir=$2

	typeset shortpath
	typeset oe
	integer i

	# Check arg
	if [[ -z "${path}" ]] || [[ -z "${topdir}" ]]; then
		return 0
	fi

	# Make sure it is the expected top level directory
	if [[ "${path##*/}" != "${topdir}" ]]; then
		return 0
	fi
	shortpath=${path%/*}
	oe=${shortpath##*/}

	# Search for a match
	let i=0
	while [[ -n "${SC_NAMES_OE_VERS[i]}" ]]
	do
		if [[ "${SC_NAMES_OE_VERS[i]}" == "${oe}" ]] ||
		     [[ "${SC_NAMES_OE_ALT_VERS[i]}" == "${oe}" ]]; then
			echo ${oe}
			return 0
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# get_oe_from_scinstalldir() scinstalldir
#
#	scinstalldir		directory in which scinstall lives
#
#	Look for the <operating_environment> component in the
#	given "scinstalldir".   If the top level directory is
#	not "Tools", we return immediately.  Below "Tools", the
#	next directory up should be the <operating_environment>
#	directory, if it exists.   We assume that there is no
#	<isa> directory on the CD.   Refer to PSARC/1999/518 and
#	PSARC/1996/133 for more information regarding the
#	CD layout.
#
#	If the directory below "Tools" matches one of the OEs in
#	SC_NAMES_OE_VERS, print the name of that oe.
#
#	This function always returns zero.
#
#####################################################
get_oe_from_scinstalldir()
{
	typeset -r scinstalldir=$1

	get_oe_from_path ${scinstalldir} ${SC_TOOLS}

	return 0
}

#####################################################
#
# get_oe_from_proddir() proddir
#
#	proddir			PRODDIR directory path from .cdtoc
#
#	Look for the <operating_environment> component in the
#	given "proddir".   If the top level directory is
#	not "Packages", we return immediately.  Below "Packages", the
#	next directory up should be the <operating_environment>
#	directory, if it exists.   We assume that there is no
#	<isa> directory on the CD.   Refer to PSARC/1999/518 and
#	PSARC/1996/133 for more information regarding the
#	CD layout.
#
#	If the directory below "Packages" matches one of the OEs in
#	SC_NAMES_OE_VERS, print the name of that oe.
#
#	This function always returns zero.
#
#####################################################
get_oe_from_proddir()
{
	typeset -r proddir=$1

	get_oe_from_path ${proddir} ${SC_PACKAGES}

	return 0
}

#####################################################
#
# check_oe_os_match() oe os
#
#	If the "oe" does not match the "os", return non-zero
#
#####################################################
check_oe_os_match()
{
	typeset -r oe=$1
	typeset -r os=$2

	if [[ -z "${oe}" ]] || [[ -z "${os}" ]]; then
		return 1
	fi

	if [[ "$(get_os_from_oe ${oe})" != "${os}" ]]; then
		return 1
	fi

	return 0
}

#####################################################
#
# getproduct() cdtoc productname cluster flag
#
#	cdtoc			name of cdtoc(4) file
#	productname		product name (PRODNAME)
#	cluster			name of software cluster or metacluster
#	flag			set to "dir" or "rel"
#
#	Search the given "cdtoc" file for a "productname" with
#	a matching software "cluster".  If "productname" is NULL,
#	all products listed in the "cdtoc" file are considered in
#	order.
#
#	When a match is found, the absolute path to the product
#	directory is returned (if "dir");   or, the release is
#	is returned (if "rel").
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
getproduct()
{
	typeset -r cdtoc=$1
	typeset -r productname="$2"
	typeset -r cluster=$3
	typeset -r flag=$4

	typeset cdtocdir
	typeset clustertoc
	typeset pname
	typeset pvers
	typeset pdir
	typeset line
	typeset value

	# Check arg
	if [[ $# -ne 4 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to getproduct()')\n" ${PROG} >&2
		return 1
	fi

	# Make sure we have and absolute path for "cdtoc"
	if [[ "${cdtoc}" != /* ]]; then
		printf "$(gettext '%s:  Internal error - bad cdtoc in getproduct()')\n" "${PROG}" >&2
		return 1
	fi

	# Make sure we have a "cdtoc" file
	if [[ ! -f "${cdtoc}" ]]; then
		printf "$(gettext '%s:  Cannot find \"%s\"')\n" "${PROG}" "${cdtoc}" | logerr
		return 1
	fi

	# Get the base directory
	cdtocdir=${cdtoc%/*}

	# Read the file, looking for a matching product
	pname=
	pvers=
	while read line
	do
		case "${line}" in
		PRODNAME=*)
			# reset
			pname=
			pvers=
			pdir=

			# PRODNAME=<value>
			value=$(expr "${line}" : 'PRODNAME=\(.*\)')

			# set pname if no productname or productname match
			if [[ -z "${productname}" ]] ||
			    [[ "${productname}" = "${value}" ]]; then
				pname="${value}"
			fi
			;;

		PRODVERS=*)
			# reset
			pvers=

			# if pname is not set, skip it
			if [[ -z "${pname}" ]]; then
				continue
			fi

			# PRODVERS=<value>
			pvers=$(expr "${line}" : 'PRODVERS=\(.*\)')

			# if pdir is set, and flag is "rel", we are done
			if [[ -n "${pdir}" ]] && [[ "${flag}" = "rel" ]]; then
				echo ${pvers}
				return 0
			fi
			;;

		PRODDIR=*)
			# reset
			pdir=

			# if pname is not set, skip it
			if [[ -z "${pname}" ]]; then
				continue
			fi

			# PRODDIR=<value>
			value=$(expr "${line}" : 'PRODDIR=\(.*\)')

			#
			# If the PRODDIR value includes a known OE,
			# see if it is matches the OE for our OS;  if
			# not, skip it.
			#
			oe=$(get_oe_from_proddir ${value})

			if [[ -n "${oe}" ]] && [[ -n "${SC_OE_VERSION}" ]] &&
			    [[ "${oe}" != "${SC_OE_VERSION}" ]] &&
			      [[ "${oe}" != "${SC_OE_ALT_VERSION}" ]]; then
				pname=
				continue
			fi

			# try to find the software cluster
			if [[ "${cdtocdir}" = "${SC_DOT_DIR}" ]]; then
				clustertoc="${SC_DOT_CLUSTERTOC}"
			else
				clustertoc="${cdtocdir}/${value}/${SC_CLUSTERTOC}"
			fi
			egrep '^CLUSTER='${cluster}'[ 	]*$|^METACLUSTER='${cluster}'[	 ]*$' ${clustertoc} >/dev/null 2>&1
			if [[ $? -eq 0 ]]; then
				pdir=${value}

				# if flag is "dir", we are done
				if [[ "${flag}" = "dir" ]]; then
					echo ${cdtocdir}/${pdir}
					return 0
				fi

				# if pvers is set, and flag is "rel", done
				if [[ -n "${pvers}" ]] && [[ "${flag}" = "rel" ]]; then
					echo ${pvers}
					return 0
				fi
			fi
			;;
		esac

	done < ${cdtoc}

	# not found
	printf "$(gettext '%s:  Cannot find \"%s\" in the %s file(s) on this CD')\n" "${PROG}" "${cluster}" ".clustertoc" | logerr
        if [[ ${cluster} == "SUNWCscdab" ]]; then
                printf "%s:  $(gettext 'Skipping package \"%s\"')\n" ${PROG} ${cluster} |logerr
        fi
	return 1
}

#####################################################
#
# print_clustertoc() clustertocfile cluster flag [maxdepth]
#
#	clustertofile 		name of clustertoc(4) file
#	cluster			name of sofware cluster or metacluster
#	flag			"packages" or "clusters" or "description"
#	maxdepth		maximum number of recursive calls
#
#	If the flag is set to "packages", list the the names of the
#	packages for the given software "cluster" defined in the given
#	"clustertocfile".  The package names are not listed in any
#	particular order, and any given	package may actually be listed
#	more than once.
#
#       If the flag is set to "clusters", list the the names of the
#       clusters embedded in the given software "cluster" as defined
#       in the given "clustertocfile".
#
#	If the flag is set to "description", print the description of
#	the software "cluster".
#
#	The format of the "clustertocfile" is described on the
#	clustertoc(4) man page.
#
#	This function may call itself recursively.   If "maxdepth" is
#	given, an error will be returned when it is equal to zero.
#	If it is not set, ${DEFAULT_MAXDEPTH} is used.
#
#	Return:
#		zero		Success
#		1		The "cluster" name is not found
#		> 1		Other failure
#
#####################################################
print_clustertoc()
{
	integer -r DEFAULT_MAXDEPTH=10

	integer -r STATE_INIT=1
	integer -r STATE_PROCESSING=2
	integer -r STATE_ENDED=3

	typeset -r clustertocfile=$1
	typeset -r cluster=$2
	typeset -r flag=$3
	typeset -r smaxdepth=$4
	integer maxdepth=${smaxdepth:-${DEFAULT_MAXDEPTH}}

	typeset state=${STATE_INIT}
	typeset line
	typeset pkglist

	#
	# METACLUSTER=<value>
	# CLUSTER=<value>
	# SUNW_CSRMEMBER=<thething>
	# SUNW_CSRMBRIFF=(<thetest> <thearg>)<thething>
	#
	typeset value
	typeset thething
	typeset thetest
	typeset thearg

	if [[ $# -ne 3 && $# -ne 4 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to print_clustertoc()')\n" ${PROG} >&2
		return 2
	fi

	if [[ ${maxdepth} -eq 0 ]]; then
		printf "$(gettext '%s:  Bad .clustertoc file (\"%s\") - too deep')\n" ${PROG} ${clustertocfile} | logerr
		return 2
	fi

	if [[ ! -r ${clustertocfile} ]]; then
		printf "$(gettext '%s:  Cannot open \"%s\"')\n" ${PROG} ${clustertocfile} | logerr
		return 2
	fi

	#
	# Read the file, first searching for our "CLUSTER".
	#
	while read line
	do
		# Reset the metacluster/cluster/pkg name
		thething=

		case "${line}" in

		#
		# Look for our "METACLUSTER" or "CLUSTER" record.
		#
		METACLUSTER=* | CLUSTER=*)
			# Make sure the state is correct
			if [[ ${state} -eq ${STATE_PROCESSING} ]]; then
				printf "$(gettext '%s:  Bad .clustertoc file (\"%s\") - CLUSTER within CLUSTER')\n" ${PROG} ${clustertocfile} | logerr
				return 2
			fi

			# METACLUSTER=<value>
			# CLUSTER=<value>
			value=$(expr "${line}" : 'CLUSTER=\(.*\)')
			if [[ -z "${value}" ]]; then
				value=$(expr "${line}" : 'METACLUSTER=\(.*\)')
			fi

			# Make sure there is a value
			if [[ -z "${value}" ]]; then
				printf "$(gettext '%s:  Bad .clustertoc file (\"%s\") - CLUSTER error')\n" ${PROG} ${clustertocfile} | logerr
				return 2
			fi

			# If this is a match, change the state
			if [[ "${value}" = "${cluster}" ]]; then
				state=${STATE_PROCESSING}
			fi
			;;

		#
		# Process all SUNW_CSRMEMBER records
		#
		SUNW_CSRMEMBER=*)
			# Make sure we are supposed to be processing
			if [[ ${state} -ne ${STATE_PROCESSING} ]]; then
				continue
			fi

			# Looking for packages?
			if [[ "${flag}" != "packages" ]]&& \
                           [[ "${flag}" != "clusters" ]]; then
				continue
			fi

			# SUNW_CSRMEMBER=<thething>
			thething=$(expr "${line}" : 'SUNW_CSRMEMBER=\(.*\)')

			# Make sure there is a value
			if [[ -z "${thething}" ]]; then
				printf "$(gettext '%s:  Bad .clustertoc file (\"%s\") - SUNW_CSRMEMBER error')\n" ${PROG} ${clustertocfile} | logerr
				return 2
			fi
			;;

		#
		# Process all SUNW_CSRMBRIFF records
		#
		SUNW_CSRMBRIFF=*)
			# Make sure we are supposed to be processing
			if [[ ${state} -ne ${STATE_PROCESSING} ]]; then
				continue
			fi

			# Looking for packages?
			if [[ "${flag}" != "packages" ]] && \
                           [[ "${flag}" != "clusters" ]]; then
				continue
			fi

			# SUNW_CSRMBRIFF=(<thetest> <thearg>)<thething>
			thetest=$(expr "${line}" : 'SUNW_CSRMBRIFF=(\(.*\)[ ].*')
			thearg=$(expr "${line}" : 'SUNW_CSRMBRIFF=(.*[ ][ ]*\(.*\)).*')
			thething=$(expr "${line}" : 'SUNW_CSRMBRIFF=(.*)\(.*\)')

			# Make sure all three are set
			if [[ -z "${thetest}" || \
			    -z "${thearg}" || \
			    -z "${thething}" ]]; then
				printf "$(gettext '%s:  Bad .clustertoc file (\"%s\") - SUNW_CSRMBRIFF error')\n" ${PROG} ${clustertocfile} | logerr
				return 1
			fi
	
			# We only support a "match" test
			case "${thetest}" in
			mach)
				# if not our machine type, skip it
				if [[ "${thearg}" != "${SC_ARCH}" ]]; then
					continue
				fi
				;;

			*)
				printf "$(gettext '%s:  Bad .clustertoc file (\"%s\") - unknown SUNW_CSRMBRIFF test')\n" ${PROG} ${clustertocfile} | logerr
				return 1
				;;
			esac

			;;
		#
		# Process the DESC
		#
		DESC=*)
			# Make sure we are supposed to be processing
			if [[ ${state} -ne ${STATE_PROCESSING} ]]; then
				continue
			fi

			# Looking for description?
			if [[ "${flag}" != "description" ]]; then
				continue
			fi

			# DESC=<ththing>
			thething=$(expr "${line}" : 'DESC=\(.*\)')

			echo ${thething}

			return 0
			;;

		#
		# Look for "END" record to match our "CLUSTER"
		#
		END)
			# If processing, change the state
			if [[ ${state} = ${STATE_PROCESSING} ]]; then
				state=${STATE_ENDED}
			fi
			;;

		esac

		# If a membership record, look for the pkg or cluster
		if [[ -n "${thething}" ]]; then
			pkglist=$(print_clustertoc ${clustertocfile} ${thething} "packages" $((maxdepth - 1)))
			case $? in
			0)	# found cluster - list packages within cluster
				if [[ "${flag}" != "clusters" ]]; then
					echo ${pkglist}
				else
					echo ${thething}
				fi
				;;

			1)	# not a cluster - list itself as a package
				if [[ "${flag}" != "clusters" ]]; then
					echo ${thething}
				fi
				;;

			2)	# error
				return 2
				;;

			esac
		fi

		# Done?
		if [[ ${state} -eq ${STATE_ENDED} ]]; then
			break;
		fi

	done < ${clustertocfile}

	#
	# If still in INIT state, then we did not find CLUSTER entry,
	# so return 1.   This may or may not be considered an error by
	# the caller.   If we are still in the PROCESSING state, then
	# we never found a matching "END" to the "CLUSTER" keyword
	# in the .clustertoc file.
	#
	if [[ ${state} -eq ${STATE_INIT} ]]; then
		return 1
	elif [[ ${state} -eq ${STATE_PROCESSING} ]]; then
		printf "$(gettext '%s:  Bad .clustertoc file (\"%s\") - no END to CLUSTER')\n" ${PROG} ${clustertocfile} | logerr

	fi

	return 0
}

#####################################################
#
# order_packages() orderfile "pkglist"
#
#	List the the names, in order, of the packages in the
#	"pkglist" found in the given "orderfile".   Each package
#	is only listed once.
#
#	The format of the "orderfile" is described on the
#	order(4) man page.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
order_packages()
{
	typeset -r orderfile=$1
	typeset pkglist; set -A pkglist $2

	typeset pkg
	typeset foo
	integer i

	if [[ $# -ne 2 && $# -ne 3 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to order_packages()')\n" ${PROG} >&2
		return 2
	fi

	# read the order file, checking for pkgs in pkglist
	while read pkg foo
	do
		# Better be just one package per line
		if [[ -n "${foo}" ]]; then
			printf "$(gettext '%s:  Bad .order file (\"%s\") - bad line')\n" ${PROG} ${orderfile} | logerr
			return 1
		fi

		if [[ -z "${pkg}" ]]; then
			continue
		fi

		# if pkg is in pkglist, print it
		let i=0
		while [[ -n "${pkglist[i]}" ]]; do
			if [[ "${pkglist[i]}" = "${pkg}" ]]; then
				echo ${pkg}
				pkglist[i]="-"
				break
			fi
			((i += 1))
		done
	done < ${orderfile}

	# make sure we got everything from our package list
	let i=0
	while [[ -n "${pkglist[i]}" ]]; do
		if [[ "${pkglist[i]}" != "-" ]]; then
			printf "$(gettext '%s:  Bad .order file (\"%s\") - missing package(s)')\n" "${PROG}" "${orderfile}" | logerr
			return 1
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# rev_order_packages() "pkglist"
#
#	Reverse the list.
#
#####################################################
rev_order_packages()
{
	typeset pkglist; set -A pkglist $1
	integer i

	if [[ $# -ne 1 ]]; then
		return 0
	fi

	let i=$(set -- ${pkglist[*]};  echo $#)
	while [[ ${i} -ne 0 ]]
	do
		((i -= 1))
		echo ${pkglist[i]}
	done

	return 0
}

#####################################################
#
# find_cdimagebasedir() directory productname cluster
#
#	directory		place to begin search of cdtoc
#	productname		product name (PRODNAME)
#	cluster			name of software cluster or metacluster
#
#	Attempt to find the cdimagebasedir for the given "product"
#	and "release" among the directories given in "directory".
#	Upon success, print the new cdimagebasedir to stdout.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
find_cdimagebasedir()
{
	typeset -r directory=$1
	typeset -r productname=$2
	typeset -r cluster=$3

	typeset foo
	integer found_cdtoc

	# Check args
	if [[ $# -ne 3 ]] ||
	    [[ -z "${directory}" ]] ||
	    [[ ! -d "${directory}" ]]; then
		printf "$(gettext '%s:  Internal error - bad call to find_cdimagebasedir()')\n" ${PROG} >&2
		return 1
	fi

	# check each possible dir
	let found_cdtoc=0
	for foo in ${directory} ${directory}/*
	do
		if [[ -f "${foo}/${SC_CDTOC}" ]]; then
			((found_cdtoc += 1))
			getproduct ${foo}/${SC_CDTOC} "${productname}" "${cluster}" "dir" >/dev/null 2>&1
			if [[ $? -eq 0 ]]; then
				echo ${foo}
				return 0
			fi
		fi
	done

	# .cdtoc found, but can't find software cluster
	if [[ ${found_cdtoc} -eq 1 ]]; then
		printf "$(gettext '%s:  Cannot find \"%s\" in the %s file(s) on this CD')\n" "${PROG}" "${cluster}" ".clustertoc" | logerr
                if [[ ${cluster} == "SUNWCscdab" ]]; then
                        printf "%s:  $(gettext 'Skipping package \"%s\"')\n" ${PROG} ${cluster} |logerr
                fi

	# can't find the .cdtoc
	else
		printf "$(gettext '%s:  The %s file is missing from this CD')\n" "${PROG}" ".cdtoc" | logerr
	fi

	# something missing - return error
	return 1
}

#####################################################
#
# install_packages() productdir "pkglist" flag ["description"]
#
#	productdir		location of packages
#	"pkglist"		list of package names
#	flag			may be set to "framework" or NULL
#	"description"		description
#
#	Install the given list of packages.
#
#	Partially installed packages are removed, then re-installed.
#	Already installed packages are skipped.
#
#	If the flag is set to "framework", we accept an exit code of
#	10 from pkgadd.   Exit code 10 says reboot required.   And,
#	we do reboot on any "framework" install/upgrade.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
install_packages()
{
	typeset -r productdir=$1
	typeset pkglist;  set -A pkglist $2
	typeset -r flag=$3
	typeset -r description="$4"

	integer i
	integer j
	integer result
	typeset cmdstring
	typeset rootarg=
	typeset pstamp1
	typeset pstamp2
	typeset buffer
	typeset responsearg=
	typeset gflg=

	# Check args
	if [[ $# -ne 3 && $# -ne 4 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to install_packages()')\n" ${PROG} >&2
		return 1
	fi

	if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
		rootarg="-R ${SC_BASEDIR}"
	fi

	# introduce the set
	printf "\n" | logmsg

	# Print description, if there is one
	if [[ -n "${description}" ]]; then
		printf "** $(gettext 'Installing %s') **\n" "${description}" | logmsg
	fi

	if [[ "${SC_OS_VERSION}" = "5.10" ]]; then
		gflg="-G"
	fi

	# for each package
	let i=0
	while [[ -n "${pkglist[i]}" ]]
	do
		# Make sure we can find the package
		if [[ ! -d ${productdir}/${pkglist[i]} ]]; then
			printf "$(gettext '%s:  Unable to find \"%s\"')\n" "${PROG}" "${pkglist[i]}" | logerr
			return 1
		fi

		# See if the package is already installed
		pkginfo ${rootarg} ${pkglist[i]} >/dev/null 2>&1

		# It IS installed
		if [[ $? -eq 0 ]]; then

			# if only partially, back it out
			pkginfo -p ${rootarg} ${pkglist[i]} >/dev/null 2>&1
			if [[ $? -eq 0 ]]; then
				printf "$(gettext 'Removing partially installed package \"%s\"')\n" "${pkglist[i]}" | logmsg

				# Package remove
				trap 'cleanup 10' HUP INT
				cmdstring="pkgrm -n -a ${adminfile} ${rootarg} ${pkglist[i]}"
				printf "${cmdstring}" >>${install_log}
				${cmdstring} >${tmperrs} 2>&1
				let result=$?
				cat ${tmperrs} >>${install_log}
				if [[ ${result} -ne 0 ]]; then
					printf "$(gettext '%s:  Failed to remove \"%s\"')\n" "${PROG}" "${pkglist[i]}" | logerr
					return 1
				fi

			# otherwise, compare PSTAMPS
			else
				pstamp1=$(pkgparam -d ${productdir} ${pkglist[i]} PSTAMP)
				pstamp2=$(pkgparam ${rootarg} ${pkglist[i]} PSTAMP)
				if [[ -z "${pstamp1}" || -z "${pstamp2}" ]]; then
					printf "$(gettext '%s:  No PSTAMP for \"%s\"')\n" "${PROG}" "${pkglist[i]}" | logerr
					return 1
				fi

				# issue skip message ...
				printf "$(gettext 'Skipping \"%s\" - already installed')\n" "${pkglist[i]}" | logmsg

				# if PSTAMPs don't match, issue warning
				if [[ "${pstamp1}" != "${pstamp2}" ]]; then
					printf "$(gettext '%s:  WARNING:  but, the installed version is not the expected version!')\n" "${PROG}" | logmsg
				fi

				# Skip it
				((i += 1))
				continue
			fi
		fi

		#
		# Install the package
		#
		# The "<pkg>.....done." message is printed in the same
		# style as used by JumpStart.
		#
		let j=$(expr ${pkglist[i]} : .\*)
		((j = 12 - j))
		buffer=${pkglist[i]}
		while [[ ${j} -gt 0 ]]
		do
			buffer="${buffer}."
			((j -= 1))
		done
		printf "\t%s" "${buffer}"
		printf "\n\t%s\n" "${pkglist[i]}" >>${install_log}

		# Response file indicated?
		responsearg=

		# Check for provided response file
		if [[ -f "${productdir}/.${pkglist[i]}.response" ]]; then
			responsearg="-r ${productdir}/.${pkglist[i]}.response"
		else
			# not found: check for /tmp indicator & file
			if [[ -f "${productdir}/.${pkglist[i]}.response.tmp" 
			    && -f /tmp/.${pkglist[i]}.response ]]; then
				responsearg="-r /tmp/.${pkglist[i]}.response"
			fi
		fi


		# Package add
		trap 'cleanup 10' HUP INT
		cmdstring="pkgadd ${gflg} -S -d ${productdir} -n -a ${adminfile} ${rootarg} ${responsearg} ${pkglist[i]}"
		${PRINTDEBUG} ${cmdstring}
		printf "${cmdstring}" >>${install_log}
		${cmdstring} >${tmperrs} 2>&1
		let result=$?

		#
		# Package install scripts are supposed
		# to use exit codes 0 through 3 to indicate
		# success or failure.  In addition, 10 is supposed
		# to be added to the exit code if the system is to be
		# rebooted after all selected packages are installed.
		# Or, 20 should be added to the code if the system
		# needs to be rebooted immediately.
		#
		# So, we accept 10 as a successful exit code when
		# flag is set to "framework", since a reboot is always
		# provided for after a "framework" install.
		#
		if [[ ${result} -eq 10 ]] && [[ "${flag}" = "framework" ]]; then
			let result=0
		fi
		if [[ ${result} -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			cat ${tmperrs} >>${install_log}
			printf "$(gettext '%s:  Installation of \"%s\" failed')\n" "${PROG}" "${pkglist[i]}" | logerr
			return 1
		fi
		printf "%s\n" ${SC_DONE} | logmsg
		cat ${tmperrs} >>${install_log}

		# reboot requested on framework install
		if [[ "${flag}" = "framework" ]]; then
			reboot_requested=${SC_TRUE}
		fi

		# next
		((i += 1))
	done

	if [[ -n "${description}" ]]; then
		printf "\n" | logmsg
	fi

	return 0
}

#####################################################
#
# remove_packages() "pkglist" ["heading"]
#
#	"pkglist"		list of package names
#	"heading"		optional heading
#
#	Remove the given list of packages.  Packages not installed
#	are ignored.
#
#	It should not be necessary to remove patches before removing
#	packages.  Patch information is automatcally removed with packages.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
remove_packages()
{
	typeset pkglist;  set -A pkglist ${1}
	typeset heading="${2}"

	typeset buffer
	typeset cmdstring
	typeset rootarg=

	integer i
	integer j
	integer found

	# Check args
	if [[ $# -lt 1 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to remove_packages()')\n" ${PROG} >&2
		return 1
	fi

	if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
		rootarg="-R ${SC_BASEDIR}"
	fi

	# make sure that there is at least one package to remove
	let i=0
	let found=0
	while [[ -n "${pkglist[i]}" ]]
	do
		# See if the package is installed
		pkginfo ${rootarg} ${pkglist[i]} >/dev/null 2>&1

		# Found one
		if [[ $? -eq 0 ]]; then
			let found=1
			break
		fi
		((i += 1))
	done

	# if nothing to remove, return now
	if [[ ${found} -eq 0 ]]; then
		return 0
	fi

	# introduce the remove set
	printf "\n" | logmsg

	# Print action
	if [[ -z "${heading}" ]]; then
		heading="$(gettext 'Removing packages')"
	fi
	printf "** %s **\n" "${heading}" | logmsg

	# for each package
	let i=0
	while [[ -n "${pkglist[i]}" ]]
	do
		# See if the package is installed
		pkginfo ${rootarg} ${pkglist[i]} >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			((i += 1))
			continue
		fi

		# print message
		let j=$(expr ${pkglist[i]} : .\*)
		((j = 12 - j))
		buffer=${pkglist[i]}
		while [[ ${j} -gt 0 ]]
		do
			buffer="${buffer}."
			((j -= 1))
		done
		printf "\t$(gettext 'Removing %s')" "${buffer}"
		printf "\n\t$(gettext 'Removing %s')" "${pkglist[i]}" >>${install_log}

		# Package remove
		trap 'cleanup 10' HUP INT
		cmdstring="pkgrm -n -a ${adminfile} ${rootarg} ${pkglist[i]}"
		printf "${cmdstring}" >>${install_log}
		${cmdstring} >${tmperrs} 2>&1
		if [[ $? -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			cat ${tmperrs} >>${install_log}
			printf "$(gettext '%s:  Failed to remove \"%s\"')\n" "${PROG}" "${pkglist[i]}" | logerr
		else
			printf "%s\n" ${SC_DONE} | logmsg
			cat ${tmperrs} >>${install_log}
		fi
		((i += 1))
	done

	return 0
}

#####################################################
#
# get_service_pkglist() rtregfile
#
#	rtregfile		rt_reg(4) file from which to get PKGLIST
#
#	Print the list of packages from PKGLIST in the given rtreg file.
#
#	This function always returns zero.
#
#####################################################
get_service_pkglist()
{
	typeset -r rtregfile=$1

	typeset key
	typeset pkglist

	# Make sure there is an rtregfile
	if [[ -z "${rtregfile}" ]] ||
	    [[ ! -r "${rtregfile}" ]]; then
		return 0
	fi

	# Parse the PKGLIST
	pkglist="$(
		LC_ALL=C; export LC_ALL;
		key="$(grep -i '^[ 	]*pkglist' ${rtregfile} | sed -n 's/^[ 	]*\([^ 	=]*\).*/\1/p')"
		key=$(set -- ${key}; echo ${1})
		sed -n 's/^[ 	]*'${key}'[ 	]*=[ 	"]*\([^";]*\).*/\1/p' ${rtregfile}
	)"
	pkglist="$(IFS=" 	," ; set -- ${pkglist}; echo $*)"

	# Print pkglist
	echo ${pkglist}

	return 0
}

#####################################################
#
# get_installed_services cdimagebasedir [partialflag]
#
#       cdimagebasedir          location of .cdtoc file
#	partialflag		print partial service with error code 2
#
#	This function prints a list of all installed SUNW data services which
#	can also be upgraded.
#
#	If the "partialflag" is set and there is a partial upgrade error (2),
#	the partially upgraded data service is printed instead of the
#	upgrade list.  A partial upgrade error is taken when a partially
#	upgraded data service is detected, but is not on the CD.
#
#	The list of installed SUNW data services is determined by searching
#	for rt_reg(4) type files in the rtreg and gdsdata directories.
#	Any SUNW data services which have rt_reg(4) files outside of the
#	standard rtreg and gdsdata directories are not identified.
#
#	If a data service upgrade was already in progress, it is added to
#	the list of installed data services.
#
#	The .cdtoc and .clustertoc files on the CD are used to determine which
#	resource types can be upgraded from the CD.  The list of installed
#	services is modified to include only those which can be upgraded.
#
#	If a data service upgrade was already in progress, but cannot be
#	upgraded from the CD, an error is taken.
#
#       Return:
#               zero            Success
#               1		Failure
#		2		Failure - partial upgrade error
#
#####################################################
get_installed_services()
{
	# Check arg
	if [[ $# -gt 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to get_installed_services()')\n" ${PROG} >&2
		return 1
	fi

        typeset -r cdimagebasedir=$1
	typeset -r partialflag=$2

	typeset -r rtregdir=${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg
	typeset -r gdsdatadir=${SC_BASEDIR}/usr/cluster/lib/rgm/gdsdata

        typeset installedservices	# installed services
        typeset upgdservices		# upgradeable installed services
        typeset upgdservice
        typeset ignoreservice
	typeset key
        typeset rtrfiles
	typeset rstype
	typeset service
	integer i
	integer j

	# See if there is a partially installed data service
	currentservice=
	if [[ -f ${SC_UPGD_UPDS_FILE} ]]; then
		loadlib ${SC_SCLIBDIR}/${SC_LIB_UPGRADE} ${SC_LOADED_UPGRADE} || return 1
		currentservice=$(upgd_get_current_dataservice)
	fi

	# Initialize the list of installed services
	installedservices=${currentservice}

        # List of data service RTR files
	rtrfiles=
	if [[ -d ${rtregdir} ]]; then
        	rtrfiles=$(ls ${rtregdir}/SUNW.*)
	fi

	# Add SunPS dummy RTR files to list, if gdsdata directory is present  
	if [[ -d ${gdsdatadir} ]]; then
        	rtrfiles="${rtrfiles} $(ls ${gdsdatadir}/SUNW.* 2>/dev/null)"
	fi

	#
	# For each rtrfile, add to the list of installedservices
	#
	j=${#SC_RESTYPE_SERVICE_MAP[*]}
	for rtrfile in ${rtrfiles}
	do
		if [[ ! -f ${rtrfile} ]]; then
			continue
		fi

		# Get the resource_type
        	rstype=$(
			LC_ALL=C; export LC_ALL;
			key="$(grep -i '^[ 	]*resource_type' ${rtrfile} | sed -n 's/^[ 	]*\([^ 	=]*\).*/\1/p')"
			key=$(set - ${key}; echo ${1})
			sed -n 's/^[ 	]*'${key}'[ 	]*=[ 	"]*\([^";]*\).*/\1/p' ${rtrfile}
		)
		# If there is no resource_type, skip it
		if [[ -z "${rstype}" ]]; then
			continue
		fi

		if [[ "${rstype}" == rac_* ]]; then
			continue
		fi

		# Check for SERVICE_NAME in the rtrfile
		service=$(
			LC_ALL=C; export LC_ALL;
			key="$(grep -i '^#%[ 	]*SERVICE_NAME' ${rtrfile} | sed -n 's/^#%[ 	]*\([^ 	=]*\).*/\1/p')"
			if [[ -n ${key} ]]; then
				key=$(set - ${key}; echo ${1})
				sed -n 's/^#%[ 	]*'${key}'[ 	]*=[ 	"]*\([^";]*\).*/\1/p' ${rtrfile}
			fi
		)

		#
		# If there is a SERVICE_NAME, use that to add to the
		# list of installedservices.    Also add the
		# resource_type/SERVICE_NAME pair to our map.
		#
		if [[ -n "${service}" ]]; then

			# Add resource_type/SERVICE_NAME pair to the map
			SC_RESTYPE_SERVICE_MAP[j]=${rstype}
			SC_RESTYPE_SERVICE_MAP[j+1]=${service}
			(( j += 2 ))

		#
		# If there is no SERVICE_NAME, use the resource_type itself,
		# unless there is an entry in our map.
		#
		else

			# Default the service name to the resource type.
			service=${rstype}

			#
			# But, if there is a matching resourct type in
			# our map, use that, instead.
			#
			let i=0
			while [[ $i -lt ${#SC_RESTYPE_SERVICE_MAP[*]} ]]
			do
				if [[ "${SC_RESTYPE_SERVICE_MAP[i]}" == "${rstype}" ]]; then
					service=${SC_RESTYPE_SERVICE_MAP[i+1]}
					break
				fi
				(( i += 2 ))
			done
		fi

		# Add the service name to our list of installed services
		installedservices="${installedservices} ${service}"
	done

	#
	# Check for RAC packages
	#
	service=$(check_installed_rac_types)
	if [[ -n "${service}" ]]; then
		installedservices="${installedservices} ${service}"
	fi

	#
	# Get the list of installed services which can be upgraded
	# from the given CD.
	#
	upgdservices=
	for service in ${installedservices}
	do
		# Skip any services already in our list.
		for upgdservice in ${upgdservices}
		do
			if [[ "${upgdservice}" == "${service}" ]]; then
				continue 2
			fi
		done

		# Skip any services we are supposed to ignore.
		for ignoreservice in ${SC_UPGD_SERVICE_IGNORE}
		do
			if [[ "${ignoreservice}" == "${service}" ]]; then
				continue 2
			fi
		done

		# See if the data service is on the CD.
		SC_SERVICE="SUNWC_DS_"
		if [[ "${service}" == "${ORACLE_RAC_SERVICE}" ]] ||
		   [[ "${service}" == rac_* ]]; then
			SC_SERVICE=${SC_RAC}
		fi
		find_cdimagebasedir "${cdimagebasedir}" "" "${SC_SERVICE}${service}" >/dev/null 2>/dev/null

		#
		# If the service is not on the CD, verify that we are not
		# looking for a partially upgraded service, then continue.
		#
		if [[ $? -ne 0 ]]; then

			# If partial upgrade, it better be there
			if [[ "${service}" == "${currentservice}" ]]; then

				# Print error message or bad service?
				if [[ -n "${partialflag}" ]]; then
					echo ${service}
				else
					echo | logerr
					printf "%s:  $(gettext 'Data service "%s" is not found.')\n" ${PROG} ${service} | logerr
					printf "%s:  $(gettext 'This data service is partially upgraded.')\n" ${PROG} | logerr
					printf "%s:  $(gettext 'You must complete the upgrade.')\n" ${PROG} | logerr
					printf "%s:  $(gettext 'Please specify the correct data services CD.')\n" ${PROG}  | logerr
					echo | logerr
				fi

				# Return partial error
				return 2
			fi

			# Otherwise, skip it.
			continue
		fi

		# Add the service to our list of upgradeable services
		upgdservices="${upgdservices} ${service}"
	done

	SC_SERVICE="SUNWC_DS_"
	# Print the list, if there is one
	if [[ -n "${upgdservices}" ]]; then
		echo ${upgdservices}
	fi

	# Done
	return 0
}

#####################################################
#
# get_installed_service_packages service
#
#       service              Service name
#
#	this function returns the list of packages associated with an already
#	installed data service.
#
#	The data service, or resource type, must have an rt_reg(4) file in
#	${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg, in order for this function to
#	complete successfully.  The PKGLIST property in the resource type's
#	rt_reg(4) file is used to determine the list of packages associated with
#	the rt.
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
get_installed_service_packages()
{
        # Check arg
        if [[ $# -ne 1 ]]; then
                printf "$(gettext '%s:  Internal error - bad call to get_installed_service_packages()')\n" ${PROG} >&2
                return 1
        fi

	typeset -r  service=$1

	typeset  pkg
	typeset  l10n_packages
	typeset  package
	typeset  packages
	typeset  addpackage
	typeset  addpackages
	typeset  rtservice
	typeset  rtfile
	typeset  rtfiles
	typeset  key
	integer  found
	integer  i

	found=0
	i=0
	rtfiles=
	while [[ ${i} -lt ${#SC_RESTYPE_SERVICE_MAP[*]} ]]
	do
		if [[ ${SC_RESTYPE_SERVICE_MAP[i+1]} == ${service} ]];then
			rtfiles="${rtfiles} ${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.${SC_RESTYPE_SERVICE_MAP[i]}"
			found=1
		fi
		(( i += 2 ))
	done

	if [[ ${found} -eq 0 ]];then
		for rtfile in $(ls ${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.*)
		do
			rtservice=$(
				LC_ALL=C; export LC_ALL;
				key="$(grep -i '^#%[ 	]*SERVICE_NAME' ${rtfile} | sed -n 's/^#%[ 	]*\([^ 	=]*\).*/\1/p')"
				if [[ -n ${key} ]] ;then
					key=$(set - ${key}; echo ${1})
					sed -n 's/^#%[ 	]*'${key}'[ 	]*=[ 	"]*\([^";]*\).*/\1/p' ${rtfile}
				fi
			)
			if [[ ${rtservice} == ${service} ]];then
				rtfiles="${rtfiles} ${rtfile}"
			fi
		done
	fi

	if [[ -z "${rtfiles}" ]];then
		rtfiles="${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.${service}"
	fi

	packages=""
	for rtfile in ${rtfiles}
	do
		addpackages=$(get_service_pkglist ${rtfile})
		for addpackage in "${addpackages}"
		do
			let found=0
			for package in "${packages}"
			do
				if [[ ${package} = ${addpackage} ]];then
					let found=1
					break
				fi
			done
			if [[ ${found} -eq 0 ]];then
				packages="${packages} ${addpackage}"
			fi
		done
	done

	let i=0
	# Locate the service in the builtin list
	while [[ ${i} -lt ${#SC_UPGD_SERVICE_MAP[*]} ]]
	do
		if [[ ${service} = ${SC_UPGD_SERVICE_MAP[i]} ]];then
			# Check the presence of each package in the builtin list
			for package in ${packages}
			do
				let found=0
				for package_bin in ${SC_UPGD_SERVICE_MAP[i+1]}
				do
					if [[ ${package} = ${package_bin} ]];then
						found=1
						break
					fi
				done
				# if any package is not in the Builtin list stop checking
				if [[ $found -eq 0 ]];then
					break
				fi
			done
			# If all packages were found in the builtin list Use builtin list Instead
			if [[ $found -eq 1 ]];then
				packages=${SC_UPGD_SERVICE_MAP[i+1]}
			fi
			break
		fi
		(( i += 2 ))
	done

	# List Corresponding L10n packages for the set of packages
	l10n_packages=
	i=0
	while [[ $i -lt ${#SC_L10N_PKG_MAP[*]} ]]
	do
		for pkg in ${SC_L10N_PKG_MAP[i+1]}
		do
			for package in ${packages}
			do
				if [[ ${pkg} == ${package} ]];then
					l10n_packages="${l10n_packages} ${SC_L10N_PKG_MAP[i]}"
					break 2
				fi
			done
		done
		(( i += 2 ))
	done

	echo ${packages} ${l10n_packages}
	return 0
}

#####################################################
#
# installframework() cdimagebasedir
#
#	cdimagebasedir		location of .cdtoc file
#
#	Install the framework packages.
#
#	Partially installed packages are removed, then re-installed.
#	Already installed packages are skipped.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
installframework()
{
	typeset -r cdimagebasedir=$1

	typeset realcdimage
	typeset productdir
	typeset productrel
	typeset pkglist
	typeset revpkglist

	# Check arg
	if [[ $# -ne 1 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to installframework()')\n" ${PROG} >&2
		return 1
	fi

	#
	# Note that the cdimagebasedir may be given as either the
	# directory containing the .cdtoc we are looking for OR
	# the directory above that.  This call to find_cdimagebasedir()
	# will reset the cdimagebasedir to be the directory containing
	# our .cdtoc.
	#
	realcdimage=$(find_cdimagebasedir "${cdimagebasedir}" "${SC_PRODUCT}" "${SC_CLUSTER}") || return 1

	# get the name of the product directory
	productdir=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "dir") || return 1

	# get the product release
	productrel=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "rel") || return 1

	# get the list of packages
	pkglist="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${SC_CLUSTER} "packages")" || return 1

	# order the list of packages
	pkglist="$(order_packages ${productdir}/${SC_ORDER} "${pkglist}")" || return 1

	# install packages
	install_packages ${productdir} "${pkglist}" "framework" "${SC_PRODUCT} ${productrel} framework" || return 1

	# indicate that the framework packages were fully installed
	let SC_FRAMEWORK_INSTALLED=1
	touch ${SC_BASEDIR}/${SC_INSTALLED_FILE}

        # make sure next reboot is a reconfig reboot
	if [[ -z "${SC_BASEDIR}" ]]; then
		touch /reconfigure
	fi

	return 0
}

#####################################################
#
# uninstallframework() clustertoc order [all]
#
#	clustertoc		name of the clustertoc file to use
#				clustertoc can be "" if "all" is specified.
#	order			name of the order file to use
#	all			if not NULL, uninstall everything in order file
#
#	Uninstall the framework packages. To prevent the uninstall of 
#	some framework packages, an "exceptions" list is used. 
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
uninstallframework()
{
	typeset -r clustertoc=$1
	typeset -r order=$2
	typeset -r all=$3
	typeset -r exceptions=${SC_UNINSTALL_EXCEPTIONS}

	typeset pkglist
	typeset revpkglist
	typeset heading
	typeset pkg
	typeset foo

	# Check args
	if [[ $# -lt 2 ]] ||
	    [[ "${clustertoc}" != /* && -z "${all}" ]] ||
	    [[ "${order}" != /* ]];  then
		printf "$(gettext '%s:  Internal error - bad call to uninstallframework()')\n" ${PROG} >&2
		return 1
	fi

	# If "all", use everything in the order file
	if [[ -n "${all}" ]]; then
		pkglist="$(cat ${order})"
	else
		# get the list of packages
		pkglist="$(print_clustertoc ${clustertoc} ${SC_CLUSTER} "packages")" || return 1

		# order the list of packages
		pkglist="$(order_packages ${order} "${pkglist}")" || return 1
	fi

	# reverse the order
	revpkglist="$(rev_order_packages "${pkglist}")"
	
       	# Weed out the exceptions
        pkglist=
        if [[ -n "${exceptions}" ]]; then
                for pkg in ${revpkglist}
                do
                        for foo in ${exceptions}
                        do
                                if [[ "${foo}" == "${pkg}" ]]; then
                                        continue 2
                                fi
                        done
                        pkglist="${pkglist} ${pkg}"
                done
		revpkglist="${pkglist}"
        fi

	# indicate that the framework packages are not fully installed
	let SC_FRAMEWORK_INSTALLED=0
	rm -f ${SC_INSTALLED_FILE}

	# remove the packages
	heading="$(printf "$(gettext 'Removing %s packages')" "Sun Cluster framework")"
	remove_packages "${revpkglist}" "${heading}" || return 1

	return 0
}


#####################################################
#
# installothers cdimagebasedir "swclusters"
#
#       cdimagebasedir          location of .cdtoc file
#       "swclusters"            software clusters to install
#
#	This function is very similar to the installframework() and
#	installservices() functions and depends on already existing functions
#	used commonly by both.
#
#	But, instead of installing a specific software cluster, it installs all
#	of the software clusters in the "swclusters" list other than SUNWCsc or
#	any of the clusters nested into SUNWCsc.
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
installothers()
{
	# Check arg
	if [[ $# -ne 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to installothers()')\n" ${PROG} >&2
		return 1
	fi

	typeset -r cdimagebasedir=$1
	typeset -r swclusters=$2
	typeset -r exclusters=${SC_CLUSTER}
	typeset excluded=${exclusters}

	typeset description
	typeset exclude
	typeset realcdimage
	typeset productdir
	typeset productrel
	typeset pkglist
	typeset revpkglist

	#
	# Note that the cdimagebasedir may be given as either the
	# directory containing the .cdtoc we are looking for OR
	# the directory above that.  This call to find_cdimagebasedir()
	# will reset the cdimagebasedir to be the directory containing
	# our .cdtoc.
	#

	realcdimage=$(find_cdimagebasedir "${cdimagebasedir}" "${SC_PRODUCT}" "${SC_CLUSTER}") || return 1

	for swcl in ${exclusters}
	do
		exclude=$(print_clustertoc() ${realcdimage}/${SC_CDTOC} $swcl "clusters")
		excluded=$(echo $excluded $exclude)
	done

	# get the name of the product directory
	productdir=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "dir") || return 1

	# get the product release
	productrel=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "rel") || return 1
	for swcluster in ${swclusters}
	do
		let ignore=0
		for exclude in ${excluded}
		do
			if [[ "${swcluster}" = "${exclude}" ]];then
				let ignore=1
				break
			fi
		done
		if [[ $ignore -eq 1 ]];then
			continue
		fi

        	# get the list of packages
        	pkglist="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${swcluster} "packages")" || return 1

		# get the product description
		description="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${swcluster} "description" 2>/dev/null)"
		if [[ -z "${description}" ]];then
			description="${SC_PRODUCT} ${productrel} $(gettext 'software')"
		fi

        	# order the list of packages
        	pkglist="$(order_packages ${productdir}/${SC_ORDER} "${pkglist}")" || return 1

        	# install packages
        	install_packages ${productdir} "${pkglist}" "" "${description}" || return 1
	done
        return 0
}

#####################################################
#
# installdocs cdimagebasedir "swcluster"
#
#       cdimagebasedir          location of .cdtoc file
#       "swcluster"            documentatation software cluster to install
#
#	This function is very similar to the installothers() and
#	installservices() functions and depends on already existing functions
#	used commonly by both. 
# 
#	But, instead of installing a specific software cluster, it installs all
#	of the documentation software clusters in the "swclusters". 
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
installdocs()
{
	# Check arg
	if [[ $# -ne 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to installothers()')\n" ${PROG} >&2
		return 1
	fi

	typeset -r cdimagebasedir=$1
	typeset -r swcluster=$2

	typeset description
	typeset realcdimage
	typeset productdir
	typeset productrel
	typeset pkglist
	typeset revpkglist
	typeset rootarg=
	typeset responsearg=

	typeset gflg=

	if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
		rootarg="-R ${SC_BASEDIR}"
	fi

	if [[ "${SC_OS_VERSION}" = "5.10" ]]; then
		gflg="-G"
	fi

	#
	# Note that the cdimagebasedir may be given as either the
	# directory containing the .cdtoc we are looking for OR
	# the directory above that.  This call to find_cdimagebasedir()
	# will reset the cdimagebasedir to be the directory containing
	# our .cdtoc.
	# 

	realcdimage=$(find_cdimagebasedir "${cdimagebasedir}" "" "${swcluster}") || return 1

	# get the name of the product directory
	productdir=$(getproduct ${realcdimage}/${SC_CDTOC} "" ${swcluster} "dir") || return 1

	# get the product release
	productrel=$(getproduct ${realcdimage}/${SC_CDTOC} "" ${swcluster} "rel") || return 1

	# get the list of packages
	pkglist="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${swcluster} "packages")" || return 1

	instpkgs=
	for pkg in ${pkglist}
	do
		instpkgver=$(pkgparam ${pkg} VERSION 2> /dev/null)
		if [[ $? -ne 0 ]];then
			instpkgs="${instpkgs} ${pkg}"
			continue
		fi

		cdpkgver=$(pkgparam -d ${productdir} ${pkg} VERSION 2> /dev/null)
		if [[ $? -ne 0 ]];then
			printf "$(gettext '%s:  Internal error - Cannot locate package \"%s\" on CDROM ')\n" ${PROG} ${pkg} >&2
			return 1
		fi

		if [[ ${pkg} == "SUNWsdocs" ]] ;then
			if [[ ${instpkgver} < ${cdpkgver} ]];then
				printf "\n$(gettext '%s:  WARNING: Documentation navigation package "%s" not upgraded.')\n" "${PROG}" "SUNWsdocs" | logmsg
			fi
			continue
		fi

		if [[ ${instpkgver} != ${cdpkgver} ]];then
			instpkgs="${instpkgs} ${pkg}"
		fi
	done

	if [[ -z "${instpkgs}" ]];then
		return 0
	fi

	# get the product description
	description="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${swcluster} "description" 2>/dev/null)"
	if [[ -z "${description}" ]];then
		description="${SC_PRODUCT} ${productrel} $(gettext 'software')"
	fi

	# order the list of packages
	pkglist="$(order_packages ${productdir}/${SC_ORDER} "${instpkgs}")" || return 1

	# Print the description
	printf "** $(gettext 'Installing %s') **\n" "${description}" | logmsg
	for pkg in ${pkglist}
	do
		#
		# Install the package
		#
		# The "<pkg>.....done." message is printed in
		# the same style as used by JumpStart.
		#
		let j=$(expr ${pkg} : .\*)
		((j = 12 - j))
		buffer=${pkg}
		while [[ ${j} -gt 0 ]]
		do
			buffer="${buffer}."
			((j -= 1))
		done
		printf "\t%s" "${buffer}"
		printf "\n\t%s\n" "${pkg}" >>${install_log}

		# Package add
		trap 'cleanup 10' HUP INT
		cmdstring="pkgadd ${gflg} -S -d ${productdir} -n -a ${adminfile} ${rootarg} ${responsearg} ${pkg}"
		${PRINTDEBUG} ${cmdstring}
		printf "${cmdstring}" >>${install_log}
		${cmdstring} >${tmperrs} 2>&1
		let result=$?
		if [[ ${result} -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			cat ${tmperrs} >>${install_log}
			printf "$(gettext '%s:  Installation of \"%s\" failed')\n" "${PROG}" "${pkg}" | logerr
			return 1
		fi
		printf "%s\n" ${SC_DONE} | logmsg
		cat ${tmperrs} >>${install_log}
	done
	return 0
}

#####################################################
#
# installservices() cdimagebasedir "services"
#
#	cdimagebasedir		location of .cdtoc file
#	"services"		list of services
#
#	Install the service packages from the "services" list.
#
#	Partially installed packages are removed, then re-installed.
#	Already installed packages are skipped.
#
#	Each Sun cluster "data service" has its own individual
#	software cluster name associated with it.  The name
#	has the format "SUNWC_DS_<srvcname>", where "srvcname"
#	is the name given to scinstall(1M).
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
installservices()
{
	typeset -r cdimagebasedir=$1
	typeset -r services="${2}"

	integer j
	typeset realcdimage
	typeset service
	typeset productdir
	typeset description
	typeset pkglist

	# Check arg
	if [[ $# -ne 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to installservices()')\n" ${PROG} >&2
		return 1
	fi

	# for each service
	let i=0
	for service in ${services}
	do
		#
		# Note that the cdimagebasedir may be given as either the
		# directory containing the .cdtoc we are looking for OR
		# the directory above that.  This call to find_cdimagebasedir()
		# will reset the cdimagebasedir to be the directory containing
		# our .cdtoc.
		#
		realcdimage=$(find_cdimagebasedir "${cdimagebasedir}" "" "${SC_SERVICE}${service}") || return 1
		# get the name of the product directory
		productdir=$(getproduct ${realcdimage}/${SC_CDTOC} "" ${SC_SERVICE}${service} "dir" 2>/dev/null)
		if [[ $? -ne 0 ]]; then
			printf "$(gettext 'Cannot find data service \"%s\" - skipping')\n" "${service}" | logmsg
			continue
		fi

		# get the product description
		description="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${SC_SERVICE}${service} "description" 2>/dev/null)"
		if [[ -z "${description}" ]]; then
			description="Data service ${service}"
		fi

		# get the list of packages
		pkglist="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${SC_SERVICE}${service} "packages")"
		if [[ $? -ne 0 ]]; then
			printf "$(gettext 'Cannot get package list for data service \"%s\" - skipping')\n" "${service}" | logmsg
			continue
		fi

		# if more than one package in cluster, find the order
		let j=$(set -- ${pkglist};  echo $#)
		if [[ ${j} -gt 1 ]]; then
			pkglist="$(order_packages ${productdir}/${SC_ORDER} "${pkglist}")"
			if [[ $? -ne 0 ]]; then
				printf "$(gettext 'Unable to determine package order for data service \"%s\" - skipping')\n" "${service}" | logmsg
				continue
			fi
		fi

		# install packages
		install_packages ${productdir} "${pkglist}" "" "${description}"

		# next
	done

	return 0
}

#####################################################
#
# uninstallservices() [exceptions]
#
#	exceptions		list of packages NOT to unininstall
#
#	Uninstall the SUNW data services packages.  The list of packages
#	to uninstall is gleened from the "pkglist" in rtreg files.
#	To prevent the uninstall of framework packages,	an "exceptions"
#	list can be given.
#
#	The order in which these packages are removed should not be
#	important, since rdepend=nocheck is specified in the admin file.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
uninstallservices()
{
	typeset -r exceptions="${1}"

	typeset -r rtregdir=${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg
	typeset -r gdsdatadir=${SC_BASEDIR}/usr/cluster/lib/rgm/gdsdata

	typeset pkg
	typeset pkglist
	typeset heading
	typeset file
	typeset services_pkglist
	typeset key
	typeset foo
        typeset rtrfiles
	typeset l10n_packages
	typeset l10_pkg

	# List of data service RTR files
	rtrfiles=
	if [[ -d ${rtregdir} ]]; then
        	rtrfiles=$(ls ${rtregdir}/SUNW.* 2>/dev/null)
	fi

	# Add SunPS dummy RTR files to list, if gdsdata directory is present  
	if [[ -d ${gdsdatadir} ]]; then
        	rtrfiles="${rtrfiles} $(ls ${gdsdatadir}/SUNW.* 2>/dev/null)"
	fi

	# Get an ordered list of packages from RTR files 
	services_pkglist="$(
		for file in ${rtrfiles}
		do
                	# Get the list of packages
                	pkglist="$(get_service_pkglist ${file})"

			# Reverse the order
			rev_order_packages "${pkglist}"
		done
	)"

	pkglist=
        # Assemble a array of localization packages installed on the node
        # Packagename V/s SUNW_PKGLIST  for the specific L10n package.
        set -A SC_L10N_PKG_MAP
        i=0
        for pkg in $(pkginfo | awk '{print $2}')
        do
                pkglist="$(pkgparam ${pkg} SUNW_PKGLIST)"
                if [[ -n "${pkglist}" ]];then
                        SC_L10N_PKG_MAP[i]=${pkg}
                        SC_L10N_PKG_MAP[i+1]="${pkglist}"
                        (( i += 2 ))
                fi
        done

	# List Corresponding L10n packages for the set of packages
        l10n_packages=
        i=0
	pkg=
        while [[ $i -lt ${#SC_L10N_PKG_MAP[*]} ]]
        do
                for l10_pkg in ${SC_L10N_PKG_MAP[i+1]}
                do
                        for pkg in ${services_pkglist}
                        do
                                if [[ ${l10_pkg} == ${pkg} ]];then
                                        l10n_packages="${l10n_packages} ${SC_L10N_PKG_MAP[i]}"
                                        break 2
                                fi
                        done
                done
                (( i += 2 ))
        done

	services_pkglist="${services_pkglist} ${l10n_packages}"
	# Weed out the exceptions
	pkglist=
	pkg=
	if [[ -n "${exceptions}" ]]; then
		for pkg in ${services_pkglist}
		do
			for foo in ${exceptions}
			do
				if [[ "${foo}" == "${pkg}" ]]; then
					continue 2
				fi
			done
			pkglist="${pkglist} ${pkg}"
		done
	fi
	services_pkglist="${pkglist}"

	# remove the packages
	heading="$(printf "$(gettext 'Removing %s data services packages')" "Sun Cluster")"
	remove_packages "${pkglist}" "${heading}" || return 1

	return 0
}

#####################################################
#
# uninstallservice service "pkglist"
#
#       service              Service name
#	"pkglist"	     List of packages to remove.
#
#	This function removes packages in the reverse order as listed in the
#	pkglist.
#
#	If a package in the pkglist is not installed, it is quietly ignored.
#
#	The service name is used in an attempt to locate an rt_reg(4) file for
#	the resource type which will include a RT_DESCRIPTION property.
#	If available, this description is incorporated into a
#	"Removing ${rt_description}" message.  If either an rt_reg(4) file or
#	 RT_DESCRIPTION are not found, the service name itself is used in a
#	 "Removing resource type ${service}" message.
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
uninstallservice()
{
        # Check arg
        if [[ $# -ne 2 ]]; then
                printf "$(gettext '%s:  Internal error - bad call to uninstallservice()')\n" ${PROG} >&2
                return 1
        fi

	typeset -r  service=$1
	typeset -r pkglist=$2

	if [[ -f "${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.${service}" ]];then
		rtfile="${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.${service}"
        	description=$(grep -i "^RT_DESCRIPTION =" ${rtfile}|awk -F\" '{print $2}'|head -1)
	else
		description=
	fi


	if [[ -z "${description}" ]]; then
		description="HA ${service} Data Service on Sun Cluster"
	fi

	# Reverse the order
	revpkglist="$(rev_order_packages "${pkglist}")"

	# remove the packages
	heading="$(printf "$(gettext 'Removing %s')" "$description")"
	remove_packages "${revpkglist}" "${heading}" no || return 1

	return 0
}

#####################################################
#
# install_patches() "patch_options"
#
#	"patch_options"		options for scpatchadm
#
#	If patch options are provided, attempt to install patches
#	by invoking scpatchadm.
#
#	Don't fail the installation if there are problems with patch
#	installation, just log all errors.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
install_patches()
{
	typeset patch_options="${*}"

	integer result

	# If no options, there is nothing to do
	if [[ -z "${patch_options}" ]]; then
		return 0
	fi

	# Print message
	printf "$(gettext 'Installing patches ... ')" | logmsg
	echo "\n" >>${install_log}

	# Run scpatchadm
	${SC_SCLIBDIR}/scpatchadm "${patch_options}" >>${install_log} 2>&1
	let result=$?
	if [[ ${result} -ne 0 ]]; then
        	printf "%s\n" ${SC_FAILED}
	else
        	printf "%s\n" ${SC_DONE}
	fi
	if [[ -f "${SC_PATCH_INSTALL_LOG}" ]]; then
		cat ${SC_PATCH_INSTALL_LOG} >>${install_log}
		echo >>${install_log}
	fi
	if [[ ${result} -ne 0 ]]; then
		printf "\n$(gettext '%s:  Problems detected during extraction or installation of patches.')\n\n" "${PROG}" | logmsg
	fi

	# Done
	return ${result}
}

#####################################################
#
# expand_adapter_options() "adapter_options"
#
#	Expand the adapter options from the default.
#	If only an adapter name is given, "trtype" is added.
#
#	"setdefaults()" should always be called before
#	calling this function.
#
#	This function always returns 0.
#
#####################################################
expand_adapter_options()
{
	typeset -r adapter_options="$1"

	typeset new_options=
	typeset subopts

	integer i
	typeset c

	# make sure options are given as expected
	if [[ $# -ne 1 ]]; then
		echo $*
		return 0
	fi

	# make sure required defaults are set
	if [[ -z "${SC_DFLT_TRANSPORT_TYPE}" ]]; then
		echo "${adapter_options}"
		return 0
	fi

	set - ${adapter_options}
	OPTIND=1
	while getopts A: c 2>/dev/null
	do
		case ${c} in
		A)	# Adapter
			subopts="${OPTARG}"

			# check for single suboption
			let i=$(IFS=, ; set - ${subopts};  echo $#)
			if [[ ${i} -ne 1 ]]; then
				new_options="${new_options} -A ${subopts}"
				continue
			fi

			# is it a standalone adapter name?
			if [[ "${subopts}" != *=* ]]; then
				subopts="name=${subopts}"
			fi

			# make sure single option is "name"
			if [[ "${subopts}" != name=* ]]; then
				continue
			fi

			# add other required suboption(s)
			subopts="${subopts},trtype=${SC_DFLT_TRANSPORT_TYPE}"

			# add it to the list
			new_options="${new_options} -A ${subopts}"

			;;

		*)	# Error
			echo "${adapter_options}"
			return 0
			;;
		esac
	done

	if [[ -n "${new_options}" ]]; then
		echo "${new_options}"
	else
		echo "${adapter_options}"
	fi

	return 0
}

#####################################################
#
# strip_type_direct() "bb_options"
#
#	Strip out "type=direct" from the bb_options.
#
#	This function always returns 0.
#
#####################################################
strip_type_direct()
{
	typeset -r bb_options="$1"

	typeset new_options=
	typeset new_subopts
	typeset subopts
	typeset subopt

	integer i
	typeset c

	# make sure options are given as expected
	if [[ $# -ne 1 ]]; then
		echo $*
		return 0
	fi

	set - ${bb_options}
	OPTIND=1
	while getopts B: c 2>/dev/null
	do
		case ${c} in
		B)	# Blackbox
			subopts="${OPTARG}"

			# Remove, from subopts
			subopts="$(IFS=, ; set - ${subopts};  echo $*)"

			new_subopts=
			for subopt in ${subopts}
			do
				if [[ "${subopt}" != "type=direct" ]]; then
					if [[ -n "${new_subopts}" ]]; then
						new_subopts="${new_subopts},${subopt}"
					else
						new_subopts="${subopt}"
					fi
				fi
			done

			if [[ -n "${new_subopts}" ]]; then
				new_options="${new_options} -B ${new_subopts}"
			fi

			;;

		*)	# Error
			echo "${bb_options}"
			return 0
			;;
		esac
	done

	echo "${new_options}"

	return 0
}

#####################################################
#
# expand_bb_options() "bb_options"
#
#	Expand the blackbox options from the default.
#	If only a blackbox name is given, "type" is added.
#
#	"setdefaults()" should always be called before
#	calling this function.
#
#	This function always returns 0.
#
#####################################################
expand_bb_options()
{
	typeset -r bb_options="$1"

	typeset new_options=
	typeset subopts

	integer i
	typeset c

	# make sure options are given as expected
	if [[ $# -ne 1 ]]; then
		echo $*
		return 0
	fi

	# make sure required defaults are set
	if [[ -z "${SC_DFLT_JUNCTION_TYPE}" ]]; then
		echo "${bb_options}"
		return 0
	fi

	set - ${bb_options}
	OPTIND=1
	while getopts B: c 2>/dev/null
	do
		case ${c} in
		B)	# Blackbox
			subopts="${OPTARG}"

			# check for single suboption
			let i=$(IFS=, ; set - ${subopts};  echo $#)
			if [[ ${i} -ne 1 ]]; then
				new_options="${new_options} -B ${subopts}"
				continue
			fi

			# is it a standalone blackbox name?
			if [[ "${subopts}" != *=* ]]; then
				subopts="name=${subopts}"
			fi

			# make sure single option is "name"
			if [[ "${subopts}" != name=* ]]; then
				continue
			fi

			# add other required suboption(s)
			subopts="${subopts},type=${SC_DFLT_JUNCTION_TYPE}"

			# add it to the list
			new_options="${new_options} -B ${subopts}"

			;;

		*)	# Error
			echo "${bb_options}"
			return 0
			;;
		esac
	done

	if [[ -n "${new_options}" ]]; then
		echo "${new_options}"
	else
		echo "${bb_options}"
	fi

	return 0
}

#####################################################
#
# print_default_cable() "adapter_options" "bb_options" "installnode"
#
#	Using the given "adapter_options" and "bb_options",
#	print an option for a default cable.  If there is
#	more than one adapter or bb, there is no default.
#
#	This function always returns 0.
#
#####################################################
print_default_cable()
{
	typeset adapter_options="$1"
	typeset bb_options="$2"
	typeset installnode="$3"

	typeset subopts;  set -A subopts ""
	typeset adapter_name=
	typeset bb_name=

	integer a
	integer b

	typeset c

	# make sure that adapter options are set
	if [[ -z "${adapter_options}" ]]; then
		if [[ -n "${SC_DFLT_JUNCTION_OPTS}" ]]; then
			adapter_options="-A ${SC_DFLT_ADAPTER_OPTS}"
		else
			return 0
		fi
	fi

	# if bb_options not set, use the default bb name
	if [[ -z "${bb_options}" ]]; then
		if [[ -n "${SC_DFLT_JUNCTION_OPTS}" ]]; then
			bb_options="-B ${SC_DFLT_JUNCTION_OPTS}"
		else
			return 0
		fi
	fi

	# remove the "-A" and "-B"
	let a=0
	let b=0
	set - ${adapter_options} ${bb_options}
	OPTIND=1
	while getopts A:B: c 2>/dev/null
	do
		case ${c} in
		A)	# Adapter
			adapter_options=${OPTARG}
			((a += 1))
			;;

		B)	# Junction
			bb_options=${OPTARG}
			((b += 1))
			;;

		*)
			return 0
			;;
		esac
	done

	# if more than 1 -A or -B, we are done
	if [[ ${a} -gt 1 || ${b} -gt 1 ]]; then
		return 0
	fi

	# make sure they are both still set
	if [[ -z "${adapter_options}" || -z "${bb_options}" ]]; then
		return 0
	fi

	# get the adapter name
	let a=0
	set -A subopts $(IFS=, ; echo ${adapter_options})
	while [[ -n "${subopts[a]}" ]]
	do
		adapter_name=$(expr "${subopts[a]}" : 'name=\(.*\)')
		if [[ -n "${adapter_name}" ]]; then
			break
		fi
		((a += 1))
	done

	# and, the bb name
	let a=0
	set -A subopts $(IFS=, ; echo ${bb_options})
	while [[ -n "${subopts[a]}" ]]
	do
		bb_name=$(expr "${subopts[a]}" : 'name=\(.*\)')
		if [[ -n "${bb_name}" ]]; then
			break
		fi
		((a += 1))
	done

	# make sure both names are set
	if [[ -z "${adapter_name}" && -z "${bb_name}" ]]; then
		return 0
	fi

	# print default cable
	echo "-m endpoint=${installnode}:${adapter_name},endpoint=${bb_name}"

	return 0
}

#####################################################
#
# initialize_cluster() "clustername" "auth_options" "adapter_options"
#    "bb_options" "cable_options" "netaddr_options" "one_node_flag"
#
#	"clustername"			cluster name
#	"auth_options"			"-T <authentication_options>"
#	"adapter_options"		"-A <adapter_options> ..."
#	"bb_options"			"-B <blackbox_options> ..."
#	"cable_options"			"-m <cable_options> ..."
#	"netaddr_options"		"-w <network_options>"
#	"postconfig_options"		"-P <postconfig_options> ..."
#	"one_node_flag"			true of false (-o)
#
#	Initialize the local CCR database.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
initialize_cluster()
{
	typeset -r clustername=$1
	typeset -r auth_options=$2
	typeset -r adapter_options=$3
	typeset -r bb_options=$4
	typeset -r cable_options=$5
	typeset -r netaddr_options=$6
	typeset -r postconfig_options=$7
	integer -r one_node_flag=$8

	integer result
	typeset c
	typeset message
	typeset cluster_options
	typeset infrastructure
	typeset cmd
	typeset cmdarg
	typeset cmdargs=
	typeset auth_string
	typeset tasklist
	typeset task
	typeset statelist

	# Check args
	if [[ $# -ne 8 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to initialize_cluster()')\n" ${PROG} >&2
		return 1
	fi

	# Set cluster options
	cluster_options=
	if [[ -n "${clustername}" ]]; then
		cluster_options="-C ${clustername}"
	fi

	# Check for scrconf usage errors
	OPTIND=1
	set - ${cluster_options} ${auth_options} ${adapter_options} ${bb_options} ${cable_options} ${netaddr_options}
	while getopts C:T:A:B:m:w: c 2>/dev/null
	do
		rm -f ${tmpconfig}
		cmdarg="-${c} ${OPTARG}"
		if [[ ${c} = A ]]; then
			cmdarg="${cmdarg},node=${mynodename}"
		fi
		cmdargs="${cmdargs} ${cmdarg}"
		cmd="scrconf -U -f ${tmpconfig} ${cmdargs}"
		${PRINTDEBUG} ${cmd}
		${cmd} 2>${tmperrs}
		let result=$?
		if [[ ${result} -eq ${SC_SCCONF_EUSAGE} ]]; then
			printf "$(gettext '%s:  Bad %s option (\"%s\")')\n" "${PROG}" "-${c}" "${cmdarg}" | logerr
		elif [[ ${result} -ne 0 ]]; then
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Unexpected result (%d) from scrconf during option check (\"%s\")')\n" "${PROG}" "${result}" "${cmdarg}" | logerr
		fi
		if [[ ${result} -ne 0 ]]; then
			rm -f ${tmpconfig}
			return 1
		fi
	done

	printf "\n" | logmsg

	# Iteratively create a tmp config file
	OPTIND=1
	cmdargs="-h node=${mynodename}"
	set - ${cluster_options} ${auth_options} ${adapter_options} ${bb_options} ${cable_options} ${netaddr_options}
	while getopts C:T:A:B:m:w: c 2>/dev/null
	do
		rm -f ${tmpconfig}
		cmdarg="-${c} ${OPTARG}"
		if [[ ${c} = A ]]; then
			cmdarg="${cmdarg},node=${mynodename}"
		fi
		cmdargs="${cmdargs} ${cmdarg}"

		case ${c} in
		C)	# clustername
			message="$(printf "$(gettext 'Initializing cluster name to \"%s\" ... ')" "${OPTARG}")"
			;;

		T)	# authentication
			message="$(printf "$(gettext 'Initializing authentication options ... ')")"
			;;


		h)	# nodename
			message="$(printf "$(gettext 'Initializing configuration for node \"%s\" ... ')" "${mynodename}")"
			;;

		A)	# adapter
			message="$(printf "$(gettext 'Initializing configuration for adapter \"%s\" ... ')" "$(print_subopt_values "${OPTARG}" "name")")"
			;;

		B)	# blackbox
			message="$(printf "$(gettext 'Initializing configuration for junction \"%s\" ... ')" "$(print_subopt_values "${OPTARG}" "name")")"
			;;

		m)	# cable
			message="$(printf "$(gettext 'Initializing configuration for cable ... ')")"
			;;

		w)	# netaddr
			message="$(printf "$(gettext 'Initializing private network address options ... ')")"
			;;

		*)	message="??? ... "
			;;
		esac

		# printf first part of message
		printf "${message}" | logmsg

		cmd="scrconf -f ${tmpconfig} ${cmdargs}"
		${PRINTDEBUG} ${cmd}
		${cmd} 2>${tmperrs}
		let result=$?
		if [[ ${result} -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Failed to initialize cluster configuration (\"%s\")')\n" "${PROG}" "${cmdarg}" | logerr
			rm -f ${tmpconfig}
			return 1
		fi
		printf "%s\n" ${SC_DONE} | logmsg
	done

	# if just a one node install, disable installmode
	if [[ ${one_node_flag} -eq ${SC_TRUE} ]]; then
		ed -s ${tmpconfig} << EOF >/dev/null
/^cluster\.properties\.installmode[ 	]/s/enabled/disabled/
w
q
EOF
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to initialize cluster configuration (\"%s\")')\n" "${PROG}" "installmode" | logerr
			rm -f ${tmpconfig}
			return 1
		fi
	fi

	# add checksum
	ccradm -i ${tmpconfig} -o
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to add CCR checksum to infrastructure table')\n" "${PROG}" | logerr
		return 1
	fi

	# Now, move file into place
	mv ${tmpconfig} ${SC_CONFIG}
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to create cluster configuration file')\n" "${PROG}" | logerr
		rm -f ${tmpconfig}
		return 1
	fi

	# Set attributes
	setfile "${SC_CONFIG}"
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Cannot set attributes of the cluster config file (\"%s\")')\n" "${PROG}" "${SC_CONFIG}" | logerr
		return 1
	fi

	# Create the postconfig CCR file
	rm -f ${SC_POSTCONFIG}
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to remove pre-existing postconfig file (\"%s\")')\n" "${PROG}" "${SC_POSTCONFIG}" | logerr
		return 1
	fi 
	auth_string="$(echo ${auth_options} | cut -f2 -d' ')"
	nodelist="$(print_subopt_values "${auth_string}" "node")"
	tasklist="$(print_subopt_values "${postconfig_options}" "task")"
	statelist="$(print_subopt_values "${postconfig_options}" "state")"
	i=-1
	for task in ${tasklist}
	do
		if [[ "${task}" == "none" ]]; then
			break
		fi

		((i += 1))
		if [[ -z "${statelist[i]}" ]]; then
			continue
		fi
			
		for nodename in ${nodelist}
		do
			echo task.${task}.${nodename}.state ${statelist[i]} >> ${SC_POSTCONFIG}
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  Cannot update the postconfig file (\"%s\")')\n" "${PROG}" "${SC_POSTCONFIG}" | logerr
				return 1
			fi
		done
	done

	if [[ -s ${SC_POSTCONFIG} ]]; then
		# add checksum
		ccradm -i ${SC_POSTCONFIG} -o
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to add CCR checksum to postconfig file (\"%s\")')\n" "${PROG}" "${SC_POSTCONFIG}" | logerr
			return 1
		fi

		# Set attributes
		setfile "${SC_POSTCONFIG}"
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot set attributes of the postconfig file (\"%s\")')\n" "${PROG}" "${SC_POSTCONFIG}" | logerr
			return 1
		fi
	fi

	# If there is no CCR table directory, add it now
	if [[ ! -f "${SC_CCRDIR}" ]]; then
		touch "${SC_CCRDIR}"
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot create the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
			return 1
		fi
		setfile "${SC_CCRDIR}"
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot set attributes of the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
			return 1
		fi
	fi

	# Add the infrastructure table to the CCR table directory
	infrastructure=${SC_CONFIG##*/}
	grep '^'${infrastructure} ${SC_CCRDIR} >/dev/null
	if [[ $? -ne 0 ]]; then
		echo ${infrastructure} >>${SC_CCRDIR}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot update the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
			return 1
		fi
	fi

	if [[ -s ${SC_POSTCONFIG} ]]; then
		
		# Add the postconfig table to CCR table directory
		postconfig=${SC_POSTCONFIG##*/}
		grep '^'${postconfig} ${SC_CCRDIR} > /dev/null
		if [[ $? -ne 0 ]]; then
			echo ${postconfig} >>${SC_CCRDIR}
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  Cannot update the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
				return 1
			fi
		fi
	fi

	# add checksum
	ccradm -i ${SC_CCRDIR} -o
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to add CCR checksum to table directory')\n" "${PROG}" | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# addnode_tocluster() sponsornode "clustername_options" "adapter_options"
#    "bb_options" "cable_options"
#
#	sponsornode			remote node for connection
#	clustername			verify cluster name
#	"adapter_options"		"-A <adapter_options> ..."
#	"bb_options"			"-B <blackbox_options> ..."
#	"cable_options"			"-m <cable_options> ..."
#
#	Initialize the local CCR database from remote cluster.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
addnode_tocluster()
{
	typeset -r sponsornode=$1
	typeset -r clustername=$2
	typeset -r adapter_options=$3
	typeset -r bb_options=$4
	typeset -r cable_options=$5

	integer result
	integer bad_options=${SC_FALSE}
	integer firstime
	typeset c
	typeset cmd
	typeset cmdarg
	typeset buffer
	typeset lastbusy
	typeset busynode
	typeset sc_config
	typeset sc_postconfig
	typeset foo

	# Check args
	if [[ $# -ne 5 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to addnode_tocluster()')\n" ${PROG} >&2
		return 1
	fi

	#
	# Wait for sponsor node to join cluster
	#
	check_node_status "${sponsornode}" || return 1

	# Verify that we are talking to the right cluster
	if [[ -n "${clustername}" ]]; then
		cmd="scrconf -a -N ${sponsornode} -C ${clustername}"
		${PRINTDEBUG} ${cmd}
		${cmd} >${tmperrs} 2>&1
		let result=$?
		if [[ ${result} -ne 0 ]]; then
			case ${result} in
			${SC_SCCONF_ENOCLUSTER})
				printf "$(gettext '%s:  \"%s\" does not belong to cluster \"%s\"')\n" "${PROG}" "${sponsornode}" "${clustername}" | logerr
				;;

			${SC_SCCONF_ENOEXIST})
				printf "$(gettext '%s:  The cluster to which \"%s\" belongs does not have a name')\n" "${PROG}" "${sponsornode}" | logerr
				;;

			${SC_SCCONF_EAUTH})
				printf "$(gettext '%s:  RPC authentication error.')\n" "${PROG}"
				printf "$(gettext '%s:  Not authorized to communicate with \"%s\".')\n" "${PROG}" "${sponsornode}"
				;;

			*)
				if [[ -s "${tmperrs}" ]]; then
					cat ${tmperrs}
				fi
				;;
			esac

			printf "$(gettext '%s:  Cluster name verification failed.')\n" "${PROG}"
			return 1
		fi
	fi

	# Check for scrconf usage errors
	OPTIND=1
	set - ${adapter_options} ${bb_options} ${cable_options}
	while getopts A:B:m: c 2>/dev/null
	do
		cmdarg="-${c} ${OPTARG}"
		if [[ ${c} = A ]]; then
			cmdarg="${cmdarg},node=${mynodename}"
		fi
		cmd="scrconf -U -a -N ${sponsornode} ${cmdarg}"
		${PRINTDEBUG} ${cmd}
		${cmd} 2>${tmperrs}
		let result=$?
		if [[ ${result} -eq ${SC_SCCONF_EUSAGE} ]]; then
			printf "$(gettext '%s:  Bad %s option (\"%s\")')\n" "${PROG}" "-${c}" "${cmdarg}" | logerr
			let bad_options=${SC_TRUE}
		elif [[ ${result} -ne 0 ]]; then
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Unexpected result (%d) from scrconf during option check (\"%s\")')\n" "${PROG}" "${result}" "${cmdarg}" | logerr
			let bad_options=${SC_TRUE}
		fi
		if [[ ${result} -ne 0 ]]; then
			rm -f ${tmpconfig}
		fi
	done

	# If one or more bad options, we are done
	if [[ ${bad_options} -eq ${SC_TRUE} ]]; then
		return 1
	fi

	printf "\n" | logmsg

	# Update cluster config
	OPTIND=1
	set - -h node=${mynodename} ${adapter_options} ${bb_options} ${cable_options} 2>/dev/null
	while getopts h:A:B:m: c 2>/dev/null
	do
		cmdarg="-${c} ${OPTARG}"
		if [[ ${c} = A ]]; then
			cmdarg="${cmdarg},node=${mynodename}"
		fi
		case ${c} in
		h)	# nodename
			buffer="$(printf "$(gettext 'node \"%s\"')" "${mynodename}")"
			;;

		A)	# adapter
			buffer="$(printf "$(gettext 'adapter \"%s\"')" "$(print_subopt_values "${OPTARG}" "name")")"
			;;

		B)	# blackbox
			buffer="$(printf "$(gettext 'junction \"%s\"')" "$(print_subopt_values "${OPTARG}" "name")")"
			;;

		m)	# cable
			buffer="$(gettext 'cable')"
			;;

		*)	buffer="?"
			;;
		esac

		printf "$(gettext 'Adding %s to the cluster configuration ... ')" "${buffer}" | logmsg

		rm -f ${tmperrs}
		cmd="scrconf -a -N ${sponsornode} ${cmdarg}"
		${PRINTDEBUG} ${cmd}
		if [[ ${c} = h ]]; then
			lastbusy=
			firsttime=${SC_TRUE}
			while true
			do
				#
				# If SC_SCCONF_EBUSY, scrconf prints
				# busy node name to stdout.
				#
				busynode="$(${cmd} 2>${tmperrs2})"
				let result=$?
				if [[ ${result} -ne ${SC_SCCONF_EBUSY} ]]; then
					if [[ ${firsttime} -ne ${SC_TRUE} ]]; then
						printf "\n$(gettext 'Attempt to add %s to the configuration ... ')" "${buffer}" | logmsg
					fi
					cat ${tmperrs2} >>${tmperrs}
					break;
				elif [[ -s ${tmperrs2} ]]; then
					cat ${tmperrs2} >>${tmperrs}
				fi
				rm -f ${tmperrs2}
				if [[ -z "${busynode}" ]]; then
					busynode="."
				fi
				if [[ ${firsttime} -eq ${SC_TRUE} ]]; then
					firsttime=${SC_FALSE}
					printf "$(gettext 'busy')\n" | logmsg
				fi
				if [[ "${lastbusy}" != "${busynode}" ]]; then
					if [[ "${busynode}" = "." ]]; then
						printf "\n$(gettext 'Waiting for all configured nodes to join the cluster ... ')" | logmsg
					else
						printf "\n$(gettext 'Waiting for \"%s\" to join the cluster ... ')" "${busynode}" | logmsg
					fi
					lastbusy=${busynode}
				fi

				# sleep
				sleep 20
			done
		else
			${cmd} >>${tmperrs} 2>&1
			let result=$?
		fi
		if [[ ${result} -eq ${SC_SCCONF_EEXIST} ]]; then
			printf "$(gettext 'skipped')\n" | logmsg
			printf "$(gettext 'Skipped %s - already configured')\n\n" "${buffer}" | logmsg

		elif [[ ${result} -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Failed to update cluster configuration (\"%s\")')\n\n" "${PROG}" "${cmdarg}" | logerr
			return 1
		else
			printf "%s\n" ${SC_DONE} | logmsg
			if [[ -n "${SC_DEBUG}" ]] && [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
		fi
	done

	# remove old tmp files
	rm -f ${tmpconfig}
	rm -f ${tmperrs}

	# get a copy of the file
	if [[ -z "${SC_BASEDIR}" ]]; then
		sc_config=${SC_CONFIG}
	else
		sc_config="$(expr "${SC_CONFIG}" : ${SC_BASEDIR}'\(.*\)')"
	fi
	printf "\n" | logmsg

	printf "$(gettext 'Copying the config from \"%s\" ... ')" "${sponsornode}" | logmsg
	cmd="scrconf -g -N ${sponsornode} ${sc_config} ${tmpconfig}"
	${PRINTDEBUG} ${cmd}
	${cmd} >${tmperrs} 2>&1
	let result=$?
	if [[ ${result} -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Unable to retrieve a copy of the cluster config')\n" "${PROG}" | logerr
		return 1
	fi
	printf "%s\n" ${SC_DONE} | logmsg
	if [[ -n "${SC_DEBUG}" ]] && [[ -s "${tmperrs}" ]]; then
		cat ${tmperrs} | logerr
	fi

	# Now, move file into place
	mv ${tmpconfig} ${SC_CONFIG}
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to create cluster configuration file')\n" "${PROG}" | logerr
		rm -f ${tmpconfig}
		return 1
	fi

	# Set attributes
	setfile "${SC_CONFIG}"
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Cannot set attributes of the cluster config file (\"%s\")')\n" "${PROG}" "${SC_CONFIG}" | logerr
		return 1
	fi

	# remove old tmp files
	rm -f ${tmppostconfig}
	rm -f ${tmperrs}

	# get a copy of the postconfig file
	if [[ -z "${SC_BASEDIR}" ]]; then
		sc_postconfig=${SC_POSTCONFIG}
	else
		sc_postconfig="$(expr "${SC_POSTCONFIG}" : ${SC_BASEDIR}'\(.*\)')"
	fi
	printf "\n" | logmsg

	printf "$(gettext 'Copying the postconfig file from \"%s\" if it exists ... ')" "${sponsornode}" | logmsg
	cmd="scrconf -g -N ${sponsornode} ${sc_postconfig} ${tmppostconfig}"
	${PRINTDEBUG} ${cmd}
	${cmd} >${tmperrs} 2>&1
	let result=$?
	if [[ ${result} -eq ${SC_SCCONF_ENOEXIST} ]]; then
		printf "%s\n" ${SC_DONE} | logmsg
		printf "$(gettext 'No postconfig file found on \"%s\", continuing')" "${sponsornode}" | logmsg
	elif [[ ${result} -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Unable to retrieve a copy of the cluster postconfig file')\n" "${PROG}" | logerr
		return 1
	else
		printf "%s\n" ${SC_DONE} | logmsg
	fi

	if [[ -n "${SC_DEBUG}" ]] && [[ -s "${tmperrs}" ]]; then
		cat ${tmperrs} | logerr
	fi

	if [[ -s ${tmppostconfig} ]]; then
		# Now, move file into place
		mv ${tmppostconfig} ${SC_POSTCONFIG}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to create cluster postconfig file')\n" "${PROG}" | logerr
			rm -f ${tmppostconfig}
			return 1
		fi

		# Set attributes
		setfile "${SC_POSTCONFIG}"
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot set attributes of the cluster postconfig file (\"%s\")')\n" "${PROG}" "${SC_CONFIG}" | logerr
			return 1
		fi
	fi

	# remove old tmp files
	rm -f ${tmppostconfig}
	rm -f ${tmperrs}

	# If there is no CCR table directory, add it now
	if [[ ! -f "${SC_CCRDIR}" ]]; then
		touch "${SC_CCRDIR}"
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot create the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
			return 1
		fi
		setfile "${SC_CCRDIR}"
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot set attributes of the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
			return 1
		fi
	fi

	# Add the infrastructure table to the CCR table directory
	infrastructure=${SC_CONFIG##*/}
	grep '^'${infrastructure} ${SC_CCRDIR} >/dev/null
	if [[ $? -ne 0 ]]; then
		echo ${infrastructure} >>${SC_CCRDIR}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Cannot update the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
			return 1
		fi
	fi

	if [[ -s ${SC_POSTCONFIG} ]]; then
		# Add the postconfig table to the CCR table directory
		postconfig=${SC_POSTCONFIG##*/}
		grep '^'${postconfig} ${SC_CCRDIR} >/dev/null
		if [[ $? -ne 0 ]]; then
			echo ${postconfig} >>${SC_CCRDIR}
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  Cannot update the cluster config table directory (\"%s\")')\n" "${PROG}" "${SC_CCRDIR}" | logerr
				return 1
			fi
		fi
	fi

	# add checksum
	ccradm -i ${SC_CCRDIR} -o
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to add CCR checksum to table directory')\n" "${PROG}" | logerr
		return 1
	fi

	# Copy cacao key and certificate files
	printf "$(gettext 'Copying the Common Agent Container keys from \"%s\" ... ')" "${sponsornode}" | logmsg
	PASSWD_DIR=/etc/opt/SUNWcacao/security
	JSSE_DIR=/etc/opt/SUNWcacao/security/jsse
	mkdir -p ${SC_BASEDIR}/$PASSWD_DIR
	mkdir -p ${SC_BASEDIR}/$JSSE_DIR
	rm -f ${SC_BASEDIR}/${PASSWD_DIR}/password
	cmd="scrconf -g -N ${sponsornode} ${PASSWD_DIR}/password ${SC_BASEDIR}/${PASSWD_DIR}/password"
	${PRINTDEBUG} ${cmd}
	${cmd} >${tmperrs} 2>&1
	let result=$?
	if [[ ${result} -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Unable to retrieve a copy of the Common Agent Container password')\n" "${PROG}" | logerr
		return 1
	fi
	for foo in agent.cert keystore truststore ; do
		rm -f ${SC_BASEDIR}/${JSSE_DIR}/$foo
		cmd="scrconf -g -N ${sponsornode} ${JSSE_DIR}/$foo ${SC_BASEDIR}/${JSSE_DIR}/$foo"
		${PRINTDEBUG} ${cmd}
		${cmd} >${tmperrs} 2>&1
		let result=$?
		if [[ ${result} -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Unable to retrieve a copy of the Common Agent Container jsse keys')\n" "${PROG}" | logerr
			return 1
		fi

		chmod 644 ${SC_BASEDIR}/${JSSE_DIR}/$foo 2>/dev/null
	done
	# password is private, must only be readable by root
	setfile ${SC_BASEDIR}/${PASSWD_DIR}/password 0600


	# If NSS is installed, copy its config info, too
	NSS_DIR=/etc/opt/SUNWcacao/security/nss
	if [[ -d ${NSS_DIR} ]]; then
		for dir in localca unknown wellknown ; do
			mkdir -p ${SC_BASEDIR}/${NSS_DIR}/${dir}
			for foo in secmod.db cert8.db key3.db ; do
				rm -f ${SC_BASEDIR}/${NSS_DIR}/${dir}/${foo}
				cmd="scrconf -g -N ${sponsornode} ${NSS_DIR}/${dir}/${foo} ${SC_BASEDIR}/${NSS_DIR}/${dir}/${foo}"
				${PRINTDEBUG} ${cmd}
				${cmd} >${tmperrs} 2>&1
				let result=$?
				if [[ ${result} -ne 0 ]]; then 
					printf "%s\n" ${SC_FAILED} | logmsg
					if [[ -s "${tmperrs}" ]]; then
		                                cat ${tmperrs} | logerr
		                        fi
		                        printf "$(gettext '%s:  Unable to retrieve a copy of the Common Agent Container NSS keys')\n" "${PROG}" | logerr
					return 1
				fi

				chmod 644 ${SC_BASEDIR}/${NSS_DIR}/${dir}/${foo} 2>/dev/null
			done

			if [[ ${dir} = "localca" || ${dir} = "wellknown" ]]; then
				rm -f ${SC_BASEDIR}/${NSS_DIR}/${dir}/${dir}.cert
				cmd="scrconf -g -N ${sponsornode} ${NSS_DIR}/${dir}/${dir}.cert ${SC_BASEDIR}/${NSS_DIR}/${dir}/${dir}.cert"
				${PRINTDEBUG} ${cmd}
				${cmd} >${tmperrs} 2>&1
				let result=$?
				if [[ ${result} -ne 0 ]]; then
					printf "%s\n" ${SC_FAILED} | logmsg
					if [[ -s "${tmperrs}" ]]; then
						cat ${tmperrs} | logerr
					fi
					printf "$(gettext '%s:  Unable to retrieve a copy of the Common Agent Container NSS keys')\n" "${PROG}" | logerr
					return 1
				fi

				chmod 644 ${SC_BASEDIR}/${NSS_DIR}/${dir}/${dir}.cert 2>/dev/null
			fi
		done
	fi	

	printf "%s\n" ${SC_DONE} | logmsg

	if [[ -n "${SC_DEBUG}" ]] && [[ -s "${tmperrs}" ]]; then
		cat ${tmperrs} | logerr
	fi

	return 0
}

#####################################################
#
# set_nodeid nodeidfile
#
#	Create the nodeid file.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
set_nodeid()
{
	typeset -r nodeidfile=$1

	typeset nodeid
	integer result

	# Check args
	if [[ $# -ne 1 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to set_nodeid()')\n" "${PROG}" >&2
		return 1
	fi

	# create the nodeid file
	printf "\n" | logmsg
	printf "$(gettext 'Setting the node ID for \"%s\" ... ')" "${mynodename}" | logmsg
	rm -f ${nodeidfile}
	nodeid=$(scrconf -p -h node=${mynodename} < ${SC_CONFIG} 2>${tmperrs})
	let result=$?
	echo ${nodeid} >${nodeidfile}
	if [[ $? -ne 0 ]] || [[ ${result} -ne 0 ]] || [[ -z "${nodeid}" ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Unable to create the node ID file (\"%s\")')\n" "${PROG}" "${nodeidfile}" | logerr
		rm -f ${nodeidfile}
		return 1
	fi
	printf "%s" ${SC_DONE} | logmsg
	printf " $(gettext '(id=%d)')\n" "${nodeid}" | logmsg

	# Set attributes
	setfile "${nodeidfile}"
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Cannot set attributes of the node ID file (\"%s\")')\n" "${PROG}" "${nodeidfile}" | logerr
		return 1
	fi

	return 0
}


#####################################################
#
# set_noclustermode flag
#
#       flag         ${SC_TRUE}   prevents node from booting into cluster mode.
#	             ${SC_FALSE}  allows node to boot into cluster mode.
#
#	The set_noclustermode() function turns on or off the ability of a node
#	to boot in cluster mode.
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
set_noclustermode()
{
   typeset -r flag=$1


	if [[ $# -ne 1 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to set_noclustermode()')\n" ${PROG} >&2
		return 1
	fi
	case ${flag} in
      		${SC_TRUE})
			printf "\n\n$(gettext 'Do not boot this node into cluster mode until upgrade is complete.')\n" | logmsg
        		;;
		${SC_FALSE})
       			;;
		*)
			printf "$(gettext '%s:  Internal error - bad call to set_noclustermode()')\n" ${PROG} >&2
       			return 1
        		;;
	esac
}

#####################################################
#
# is_globaldevfs nodeid
#
#	nodeid				Node ID
#
#	Check to see if there is a mount entry in either
#	${SC_BASEDIR}/etc/vfstab or /etc/mnttab for
#	${SC_GLOBALDEVDIR}/node@<nodeid>.
#
#	Return:
#		0		There is no mount entry
#		1		There is a mount entry
#		-1		Error
#
#####################################################
is_globaldevfs()
{
	typeset -r nodeid=$1

	typeset -r globaldevmountp=${SC_GLOBALDEVDIR}/node@${nodeid}
	typeset special
	typeset fsckdev
	typeset mountp
	typeset foo

	# Check args
	if [[ $# -ne 1 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to is_globaldevfs()')\n" "${PROG}" >&2
		return 1
	fi

	# Check ${SC_BASEDIR}/etc/vfstab for the global mount
	while read special fsckdev mountp foo
	do
		case ${special} in
		'#'* | '')	# Ignore comments, empty lines
				continue
				;;
		esac

		if [[ "${mountp}" = "${globaldevmountp}" ]]; then
			return 1
		fi
	done < ${SC_BASEDIR}/etc/vfstab
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Error reading %s')\n" "${PROG}" "${SC_BASEDIR}/etc/vfstab" | logerr
		return 1
	fi

	# Check /etc/mnttab for the mount
	while read special mountp foo
	do
		if [[ "${mountp}" = "${globaldevmountp}" ]]; then
			return 1
		fi
	done < /etc/mnttab
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Error reading %s')\n" "${PROG}" "/etc/mnttab" | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# is_globalfs_okay [global_fs] [msgtype]
#
#	global_fs			File system name used with -G
#	msgtype				Type of error message to print
#		1				stderr format w/ "failed"
#		2				stderr format w/out "failed"
#		3				stdout format (interactive)
#
#	If msgtype is not given, default is type "1".
#
#	If global_fs is not given, the default is assumed.
#
#	Check to see if the file system name to use for the global
#	devices filesystem is okay to use.
#
#	Return:
#		0		Okay to use
#		1		Does not begin with /
#		2		Is not a directory
#		3		Is not a mount point in /etc/vfstab
#		4		Error reading /etc/vfstab
#		5		Is not a mount point in /etc/mnttab
#		6		Error reading /etc/mnttab
#		7		Is not empty
#		8		Does not include lost+found
#		9		Is under /global
#		10		Other error
#
#####################################################
is_globalfs_okay()
{
	typeset global_fs=$1
	typeset msgtype=$2

	typeset failed_msg=
	typeset logcmd=cat
	typeset prefix=
	typeset special
	typeset fsckdev
	typeset mountp
	typeset foo
	typeset dir

	integer found

	# Check args
	if [[ $# -gt 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to is_globalfs_okay()')\n" "${PROG}" >&2
		return 10
	fi

	# Setup message type
	if [[ -z "${msgtype}" ]]; then
		msgtype=1
	fi
	case ${msgtype} in
	2)	# stderr format w/out "failed"
		logcmd=logerr
		prefix="${PROG}:  "
		;;

	3)	# stdout format (interactive)
		;;

	*)	# stderr format w/ "failed"
		failed_msg="${SC_FAILED}"
		logcmd=logerr
		prefix="${PROG}:  "
		;;
	esac

	# If "global_fs" is not set, use default
	if [[ -z "${global_fs}" ]]; then
		global_fs=${SC_GLOBALDEVFS}
	fi

	# Make sure it begins with /
	if [[ ${global_fs} != /* ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%sGlobal file system device name must begin with slash (\"/\").')\n" "${prefix}" | ${logcmd}
		return 1
	fi

	# Make sure it is a directory
	if [[ ! -d ${SC_BASEDIR}${global_fs} ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s%s is not a directory or file system mount point.')\n" "${prefix}" "${SC_BASEDIR}${global_fs}" | ${logcmd}
		return 2
	fi

	# Under /global?
	if [[ ${global_fs} == /global/* ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s\"%s\" is in %s.')\n" "${prefix}" "${global_fs}" "/global" | ${logcmd}
		return 9
	fi

	#
	# Check for "global_fs" in ${SC_BASEDIR}/etc/vfstab
	#
	let found=0
	while read special fsckdev mountp foo
	do
		case ${special} in
		'#'* | '')	# Ignore comments, empty lines
				continue
				;;
		esac

		if [[ "${mountp}" = "${global_fs}" ]]; then
			let found=1
			break
		fi
	done < ${SC_BASEDIR}/etc/vfstab
	if [[ $? -ne 0 ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%sError reading %s.')\n" "${prefix}" "${SC_BASEDIR}/etc/vfstab" | ${logcmd}
		return 4
	fi

	# If not found in vfstab, error
	if [[ ${found} -eq 0 ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s%s is not a mount point in %s.')\n" "${prefix}" "${global_fs}" "${SC_BASEDIR}/etc/vfstab" | ${logcmd}
		return 3
	fi

	# Make sure it is actually mounted
	let found=0
	while read special mountp foo
	do
		if [[ "${mountp}" == "${SC_BASEDIR}${global_fs}" ]]; then
			let found=1
			break
		fi
	done < /etc/mnttab
	if [[ $? -ne 0 ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%sError reading %s.')\n" "${prefix}" "/etc/mnttab" | ${logcmd}
		return 6
	fi

	# If not found in mnttab, error
	if [[ ${found} -eq 0 ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s%s is not mounted.')\n" "${prefix}" "${global_fs}" | ${logcmd}
		return 5
	fi

	# Make sure it is an empty file system
	let found=0
	for dir in ${SC_BASEDIR}${global_fs}/.* ${SC_BASEDIR}${global_fs}/*
	do
		case ${dir} in
		${SC_BASEDIR}${global_fs}/.)
			;;

		${SC_BASEDIR}${global_fs}/..)
			;;

		${SC_BASEDIR}${global_fs}/lost+found)
			((found += 1))
			;;

		*)
			[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
			printf "$(gettext '%s\"%s\" is not empty.')\n" "${prefix}" "${SC_BASEDIR}${global_fs}" | ${logcmd}
			return 7
			;;
		esac
	done

	# No lost+found?
	if [[ ${found} -eq 0 ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s\"%s\" does not include a %s directory.')\n" "${prefix}" "${SC_BASEDIR}${global_fs}" "lost+found" | ${logcmd}
		return 8
	fi

	return 0
}

#####################################################
#
# is_globalcspecial_okay global_cspecial [msgtype]
#
#	global_cspecial			Character special device used w/ -G
#	msgtype				Type of error message to print
#		1				stderr format w/ "failed"
#		2				stderr format w/out "failed"
#		3				stdout format (interactive)
#
#	If msgtype is not given, default is type "1".
#
#	Check to see if the given character special device is okay to use for
#	the global devices filesystem.
#
#	Return:
#		0		Okay to use
#		1		Does not begin with /
#		2		Is not a character special device
#		3		Is already in use by /etc/vfstab
#		4		Error reading /etc/vfstab
#		10		Other error
#
#####################################################
is_globalcspecial_okay()
{
	typeset -r global_cspecial=$1
	typeset msgtype=$2

	typeset failed_msg=
	typeset logcmd=cat
	typeset prefix=
	typeset special
	typeset fsckdev
	typeset mountp
	typeset foo

	integer found

	# Check args
	if [[ $# -gt 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to is_globalcspecial_okay()')\n" "${PROG}" >&2
		return 10
	fi

	# Setup message type
	if [[ -z "${msgtype}" ]]; then
		msgtype=1
	fi
	case ${msgtype} in
	2)	# stderr format w/out "failed"
		logcmd=logerr
		prefix="${PROG}:  "
		;;

	3)	# stdout format (interactive)
		;;

	*)	# stderr format w/ "failed"
		failed_msg="${SC_FAILED}"
		logcmd=logerr
		prefix="${PROG}:  "
		;;
	esac

	# Make sure it begins with /
	if [[ ${global_cspecial} != /* ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%sDevice name must begin with slash (\"/\").')\n" "${prefix}" | ${logcmd}
		return 1
	fi

	# Makes sure it exists
	if [[ ! -a ${SC_BASEDIR}${global_cspecial} ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s%s is not found.')\n" "${prefix}" "${global_cspecial}" | ${logcmd}
		return 2
	fi

	# Make sure it is a character special device
	if [[ ! -c ${SC_BASEDIR}${global_cspecial} ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s%s is not a character special device.')\n" "${prefix}" "${global_cspecial}" | ${logcmd}
		return 2
	fi

	# Make sure it is not given in vfstab
	let found=0
	while read special fsckdev mountp foo
	do
		case ${special} in
		'#'* | '')	# Ignore comments, empty lines
				continue
				;;
		esac

		if [[ "${special}" = "${global_cspecial}" ]] ||
		    [[ "${fsckdev}" = "${global_cspecial}" ]]; then
			let found=1
			break
		fi
	done < ${SC_BASEDIR}/etc/vfstab
	if [[ $? -ne 0 ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%sError reading %s.')\n" "${prefix}" "${SC_BASEDIR}/etc/vfstab" | ${logcmd}
		return 4
	fi

	# If found in vfstab, error
	if [[ ${found} -ne 0 ]]; then
		[[ -n "${failed_msg}" ]] && echo ${failed_msg} | logmsg
		printf "$(gettext '%s\"%s\" is a mount device in %s.')\n" "${prefix}" "${global_cspecial}" "${SC_BASEDIR}/etc/vfstab" | ${logcmd}
		return 3
	fi

	return 0
}

#####################################################
#
# create_globaldevfs nodeid [global] [checkflag]
#
#	nodeid				ID of this node
#	global				file system or special device
#	nocheckflag			if given, do not re-check "global"
#
#	Create a file system, and mount it on
#	${SC_BASEDIR}${SC_GLOBALDEVDIR}/node@<id>.
#
#	If "global" is given and is the name of a mounted file system,
#	a check is made to verify that it is empty.  If it is, it
#	is unmounted, and a new file system created, if needed.  An
#	entry is added to ${SC_BASEDIR}/etc/vfstab, and it it mounted.
#
#	If "global" is not given, ${SC_GLOBALDEVFS} is used as the default.
#
#	"global" may also be given as the name of a special device.
#	There must not be a mount entry for the device.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
create_globaldevfs()
{
	typeset -r nodeid=$1
	typeset global=$2
	typeset -r nocheckflag=$3

	typeset global_fs=
	typeset global_cspecial=
	typeset global_bspecial=
	typeset -r globaldevmountp=${SC_GLOBALDEVDIR}/node@${nodeid}

	typeset special
	typeset fsckdev
	typeset mountp
	typeset foo

	typeset dir
	typeset vfstabline
	integer result
	integer found

	# Check args
	if [[ $# -lt 1 ]] || [[ $# -gt 3 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to create_globaldevfs()')\n" "${PROG}" >&2
		return 1
	fi

	# See if the globaldevmountp already exists
	printf "$(gettext 'Checking for global devices global file system ... ')" "${globaldevmountp}" | logmsg
	is_globaldevfs ${nodeid} 2>${tmperrs}
	case $? in
	0)	# Not found
		printf "%s\n" ${SC_DONE} | logmsg
		;;

	1)	# Found it
		printf "%s\n" ${SC_DONE} | logmsg
		printf "$(gettext 'Already mounted - %s')\n" "${globaldevmountp}"
		return 0
		;;

	*)	# Error
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Cannot determine if global mount already exists')\n" "${PROG}" | logerr
		return 1
		;;
	esac

	#
	# But, be sure that our globaldev mount point exists;
	# it should have been created by the Sun Cluster packages.
	#
	if [[ ! -d "${SC_BASEDIR}${globaldevmountp}" ]];  then
		printf "$(gettext '%s:  %s is not found')\n" "${PROG}" "${SC_BASEDIR}${globaldevmountp}" | logerr
		return 1
	fi

	# If "global" is not set, use default
	if [[ -z "${global}" ]]; then
		global=${SC_GLOBALDEVFS}
	fi

	#
	# If "global" is a character special device, set global_cspecial
	# otherwise, set global_fs.
	#
	if [[ -c "${global}" ]]; then
		global_cspecial=${global}
	else
		global_fs=${global}
	fi

	#
	# If it is a file system, check to see if all requirements are met.
	# This check should be redundant, as it is also called when command
	# line options are processed.
	#
	if [[ -n "${global_fs}" ]]; then

		# Check device?
		if [[ -z "${nocheckflag}" ]]; then
			printf "$(gettext 'Checking device to use for global devices file system ... ')" | logmsg
			is_globalfs_okay ${global_fs}
			if [[ $? -ne 0 ]]; then
				return 1
			fi
			printf "%s\n" ${SC_DONE} | logmsg
		fi

		# Attempt unmount
		umount ${SC_BASEDIR}${global_fs} 2>${tmperrs}
		if [[ $? -ne 0 ]]; then
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Cannot unmount \"%s\"')\n" "${PROG}" "${SC_BASEDIR}${global_fs}" | logerr
			return 1
		fi

		# Set global_cspecial
		while read special fsckdev mountp foo
		do
			case ${special} in
			'#'* | '')	# Ignore comments, empty lines
					continue
					;;
			esac

			if [[ "${mountp}" = "${global_fs}" ]]; then
				global_cspecial=${fsckdev}
				break
			fi
		done < ${SC_BASEDIR}/etc/vfstab
		if [[ -z "${global_cspecial}" ]]; then
			printf "$(gettext '%s:  Error reading %s')\n" "${PROG}" "${SC_BASEDIR}/etc/vfstab" | logerr
			return 1
		fi

	elif [[ -n "${global_cspecial}" ]]; then

		# Check device?
		if [[ -z "${nocheckflag}" ]]; then
			printf "$(gettext 'Checking device to use for global devices file system ... ')" | logmsg
			is_globalcspecial_okay ${global_cspecial}
			if [[ $? -ne 0 ]]; then
				return 1
			fi
			printf "%s\n" ${SC_DONE} | logmsg
		fi
	else
		printf "$(gettext '%s:  Internal error - create_globaldevfs')\n" "${PROG}" | logerr
		return 1
	fi

	# Set global_bspecial
	foo=$(expr "${global_cspecial}" : '/..*\(/rdsk/\)')
	if [[ "${foo}" != "/rdsk/" ]]; then
		printf "$(gettext '%s:  %s is not a raw disk device')\n" "${PROG}" "${OPTARG}" | logerr
		return 1
	fi
	global_bspecial=$(echo "${global_cspecial}" | sed 's/rdsk/dsk/')
	if [[ ! -b "${global_bspecial}" ]]; then
		printf "$(gettext '%s:  \"%s\" is not a block special device')\n" "${PROG}" "${global_bspecial}" | logerr
		return 1
	fi

	# Search /etc/mnttab for "global_bspecial";  it must not be mounted
	while read special mountp foo
	do
		if [[ "${special}" = "${global_cspecial}" ]] ||
		    [[ "${special}" = "${global_bspecial}" ]];  then
			printf "$(gettext '%s:  \"%s\" is already mounted')\n" "${PROG}" "${special}" | logerr
			return 1
		fi
	done < /etc/mnttab
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Error reading %s')\n" "${PROG}" "/etc/mnttab" | logerr
		return 1
	fi

	# If not a filesystem, create one
	if [[ -z "${global_fs}" ]]; then
		printf "$(gettext 'Creating global devices file system on %s ... ')" "${global_cspecial}" | logmsg
		newfs ${global_cspecial} </dev/null 2>${tmperrs} >/dev/null
		if [[ $? -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Failed to create global devices file system (%s)')\n" "${PROG}" "${global_cspecial}" | logerr
			return 1
		fi
		printf "%s\n" ${SC_DONE} | logmsg
	fi

	# Edit vfstab
	printf "$(gettext 'Updating %s ... ')" "vfstab" | logmsg
	vfstabline="$(echo "${global_bspecial}\t${global_cspecial}\t${globaldevmountp}\tufs\t2\tno\tglobal")"
	grep '^'${global_bspecial}'[ 	]' ${SC_BASEDIR}/etc/vfstab >/dev/null 2>&1
	if [[ $? -eq 0 ]]; then
		foo=$(echo ${global_bspecial} | sed 's#/#\\/#g')
		ed -s ${SC_BASEDIR}/etc/vfstab << EOF >/dev/null 2>&1
/^${foo}/s/^/#/
\$a
${vfstabline}
.
w
q
EOF
		let result=$?
	else
		echo "${vfstabline}" >>${SC_BASEDIR}/etc/vfstab
		let result=$?
	fi
	if [[ ${result} -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		printf "$(gettext '%s:  Failed to update %s')\n" "${PROG}" "${SC_BASEDIR}/etc/vfstab" | logerr
		return 1
	fi
	printf "%s\n" ${SC_DONE} | logmsg

	# Attempt to remove the old mount point
	if [[ -n "${global_fs}" ]] && [[ -d "${global_fs}" ]]; then
		rmdir ${global_fs} 2>/dev/null
	fi

	return 0
}

#####################################################
#
# configure_did_entry       sponsornode
#
#		if ${sponsornode} = ${mynodename}
#		this is the first node
#
#	Obtain and make proper entry for did driver in
#	/etc/name_to_major. If first node calculate best value;
#	if subsequent node, query sponsor for value to use.
#	Update /etc/minor_perm.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
configure_did_entry()
{
	typeset -r sponsornode=$1

	typeset -r file=${SC_BASEDIR}/etc/name_to_major
	typeset -r perm_file=${SC_BASEDIR}/etc/minor_perm
	typeset -r driver=did

	integer -r SC_MAX_MAJOR_NUMBER=16383	# Maximum major number allowed
	integer -r default_major=300
	integer -r delta=15
	integer  highest_major=
	integer  preferred_major=
	integer  sponsor_major=

	typeset sponsor_did_major=
	typeset status=

	# check args
	if [[ $# -ne 1 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to configure_did_entry()')\n" "${PROG}" >&2
		return 1
	fi

	# see if did driver entry already exists in name_to_major
	sponsor_did_major=$(grep -w ${driver} ${file}) 2>/dev/null
	if [[ -z ${sponsor_did_major} ]]; then
	# no did entry

		# obtain major number to use
		printf "$(gettext 'Setting the major number for the \"%s\" driver ... ')" ${driver} | logmsg
		# am I first node?
		if [[ "${sponsornode}" = ${mynodename} ]]; then
			# yes, am first node

			# figure out preferred major number: either default or the
			# highest number currently in use plus ${delta}, whichever is greater
			# this number is logically guaranteed not to be in use

			# find highest major number in use
			sort -nrk 2,2 ${file} | read ignore highest_major

			preferred_major=$((${highest_major} + ${delta}))
			if [[ ${preferred_major} -lt ${default_major} ]]; then
				preferred_major=${default_major}
			fi

			# is preferred_major too big?
			if [[ ${preferred_major} -gt ${SC_MAX_MAJOR_NUMBER} ]]; then
				# no available major to use:  FATAL
				printf "%s\n" ${SC_FAILED} | logmsg
				printf "$(gettext '%s: no available major number for \"%s\" driver')\n" ${PROG} ${driver} | logerr
				return 1
			fi

		else
		# not first node; request major from sponsor

			printf "\n$(gettext 'Obtaining the major number for the \"%s\" driver from \"%s\" ... ')" ${driver} ${sponsornode} | logmsg

			sponsor_did_major=$(scrconf -d ${driver} -N ${sponsornode} 2>&1)
			# scrconf -d -N returns its value on stderr, not stdout
			if [[ $? -ne 0 ]]; then
				# no response from sponsor: FATAL
				printf "%s\n" ${SC_FAILED} | logmsg
				printf "$(gettext '%s:  unable to get \"%s\" driver major number from \"%s\"')\n" ${PROG} ${driver} ${sponsornode} | logmsg
				return 1
			fi
			preferred_major=$(echo ${sponsor_did_major} | awk -F',' '{print $1}')
			if [[ ${preferred_major} -eq -1 ]]; then
				# did driver not registered on sponsor: FATAL
				printf "%s\n" ${SC_FAILED} | logmsg
				printf "$(gettext '%s: \"%s\" driver not registered on \"%s\" ')\n" ${PROG} ${driver} ${sponsornode} | logerr
				return 1
			fi
			grep -w ${preferred_major} ${file} > /dev/null 2>&1
			if [[ $? -eq 0 ]]; then
				# preferred_major not available on new node: FATAL
				printf "%s\n" ${SC_FAILED} | logmsg
				printf "$(gettext '%s: major number \"%s\" already in use on \"%s\". Unable to add did driver.')\n" ${PROG} ${preferred_major} ${mynodename} | logerr
				return 1
			fi
		fi
		# now have preferred_major

		# make entry in /etc/name_to_major file
		echo "${driver} ${preferred_major}" >> ${file}

		# make entry in /etc/minor_perm file based on pattern
		# of 'st' devices (if present)
		modestr=$(grep "^st:" ${perm_file} | awk '{printf "%s %s %s", $2, $3, $4}')
		if [[ -n "${modestr}" ]]; then
			echo "${driver}:*,tp ${modestr}" >> ${perm_file}
		fi

		# reconfig reboot will perform registrations and create all dev links
		printf "%s\n" ${SC_DONE} | logmsg
		printf "$(gettext '\"%s\" driver major number set to %d')\n" ${driver} ${preferred_major} | logmsg

	else
	# did entry already exists

		# if I'm first node just accept existing entry
		# if I'm not first node make sure entry matches my sponsor node
		if [[ "${sponsornode}" != ${mynodename} ]]; then
			printf "$(gettext 'Verifying the major number for the \"%s\" driver with \"%s\" ... ')" ${driver} ${sponsornode} | logmsg

			# save value of my existing entry
			preferred_major=$(echo ${sponsor_did_major} | awk '{print $2}')

			# and ask the sponsor for its entry
			sponsor_did_major=$(scrconf -d ${driver} -N ${sponsornode} 2>&1)
			# scrconf -d -N returns its value on stderr, not stdout
			if [[ $? -ne 0 ]]; then
				# no response from sponsor: FATAL
				printf "%s\n" ${SC_FAILED} | logmsg
				printf "$(gettext '%s:  unable to compare \"%s\" driver major number with \"%s\"')\n" ${PROG} ${driver} ${sponsornode} | logmsg
				return 1
			fi
			sponsor_major=$(echo ${sponsor_did_major} | awk -F',' '{print $1}')
			if [[ ${sponsor_major} -eq -1 ]]; then
				# did driver not registered on sponsor: FATAL
				printf "%s\n" ${SC_FAILED} | logmsg
				printf "$(gettext '%s: \"%s\" driver not registered on \"%s\" ')\n" ${PROG} ${driver} ${sponsornode} | logerr
				return 1
			fi
			if [[ ${sponsor_major} -ne ${preferred_major} ]]; then
				# existing self entry does not match entry on sponsor: FATAL
				printf "%s\n" ${SC_FAILED} | logmsg
				printf "$(gettext '%s: existing entry for \"%s\" driver differs from major number in use on \"%s\" ')\n" ${PROG} ${driver} ${sponsornode} | logerr
				return 1
			fi
			printf "%s\n" ${SC_DONE} | logmsg

		fi
	fi # check for did entry in /etc/name_to_major
	return 0
}

#####################################################
#
# check_node_status
#
#	Wait for the node to come up. This function
#	takes the node name as input and then waits for 
#	the node to come up. 
#	
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
check_node_status()
{
	typeset -r node=$1
	typeset cmd

	cmd="scrconf -x 10 -N ${node}"
	${PRINTDEBUG} ${cmd}
	${cmd} >${tmperrs} 2>&1
	let result=$?
	
	if [[ ${result} -eq ${SC_SCCONF_ETIMEDOUT} ]]; then
		printf "\n" | logmsg
		printf "$(gettext 'Current time - %s')\n" "$(date)" | logmsg
		printf "$(gettext 'Waiting for node \"%s\" to come up ...')" "${node}" | logmsg	
		cmd="scrconf -x ${SC_WTIMEOUT} -N ${node}"
		${PRINTDEBUG} ${cmd}
		${cmd} >${tmperrs} 2>&1
		let result=$?
		if [[ ${result} -eq ${SC_SCCONF_ETIMEDOUT} ]]; then
			printf "$(gettext 'timed out')\n" | logmsg
		elif [[ ${result} -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
		else
			printf "$(gettext 'done')\n" | logmsg
			printf "$(gettext 'Current time - %s')\n" "$(date)" | logmsg
		fi
	fi
	if [[ ${result} -ne 0 ]] &&
		[[ ${result} -ne ${SC_SCCONF_ETIMEDOUT} ]]; then
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Failed communications with \"%s\"')\n" "${PROG}" "${node}" | logerr
	fi
	if [[ ${result} -ne 0 ]]; then
		return 1
	fi
	return 0
}

#####################################################
#
# hosts_config
#
#       Add cluster node entries to the /etc/inet/hosts
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
hosts_config()
{
        printf "$(gettext 'Adding clusternode entries to /etc/inet/hosts ... ')" | logmsg
        ${SC_UPDATE_HOSTS} 2>${tmperrs}
        if [[ $? -ne 0 ]]; then
                printf "%s\n" ${SC_FAILED} | logmsg
                if [[ -s "${tmperrs}" ]]; then
                        cat ${tmperrs} | logerr
                fi
                printf "$(gettext '%s:  Unable to update %s file')\n" "${PROG}" "hosts" | logerr
                return 1
        fi

        printf "%s\n" ${SC_DONE} | logmsg
}

#####################################################
#
# ntp_config
#
#	Setup a default NTP configuration.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
ntp_config()
{
	typeset -r file=${SC_BASEDIR}/etc/inet/ntp.conf

	# See if /etc/inet/ntp.conf already exists
	printf "$(gettext 'Verifying that NTP is configured ... ')" | logmsg
	if [[ -f ${file} ]]; then
		printf "%s\n" ${SC_DONE} | logmsg
		return 0
	fi
	printf "%s\n" ${SC_DONE} | logmsg

	# If ntp.conf does not exist, copy ntp.cluster to ntp.conf.cluster
	printf "$(gettext 'Initializing NTP configuration ... ')" | logmsg
	cp ${SC_BASEDIR}/etc/inet/ntp.cluster ${file}.cluster 2>${tmperrs}
	if [[ $? -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Unable to install a default %s file')\n" "${PROG}" "ntp.conf.cluster" | logerr
		return 1
	fi

        ${SC_UPDATE_NTP} initialize 2>${tmperrs}
        if [[ $? -ne 0 ]]; then
                printf "%s\n" ${SC_FAILED} | logmsg
                if [[ -s "${tmperrs}" ]]; then
                        cat ${tmperrs} | logerr
                fi
                printf "$(gettext '%s:  Unable to initialize %s file')\n" "${PROG}" "ntp.conf.cluster" | logerr
                return 1
        fi

	printf "%s\n" ${SC_DONE} | logmsg

	return 0
}

#####################################################
#
# remote_config
#
#       Configure system files on other cluster nodes.
#               - Add peers to /etc/inet/ntp.conf.cluster
#               - Add nodes to /etc/inet/hosts
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
remote_config()
{
        typeset -r infrafile=${SC_CONFIG}
        typeset nodelist="$(sed -n 's/^cluster\.nodes\.[1-9][0-9]*\.name[	]\(.*\)/\1/p' ${infrafile})"
        typeset node
	typeset cmdargs
	typeset cmd
        for node in ${nodelist}
        do
                if [[ "${node}" == "${mynodename}" ]];then
                        continue
                fi

		#
		# Make sure all services are up on the node
		#
		check_node_status "${node}" || return 1
                for cmdargs in "ntp.conf.cluster" "hosts"
                do
                        printf "$(gettext 'Updating file (\"%s\") on node %s ... ')" "${cmdargs}" "${node}" | logmsg
			if [[ "${cmdargs}" == "ntp.conf.cluster" ]]; then
				cmd="scrconf -N ${node} -s cmd=update_ntp"
			else 
				cmd="scrconf -N ${node} -s cmd=update_hosts" 	
			fi

                        ${PRINTDEBUG} ${cmd}
                        ${cmd} 2>${tmperrs}
                        let result=$?
                        if [[ ${result} -ne 0 ]]; then
                                printf "%s\n" ${SC_FAILED} | logmsg
                                if [[ -s "${tmperrs}" ]]; then
                                        cat ${tmperrs} | logerr
                                fi
                                printf "$(gettext '%s:  Failed to configure (\"%s\") on node %s')\n" "${PROG}" "${cmdargs}" "${node}"| logerr
                                return 1
                        fi
                        printf "%s\n" ${SC_DONE} | logmsg
                done
        done
}

#####################################################
#
# nsswitch_config
#
#       Ensure that the local "files" are refered before
#       remote queries for most databases in "nsswitch.conf"
#
#       Ensure that the "cluster" is set as the first switch
#       for "hosts" and "netmasks" databases in "nsswitch.conf"
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
nsswitch_config()
{
        typeset -r file=${SC_BASEDIR}/etc/nsswitch.conf
        typeset -r updfile=${file}.${PROG}.$$
        typeset db
        typeset numlines
        typeset db_files='
                        passwd
                        group
                        hosts
                        ipnodes
                        networks
                        protocols
                        rpc
                        ethers
                        netmasks
                        bootparams
                        publickey
                        automount
                        aliases
                        services
                        auth_attr
                        prof_attr
                        project
                        sendmailvars
                        '

        typeset db_cluster='
                        hosts
                        netmasks
                        '
        # Add the "cluster" switch
        printf "$(gettext 'Updating %s ... ')\n" "nsswitch.conf" | logmsg

        cp /dev/null ${updfile}
        while read line
        do
                dbline=${line%%:*}
                found=0
                switches=
                space=
                for db in $db_cluster
                do
                        if [[ "${db}" == "${dbline}" ]];then
                                found=1
                                switches="cluster"
                                space=" "
                                break
                        fi
                done
                for db in $db_files
                do
                        if [[ "${db}" == "${dbline}" ]];then
                                found=1
                                switches="${switches}${space}files"
                                break
                        fi
                done
                if [[ ${found} -eq 0 ]];then
                        echo "$line" >> ${updfile}
                        continue
                fi
                pointer=prefix
                prefix=
                suffix=
                for switch in ${line##*:}
                do
                        if [[ "${switch}" == cluster ]];then
                                continue
                        fi
                        if [[ "${switch}" == files ]];then
                                continue
                        fi
                        switches="${switches} ${switch}"
                done

		echo "#$line" >> ${updfile}

		# In Solaris 10, a new entry "nis" has been added to
                # ipnodes database in nsswitch.conf file. This is causing
                # hangs when NIS is down. The fix for this is, to put
                # TRYAGAIN=0 rule next to nis entry. In addition, keep
                # "files" before "nis" just like hosts database.

		if [[ "${db}" == "ipnodes" ]] && [[ "${SC_OS_VERSION}" = "5.10" ]];then
                        space=" "
                        ipconfig=${line}

                        # Get the string that's in square brackets which 
			# is next to nis
                        str=$(expr "$ipconfig" : ".*nis[ ]*\[\(.*\)\] *")

                        # If string is null means there is no status next
                        # to nis, update the status string with TRYAGAIN=0
                        try_str="TRYAGAIN=0"

                        if [[ -z $str ]]; then
                                new_ip="[$try_str]"
			new_ipconfig=`echo $ipconfig | sed "s/nis/nis $new_ip/"`
                        else
                                echo $str | grep -i TRYAGAIN >/dev/null 2>&1
                                if [[ $? -ne  0 ]]; then
                                        # TRYAGAIN is not there, update the
                                        # status string with TRYAGAIN=0
                                        new_ip=${try_str}${space}${str}
                                else
                                        # TRYAGAIN is there, so make sure that
                                        # value is zero
					new_ip=`echo $str | sed -e 's/TRYAGAIN=.*/TRYAGAIN=0/'`
                                fi
				new_ipconfig=`echo $ipconfig | sed "s/$str/$new_ip/"`

                        fi
                        echo ${new_ipconfig} >> ${updfile}

                else
			echo "${line}"|sed -e "s/\(.*:[    ]*\).*/\1${switches}/" >> ${updfile}
		fi

        done < ${file}

	# Make sure that there is a single "db" entry
	for db in $db_cluster
	do
		numlines=$(grep -c '^'${db}':[  ]' ${file} 2>/dev/null)
		case ${numlines} in
		0)      # No lines
			printf "$(gettext '%s: WARNING: %s does not include a \"%s\" entry')\n" "${PROG}" "${file}" "${db}" |logmsg
			
			# Add a new line which includes both the files and cluster switches
			space=" "
			switches="cluster${space}files${space}nis"
			echo "${db}:   ${switches}" >> ${updfile}
			;;

		1)      # One line is good
			;;

		*)      # More than one line is not good
			printf "$(gettext '%s: WARNING: %s has %s \"%s\" entries!')\n" "${PROG}" "${file}" "${numlines}" "${db}" |logmsg
			;;
		esac
	done

	for db in $db_files
	do
		numlines=$(grep -c '^'${db}':[  ]' ${file} 2>/dev/null)
		case ${numlines} in
		0|1)    # No lines or One line
			;;
	
		*)      # More than one line is not good
			printf "$(gettext '%s: WARNING: %s has %s \"%s\" entries!')\n" "${PROG}" "${file}" "${numlines}" "${db}" |logmsg
			;;
		esac
	done

        # Report errors
        if [[ $? -ne 0 ]]; then
                printf "%s\n" ${SC_FAILED} | logmsg
                printf "$(gettext '%s:  Failed to update %s')\n" "${PROG}" "${file}" | logerr
                return 1
        fi

        cp ${updfile} ${file} || return 1
        rm -f ${updfile}

        # Done
        printf "%s\n" ${SC_DONE} | logmsg

        return 0
}


#####################################################
#
# ipmp_config
#
#       The purpose of this function is to create single adapter
#       IP Multipathing groups on all configured public adapters.
#
#       If a group name is already configured on an adapter no change
#       is made. Otherwise a group name "sc_ipmp<number>" is assigned.
#
#       This function may modify one or more /etc/hostname.<if> files.
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
ipmp_config()
{
        # check arg
        if [[ $# != 0 ]]  then
                printf "%s:  $(gettext 'Internal error - bad call to ipmp_config()')\n" ${PROG} >&2
                return 2
        fi
        typeset -r runtype=$1

        typeset ip_script=${SC_SCLIBDIR}/${SC_IP_PERL}
        typeset adapter                 # List of Adapters
        typeset ipmpgroup               # List of IPMP group for Adapter
        typeset adapter_config          # List of adapter_config for Adapter
        typeset testaddr                # List of Test-address for Adapter
        typeset address                 # List of Base-IP-Address for Adapter
        set -A ipmpgroup
        set -A adapter
        set -A adapter_config
        set -A testaddr
        set -A address
        integer testaddr_optional=$(${ip_script} is_testaddr_optional)
        integer i
        integer j
        integer k
        integer found
        typeset entry
        typeset entry_1
        typeset entry_2
        typeset read_flag
        typeset okay
        typeset -l lnetmask
        integer testaddr_flag
        integer deprecated_flag
        integer count_testaddr
        integer wordcount
        integer read_addr
        integer incomplete
        integer config_errors

        if [[ ${testaddr_optional} -eq 0 ]];then
                return 0
        fi

        printf "\n$(gettext 'Configuring IP Multipathing groups in "%s" files')\n" "${SC_BASEDIR}/etc/hostname.<adapter>" | logmsg

        adapter_list=$(cd ${SC_BASEDIR}/etc;ls hostname.*|sed -e "s/hostname\.//g")

        let i=0
        for adp in $adapter_list
        do
                adp_ip=$(${ip_script} get_adapter_ip ${adp})
                if [[ "${adp_ip}" == "0.0.0.0" ]];then
                        printf "\n\n$(gettext 'Invalid adapter configuration file "%s"')\n" "${SC_BASEDIR}/etc/hostname.${adp}" | logmsg
                fi
                adapter[i]=${adp}
                adapter_ip[i]=${adp_ip}
                (( i += 1 ))
        done

        let j=0
        let i=0

        # Parse /etc/hostname.<adapter> files
        let i=0
        while [[ ${i} -lt ${#adapter[*]} ]]
        do
                let testaddr_flag=0
                let deprecated_flag=0
                let count_testaddr=0
                let wordcount=0
                read_flag="addr"
                let read_addr=0
                adapterconfig=$(nawk -F# '{print $1}' ${SC_BASEDIR}/etc/hostname.${adapter[i]} 2> /dev/null)
                for word in ${adapterconfig}
                do
                        (( wordcount += 1 ))
                        case ${word} in
                        group)
                                read_flag="group"
                                ;;
                        addif)
                                if [[ $testaddr_flag -eq 1 ]] ;then
                                        ((count_testaddr += 1))
                                        if [[ $deprecated_flag -eq 1 ]] ; then
                                                testaddr[i]=${address[i]}
                                                testaddr_ip[i]=$(${ip_script} get_ip_in_hosts ${testaddr[i]})
                                        else
                                                adapter_config[i]="wrong"
                                                printf "\n%s:  $(gettext 'Configuration error in "%s".')\n" ${PROG} "${SC_BASEDIR}/etc/hostname.${adapter[i]}" |logerr
                                                printf "%s:  $(gettext 'test address (-failover) must be deprecated.')\n" ${PROG} | logerr
                                        fi
                                fi
                                let testaddr_flag=0
                                let deprecated_flag=0
                                read_flag="addr"
                                ;;
                        -failover)
                                let testaddr_flag=1
                                ;;
                        deprecated)
                                let deprecated_flag=1
                                ;;
                        netmask|broadcast|destination|index|metric|mtu)
                                read_flag="skip"
                                ;;
                        \\|-*|up|down)
                                ;;
                        *)
                                case ${read_flag} in
                                group)
                                        ipmpgroup[i]=${word}
                                        read_flag=
                                        ;;
                                addr)
                                        address[i]=${word}
                                        address_ip=$(${ip_script} get_ip_in_hosts ${testaddr[i]})
                                        if [[ -z $address_ip ]];then
                                                printf "\n%s:  $(gettext 'Configuration error in "%s".')\n" ${PROG} "${SC_BASEDIR}/etc/hostname.${adapter[i]}" |logerr
                                                printf "%s:  $(gettext 'Hostname"%s" is not in "%s".')\n" ${PROG} ${word} "${SC_BASEDIR}/etc/hosts" | logerr
                                                adapter_config[i]="wrong"
                                        fi

                                        read_flag=
                                        let read_addr=1
                                        ;;
                                skip)
                                        read_flag=
                                        ;;
                                *)
                                        ;;
                                esac
                                if [[ ${read_addr} -eq 0 ]];then
                                        read_flag="addr"
                                fi
                                ;;
                        esac
                done
                if [[ $testaddr_flag -eq 1 ]] ;then
                        ((count_testaddr += 1))
                        if [[ $deprecated_flag -eq 1 ]] ; then
                                testaddr[i]=${address[i]}
                                testaddr_ip[i]=$(${ip_script} get_ip_in_hosts ${testaddr[i]})
                       	fi 
                fi

                case ${wordcount}  in
                0)
                        adapter_config[i]="missing";
                        ;;
                1)
                        adapter_config[i]="needed";
                        ;;
                *)
                        case ${count_testaddr} in
                        0|1)
                                if [[ "${adapter_config[i]}" != "wrong" ]];then
                                        if [[ -z "${ipmpgroup[i]}" ]];then
                                                adapter_config[i]="addgroup"
                                        else
                                                adapter_config[i]="complete"
                                        fi
                                fi
                                ;;
                        *)
                                adapter_config[i]="wrong"
                                printf "\n%s:  $(gettext 'Configuration error in"%s".')\n" ${PROG} "${SC_BASEDIR}/etc/hostname.${adapter[i]}" | logerr
                                printf "%s:  $(gettext 'Only one "-failover" address can be configured on an adapter.')\n" ${PROG} | logerr
                                ;;
                        esac
                        ;;
                esac
                if [[ -n "${readflag}" ]] then
                                adapter_config[i]="wrong"
                fi

        (( i += 1 ))
        done

        if [[ "${runtype}" == "check" ]] &&
           [[ -n "${SC_UPGD_TST_IF}" ]] ;then
                printf "\n\n$(gettext 'Checking "-S" option IP Multipathing test addresses')\n" ${PROG} | logmsg
        fi
        let group_no=0
        let i=0
        while [[ ${i} -lt ${#adapter[*]} ]]
        do
                if [[ "${adapter_config[i]}" == "complete" ]];then
                        printf "\n$(gettext 'IP Multipathing already configured in "%s".')\n" "${SC_BASEDIR}/etc/hostname.${adapter[i]}" | logmsg
                        (( i += 1 ))
                        continue
                fi

                found=1
                group="sc_ipmp${group_no}"
                while [[ ${found} -eq 1 ]]
                do
                        found=0
                        j=0
                        while [[ ${j} -lt ${#adapter[*]} ]]
                        do
                                group="sc_ipmp${group_no}"
                                if [[ "${group}" == "${ipmpgroup[j]}" ]];then
                                        found=1
                                        (( group_no += 1 ))
                                        break
                                fi
                                (( j += 1 ))
                        done
                done

                ipmpgroup[i]=${group}
                (( group_no += 1 ))

                case ${adapter_config[i]} in
                "missing")
                        printf "\n$(gettext 'Creating "%s".')\n" "${SC_BASEDIR}/etc/hostname.${adapter[i]}" | logmsg
                        echo "${adapter_ip[i]} deprecated -failover netmask + broadcast + group ${ipmpgroup[i]} up" > ${SC_BASEDIR}/etc/hostname.${adapter[i]} || return 1
                        ;;
                "needed")
                        printf "\n$(gettext 'Updating "%s".')\n" "${SC_BASEDIR}/etc/hostname.${adapter[i]}" | logmsg
                        echo "${address[i]} netmask + broadcast + group ${ipmpgroup[i]} up" > ${SC_BASEDIR}/etc/hostname.${adapter[i]} || return 1
                        ;;
                "addgroup")
                        printf "\n$(gettext 'Updating "%s".')\n" "${SC_BASEDIR}/etc/hostname.${adapter[i]}" | logmsg
                        cat ${SC_BASEDIR}/etc/hostname.${adapter[i]}
                        echo "group ${ipmpgroup[i]}" >> ${SC_BASEDIR}/etc/hostname.${adapter[i]} || return 1
                        ;;
                        *)
                        ;;
                esac
        (( i += 1 ))
        done
        return 0
}

#####################################################
#
# set_eeprom
#
#	Sets EEPROM parameters
#	by issuing command :
#
#             eeprom "local-mac-address?"=true
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
set_eeprom()
{
	typeset value

	printf "$(gettext 'Ensure that the %s parameter "%s" is set to "%s" ... ')" "EEPROM" "local-mac-address?" "true" | logmsg
	value=$(eeprom "local-mac-address?")
	value=$(IFS=\=; set -- ${value}; echo $2)

	if [[ ${value} = "true" ]];then
		printf "%s\n" ${SC_DONE} | logmsg
		return 0
	fi

	eeprom "local-mac-address?"=true
	if [[ $? -ne 0 ]];then
		printf "%s\n" ${SC_FAILED} | logmsg
		printf "$(gettext '%s:  Failed to change the "%s" setting to "%s".')\n" "${PROG}" "local-mac-address?" "true" | logerr
		return 1
	fi

	printf "%s\n" ${SC_DONE} | logmsg
	printf "$(gettext 'The "%s" parameter setting has been changed to "%s".')\n" "local-mac-address?" "true" | logmsg
	return 0

}

#####################################################
#
# powerm_off
#
#	Turn off power management.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
powerm_off()
{
	typeset -r file=${SC_BASEDIR}/etc/power.conf
	typeset suffix

	# Don't let /etc/init.d/sysid.sys ask us to turn it on again
	rm -f ${SC_BASEDIR}/etc/.PM_RECONFIGURE 2>/dev/null

	# See if /etc/power.conf exists
	printf "$(gettext 'Verifying that power management is NOT configured ... ')" | logmsg
	if [[ ! -f ${file} ]]; then
		printf "%s\n" ${SC_DONE} | logmsg
		return 0
	fi
	printf "%s\n" ${SC_DONE} | logmsg

	# If it does exist, re-name it
	suffix=$(date +'%'m'%'d'%'y'%'H'%'M'%'S)
	printf "$(gettext 'Unconfiguring power management ... ')" | logmsg
	mv ${file} ${file}.${suffix} 2>${tmperrs}
	if [[ $? -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  Unable to disable power management')\n" "${PROG}" | logerr
		return 1
	fi
	printf "%s\n" ${SC_DONE} | logmsg

	# More configuration required
	printf "$(gettext '%s has been renamed to %s')\n" "${file}" "${file}.${suffix}" | logmsg
	printf "$(gettext 'Power management is incompatible with the HA goals of the cluster.')\n" | logmsg
	printf "$(gettext 'Please do not attempt to re-configure power management.')\n" | logmsg

	return 0
}

#####################################################
#
# router_disable
#
#	Ensure that node will not act as a router.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
router_disable()
{
	typeset -r file=${SC_BASEDIR}/etc/notrouter

	# See if /etc/notrouter already exists
	printf "$(gettext 'Ensure network routing is disabled ... ')" ${file} | logmsg
	if [[ ! -f ${file} ]]; then
		(umask 022; touch ${file} 2>${tmperrs})
		if [[ $? -ne 0 ]]; then
			printf "%s\n\n" ${SC_FAILED} | logmsg
			if [[ -s "${tmperrs}" ]]; then
				cat ${tmperrs} | logerr
			fi
			printf "$(gettext '%s:  Unable to disable network routing')\n" "${PROG}" | logerr
			return 1
		fi
		printf "%s\n" ${SC_DONE} | logmsg

		# Explanation
		printf "$(gettext 'Network routing has been disabled on this node by creating %s.')\n" "${file}" | logmsg
		printf "$(gettext 'Having a cluster node act as a router is not supported by Sun Cluster.')\n" | logmsg
		printf "$(gettext 'Please do not re-enable network routing.')\n" | logmsg
	else
		printf "%s\n" ${SC_DONE} | logmsg
	fi

	return 0
}

#####################################################
#
# print_errorexit_msg [beep]
#
#	beep				beep
#
#	Print the error exit message
#
#	This function always returns 0.
#
#####################################################
print_errorexit_msg()
{
	typeset beep=$1

	printf "\n" >&2
	printf "$(gettext '%s:  %s did NOT complete successfully!')\n\n" "${PROG}" "${PROG}" >&2
	if [[ -s "${install_log}" ]]; then
		printf "\n" >>${install_log}
		printf "$(gettext '%s:  %s did NOT complete successfully!')\n\n" "${PROG}" "${PROG}" >>${install_log}
	fi

	# beep
	if [[ -n "${beep}" ]]; then
		echo "\a\c"
	fi

	return 0
}

#####################################################
#
# print_release [verbose] [releasefile]
#
#	verbose				print verbose
#	releasefile			use this release file			
#
#	Print release information
#
#	This function always returns 0.
#
#####################################################
print_release()
{
	typeset -r verbose=$1
	typeset releasefile=$2

	typeset pvers				# dot.release PRODVERS
	typeset pvers_build			# dot.release PRODVERS_BUILD
	typeset pvers_string			# dot.release PRODVERS_STRING

	typeset -r rtregdir=${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg
	typeset -r gdsdatadir=${SC_BASEDIR}/usr/cluster/lib/rgm/gdsdata

	typeset pkgname
	typeset pkgversion
	typeset pkgpatches
	typeset pkglist
        typeset rtrfiles
	typeset tmplist
	typeset foo

	#
	# If not installed, print message when verbose.
	# It is not an error to not be installed.
	#
	if [[ -z "${releasefile}" ]] &&
	    [[ ! -f ${SC_DOT_RELEASE} ]]; then
		if [[ -n "${verbose}" ]]; then
			printf "$(gettext '%s is not installed')\n" "${SC_PRODUCT}"
		fi
		return 0
	fi
	if [[ -z "${releasefile}" ]]; then
		releasefile=${SC_DOT_RELEASE}
	fi

	#
	# Read the dot.release file looking for PRODVERS* values.
	# Comments (#) may only begin in column zero.
	#
        while read line
        do
                case "${line}" in
                PRODVERS=*)
                        pvers="$(expr "${line}" : 'PRODVERS=\(.*\)')"
			pvers=$(set -- ${pvers};  echo $1)
			;;

		PRODVERS_BUILD=*)
                        pvers_build="$(expr "${line}" : 'PRODVERS_BUILD=\(.*\)')"

			# pvers_build includes '_', instead of space
			foo=
			for word in ${pvers_build}
			do
				if [[ -z "${foo}" ]]; then
					foo=${word}
				else
					foo=${foo}_${word}
				fi
			done
			pvers_build=${foo}
			;;

		PRODVERS_STRING=*)
                        pvers_string=$(expr "${line}" : 'PRODVERS_STRING=\(.*\)')
			;;
		esac
	done < ${releasefile}

	# If not verbose, simply print the version and build as "one word"
	if [[ -z "${verbose}" ]]; then
		if [[ -z "${pvers_build}" ]]; then
			echo ${pvers}
		else
			echo ${pvers}_${pvers_build}
		fi

		# If not verbose, we are done
		return 0
	fi

	#
	# If we are here, we are verbose.
	#
	# The complete PRODVERS_STRING is printed, followed by a list
	# of all installed framework and data services packages.  All
	# packages are printed with their versions and patch revs.
	#

	# Get the list of all framework packages
	pkglist=$(cat ${SC_DOT_ORDER})

	# List of data service RTR files
	rtrfiles=
	if [[ -d ${rtregdir} ]]; then
        	rtrfiles=$(ls ${rtregdir}/SUNW.* 2>/dev/null)
	fi

	# Add SunPS dummy RTR files to list, if gdsdata directory is present  
	if [[ -d ${gdsdatadir} ]]; then
        	rtrfiles="${rtrfiles} $(ls ${gdsdatadir}/SUNW.* 2>/dev/null)"
	fi

	for file in ${rtrfiles}
	do
               	# Get the list of data service packages and add to package list
               	pkglist="${pkglist} $(get_service_pkglist ${file})"
	done

	# Add data services Answerbook
	pkglist="${pkglist} SUNWscdab"

	# Remove possible duplicates
	for pkgname in ${pkglist}
	do
		for foo in ${tmplist}
		do
			if [[ "${pkgname}" == "${foo}" ]]; then
				continue 2
			fi
		done
		tmplist="${tmplist} ${pkgname}"
	done
	pkglist="${tmplist}"

	# print the PRODVERS_STRING
	echo ${pvers_string}

	# for each installed package, print version and patch list
	for pkgname in ${pkglist}
	do
		# If not installed, continue
		pkginfo ${pkgname} >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			continue
		fi

		# Get version and patchlist
		pkgversion=$(pkgparam ${pkgname} VERSION 2>/dev/null)
		pkgpatches=$(pkgparam ${pkgname} PATCHLIST 2>/dev/null)

		# Print package name, version, and patchlist
		printf "%-14s %s" "${pkgname}:" "${pkgversion}"
		if [[ -n "${pkgpatches}" ]]; then
			echo ", ${pkgpatches}"
		else
			echo
		fi
	done

	return 0
}

#####################################################
#
# create_zipfile dir zipfile use_gzip [logfile] [excludefile]
#
#	dir				directory to zip
#	zipfile				name of the zipfile
#	use_gzip			set to 1 if it is okay to use gzip
#	logfile				name of the log file
#	excludefile			name of the exclude file
#
#	This function is used to create either a tar file or a
#	gzipped tar file of the contents of the given "dir".
#	If "use_gzip" is set to "1", a gzipped tar file is created.
#	otherwise, a tar file is created.
#
#	If "logfile" is given, stdout/stderr of all commands is appended
#	to the "logfile".   Otherwise, it is dropped.
#
#	If an "excludefile" is given, it is used with the "X" option to
#	the tar(1) command.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
create_zipfile()
{
	typeset dir=${1}
	typeset zipfile=${2}
	typeset use_gzip=${3}
	typeset logfile=${4}
	typeset excludefile=${5}

	# Remove the zip file, if it already exists
	rm -f ${zipfile} 

	# Log file
	if [[ -z "${logfile}" ]]; then
		logfile=/dev/null
	fi

	# Create the zip/tar
	(
		# change dir
		cmd="cd ${dir}"
		echo ${cmd} >>${logfile}
		eval ${cmd} >>${logfile} 2>&1 || return 1

		# exclude file
		if [[ -n "${excludefile}" ]]; then
			cmd="tar cXf ${excludefile}"
		else
			cmd="tar cf"
		fi

		# zip or tar
		if [[ "${use_gzip}" == 1 ]]; then
			cmd="${cmd} - . | gzip - >${zipfile}"
		else
			cmd="${cmd} ${zipfile} ."
		fi

		# Run the command
		echo ${cmd} >>${logfile}
		eval ${cmd} >>${logfile} 2>&1 || return 1

		# done
		return 0
	)

	return $?
}


#####################################################
#
# scrconf_static [scrconf options]
#
#	scrconf_options		options to scrconf
#	
#	Run the static version of the scrconf command
#	from the Tools/lib directory (/usr/cluster/lib/sc
#	in case of pre-installed packages) with the 
#	provided options.
#	
#	This function always returns the exit code of scrconf 
#	or 1 if scrconf cannot be run.
#
#####################################################
scrconf_static()
{
	typeset -r scrconf_options="$*"
	
    (
	typeset tools_cmd=${SC_SCLIBDIR}/scrconf
	typeset installed_cmd=${SC_BASEDIR}/usr/cluster/lib/sc/scrconf_static
	
	if [[ -x ${tools_cmd} ]]; then
		eval ${tools_cmd} ${scrconf_options}
		return $?
	elif [[ -x ${installed_cmd} ]]; then
		eval ${installed_cmd} ${scrconf_options}
		return $?
	else
		return 1
	fi
    )
	return $?
}

#####################################################
#
# verify_M_option() patch_options
#
#	Checks for basic sanity in provided patch 
#	options. Checks for both malformed options
#	as well as missing patch directory etc.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#	
#####################################################
verify_M_option()
{
	typeset patch_options=$1

	typeset patchdir
	typeset patchfile
	typeset bad_entry
	integer found

	patchdir="$(print_subopt_values "${patch_options}" "patchdir")"
	if [[ -z ${patchdir} ]]; then
		printf "$(gettext '%s:  No patch directory specified with -M')\n" "${PROG}" | logerr
		return 1
	fi

	bad_entry="$(echo ${patchdir} | /usr/bin/nawk '{print $2}')"
	if [[ -n "${bad_entry}" ]]; then
		printf "$(gettext '%s:  Multiple patch directories specified with -M')\n" "${PROG}" | logerr
		return 1
	fi

	if [[ ! -d ${patchdir} ]]; then
		printf "$(gettext '%s:  Can not access patch directory \"%s\"')\n" "${PROG}" "${patchdir}" | logerr
		return 1
	fi

	patchfile="$(print_subopt_values "${patch_options}" "patchlistfile")"

	if [[ -z "${patchfile}" ]]; then
		return 0
	fi
	
	bad_entry=
	bad_entry="$(echo ${patchfile} | /usr/bin/nawk '{print $2}')"
	if [[ -n "${bad_entry}" ]]; then
		printf "$(gettext '%s:  Multiple patch list files specified with -M')\n" "${PROG}" | logerr
		return 1
	fi

	if [[ ! -r ${patchdir}/${patchfile} ]]; then
		printf "$(gettext '%s:  Can not access patch list file \"%s\"')\n" "${PROG}" "${patchdir}/${patchfile}" | logerr
		return 1
	fi

	bad_entry=
	bad_entry="$(cat ${patchdir}/${patchfile} | /usr/bin/nawk '{print $2}')"				
	if [[ -n "${bad_entry}" ]]; then
		printf "$(gettext '%s:  More than one entry per line specified in patch list file \"%s\"')')\n" "${PROG}" "${patchdir}/${patchfile}" | logerr
		return 1
	fi

	return 0
}

#####################################################
#
# autoclient_fs() cdimagebasedir mynodename
#
#	cdimagebasedir			location of cdimage
#	mynodename			the name of this node
#
#	This function looks up the filesystem location
#	of "cdimagebasedir".  And, upon success, this function prints
#	two values;   the first is the name of the machine on which
#	the filesystem is located;  and, the second is the name of
#	the filesystem to NFS mount at JumpStart time.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
autoclient_fs()
{
	typeset -r cdimagebasedir=$1
	typeset -r mynodename=$2

	typeset autoscinstallhost=
	typeset autoscinstalldir=

	typeset foo
	typeset tmp1
	typeset tmp2

	if [[ $# -ne 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to autoclient_fs()')\n" ${PROG} >&2
		return 1
	fi

	#
	# Set the autoscinstallhost and the autoscinstalldir.
	# Verify that the ${cdimagebasedir} is either nfs or ufs
	# and that all directories immediately under ${cdimagebasedir}
	# are located on the same filesystem.
	#
	df -F ufs ${cdimagebasedir} >/dev/null 2>&1
	if [[ $? -eq 0 ]]; then

		# Get the device name portion
		tmp1=$(df -F ufs ${cdimagebasedir} | sed 's/[()]/ /g' | nawk '{ print $2 }')
		if [[ -z "${tmp1}" ]]; then
			printf "$(gettext '%s:  Cannot determine device name for \"%s\"')\n" "${PROG}" "${cdimagebasedir}" | logerr
			return 1
		fi

		# Make sure that at least next level dirs are on the same fs
		for foo in ${cdimagebasedir}/*
		do
			df -F ufs ${foo} >/dev/null 2>&1
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  \"%s\" is not in a ufs filesystem')\n" "${PROG}" "${foo}" | logerr
				return 1
			fi
			tmp2=$(df -F ufs ${foo} | sed 's/[()]/ /g' | nawk '{ print $2 }')
			if [[ -z "${tmp2}" ]]; then
				printf "$(gettext '%s:  Cannot determine device name for \"%s\"')\n" "${PROG}" "${foo}" | logerr
				return 1
			fi
			if [[ ${tmp1} != ${tmp2} ]]; then
				printf "$(gettext '%s:  \"%s\" and \"%s\" are not in the same filesystem')\n" "${PROG}" "${cdimagebasedir}" "${foo}" | logerr
				return 1
			fi
		done

		autoscinstallhost=${mynodename}
		autoscinstalldir=${cdimagebasedir}
	else
		# Get the host:filesystem portion
		df -F nfs ${cdimagebasedir} >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Unsupported filesystem type for \"%s\"')\n" "${PROG}" "${cdimagebasedir}" | logerr
			return 1
		fi
		tmp1=$(df -F nfs ${cdimagebasedir} | sed 's/[()]/ /g' | nawk '{ print $2 }')
		if [[ -z "${tmp1}" ]] || [[ "${tmp1}" != *:* ]]; then
			printf "$(gettext '%s:  Cannot determine nfs server for \"%s\"')\n" "${PROG}" "${cdimagebasedir}" | logerr
			return 1
		fi

		# Make sure that at least next level dirs are on the same fs
		cd ${cdimagebasedir}
		for foo in ${cdimagebasedir}/*
		do
			df -F nfs ${foo} >/dev/null 2>&1
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  \"%s\" is not in an nfs filesystem')\n" "${PROG}" "${foo}" | logerr
				return 1
			fi
			tmp2=$(df -F nfs ${foo} | sed 's/[()]/ /g' | nawk '{ print $2 }')
			if [[ -z "${tmp2}" ]] || [[ "${tmp2}" != *:* ]]; then
				printf "$(gettext '%s:  Cannot determine nfs server for \"%s\"')\n" "${PROG}" "${foo}" | logerr
				return 1
			fi
			if [[ ${tmp1} != ${tmp2} ]]; then
				printf "$(gettext '%s:  \"%s\" and \"%s\" are not in the same filesystem')\n" "${PROG}" "${cdimagebasedir}" "${foo}" | logerr
				return 1
			fi
		done

		# Split into host and filesystem name
		set -A foo $(IFS=: ; echo ${tmp1})

		# Install host
		autoscinstallhost=${foo[0]}

		#
		# Install dir
		#

		# set tmp1 to mounted directory
		tmp1=${foo[1]}

		# set tmp2 to ${cdimagebasedir} minus mount point directory
		tmp2=$(df -F nfs ${cdimagebasedir} | sed 's/[()]/ /g' | nawk '{ print $1 }')
		tmp2=$(echo ${cdimagebasedir} | sed -n 's#'${tmp2}'\(.*\)#\1#p')

		# combine tmp1 and tmp2 for directory to mount
		autoscinstalldir=${tmp1}${tmp2}
	fi

	if [[ -z "${autoscinstallhost}" ]] ||
	    [[ -z "${autoscinstalldir}" ]]; then
		printf "$(gettext '%s:  Unable to use \"%s\"')\n" "${PROG}" "${cdimagebasedir}" | logerr
		return 1
	fi

	# Print the two fields
	echo ${autoscinstallhost} ${autoscinstalldir}

	return 0

}


###################################################################
#
# check_versions cdimagebasedir [sponsorname]
#
#       cdimagebasedir          location of .cdtoc file
#       sponsorname             a cluster node name
#
#       Run versions checks to verify if installing these packages
#       will allow the node join existing cluster.
#
#       For the check to complete, an upgraded version of scadmd
#       must be reached on one of the cluster node. This command
#       will try to contact all the cluster names it knows about
#       (sponsor name passed as parameter plus local name list),
#       until it gets an explicit result (0 or 1).
#
#       It could be that for the first upgrade none of the cluster
#       nodes are running and upgraded version of scadmd. In this
#       case we will not be able to determine the node ability to
#       join the cluster back.
#
#       Returned values (match version check command semantic):
#
#               0 - node can join the cluster
#               1 - node cannot join the cluster
#               2 - could not determine the node ability to join
#
###################################################################
check_versions()
{

	typeset cmd
	typeset cluster_node
	integer result=3

	#
	# get arguments
	#
        typeset cdimagebasedir=$1
	typeset sponsorname=$2

	#
	# vp files from packages
	#
	typeset -r vp_file_dir="${SC_PACKAGESDIR}/SUNWscr/reloc/etc/cluster/vp/,${SC_PACKAGESDIR}/SUNWscu/reloc/usr/cluster/lib/vp/"

	#
	# we are supposed to try all cluster node names we know about until the command works
	#
	typeset -r infrafile=${SC_CONFIG}
	typeset nodelist="$(sed -n 's/^cluster\.nodes\.[1-9][0-9]*\.name[ 	]\(.*\)/\1/p' ${infrafile})"

	#
	# Check args
	#
	if [[ $# -ne 1 && $# -ne 2 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to check_versions()')\n" ${PROG} >&2
		return 1
	fi
	if [[ -z "${cdimagebasedir}" ]]; then
		printf "$(gettext '%s:  Internal error - bad call to check_versions()')\n" ${PROG} >&2
		return 2
	fi

	#
	# if node list empty and we have a sponsor node name
	#
	if [[ -z "${nodelist}" ]] && [[ -n "${sponsorname}" ]]; then
		nodelist="${sponsorname}"
	fi

	for cluster_node in ${nodelist}
	do
		#
		# Skip me
		#
		if [[ ${cluster_node} == ${mynodename} ]]; then
			continue
		fi

		#
		# prepare command
		#
		cmd="${SC_SCLIBDIR}/scversioncheck -c ${cluster_node} -d ${vp_file_dir}"

		#
		# invoke command
		#
		# version check command will print error messages on stderr
		#
		${cmd} 2>${tmperrs}
		result=$?

		#
		# if check succeeded in determining the node joinability status
		# then we are done !
		#
		if [[ ${result} -eq 0 ]] || [[ ${result} -eq 1 ]]; then
			break
		fi

		#
		# else...
		# log potential error messages
		#
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
	done

	#
	# print message if needed
	#
	if [[ ${result} -eq 1 ]]; then

		#
		# node cannot join the cluster
		# (the installation process will terminate)
		#
		printf "\n$(gettext '%s:  The version of the new Sun Cluster software being installed')\n" "${PROG}" >&2
		printf "$(gettext '%s:  is likely to be non compatible with the Sun Cluster software')\n" "${PROG}" >&2
		printf "$(gettext '%s:  already running on the cluster nodes.')\n" "${PROG}" >&2
		printf "$(gettext '%s:  If this is a non-rolling upgrade, the -O option may')\n" "${PROG}" >&2
		printf "$(gettext '%s:  be used to bypass version checking')\n" "${PROG}" >&2
		printf "$(gettext '%s:  (see scinstall(1M) and scversions(1M)).')\n" "${PROG}" >&2

	elif [[ ${result} -eq 2 ]]; then

		#
		# could not check whether or not the node can join
		# (the installation process will go through)
		#
		# that might be because it is the first rolling, and no scadmd server has been updated yet,
		# in this case we are supposed to continue !
		#
		printf "\n$(gettext '%s:  Could not determine whether or not the software being')\n" "${PROG}" >&2
		printf "$(gettext '%s:  installed will allow this node to join the cluster. ')\n" "${PROG}" >&2
	fi
	return ${result}
}


###################################################################
#
# checkpkgarch cdimagebasedir 
#
#       cdimagebasedir          location of .cdtoc file
#
#       Checks the architecture of the packages to be installed 
#	against that of the current system archictecture.  If all 
#	package architectures match that of the system, then success,
#	if not, failure.
#
#       Return:
#
#               zero - success
#               non-zero - failure
#
###################################################################
checkpkgarch() 
{

	integer i
	
	typeset src_arch
	typeset realcdimage
	typeset productdir
	typeset pkglist

	# get arguments
        typeset -r cdimagebasedir=$1
	
	#
	# Note that the cdimagebasedir may be given as either the
	# directory containing the .cdtoc we are looking for OR
	# the directory above that.  This call to find_cdimagebasedir()
	# will reset the cdimagebasedir to be the directory containing
	# our .cdtoc.
	#
	realcdimage=$(find_cdimagebasedir "${cdimagebasedir}" "${SC_PRODUCT}" "${SC_CLUSTER}") || return 1
	
	# get the name of the product directory
	productdir=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "dir") || return 1

	# get the list of packages
	pkglist="$(print_clustertoc ${productdir}/${SC_CLUSTERTOC} ${SC_CLUSTER} "packages")" || return 1

	
	# Check ARCH
	let i=0
	while [[ -n "${pkglist[i]}" ]]
	do
	 	# Check the ARCH of the package
		src_arch=$(pkgparam -d ${productdir} ${pkglist[i]} ARCH 2>/dev/null)
		if [[ "${src_arch}" != "all" &&
		    "${src_arch}" != *${SC_ARCH}* ]]; then
			printf "$(gettext '%s:  The distribution architecture (%s) does not match your system (%s)')\n" "${PROG}" "${src_arch}" "${SC_ARCH}" | logerr
			return 1
		fi
		((i += 1))
	done
	
	return 0
}

########################################################################
#
# checkforprereqs cdimagebasedir
#
#	cdimagebasedir	location of .cdtoc file
#
#	Checks for certain package dependencies.
#
#	Return:
#
#		zero - packages accounted for
#		non-zero - one or more missing packages
#
########################################################################
checkforprereqs()
{

	realcdimage=$(find_cdimagebasedir "${cdimagebasedir}" "${SC_PRODUCT}" "${SC_CLUSTER}") || return 1

	# get the name of the product directory
	productdir=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "dir") || return 1

	# Check for Sun Web Console
	checkfor_swc
	 exit1=$?

	# Check for JDMK
	checkfor_JDMK ${productdir}
	exit2=$?

	# Check for Common Agent Container
	checkfor_cacao ${productdir}
	exit3=$?

	if [[ ${exit1} -ne 0 || ${exit2} -ne 0 || ${exit3} -ne 0 ]]; then
		return 1
	fi

        return 0
}

########################################################################
#
# checkfor_swc
#
#	Checks to see if Sun Web Console is installed.
#
#	Return:
#	
#		zero - success
#		non-zero - failure
#
########################################################################
checkfor_swc()
{

	typeset rootarg=

        if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
                rootarg="-R ${SC_BASEDIR}"
        fi


	# Check to see if it is installed
	pkginfo ${rootarg} SUNWmconr 1>/dev/null 2>&1
	exit_code=$?

	# Check the basedir of SWC already installed
	pkg_basedir=$(pkgparam ${rootarg} SUNWmconr BASEDIR 2>/dev/null)

	if [[ ${exit_code} -ne 0 || ${pkg_basedir} != "/" ]]; then
		printf "$(gettext 'Sun Web Console is not currently installed.')\n" | logerr
  		 printf "$(gettext 'Please refer to the documentation for installation instructions.')\n\n" | logerr
                return 1
	fi

	return 0
}

########################################################################
#
# checkfor_JDMK productdir
#
#	productdir	the name of the product directory
#
#	Checks to see if JDMK is in the package list.  If it is
#	then that means that it is bundled with SunCluster.  If
#	not, then it is separate and needs to be checked for.
#
#	Return:
#	
#		zero - JDMK is in package list, or is installed already
#		non-zero - JDMK is not installed
#
########################################################################
checkfor_JDMK()
{

	typeset rootarg=

        if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
                rootarg="-R ${SC_BASEDIR}"
        fi

	# Check to see if it is in the .order file
	grep SUNWjdmk-runtime ${productdir}/${SC_ORDER} 1>/dev/null 2>&1
	in_order_file=$?

	if [[ ${in_order_file} -eq 0 ]]; then
		# is in .order file, so scinstall will install it
                return 0
	fi

	# Check to see if it is installed
	pkginfo ${rootarg} SUNWjdmk-runtime 1>/dev/null 2>&1
	exit_code_1=$?
	pkginfo ${rootarg} SUNWjdmk-runtime-jmx 1>/dev/null 2>&1
	exit_code_2=$?

	# Check the basedir of the JDMK already installed
	pkg_basedir_1=$(pkgparam ${rootarg} SUNWjdmk-runtime BASEDIR 2>/dev/null)
	pkg_basedir_2=$(pkgparam ${rootarg} SUNWjdmk-runtime-jmx BASEDIR 2>/dev/null)

	if [[ ${exit_code_1} -ne 0 || ${exit_code_2} -ne 0 || ${pkg_basedir_1} != "/opt" || ${pkg_basedir_2} != "/opt" ]]; then
		printf "$(gettext 'The Java Dynamic Management Kit is not currently installed.')\n" | logerr
  		 printf "$(gettext 'Please refer to the documentation for installation instructions.')\n\n" | logerr
                return 1
	fi

	return 0

}


########################################################################
#
# checkfor_cacao productdir
#
#	productdir	the name of the product directory
#
#	Checks to see if Cacao is in the package list.	If it is
#	then that means that it is bundled with SunCluster.  If
#	not, then it is separate and needs to be checked for.
#
#	Return:
#	
#		zero - Cacao is in package list, or is installed already
#		non-zero - Cacao is not installed
#
########################################################################
checkfor_cacao()
{

	typeset rootarg=

        if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
                rootarg="-R ${SC_BASEDIR}"
        fi

	grep SUNWcacao ${productdir}/${SC_ORDER} 1>/dev/null 2>&1
	in_order_file=$?

	if [[ ${in_order_file} -eq 0 ]]; then
		# is in .order file, so scinstall will install it
                return 0
	fi

	# Since SUNWcacaocfg is a prerequisite for SUNWcacao, we only
	# need to check for the latter.

	# Check to see if it is installed
	pkginfo ${rootarg} SUNWcacao 1>/dev/null 2>&1
	exit_code=$?

	# Check the basedir of the Common Agent Container already installed
	pkg_basedir=$(pkgparam ${rootarg} SUNWcacao BASEDIR 2>/dev/null)

	if [[ ${exit_code} -ne 0 || ${pkg_basedir} != "/opt" ]]; then
    		printf "$(gettext 'The Common Agent Container is not currently installed.')\n" | logerr
  		 printf "$(gettext 'Please refer to the documentation for installation instructions.')\n\n" | logerr
                return 1
	fi

	return 0

}

####################################################
#
# get_vlan_capability() adapterdriver
#
#	Consult the defaults file to decide if the
#	provided adapter driver is capable of supporting
#	vlans. Unknown adapters are considered capable
#	of vlans.
#
#	Return values:
#		0	- not capable of vlans
#		1	- capable of vlans
#
####################################################
get_vlan_capability()
{
	thisadapter=${1}

	typeset vlan_test
	typeset thisadapterdriver

	thisadapterdriver=$(expr ${thisadapter} : '\([a-z0-9]*[a-z]\)')
	for drv in ${SC_DFLT_KNOWN_VLAN_CAPABLE_ADAPTERS}
	do
		if [[ "${drv}" == "${thisadapterdriver}" ]]; then
			return 1
		fi
	done
	let drv_known=0

	for drv in ${SC_DFLT_ALL_SUPPORTED_ADAPTERS}
	do
		if [[ "${drv}" == "${thisadapterdriver}" ]]; then
			return 0
		fi
	done
	return 1
}

####################################################
#
# get_phys_adap adapter
#
#	Use the VLAN MULTIPLIER to deduce and print
#	the physical adapter name given any adapter name.
#
#	Return values:
#		always 0
#
####################################################
get_phys_adap()
{
	typeset -r adapter=${1}

	typeset adapterdriver
	typeset adapterinst
	typeset physinst
	typeset phys_adapter

	adapterdriver=$(expr ${adapter} : '\([a-z0-9]*[a-z]\)')
	adapterinst=$(expr ${adapter} : '[a-z0-9]*[a-z]\([0-9]*\)')
	physinst=$(expr ${adapterinst} % ${SC_VLAN_MULTIPLIER} )
	phys_adapter=${adapterdriver}${physinst}

	echo ${phys_adapter}

	return 0
}

####################################################
#
# check_installed_rac_types
#
#     Check which type of RAC was installed by checking
#     its installed packages.
#
#     Return values:
#		${SC_TRUE}	rac is installed
#		${SC_FALSE}	no rac is installed
#
####################################################
check_installed_rac_types()
{
	typeset installed_racservice
	integer rac_installed=${SC_FALSE}
 
	# Check framework package
	pkginfo -q SUNWscucm
	if [[ $? -ne 0 ]]; then
		return ${rac_installed}
	else
		installed_racservice=${ORACLE_RAC_SERVICE}
		rac_installed=${SC_TRUE}
	fi

	# Check different storage type
	pkginfo -q SUNWcvmr
	if [[ $? -eq 0 ]]; then
		installed_racservice="${installed_racservice} rac_cvm"
		rac_installed=${SC_TRUE}
	fi

	pkginfo -q SUNWschwr
	if [[ $? -eq 0 ]]; then
		installed_racservice="${installed_racservice} rac_hwraid"
		rac_installed=${SC_TRUE}
	fi

	pkginfo -q SUNWscmd
	if [[ $? -eq 0 ]]; then
		installed_racservice="${installed_racservice} rac_svm"
		rac_installed=${SC_TRUE}
	fi

	echo ${installed_racservice}
	return ${rac_installed}
}

