#!/bin/sh
#
# Configure Solbourne server for diskless clients
#
# Copyright (c) 1989 Solbourne Computer, Inc.


#
# Global variables
#

# The original set up was that -f /some/directory meant to look in
# /some/directory for a bunch of tar files named by the tuple
# <module>.<arch>[.<release>].  To support installing from a cd-rom,
# this required some tweaking (for some reason, Sun didn't follow that
# naming scheme ;-).  The current arrangement will give the old
# behaviour if no config file is specified.  If a config file is
# given, then /some/directory is used as a prefix for each path name
# given in the config file.  In general, /some/directory will be the
# mount point for a cd-rom, although it could just as easily be a
# local disk.

Myname=`basename "$0"`
Usage="[-v] [-x] [-r] [-u] [-e] [-f tapefile] [-p pid] [-c config] [-t tapetype]  
			[-n os_release_name] client_arch
Where:
	'-v'		- specifies verbose output mode
	'-x'		- turns on shell execution trace
	'-r'		- always rewind tape between tar image files
	'-u'		- use server's '/usr' for clients (This is
			  the default for Solbourne servers unless
			  '-n <os_release_name>' is specified
	'-e'		- specifies that /etc/exports will NOT be modified
	'-f tapefile	- specifies tape device, or pathname from which
			  to read architecture-dependent
			  directories
	'-p <pid>'	- specifies process id of parent sysadmin install tool
			  (should be used only by tool)
	'-c <config>'	- specifies selected tape/on-disk configuration file
	'-t <tapetype>'	- specifies tape size - either '1/2' or '1/4'
	'-n <os_release_name>' - suffix name to use for machine 
			  dependent directories  (i.e., '-n sunos.4.1',
			  '-n sunos.4.0.3', or '-n ossmp.4.0d')
	'client_arch'	- machine type of diskless clients to be supported
			  by this server (sun2, sun3, sun4, sun4c, sun386, Series4, Series5, Series6, S4000)"

Vflag=:
Xflag=
TAPEFILE="/dev/nrst0"
NOREWIND=unknown
FINISH_REWIND=no
USE_SERVER_USR=no
NO_EXPORTS=no

#
# Filesystem specific pathnames
#

EXPORT_DIR=${EXPORT_DIR-/export}
EXPORT_ROOT=${EXPORT_ROOT-${EXPORT_DIR}/root}
EXPORT_USR=${EXPORT_USR-${EXPORT_DIR}/exec}
EXPORT_SWAP=${EXPORT_SWAP-${EXPORT_DIR}/swap}
EXPORT_FILE=${EXPORT_FILE-/etc/exports}
INETD_CONF=${INETD_CONF-/etc/inetd.conf}
BOOTPARAMS=${BOOTPARAMS-/etc/bootparams}
LOG=${LOG-/var/log/diskless_log}
MYPATH=${MYPATH-/usr/etc/setup}
SERVER_HOME=${SERVER_HOME-/home}
SERVER_HOSTS=${SERVER_HOSTS-/etc/hosts}
SERVER_BOOT=${SERVER_BOOT-/etc/bootparams}
CLIENT_HOME=${CLIENT_HOME-/home}
TFTPBOOT=${TFTPBOOT-/tftpboot}
TMP_ERR=/tmp/ins_err$$
TMP_CLN=/tmp/ins_cln$$
SDF=/tmp/ins_sdf$$
TOC=/tmp/ins_toc$$

#
# Sysadmin tool variables
#
ADMPID=""
CONFFILE=""
TAPETYPE=""
CDROM=""
TMP_ADMOUT=/tmp/ins_cs$$
TMP_ROOTTAPE=/tmp/ins_root$$
TMP_USRTAPE=/tmp/ins_usr$$
TMP_CONFFILE=/tmp/ins_conf$$

#
# Set up default path
#

PATH=/bin:/usr/bin:/usr/ucb:/usr/etc:${MYPATH}
export PATH

#
# Main function - test arguments and verify valid call
#
main()
{
	echo >/dev/null		# Shell bug workaround

	# Get optional argument

	while getopts "vxruef:t:p:c:n:" arg
	do
		case "$arg" in
		v)
			Vflag= ;;
		x)	Xflag="set -x"
			set -x;;
		u)	USE_SERVER_USR="yes";;
		e)	NO_EXPORTS="yes";;
		f)
			TAPEFILE="$OPTARG";;
		t)
			TAPETYPE="$OPTARG";;
		p)
			ADMPID="$OPTARG"
			TMP_ADMOUT=/tmp/ins_cs${ADMPID};;
		c)
			CONFFILE="$OPTARG"
			if [ ! -f "$CONFFILE" ]
			then
				fatal "Tape configuration file \"$CONFFILE\" does not exist"
			fi
			build_tmpconf;;
		r)
			NOREWIND=fsf;;
		n)	CLIENT_MACH_NAME=".$OPTARG";;
		*)
			usage ;;
		esac
	done

	shift `expr $OPTIND - 1`

	if [ $# -lt 1 ]
	then
		usage
	fi

	# Verify machine architecture(s) specified

	verify_arch $*

	# Test to see if we are running as root

	am_i_root || fatal "Must be run by the super user"

	# Check TAPEFILE name to see if remote hostname was specified

	if [ "`expr \"${TAPEFILE}\" : '.*\(:\)'`" = ":" ]
	then
		TAPESERVER="`expr \"${TAPEFILE}\" : '\(.*\):'"
		TAPEFILE="`expr \"${TAPEFILE}\" : '.*:\(.*\)'`"
		REMOTE_CMD="rsh ${TAPESERVER} -n"
	else
		TAPESERVER=""
		REMOTE_CMD=""
	fi

	# See if TAPEFILE specifies a true tape device or regular file

	check_tapefile "${TAPEFILE}"

	log "========== Configuring Server =========="
	log "$* Started `date`"
	trap "sig_abort; wrapup 1" 1 2 3 15

	# Do real work in a sub-shell, to capture error output for log
	# Redirect standard input to /dev/null, just in case something
	# tries to read.

	if [ "$ADMPID" != "" ]
	then
		do_cmd ${*+"$@"} >${TMP_ERR} 2>&1
	else
		(
			do_cmd ${*+"$@"} 2>&1
		) </dev/null | tee ${TMP_ERR}
	fi

	# Record any error output to log, and display to user
	# If there was an error, pass exit status up

	cmp -s ${TMP_ERR} /dev/null
	wrapup $?
}

