#! /bin/ksh
#
# ident "@(#)scinstall_common.ksh	1.65	04/08/03 SMI"
#
# Copyright 1999-2004 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

	integer i
	integer countme
	integer found

	# 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")"

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

	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
}

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

#####################################################
#
# SUNWexplo_responsefile()
#
#	Creates "response" file for pkgadd of SUNWexplo.
#
#	The filename is prescribed and expected by
#	usr/src/pkgdefs/Makefile.targ.
#
#	Returns non-zero on error.
#
#####################################################
SUNWexplo_responsefile()
{
	typeset output_file=/tmp/.SUNWexplo.response
	typeset install_date=$(/usr/bin/date -u)
	typeset hostid=$(/usr/bin/hostid)
	typeset exp_home=/opt/SUNWexplo
	typeset exp_etc=/etc/opt/SUNWexplo
	typeset unspecified="\"unspecified\"" # No I18n

	# two possible locations for prior config options
	typeset prior_opts_dir1=${exp_home}/etc # pre ver 4.0
	typeset prior_opts_dir2=${exp_etc}	# ver 4.0
	typeset prior_opts_file=default/explorer
	typeset prior_opts=${prior_opts_dir1}/${prior_opts_file}

	# if prior defaults file exists read its values
	test -r "${prior_opts}" || prior_opts=${prior_opts_dir2}/${prior_opts_file}
	test -r "${prior_opts}" && . ${prior_opts}

	# setup values: if values from prior defaults then reuse
	# else set our own defaults
	typeset exp_contract_id=${unspecified}
	if [[ ! -z ${EXP_CONTRACT_ID} ]]; then
		exp_contract_id=${EXP_CONTRACT_ID}
	fi
	typeset exp_email=""
	if [[ ! -z ${EXP_EMAIL} ]]; then
		exp_email=${EXP_EMAIL}
	fi
	typeset exp_user_email=${unspecified}
	if [[ ! -z ${EXP_USER_EMAIL} ]]; then
		exp_user_email=${EXP_USER_EMAIL}
	fi
	typeset exp_phone=${unspecified}
	if [[ ! -z ${EXP_PHONE} ]]; then
		exp_phone=${EXP_PHONE}
	fi
	typeset exp_geo="AMERICAS" # No I18n
	if [[ ! -z ${EXP_GEO} ]]; then
		exp_geo=${EXP_GEO}
	fi
	typeset exp_reply=${unspecified}
	if [[ ! -z ${EXP_REPLY} ]]; then
		exp_reply=${EXP_REPLY}
	fi
	typeset exp_address_zip=${unspecified}
	if [[ ! -z ${EXP_ADDRESS_ZIP} ]]; then
		exp_address_zip=${EXP_ADDRESS_ZIP}
	fi
	typeset exp_notify=""
	if [[ ! -z ${EXP_NOTIFY} ]]; then
		exp_notify=${EXP_NOTIFY}
	fi
	typeset exp_user_name=${unspecified}
	if [[ ! -z ${EXP_USER_NAME} ]]; then
		exp_user_name=${EXP_USER_NAME}
	fi
	typeset exp_customer_name=${unspecified}
	if [[ ! -z ${EXP_CUSTOMER_NAME} ]]; then
		exp_customer_name=${EXP_CUSTOMER_NAME}
	fi
	typeset exp_address_1=${unspecified}
	if [[ ! -z ${EXP_ADDRESS_1} ]]; then
		exp_address_1=${EXP_ADDRESS_1}
	fi
	typeset exp_address_2=${unspecified}
	if [[ ! -z ${EXP_ADDRESS_2} ]]; then
		exp_address_2=${EXP_ADDRESS_2}
	fi
	typeset exp_address_city=${unspecified}
	if [[ ! -z ${EXP_ADDRESS_CITY} ]]; then
		exp_address_city=${EXP_ADDRESS_CITY}
	fi
	typeset exp_address_state=${unspecified}
	if [[ ! -z ${EXP_ADDRESS_STATE} ]]; then
		exp_address_state=${EXP_ADDRESS_STATE}
	fi
	typeset exp_address_country=${unspecified}
	if [[ ! -z ${EXP_ADDRESS_COUNTRY} ]]; then
		exp_address_country=${EXP_ADDRESS_COUNTRY}
	fi
	typeset exp_address_country_code="US" # No I18n
	if [[ ! -z ${EXP_ADDRESS_COUNTRY_CODE} ]]; then
		exp_address_country_code=${EXP_ADDRESS_COUNTRY_CODE}
	fi
	typeset exp_serial=${unspecified}
	typeset ex_ser_host=$(eval echo $"EXP_SERIAL_${hostid}")
	if [[ ! -z ${ex_ser_host} ]]; then
		exp_serial=${ex_ser_host}
	fi
	typeset exp_install=$(date -u '+%m/%d/%Y')
	typeset ex_inst_host=$(eval echo $"EXP_INSTALL_${hostid}")
	if [[ ! -z ${ex_inst_host} ]]; then
		exp_install=${ex_inst_host}
	fi

	# Create response file suitable for Explorer 4.0
	cat > ${output_file} <<END
BASEDIR=${exp_home}
EXP_HOME=${exp_home}
EXP_LICENSE=${install_date}
EXP_DEFAULTS="${exp_etc}/default/explorer"
EXP_DEF_VERSION="4.0, FCS"
EXP_CONTRACT_ID=${exp_contract_id}
EXP_EMAIL=${exp_email}
EXP_EMAIL_SPLIT="4194304"
EXP_USER_EMAIL=${exp_user_email}
EXP_PHONE=${exp_phone}
EXP_LIB=${exp_home}/lib
EXP_GEO=${exp_geo}
EXP_REPLY=${exp_reply}
EXP_WHICH="default"
EXP_CRONTAB_RUN="no"
EXP_CRONTAB_HOUR=""
EXP_CRONTAB_MINUTE=""
EXP_CRONTAB_DAY=""
EXP_RUN="no"
EXP_SUBJECT="%C %I %E"
EXP_PATH="/usr/bin:/usr/sbin"
EXP_ADDRESS_ZIP=${exp_address_zip}
EXP_NOTIFY=${exp_notify}
EXP_USER_NAME=${exp_user_name}
EXP_CUSTOMER_NAME=${exp_customer_name}
EXP_ADDRESS_1=${exp_address_1}
EXP_ADDRESS_2=${exp_address_2}
EXP_ADDRESS_CITY=${exp_address_city}
EXP_ADDRESS_STATE=${exp_address_state}
EXP_ADDRESS_COUNTRY=${exp_address_country}
EXP_ADDRESS_COUNTRY_CODE=${exp_address_country_code}
EXP_SERIAL_${hostid}=${exp_serial}
EXP_INSTALL_${hostid}=${exp_install}
EXP_CRON_WHICH="adm,lp,root,sys,uucp"
EXP_INSTALL_${hostid}=${exp_install}
EXP_LICENSE=${install_date}
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_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}" ]]; 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}" ]]; 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}" ]]; 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=

	# 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

	# 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]}
		if [[ ${j} -lt 3 ]]; then
			buffer="${buffer}.."
		else
			while [[ ${j} -gt 0 ]]
			do
				buffer="${buffer}."
				((j -= 1))
			done
		fi
		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 -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]}
		if [[ ${j} -lt 3 ]]; then
			buffer="${buffer}.."
		else
                    	while [[ ${j} -gt 0 ]]
			do
                          	buffer="${buffer}."
				((j -= 1))
			done
		fi
		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
