#! /bin/ksh -p
#
# ident "@(#)scinstall.ksh 1.59     02/02/09 SMI"
#
# Copyright (c) 1999-2002 Sun Microsystems, Inc.
# All rights reserved.
# Use is subject to license terms.
#

#####################################################
#
# Global constants which may be set from the environment
#
# SC_CLUSTER specifies the name of the software cluster
#  for the framework (see clustertoc(4)).
#
# SC_PRODUCT must match a product in .cdtoc.
#
# SC_SERVICE is the software cluster name prefix to use
#  when searching .clustertoc files for data services.
#
# SC_BASEDIR is the framework's BASEDIR (used with pkgadd (-R)).
#
# SC_CONFIG is the name of the CCR infrastructure file.
#
# SC_CCRDIR is the name of the CCR table directory.
#
# SC_NODEID is the name of the nodeid file.
#
# SC_DEFAULTS defines certain defaults for scinstall, including
#  platform dependent defaults.   If "SC_DEFAULTS" does not
#  begin with a "/", the file is expected to be in
#  ${productdir}/../Tools
#
# SC_WTIMEOUT is the amount of time to wait (in seconds) to
#  establish communications with the sponsor node.   The sponsor
#  node must be in the cluster for communications to succeed.
#  The default value is 24 hours.
#
# SC_GLOBALDEVFS is the default name of the file system to tear down, re-newfs
#  and use as the new global file system for /global/.devices/node@<nodeid>.
#
# SC_MAXNODES is the maximum number of nodes which are supported in
#  the cluster for a particular release.
#
# SC_DEBUG can be set to non-NULL to turn on additional debug
#  information and to avoid re-booting
#
# SC_REBOOT if set to non-NULL, auto-reboot the install
#
# SC_INTERACTIVE can be set to non-NULL to indicate interactive
#
# SC_PASS is used to indicate the pass.  Uninstall uses two passes;
#  the first pass relocates scinstall, and the second pass runs uninstall
#  from the relocated copy.  This prevents uninstall from uninstalling
#  the running copy of scinstall.
#
# SC_SCADMINDIR can be reset from the default of /usr/cluster/lib/scadmin
#
# SC_ARCHIVE can be set to an archive directory for uninstall.
#
# SC_INSTALL_LOG can be set to an install log.
#
#####################################################
typeset -r SC_CLUSTER=${SC_CLUSTER:-SUNWCsc}
typeset -r SC_PRODUCT=${SC_PRODUCT:-SunCluster}
typeset -r SC_SERVICE=${SC_SERVICE:-SUNWC_DS_}
typeset -r SC_BASEDIR=${SC_BASEDIR:-}
typeset -r SC_CONFIG=${SC_CONFIG:-${SC_BASEDIR}/etc/cluster/ccr/infrastructure}
typeset -r SC_CCRDIR=${SC_CCRDIR:-${SC_BASEDIR}/etc/cluster/ccr/directory}
typeset -r SC_NODEID=${SC_NODEID:-${SC_BASEDIR}/etc/cluster/nodeid}
typeset -r SC_DEFAULTS=${SC_DEFAULTS:-defaults}
integer -r SC_WTIMEOUT=${SC_WTIMEOUT:-$((60 * 60 * 24))}
typeset -r SC_GLOBALDEVFS=${SC_GLOBALDEVFS:-/globaldevices}
typeset -r SC_MAXNODES=${SC_MAXNODES:-8}
typeset -r SC_DEBUG=${SC_DEBUG:-}
typeset    SC_REBOOT=${SC_REBOOT:-}
typeset    SC_INTERACTIVE=${SC_INTERACTIVE:-}
typeset    SC_PASS=${SC_PASS:-1}
typeset    SC_SCADMINDIR=${SC_SCADMINDIR:-${SC_BASEDIR}/usr/cluster/lib/scadmin}
typeset    SC_ARCHIVE=${SC_ARCHIVE:-}
typeset    SC_INSTALL_LOG=${SC_INSTALL_LOG:-}

# debugging
typeset PRINTDEBUG
if [[ -n "${SC_DEBUG}" ]]; then
	PRINTDEBUG="echo \n"
	PRINTFDEBUG="printf"
else
	PRINTDEBUG=:
	PRINTFDEBUG=:
fi

#####################################################
#
# scrconf(1M) exit codes
#
# !!! IMPORTANT NOTE - begin !!!
# !!! IMPORTANT NOTE - begin !!!
#
#	The following exit codes must always be synchronized with the
#	scconf_errno_t codes defined in scconf.h!
#
# !!! IMPORTANT NOTE - end   !!!
# !!! IMPORTANT NOTE - end   !!!
#
#####################################################
integer -r SC_SCCONF_NOERR=0		# normal return - no error
integer -r SC_SCCONF_EPERM=1		# permission denied
integer -r SC_SCCONF_EEXIST=2		# object already exists
integer -r SC_SCCONF_ENOEXIST=3		# object does not exist
integer -r SC_SCCONF_ESTALE=4		# object or handle is stale
integer -r SC_SCCONF_EUNKNOWN=5		# unknown type
integer -r SC_SCCONF_ENOCLUSTER=6	# cluster does not exist
integer -r SC_SCCONF_ENODEID=7		# ID used in place of node name
integer -r SC_SCCONF_EINVAL=8		# invalid argument
integer -r SC_SCCONF_EUSAGE=9		# command usage error
integer -r SC_SCCONF_ETIMEDOUT=10	# call timed out
integer -r SC_SCCONF_EINUSE=11		# already in use
integer -r SC_SCCONF_EBUSY=12		# busy, try again later
integer -r SC_SCCONF_EINSTALLMODE=13	# install mode
integer -r SC_SCCONF_ENOMEM=14		# not enough memory
integer -r SC_SCCONF_ESETUP=15		# setup attempt failed
integer -r SC_SCCONF_EUNEXPECTED=16	# internal or unexpected error
integer -r SC_SCCONF_EBADVALUE=17	# bad ccr table value
integer -r SC_SCCONF_EOVERFLOW=18	# message buffer overflow
integer -r SC_SCCONF_EQUORUM=19		# operation would compromise quorum
integer -r SC_SCCONF_TM_EBADOPTS=20	# bad transport TM "options"
integer -r SC_SCCONF_TM_EINVAL=21	# other transport TM error
integer	-r SC_SCCONF_DS_ESUSPENDED=22	# Device service in a suspended state
integer	-r SC_SCCONF_DS_ENODEINVAL=23	# Node specified is not in cluster
integer -r SC_SCCONF_EAUTH=24		# authentication error
integer	-r SC_SCCONF_DS_EINVAL=25	# Device service in an invalid state
integer	-r SC_SCCONF_EIO=25		# IO error

#####################################################
#
# scrgadm(1M) exit codes used by upgrade
#
# !!! IMPORTANT NOTE - begin !!!
# !!! IMPORTANT NOTE - begin !!!
#
#	The following exit codes must always be synchronized with the
#	scha_err_t codes defined in scha_err.h and scha_priv.h!
#
# !!! IMPORTANT NOTE - end   !!!
# !!! IMPORTANT NOTE - end   !!!
#
#####################################################
integer -r SCHA_ERR_NOERR=0		# no error was found
integer -r SCHA_ERR_RG=12		# invalid resource group
integer -r SCHA_ERR_RT=13		# invalid resource type
integer -r SCHA_ERR_RSRC=14		# invalid resource
integer -r SCHA_ERR_FILE=106		# file or dir name not exist
					# can't create