#
# Extract appropriate data from tape
#

do_cmd()
{
	trap 1 2 3 15

	$Vflag display "	** Server diskless client configuration **"

	if [ "${ISTAPE}" != "" ]
	then
		if [ "${REMOTE_CMD}" = "" ]
		then
			# Make sure tape drive is valid
			mt -f $TAPEFILE status >/dev/null 2>&1

			if [ $? -ne 0 ]
			then
				fatal "Can not access tape drive \"$TAPEFILE\""
			fi
		else
			# Make sure remote machine is accessible
			${REMOTE_CMD} date >/dev/null 2>&1

			if [ $? -ne 0 ]
			then
				fatal "Can not access tape drive - check /.rhosts on \"$TAPESERVER\""
			fi
		fi
	fi

	ps -ax | grep -v grep | grep automount >/dev/null 2>&1
	automnt=$?

	# Create (if required) /export and subdirectories

	for i in ${EXPORT_DIR} ${EXPORT_ROOT} ${EXPORT_USR} ${EXPORT_SWAP} \
			${EXPORT_DIR}/share ${EXPORT_DIR}/dump ${EXPORT_DIR}/crash \
			${SERVER_HOME} ${EXPORT_USR}/kvm
	do
		if [ ! -d "$i" ]
		then
			mkdir "$i" || fatal "Can not create \"$i\""
		fi

		if [ $automnt -eq 0 ]
		then
	        	$Vflag display "Searching for remote mount point for $i ..."
		fi

		if ( remote_mount ${i} )
		then
			continue
		fi

		chmod 755 "$i"  2>/dev/null || error "WARNING -- Can not change permissions on \"$i\""
	done

	# Create Series4/Series5/Series6/S4000 "/usr" by linking to the real /usr
	# Make /export/exec/kvm symlink for server architecture

	Host=`arch -k`

	if [ "$CLIENT_MACH_NAME" != "" ]
	then
		MACH_NAME=$Host$CLIENT_MACH_NAME
	else
		MACH_NAME=$Host
	fi

	if [ "$USE_SERVER_USR" = "yes" ]
	then
		if [ ! -d ${EXPORT_USR}/${MACH_NAME} ]
		then
			ln -s /usr ${EXPORT_USR}/${MACH_NAME}
		fi
		if [ ! -d ${EXPORT_USR}/kvm/${MACH_NAME} ]
		then
			if [ $Host = "Series4" -o $Host = "Series5"  \
				-o $Host = "Series6" -o $Host = "S4000" ]
			then
				ln -s /usr/kvm ${EXPORT_USR}/kvm/${MACH_NAME}
			fi
		fi

	elif [ "$CLIENT_MACH_NAME" = "" ]
	then
		case $Host in
		Series4 | Series5 | Series6 | S4000)
			for i in Series4 Series5 Series6 S4000
			do
				if [ ! -d ${EXPORT_USR}/$i ]
				then
					ln -s /usr ${EXPORT_USR}/$i ||
						fatal "Can not create symbolic links in \"${EXPORT_USR}\""
				fi
				if [ "$Host" = "$i" ]
				then
					if [ ! -d ${EXPORT_USR}/kvm/$i ]
					then
						ln -s /usr/kvm ${EXPORT_USR}/kvm/$i
					fi
				fi
			done
			;;
		esac
	fi

	# Check for /tftpboot and create it if necessary

	create_tftpboot || exit 1


	# Make sure rpc.bootparamd is running

	start_rpcbootparamd || exit 1

	# Common directories and data files have been set up.  Now
	# loop through each machine architecture specified.

	while [ $# -gt 0 ]
	do
		config_arch "$1"
		shift
	done

	# If /etc/exports is to be modified, update.

	if [ "$NO_EXPORTS" = "no" ]
	then

		# Verify necessary server directories are being exported

		set +x
		update_exports || exit 1
		${Xflag}

		# Run /usr/etc/exportfs to read in new exports file

		# Unexport any current directories that are no longer
		# in the /etc/exports file.  Ignore any errors

		for fs in `awk '{ print $1 }' /etc/xtab`
		do
			grep -s "$fs" /etc/exports || exportfs -u "$fs"
		done 	>/dev/null 2>/dev/null

		# Now export/reexport everything in exports file
		exp_err=`exportfs -a 2>&1`

		if [ "$exp_err" != "" ]
		then
			display "WARNING: $exp_err - check \"${EXPORT_FILE}\""
		fi
	fi

	# Start up nfsd and mountd if necessary

	ps -ax | grep -v grep | grep nfsd >/dev/null 2>&1

	if [ $? -ne 0 ]
	then
		nfsd 8&
		rpc.mountd -n >/dev/console
	fi
}

#
# Machine architecture configuration -- read root and usr from tape
#