#
#       cdimagebasedir          location of .cdtoc file
#
#	This function prints a list of all installed SUNW data services which
#	can also be upgraded.
#
#	The list of installed SUNW data services is determined by searching for
#	rt_reg(4) files found in ${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg.
#	Any SUNW data services which have rt_reg(4) files outside of the
#	standard rtreg directory are not identified.
#
#	This function also calls upgd_get_current_dataservice(). And, if that
#	returns a resource type name, that name is added, or moved, to the top
#	of the list of installed 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 the data service identified by upgd_get_current_dataservice() is not
#	upgradeable, an error return is taken.
#
#       Return:
#               zero            Success
#               non-zero        Failure
#
#####################################################
get_installed_services()
{
	# Check arg
	if [[ $# -ne 1 ]]; then
		printf "$(gettext '%s:  Internal error - bad call to get_installed_services()')\n" ${PROG} >&2
		return 1
	fi

        typeset -r cdimagebasedir=$1

        typeset installedservices	# Services Installed on the system
        typeset installedservice
        typeset upgdservices		# List of Installed services that can be upgraded with the CD
        typeset upgdservice
        typeset ignoreservice
	typeset key
        typeset rtrfiles
        integer found
	integer i
	integer j

	currentservice=$(upgd_get_current_dataservice)

	installedservices=${currentservice}

        # List of data service RTR files
        rtrfiles=$(ls ${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.*)

	# Add SunPS dummy RTR files to list if gdsdata directory is present
	if [[ -d /usr/cluster/lib/rgm/gdsdata ]]; then
        	rtrfiles="${rtrfiles} $(ls /usr/cluster/lib/rgm/gdsdata/SUNW.*)"
	fi

	j=${#SC_RESTYPE_SERVICE_MAP[*]}
	for rtrfile in ${rtrfiles} 
	do
		installedservice=$(
			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 [[ -n ${installedservice} ]];then
			SC_RESTYPE_SERVICE_MAP[j]=$(
				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}
			)
			SC_RESTYPE_SERVICE_MAP[j+1]=${installedservice}
			installedservices="${installedservices} ${installedservice}"
			(( j += 2 ))
			continue
		fi

        	installedservice=$(
			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}
		)

		let i=0
		while [[ $i -lt ${#SC_RESTYPE_SERVICE_MAP[*]} ]]
		do
			if [[ "${SC_RESTYPE_SERVICE_MAP[i]}" == "${installedservice}" ]];then
				installedservice=${SC_RESTYPE_SERVICE_MAP[i+1]}
				break
			fi
			(( i += 2 ))
		done
		installedservices="${installedservices} ${installedservice}"
	done

	upgdservices=""
	for installedservice in ${installedservices}
	do
		let found=0
		for ignoreservice in ${SC_UPGD_SERVICE_IGNORE} ]]
		do
			if [[ "${ignoreservice}" = "${installedservice}" ]];then
				let found=1
				break
			fi
		done
		if [[ ${found} -eq 1 ]];then
			continue
		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.
		#
		find_cdimagebasedir "${cdimagebasedir}" "" "${SC_SERVICE}${installedservice}" > /dev/null 2> /dev/null
		if [[ $? -eq 0 ]];then
			let found=0
			for upgdservice in ${upgdservices}
			do
				if [[ "${installedservice}" = "${upgdservice}" ]];then
					let found=1
					break
				fi
			done
			if [[ ${found} -eq 0 ]];then
				upgdservices=$(echo ${upgdservices} ${installedservice})
			fi
		else
			if [[ "${installedservice}" = "${currentservice}" ]];then
				printf "%s:  $(gettext 'Data service "%s" is not found.')\n" ${PROG} ${service} | logerr
				printf "%s:  $(gettext 'Please, load the correct data services CD.')\n" ${PROG}  | logerr
				return 1
			fi
		fi
	done
	echo ${upgdservices}

	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} /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="/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=

	if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
		rootarg="-R ${SC_BASEDIR}"
	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}
		if [[ ${j} -lt 3 ]]; then
			buffer="${buffer}.."
		else
                    	while [[ ${j} -gt 0 ]]
			do
                          	buffer="${buffer}."
				((j -= 1))
			done
		fi
		printf "\t%s" "${buffer}"
		printf "\n\t%s\n" "${pkg}" >>${install_log}

		# Package add
		trap 'cleanup 10' HUP INT
		cmdstring="pkgadd -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 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=$(ls ${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.*)

	# Add SunPS dummy RTR files to list if gdsdata directory is present
	if [[ -d /usr/cluster/lib/rgm/gdsdata ]]; then
        	rtrfiles="${rtrfiles} $(ls /usr/cluster/lib/rgm/gdsdata/SUNW.*)"
	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"
#	"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
	integer -r one_node_flag=$7

	integer result
	typeset c
	typeset message
	typeset cluster_options
	typeset infrastructure
	typeset cmd
	typeset cmdarg
	typeset cmdargs=

	# Check args
	if [[ $# -ne 7 ]]; 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

	# 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

	# 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 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
	#
	cmd="scrconf -x 10 -N ${sponsornode}"
	${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 \"%s\" to join the cluster ... ')" "${sponsornode}" | logmsg
		cmd="scrconf -x ${SC_WTIMEOUT} -N ${sponsornode}"
		${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 'established')\n" | logmsg
		fi
		printf "$(gettext 'Current time - %s')\n" "$(date)" | logmsg
	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}" "${sponsornode}" | logerr
	fi
	if [[ ${result} -ne 0 ]]; then
		return 1
	fi

	# 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

	# 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

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

#####################################################
#
# 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 'Installing a default 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
	printf "%s\n" ${SC_DONE} | logmsg

	# More configuration required
	printf "$(gettext 'Please complete the NTP configuration after %s has finished.')\n" "${PROG}" | logmsg

	return 0
}

#####################################################
#
# nsswitch_addcluster db
#
#	db		- nss "database" (e.g., hosts or netmasks)
#
#	Add "cluster" switch to entry for "db" in nsswitch.conf.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
nsswitch_addcluster()
{
	typeset -r db=$1

	typeset -r file=${SC_BASEDIR}/etc/nsswitch.conf
	typeset dbline
	typeset numlines

	# See if "cluster" switch already exists for the "db" entry
	printf "$(gettext 'Verifying that \"%s\" is set for \"%s\" in %s ... ')" "cluster" "${db}" "nsswitch.conf" | logmsg
	grep '^'${db}':[ 	].*[ 	]cluster[ 	]..*' ${file} 2>${tmperrs} >/dev/null
	case $? in
	2)	# Read error
		printf "%s\n" ${SC_FAILED} | logmsg
		if [[ -s "${tmperrs}" ]]; then
			cat ${tmperrs} | logerr
		fi
		printf "$(gettext '%s:  error reading %s')\n" "${PROG}" "${file}" | logerr
		return 1
		;;

	0)	# Found it!
		printf "%s\n" ${SC_DONE} | logmsg
		return 0
		;;

	*)	# Not found
		printf "%s\n" ${SC_DONE} | logmsg
		;;
	esac

	# Add the "cluster" switch
	printf "$(gettext 'Adding the \"%s\" switch to \"%s\" in %s ... ')" "cluster" "${db}" "nsswitch.conf" | logmsg

	# Make sure that there is a single "db" entry
	numlines=$(grep -c '^'${db}':[ 	]' ${file} 2>/dev/null)
	case ${numlines} in
	0)	# No lines
		printf "%s\n" ${SC_FAILED} | logmsg
		printf "$(gettext '%s:  %s does not include a \"%s\" entry')\n" "${PROG}" "${file}" "${db}" | logerr
		return 1
		;;

	1)	# One line is good
		;;

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

	# Get the line
	dbline=$(grep '^'${db}':[ 	]' ${file} 2>/dev/null)

	# Add the entry
	ed -s ${file} << EOF >/dev/null 2>&1
