#! /bin/ksh
#
# ident "@(#)scinstall_jumpstart.ksh 1.7     01/02/07 SMI"
#
# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
# All rights reserved.
#

#####################################################
#
# copycdimage() src dst
#
#	src	- source of copy
#	dst	- destination of copy
#
#	Copy src to dst of an entire directory
#	hierarchy.   The destination directory
#	must not already exist.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
copycdimage()
{
	typeset -r src=$1
	typeset -r dst=$2

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

	# Make sure that the src directory exists
	if [[ ! -d "${src}" ]]; then
		printf "$(gettext '%s:  Cannot find \"%s\"')\n" "${PROG}" "${src}" | logerr       
		return 1
	fi

	# Make sure that the dst directory does NOT exist
	if [[ -f "${dst}" ]] || [[ -d "${dst}" ]]; then
		printf "$(gettext '%s:  \"%s\" already exists')\n" "${PROG}" "${dst}" | logerr       
		return 1
	fi

	# Perform the copy
 	printf "$(gettext 'Copying \"%s\"')\n" "${src}" | logmsg
	mkdir -m 755 -p ${dst}
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to create %s')\n" "${PROG}" "${dst}" | logerr
		return 1
	fi
	rm -f ${tmperr}
	(
		cd ${src} 2>>${tmperr} || return 1
		find . -depth -print | cpio -pdmu ${dst} 2>${tmperr} >/dev/null || return 1
		return 0
	)
	if [[ $? -ne 0 ]]; then
		[[ -s ${tmperr} ]] && cat ${tmperr} | logerr
		rm -r ${tmperr}
		printf "$(gettext '%s:  Failed to copy \"%s\"')\n" "${PROG}" "${src}" | logerr
		return 1
	fi  
 	printf "$(gettext 'Completed copy of \"%s\"')\n" "${src}" | logmsg

	return 0
}