config_arch()
{
	MACH=${1-Series5}
	MACH_NAME="${MACH}${CLIENT_MACH_NAME}"
	if [ "$MACH" = "$MACH_NAME" ]
	then
		Proto=${EXPORT_ROOT}/proto.${MACH_NAME}
	else
		Proto=${EXPORT_ROOT}/${MACH_NAME}
	fi
	Usr=${EXPORT_USR}/${MACH_NAME}

	$Vflag display "Configuring server for ${MACH} diskless clients"

	# Check if we have enough disk space for this architecture

	check_free $MACH $Proto $Usr

	# For Solbourne release tape, check tape TOC/BOM for file
	# position info.

	case "$MACH" in
	Series4|Series5|Series6|S4000)
		check_tape_toc;;
	esac

	# Check if prototype root and client usr directories are going
	# to be read from tape.  If so, prompt now for tape file numbers

	if [ ! -d "$Proto" ]
	then
		get_tape_filenum "$MACH" root || exit 1
		Root_num=$TAPE_FILENUM
	else
		Root_num=""
	fi

	if [ ! -d "$Usr" ]
	then
		get_tape_filenum "$MACH" usr || exit 1
		Usr_num=$TAPE_FILENUM
	else
		Usr_num=""
	fi

	# If both root and usr will be read, ask if they are on
	# the same tape

	if [ "$Root_num" != "" -a "$Usr_num" != "" -a "$CONFFILE" = ""  \
			-a "$CDROM" = "" -a "$ROOT_TAPENO" != "$USR_TAPENO" ]
	then
		echo -n "Are the \"root\" and \"usr\" images on the same tape? " >/dev/tty
		read SAMETAPE </dev/tty

		# Allow either "yes" or "y" for the affirmative
		if [ "$SAMETAPE" = "yes" ]
		then
			SAMETAPE="y"
		fi
	elif [ "$ROOT_TAPENO" != "" -a "$ROOT_TAPENO" = "$USR_TAPENO" ]
	then
		SAMETAPE="y"
	else
		SAMETAPE="no"
	fi

	# Now get prototype root directory if necessary


	if [ ! -d "$Proto" ]
	then
		echo "rm -rf ${Proto}" >>${TMP_CLN}
		mkdir ${Proto} || fatal "Can not create \"${Proto}\""

		# Now read into it from distribution tape

		cd $Proto || exit 1
		TAPE_FILENUM=${Root_num-1}
		read_tape ${MACH} root $ROOT_TAPENO $ROOT_TAPEFORMAT || exit 1

		# For Solbourne systems, remove any vmunix* that comes from
		# the distribution tape.  Install_client will get a new one
		# from /export/exec/kvm/${MACH_NAME}

		case "$MACH" in
		Series4|Series5|Series6|S4000)
			rm -f vmunix*
		esac

		# Make sure the usr directory exists

		mkdir usr 2>/dev/null && chmod 755 usr 2>/dev/null

		# Build the device directory -- tar for the root fs is a joke!

		cd dev || exit 1
		case "$MACH" in
		sun*)
			./MAKEDEV std pty0 pty1 pty2 win0 win1 win2 ||
			fatal "Can not make devices in prototype directory"
			if [ ! -f sd0a ]
			then
				./MAKEDEV sd0 || fatal "Can not make disk devices"
			fi
			if [ ! -f rst0 ]
			then
				./MAKEDEV st0 || fatal "Can not make tape devices"
			fi
			for i in bwtwo0 cgtwo0 cgfour0
			do
				if [ ! -f $i ]
				then
					./MAKEDEV $i ||
					fatal "Can not make $i"
				fi
			done
			;;
		*)
			./MAKEDEV std pty0 pty1 pty2 win0 win1 win2 ||
			fatal "Can not make devices in prototype directory"

			if test $MACH = "S4000"
			then
				./MAKEDEV sga0 sga1 sga2 sga10 sga11\
				sga12 sga20 sga21 sga22 ||
				fatal "Can not make devices in prototype directory"
			fi
			;;
		esac
	fi

	# Verify that we have the /usr directory for this architecture


	if [ ! -d "$Usr" ]
	then
		# First create the high level directory

		echo "rm -rf $Usr" >>${TMP_CLN}
		mkdir $Usr || fatal "Can not create \"$Usr\""
		chmod 755 $Usr || fatal "Can not change permissions on \"$Usr\""

		# Now read into it from the distribution tape

		cd $Usr || exit 1
		TAPE_FILENUM=${Usr_num-1}
		read_tape ${MACH} usr $USR_TAPENO $USR_TAPEFORMAT || exit 1

		# Make sure there is a "kvm" directory mount point

		if [ ! -d kvm ]
		then
			mkdir kvm >/dev/null 2>/dev/null
			chmod 755 kvm >/dev/null 2>/dev/null
		fi
	fi

	# Read in other selected tape files as specified in <conffile>

	do_tapeinput

	# We now have the usr directory.  Copy things into /tftpboot

	config_tftpboot

	# Done with tape - rewind
	if [ "$FINISH_REWIND" = "yes" ]
	then
		rewind_tape
	fi
}

#
# Check root, swap, and export directories to see if we have enough
# free disk space
#

check_free()
{
	Mach=${1-${MACH}}
	Rdir=${2-${EXPORT_ROOT}/proto.${MACH}}
	Udir=${3-${EXPORT_USR}/${MACH_NAME}}
	Rpdir=`dirname $Rdir`
	Updir=`dirname $Udir`

	if [ "$Mach" = "Series4" -o "$Mach" = "Series5" \
		-o "$Mach" = "Series6" -o "$Mach" = "S4000" ]
	then
		# For Series4/Series5/Series6/S4000 architectures, / is small,
		# /usr is a symbolic link,
		# so we need only room for a big (32M) swap.
		Needr=2000
		Neede=1
		Needs=32768
	else
		# For other architectures, root needs are small,
		# but exec (/usr) is large, and swap is 16m.
		Needr=1000
		Neede=20000
		Needs=16384
	fi

	# Now check root, exec, and swap directories to see what we have

	Haver=`df ${Rpdir}|tail -1` || return
	Havee=`df ${Updir}|tail -1` || return
	Haves=`df $EXPORT_SWAP|tail -1` || return

	# If everything is on the same file system, add values

	if [ "$Havee" = "$Haves" ]
	then
		Neede=`expr $Neede + $Needs`
		Needs=0
	fi

	if [ "$Haver" = "$Havee" ]
	then
		Needr=`expr $Needr + $Neede`
		Neede=0
	fi

	if [ "$Haver" = "$Haves" ]
	then
		Needr=`expr $Needr + $Needs`
		Needs=0
	fi

	# Get real free values

	Haver=`echo $Haver|awk '{print $4}'`
	Havee=`echo $Havee|awk '{print $4}'`
	Haves=`echo $Haves|awk '{print $4}'`

	if [ "$Haver" -lt "$Needr" -o "$Havee" -lt "$Neede" -o "$Haves" -lt "$Needs" ]
	then
		display "WARNING:  There may not be enough free disk space
WARNING:  in $Rpdir, $Updir, or ${EXPORT_SWAP}
WARNING:  for architecture dependent executables or
WARNING:  client swap files
"
	fi
}