####################################################
#
# scinstall argument variables
#
#	The following variables are inserted both in the log files
#	created by interactive scinstall AND in the custom
#	JumpStart databases created by "scinstall -c".
#
# SC_ARGVAR_NODENAME[<IDX>]	nodename	# index to use for other vars
# SC_ARGVAR_JUMPSTARTDIR	jumpdir		# JumpStart directory
# SC_ARGVAR_CLUSTERNAME		clustername	# name of the cluster
# SC_ARGVAR_AUTHLIST		nodename ...	# list of nodes (auth)
# SC_ARGVAR_AUTHTYPE		SYS|DES		# auth type
# SC_ARGVAR_NETADDR		netaddr		# transport netaddr
# SC_ARGVAR_NETMASK		netmask		# transport netmask
# SC_ARGVAR_TWONODES		0 | 1		# 1 if two node cluster
# SC_ARGVAR_DIRECT		0 | 1		# 1 if direct connect (2 node)
# SC_ARGVAR_SPONSORNODE[<IDX>]	nodename	# name of sponsor node
# SC_ARGVAR_JUNCTIONS[<IDX>]	switch ...	# junctions for node '<IDX>'
# SC_ARGVAR_JUNCTYPES[<IDX>]	junctypes ...	# junc types for node '<IDX>'
# SC_ARGVAR_ADAPTERS[<IDX>]	adap ...	# adapters for node '<IDX>'
# SC_ARGVAR_ETHERADAP[<IDX>]	adap 0|1	# 1 if "adap" is ethernet
# SC_ARGVAR_TRTYPES[<IDX>]	trtype ...	# trtypes for adap list, '<IDX>'
# SC_ARGVAR_E2CABLES[<IDX>]	cable ...	# cables for node '<IDX>'
# SC_ARGVAR_E1PORTS[<IDX>]	port ...	# ports for this endpoint
# SC_ARGVAR_E2PORTS[<IDX>]	port ...	# ports for other endpoint
# SC_ARGVAR_GDIR[<IDX>]		globaldev	# -G for node '<IDX>'
# SC_ARGVAR_SERVICES[<IDX>]	service ...	# data services for '<IDX>'
#
#	The following variables are only set in the JumpStart data
#	files.
#
# SC_AUTOINSTALL_HOST		host		# host to nfs mount
# SC_AUTOINSTALL_DIR		dir		# dir to nfs mount
# SC_AUTOINSTALL_TOOLSDIR	tools dir	# where to find Tools
#
#	The following variables are used in conjunction with the
#	other jumpstart and/or interactive install variables,
#	but are not stored with the log or JumpStart data.
#
# SC_IDX			index		# current index into ARGVARs
# SC_INDICES			index ...	# list of indices into ARGVARs
# SC_OTHERNODE			nodename	# other node in 2 node cluster
#
####################################################
typeset SC_ARGVAR_NODENAME
typeset SC_ARGVAR_JUMPSTARTDIR
typeset SC_ARGVAR_CLUSTERNAME
typeset SC_ARGVAR_AUTHLIST
typeset SC_ARGVAR_AUTHTYPE
typeset SC_ARGVAR_NETADDR
typeset SC_ARGVAR_NETMASK
typeset SC_ARGVAR_TWONODES
typeset SC_ARGVAR_DIRECT
typeset SC_ARGVAR_SPONSORNODE
typeset SC_ARGVAR_JUNCTIONS
typeset SC_ARGVAR_JUNCTYPES
typeset SC_ARGVAR_ADAPTERS
typeset SC_ARGVAR_ETHERADAP
typeset SC_ARGVAR_TRTYPES
typeset SC_ARGVAR_E2CABLES
typeset SC_ARGVAR_E1PORTS
typeset SC_ARGVAR_E2PORTS
typeset SC_ARGVAR_GDIR

typeset SC_IDX
typeset SC_INDICES
typeset SC_OTHERNODE

#####################################################
#
# Global variables for autodiscovery
#
# SC_DISCOVER_DOAUTO		# Non-NULL for full autodiscovery
# SC_DISCOVER_MADECONTACT	# Non-NULL for contact made with sponsornode
#
#####################################################
typeset SC_DISCOVER_DOAUTO
typeset SC_DISCOVER_MADECONTACT

#####################################################
#
# I18N
#
#	Note - If a "locale" directory is found
#	in the same directory from which scinstall is
#	run, the TEXTDOMAINDIR is set to that directory.
#	This will typically be the case when running
#	from cd-rom (or, a copy of the cd-rom).
#
#####################################################
typeset -x TEXTDOMAIN=SUNW_SC_INSTALL
typeset -x TEXTDOMAINDIR=/usr/cluster/lib/locale

#####################################################
#
# Variables which will be set from the scinstall defaults file
#
#####################################################
typeset SC_DFLT_KNOWN_ETHERNET_ADAPTERS
typeset SC_DFLT_KNOWN_TRTYPES
typeset SC_DFLT_TRANSPORT_TYPE
typeset SC_DFLT_ADAPTER_NAME
typeset SC_DFLT_ADAPTER_OPTS
typeset SC_DFLT_JUNCTION_TYPE
typeset SC_DFLT_JUNCTION_NAME1
typeset SC_DFLT_JUNCTION_NAME2
typeset SC_DFLT_JUNCTION_OPTS
typeset SC_DFLT_NETADDR
typeset SC_DFLT_NETMASK
typeset SC_DFLT_GDIR

#####################################################
#
# Constant globals
#
#####################################################

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

# Set LD_LIBRARY_PATH, if needed
if [[ -n "${SC_BASEDIR}" ]] && [[ "${SC_BASEDIR}" != "/" ]]; then
	LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/lib:/usr/cluster/lib:${SC_BASEDIR}/usr/lib:${SC_BASEDIR}/usr/cluster/lib
	export LD_LIBRARY_PATH
fi

# Set SCCONF_BASEDIR, used by scrconf to find the clpl tables
SCCONF_BASEDIR=${SC_BASEDIR}; export SCCONF_BASEDIR

