#! /bin/sh
#
#	Copyright 06/24/96 Sun Microsystems, Inc.  All Rights Reserved.
#
#ident	"@(#)hafs_mount.sh	1.41	96/06/24 SMI"
#
# hafs_mount [logical_hostnames...]
#	Actually uses environment variable HA_METASETSERVE
#
#	Called by cltrans_fsck_mount.
#
#	fsck and mount file systems associated with given logical_hostnames.
#
#	Exits non-zero if any of the mounts failed.
#
# Environment:
#	(see HA_ENV for details)
#

argv0=`basename $0`
PROG=$argv0
LOGGER=logger
TMPERR=/tmp/${PROG}.err
TMPLOCKERR=/tmp/${PROG}.lockdeb.err

if [ -z "$HA_CLUSTER" ]; then
	if [ ! -r "$HA_ENV" ]; then
		$LOGGER -p local7.err -t "hadf" "$argv0:  Cannot determine correct HA environment"
		exit 1
	fi
	. $HA_ENV
fi

# include HA utilities library
. utilities
if [ $? -ne 0 ]; then
	$LOGGER -p $HA_SLOGLEV -t "$HA_SLOGTAG" "$argv0:  Cannot find HA utilities library!"
	exit 1
fi

#
# saveentry fstype special mountp fsckdev options
#
saveentry() {
	echo "$1 $2 $3 $4 $5" >> $ALTM
}

#
# ha_mountall <vfstab>
#
#	fsck each currently unmounted file system in the specified
#	vfstab.  Construct a list of all the file systems for which
#	fsck returned success, then mount them.  If any errors are
#	encountered along the way, log the errors and continue
#	processing the remaining file systems.
#
#	Return codes:
#		0	all file systems checked and mounted successfully
#		1	wrong usage
#		2	specified vfstab not readable
#		3	some file system(s) failed to fsck or
#			bad fstype specified or no fsckdev specified
#		4	simple mount with no args failed