#
# Check Solbourne release tapes for standard TOC/BOM
#

check_tape_toc()
{
	# If cdrom or net, check for TOC, set "tape" filenumbers and return. 
	# If not a tape, or a configuration file was specified, just return
	if [ "${CDROM}" != "" ]
	then
		if [ ! -f "${CDROM}/TOC" ]
		then
			fatal "\"${CDROM}/TOC\" does not exist"
		fi
		set_tape_filenum ${MACH}
		return
	elif [ "${ISTAPE}" = "" -o "${CONFFILE}" != "" ]
	then
		return
	fi

	CURRENT_TAPE=x
	while [ "$CURRENT_TAPE" != "$ROOT_TAPENO" ]
	do
		Tape_wanted="${ROOT_TAPENO-1} (containing \"root\" for \"$MACH\" architecture)"

		# Prompt for ready to read tape
		if [ "$ADMPID" = "" ]
		then
			# Flush any previous input
			(stty raw; stty -raw) >/dev/tty 2>/dev/null
			display "Load release tape $Tape_wanted"
			echo -n "Enter RETURN when ready: " >/dev/tty
			line </dev/tty >/dev/null
		else
			sysadm_prompt "Load release tape $Tape_wanted"
		fi

		# Set program variables for standard tar locations
		set_tape_filenum ${MACH}

		# Flag this tape as having been read already
		LAST_TAPENO=${CURRENT_TAPE}

		# If tape mounted is not same as tape containing root, let
		# user know

		if [ "$CURRENT_TAPE" != "$ROOT_TAPENO" ]
		then
			display "The current release tape does not contain \"root\""
			display "You must load release tape $Tape_wanted"
		fi
	done
}

#
# set_tape_filenum()
# this function sets ROOT_TAPENO ROOT_TAPEFILE USR_TAPENO USR_TAPEFILE
# KVM_TAPENO and KVM_TAPEFILE by eval'ing a file generated by parsing
# a TOC and a SDF(package description file)
# 
# Uses the global ${MACH} to determine which KVM file to select
# 
set_tape_filenum()
{
	Etmp=/tmp/Evalme$$
	echo "rm -rf $Etmp ${SDF} ${TOC}" >>${TMP_CLN}

	TAPE_FILENUM=0
	
	if [ "${CDROM}" != "" ]
	then
		Input="${CDROM}/TOC"
	else
		position_tape "${MACH}" "-TOC-" || fatal "Can not position tape"
		Input="/dev/`expr \"${TAPEFILE}\" : '/dev/\(.*\)'`"
	fi

	${REMOTE_CMD} dd if=${Input} bs=1k 2>/dev/null | dd of=$TOC 2>/dev/null

	if [ $? -ne 0 ]
	then
		fatal "Error occurred reading tape"
	fi

	if [ "${CDROM}" != "" ]
	then
		awk_script=`search_path image.awk`
	else
		awk_script=`search_path toc.awk`
	fi

	eval `awk -f $awk_script MACH=${MACH} - < $TOC`

	if [ "$New_style" -eq 1 ]
	then
		handle_new_style
	else
		handle_old_style
	fi

	rm -f $SDF $TOC
}

# This builds a CONFFILE listing all the optional software that can be
# installed on a Solbourne.

handle_new_style()
{
	TAPE_FILENUM=$Optional_fileno

	if [ "${CDROM}" != "" ]
	then
		Input="${CDROM}/PDF1"
	else
		position_tape "${MACH}" "-SDF-" || fatal "Can not position tape"
	fi

	${REMOTE_CMD} dd if=${Input} bs=1k 2>/dev/null | dd of=$SDF 2>/dev/null

	if [ $? -ne 0 ]
	then
		fatal "Error occurred reading tape"
	fi

	if [ "${CDROM}" != "" ]
	then
		echo "${KVM_TAPENO}	kvm" >${TMP_CONFFILE}
		awk_script=`search_path optimage.awk`
	else
		echo "${KVM_TAPENO}	${KVM_TAPEFILE}	kvm" >${TMP_CONFFILE}
		awk_script=`search_path opt.awk`
	fi

	awk -f $awk_script $SDF \
		| sort >> ${TMP_CONFFILE}
	return
}

handle_old_style()
{
	TAPE_FILENUM=$Sdf_Tapefile
	position_tape "${MACH}" "-SDF-" || fatal "Can not position tape"

	${REMOTE_CMD} dd if=${Input} bs=1k 2>/dev/null | dd of=$SDF 2>/dev/null
	awk_script=`search_path old.awk`
	eval `awk -f $awk_script MACH=${MACH} - < $SDF`
	return
}

#
# Read standard distribution tape for this architecture into current directory
#