# Program name and args list
typeset -r PROG=${0##*/}
typeset -r ARGS=$*

# Global internationalized constant strings
typeset -r YES=$(gettext 'yes')		# I18N "yes"
typeset -r NO=$(gettext 'no')		# I18N "no"

# CMDs
#
# NOTE that we do not use the builtin pwd.   The builtin returns
# the value of ${PWD}, which carries with it a history of how
# you got to the current directory.   However, the standalone
# command does not rely on anything which came before, and this
# is what we are interested in, as it makes it easier for us
# to process filesystem mount point information.
#
typeset -r CMD_PWD=/usr/bin/pwd

# Libs
typeset -r SC_DEFAULTS_2=${SC_SCADMINDIR}/defaults
typeset -r SC_SCADMINLIBDIR=${SC_SCADMINDIR}/lib
typeset -r SC_LIB_SC=sc_common
typeset -r SC_LIB_COMMON=scinstall_common
typeset -r SC_LIB_UNINSTALL=scinstall_uninstall
typeset -r SC_LIB_ARGVARS=scinstall_argvars
typeset -r SC_LIB_INTERACTIVE=scinstall_interactive
typeset -r SC_LIB_JUMPSTART=scinstall_jumpstart
typeset -r SC_LIB_UPGRADE=scinstall_upgrade
typeset -r SC_HW_LISTS=upgrade_lists

# Other constants
typeset -r SC_TMPDIR_DIR=${SC_BASEDIR}/var/cluster/run
typeset -r SC_TMPDIR=${SC_TMPDIR_DIR}/scinstall
typeset -r SC_LOCKDIR=${SC_TMPDIR}
typeset -r SC_ARCH=${SI_ARCH:-$(uname -p)}
typeset -r SC_CWD=$(${CMD_PWD})
typeset -r SC_CDTOC=.cdtoc
typeset -r SC_CLUSTERTOC=.clustertoc
typeset -r SC_ORDER=.order
typeset -r SC_DOT_DIR=${SC_SCADMINDIR}
typeset -r SC_DOT_CDTOC=${SC_DOT_DIR}/dot.cdtoc
typeset -r SC_DOT_CLUSTERTOC=${SC_DOT_DIR}/dot.clustertoc
typeset -r SC_DOT_ORDER=${SC_DOT_DIR}/dot.order
typeset -r SC_LOGDIR=${SC_BASEDIR}/var/cluster/logs/install
typeset -r SC_ILOGDIR=${SC_LOGDIR}
typeset -r SC_ILOGBASE=.interactive.log
typeset -r REQUIRED_PKG="SUNWesu"
typeset -r UPGD_REQUIRED_PKG="SUNWpl5u"
typeset -r SC_GLOBALDEVDIR=/global/.devices
typeset -r SC_RMDIR=${SC_BASEDIR}/var/cluster/uninstall/uninstall.$$
typeset -r SC_SUNOS=$(uname -r)

typeset -r SC_SCCHECK=sccheck

integer -r SC_TRUE=1
integer -r SC_FALSE=0

# TRANSLATION_NOTE
#
# "done" is used to denote the successful completion of a task.
# e.g., when a task starts the user will see:
#
#     Initializing cluster name to "boston" ... 
#
# and when the task successfully completes the user will see:
#
#     Initializing cluster name to "boston" ... done
#
# SC_DONE is used to eliminate the task of having to substitute this
# word throughout this command's functions.
#
typeset -r SC_DONE=$(gettext 'done')

# TRANSLATION_NOTE
#
# "failed" is used to denote the unsuccessful completion of a task.
# e.g., when a task starts the user will see:
#
#     Initializing cluster name to "boston" ... 
#
# or if the task did not complete the user will see:
#
#     Initializing cluster name to "boston" ... failed
#
# SC_FAILED is used to eliminate the task of having to substitute this
# word throughout this command's functions.
#
typeset -r SC_FAILED=$(gettext 'failed')

# JumpStart - autoinstall
#
# <jmpdir>/autoscinstall.d
# <jmpdir>/autoscinstall.d/<release>
# <jmpdir>/autoscinstall.d/<release>/autoscinstall.class
# <jmpdir>/autoscinstall.d/<release>/autoscinstall.finish
# <jmpdir>/autoscinstall.d/<release>/autoscinstall.finish_ksh
# <jmpdir>/autoscinstall.d/nodes
# <jmpdir>/autoscinstall.d/nodes/<nodename>/autoscinstall.data
# <jmpdir>/autoscinstall.d/clusters/<clustername>/<nodename>->../../node/<name>
#
typeset -r SC_AUTOSCINSTALL_D=autoscinstall.d
typeset -r SC_AUTOSCCLASS=autoscinstall.class
typeset -r SC_AUTOSCFINISH=autoscinstall.finish
typeset -r SC_AUTOSCFINISH_KSH=autoscinstall.finish_ksh
typeset -r SC_AUTOSCCONFIGDIR=${SC_AUTOSCINSTALL_D}/nodes
typeset -r SC_AUTOSCDATA=autoscinstall.data
typeset -r SC_RULES=rules
typeset -r SC_CHECK=check

# Upgrade constants
typeset -r SC_UPGD_PRESERVEDIR=${SC_LOGDIR}/preserve
#
# !!! IMPORTANT NOTE - begin !!!
# !!! IMPORTANT NOTE - begin !!!
#
#	The name of the flag file must always be synchronized with the
#	names in cluster/src/cmd/rpc.scadmd/rpc_scadmd_proc.c,
#	src/cmd/dcs/scgdevs.c and
#	cluster/src/cmd/initpkg/init.d/MOUNTGFSYS!
#
# !!! IMPORTANT NOTE - end   !!!
# !!! IMPORTANT NOTE - end   !!!
#
typeset -r SC_UPGD_FLAGFILE=/scnoreservedisks
# NAFO stuff
#XXX If 4228835 gets fixed, replace the following line with:
#XXX	typeset -r SC_UPGD_PNMDIR=/etc/cluster/pnm
typeset -r SC_UPGD_PNMDIR=/etc/cluster
typeset -r SC_UPGD_PNMCONFIG=pnmconfig
typeset -r SC_UPGD_PNMPARAMS=pnmparams
typeset -r SC_UPGD_STATICROUTES=staticroutes.nafo
# root directory of configuration directories for previous releases
typeset -r SC_UPGD_CONFIGROOT=/etc/opt
typeset -r SC_UPGD_CONFIGFILE=${SC_UPGD_PRESERVEDIR}/${PROG}.upgrade_data
typeset -r SC_UPGD_SDS_SETCONVERT=setconvert
#
# configuration files whose names and locations must be updated if
# changes are ever made to the actual files
#
typeset -r SC_UPGD_30VFSTAB=/etc/vfstab
typeset -r SC_UPGD_22DIDCONF=/etc/did.conf
typeset -r SC_UPGD_30DIDCONF=${SC_BASEDIR}/etc/cluster/ccr/did_instances
# miscellaneous keywords
typeset -r SC_UPGD_KEY_SSVM=SSVM
typeset -r SC_UPGD_KEY_SDS=SDS
typeset SC_UPGD_SAVEDIR
typeset SC_UPGD_FMWKDIR
# SC2.2
typeset -r SC_UPGD_SC22BINDIR=/opt/SUNWcluster/bin
typeset -r SC_UPGD_CCDFILE=ccd.database			# CCD file
typeset -r SC_UPGD_RELPATCHDIR=../Tools/Upgrade/Patches
typeset -r SC_UPGD_PATCHORDERFILE=patch_order
typeset SC_UPGD_CDBFILE					# CDB file
#####################################################
#
# Variables which will be set from the SC_UPGD_CONFIGFILE file
#
#####################################################
typeset SC_UPGD_CONFIG_CLUSTER
typeset SC_UPGD_CONFIG_MEMBERS
typeset SC_UPGD_CONFIG_LHOSTS

# Temp files and other stuff
typeset -r pid=$$
typeset lockfile=${SC_LOCKDIR}/${PROG}.lock
typeset install_log=${SC_INSTALL_LOG:-${SC_LOGDIR}/${PROG}.log.${pid}}
typeset -r adminfile=${SC_TMPDIR}/${PROG}.admin.${pid}
typeset -r tmpconfig=${SC_CONFIG}.${PROG}.tmp
typeset -r mynodename=$(uname -n)
typeset -r tmperrs=${SC_TMPDIR}/${PROG}.cmderr.${pid}
typeset -r tmperrs2=${SC_TMPDIR}/${PROG}.cmderr.${pid}.2
typeset -r tmpfile=${SC_TMPDIR}/${PROG}.cmdoutput.${pid}
typeset -r tmp_upgdfile=${SC_TMPDIR}/${PROG}.upgd.${pid}
typeset -r tmp_nodeid=${SC_TMPDIR}/${PROG}.nodeid.${pid}

#####################################################
#
# Variable globals
#
#####################################################
typeset SC_ILOG				# log of responses in interactive mode
typeset SC_SCLIBDIR			# where to find ksh includes and more
typeset SC_DEFAULTSDIR			# where to find defaults file
typeset SC_ARGVARS			# array of command line options

integer SC_LOADED_COMMON=${SC_FALSE}	# set to 1 when common libs loaded
integer SC_LOADED_UNINSTALL=${SC_FALSE}	# set to 1 when uninstall libs loaded
integer SC_LOADED_INTERACTIVE=${SC_FALSE} # set to 1 when interactive lib loaded
integer SC_LOADED_JUMPSTART=${SC_FALSE}	# set to 1 when jumpstart lib loaded
integer SC_LOADED_ARGVAR=${SC_FALSE}	# set to 1 when argvar lib loaded
integer SC_LOADED_DEFAULTS=${SC_FALSE}	# set to 1 when defaults are loaded
# used by upgrade
integer SC_LOADED_UPGRADE=${SC_FALSE}	# set to 1 when upgrade lib is loaded
integer SC_LOADED_HW=${SC_FALSE}	# set to 1 when HW lists are loaded
integer SC_LOADED_CONFIGFILE=${SC_FALSE} # set to 1 when upgrade configuration
					# is loaded

integer SC_LOCK_ISSET=0			# set to 1 once we have set our lock
integer SC_FRAMEWORK_INSTALLED=0	# set to 1 after framework installed
integer SC_PATCHES_INSTALLED=0		# set to 1 after patches installed

####################################################
#
# Create descriptor 4 as a dup of the original stdout.
# Functions which want to print user visible output
# to the original stdout have the option of using
# descriptor 4.
#
####################################################
exec 4>&1

#####################################################
#
# openfile() "filename"
#
#	Create the given "filename", if it does
#	not exist.  Any needed directories are created.
#
#	The filename must begin with slash.
#
#	Return values:
#		0	- success
#		1	- error
#
#####################################################
openfile()
{
	typeset filename=${1}

	typeset dir
	typeset dirs

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

	# make sure filename begins with /
	if [[ "${filename}" != /* ]]; then
		printf "$(gettext '%s:  Internal error - bad filename passed to openfile()')\n" ${PROG} >&2
		return 1
	fi

	# get the list of dirs we must create
	dir=${filename}
	dirs=
	while [[ "${dir}" != "/" ]]
	do
		dir="$(dirname ${dir})"
		if [[ -d "${dir}" ]]; then
			break
		else
			dirs="${dir} ${dirs}"
		fi
	done
			
	# create each directory with group sys
	for dir in ${dirs}
	do
		mkdir -m 0755 ${dir} || return 1
		chgrp sys ${dir} || return 1
	done

	# create the file
	touch ${filename} || return 1

	return 0
}

#####################################################
#
# create_tmpdir()
#
#	Create the temp directory, if it does
#	not already exist.   The caller must be root.
#
#####################################################
create_tmpdir()
{
	openfile ${SC_TMPDIR}/${PROG}.tmp.${pid} || return 1
	rm -f ${SC_TMPDIR}/${PROG}.tmp.${pid}

	return 0
}

#####################################################
#
# openlog()
#
#	Create the install log file.  If it does not
#	exist, "logmsg" and "logerr" will not create it.
#
#####################################################
openlog()
{
	openfile ${install_log}

	return $?
}

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

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

#####################################################
#
# cleanup() [exitstatus]
#
#	existatus		status to exit with
#
#	Cleanup and exit.
#	Remove lock file.
#	Print a message when exit status is not zero.
#
#	If "exitstatus" is not given, zero is assumed.
#
#	If "exitstatus" is -1, cleanup, but do not exit.
#
#####################################################
cleanup()
{
	integer exitstatus=$1 

	# Cleanup lockfile and other files
	if [[ -f ${lockfile} ]] && [[ "$(cat ${lockfile})" = ${pid} ]]; then
		rm -f ${lockfile}
	fi
	rm -f ${adminfile} ${tmpconfig} ${tmpfile} ${tmperrs} ${tmperrs2} ${tmp_nodeid} ${tmp_nsswitch}

	# Print exit message on error
	if [[ ${exitstatus} -gt 0 ]]; then
		print_errorexit_msg
	fi

	# Logfile
	if [[ -s "${install_log}" ]]; then
		printf "\n"
		printf "$(gettext 'Log file - %s')\n\n" "${install_log}"
	elif [[ -f "${install_log}" ]]; then
		rm -f ${install_log}
	fi
		

	# Return or exit
	if [[ ${exitstatus} -eq -1 ]]; then
		return 0
	fi

	exit ${exitstatus}
}


#####################################################
#
# screboot()
#
#	Cleanup, reboot, and exit.  If SC_REBOOT
#	is not set, do not reboot.  And, print reboot message
#	when SC_BASEDIR is not set.
#
#####################################################
screboot()
{
	# make sure next reboot is a reconfig reboot
	if [[ -z "${SC_BASEDIR}" ]]; then
		touch /reconfigure
	fi

	# if SC_REBOOT not set, just print message
	if [[ -z "${SC_REBOOT}" ]]; then

		# if SC_BASEDIR is set, assume JumpStart
		# if SC_INTERACTIVE is set, we are interactive
		# Do not print message if JumpStart or interactive
		if [[ -z "${SC_BASEDIR}" ]] &&
		    [[ -z "${SC_INTERACTIVE}" ]]; then
			printf "\n" | logmsg
			printf "$(gettext 'Please re-boot this machine.')\n\n" | logmsg
			return 0
		fi
	else
		# cleanup without exiting
		cleanup -1

		printf "\n" | logmsg
		printf "$(gettext 'Rebooting ... ')\n\n" | logmsg
		/usr/sbin/reboot
	fi

	# Return
	return 0
}

#####################################################
#
# print_usage()
#
#	Print usage message to stderr
#
#####################################################
print_usage()
{
	echo "$(gettext 'usage'):  ${PROG} [-p [-v]]" >&2
	echo "\t${PROG} -i [-k] [-d <dir>] [-s <srvc>,...] [-F|-N <node> [<FN-opts>]]]" >&2
	echo "\t${PROG} -u begin -F|-N <node> [-d <dir>] [-O] [<FN-opts>]" >&2
	echo "\t${PROG} -u finish [-s <srvc>,... [-d <dir>]] [-q <quorum_options>]" >&2
	echo "\t${PROG} -u update [-s <srvc>,... | all [-d <dir>]]" >&2
	echo "\t${PROG} -a <dir> [-d <dir>]" >&2
	echo "\t${PROG} -c <dir> -h <node> [-d <dir>] [-s <srvc>,...] [-F|-N <node> [<FN-opts>]]" >&2
	echo "\t${PROG} -r [-N <node>] [-G <mount_point>]" >&2

	echo >&2
	echo "---------------" >&2

	printf "$(gettext '%s for install (-i) or jumpstart client setup (-c):')\n" "<FN-opts>" >&2
	echo "\t[-C <clustername>]"
	echo "\t[-G {<special> | <filesystem>}]"
	echo "\t[-A {<adapter> | name=<adapter>[,trtype=<type>][,<properties>]}] ..." >&2
	echo "\t[-B {<junct> | name=<junct>[,type=<type>][,<properties>]}] ..." >&2
	echo "\t[-m endpoint=[<thisnode>]:<name>[@<port>],endpoint=[[<node>]:]<name>[@<port>]] ..." >&2

	echo >&2

	printf "$(gettext 'Additional %s accepted when installing first node:')\n" "<FN-opts>" >&2
	echo "\t[-T node=<node>[,...][,authtype=<authtype>]]" >&2
	echo "\t[-w netaddr=<netaddr>[,netmask=<netmask>]]" >&2

	printf "$(gettext '%s for upgrade (-u begin):')\n" "<FN-opts>" >&2
	echo "\t[-G {<special> | <filesystem>}]"

	echo >&2

	printf "$(gettext 'Additional %s for upgrade begin:')\n" "<FN-opts>" >&2
	echo "\t[-T node=<node>[,...][,authtype=<authtype>]]" >&2
	echo "\t[-w netaddr=<netaddr>[,netmask=<netmask>]]" >&2

	echo >&2

	return 0
}

#####################################################
#
# loadlib() libname [flag]
#
#	If the "flag" is not set to SC_TRUE, load the
#	named include file.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
loadlib()
{
	typeset libname=$1
	if [[ $# == 2 ]]; then
		integer flag=$2

		# Check flag
		if (( flag == SC_TRUE )); then
			return 0
		fi
	fi

	# Load the library
	. ${libname}
	if [[ $? != 0 ]]; then
		printf "$(gettext '%s:  Unable to load \"%s\"')\n" "${PROG}" "${libname}" >&2
		return 1
	fi
}

#####################################################
#
# Main
#
#####################################################
main()
{
	typeset legal_opts
	typeset required_opts
	typeset nodeid
	typeset installnode
	typeset exceptions
	typeset cmd
	typeset dir
	typeset foo
	typeset c
	typeset rootarg=
	typeset package=
	integer pkgerr=${SC_FALSE}
	integer i
	integer found

	# set from environment
	typeset scinstalldir=
	integer ismember=${SC_FALSE}

	# command line
	typeset command_line="${PROG} $*"	# entire command line

	# set from command line options
	typeset mode=				# -i|-u|-a|-c|-p
	typeset install_dir			# -a <install_dir>
	typeset jumpstart_dir			# -c <jumpstart_dir>
	typeset hnodename=			# -h <nodename>
	typeset cdimagebasedir=			# -d <cdimage_dir>
	typeset services=			# -s <srvc,...>
	typeset sponsornode=			# -N <clusternode>
	typeset clustername=			# -C <clustername>
	typeset global=${SC_GLOBALDEVFS}	# -G {<special> | <filesystem>}

	typeset auth_options=			# "-T <authentication_options>"
	typeset adapter_options=		# "-A <adapter_options> ..."
	typeset bb_options=			# "-B <blackbox_options> ..."
	typeset cable_options=			# "-m <cable_options> ..."
	typeset netaddr_options=		# "-w <network_options>"

	typeset upgrade_mode=			# -u {<begin> | <finish>}
	typeset quorum_options=			# "-q <quorum_options>"
	integer upgrade_skip_hw=${SC_FALSE}	# -O

	integer vflg=${SC_FALSE}		# -v
	integer kflg=${SC_FALSE}		# -k
	integer Fflg=${SC_FALSE}		# -F

	#
	# The "scinstalldir" is the directory from which this script is run.
	# Make sure that it is set with an absolute path;  and, remove
	# trailing dots.
	#
	scinstalldir=$(dirname $0)
	if [[ "${scinstalldir}" != /* ]]; then
		scinstalldir=${SC_CWD}/${scinstalldir}
	fi
	while [[ "${scinstalldir##*/}" = "." ]]
	do
		scinstalldir=$(dirname ${scinstalldir})
	done

	# re-set TEXTDOMAINDIR (used by gettext(1))
	if [[ -d ${scinstalldir}/locale ]]; then
		TEXTDOMAINDIR=${scinstalldir}/locale
	fi

	# determine location of support files
	if [[ -d ${scinstalldir}/lib ]]; then
		SC_SCLIBDIR=${scinstalldir}/lib
	elif [[ -d ${SC_SCADMINLIBDIR} ]]; then
		SC_SCLIBDIR=${SC_SCADMINLIBDIR}
	else
		printf "%s:  $(gettext 'Support files not found')\n" ${PROG} >&2
		return 1
	fi
	SC_DEFAULTSDIR=${SC_SCLIBDIR}/..

	# load common functions
	loadlib ${SC_SCLIBDIR}/${SC_LIB_SC} ${SC_LOADED_COMMON} || return 1
	loadlib ${SC_SCLIBDIR}/${SC_LIB_COMMON} ${SC_LOADED_COMMON} || return 1
	let SC_LOADED_COMMON=${SC_TRUE}

	# set defaults variables from the defaults file
	if [[ ${SC_DEFAULTS} == /* ]]; then
		loadlib ${SC_DEFAULTS} ${SC_LOADED_DEFAULTS} || return 1
	else
		loadlib ${SC_DEFAULTSDIR}/${SC_DEFAULTS} ${SC_LOADED_DEFAULTS} || return 1
	fi
	let SC_LOADED_DEFAULTS=${SC_TRUE}

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

	#
	# Make sure extended system utilities are installed (sort, awk, etc...)
	#
	for package in ${REQUIRED_PKG}
	do
		pkginfo ${rootarg} ${package} >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  \"%s\" is not installed')\n" "${PROG}" "${package}" >&2
			pkgerr=${SC_TRUE}
		fi
	done
	if [[ ${pkgerr} -eq ${SC_TRUE} ]]; then
		return 1
	fi

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

	#
	# Get command line options
	#

	# If no options are set, go immediately to interactive mode
	if [[ $# -eq 0 ]]; then

		# load argvar and interactive
		loadlib ${SC_SCLIBDIR}/${SC_LIB_ARGVARS} ${SC_LOADED_ARGVAR} || return 1
		let SC_LOADED_ARGVAR=${SC_TRUE}
		loadlib ${SC_SCLIBDIR}/${SC_LIB_INTERACTIVE} ${SC_LOADED_INTERACTIVE} || return 1
		let SC_LOADED_INTERACTIVE=${SC_TRUE}

		# run interactive, then return
		interactive_main
		return $?
	fi

	# First, get mode of operation
	let i=0
	mode=
	while getopts iru:a:c:pvkh:d:s:FN:C:T:A:B:m:G:w:q:O c 2>/dev/null
	do
		case ${c} in
		i|r|u|a|c|p)	# directs mode of operation
			if [[ -n "${mode}" && "${mode}" != ${c} ]]; then
				print_usage
				return 1
			fi

			# -a, -c, and -u take args
			case ${c} in
			a)
				install_dir=${OPTARG}
				;;
			c)
				jumpstart_dir=${OPTARG}
				;;
			u)
				upgrade_mode=${OPTARG}

				if [[ "${upgrade_mode}" != "begin" && \
				    "${upgrade_mode}" != "finish" && \
				    "${upgrade_mode}" != "update" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				;;
			esac

			# set mode
			mode=${c}
			((i += 1))
			;;

		\?)	# bad option
			print_usage
			return 1
			;;

		*)	# other options
			((i += 1))
			;;
		esac
	done

	# if mode not set, usage error
	if [[ -z "${mode}" ]]; then
		print_usage
		return 1
	fi

	# check other options
	OPTIND=1
	while getopts iru:a:c:pvkh:d:s:FN:C:T:A:B:m:G:w:q:O c 2>/dev/null
	do
		case ${c} in
		i|r|u|a|c|p)	# directs mode of operation
			;;

		v)	# verbose
			case "${mode}" in
			p)
				;;
			*)
				print_usage
				return 1
				;;
			esac
			vflg=${SC_TRUE}
			;;

		k)	# do not install packages
			case "${mode}" in
			i)
				;;
			*)
				print_usage
				return 1
				;;
			esac
			kflg=${SC_TRUE}
			;;

		h)	# nodename of JumpStart install client
			case "${mode}" in
			c)
				;;
			*)
				print_usage
				return 1
				;;
			esac
			hnodename=${OPTARG}
			;;

		d)	# cdimage base directory
			case "${mode}" in
			i|a|u|c)
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# make sure that the directory exists
			if [[ ! -d "${OPTARG}" ]]; then
				printf "$(gettext '%s:  \"%s\" is an unknown directory')\n" "${PROG}" "${OPTARG}" >&2
				return 1
			fi
	
			cdimagebasedir=${OPTARG}

			# set it to an absolute path, if it is not already
			if [[ "${cdimagebasedir}" != /* ]]; then
				cdimagebasedir=${SC_CWD}/${cdimagebasedir}
				while [[ "${cdimagebasedir}" != "/" ]]
				do
					if [[ -f ${cdimagebasedir}/${SC_CDTOC} ]]; then
						break
					fi
					cdimagebasedir=$(dirname ${cdimagebasedir})
				done
				if [[ ! -f ${cdimagebasedir}/${SC_CDTOC} ]]; then
					printf "$(gettext '%s:  bad -d option')\n" "${PROG}" >&2
					print_usage
					return 1
				fi
			fi
			;;

		s)	# services to install
			case "${mode}" in
			i|a|c)
				;;
			u)
				if [[ "${upgrade_mode}" == "begin" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac
			services="$(IFS=, ; echo ${OPTARG})"

			duplicate ${services}
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  Bad -s option')\n" "${PROG}" >&2
				return 1
			fi
			;;

		F)	# first node
			case "${mode}" in
			i|c)
				;;
			u)
				if [[ "${upgrade_mode}" == "finish" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# make sure -N is not also set
			if [[ -n "${sponsornode}" ]]; then
				print_usage
				return 1
			fi

			Fflg=${SC_TRUE}
			;;

		N)	# sponsor node
			case "${mode}" in
			i|c|r)
				;;
			u)
				if [[ "${upgrade_mode}" == "finish" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# make sure -N is not also set
			if [[ ${Fflg} -eq ${SC_TRUE} ]]; then
				print_usage
				return 1
			fi

			sponsornode=${OPTARG}
			;;

		C)	# clustername
			case "${mode}" in
			i|c)
				;;
			*)
				print_usage
				return 1
				;;
			esac
			if [[ "${OPTARG}" = *=* ]]; then
				clustername=$(expr "${OPTARG}" : 'cluster=\(.*\)')
			else
				clustername=${OPTARG}
			fi
			if [[ -z "${clustername}" ]]; then
				print_usage
				return 1
			fi
			;;

		T)	# authentication options
			case "${mode}" in
			i|c)
				;;

			u)
				if [[ "${upgrade_mode}" == "finish" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# check opts
			legal_opts="authtype,node"
			required_opts=
			check_opts "${OPTARG}" "${legal_opts}" "${required_opts}"
			if [[ $? -ne 0 ]] || [[ -n "${auth_options}" ]]; then
				print_usage
				return 1
			fi
			auth_options="-T ${OPTARG}"
			;;

		A)	# adapter options
			case "${mode}" in
			i|c)
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# make sure that "node" is NOT given
			legal_opts=
			required_opts="node"
			check_opts "${OPTARG}" "${legal_opts}" "${required_opts}"
			if [[ $? -eq 0 ]]; then
				print_usage
				return 1
			fi

			# The rest of the adapter options checking is
			# deferred until we can can get at our "defaults"
			# file, and defaults can be set.

			adapter_options="${adapter_options} -A ${OPTARG}"
			;;

		B)	# blackbox options
			case "${mode}" in
			i|c)
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# Blackbox options checking is deferred until we can
			# can get at our "defaults" file, and defaults
			# can be set.

			bb_options="${bb_options} -B ${OPTARG}"
			;;

		m)	# cable options
			case "${mode}" in
			i|c)
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# check opts
			legal_opts="endpoint"
			required_opts="endpoint"
			check_opts "${OPTARG}" "${legal_opts}" "${required_opts}"
			if [[ $? -ne 0 ]]; then
				print_usage
				return 1
			fi
			cable_options="${cable_options} -m ${OPTARG}"
			;;

		G)	# global file system option
			case "${mode}" in
			i|c|r)
				;;
			u)
				if [[ "${upgrade_mode}" == "finish" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac
			if [[ -z "${OPTARG}" ]]; then
				print_usage
				return 1
			fi

			# Make sure it begins with "/"
			if [[ "${OPTARG}" != /* ]]; then
				printf "$(gettext '%s:  Global file system name must begin with slash (\"/\")')\n" "${PROG}" | logerr
				return 1
			fi

			#
			# As long as we are not setting up JumpStart,
			# we can do some additional sanity checking here.
			#
			if [[ "${mode}" = [iu] ]]; then

				# <special> or <filesystem>?
				if [[ -c "${OPTARG}" ]]; then
					foo=$(expr "${OPTARG}" : '/..*\(/rdsk/\)')
					if [[ "${foo}" != "/rdsk/" ]]; then
						printf "$(gettext '%s:  %s is not a raw disk device')\n" "${PROG}" "${OPTARG}" | logerr
						return 1
					fi
				elif [[ ! -d "${SC_BASEDIR}${OPTARG}" ]]; then
					printf "$(gettext '%s:  %s is not a raw disk device')\n" "${PROG}" "${OPTARG}" | logerr
					printf "$(gettext '%s:  %s is not a file system mount point')\n" "${PROG}" "${OPTARG}" | logerr
					return 1
				fi

			elif [[ "${mode}" = r ]]; then

				# file?
				if [[ -f "${OPTARG}" ]]; then
					printf "$(gettext '%s:  %s is not a directory')\n" "${PROG}" "${OPTARG}" | logerr
					return 1
				fi
			fi

			global=${OPTARG}
			;;

		w)	# netaddr options
			case "${mode}" in
			i|c)
				;;
			u)
				if [[ "${upgrade_mode}" == "finish" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac

			# check opts
			legal_opts="netaddr,netmask"
			required_opts="netaddr"
			check_opts "${OPTARG}" "${legal_opts}" "${required_opts}"
			if [[ $? -ne 0 ]] || [[ -n "${netaddr_options}" ]]; then
				print_usage
				return 1
			fi
			netaddr_options="-w ${OPTARG}"
			;;

		q)	# quorum options
			case "${mode}" in
			u)
				if [[ "${upgrade_mode}" == "begin" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac

			#
			# check opts
			#
			# This needs to be kept in sync with scconf(1M)'s quorum
			# options
			#
			legal_opts="globaldev,node"
			required_opts="globaldev"
			check_opts "${OPTARG}" "${legal_opts}" "${required_opts}"
			if [[ $? -ne 0 ]]; then
				print_usage
				return 1
			fi
			quorum_options="${OPTARG}"
			;;

		O)	# override hardware check
			case "${mode}" in
			u)
				if [[ "${upgrade_mode}" == "finish" ]]; then
					print_usage
					return 1
				fi
				;;
			*)
				print_usage
				return 1
				;;
			esac
			let upgrade_skip_hw=${SC_TRUE}
			;;

		\?)	# bad option
			print_usage
			return 1
			;;

		*)	# we missed something!
			printf "$(gettext '%s:  Internal error checking usage')\n" "${PROG}" >&2
			return 1
			;;
		esac
	done
	shift $((OPTIND - 1))

	# Make sure there are no remaining args
	if [[ $# -ne 0 ]]; then
		print_usage
		return 1
	fi

	# Make sure all required options are given, and set installnode
	case ${mode} in 
	i | u)
	     # Install or Upgrade
		installnode=${mynodename}
		;;

	c)   # Autoinstall client retuires -h
		if [[ -z "${hnodename}" ]]; then
			print_usage
			return 1
		fi
		installnode=${hnodename}
		;;

	*)   # Others
		installnode=
		;;
	esac

	# If -F, make sure sponsornode is set to installnode
	if [[ ${Fflg} -eq ${SC_TRUE} ]]; then
		sponsornode=${installnode}

	elif [[ -n "${sponsornode}" ]] &&
	    [[ "${sponsornode}" = "${installnode}" ]]; then
		printf "$(gettext '%s:  -N cannot be used with the name of the node being installed')\n" "${PROG}" >&2
		return 1
	fi

	# Check for sponsor node, if required
	if [[ -n "${clustername}" ]] ||
	    [[ -n "${auth_options}" ]] ||
	    [[ -n "${adapter_options}" ]] ||
	    [[ -n "${bb_options}" ]] ||
	    [[ -n "${cable_options}" ]] ||
	    [[ -n "${netaddr_options}" ]] ||
	    [[ "${upgrade_mode}" == "begin" ]] ; then
		if [[ -z "${sponsornode}" ]]; then
			print_usage
			return 1
		fi
	fi

	# Check for first node, if required
	if [[ -n "${auth_options}" ]] ||
	    [[ -n "${netaddr_options}" ]]; then
		if [[ ${Fflg} -ne ${SC_TRUE} ]]; then
			print_usage
			return 1
		fi
	fi

	# If not set w/ -d, attempt to set base dir by searching for .cdtoc
	if [[ "${mode}" == [iuac] ]] &&
	    [[ -z "${cdimagebasedir}" ]]; then

		# make certain scinstalldir begins w/ "/"
		if [[ "${scinstalldir}" != /* ]]; then
			printf "$(gettext '%s:  Internal error')\n" "${PROG}" >&2
			return 1
		fi

		# look for the .cdtoc file
		cdimagebasedir=${scinstalldir}
		while [[ "${cdimagebasedir}" != "/" ]]
		do
			if [[ -f ${cdimagebasedir}/${SC_CDTOC} ]]; then
				break
			fi
			cdimagebasedir=$(dirname ${cdimagebasedir})
		done
		if [[ ! -f ${cdimagebasedir}/${SC_CDTOC} ]]; then
			cdimagebasedir=
		fi
	fi

	# Set defaults as needed and perform deferred options checking
	if [[ -n "${sponsornode}" ]]; then

		# If first node on install or jumpstart, set clustername
		if [[ "${mode}" = [ic] ]] &&
		    [[ -z "${clustername}" ]] &&
		    [[ "${sponsornode}" == "${installnode}" ]]; then
			clustername=${installnode}
		fi

		# If install or jumpstart, set default adapter
		if [[ "${mode}" = [ic] ]]; then
			if [[ -z "${adapter_options}" ]]; then
				if [[ -z "${SC_DFLT_ADAPTER_OPTS}" ]]; then
					printf "$(gettext '%s:  -A option must be given (no default)')\n", "${PROG}" >&2
					return 1
				fi
				adapter_options="-A ${SC_DFLT_ADAPTER_OPTS}"
			else
				adapter_options="$(expand_adapter_options "${adapter_options}")"
			fi
		fi

		# Adapter options can now be checked
		if [[ -n "${adapter_options}" ]]; then
			legal_opts=	# can't check props, so allow anything 
			required_opts="trtype,name"
			check_optslist "${adapter_options}" "A" "${legal_opts}" "${required_opts}"
			if [[ $? -ne 0 ]]; then
				print_usage
				return 1
			fi
		fi

		# Fix bb options
		if [[ -z "${bb_options}" ]]; then

			# If first node on install and bb not given, default 
			if [[ "${mode}" = i ]] &&
			    [[ "${sponsornode}" = "${installnode}" ]] &&
			    [[ -n "${SC_DFLT_JUNCTION_OPTS}" ]]; then
				bb_options="-B ${SC_DFLT_JUNCTION_OPTS}"
			fi
		else
			bb_options="$(strip_type_direct "${bb_options}")"
			bb_options="$(expand_bb_options "${bb_options}")"
		fi

		# Blackbox options can now be checked
		if [[ -n "${bb_options}" ]]; then
			legal_opts=	# can't check props, so allow anything 
			required_opts="type,name"
			check_optslist "${bb_options}" "B" "${legal_opts}" "${required_opts}"
			if [[ $? -ne 0 ]]; then
				print_usage
				return 1
			fi
		fi

		# Check cable options
		if [[ -n "${cable_options}" ]]; then
			check_cable_opts "${cable_options}" "${installnode}" "${adapter_options}"
			if [[ $? -ne 0 ]]; then
				print_usage
				return 1
			fi
		fi

		# Insert nodename in cable options on install, if needed
		if [[ "${mode}" = i ]] &&
		    [[ -n "${cable_options}" ]]; then
			cable_options="$(echo "${cable_options}" | sed 's/endpoint=:/endpoint='${installnode}':/g')"
		fi

		# Set default cable, if necessary.
		if [[ -z "${cable_options}" ]] &&
		    [[ -n "${adapter_options}" ]] &&
		    [[ -n "${bb_options}" ]]; then
			cable_options=$(print_default_cable "${adapter_options}" "${bb_options}" "${installnode}")
		fi

		# If install and not first node, there must be a cable
		if [[ "${mode}" = i ]] &&
		    [[ -z "${cable_options}" ]] &&
		    [[ "${sponsornode}" != "${installnode}" ]]; then
			printf "$(gettext '%s:  Unable to establish default cable option;  please use -m')\n" "${PROG}" | logerr
			return 1
		fi
	fi

	#
	# Switch on scinstall mode of operation
	#
	case ${mode} in 
	i)	#
		# Install
		#

		# must be root
		verify_isroot || return 1

		# create the temp directory, in case it is not there
		create_tmpdir || return 1

		# set lockfile
		setlock ${lockfile} || return 1

		# open the logfile
		openlog || return 1

		# add the command line to the logfile
		echo "\n${command_line}\n" >>${install_log}

		# does OS look like it might include cluster hooks?
		is_os_okay
		if [[ $? -ne ${SC_TRUE} ]]; then
			printf "$(gettext '%s:  This release of SunOS does not support this release of SunCluster')\n" "${PROG}" >&2
			return 1
		fi

		# if cluster member, we are limited to just installing services
		is_cluster_member
		ismember=$?
		if [[ ${ismember} -eq ${SC_TRUE} ]] &&
		    [[ -z "${services}" ]]; then
			printf "$(gettext '%s:  Cannot install nodes which are already active in the cluster')\n" "${PROG}" >&2
			return 1
		fi

		# check for cdimage
		verify_d_option ${cdimagebasedir} || return 1

		# if sponsornode given and not a cluster member, verify -G
		if [[ -n "${sponsornode}" ]] &&
		    [[ ${ismember} -eq ${SC_FALSE} ]]; then
			verify_G_option ${global} || return 1
		fi

		# create the admin file
		admin || return 1

		# if -k not given and not a cluster member, install framework
		if [[ ${kflg} -eq ${SC_FALSE} ]] &&
		    [[ ${ismember} -eq ${SC_FALSE} ]]; then
			installframework ${cdimagebasedir} || return 1
		fi

		# if services, install services
		if [[ -n "${services}" ]]; then
			installservices "${cdimagebasedir}" "${services}"
			if [[ $? -ne 0 ]]; then
				return 1
			fi
		fi

		# if sponsornode given and not a cluster member, add the node
		if [[ -n "${sponsornode}" ]] &&
		    [[ ${ismember} -eq ${SC_FALSE} ]]; then

			# Am I the first node?
			if [[ "${sponsornode}" = "${mynodename}" ]]; then

				# initialize single node cluster
				initialize_cluster \
					"${clustername}" \
					"${auth_options}" \
					"${adapter_options}" \
					"${bb_options}" \
					"${cable_options}" \
					"${netaddr_options}" \
				    || return 1

			# otherwise, add node to existing cluster
			else

				# WARN users of skipped options when adding node
				if [[ -n "${auth_options}" ]]; then
					printf "$(gettext '%s:  WARNING: -T option is ignored when adding a node')\n" "${PROG}" | logmsg
				fi
				if [[ -n "${netaddr_options}" ]]; then
					printf "$(gettext '%s:  WARNING: -w option is ignored when adding a node')\n" "${PROG}" | logmsg
				fi

				addnode_tocluster \
					"${sponsornode}" \
					"${clustername}" \
					"${adapter_options}" \
					"${bb_options}" \
					"${cable_options}" \
				    || return 1
			fi

			# Create the nodeid file
			printf "\n" | logmsg
			set_nodeid ${tmp_nodeid} || return 1

			# Get the nodeid
			nodeid=$(cat ${tmp_nodeid})

			# configure major number for did device driver
			printf "\n" | logmsg
			configure_did_entry ${sponsornode} || return 1

			# Create global devices file system, if not already done
			printf "\n" | logmsg
			create_globaldevfs "${nodeid}" "${global}" "nocheckflag" || return 1

			# Setup default NTP configuration
			printf "\n" | logmsg
			ntp_config || return 1

			# Add "cluster" switch to hosts entry in nsswitch.conf
			printf "\n" | logmsg
			nsswitch_config || return 1

			# Turn off power management
			printf "\n" | logmsg
			powerm_off || return 1

			# Disable network routing
			printf "\n" | logmsg
			router_disable || return 1

			# Create the final nodeid file
			mv ${tmp_nodeid} ${SC_NODEID} || return 1

			# reboot or reboot message
			screboot
		fi

		return 0
		;;

	r)	#
		# Uninstall
		#

		# must be root
		verify_isroot || return 1

		# if cluster member, fail
		is_cluster_member
		ismember=$?
		if [[ ${ismember} -eq ${SC_TRUE} ]]; then
			printf "\n"
			printf "$(gettext '%s:  You cannot uninstall Sun Cluster from active cluster members.')\n" "${PROG}" >&2
			printf "$(gettext '%s:')\n" ${PROG} >&2
			printf "$(gettext '%s:  If you have not already done so, follow the procedures for')\n" "${PROG}" >&2
			printf "$(gettext '%s:  unconfiguring a cluster node.  This includes removal of any')\n" "${PROG}" >&2
			printf "$(gettext '%s:  device group references to this node.')\n" "${PROG}" >&2

			printf "$(gettext '%s:')\n" ${PROG} >&2
			printf "$(gettext '%s:  Also, do not forget to make the required updates to %s.')\n" "${PROG}" "/etc/vfstab" >&2
			printf "$(gettext '%s:  All global device mounts, other than %s,')\n" "${PROG}" "node@<this_nodeid>" >&2
			printf "$(gettext '%s:  must be removed.')\n" "${PROG}" >&2

			printf "$(gettext '%s:')\n" ${PROG} >&2
			printf "$(gettext '%s:  Then, reboot this node in non-cluster mode and try again.')\n" "${PROG}" >&2
			return 1
		fi

		#
		# Pass 1 - re-locate a working scinstall image
		#
		if [[ ${SC_PASS} -eq 1 ]]; then
			
			# Make sure we are running from /usr/cluster/bin
			if [[ "${scinstalldir}" != "${SC_BINDIR}" ]]; then
				printf "$(gettext '%s:  Uninstall must be run from \"%s\".')\n" "${PROG}" "${SC_BINDIR}" >&2
				return 1
			fi

			# Set SC_RMDIR variables for the next pass
			SC_SCADMINDIR=${SC_RMDIR}/${PROG}
			SC_ARCHIVE=${SC_RMDIR}/archive
			SC_INSTALL_LOG=${SC_RMDIR}/log
			export SC_SCADMINDIR
			export SC_ARCHIVE
			export SC_INSTALL_LOG

			# Load the uninstall lib
			loadlib ${SC_SCLIBDIR}/${SC_LIB_UNINSTALL} ${SC_LOADED_UNINSTALL} || return 1

			# Create the SC_RMDIR and related directories
			openfile ${SC_RMDIR}/${PROG}.tmp.${pid} || return 1
			rm -f ${SC_RMDIR}/${PROG}.tmp.${pid}
			for dir in ${SC_SCADMINDIR} ${SC_ARCHIVE}
			do
				if [[ ! -d ${dir} ]]; then
					mkdir -m 0755 ${dir} || return 1
					chgrp sys ${dir} || return 1
				fi
			done

			# Relocate
			scinstall_relocate ${SC_SCADMINDIR} || return 1
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  Uninstall failed.')\n" "${PROG}" >&2
				return 1
			fi

			# Exec
			SC_PASS=2
			export SC_PASS
			cmd="${SC_RMDIR}/${PROG}/${PROG} -r"
			if [[ -n "${global}" ]]; then
				cmd="${cmd} -G ${global}"
			fi
			if [[ -n "${sponsornode}" ]]; then
				cmd="${cmd} -N ${sponsornode}"
			fi
			exec ${cmd}

			# Error
			printf "$(gettext '%s:  Internal error')\n" "${PROG}" >&2
			return 1
		fi

		# If we got this far, it better be PASS 2
		if [[ ${SC_PASS} -ne 2 ]]; then
			printf "$(gettext '%s:  Internal error')\n" "${PROG}" >&2
			return 1
		fi

		#
		# Pass 2 - doit
		#

		# create the temp directory, in case it is not there
		create_tmpdir || return 1

		# set lockfile
		setlock ${lockfile} || return 1

		# open the logfile
		openlog || return 1

		# add the command line to the logfile
		echo "\n${command_line}\n" >>${install_log}

		# Load the uninstall lib
		loadlib ${SC_SCLIBDIR}/${SC_LIB_UNINSTALL} ${SC_LOADED_UNINSTALL} || return 1

		# Do what we can when /etc/cluster/nodeid is still around
		nodeid=
		if [[ -f ${SC_NODEID} ]]; then

			# Set nodeid
			nodeid=$(cat ${SC_NODEID}) || return 1

			# Verify that no global mounts or shared devices exist
			let i=0
			verify_no_globalmounts ${nodeid} || ((i+=1))
			verify_no_globaldevs   ${nodeid} || ((i+=1))
			if [[ $i -ne 0 ]]; then
				printf "\n" | logerr
				printf "$(gettext '%s:  It is not safe to uninstall with these outstanding errors.')\n" "${PROG}" | logerr
				printf "$(gettext '%s:  Refer to the documentation for complete uninstall instructions.')\n" "${PROG}" | logerr

				printf "$(gettext '%s:  Uninstall failed.')\n" "${PROG}" | logerr
				return 1
			fi

			# Create archive of important config stuff
			scinstall_archive ${SC_ARCHIVE} || return 1

			# Attempt to backout node from established cluster.
			SC_ILOG=
			if [[ -f "${SC_ILOGDIR}/${SC_ILOGBASE}.1" ]]; then
				SC_ILOG=${SC_ILOGDIR}/${SC_ILOGBASE}.1
			fi    
			scinstall_unconfigure_node "${sponsornode}" "${SC_ILOG}"

			# Restore /etc/vfstab
			scinstall_restore_vfstab ${nodeid} ${global} || return 1

			# Restore /etc/path_to_inst
			scinstall_restore_path_to_inst ${nodeid} || return 1

			# Remove /etc/cluster/nodeid
			rm -f ${SC_NODEID}
		fi

		# Remove "cluster" switches from nsswitch.conf
		nsswitch_unconfig || return 1

		# create the admin file
		admin || return 1

		# Remove all data services packages (exceptions are in .order)
		exceptions="$(cat ${SC_DOT_ORDER} 2>/dev/null)"
		uninstallservices "${exceptions}"

		# Remove all framework cluster packages
		uninstallframework ${SC_DOT_CLUSTERTOC} ${SC_DOT_ORDER} all || return 1

		# Remove unwanted files
		scinstall_uninstall_cleanup;

		# Print final messages
		scinstall_uninstall_messages;

		# Unset the lock
		rm -f ${lockfile}

		# Force a reboot, if SC_REBOOT was set (path_to_inst updated)
		if [[ -n "${SC_REBOOT}" ]]; then
			screboot
		fi

		return 0
		;;

	u)	#
		# Upgrade
		#

		# Upgrade requires perl for now
		for package in ${UPGD_REQUIRED_PKG}
		do
			pkginfo ${rootarg} ${package} >/dev/null 2>&1
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  \"%s\" is not installed')\n" "${PROG}" "${package}" >&2
				pkgerr=${SC_TRUE}
			fi
		done
		if [[ ${pkgerr} -eq ${SC_TRUE} ]]; then
			return 1
		fi
				
		loadlib ${SC_SCLIBDIR}/${SC_LIB_UPGRADE} ${SC_LOADED_UPGRADE} || return 1
		let SC_LOADED_UPGRADE=${SC_TRUE}

		# must be root
		verify_isroot || return 1

		# create the temp directory, in case it is not there
		create_tmpdir || return 1

		# set lockfile
		setlock ${lockfile} || return 1

		# open the logfile
		install_log=${SC_LOGDIR}/${PROG}.upgrade.log.${pid}
		openlog || return 1

		# add the command line to the logfile
		echo "\n${command_line}\n" >>${install_log}

		# if cluster member, we are limited to just installing services
		is_cluster_member
		ismember=$?

		# 'begin'
		case "${upgrade_mode}" in
		"begin")
			if (( ismember == SC_TRUE )); then
				printf "%s:  $(gettext 'This node is already running Sun Cluster 3.0')!\n" ${PROG} >&2
				return 1
			fi

			# create the admin file
			admin || return 1

			# check for cdimage
			verify_d_option ${cdimagebasedir} || return 1

			upgd_begin ${cdimagebasedir} ${upgrade_skip_hw} clustername adapter_options bb_options cable_options || return 1

			# Am I the first node?
			if [[ "${sponsornode}" = "${mynodename}" ]]; then

				# initialize single node cluster
				${PRINTFDEBUG} "initialize_cluster\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n" \
				    "${clustername}" \
				    "${auth_options}" \
				    "${adapter_options}" \
				    "${bb_options}" \
				    "${cable_options}" \
				    "${netaddr_options}"
				initialize_cluster \
				    "${clustername}" \
				    "${auth_options}" \
				    "${adapter_options}" \
				    "${bb_options}" \
				    "${cable_options}" \
				    "${netaddr_options}" || return 1

			# add node to existing cluster
			else

				# WARN users of skipped options when adding node
				if [[ -n "${auth_options}" ]]; then
					printf "%s:  $(gettext 'WARNING: -T option is ignored when adding a node')\n" ${PROG} | logmsg
				fi
				if [[ -n "${netaddr_options}" ]]; then
					printf "%s:  $(gettext 'WARNING: -w option is ignored when adding a node')\n" ${PROG} | logmsg
				fi

				${PRINTFDEBUG} "addnode_tocluster\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n" \
				    "${sponsornode}" \
				    "${clustername}" \
				    "${adapter_options}" \
				    "${bb_options}" \
				    "${cable_options}"
				addnode_tocluster \
				    "${sponsornode}" \
				    "${clustername}" \
				    "${adapter_options}" \
				    "${bb_options}" \
				    "${cable_options}" || return 1
			fi

			# Create the temporary nodeid file
			printf "\n" | logmsg
			set_nodeid ${tmp_nodeid} || return 1

			# Get the nodeid
			nodeid=$(cat ${tmp_nodeid})

			# configure major number for did device driver
			printf "\n" | logmsg
			configure_did_entry ${sponsornode} || return 1

			# Create global devices file system, if not already done
			printf "\n" | logmsg
			create_globaldevfs "${nodeid}" "${global}" || return 1

			# Setup default NTP configuration
			printf "\n" | logmsg
			ntp_config || return 1

			# Add "cluster" switch to hosts entry in nsswitch.conf
			printf "\n" | logmsg
			nsswitch_config || return 1

			# Turn off power management
			printf "\n" | logmsg
			powerm_off || return 1

			# Disable network routing
			printf "\n" | logmsg
			router_disable || return 1

			# Create the final nodeid file
			mv ${tmp_nodeid} ${SC_NODEID} || return 1

			# reboot or reboot message
			screboot
			;;

		"finish")
			if (( ismember == SC_FALSE )); then
				printf "%s:  $(gettext 'This node is not running Sun Cluster 3.0')!\n" ${PROG} >&2
				return 1
			fi

			# create the admin file
			admin || return 1

			upgd_finish "${quorum_options}" || return 1

			if [[ -n ${services[*]} && \
			    ! -f ${SC_UPGD_FLAGFILE} ]]; then

				# check for cdimage
				verify_d_option ${cdimagebasedir} || return 1

				upgd_data_services "${services[*]}" ${cdimagebasedir} || return 1
			fi

			rm -f ${adminfile}
			;;

		"update")

			# check for cdimage
			verify_d_option ${cdimagebasedir} || return 1

			patch_upgd "${services[*]}" ${cdimagebasedir} || return 1
			;;
		esac

		return 0
		;;

	a)	#
		# Install cdimages onto install server
		#

		# must be root
		verify_isroot || return 1

		# load jumpstart functions
		loadlib ${SC_SCLIBDIR}/${SC_LIB_JUMPSTART} ${SC_LOADED_JUMPSTART} || return 1
		let SC_LOADED_JUMPSTART=${SC_TRUE}

		# check for cdimage
		verify_d_option ${cdimagebasedir} || return 1

		# copy the CD
		copycdimage ${cdimagebasedir} ${install_dir} || return 1

		return 0
		;;

	c)	#
		# Setup autoinstall client on install server
		#

		# must be root
		verify_isroot || return 1

		# load argvar and jumpstart functions
		loadlib ${SC_SCLIBDIR}/${SC_LIB_ARGVARS} ${SC_LOADED_ARGVAR} || return 1
		let SC_LOADED_ARGVAR=${SC_TRUE}
		loadlib ${SC_SCLIBDIR}/${SC_LIB_JUMPSTART} ${SC_LOADED_JUMPSTART} || return 1
		let SC_LOADED_JUMPSTART=${SC_TRUE}

		# check for cdimage
		verify_d_option ${cdimagebasedir} || return 1

		# setup JumpStart client
		autoclient || return 1

		return 0
		;;

	p)	# print release info
		if [[ ${vflg} -eq ${SC_TRUE} ]]; then
			print_release verbose || return 1
		else
			print_release || return 1
		fi
		;;
	esac

	return 0
}

	umask 0
	main $*
	cleanup $?