# The return codes from fsck have the following meanings.
#	 0 - file system is unmounted and okay
#	32 - file system is unmounted and needs checking (fsck -m only)
#	33 - file system is already mounted
#	34 - cannot stat device
#	36 - uncorrectable errors detected - terminate normally
#	37 - a signal was caught during processing
#	39 - uncorrectable errors detected - terminate right away
#	40 - for root, same as 0 (used by bcheckrc to remount root)
#
#	0|33|40 we regard as OK; the rest are errors
#
ha_mountall()
{
	# Next line is fix for bugid 1256164
	ufs_fscklist=""
	retvalue=0
	ALTM=$HA_TMP/hamountall.$$
	HA_mounted_fs=$HA_TMP/mounted_fs

	#
	# Process command line args
	#
	if [ $# -ne 1 ]; then
		logerr "Usage: ha_mountall <file_system_table>"
		return 4
	fi

	rm -f $HA_mounted_fs
	mount > $HA_mounted_fs
	if [ $? -ne 0 ]; then
		logerr "mount command failed"
		return 3
	fi

	# get file system table name and make sure file exists
	FSTAB=$1

	if [ ! -r "$FSTAB" ]; then
		logerr "file system table ($FSTAB) not readable"
		return 2
	fi

	#	file-system-table format:
	#
	#	column 1:	special-  block special device or resource name
	#	column 2: 	fsckdev-  char special device for fsck 
	#	column 3:	mountp-   mount point
	#	column 4:	fstype-   File system type
	#	column 5:	fsckpass- not used by this script
	#	column 6:	automnt-  not used by this script
	#	column 7:	mntopts-  -o specific mount options

	#	White-space separates columns.
	#	Lines beginning with \"#\" are comments.  Empty lines are
	#	ignored.  A '-' in any field is a no-op.

	# We first sort the file_system_table by the mount point (column 3),
	# and then do the mounts in that order.  This ensures that
	# directories higher in the hierarchy get mounted first, before
	# directories which are lower in the hierarchy and which have
	# their mount points living in the higher-up directories.

	# sort on the third (+2) field, which is the mount point
	HA_sorted_fstab=${HA_VAR}/fstab_sorted
	rm -f $HA_sorted_fstab
	sort -b +2 $FSTAB -o $HA_sorted_fstab 2>$TMPERR
	if [ $? -ne 0 ]; then
		logerr "Cannot sort ${FSTAB}: `cat $TMPERR`"
		READFSTAB=$FSTAB
	else
		READFSTAB=$HA_sorted_fstab
	fi
	rm -f $TMPERR

	#
	# Read READFSTAB, fsck'ing appropriate file systems:
	#
	exec < $READFSTAB
	while  read special fsckdev mountp fstype fsckpass automnt mntopts
	do
		case $special in
		'#'* | '')	#  Ignore comments, empty lines
				continue ;;
		'-')		#  Ignore no-action lines
				continue ;;
		*)		;;
		esac

		if [ "$fstype" = "-"  -o "$fstype" != "ufs" ]; then
			logerr "unsupported FSType ($fstype) for $special"
			continue
		fi

		if [ "$mntopts" != "-" ]; then
			OPTIONS="-o $mntopts"		# Use mount options if any
		else
			OPTIONS=""
		fi

		# don't need to fsck if it's already mounted
		# Use grep instead of expr to eliminate the debugging output
		grep "[ 	]$special[ 	]" $HA_mounted_fs > /dev/null 2>&1
		if [ $? -eq 0 ]; then
			continue
		fi

		#
		# Can't fsck if no fsckdev is specified
		#
		if [ "$fsckdev" = "-" ]; then
			logwarning "no fsckdev specified for $special"
			# we'll try to mount it anyway
			saveentry $fstype $special $mountp "$fsckdev" "$OPTIONS"
			retvalue=3
			continue
		fi

		ufs_fscklist="$ufs_fscklist $fsckdev"
		saveentry $fstype $special $mountp $fsckdev "$OPTIONS"

	done
	rm -f $HA_sorted_fstab

	fsck_ok=1

	# fsck ufs's
	#
	if [ "$ufs_fscklist" ]; then
		/usr/sbin/fsck -o p $ufs_fscklist </dev/null >/dev/null 2>&1
		case $? in
		0|40|33)        # file system OK
			;;
		*)	# couldn't fix the file system
			fsck_ok=0
			retvalue=3
			;;
		esac
	fi

	# Mount file systems.
	# Use the -O "Overlay" mount option to handle the case where
	# the device is busy because somebody who really shouldn't be
	# is using it.  (For example, a system admin cd's to the mount
	# point when the file system is unmounted.)  We'll further try
	# to avoid this situation by moving the mount point(s) aside
	# when we don't own the diskset.

	if [ ! -f "$ALTM" ]; then
		return $retvalue
	fi

	exec < $ALTM
	while  read fstype special mountp fsckdev options
	do
		# make sure mount point exists
		if [ ! -d $mountp ]; then
			hidden_name=`dirname ${mountp}`/.`basename ${mountp}.not_served`
			if [ -d $hidden_name ]; then
				# restore previously unused mount point
				mv $hidden_name $mountp
			else
				mkdir -p $mountp
			fi
		fi

		if [ $fsck_ok -eq 1 -o "$fsckdev" = "-" ]; then

			/usr/lib/fs/ufs/mount $options -O $special $mountp > /dev/null 2> /tmp/mount.err
			if [ $? -ne 0 ]; then
				logerr "mount failed on $mountp: `cat /tmp/mount.err`"
				rm -f /tmp/mount.err
				retvalue=5
			fi
		else
			/usr/sbin/fsck -m -F $fstype $fsckdev </dev/null >/tmp/fsck.err 2>&1
			case $? in
			0|40)	/usr/lib/fs/ufs/mount $options -O $special $mountp > /dev/null 2> /tmp/mount.err
				if [ $? -ne 0 ]; then
					logerr "mount failed on $mountp: `cat /tmp/mount.err`"
					rm -f /tmp/mount.err
					retvalue=5
				fi
				;;
			*)	logerr "fsck of $fsckdev failed: `cat /tmp/fsck.err`"
				rm -f /tmp/fsck.err
				retvalue=3
				;;
			esac
		fi
	done

	/usr/bin/rm -f $ALTM
	rm -f $HA_mounted_fs

	return $retvalue
}