read_tape()
{
	Mach="${1-${MACH}}"
	Fs="${2}"
	Tapeno="${3-1}"
	Format=`echo ${4-tar} | tr A-Z a-z`

	if [ "$Fs" = "" ]
	then
		fatal "Missing file system name to read from tapefile"
	fi

	# If input is a tape file, position to correct tape mark

	if [ "$ISTAPE" != "" ]
	then
		if [ "$CONFFILE" != "" -o "$Mach" = "Series4" -o "$Mach" = "Series5" \
					-o "$Mach" = "Series6" -o "$Mach" = "S4000" ]
		then
			if [ "$LAST_TAPENO" != "$Tapeno" ]
			then
				SAMETAPE="no"
				if [ "$LAST_TAPENO" != "" ]
				then
					rewind_tape
				else
					FINISH_REWIND=yes
				fi
			else
				SAMETAPE="y"
			fi
			LAST_TAPENO="$Tapeno"
		fi
		if [ "$SAMETAPE" != "y" ]
		then
			Tape_wanted="$Tapeno (containing \"${Fs}\" for \"$MACH\" architecture)"

			if [ "$ADMPID" = "" ]
			then
				# Flush any previous input
				(stty raw; stty -raw) >/dev/tty 2>/dev/null
				display "Load release tape $Tape_wanted"
				echo -n "Enter RETURN when ready: " >/dev/tty
				line </dev/tty >/dev/null
			else
				sysadm_prompt "Load release tape $Tape_wanted"
			fi
		fi
		display "Reading \"$Fs\" ..."
		position_tape "$Mach" "$Fs" || fatal "Can not position tape"
		Input="/dev/`expr \"${TAPEFILE}\" : '/dev/\(.*\)'`"
	else
		if [ `expr match "$Tapeno" '.*/.*'` != 0 ]
		then
			# Coming from a cd-rom-like tree
			Input=${TAPEFILE}/$Tapeno
		else
			# Old-style compatibility
			Input="${TAPEFILE}/${Fs}.${MACH_NAME}"
		fi

		if [ ! -f "$Input" ]
		then
			fatal "Input file \"$Input\" does not exist"
		fi
		display "Reading \"$Fs\"..."
	fi

	# Handle compressed tar files, created with tar | compress.

	if [ $Format = tarz ]
	then
		filter="uncompress"
	elif [ $Format = tar ]
	then
		filter="cat"
	else
		fatal "Unknown file format \"$Format\""
	fi

	# Now read input file into current directory
	if [ "${Fs}" = "kvm" -o "${Fs}" = "Kvm" ]
	then
		(
		echo "rm -rf ${EXPORT_USR}/kvm/${MACH_NAME}" >>${TMP_CLN}
		mkdir ${EXPORT_USR}/kvm ${EXPORT_USR}/kvm/${MACH_NAME} 2>/dev/null
		cd ${EXPORT_USR}/kvm/${MACH_NAME}
		${REMOTE_CMD} dd if=${Input} bs=${BS}b 2>/dev/null |
			$filter |
			(tar xpfbB - ${BS} || exit 1; 
				 dd of=/dev/null bs=${BS}b 2>/dev/null)

		# Check for errors

		if [ $? -ne 0 ]
		then
			fatal "Error occurred reading tape"
		fi
		)
	elif [ "${Fs}" = "Sys" -o "${Fs}" = "sys" -a -h sys ]
	# SunOS 4.1 has sys in kvm/sys -- correct symbolic link
	then
		mkdir ../kvm/${MACH_NAME}/sys 2>/dev/null
		mv sys sys.orig
		ln -s ../kvm/${MACH_NAME}/sys sys
		${REMOTE_CMD} dd if=${Input} bs=${BS}b 2>/dev/null |
			$filter |
			(tar xpfbB - ${BS} || exit 1; 
				 dd of=/dev/null bs=${BS}b 2>/dev/null)

		# Check for errors
		if [ $? -ne 0 ]
		then
			rm -f sys
			mv sys.orig sys
			fatal "Error occurred reading tape"
		fi
		rm -f sys
		mv sys.orig sys
	else

		${REMOTE_CMD} dd if=${Input} bs=${BS}b 2>/dev/null |
			$filter |
			(tar xpfbB - ${BS} || exit 1; 
				 dd of=/dev/null bs=${BS}b 2>/dev/null)

		# Check for errors

		if [ $? -ne 0 ]
		then
			fatal "Error occurred reading tape"
		fi
	fi
}

#
# Rewind current installation tape
#

rewind_tape()
{
	${REMOTE_CMD} mt -f $TAPEFILE rewind

	if [ $? -ne 0 ]
	then
		fatal "Can not rewind tape"
	fi
}

#
# Position installation tape to correct file mark
#

position_tape()
{
	Mach="${1-${MACH}}"
	Fs="${2}"

	Tapen="/dev/`expr \"${TAPEFILE}\" : '/dev/\(.*\)'`"

	# 4.0.1 and newer systems support the "mt asf <x>" command.
	# We must check to see if our tape host supports it.

	if [ "${NOREWIND}" = "unknown" ]
	then
		# Rewind tape just to see that it is responding
		${REMOTE_CMD} mt -f $TAPEFILE rewind

		if [ $? -ne 0 ]
		then
			if [ "${REMOTE_CMD}" = "" ]
			then
				fatal "Can not rewind tape"
			else
				fatal "Can not rewind tape - check /.rhosts on \"$TAPESERVER\""
			fi
		fi

		# Now try "asf"

		if [ "`${REMOTE_CMD} mt -f $TAPEFILE asf 1 2>&1`" = "" ]
		then
			# Okay, we do not need to do rewind
			NOREWIND=asf
		else
			# Old system, must do it the slow way
			NOREWIND=fsf
		fi
	fi

	if [ "${NOREWIND}" = "fsf" ]
	then
		# Always rewind tape to beginning, even if root and usr are on the
		# same tape.  While this takes longer, it should be safer

		${REMOTE_CMD} mt -f $TAPEFILE rewind

		if [ $? -ne 0 ]
		then
			if [ "${REMOTE_CMD}" = "" ]
			then
				fatal "Can not rewind tape"
			else
				fatal "Can not rewind tape - check /.rhosts on \"$TAPESERVER\""
			fi
		fi
	fi

	${REMOTE_CMD} mt -f $Tapen ${NOREWIND} $TAPE_FILENUM

	if [ $? -ne 0 ]
	then
		fatal "Can not position tape"
	fi
}

#
# Get tape file number for desired file system tar image
#