#####################################################
#
# autoclient_jumpdir() jumpstart_dir autoscreleasedir
#
#	jumpstart_dir			location of JumpStart directory
#	autoscreleasedir		release direcotry
#
#	Check for errors, and setup our JumpStart
#	infrastructure, as needed.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
autoclient_jumpdir()
{
	typeset -r jumpstart_dir=$1
	typeset -r autoscreleasedir=$2

	typeset finish

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

	# Jumpstart directory must begin with /
	if [[ "${jumpstart_dir}" != /* ]]; then
		printf "$(gettext '%s:  The JumpStart directory name must begin with slash (/)')\n" "${PROG}" | logerr
		return 1
	fi

	# Make sure it is a directory
	if [[ ! -d "${jumpstart_dir}" ]]; then
		printf "$(gettext '%s:  Cannot find JumpStart directory \"%s\"')\n" "${PROG}" "${jumpstart_dir}" | logerr
		return 1
	fi

	# set lockfile
	lockfile=${jumpstart_dir}/.${PROG}.lock
	setlock ${lockfile} || return 1


	# Make sure there is a check program
	if [[ ! -x ${jumpstart_dir}/${SC_CHECK} ]]; then
		printf "$(gettext '%s:  \"%s\" is not a JumpStart directory')\n" "${PROG}" "${jumpstart_dir}" | logerr
		printf "$(gettext '%s:  There is no executable \"%s\" utility')\n" "${PROG}" "${SC_CHECK}" | logerr
		return 1
	fi

	# Create a rules file, if it does not exist
	if [[ ! -f ${jumpstart_dir}/${SC_RULES} ]]; then
		echo >${jumpstart_dir}/${SC_RULES} || return 1
		chmod 0644 ${jumpstart_dir}/${SC_RULES} || return 1
		printf "$(gettext 'Created an empty \"%s\" file')\n" "${SC_RULES}" | logmsg
	fi

	#
	# Make sure that our release directory exists for
	# release specific autohainstall.class and autohainstall.finish.
	#
	if [[ ! -d "${jumpstart_dir}/${autoscreleasedir}" ]]; then
		mkdir -m 755 -p ${jumpstart_dir}/${autoscreleasedir}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to create %s')\n" "${PROG}" "${jumpstart_dir}/${autoscreleasedir}" | logerr
			return 1
		fi
		printf "$(gettext 'Created \"%s\"')\n" "${autoscreleasedir}" | logmsg
	fi

	#
	# Copy in the class file, if it is not already there.
	# Issue warning on mis-match.
	#
	if [[ ! -f ${SC_SCLIBDIR}/${SC_AUTOSCCLASS} ]]; then
		printf "$(gettext '%s:  \"%s\" file not found in \"%s\"')\n" "${PROG}" "${SC_AUTOSCCLASS}" "${SC_SCLIBDIR}" | logerr
		return 1
	fi
	if [[ -f ${jumpstart_dir}/${autoscreleasedir}/${SC_AUTOSCCLASS} ]]; then
		diff ${SC_SCLIBDIR}/${SC_AUTOSCCLASS} ${jumpstart_dir}/${autoscreleasedir}/${SC_AUTOSCCLASS} >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			printf "$(gettext 'NOTE:  The class file in \"%s\" no longer matches the original')\n" "${autoscreleasedir}" | logmsg
		fi
	else
		cp -p ${SC_SCLIBDIR}/${SC_AUTOSCCLASS} ${jumpstart_dir}/${autoscreleasedir}
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to copy the autoinstall class file')\n" "${PROG}" | logerr
			return 1
		fi
		printf "$(gettext 'Copied  \"%s\" to %s')\n" "${SC_AUTOSCCLASS}" "${autoscreleasedir}" | logmsg
	fi

	#
	# Copy in the finish files, if it they not already there.
	#
	for finish in ${SC_AUTOSCFINISH} ${SC_AUTOSCFINISH_KSH}
	do
		if [[ ! -f ${SC_SCLIBDIR}/${finish} ]]; then
			printf "$(gettext '%s:  \"%s\" file not found in \"%s\"')\n" "${PROG}" "${finish}" "${SC_SCLIBDIR}" | logerr
			return 1
		fi
		if [[ -f ${jumpstart_dir}/${autoscreleasedir}/${finish} ]]; then
			diff ${SC_SCLIBDIR}/${finish} ${jumpstart_dir}/${autoscreleasedir}/${finish} >/dev/null 2>&1
			if [[ $? -ne 0 ]]; then
				printf "$(gettext 'NOTE:  The finish script in \"%s\" no longer matches the original')\n" "${autoscreleasedir}" | logmsg
			fi
		else
			cp -p ${SC_SCLIBDIR}/${finish} ${jumpstart_dir}/${autoscreleasedir}
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  Failed to copy the autoinstall finish script')\n" "${PROG}" | logerr
				return 1
			fi
			printf "$(gettext 'Copied  \"%s\" to %s')\n" "${finish}" "${autoscreleasedir}" | logmsg
		fi
	done

	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

}

#####################################################
#
# autoclient_create_installdata() jumpstart_dir installnode [clustername]
#
#	jumpstart_dir			location of JumpStart directory
#	installnode			name of node being installed
#	clustername			name of the cluster
#
#	Create the following, if it does not already exist:
#
#	${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}
#	${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}
#	${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA}
#	${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters
#
#	If possible, also create the following:
#
#	${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters/${clustername}
#	${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters/${clustername}
#	  and, a symlink to the nodename for the cluster node
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
autoclient_create_installdata()
{
	typeset -r jumpstart_dir=$1
	typeset -r installnode=$2
	typeset -r clustername=$3

	typeset dir
	typeset dirs

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

	# Create the nodes directory, installnode directory, and clusters dir
	dirs="\
		${jumpstart_dir}/${SC_AUTOSCCONFIGDIR} \
		${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode} \
		${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters \
	"
	if [[ -n "${clustername}" ]]; then
		dirs="${dirs} ${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters/${clustername}"
	fi
	for dir in ${dirs}
	do
		if [[ ! -d ${dir} ]]; then
			mkdir -m 755 -p ${dir}
			if [[ $? -ne 0 ]]; then
				printf "$(gettext '%s:  Failed to create %s')\n" "${PROG}" "${dir}" | logerr
				return 1
			fi
			printf "$(gettext 'Created \"%s\"')\n" "${dir}" | logmsg
		fi
	done

	# Remove any old nodenames in the clusters directory
	rm -f ${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters/*/${installnode}

	# If clustername is given, attempt to create the link
	if [[ -n "${clustername}" ]]; then
		ln -s ../../nodes/${installnode} ${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters/${clustername}/${installnode} 2>/dev/null
		printf "$(gettext 'Created \"%s\"')\n" "${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/../clusters/${clustername}/${installnode}" | logmsg
	fi

	# Remove the old data file
	rm -f ${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA}

	# Create the new data file
	argvar_append_all_tofile "${SC_IDX}" ${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA} || return 1
	printf "$(gettext 'Created \"%s\"')\n" "${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA}" | logmsg

	# Add the mount and other SC_AUTOINSTALL_ variables to it
	echo "SC_AUTOINSTALL_HOST=\"$(echo ${SC_AUTOINSTALL_HOST})\"" >>${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA} || return 1
	echo "SC_AUTOINSTALL_DIR=\"$(echo ${SC_AUTOINSTALL_DIR})\"" >>${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA} || return 1
	echo "SC_AUTOINSTALL_TOOLSDIR=\"$(echo ${SC_AUTOINSTALL_TOOLSDIR})\"" >>${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA} || return 1
	echo "SC_AUTOINSTALL_PATCHES=\"patches\"" >>${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA} || return 1
	echo "SC_AUTOINSTALL_ARCHIVE=\"archive\"" >>${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA} || return 1
	echo "SC_AUTOINSTALL_FINISH=\"finish\"" >>${jumpstart_dir}/${SC_AUTOSCCONFIGDIR}/${installnode}/${SC_AUTOSCDATA} || return 1

	return 0
}

#####################################################
#
# autoclient_update_rules() jumpstart_dir autoscreleasedir installnode
#
#	jumpstart_dir			location of JumpStart directory
#	autoscreleasedir		release direcotry
#	installnode			name of node being installed
#
#	Update the rules file in the given "jumpstart_dir"
#	for the given "installnode".
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
autoclient_update_rules()
{
	typeset -r jumpstart_dir=$1
	typeset -r autoscreleasedir=$2
	typeset -r installnode=$3

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

	# Remove any old entries from the rules file
	grep '^hostname[ 	][ 	]*'${installnode}'[ 	]' ${jumpstart_dir}/${SC_RULES} >/dev/null 2>&1
	if [[ $? -eq 0 ]]; then
		printf "$(gettext 'Removing host \"%s\" from \"%s\" file') ...\n" "${installnode}" "${SC_RULES}"
		ex ${jumpstart_dir}/${SC_RULES} >/dev/null 2>&1 <<END
1,\$g/^hostname[ 	][ 	]*${installnode}[ 	].*/d
wq
END
	fi

	# Add the new entry to the rules file
	printf "$(gettext 'Updating \"%s\" file for host \"%s\"') ...\n" "${SC_RULES}" "${installnode}"

	# our ex script will not work if file is all comments, so ...
	grep -v '^#' ${jumpstart_dir}/${SC_RULES} >/dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		echo >>${jumpstart_dir}/${SC_RULES}
	fi

	# insert before first non-blank or non-comment line
	ex ${jumpstart_dir}/${SC_RULES} >/dev/null 2>&1 <<END
set wrapscan
set magic
$
/^[^#]*\$/
i
hostname ${installnode}  -  ${autoscreleasedir}/${SC_AUTOSCCLASS}  ${autoscreleasedir}/${SC_AUTOSCFINISH}
.
wq
END
	if [[ $? -ne 0 ]]; then
		printf "$(gettext '%s:  Failed to update the \"%s\" file correctly')\n" "${PROG}" "${SC_RULES}" | logerr
		return 1
	fi

	# Run the "check" program to create "rules.ok"
	printf "\n"
	printf "$(gettext 'Running the \"%s\" utility')...\n" "${SC_CHECK}" | logmsg

	# Subshell
	(
		typeset retval

		cd ${jumpstart_dir}
		echo "--------------------------------"
		./check
		retval=$?
		echo "--------------------------------"
		return ${retval}
	)

	if [[ $? -ne 0 ]]; then
		printf "\n"
		printf "$(gettext '%s:  \"%s\" failed')\n" "${PROG}" "${SC_CHECK}" | logerr
		printf "$(gettext '%s:  Correct the problem and re-run \"%s\"')\n" "${PROG}" "${SC_CHECK}" | logerr
		return 1
	fi

	# Done
	printf "\n"

	return 0
}

#####################################################
#
# autoclient_argvars()
#
#	The following variables are all inherited from the
#	caller:
#
#	cdimagebasedir			base direcotory for cdimage
#	jumpstart_dir			location of JumpStart directory
#	installnode			node to install (given w/ -h)
#	services			space seperated list of services
#	sponsornode			sponsor node (given w/ -N)
#	global				global devices directory
#	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" 
#
#	Set the SC_ARGVAR_ variables based on the variables listed
#	above as inherited from the caller.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
autoclient_argvars()
{
	typeset foo
	typeset adapter
	typeset endpoints
	typeset cables
	typeset e1ports
	typeset e2ports

	integer i
	integer j
	integer me
	integer him
	integer found

	# Clear the new argvars
	argvar_clear_all_argvars

	# Set SC_ARGVAR_NODENAME
	SC_ARGVAR_NODENAME[SC_IDX]=${installnode}

	# Set SC_ARGVAR_JUMPSTARTDIR
	SC_ARGVAR_JUMPSTARTDIR=${jumpstart_dir}

	# Set SC_ARGVAR_CLUSTERNAME
	SC_ARGVAR_CLUSTERNAME=${clustername}

	# Set SC_ARGVAR_AUTHLIST and SC_ARGVAR_AUTHTYPE
	if [[ "${sponsornode}" = "${installnode}" ]]; then
		SC_ARGVAR_AUTHLIST="$(print_subopt_values "${auth_options}" "node")"
		SC_ARGVAR_AUTHTYPE="$(print_subopt_values "${auth_options}" "authtype")"
	fi

	# Set SC_ARGVAR_NETADDR and SC_ARGVAR_NETMASK
	if [[ "${sponsornode}" = "${installnode}" ]]; then
		SC_ARGVAR_NETADDR="$(print_subopt_values "${netaddr_options}" "netaddr")"
		SC_ARGVAR_NETMASK="$(print_subopt_values "${netaddr_options}" "netmask")"
	fi

	# Set SC_ARGVAR_SPONSORNODE
	SC_ARGVAR_SPONSORNODE[SC_IDX]=${sponsornode}

	# Set SC_ARGVAR_JUNCTIONS and SC_ARGVAR_JUNCTYPES
	SC_ARGVAR_JUNCTIONS[SC_IDX]="$(print_subopt_values "${bb_options}" "name")"
	SC_ARGVAR_JUNCTYPES[SC_IDX]="$(print_subopt_values "${bb_options}" "type")"

	# Set SC_ARGVAR_DIRECT
	if [[ -n "${SC_ARGVAR_JUNCTIONS}" ]]; then
		SC_ARGVAR_DIRECT=0
	elif [[ "${sponsornode}" = "${installnode}" ]]; then
		SC_ARGVAR_DIRECT=1
	else
		SC_ARGVAR_DIRECT=
	fi

	# Set SC_ARGVAR_ADAPTERS and SC_ARGVAR_TRTYPES
	SC_ARGVAR_ADAPTERS[SC_IDX]="$(print_subopt_values "${adapter_options}" "name")"
	SC_ARGVAR_TRTYPES[SC_IDX]="$(print_subopt_values "${adapter_options}" "trtype")"

	# Set SC_ARGVAR_E2CABLES, SC_ARGVAR_E1PORTS, and SC_ARGVAR_E2PORTS
	let i=0
	set -A cables
	set -A e1ports
	set -A e2ports
	set -A endpoints $(print_subopt_values "${cable_options}" "endpoint")
	while true
	do
		# Are we done?
		if [[ -z "${endpoints[i]}" ]]; then
			break
		fi

		# Make sure we have an even number
		if [[ -z "${endpoints[i+1]}" ]]; then
			printf "$(gettext '%s:  There must be two endpoints per cable')\n" ${PROG} >&2
			printf "$(gettext '%s:  Internal error - bad cable_options passed to autoclient()')\n" ${PROG} >&2
			return 1
		fi

		# Decide which of the two endpoints in this set connects to me
		let j=0
		let me=-1
		while [[ ${j} -lt 2 ]]; do
			set -A foo
			if [[ ${endpoints[i+j]} = :* ]]; then
				foo=${installnode}
			elif [[ ${endpoints[i+j]} = *:* ]]; then
				set -A foo $(IFS=: ; echo ${endpoints[i+j]})
			fi
			if [[ "${foo}" = "${installnode}" ]]; then
				if [[ ${me} -ne -1 ]]; then
					printf "$(gettext '%s:  Only one end of a cable can attach to this node')\n" ${PROG} >&2
					printf "$(gettext '%s:  Internal error - bad cable_options passed to autoclient()')\n" ${PROG} >&2
					return 1
				fi
				let me=${j}
			fi
			((j += 1))
		done
		if [[ ${me} -eq -1 ]]; then
			printf "$(gettext '%s:  At least one end of each cable must attach to this node')\n" ${PROG} >&2
			printf "$(gettext '%s:  Internal error - bad cable_options passed to autoclient()')\n" ${PROG} >&2
			return 1
		fi

		# Set him, the compliment to me
		let him=$((me ^ 1))

		# Which adapter am I?
		foo=$(expr "${endpoints[i+me]}" : '.*:\([^@]*\).*')
		let j=0
		let found=0
		for adapter in ${SC_ARGVAR_ADAPTERS[SC_IDX]}
		do
			if [[ "${foo}" = "${adapter}" ]]; then
				let found=1
				break
			fi
			((j += 1))
		done
		if [[ ${found} -eq 0 ]]; then
			printf "$(gettext '%s:  Cable endpoint must match one of the adapters')\n" ${PROG} >&2
			printf "$(gettext '%s:  Internal error - bad cable_options passed to autoclient()')\n" ${PROG} >&2
			return 1
		fi

		# Set cables and ports
		set -A foo $(IFS=@ ; echo ${endpoints[i+me]})
		e1ports[j]=${foo[1]}
		set -A foo $(IFS=@ ; echo ${endpoints[i+him]})
		cables[j]=${foo[0]}
		e2ports[j]=${foo[1]}

		# Next set of two
		((i += 2))
	done

	# cables and ports line up with adapters
	let i=0
	for adapter in ${SC_ARGVAR_ADAPTERS[SC_IDX]}
	do
		if [[ -z "${cables[i]}" ]]; then
			cables[i]="@"
		fi
		if [[ -z "${e1ports[i]}" ]]; then
			e1ports[i]="@"
		fi
		if [[ -z "${e2ports[i]}" ]]; then
			e2ports[i]="@"
		fi
		((i += 1))
	done
	SC_ARGVAR_E2CABLES="${cables[*]}"
	SC_ARGVAR_E1PORTS="${e1ports[*]}"
	SC_ARGVAR_E2PORTS="${e2ports[*]}"

	# Set SC_ARGVAR_GDIR
	SC_ARGVAR_GDIR[SC_IDX]=${global}

	# Set SC_ARGVAR_SERVICES
	SC_ARGVAR_SERVICES[SC_IDX]="${services}"

	return 0
}

#####################################################
#
# autoclient()
#
#	The following variables are all inherited from the
#	caller:
#
#	mynodename			the name of this machine
#	cdimagebasedir			base direcotory for cdimage
#	jumpstart_dir			location of JumpStart directory
#	installnode			node to install (given w/ -h)
#	services			space seperated list of services
#	sponsornode			sponsor node (given w/ -N)
#	global				global devices directory
#	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" 
#
#	Add jumpstart client "installnode" to the "jumstart_dir".
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
autoclient()
{
	#
	# This SC_ARGVAR_ list must be identical to the list used
	# by interactive install.   We must re-declare the
	# list here in order to limit the scope.
	#
	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_TRTYPES
	typeset SC_ARGVAR_E2CABLES
	typeset SC_ARGVAR_E1PORTS
	typeset SC_ARGVAR_E2PORTS
	typeset SC_ARGVAR_GDIR
	typeset SC_ARGVAR_SERVICES

	typeset SC_IDX=0
	typeset SC_INDICES=${SC_IDX}
	typeset SC_OTHERNODE=

	typeset SC_AUTOINSTALL_HOST=
	typeset SC_AUTOINSTALL_DIR=

	typeset autoscreleasedir=

	typeset foo
	typeset realcdimage
	typeset productrel
	typeset productdir
	typeset toolsdir

	# Check args and variables inherited from caller
	if [[ $# -ne 0 ]] ||
	    [[ -z "${cdimagebasedir}" ]] ||
	    [[ -z "${jumpstart_dir}" ]] ||
	    [[ -z "${installnode}" ]]; then
		printf "$(gettext '%s:  Internal error - bad call to autoclient()')\n" ${PROG} >&2
		return 1
	fi

	# Additional argument checking when "sponsornode" is set
	if [[ -n "${sponsornode}" ]]; then
		
		# Certain options are ignored for the add node operation
		if [[ "${sponsornode}" != "${installnode}" ]]; then

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

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

		# Other options must be present
		if [[ -z "${adapter_options}" ]]; then
			print_usage
			return 1
		fi
	fi

	# Set the SC_ARGVAR_ variables from the options variables
	autoclient_argvars || return 1

	# Set the SC_AUTOINSTALL_HOST and SC_AUTOINSTALL_DIR
	set -A foo $(autoclient_fs ${cdimagebasedir} ${mynodename})
	if [[ -z "${foo[0]}" ]] || [[ -z "${foo[1]}" ]]; then
		return 1
	fi
	SC_AUTOINSTALL_HOST=${foo[0]}
	SC_AUTOINSTALL_DIR=${foo[1]}
	set -A foo

	# Set the name of the release directory
	realcdimage=$(find_cdimagebasedir "${cdimagebasedir}" "${SC_PRODUCT}" "${SC_CLUSTER}") || return 1
	productrel=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "rel") || return 1

	# Set the name of the autosc release dir
	autoscreleasedir=${SC_AUTOSCINSTALL_D}/${productrel}

	# Get the Tools directory;  it should be at the same level as Product
	productdir=$(getproduct ${realcdimage}/${SC_CDTOC} "${SC_PRODUCT}" ${SC_CLUSTER} "dir") || return 1
	toolsdir=$(dirname ${productdir})/Tools
	if [[ ! -d "${toolsdir}" ]]; then
		printf "$(gettext '%s:  Cannot find \"%s\"')\n" "${PROG}" "${toolsdir}"
		return 1
	fi

	# Make Tools directory relative to cdimagebasedir
	SC_AUTOINSTALL_TOOLSDIR=$(echo ${toolsdir} | sed -n 's#'${cdimagebasedir}/'\(.*\)#\1#p')

	# Set up our JumpStart directory, as needed
	autoclient_jumpdir ${jumpstart_dir} ${autoscreleasedir} || return 1

	# Create the data file from our SC_ARGVAR_ and SC_AUTOINSTALL_ vars
	autoclient_create_installdata ${jumpstart_dir} ${installnode} ${clustername} || return 1

	# Update the rules file
	autoclient_update_rules ${jumpstart_dir} ${autoscreleasedir} ${installnode} || return 1

	return 0
}