/^${db}:[ 	]/a
${dbline}
.
-2
/^${db}:[ 	]/s/^/#/
/^${db}:[ 	]/s/^${db}:[ 	]*/${db}:      cluster /
w
q
EOF

	# 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

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

	return 0
}

#####################################################
#
# nsswitch_config
#
#	Add "cluster" switch to hosts entry in nsswitch.conf.
#	Add "cluster" switch to netmasks entry in nsswitch.conf.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
nsswitch_config()
{
	# hosts
	nsswitch_addcluster hosts || return 1

	# netmasks
	printf "\n" | logmsg
	nsswitch_addcluster netmasks || return 1

	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 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=$(ls ${SC_BASEDIR}/usr/cluster/lib/rgm/rtreg/SUNW.*)

	# Add SunPS dummy RTR files to list if gdsdata directory is present
	if [[ -d /usr/cluster/lib/rgm/gdsdata ]]; then
        	rtrfiles="${rtrfiles} $(ls /usr/cluster/lib/rgm/gdsdata/SUNW.*)"
	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=/etc/cluster/ccr/infrastructure
	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 architecture.  If all
#	package architectures match that of the system, then success,
#	if not, failure.
#
#	Return:
#	
#		zero - success
#		non-zero - failure
#
########################################################################
checkpkgarch()
{

	integer i
	integer bad_pkg_count

	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 Cacao
	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 Cacao already installed
	pkg_basedir=$(pkgparam ${rootarg} SUNWcacao BASEDIR 2>/dev/null)

	if [[ ${exit_code} -ne 0 || ${pkg_basedir} != "/opt" ]]; then
		printf "$(gettext 'The Common Management Services 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

}