get_tape_filenum()
{
	Mach="${1-${MACH_NAME}}"
	Fs="${2}"

	# If tape configuration file or image was specified, use it

	if [ "$CONFFILE" != "" -o "$CDROM" != "" ]
	then
		case "$Fs" in
		root)	TAPE_FILENUM=$ROOT_TAPEFILE;;
		usr)	TAPE_FILENUM=$USR_TAPEFILE;;
		*)	fatal "Can not determine tape file number"
		esac
		return
	fi

	# If we're not installing from a tape, then fake it

	if [ "$ISTAPE" = "" ]
	then
		TAPE_FILENUM=1
		return
	fi

	# For Solbourne servers, assume a constant place
	# if no label found
	if [ "$Mach" = "Series4" -o "$Mach" = "Series5"  \
				-o "$Mach" = "Series6" -o "$Mach" = "S4000" ]
	then
		case "$Fs" in
		root)
			TAPE_FILENUM=$ROOT_TAPEFILE
			if [ "$TAPE_FILENUM" = "" ]
			then
				TAPE_FILENUM=2
			fi;;
		usr)
			TAPE_FILENUM=$USR_TAPEFILE
			if [ "$TAPE_FILENUM" = "" ]
			then
				TAPE_FILENUM=3
			fi;;
		*)
			fatal "Can not determine tape file number";;
		esac
	else
		while :
		do
			echo -n "Enter tape file number for \"$Fs\":  " >/dev/tty
			read Filen </dev/tty
			case "$Filen" in
			[0-9]* )
				TAPE_FILENUM=`expr $Filen - 1`
				break;;
			*)
				display "Invalid tape file number";;
			esac
		done
	fi
}

#
# Tape configuration file utilities
#

build_tmpconf()
{
	if [ "$CONFFILE" = "" ]
	then
		fatal "Tape configuration file not specified"
	fi


# Extract root tar tape information from configuration file

	sed -n	-e '/^[ 	]*#/d' 	\
		-e 's/\(.*\)[ 	]*#.*/\1/' 	\
		-e 's/[ 	]*$//'	\
		-e '/[ 	]root$/p' \
		-e '/[ 	]root[ 	][ 	]*.*$/p' \
		<$CONFFILE >$TMP_ROOTTAPE ||
			fatal "Can not determine root tar tape location"

	cmp -s $TMP_ROOTTAPE /dev/null && fatal "Can not determine root tar tape location"

	eval `awk '\
		NF == 4 { printf ("ROOT_TAPENO=%s ;", $1); \
			  printf ("ROOT_TAPEFILE=%s ;", $2); \
			  print "ROOT_TAPEFORMAT="$4 \
			} \
		NF != 4 { printf ("ROOT_TAPENO=%s ;", $1); \
			  print "ROOT_TAPEFILE="$2 \
			}' <$TMP_ROOTTAPE 2>/dev/null`

	rm -f $TMP_ROOTTAPE

# Extract usr tar tape information from configuration file

	sed -n	-e '/^[ 	]*#/d' 	\
		-e 's/\(.*\)[ 	]*#.*/\1/' 	\
		-e 's/[ 	]*$//'	\
		-e '/[ 	]usr$/p' \
		-e '/[ 	]usr[ 	][ 	]*.*$/p' \
		<$CONFFILE >$TMP_USRTAPE ||
			fatal "Can not determine usr tar tape location"

	cmp -s $TMP_USRTAPE /dev/null && fatal "Can not determine usr tar tape location"

	eval `awk '\
		NF == 4 { printf ("USR_TAPENO=%s ;", $1); \
			  printf ("USR_TAPEFILE=%s ;", $2); \
			  print "USR_TAPEFORMAT="$4 \
			} \
		NF != 4 { printf ("USR_TAPENO=%s ;", $1); \
			  print "USR_TAPEFILE="$2; \
			}' <$TMP_USRTAPE 2>/dev/null`

	rm -f $TMP_USRTAPE

# Build new temporary tape configuration file of selected
# directories not including root and usr

	sed	-e '/^[ 	]*#/d' 	\
		-e 's/\(.*\)[ 	]*#.*/\1/' 	\
		-e 's/[ 	]*$//'	\
		-e '/^$/d'	\
		-e '/[ 	]root$/d'	\
		-e '/[ 	]root[ 	][ 	]*.*$/d' \
		-e '/[ 	]usr$/d' \
		-e '/[ 	]usr[ 	][ 	]*.*$/d' \
		<$CONFFILE >$TMP_CONFFILE

	cmp -s $TMP_CONFFILE /dev/null && rm -f $TMP_CONFFILE
}

#
# Read all selected tape files specified in <conffile>
#

do_tapeinput()
{
	Usr=${EXPORT_USR}/${MACH_NAME}

	# If no tape configuration file was specified, just return

	if [ "$CONFFILE" = "" ]
	then
		# Don't do anything for non-Solbourne equipment
		if [ "${MACH}" != "Series4" -a "${MACH}" != "Series5"  \
				-a "${MACH}" != "Series6" -a "${MACH}" != "S4000" ]
		then
			return
		fi

		# If we are a Series4, Series5, Series6, or S4000, we must set 
		# up /export/exec/kvm

		# If directory already exists, just return
		if [ -d ${EXPORT_USR}/kvm/${MACH_NAME} ]
		then
			return
		fi

		if [ "$New_style" -eq 0 ]
		then
			# Must read kvm from tape - build tape config file
			echo "${KVM_TAPENO}	${KVM_TAPEFILE}	kvm" >${TMP_CONFFILE}
		fi

		# Fall through as if we had a configuration file specified

	fi

	# If only "root" and "usr" were specified in configuration file,
	# just return


	if [ ! -r $TMP_CONFFILE ]
	then
		return
	fi

	# Set current directory to "usr"

	cd $Usr || exit 1

	# If client's /usr is soft-linked to server's /usr, don't read tape
	# except for kvm of a different host type.
	
	if [ -h "$Usr" ]
	then
		Soft_usr=1
	else
		Soft_usr=0
	fi

	# If client's /usr/kvm is soft-linked to server's, don't read kvm
	# from tape.

	if [ -h "${EXPORT_USR}/kvm/${MACH_NAME}" ]
	then
		Soft_kvm=1
	else
		Soft_kvm=0
	fi

	# If both /usr and /usr/kvm are already soft-linked, just return

	if [ $Soft_usr -eq 1 -a $Soft_kvm -eq 1 ]
	then
		return
	fi

	# Loop through each line of configuration file

	while input=`line`
	do
		if [ "$ISTAPE" != "" ]
		then
			eval `echo "$input" | awk '\
				NF == 4 { printf ("FS_TAPENO=%s ;", $1); \
					  printf ("TAPE_FILENUM=%s ;", $2); \
					  printf ("FS_NAME=%s ;", $3); \
					  print "FORMAT="$4 \
					} \
				NF != 4 { printf ("FS_TAPENO=%s ;", $1); \
					  printf ("TAPE_FILENUM=%s ;", $2); \
					  printf ("FS_NAME=%s ;", $3); \
					  print "FORMAT=tar" \
					}'`
		else
			eval `echo "$input" | awk '\
				NF == 3 { printf ("FS_TAPENO=%s ;", $1); \
					  printf ("FS_NAME=%s ;", $2); \
					  print "FORMAT="$3 \
					} \
				NF != 3 { printf ("FS_TAPENO=%s ;", $1); \
					  printf ("FS_NAME=%s ;", $2); \
					  print "FORMAT=tar" \
					}'`
		fi

		if [ $FS_NAME != "kvm" -a $FS_NAME != "Kvm" ]
		then
			if [ $Soft_usr -eq 1 ]
			then
				continue
			fi
		else
			if [ $Soft_kvm -eq 1 ]
			then
				continue
			fi
		fi
		read_tape ${MACH} $FS_NAME $FS_TAPENO $FORMAT
	done <${TMP_CONFFILE}

	# Exit on error from sub-shell
	if [ $? -ne 0 ]
	then
		exit 1
	fi
}