#
# ha_quotas() -		
#
ha_quotas_diskset() {
	#
	# quotas are not supported by HA-NFS.  Just issue warning message
	# to that effect and go on.  Caution: if this code is changed to
	# actually check quotas, then reconsider whether caller can
	# legit fork (caller is elsewhere in this same script).
	#
	DS=$1
	# mntopts is field number 7
	grep '^[^#-]' $HA_FILES/vfstab.${DS} | awk '{print $7}'| \
	  egrep 'quota|rq' >/dev/null 2>&1
	if [ $? -eq 0 ]; then
		logwarning "quotas are not supported for HA-NFS, see $HA_FILES/vfstab.${DS}"
	fi
}


#
# XXX Debugging: Try to figure out if kernel lock tables are
# screwed up.  We do this by trying to get an exclusive lock on
# every file in /bin/*.  We believe that none of those files
# should be locked, so if one of them is locked, that is 
# evidence of a kernel lock table glitch.
#
locking_debugging()
{
	nfs_touchfile -xg /bin/* >$TMPLOCKERR 2>&1
	if [ $? -ne 0 ] && grep 'locked' $TMPLOCKERR >/dev/null 2>&1 ; then
		logerr "Locking botch: `cat $TMPLOCKERR`"
		# XXX Don't bother to send mail for now, wait till we have
		# the patch to notify by mail.
		# MAILEES=""
		# /usr/ucb/Mail -s "locking botch on `uname -n` at `date`" $MAILEES <$TMPLOCKERR
	fi
	rm -f $TMPLOCKERR
}



#
# main
#
exitstatus=0

OLDMOUNTSFILE=$HA_VAR/volatile/hadf.mountedsets
TSFILE=${OLDMOUNTSFILE}.ts

# Record our start time, to use as the timestamp at end of script:
touch ${TSFILE}.new >/dev/null 2>&1

OLDMOUNTSOK=0
OLDMOUNTS=""
OLDTIMESTAMP=`fdl_filemtime $TSFILE 2>/dev/null`
if [ $? -ne 0 ]; then
	OLDTIMESTAMP=0
elif [ -s $OLDMOUNTSFILE ]; then
	OLDMOUNTS="`cat $OLDMOUNTSFILE 2>/dev/null`"
	if [ $? -eq 0 ]; then
		OLDMOUNTSOK=1
	else
		OLDMOUNTS=""
	fi
fi
rm -f $OLDMOUNTSFILE $TSFILE >/dev/null 2>&1


# XXX Debug local locking problems.
# XXX comment out for now:  locking_debugging &


#
# mountall for each diskset that is not already mounted.
#
for DS in $HA_METASETSERVE ; do
	VFSMT=`fdl_filemtime $HA_FILES/vfstab.${DS} 2>/dev/null`
	if [ $? -ne 0 ]; then
		VFSMT=`fdl_timesecs`
	fi		
	# Must do the mounts if either DS wasn't a member of OLDMOUNTS or
	# if vfstab file has changed since the last time this script ran.
	# Note that if OLDMOUNTSOK==0 then OLDMOUNTS is empty, so
	# the next line works, without having to test OLDMOUNTSOK 
	# explicitly.
	is_member $DS "$OLDMOUNTS"
	if [ $? -ne 0 -o $VFSMT -ge $OLDTIMESTAMP ]; then
		ha_mountall $HA_FILES/vfstab.${DS}
		HAMARC=$?
		if [ $HAMARC -ne 0 ]; then
			exitstatus=$HAMARC
		fi			
		# Can fork off next command because it only issues 
		# warning messages:
		ha_quotas_diskset $DS &
	fi
done


# 
# Iff everything worked, write out hadf.mountedsets state file
#
if [ $exitstatus -eq 0 ]; then
	echo "$HA_METASETSERVE" > $OLDMOUNTSFILE
	if [ $? -ne 0 -o ! -s $OLDMOUNTSFILE ]; then
		rm -f $OLDMOUNTSFILE
	else
		# Writing out the hadf.mountedsets file succeeded,
		# so also set the timestamp of when we ran.  We use
		# the time when this script started as the timestamp,
		# in case the file(s) changed while we were running.
		cp -p ${TSFILE}.new ${TSFILE} >/dev/null 2>&1
		if [ $? -ne 0 ]; then
			rm -f $TSFILE $OLDMOUNTSFILE
		fi
	fi
fi

# propagate error up to caller
exit $exitstatus
