#! /bin/ksh
#
# ident "@(#)scinstall_uninstall.ksh 1.7     04/05/13 SMI"
#
# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#####################################################
#
# scinstall_relocate() dir
#
#	dir		- destination directory (must already exist)
#
#	Relocate scinstall and related libraries.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
scinstall_relocate()
{
	typeset -r dir=${1}

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

	# Copy scinstall itself
	(
		cd /usr/cluster/bin || return 1
		echo scinstall | cpio -pdu ${dir} 2>/dev/null || return 1
	)

	# And, the libraries and related stuff
	(
		cd /usr/cluster/lib/scadmin || return 1
		find . -depth -print | cpio -pdu ${dir} 2>/dev/null || return 1
	)

	return 0
}

#####################################################
#
# scinstall_uninstall_state()
#
#	Clear any leftover state from the interactive logs.
#	
#	This function always returns zero.
#
#####################################################
scinstall_uninstall_state()
{
	typeset file

	for file in ${SC_ILOGDIR}/${SC_ILOGBASE}.*
	do
		ed -s ${file} << EOF >/dev/null 2>&1
g/^SC_ARGVAR_STATE/s/^/#/
w
q
EOF
	done

	return 0
}

#####################################################
#
# verify_no_globalmounts() nodeid
#
#	nodeid		- node ID of this node
#
#	If global mounts, other than for /global/.devices/node@<id>,
#	are found in /etc/vfstab, print error messages, and return non-zero.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
verify_no_globalmounts()
{
	typeset -r nodeid=${1}

	typeset special
	typeset fsckdev
	typeset mountp
	typeset type
	typeset pass
	typeset boot
	typeset options
	typeset foo

	typeset option

	integer err=0

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

	# Print message
	printf "\n" | logmsg
	printf "$(gettext 'Verifying that no unexpected global mounts remain in %s ... ')" "/etc/vfstab" | logmsg

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

		# Also, skip /global/.devices/node@<id>
		if [[ "${mountp}" == /global/.devices/node@${nodeid} ]]; then
			continue
		fi

		# Check for /dev/global and /dev/did
		if [[ "${special} == /dev/did/* ]] ||
		    [[ "${special} == /dev/global/* ]];  then
			if [[ ${err} -eq 0 ]]; then
				printf "%s\n" ${SC_FAILED} | logmsg
			fi
			printf "$(gettext '%s:  %s is still configured as a global mount.')\n" "${PROG}" "${mountp}" | logerr
			((err+=1))
			continue
		fi

		# Check for global option
		if [[ -n "${options}" ]]; then
			options="$(IFS=, ; set -- ${options}; echo $*)"
			for option in ${options}
			do
				if [[ "${option}" == "global" ]]; then
					if [[ ${err} -eq 0 ]]; then
						printf "%s\n" ${SC_FAILED} | logmsg
					fi
					printf "$(gettext '%s:  %s is still configured as a global mount.')\n" "${PROG}" "${mountp}" | logerr
					((err+=1))
				fi
			done
		fi
	done < /etc/vfstab

	if [[ ${err} -eq 0 ]]; then
		printf "%s\n" ${SC_DONE} | logmsg
	fi

	return ${err}
}

#####################################################
#
# verify_no_globaldevs() nodeid
#
#	nodeid		- node ID of this node
#
#	If non-hidden global devices still exist for this
#	node, print error messages, and return non-zero.
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
verify_no_globaldevs()
{
	typeset -r nodeid=${1}

	typeset files
	typeset file
	typeset services
	typeset service

	integer err=0

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

	# Print message
	printf "\n" | logmsg
	printf "$(gettext 'Verifying that no device services still reference this node ... ')" | logmsg

	#
	# Check /etc/cluster/ccr/dcs_service_<N> for non-hidden
	# device groups on this node
	#
	files="$(
		cd /etc/cluster/ccr 2>/dev/null || return 0
		ls | grep 'dcs_service_[0-9][0-9]*$'
	)"
	services="$(
	    for file in ${files}
	    do
		file=/etc/cluster/ccr/${file}
		dd conv=lcase <${file} 2>/dev/null | nawk -v nodeid=${nodeid} '
			BEGIN {
				dcs_servicename=""
				dcs_nodes=""
				autogenerated=0
			}
			/^dcs_servicename/ {
				# Set the value for autogenerated
				dcs_servicename=$2
			}

			/^dcs_nodes/ {
				# Strip out the keyword
				dcs_nodes=substr($0, index($0, "("))
			}
			/^autogenerated/ {
				# Set the value for autogenerated
				autogenerated=$2
			}
			END {
				# If autogenerated is not set, look for our node
				if (autogenerated == 0) {

					# Make sure we have a service name
					if (dcs_servicename == NULL)
						dcs_servicename="<unknown>";

					# Strip out all (, ), space, and ,.
					gsub("[\\(\\), ]", " ", dcs_nodes);

					# Split into an array
					# every odd element is a node number
					split(dcs_nodes, dcs_nodes_array);

					# Look for our nodeid in every
					# other element
					for (i = 1; dcs_nodes_array[i]; i+=2) {
						if (dcs_nodes_array[i] == nodeid) {
							printf("%s\n", dcs_servicename);
							break;
						}
					}
				}
			}
		'
	    done
	)"

	for service in ${services}
	do
		if [[ ${err} -eq 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
		fi
		printf "$(gettext '%s:  This node is still configured to host device service \"%s\".')\n" "${PROG}" "${service}" | logerr
		((err+=1))
	done

	if [[ ${err} -eq 0 ]]; then
		printf "%s\n" ${SC_DONE} | logmsg
	fi

	return ${err}
}

#####################################################
#
# scinstall_archive() archivedir
#
#	archivedir		- destination directory (must already exist)
#
#	Create archive of /etc/cluster, /etc/path_to_inst, /etc/vfstab,
#	and /etc/nsswitch.conf in the "archivedir".
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
scinstall_archive()
{
	typeset -r archivedir=${1}
	typeset -r files="/etc/cluster /etc/path_to_inst /etc/vfstab /etc/nsswitch.conf"

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

	# Print message
	printf "\n" | logmsg
	printf "$(gettext 'Archiving the following to %s:')\n" ${archivedir} | logmsg

	# Archive each file or directory
	for file in ${files}
	do
		# Make sure that there is something to archive
		if [[ ! -f ${file} ]] && [[ ! -d ${file} ]]; then
			continue
		fi

		# Print message
		echo "    ${file} ..." | logmsg

		# Copy
		find ${file} -depth -print | cpio -pdu ${archivedir} 2>/dev/null
		if [[ $? -ne 0 ]]; then
			printf "$(gettext '%s:  Failed to archive %s.')\n" "${PROG}" "${file}" | logerr
			return 1
		fi
	done

	return 0
}

#####################################################
#
# scinstall_unconfigure_node() [sponsornode] [interactive_log]
#
#	sponsornode	- node to contact for cleaning up cluster
#	interactive_log	- log to search for "sponsornode"
#
#	Attempt to unconfigure this node from the established cluster.
#
#	Unless "sponsornode" is given, the infrastructure table is first
#	inspected to determine if this is already the last cluster node.
#	If it is, return immediately to the caller.
#
#	If "sponsornode" is given, this function will only attempt to
#	communicate with the "sponsornode".
#
#	If "interactive_log" is given and "sponsornode" is not given,
#	this function will attempt to first communicate with the
#	SPONSORNODE[0] given in the log file.  It will then attempt
#	to communicate with all other nodes given in
#	/etc/ccr/infrastructure.
#
#	The scrconf command will attempt to unconfigure all transport
#	references first.  It will then attempt to unconfigure the node
#	itself.
#
#	Errors may result in attempts to connect, attempts to authenticate,
#	attempts to unconfigure transport, and attempts to unconfigure
#	this node.
#
#	All errors will be printed as warnings.
#
#	This function always returns zero.
#
#	Return:
#		zero		Always
#
#####################################################
scinstall_unconfigure_node()
{
	typeset -r sponsornode=${1}
	typeset -r interactive_log=${2}

	typeset -r infrafile=/etc/cluster/ccr/infrastructure

	typeset nodelist=${sponsornode}
	typeset golden=
	typeset tmplist
	typeset node

	integer count
	integer result
	integer found

	# If "sponsornode" was not given, construct a list of nodes
	if [[ -z "${sponsornode}" ]]; then

		# If we are the last node, we are done
		[[ -f ${infrafile} ]] || return 0
		let count=$(grep -c 'cluster\.nodes\.[1-9][0-9]*\.name[ 	]' ${infrafile} 2>/dev/null || echo 0)
		if [[ ${count} -lt 2 ]]; then
			return 0
		fi

		# Find the node in the log
		if [[ -n "${interactive_log}" ]] &&
		    [[ -r "${interactive_log}" ]]; then
			golden="$(. ${interactive_log}; echo ${SPONSORNODE[0]})"
		fi

		# Get the nodelist
		tmplist="$(sed -n 's/^cluster\.nodes\.[1-9][0-9]*\.name[ 	]\(.*\)/\1/p' ${infrafile})"
		for node in ${tmplist}
		do
			# Skip me
			if [[ ${node} == ${mynodename} ]]; then
				continue
			fi

			# Order
			if [[ ${node} == "${golden}" ]]; then
				nodelist="${node} ${nodelist}"
			else
				nodelist="${nodelist} ${node}"
			fi
		done
	fi
	nodelist="$(echo ${nodelist})"

	# If there is no nodelist, we are done
	[[ -n "${nodelist}" ]] || return 0

	# Print message
	printf "\n" | logmsg
	printf "$(gettext 'Attempting to contact the cluster ...')\n" | logmsg

	# Try each of the nodes
	let found=0
	for node in ${nodelist}
	do
		# Try to contact node
		printf "    $(gettext 'Trying \"%s\" ... ')" "${node}" | logmsg
                scrconf -x 10 -N ${node} >${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
                        if [[ -s "${tmperrs}" ]]; then
                                cat ${tmperrs} | logmsg
                        fi
                else
                        printf "$(gettext 'okay')\n" | logmsg
			let found=1
                fi
	done

	# Remove error file
	rm -f ${tmperrs}           

	# If we don't think we have a node, we are done
	if [[ ${found} -eq 0 ]]; then

		# Print messages
		printf "$(gettext 'Unable to contact the cluster.')\n" | logmsg
		printf "$(gettext 'Additional housekeeping may be required to unconfigure')\n" | logmsg
		printf "$(gettext '%s from the active cluster.')\n" "${mynodename}" | logmsg

		# Done
		return 0
	fi

	#
	# Try to unconfigure using the responsive node
	#

	# Print message
	printf "\n" | logmsg
	printf "$(gettext 'Attempting to unconfigure %s from the cluster ... ')" "${mynodename}" | logmsg

	scrconf -r -h ${mynodename} -N ${node} >${tmperrs} 2>&1
	if [[ -s ${tmperrs} ]]; then
		printf "%s\n" "${SC_FAILED}" | logmsg
		printf "$(gettext 'Please consider the following warnings:')\n" | logmsg
		printf "\n" | logmsg
		cat ${tmperrs} | sed 's/^/    /' | logmsg
		printf "\n" | logmsg

		printf "$(gettext 'Additional housekeeping may be required to unconfigure')\n" | logmsg
		printf "$(gettext '%s from the active cluster.')\n" "${mynodename}" | logmsg
	else
		printf "%s\n" "${SC_DONE}" | logmsg
	fi

	# Done
	return 0
}

#####################################################
#
# scinstall_restore_vfstab() nodeid [mount_point]
#
#	Attempt to restore the /etc/vfstab file.
#
#	If "mount_point" is given, test the "mount_point" and attempt
#	to modify /etc/vfstab to re-mount the /global/.node@<nodeid>
#	filesystem on the given "mount_point".  Otherwise, attempt to
#	remount on the default /globaldevices.
#
#	A new mount point directory may be created as a result of calling
#	this function.
#	
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
scinstall_restore_vfstab()
{
	typeset -r nodeid=${1}
	typeset -r mount_point=${2}

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

	typeset special
	typeset fsckdev
	typeset mountp
	typeset type
	typeset pass
	typeset boot
	typeset options
	typeset foo

	typeset rdev=
	typeset dev=
	typeset slice=
	typeset oldline=
	typeset newline=

	integer stillmounted=0		# Old directory is still mounted

	integer isinuse=0		# New dir is in use
	integer isfile=0		# New dir exists, but not as a dir
	integer isstuff=0		# New dir has stuff in it
	integer isnodir=0		# New dir is not available

	integer result

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

	# See if there is a globaldevfs mount
	is_globaldevfs ${nodeid}
	if [[ $? -ne 1 ]]; then
		return 0
	fi

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

	# Set mount_point
	if [[ -z "${mount_point}" ]]; then
		mount_point=/globaldevices
	fi

	# Check mnttab for old and new mount points
	while read special mountp foo
	do
		# Check for old mount point
		if [[ "${mountp}" == "${globaldevmountp}" ]]; then
			let stillmounted=1
		fi

		# Check for new mount point
		if [[ "${mountp}" == ${mount_point}* ]]; then
			let isinuse=1
		fi

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

	# Check /etc/vfstab for old and new mount points
	oldline=
	newline=
	while read special fsckdev mountp type pass boot options foo
	do
		# Skip comments and blank lines
		case ${special} in
		'#'* | '')	# Ignore comments, empty lines
				continue
				;;
		esac

		# Check for old mount point and construct replacement
		if [[ "${mountp}" == "${globaldevmountp}" ]]; then
			oldline=${special}

			dev=
			rdev=
			slice=

			# If did device, translate "special" and "fsckdev"
			if [[ "${fsckdev}" == /dev/did/rdsk/d*s* ]]; then
				rdev="$(echo ${fsckdev} | sed -n 's#/dev/did/rdsk/\(d[0-9][0-9]*\)s[0-9][0-9]*#\1#p')"
				slice="$(echo ${fsckdev} | sed -n 's#/dev/did/rdsk/d[0-9][0-9]*s\([0-9][0-9]*\)#\1#p')"
				rdev="$(scdidadm -o path -l ${rdev} 2>/dev/null)"
				rdev=$(echo ${rdev})
				rdev=${rdev}s${slice}
				if [[ "${rdev}" == /dev/rdsk/* ]]; then
					dev="$(echo ${rdev} | sed -n 's#/dev/rdsk/\(.*\)#\1#p')"
					dev=/dev/dsk/${dev}
				fi
			else
				rdev=${fsckdev}
				dev=${special}
			fi
			rdev=$(echo ${rdev})
			dev=$(echo ${dev})

			# Construct the new line
			if [[ -n "${dev}" ]] && [[ -n "${rdev}" ]]; then
				newline="$(echo "${dev}\t${rdev}\t${mount_point}\tufs\t2\tyes\t-")"
			fi
		fi

		# Check for new mount point
		if [[ "${mountp}" == ${mount_point}* ]]; then
			let isinuse=1
		fi
	done < /etc/vfstab
	if [[ $? -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		printf "$(gettext '%s:  Error reading %s')\n" "${PROG}" "/etc/vfstab" | logerr
		return 1
	fi

	# If vfstab does not include an old mount point, we are done.
	# But, note that this should never be true, if we are here.
	if [[ -z "${oldline}" ]];  then
		printf "%s\n" ${SC_FAILED} | logmsg
		printf "$(gettext '%s:  An internal error has occurred')\n" "${PROG}"
		return 1
	fi

	# Additional checks to see if new mount point is already in use
	if [[ -r "${mount_point}" ]] && [[ ! -d "${mount_point}" ]]; then
		let isfile=1
	fi
	if [[ -d "${mount_point}" ]]; then
		for file in ${mount_point}/.* ${mount_point}/*
		do
			case ${file} in
			${mount_point}/.)
				;;

			${mount_point}/..)
				;;

			*)
				let isstuff=1
				break
				;;
			esac
		done
	fi

	# Attempt to create new directory, if it does not exist
	if [[ ${isinuse} -eq 0 ]] &&
	    [[ ${isfile} -eq 0 ]] &&
	    [[ ${isstuff} -eq 0 ]];  then
	    	if [[ ! -d ${mount_point} ]]; then
			mkdir -m 0755 -p ${mount_point} 2>/dev/null
			if [[ $? -ne 0 ]]; then
				let isnodir=1
			fi
		fi
	fi

	# Edit vfstab
	foo="$(echo ${oldline} | sed 's#/#\\/#g')"
	if [[ ${isinuse} -ne 0 ]] ||
	    [[ ${isfile} -ne 0 ]] ||
	    [[ ${isstuff} -ne 0 ]] ||
	    [[ ${isnodir} -ne 0 ]] ||
	    [[ -z "${newline}" ]]; then
		ed -s /etc/vfstab << EOF >/dev/null 2>&1
/^${foo}/s/^/#/
w
q
EOF
		let result=$?
	else
		ed -s /etc/vfstab << EOF >/dev/null 2>&1
/^${foo}/s/^/#/
\$a
${newline}
.
w
q
EOF
		let result=$?
	fi

	if [[ ${result} -ne 0 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		printf "$(gettext '%s:  Failed to update %s')\n" "${PROG}" "/etc/vfstab" | logerr
		return 1
	fi

	printf "%s\n" ${SC_DONE} | logmsg
	printf "$(gettext 'The %s file was updated successfully.')\n" "/etc/vfstab" | logmsg
	printf "$(gettext 'The original entry for %s has been commented out.')\n" "${globaldevmountp}" | logmsg
	if [[ ${isinuse} -ne 0 ]] ||
	    [[ ${isfile} -ne 0 ]] ||
	    [[ ${isstuff} -ne 0 ]] ||
	    [[ ${isnodir} -ne 0 ]] ||
	    [[ -z "${newline}" ]]; then
		printf "$(gettext 'However, a new entry for %s has not been added.')\n" "${mount_point}"
		if [[ ${isinuse} -ne 0 ]]; then
			printf "$(gettext '%s is already in use.')\n" "${mount_point}"
		elif [[ ${isfile} -ne 0 ]]; then
			printf "$(gettext '%s exists, but is not a directory.')\n" "${mount_point}"
		elif [[ ${isstuff} -ne 0 ]]; then
			printf "$(gettext '%s exists, but is not empty.')\n" "${mount_point}"
		elif [[ ${isnodir} -ne 0 ]]; then
			printf "$(gettext 'It was not possible to create %s.')\n" "${mount_point}"
		elif [[ -z "${newline}" ]]; then
			printf "$(gettext 'It was not possible to determine the correct device names.')\n"
		fi
	else
		printf "$(gettext 'And, a new entry has been added for %s.')\n" "${mount_point}" | logmsg
	fi

	# Attempt to unmount the old device
	if [[ ${stillmounted} -eq 1 ]]; then

		# Print message
		printf "\n" | logmsg
		printf "$(gettext 'Unmounting %s ... ')" "${globaldevmountp}" | logmsg
		umount ${globaldevmountp} >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			printf "$(gettext 'Failed to unmount %s.')\n" "${globaldevmountp}"
		else
			printf "%s\n" ${SC_DONE} | logmsg
			let stillmounted=0
		fi
	fi
	
	# Attempt to mount the new device
	if [[ ${stillmounted} -eq 0 ]] &&
	    [[ ${isinuse} -eq 0 ]] &&
	    [[ ${isfile} -eq 0 ]] &&
	    [[ ${isstuff} -eq 0 ]] &&
	    [[ ${isnodir} -eq 0 ]] &&
	    [[ -n "${newline}" ]];  then

		# Print message
		printf "\n" | logmsg
		printf "$(gettext 'Mounting %s on %s ... ')" "${dev}" "${mount_point}" | logmsg

		# Force any remaining umounts
		umount -f ${globaldevmountp} >/dev/null 2>&1
		umount -f ${dev} >/dev/null 2>&1

		# fsck
		fsck -m ${rdev} >${tmperrs} 2>&1
		if [[ $? -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			if [[ -s ${tmperrs} ]]; then
				cat ${tmperrs} | logerr
			fi
			rm -f ${tmperrs}
			printf "$(gettext '%s failed for %s.')\n" "fsck" "${rdev}" | logerr
			return 1
		fi
			
		# mount
		mount ${mount_point} >${tmperrs} 2>&1
		if [[ $? -ne 0 ]]; then
			printf "%s\n" ${SC_FAILED} | logmsg
			if [[ -s ${tmperrs} ]]; then
				cat ${tmperrs} | logerr
			fi
			rm -f ${tmperrs}
			printf "$(gettext '%s failed for %s.')\n" "mount" "${rdev}" | logerr
			return 1
		fi

		# Done
		rm -f ${tmperrs}
		printf "%s\n" ${SC_DONE} | logmsg

		# Remove old junk
		if [[ -d ${mount_point}/dev ]] ||
		    [[ -d ${mount_point}/devices ]];  then
			printf "\n" | logmsg
			printf "$(gettext 'Cleaning up %s ... ')" "${mount_point}" | logmsg
			rm -rf ${mount_point}/dev
			rm -rf ${mount_point}/devices
			printf "%s\n" ${SC_DONE} | logmsg
		fi
	fi

	return 0
}

#####################################################
#
# scinstall_restore_path_to_inst() nodeid
#
#	nodeid		- node ID of this node
#
#	Attempt to restore the /etc/path_to_inst file by stipping
#	out the "node@<nodeid>" prefix from each line.
#	
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
scinstall_restore_path_to_inst()
{
	typeset -r nodeid=${1}

	typeset -r file=/etc/path_to_inst

	integer count

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

	# See if there is something to update
	let count=$(grep -c '^"/node@'${nodeid}'/' ${file} 2>/dev/null || echo 0)
	if [[ ${count} -lt 1 ]]; then
		return 0
	fi

	# Print message
	printf "\n" | logmsg
	printf "$(gettext 'Updating %s ... ')" "${file}" | logmsg

	# Make certain that the next reboot is a reconfig reboot
	touch /reconfigure

	# Remove all of the node stuff
	ed -s ${file} << EOF >/dev/null 2>&1
1,\$s/^"\/node@${nodeid}/"/
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

	# Force a reboot later
	SC_REBOOT=1

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

	return 0
}

#####################################################
#
# scinstall_uninstall_cleanup()
#
#	Remove unwanted files.
#	
#	This function always returns zero.
#
#####################################################
scinstall_uninstall_cleanup()
{
	typeset device
	typeset didadmin=/devices/pseudo/did@0:admin
	typeset file

	# Anything to do?
	if [[ ! -d /etc/cluster ]] &&
	    [[ ! -h /dev/global ]] &&
	    [[ ! -h /dev/md/shared ]] &&
	    [[ ! -d /dev/did ]] &&
	    [[ ! -c ${didadmin} ]]; then
		return 0
	fi

	# Print message
	printf "\n" | logmsg
	printf "$(gettext 'Removing the following:')\n" | logmsg

	# Remove each file or directory
	for file in /etc/cluster /dev/global /dev/md/shared /dev/did
	do
		# Make sure that there is something to zap
		if [[ ! -f ${file} ]] &&
		    [[ ! -d ${file} ]] &&
		    [[ ! -h ${file} ]]; then
			continue
		fi

		# Print message
		echo "    ${file} ..." | logmsg

		# Remove
		rm -rf ${file}
	done

	# We have to treat /devices specially, or command line may be too long
	echo "    /devices/pseudo/did@0:* ..." | logmsg
	for device in /devices/pseudo/did@0:*
	do
		# Do didadmin last, so that we know everything is gone
		if [[ ${device} == ${didadmin} ]]; then
			continue
		fi

		rm -f ${device}
	done
	rm -f ${didadmin}

	return 0
}

#####################################################
#
# scinstall_uninstall_messages()
#
#	Print final exit messages.
#	
#	This function always returns zero.
#
#####################################################
scinstall_uninstall_messages()
{
	# /etc/inet/ntp.conf
	printf "\n" | logmsg
	printf "$(gettext 'The %s file has not been updated.')\n" "/etc/inet/ntp.conf" | logmsg
	printf "$(gettext 'You may want to remove it or update it after uninstall has completed.')\n" | logmsg

	# /var/cluster
	printf "\n" | logmsg
	printf "$(gettext 'The %s directory has not been removed.')\n" "/var/cluster" | logmsg
	printf "$(gettext 'Among other things, this directory contains')\n" | logmsg
	printf "$(gettext 'uninstall logs and the uninstall archive.')\n" |logmsg
	printf "$(gettext 'You may remove this directory once you are satisfied')\n" | logmsg
	printf "$(gettext 'that the logs and archive are no longer needed.')\n" | logmsg

	# Pause before rebooting
	sleep 5

	# Done
	printf "\n" | logmsg

	return 0
}

#####################################################
#
# nsswitch_rmcluster() file db
#
#	file		- nsswitch file (e.g., nsswitch.conf, nsswitch.files, etc)
#	db		- nss "database" (e.g., hosts or netmasks)
#
#	Remove "cluster" switch from from entry for "db" in nsswitch files
#
#	Return:
#		zero		Success
#		non-zero	Failure
#
#####################################################
nsswitch_rmcluster()
{
	typeset -r file=$1
	typeset -r db=$2

	typeset dbline
	typeset numlines

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

	# See if "cluster" switch exists for the "db" entry
	grep '^'${db}':[ 	].*[ 	]cluster' ${file} 2>&1 >/dev/null
	if [[ $? -ne 0 ]];  then
		return 0
	fi

	# Remove the "cluster" switch
	printf "\n" | logmsg
	printf "$(gettext 'Removing the \"%s\" switch from \"%s\" in %s ... ')" "cluster" "${db}" "${file}" | logmsg

	# Make sure that there is a single "db" entry
	numlines=$(grep -c '^'${db}':[ 	]' ${file} 2>/dev/null)
	if [[ ${numlines} -gt 1 ]]; then
		printf "%s\n" ${SC_FAILED} | logmsg
		printf "$(gettext '%s:  %s has %s \"%s\" entries!')\n" "${PROG}" "${file}" "${numlines}" "${db}" | logerr
		return 1
	fi

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

	# Remove the cluster switch, after commenting out the original
	ed -s ${file} << EOF >/dev/null 2>&1
/^${db}:[ 	]/a
${dbline}
.
-2
/^${db}:[ 	]/s/^/#/
/^${db}:[ 	]/s/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_unconfig()
#
#	Remove "cluster" switch from hosts entry in nsswitch files
#	Remove "cluster" switch from netmasks entry in nsswitch files
#
#	Return:
#		zero		Success
#				Failures on nsswitch files that are not
#				nsswitch.conf (ie. nsswitch.ldap) will
#				be ignored and zero will be returned.
#
#		non-zero	Failure when file is nsswitch.conf.
#
#####################################################
nsswitch_unconfig()
{
	typeset ext
	typeset file

	for ext in ${SC_NSSWITCH_EXT}
	do
		file=/etc/nsswitch."${ext}"
		# hosts
		nsswitch_rmcluster "${file}" hosts
		if [ $? -ne 0 ] && [ ${file} = "/etc/nsswitch.conf" ]; then
			return 1
		fi

		# netmasks
		nsswitch_rmcluster "${file}" netmasks
		if [ $? -ne 0 ] && [ ${file} = "/etc/nsswitch.conf" ]; then
			return 1
		fi
	done
	return 0
}