#
# Create /etc/bootparams and start rpc.bootparamd if necessary
#

start_rpcbootparamd()
{
	if [ ! -f ${BOOTPARAMS} ]
	then
		cp /dev/null ${BOOTPARAMS} || fatal "Can not create \"${BOOTPARAMS}\""
	fi

	# Check to see if rpc.bootparamd is running

	expr "`ps -ax|grep rpc.bootparamd|grep -v grep`" : '.*rpc.bootparamd' >/dev/null

	if [ $? -ne 0 ]
	then
		rarpd ex0 `hostname` 2>/dev/null
		rarpd ex1 `hostname` 2>/dev/null
		rarpd ei0 `hostname` 2>/dev/null
		rarpd -a 2>/dev/null
		rpc.bootparamd
	fi
}

#
# Create tftpboot directory if necessary, and verify inetd.conf
#

create_tftpboot()
{
	if [ ! -d ${TFTPBOOT} ]
	then
		mkdir ${TFTPBOOT} || fatal "Can not create \"${TFTPBOOT}\""
		cp /usr/stand/*boot* ${TFTPBOOT} ||
			fatal "Can not copy /usr/stand/*boot* to ${TFTPBOOT}"
	fi

	rm -f ${TFTPBOOT}/tftpboot && ln -s . ${TFTPBOOT}/tftpboot

	if [ $? -ne 0 ]
	then
		fatal "Can not create symbolic link in \"${TFTPBOOT}\""
	fi

	# Fixup /etc/inetd.conf

	ed - ${INETD_CONF} <<-_EOF_ || fatal "Can not update ${INETD_CONF}"
		g/tftp/s/^#//
		g/tftp/s/-s //
		w
		q
	_EOF_

	# Send SIGHUP to inetd to reread new configuration

	ipid=`ps -ax|grep inetd|grep -v grep|awk '{print $1}'`

	if [ "$ipid" != "" ]
	then
		kill -1 $ipid
	else
		display "WARNING: inetd server not sent SIGHUP"
		display "WARNING: Type 'kill -1 <inet pid>' or reboot"
	fi
}

#
# Install /usr/stand files for this architecture into /tftpboot directory
#

config_tftpboot()
{
	if [ -d ${EXPORT_USR}/kvm/${MACH_NAME}/stand ]
	then
		if [ ! -f ${EXPORT_USR}/kvm/${MACH_NAME}/stand/boot.sun4 ]
		then
			if [ "${MACH}" = "Series4" -o "${MACH}" = "Series5"  \
					-o "${MACH}" = "Series6" -o "${MACH}" = "S4000" ]
			then
				cp /dev/null ${EXPORT_USR}/kvm/${MACH_NAME}/stand/boot.sun4
			fi
		fi
		cp ${EXPORT_USR}/kvm/${MACH_NAME}/stand/*boot* ${TFTPBOOT} && rm -rf ${TFTPBOOT}/copy
		if [ $? -ne 0 ]
		then
			fatal "Can not copy ${EXPORT_USR}/kvm/${MACH_NAME}/stand files to ${TFTPBOOT}"
		fi
	else
		cp ${EXPORT_USR}/${MACH_NAME}/stand/*boot* ${TFTPBOOT} && rm -f ${TFTPBOOT}/copy
		if [ $? -ne 0 ]
		then
			fatal "Can not copy ${EXPORT_USR}/${MACH_NAME}/stand files to ${TFTPBOOT}"
		fi
	fi

}

#
# Check tape file name specified (or default) to see if input
# is a real tape device or plain file name.  Set the
# ISTAPE environment variable appropriately

check_tapefile()
{
	Tape="`expr \"${1}\" : '/dev/.*\(..\)[0-9]'`"

	case "$Tape" in
	ar | st)
	#	BS=20
		BS=16
		ISTAPE="$Tape";;
	mt | xt)
		BS=20
		ISTAPE="$Tape";;
	*)
		if [ "$CONFFILE" = "" ]
		then
			CDROM="${1}"
		fi
		BS=20
		ISTAPE="";;
	esac

# Check for sysadmin passing tape architecture and size info

	case "$TAPETYPE" in
	1/2)
		BS=20;;
	1/4)
		BS=16;;
	*)
		;;
	esac
}

#
# Verify machine architecture is being supported
#

verify_arch()
{
	while [ $# -gt 0 ]
	do
		case "$1" in
		sun2 | sun3 | sun4 | sun4c | sun386 | Series4 | Series5 | Series6 | S4000)
			;;
		*)
			error "Invalid machine architecture \"$1\""
			usage
			exit 1
		esac
		shift
	done
}

#
# Display message directly to user terminal
#

display()
{
	if [ "${ADMPID}" != "" ]
	then
		sysadm_display NOWAIT "$*"
	else
		echo "$*" >/dev/tty
	fi
}

#
# fatal error - print message and exit
#

fatal()
{
	error "$*"
	wrapup 1
	exit 1
}

#
# error() prints an error message to stderr
#

error()
{
	if [ "${ADMPID}" != "" ]
	then
		if [ -f ${TMP_ERR} ]
		then
			Errout="`cat ${TMP_ERR}`
$Myname: $*"
			rm -f ${TMP_ERR}
		else
			Errout="$Myname: $*"
		fi
		sysadm_display NOWAIT "$Errout"
	else
		echo "$Myname: $*" 1>&2
	fi
}

#
# usage() prints a usage message (usage is in the variable Usage)
#

usage()
{
	if [ "${ADMPID}" != "" ]
	then
		sysadm_display NOWAIT "usage: $Myname $Usage"
	else
		echo "usage: $Myname $Usage" 1>&2
	fi
	exit 2
}

#
# Let sysadmin installation tool handle display to user
#

sysadm_display()
{
	# First argument signals WAIT/NOWAIT/EXIT; print on separate line
	echo "$1" >${TMP_ADMOUT}
	shift
	echo "$*" >>${TMP_ADMOUT}

	while
		kill -0 ${ADMPID:-x} 2>/dev/null || break
		test -f ${TMP_ADMOUT}
	do
		sleep 2
	done

	if [ -f ${TMP_ADMOUT} ]
	then
		echo "$*" >/dev/tty
		rm -f ${TMP_ADMOUT}
	fi
}

#
# System administration tool interface to prompt user for input
#

sysadm_prompt()
{
	if [ "$ADMPID" = "" ]
	then
		display "$*"
	else
		sysadm_display WAIT "$*"
	fi
}


#
# Signal cleanup.  Print message to user and to log
#

sig_abort()
{
	log "Aborted by signal"
	display ""
}

#
# Record message in installation log
#
log()
{
	echo "$Myname: $*" >>${LOG}
}

#
# Look through $PATH for a particular file.  The idea is that we
# can have non-executable files that are somewhere along the search
# path.  $1 is the file to search for.  If found, the full pathname
# of the file is sent to standard out (presumably our caller is doing
# something like var=`search_path filename`).
#

search_path()
{
	found_it=0
	for i in `echo $PATH | sed 's/:/ /g'`
	do
		if [ -f $i/$1 ]
		then
			found_it=1
			break
		fi
	done

	if [ $found_it = 0 ]
	then
		echo $1: not found 1>&2
		exit 1
	fi

	echo $i/$1
}

#
# Final command wrap up.  Record errors and completion in log.
# Remove temporary files
#

wrapup()
{
	trap '' 1 2 3 15

	Errout="Failed due to error.  Cleaning up directories"
	if [ -f ${TMP_ERR} ]
	then
		cat ${TMP_ERR} >>${LOG}
		if [ "$ADMPID" != "" ]
		then
			Errout="`cat ${TMP_ERR}`
$Errout"
		fi
	fi

	log "Completed with status $1 `date`"
	if [ "$1" != "0" ]
	then
		log "***** Failed due to error. *****"
		if [ -f ${TMP_CLN} ]
		then
			error "$Errout"
			sh ${TMP_CLN}
		else
			error "$Errout"
		fi

		# Unexport any current directories that are no longer
		# in the /etc/exports file.  Ignore any errors
	
		for fs in `cut -d' ' -f1 </etc/xtab`
		do
			grep -s "$fs" /etc/exports || exportfs -u "$fs"
		done 	>/dev/null 2>/dev/null

		exportfs -a 2>/dev/null
		if [ "$ADMPID" != "" ]
		then
			sysadm_display EXIT "Server Configuration Failed"
		fi
	elif [ "$ADMPID" != "" ]
	then
		sysadm_display EXIT "Server Configuration Completed Successfully"
	fi
	rm -f ${TMP_ERR} ${TMP_CLN} ${TMP_CONFFILE} ${TMP_ROOTTAPE} ${TMP_USRTAPE}
	exit $1
}

#
# /usr/bin/dirname is part of the SysV package, and so it's existence can
# not be counted on
#
dirname()
{
	expr ${1-.}'/' : '\(/\)[^/]*/$' \| ${1-.}'/' : '\(.*[^/]\)//*[^/][^/]*//*$' \| .
}

#
# am_i_root is the equivalent of
#   (id | grep 'uid=0') > /dev/null
# which cannot be relied on because id is SysV only
#
am_i_root()
{
	if [ `whoami` = "root" ]
	then
		return 0
	else
		return 1
	fi
}

#
# Determine if a directory is remote-mounted.
#

remote_mount()
{
	# The reason for scanning both /etc/fstab and /etc/mtab is
	# to catch the automounter doing things behind our back.  This
	# is not a complete solution -- if the automounter isn't running,
	# but *is* mounting a particular directory for us, we lose.  Not
	# much that can be done about it, since in the general case it
	# is impractical to attempt to determine what the automounter will
	# do on any given system.
 
	# Due to an oddity with "expr length /" dying with a syntax
	# error, we prepend X to all length determinations.  Since all
	# we really care about are relative lengths, this has no
	# effect -- a constant offset of 1 doesn't really matter....

	dir=${1}
	longest=""
	length=0
	result=1
	for i in `cat /etc/fstab /etc/mtab | awk '{print $2}' | sort -u`
	do
		if [ 0 != `expr "${dir}" : "${i}.*"` \
			-a \( `expr length "X$i" - 1` -gt $length \) ]
		then
			longest="${i}"
			length=`expr length "X$i" - 1`
		fi
	done

	# The bit with head is because we will (probably) get multiple
	# entries returned.  Just to avoid confusing expr (and myself),
	# it's easist to throw away any duplicates.

	if [ "$longest" != "" ]
	then
		# That's [<spc><tab>]
		source=`cat /etc/fstab /etc/mtab \
			| grep -e '[ 	]'$longest'[ 	]' \
			| head -1 \
			| awk '{print $1}'`
		if [ "`expr "${source}" : '..*\(:\)'`" = ":" ]
		then
			# Return true -- it's not a local mount
			result=0
		fi
	fi

	return $result
}

#
# Perform main function
#

main $*
exit 0
