#!/usr/bin/ksh
#
#pragma ident	"@(#)hastorageplus_validate.ksh	1.3	03/12/08 SMI"
#
# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.


 #######################################################################
#									#
#	HA Storage Plus Validate Method					#
#									#
 #######################################################################


#pragma ident	"@(#)hastorageplus_lib.ksh	1.11	03/12/08 SMI"
#
# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.


 #######################################################################
#									#
#	HA Storage Plus Functions Library				#
#									#
 #######################################################################


#################################
#  environment characteristics  #########################################
#################################					#
#									#
# Comments:	-f  disables wildcard expansion.			#
#		+u  treats undefined variables as null.			#
#		-p  sets environment for suid mode.			#
#									#
#########################################################################
set -fp +u


##################################
#  Command names & system files  ########################################
##################################					#
#									#
# Comments:	Fully qualified command names used by all methods.	#
#		It is a must to use fully qualified names for security	#
#		    reasons.						#
#									#
#########################################################################
AWK="/bin/nawk"
CAT="/bin/cat"
CUT="/bin/cut"
DATE="/bin/date"
DIFF="/bin/diff"
DIRNAME="/bin/dirname"
GETTEXT="/bin/gettext"
GREP="/bin/grep"
LS="/bin/ls"
MKDIR="/bin/mkdir"
MOUNT="/usr/sbin/mount"
MV="/bin/mv"
PRINTF="/bin/printf"
RM="/bin/rm"
SED="/bin/sed"
SLEEP="/bin/sleep"
SORT="/bin/sort"
TOUCH="/bin/touch"
UMOUNT="/usr/sbin/umount"
WC="/usr/bin/wc"

MOUNTO="$MOUNT -O"			# Mount in overlay mode
UMOUNTF="$UMOUNT -f"			# Forced unmount

MYDIR="$($DIRNAME "$0")"

MNTTAB_FILE="/etc/mnttab"
VFSTAB_FILE="/etc/vfstab"

FSCK_DO_NOTHING="/bin/true"		# Skip fsck
FSCK_KNOWN_FS="/usr/sbin/fsck -o p"
FSCK_UNKNOWN_FS="/usr/sbin/fsck"


#####################
#  Global variables #####################################################
#####################						    	#
#									#
#########################################################################
method=""		# The name of the method we are currently running
status_message=""	# The text that will be added to the resource status
			#   in case Update fails.


################
#  Properties  ##########################################################
################						    	#
#									#
# Comments:	Only these properties are recognised by HAS+.		#
#									#
#########################################################################
AFFINITYON=""
FILESYSTEMCHECKCOMMAND=""
FILESYSTEMMOUNTPOINTS=""
GLOBALDEVICEPATHS=""

RESOURCE_NAME=""
RESOURCE_TYPE=""
RESOURCE_GROUP=""


#####################
#  Temporary files  #####################################################
#####################							#
#									#
# Comments:	These files are erased on method completion, except for	#
#		    OLDMNTS_FILE which subsists on non primary nodes	#
#		    after an update.					#
#		RGM garantees that tuple (resource name,resource group)	#
#		    is unique (Resource name is already unique).	#
#									#
#########################################################################
TMPERR_FILE="/var/cluster/run/HAStoragePlus_errors_$$"
OLDMNTS_FILE="/var/cluster/run/HAStoragePlus_oldmounts.\$RESOURCE_NAME.\$RESOURCE_GROUP"
NEWMNTS_FILE="/var/cluster/run/HAStoragePlus_newmounts.\$RESOURCE_NAME.\$RESOURCE_GROUP"
PRIVOUT_FILE="/var/cluster/run/HAStoragePlus_private.\$RESOURCE_NAME.\$RESOURCE_GROUP"


################################
#  I18N environment variables  ##########################################
################################					#
#									#
# Comments:	A "export" is required in order for the environment to	#
#		    be preserved when calling private binaries.		#
#									#
#########################################################################
export TEXTDOMAINDIR=/usr/cluster/lib/locale
export TEXTDOMAIN=SUNW_SC_CMD


###########################
#  Debug & log variables  ###############################################
###########################						#
#									#
# Comments:	Debug variables are currently hardcoded. Maybe it could	#
#		    changed to extension properties.			#
#									#
# Note:		If DEBUG_LOGFILE is not Null, validate method don't	#
#		    print to stderr because stderr is redirected to the	#
#		    debug file.						#
#									#
#########################################################################
DEBUG_LOGFILE=""
DEBUG_LEVEL=0
DEBUG_LOG_SIZE=524288	# = 512KB
SCLOGGER=/usr/cluster/lib/sc/scds_syslog


###################
#  TNF Profiling  #######################################################
###################							#
#									#
# Note:		If TNF_LOGFILE is not Null, private methods record TNF	#
#		    probes to TNF_LOGFILE, however their return code is #
#		    lost (and thus 0, even if an error occurs).		#
#									#
#########################################################################
TNF_LOGFILE=""

tnf_record()
{
	/bin/prex -o "$TNF_LOGFILE" ${@:-} >/dev/null 2>&1 << EOF
enable hasp
trace hasp
continue
EOF
	return 0
}
[[ -n "$TNF_LOGFILE" ]] && typeset tnf="tnf_record"


##################
#  Trap signals  ########################################################
##################							#
#									#
# Comments:	Traps TERM and KILL signal to ensure the process gets	#
#		    killed and exit with return code 1.			#
#									#
#########################################################################
TRAPSIGNALS="TERM KILL"
trap "trap - $TRAPSIGNALS; /usr/bin/pkill -g $$;exit 1" $TRAPSIGNALS


################
#  syslog_tag  ##########################################################
################							#
#									#
# Synopsis:	Prints HAS+ syslog tag.					#
#									#
# Arguments:	None.							#
#									#
# Return code:	None.							#
#									#
# Sequence:	1. Prints syslog tag: SC[pkg.method]: GROUP:RESOURCE	#
#									#
# Comments:	None.							#
#									#
#########################################################################
syslog_tag()
{
	print "SC[${pkg:-??}.${method:-??}]:${RESOURCE_GROUP:-??}:${RESOURCE_NAME:-??}"
}


#################
#  scds_syslog  #########################################################
#################							#
#									#
# Synopsis:	Prints to syslog.					#
#									#
# Arguments:	-p <prio>   Priority.					#
#		-t <tag>    Tag.					#
#		-m <msg>    Format string message. (see printf(1))	#
#		parameters  Parameters for the message.			#
#									#
# Return code:	None.							#
#									#
# Sequence:	1. Prints message to stderr if method == Validate and	#
#		    priority == error.					#
#		2. Prints message to syslog.				#
#									#
# Comments:	None.							#
#									#
#########################################################################
scds_syslog()
{

	typeset priority=""
	typeset msg_format=""
	typeset tag=""
	typeset msg

	$SCLOGGER "$@" &

	while getopts :p:t:m: opt; do
	case $opt in
		p)	priority="$OPTARG";;
		t)	tag="$OPTARG";;
		m)	msg_format="$OPTARG";;
		*)	;;
	esac
	done

	shift $(expr $OPTIND - 1)

	if [[ "$priority" = "error" ]] && [[ "$method" = "validate" ]]; then

		msg="$($PRINTF "$($GETTEXT "${msg_format}")" "${@:-}")"
		print -r -u2 -- "$msg"

	fi


	[[ -z "${TRACE_INIT:-""}" ]] && init_trace

	if [[ "${DEBUG_LOGFILE}" != "/dev/console" ]]; then

		msg="$($PRINTF "${msg_format}" "${@:-}")"
		print -r -- "$($DATE '+%b %d %T') $tag: $msg" >> $DEBUG_LOGFILE

	fi
}


#################
#  write_trace  #########################################################
#################							#
#									#
# Synopsis:	Writes a log for debug purposes.			#
#									#
# Arguments:	$1		Format string. (see printf(1))		#
#		$@ (shifted)	Parameters.				#
#									#
# Return code:	None.							#
#									#
# Sequence:	1. Writes formatted string to the log file, if any.	#
#									#
# Comments:	None.							#
#									#
#########################################################################
write_trace()
{
	typeset msg_text="${1:-""}"
	typeset msg

	shift

	msg="$($PRINTF "${msg_text}" "${@:-}")"
	[[ -z "${TRACE_INIT:-""}" ]] && init_trace
	[[ "${DEBUG_LOGFILE}" != "/dev/console" ]] && \
	    print -r -- "$($DATE '+%b %d %T') $(syslog_tag): $msg" \
		>> $DEBUG_LOGFILE
}


#################################
#  read_arguments_from_cmdline  #########################################
#################################					#
#									#
# Synopsis:	Reads and checks command line arguments and sets	#
#		resource variables accordingly.				#
#									#
# Arguments:	$@	Command line arguments passed by RGM.		#
#									#
# Return code:	None.							#
#									#
# Sequence:	1. Reads variables from command line.			#
#		2. Reads extension properties from the CCR.		#
#		3. Checks that variables are set.			#
#									#
# Comments:	Any failure makes the function return immediately.	#
#		NOT to be used with Validate callback.			#
#									#
#########################################################################
read_arguments_from_cmdline()
{
	typeset -i invalid_option_found=0
	typeset save_opt=""
	typeset save_optarg=""

	# Callbacks other than Validate accept only a restricted set of
	# options.

	while getopts :R:T:G: opt; do
		case $opt in
			R)	RESOURCE_NAME="$OPTARG" ;;
			T)	RESOURCE_TYPE="$OPTARG" ;;
			G)	RESOURCE_GROUP="$OPTARG" ;;
			*)	invalid_option_found=1
				save_opt="$opt"
				save_optarg="$OPTARG"
				break ;;
		esac
	done

	read_extension_properties

	if [[ $invalid_option_found -eq 1 ]]; then
		
		scds_syslog -p error -t $(syslog_tag) -m \
		    "Invalid command line parameter %s %s." \
		    "$save_optarg" "$save_opt"
		return 1

	fi

	# Ensure that the resource name, type and group variables are set.
	if [[ -z "$RESOURCE_NAME" ]] || [[ -z "$RESOURCE_TYPE" ]] ||
	    [[ -z "$RESOURCE_GROUP" ]]; then

		scds_syslog -p error -t $(syslog_tag) -m \
		    "Too few arguments specified on command line."
		return 1
	fi

	return 0
}


######################################
#  extract_extension_property_value  ####################################
######################################					#
#									#
# Synopsis:	Extracts the values of an extension property from stdin	#
#		    to stdout.						#
#									#
# Arguments:	None.							#
#									#
# Return code:	0	If everything succeeded.			#
#		1	If something failed.				#
#									#
# Sequence:	1. Discards data type from input.			#
#		2. Prints each value to stdout, separated by a newline.	#
#									#
# Comments:	For extension properties, scha_resource_get() returns	#
#		    the data type followed by the values.		#
#									#
#########################################################################
extract_extension_property_value()
{
	typeset -i rc=0
	typeset type
	typeset value

	read type || return 1

	while read value; do
		print "$value" || rc=1
	done

	return $rc
}


#########################
#  read_property_value  #################################################
#########################						#
#									#
# Synopsis:	Extracts the value of a property from the CCR to	#
#		    stdout.						#
#									#
# Arguments:	$1	Property name.					#
#									#
# Return code:	0	If everything succeeded.			#
#		1	If something failed.				#
#									#
# Sequence:	1. Reads the value from the CCR.	    		#
#									#
# Comments:	None.							#
#									#
#########################################################################
read_property_value()
{
	typeset property="$1"
	typeset -i rc=0
	typeset -i log_flag=1
	typeset option_tag
	typeset extension=""

	case $property in
		R_description| \
		Type| \
		On_off_switch| \
		Monitored_switch| \
		Resource_state| \
		Cheap_probe_interval| \
		Thorough_probe_interval| \
		Retry_count| \
		Retry_interval| \
		Failover_mode| \
		Resource_dependencies| \
		Resource_dependencies_weak| \
		Logical_hostnames_used| \
		Status| \
		START_TIMEOUT| \
		STOP_TIMEOUT| \
		PRIMARIES_CHANGED_TIMEOUT | \
		VALIDATE_TIMEOUT| \
		UPDATE_TIMEOUT| \
		INIT_TIMEOUT| \
		FINI_TIMEOUT| \
		BOOT_TIMEOUT| \
		MONITOR_START_TIMEOUT| \
		MONITOR_STOP_TIMEOUT| \
		MONITOR_CHECK_TIMEOUT| \
		Status_Node| \
		Resource_state_Node)
		option_tag="$property"
		extension=""
		;;
	DEBUG_LOGFILE| \
	DEBUG_LOG_SIZE| \
	DEBUG_LEVEL)
		log_flag=0
		option_tag="Extension"
		extension="$*"
		;;
	*)
		option_tag="Extension"
		extension="$*"
		;;
	esac

	if [[ -z "$extension" ]]; then
		
		scha_resource_get -O $option_tag \
		    -R "$RESOURCE_NAME" -G "$RESOURCE_GROUP"

	else

		scha_resource_get -O $option_tag \
		    -R "$RESOURCE_NAME" -G "$RESOURCE_GROUP" $extension \
		    | extract_extension_property_value

	fi

	rc=$?

	if [[ $rc -ne 0 ]]; then

		[[ $log_flag -eq 1 ]] && \
		    scds_syslog -p error -t $(syslog_tag) -m \
			"Failed to retrieve property %s." "$property"
		rc=1

	fi

	return $rc
}


################
#  init_trace  ##########################################################
################							#
#									#
# Synopsis:	Initializes the debug log environment.			#
#									#
# Arguments:	None.							#
#									#
# Return code:	None.							#
#									#
# Comments:	Only regular files are allowed unless Null specified.	#
#									#
#########################################################################
init_trace()
{
	typeset debugdir

	[[ -z "$DEBUG_LEVEL" ]] &&
	    DEBUG_LEVEL=$(read_property_value DEBUG_LEVEL)

	TRACE_INIT="done"

	# If no debug file is defined, set it to /dev/console.
	# This will disable the traces.
	[[ -z "$DEBUG_LOGFILE" ]] && DEBUG_LOGFILE="/dev/console" && return

	if [[ -a "$DEBUG_LOGFILE" ]] && [[ ! -f "$DEBUG_LOGFILE" ]]; then
		scds_syslog -p error -t $(syslog_tag) -m \
		    "DEBUG_LOGFILE (%s) is not a regular file." \
		    "$DEBUG_LOGFILE"
		DEBUG_LOGFILE="/dev/console"
		return
	fi

	debugdir=$($DIRNAME "$DEBUG_LOGFILE")

	[[ -n "$debugdir" ]] && $MKDIR -p "$debugdir"

	[[ "$DEBUG_LOGFILE" = *\. ]] && \
	    DEBUG_LOGFILE="${DEBUG_LOGFILE}${RESOURCE_NAME:-""}"

	[[ -z "$DEBUG_LOG_SIZE" ]] && DEBUG_LOG_SIZE=300000

	$TOUCH "$DEBUG_LOGFILE" 2> /dev/null

	# Note that this makes Validate not print to stderr.
	exec 2>> "$DEBUG_LOGFILE"

	rotate_log
}


################
#  rotate_log  ##########################################################
################							#
#									#
# Synopsis:	Renames the debug log and creates a new one.		#
#									#
# Arguments:	None.							#
#									#
# Return code:	None.							#
#									#
# Comments:	Same as syslog files.					#
#									#
#########################################################################
rotate_log()
{
	typeset -i log_size=0

	[[ ! -f "$DEBUG_LOGFILE" ]] && return

	log_size=$($LS -l "$DEBUG_LOGFILE" | $AWK '{print $5}')
	[[ $log_size -lt $DEBUG_LOG_SIZE ]] && return

	[[ -f "$DEBUG_LOGFILE.1" ]] && $RM -f "$DEBUG_LOGFILE.1"
	[[ -f "$DEBUG_LOGFILE.0" ]] &&
	    $MV "$DEBUG_LOGFILE.0" "$DEBUG_LOGFILE.1"
	$MV "$DEBUG_LOGFILE" "$DEBUG_LOGFILE.0"
	$TOUCH "$DEBUG_LOGFILE"
}


################
#  set_status  ##########################################################
################							#
#									#
# Synopsis:	Sets resource status.					#
#									#
# Arguments:	$1	Status (OK, DEGRAGED, FAULTED, UNKNOWN,		#
#			    OFFLINE).					#
#		[$2]	Optional message.				#
#									#
# Return code:	None.							#
#									#
# Comments:	Status is not validated.				#
#									#
#########################################################################
set_status()
{
	typeset rstatus="${1:-UNKNOWN}"
	typeset rmsg=${2:-""}
	typeset prev_status

	prev_status=$(read_property_value Status)

	# WARNING: Assumption here is that all previous scha_resource_setstatus
	# commands did NOT use the -m option. If it was ever used, more parsing
	# of prev_status is required here.

	[[ "$prev_status" = "$rstatus" ]] && return

	if [[ -n "$rmsg" ]]; then

		scha_resource_setstatus -s $rstatus -m "$rmsg" \
		    -R $RESOURCE_NAME -G $RESOURCE_GROUP

	else

		scha_resource_setstatus -s $rstatus \
		    -R $RESOURCE_NAME -G $RESOURCE_GROUP

	fi
}


################
#  initialize  ##########################################################
################							#
#									#
# Synopsis:	Initializes syslog and debug stuff.			#
#									#
# Arguments:	None.							#
#									#
# Return code:	None.							#
#									#
# Comments:	None.							#
#									#
#########################################################################
initialize()
{
	# uncomment to turn on tracing in all functions
	# typeset -ft $(typeset +f)

	# Needed for syslog messages
	pkg=SUNWscu
}


###############################
#  read_extension_properties  ###########################################
###############################						#
#									#
# Synopsis:	Reads extension properties from the CCR and fill 	#
#		    corresponding [uppercase] variables with its value.	#
#									#
# Arguments:	None.							#
#									#
# Return code:	None.							#
#									#
# Sequence:	1. Reads GlobalDevicePaths property.			#
#		2. Reads FileSystemMountPoints property.		#
#		3. Reads AffinityOn property.				#
#		4. Reads FilesystemCheckCommand property.		#
#									#
#########################################################################
read_extension_properties()
{
	GLOBALDEVICEPATHS=$(read_property_value GlobalDevicePaths)

	FILESYSTEMMOUNTPOINTS=$(read_property_value FileSystemMountPoints)

	AFFINITYON=$(read_property_value AffinityOn)

	FILESYSTEMCHECKCOMMAND=$(read_property_value FilesystemCheckCommand)
}


#############
#  set_var  #############################################################
#############								#
#									#
# Synopsis:	Gets a pair "name=value" and sets variable named "name"	#
#		    [uppercase] to value "value".			#
#									#
# Arguments:	$1	String of the form "name=value"			#
#									#
# Return code:	None.							#
#									#
# Comments:	Variable corresponding to name is NAME [uppercase].	#
#									#
#########################################################################
set_var()
{
	typeset -u param
	typeset val

	[[ -z ${1:-""} ]] && return

	val="${1##*=}"
	param="${1%%=*}"

	write_trace "set_var: %s = %s" "$param" "$val"

	eval ${param}=\"${val}\"
}


###############
#  sort_list  ###########################################################
###############								#
#									#
# Synopsis:	Sorts the given list contained in the given variable	#
#		    and removes \n.					#
#									#
# Arguments:	$1	Name of variable which contains the list.	#
#									#
# Return code:	None.							#
#									#
# Sequence:	1. Reads variable content.				#
#		2. Sorts the content and removes \n.			#
#		3. Stores content to variable.				#
#									#
# Comments:	A generated temporary variable is used (_list$$).	#
#									#
#########################################################################
sort_list()
{
	typeset _list_varname="$1"
	eval typeset _list$$=\"\$$_list_varname\"

	eval write_trace \"%s unordered: %s\" \"_list$$\" \"\$_list$$\"

	eval _list$$=\$\(for i in \$_list$$\; do echo \$i\; done \| $SORT\)

	# This trick replaces \n by a space
	eval _list$$=\$\(echo \$_list$$\)
	eval write_trace \"%s ordered, no CR: %s\" \"_list$$\" \"\$_list$$\"

	# Store result in original variable
	eval $_list_varname=\"\$_list$$\"
}


################
#  is_mounted  ##########################################################
################						    	#
#									#
# Synopsis:	Checks if a FS is mounted or not.			#
#									#
# Arguments:	$1	Mount point to check.				#
#									#
# Return code:	0	If FS is not mounted.				#
#		1	If FS is mounted.				#
#									#
# Sequence:	1. Searches for the mount point in /etc/mnttab.		#
#									#
# Comments:	/etc/mnttab is assumed to have updated mount info.	#
#									#
#########################################################################
is_mounted()
{
	typeset mountp="$1"

	# The existense of the mnttab file is not checked here.

	eval $AWK \'\$2 == \"$mountp\" { exit 1 }\' $MNTTAB_FILE

	if [[ $? -eq 1 ]]; then

		write_trace "%s is already mounted" "$mountp"
		return 1

	fi

	write_trace "%s is not present in %s" "$mountp" "$MNTTAB_FILE"
	return 0
}


#########################
#  is_local_mountpoint  #################################################
#########################					    	#
#									#
# Synopsis:	Checks if a FS is a local mount point or not.		#
#									#
# Arguments:	$1	Mount point to check.				#
#									#
# Return code:	0	If FS is a global mount point.			#
#		1	If FS is a local mount point.			#
#		-1	If FS is not in /etc/vfstab.			#
#									#
# Sequence:	1. Searches for the mount point in /etc/vfstab.		#
#									#
# Comments:	None.							#
#									#
#########################################################################
is_local_mountpoint()
{
	typeset fsp="$1"
	typeset special
	typeset fsckdev
	typeset mountp
	typeset type
	typeset pass
	typeset atboot
	typeset options

	# Fetches the corresponding line in $VFSTAB_FILE.
	# The following line skips comments and extract the fields.
	# Note that $3 = mountp = fsp.
	# Note that whitespaces in words are forbidden in $VFSTAB_FILE.

	eval $AWK \'/^[^\#]/ \&\& \$3 == \"$fsp\" { print }\' \
	    $VFSTAB_FILE | read special fsckdev mountp type pass atboot options

	if [[ -z "$special" ]]; then

		write_trace "%s is not present in %s" "$fsp" "$VFSTAB_FILE"
		return -1

	fi

	# Check if "global" is specified.
	# Encapsulate options in commas to avoid corner cases.
	#   ex: forcedirectio,logging,global
	#	-> ,forcedirectio,logging,global,
	options=",$options,"

	if [[ "$options" = *,global,* ]]; then

		write_trace "%s is globally mounted" "$mountp"
		return 0

	fi

	write_trace "%s is a local mount point" "$mountp"

	return 1
}


##############
#  fs_check  ############################################################
##############								#
#									#
# Synopsis:	Performs a fsck check on the specified FS.		#
#									#
# Arguments:	$1	FS type (ufs, vxfs, etc.).			#
#		$2	Mount point.					#
#		$3	Raw device.					#
#									#
# Return code:	0	If everything succeeded.			#
#		1	If something failed.				#
#									#
# Sequence:	1. Checks for the type of FS.				#
#		2. Do the fsck on the device.				#
#									#
# Comments:	Returns immediately is fsck == FSCK_DO_NOTHING.		#
#									#
#########################################################################
fs_check()
{
	typeset fstyp="$1"
	typeset mountp="$2"
	typeset special="$3"
	typeset -i rc=0
	typeset fsck="$FILESYSTEMCHECKCOMMAND"

	if [[ "$fsck" = "$FSCK_DO_NOTHING" ]]; then

		scds_syslog -p info -t $(syslog_tag) -m \
		    "Skipping file system check of %s." "$mountp"
		return 0

	fi

	case $fstyp in
		ufs|vxfs)
			[[ -z "$FILESYSTEMCHECKCOMMAND" ]] && \
				fsck="$FSCK_KNOWN_FS"
			;;
		*)
			[[ -z "$FILESYSTEMCHECKCOMMAND" ]] && \
				fsck="$FSCK_UNKNOWN_FS"
			;;
	esac

	scds_syslog -p info -t $(syslog_tag) -m \
	    "About to perform file system check of %s (%s) using command %s." \
	    "$mountp" "$special" "$fsck"

	$fsck "$special" >$TMPERR_FILE 2>&1
	rc=$?

	if [[ $rc -eq 0 ]]; then

		scds_syslog -p info -t $(syslog_tag) -m \
		    "File system check of %s (%s) successful." \
		    "$mountp" "$special"

		write_trace "$($CAT $TMPERR_FILE)"

	else

		scds_syslog -p error -t $(syslog_tag) -m \
		    "File system check of %s (%s) failed: (%d) %s." \
		    "$mountp" "$special" $rc "$($CAT $TMPERR_FILE)"
		rc=1

	fi

	# Delete the temp file.
	$RM -f $TMPERR_FILE

	return $rc
}


#####################
#  umount_local_fs  #####################################################
#####################							#
#									#
# Synopsis:	Unmounts local FS given in argument.			#
#									#
# Arguments:	$1	List of FS.					#
#									#
# Return code:	0	If everything succeeded.			#
#		1	If something failed.				#
#									#
# Sequence:	1. Unmounts [forcibly] only local FS (with -a flag).	#
#		2. Waits for them to be unmounted.			#
#									#
# Comments:	Each file system is verified to be unmounted after	#
#		    performing the unmount operation. This verification	#
#		    is done within a sleep wait loop.			#
#									#
#########################################################################
umount_local_fs()
{
	typeset fslist="$1"
	typeset -i rc
	typeset fsp
	typeset -i failure_detected=0
	typeset -i try
	typeset -i umountrc
	typeset localmountpoints=""
	typeset tmplist

	[[ -z "$fslist" ]] && return 0

	# Note: Because 'umount -a' is used, there is no need to reverse-sort
	# the list of file system mount points.

	for fsp in $fslist; do

		# Check if file system is a local mount point.
		is_local_mountpoint $fsp
		rc=$?

		if [[ $rc -eq 1 ]]; then

			# fsp is a local mount point.
			is_mounted $fsp

			rc=$?

			if [[ $rc -eq 1 ]]; then

				scds_syslog -p info -t $(syslog_tag) -m \
				    "%s is locally mounted. Unmounting it." \
				    "$fsp"
				localmountpoints="$localmountpoints $fsp"

			else

				# A local file system is found to be unmounted.
				# Log a informational message, and continue.
				# Returning an error here would make the stop
				# method non-idempotent.

				scds_syslog -p info -t $(syslog_tag) -m \
				    "%s is erroneously found to be unmounted." \
				    "$fsp"

			fi

		elif [[ $rc -eq 0 ]]; then

			# fsp is a global mount point.
			write_trace \
			    "%s is a global mountpoint, not unmounting it" \
			    "$fsp"

		else

			# fsp is not a mount point (should not happen).
			write_trace "%s is not a mount point" "$fsp"

		fi

	done

	[[ -z "$localmountpoints" ]] && return 0

	$UMOUNTF -a $localmountpoints >$TMPERR_FILE 2>&1
	umountrc=$?

	if [[ $umountrc -eq 1 ]]; then

		# Errors will be logged later, thus TMPERR_FILE should not be
		# removed at this point.

		failure_detected=1

	else

		scds_syslog -p info -t $(syslog_tag) -m \
		    "Successfully unmounted %s." "$localmountpoints"

		write_trace "$($CAT $TMPERR_FILE)"

		$RM -f $TMPERR_FILE

	fi

	# Sleep wait till the unmount is confirmed
	try=0
	while true; do
		tmplist=""
		for fsp in $localmountpoints; do
			is_mounted $fsp
			rc=$?

			[[ $rc -eq 1 ]] && tmplist="$tmplist $fsp"
		done
		localmountpoints="$tmplist"

		[[ -z "$localmountpoints" ]] && break

		# If umount failed, we cannot loop forever if we are doing an
		# Update. Let's sleep 3 times at most (i.e. check 4 times).
		[[ $method = "update" ]] && [[ $failure_detected -eq 1 ]] && \
		    [[ $try -eq 3 ]] && break

		# If umount failed and we are doing a Stop, loop forever till
		# RGM timeouts.

		$SLEEP 5
		let try+=1
	done

	# Log an error for file systems which could not be unmounted
	# This may happen only in case of a failure detected.
	if [[ -n "$localmountpoints" ]]; then

		scds_syslog -p error -t $(syslog_tag) -m \
		    "Failed to unmount %s: (%d) %s." \
		    "$localmountpoints" $umountrc "$($CAT $TMPERR_FILE)"

		$RM -f $TMPERR_FILE

		if [[ $method = "update" ]]; then

			# Add to status message failing mount points.
			# Message will be of the form:
			#
			# <...> -- Failed to unmount: m1 m2 [...]."
			#
			# where m1 m2 stands for the mount points.

			typeset msg

			# Remove leading and trailing blanks.
			sort_list localmountpoints

			[[ -n "$status_message" ]] && \
			    status_message="${status_message} -- "

			msg="Failed to unmount"
			status_message="${status_message}${msg}: "
			status_message="${status_message}${localmountpoints}."

		fi

		return 1

	fi

	return 0
}


##############
#  fs_mount  ############################################################
##############								#
#									#
# Synopsis:	Mounts FS given in argument.				#
#									#
# Arguments:	$1	List of FS.					#
#									#
# Return code:	0	If everything succeeded.			#
#		1	If something failed.				#
#									#
# Sequence:	1. Does a fsck on the FS.				#
#		2. Mounts the "good" FS.				#
#		2. Waits for them to be really mounted.			#
#									#
# Comments:	Each file system is verified to be mounted after	#
#		    performing the mount operation. This verification	#
#		    is done within a sleep wait loop.			#
#		An error is returned if a global FS is found to be	#
#		    unmounted and AffinityOn is false, i.e. mounts of	#
#		    an unmounted global FS can occur only if AffinityOn	#
#		    is true.						#
#									#
#########################################################################
fs_mount()
{
	typeset fslist="$1"
	typeset -i rc
	typeset special
	typeset fsckdev
	typeset mountp
	typeset type
	typeset remains
	typeset -i totalfiles
	typeset fsp
	typeset validmounts=""		# List of FS which did fsck/mount.
	typeset failedfsck=""		# List of FS which didn't fsck.
	typeset failedmounts=""		# List of FS which didn't mount.
	typeset wrongaffinitymounts=""  # List of FS which have an incorrect
					#   affinity specification.
	typeset -i failure_detected=0	# Flag set if a mount/fsck fails.
	typeset tmplist

	[[ -z "$fslist" ]] && return 0

	# First, we have to sort the list of mount points, in order to support
	# nested mounts.

	sort_list fslist

	# For each mount point,
	#
	# 1. Look up entry in $VFSTAB_FILE
	# 2. Skip if fs is already mounted
	#    Otherwise -
	# 3. Do fsck
	# 4. Mount file system

	for fsp in $fslist; do
		# Fetches the corresponding line in $VFSTAB_FILE
		# Note that $3 = mountp = fsp.
		eval $AWK \'/^[^\#]/ \&\& \$3 == \"$fsp\" { print }\' \
			$VFSTAB_FILE | read special fsckdev mountp type remains

		is_mounted $mountp
		rc=$?

		if [[ $rc -eq 1 ]]; then

			scds_syslog -p info -t $(syslog_tag) -m \
			    "File system associated with %s is already mounted." \
			    "$mountp"

			# Continue loop (for fsp).
			continue

		fi

		is_local_mountpoint $mountp
		rc=$?

		if [[ $rc -eq 0 ]] && [[ "$AFFINITYON" = "FALSE" ]]; then

			# A global file system was found to be unmounted.
			# However, AFFINITYON is FALSE.
			#
			# As per the functional spec, a unmounted global file
			# system can only be mounted if AFFINITYON is TRUE.
			failure_detected=1
			wrongaffinitymounts="$wrongaffinitymounts $mountp"

			scds_syslog -p error -t $(syslog_tag) -m \
			    "Unmounted global file system (%s) detected. Incorrect AffinityOn specification." \
			    "$mountp"


			# Continue loop (for fsp).
			continue

		elif [[ $rc -eq -1 ]]; then

			# mountp is not a mount point (should not happen),
			# continue loop (for $fsp).
			continue

		fi

		# Conduct the file system check
		fs_check $type $mountp $fsckdev
		rc=$?

		if [[ $rc -ne 0 ]]; then

			# FS seems invalid.
			failure_detected=1
			failedfsck="$failedfsck $mountp"

			# Continue loop (for fsp).
			continue

		fi

		# Check if the mount directory is not empty i.e. any entries
		# other than "." and ".." do exist. Log an informational
		# message for the non empty mount directory.

		totalfiles=$($LS -a1 $mountp 2>/dev/null | $WC -l)
		if [[ $totalfiles -eq 2 ]]; then

			scds_syslog -p info -t $(syslog_tag) -m \
			    "About to mount %s." "$mountp"


		else

			scds_syslog -p info -t $(syslog_tag) -m \
			    "About to mount %s. Underlying files/directories will be inaccessible." \
			    "$mountp"

		fi

		$MOUNT $mountp >$TMPERR_FILE 2>&1
		rc=$?

		if [[ $rc -ne 0 ]]; then

			# Mount failed. Try an overlay mount.
			scds_syslog -p info -t $(syslog_tag) -m \
			    "Mount of %s failed. Trying an overlay mount." \
			    "$mountp"

			$MOUNTO $mountp >$TMPERR_FILE 2>&1
			rc=$?

			if [[ $rc -ne 0 ]]; then

				# Mount really failed.
				failure_detected=1
				failedmounts="$failedmounts $mountp"

				scds_syslog -p error -t $(syslog_tag) -m \
				    "Mount of %s failed: (%d) %s." \
				    "$mountp" $rc "$($CAT $TMPERR_FILE)"

				$RM -f $TMPERR_FILE

				# Continue loop (for fsp).
				continue
			fi
		fi

		write_trace "$($CAT $TMPERR_FILE)"
		$RM -f $TMPERR_FILE

		scds_syslog -p info -t $(syslog_tag) -m \
		    "Mount of %s successful." "$mountp"

		validmounts="$validmounts $mountp"

	done

	write_trace "wrongaffinitymounts: %s" "$wrongaffinitymounts"
	write_trace "failedfsck: %s" "$failedfsck"
	write_trace "failedmounts: %s" "$failedmounts"
	write_trace "validmounts: %s" "$validmounts"

	# Check and wait that every *valid* file system is mounted.
	# Dummy check since mount is synchronous, or is it?

	while true; do
		tmplist=""
		for fsp in $validmounts; do
			is_mounted $fsp
			rc=$?

			if [[ $rc -eq 1 ]]; then
				scds_syslog -p info -t $(syslog_tag) -m \
				    "%s is confirmed as mounted." "$fsp"
			else
				tmplist="$tmplist $fsp"
			fi
		done
		validmounts="$tmplist"

		[[ -z "$validmounts" ]] && break

		$SLEEP 5
	done

	if [[ $method = "update" ]] && [[ $failure_detected -eq 1 ]]; then

		# Add to status message failing mount points.
		# Message will be of the form (Sentences not applicable are not
		# printed): (On one line only)
		#
		# <...> -- Incorrect AffinityOn setting for mount points: m1 m2
		# [...]. -- Failed to fsck: m1 m2 [...]. -- Failed to mount: m1
		# m2 [...].
		#
		# where m1 m2 stands for the mount points.

		typeset msg

		if [[ -n "$wrongaffinitymounts" ]]; then

			# Remove leading and trailing blanks.
			sort_list wrongaffinitymounts

			[[ -n "$status_message" ]] && \
			    status_message="${status_message} -- "

			msg="Incorrect AffinityOn setting for mount points"
			status_message="${status_message}${msg}: "
			status_message="${status_message}${wrongaffinitymounts}."

		fi

		if [[ -n "$failedfsck" ]]; then

			# Remove leading and trailing blanks.
			sort_list failedfsck

			[[ -n "$status_message" ]] && \
			    status_message="${status_message} -- "

			msg="Failed to fsck"
			status_message="${status_message}${msg}: "
			status_message="${status_message}${failedfsck}."

		fi

		if [[ -n "$failedmounts" ]]; then

			# Remove leading and trailing blanks.
			sort_list failedmounts

			[[ -n "$status_message" ]] && \
			    status_message="${status_message} -- "

			msg="Failed to mount"
			status_message="${status_message}${msg}: "
			status_message="${status_message}${failedmounts}."

		fi

	fi

	return $failure_detected
}
#include_hastorageplus_lib

method="validate"


##########################################
#  validate_read_arguments_from_cmdline  ################################
##########################################				#
#									#
# Synopsis:	Read and check command line arguments and set resource	#
#		variables accordingly.					#
#									#
# Arguments:	$@	Command line arguments passed by RGM.		#
#									#
# Return code:	0	If everything succeeded.			#
#		1	If a failure occured.				#
#									#
# Sequence:	1. Reads variables from command line.			#
#		2. If update, reads missing properties from the CCR.	#
#		3. Checks that variables are set.			#
#									#
# Comments:	Any failure makes the function return immediately.	#
#		Function similar to read_arguments_from_cmdline() but	#
#		    specific to the Validate method.			#
#									#
#########################################################################
validate_read_arguments_from_cmdline()
{
	typeset -i invalid_option_found=0
	typeset save_opt=""
	typeset save_optarg=""
	typeset operation="validate"

	# Validate callback accepts every recognised option.

	# Determine if an update is performed or a normal validation.
	while getopts ugcr:R:T:G:x: opt; do
		case $opt in
			R)	RESOURCE_NAME="$OPTARG" ;;
			T)	RESOURCE_TYPE="$OPTARG" ;;
			G)	RESOURCE_GROUP="$OPTARG" ;;
			u)	operation="update" ;;
			*)	;;
		esac
	done

	if [[ $operation = "update" ]]; then

		# Because this is an update, some properties may not
		# have been specified. Pre-read them from the CCR.
		# Some properties may be overwritten by command line.
		read_extension_properties

	fi

	while getopts ugcr:R:T:G:x: opt; do
		case $opt in
			R)	RESOURCE_NAME="$OPTARG" ;;
			T)	RESOURCE_TYPE="$OPTARG" ;;
			G)	RESOURCE_GROUP="$OPTARG" ;;
			r|x)	set_var "$OPTARG" ;;
			g|c|u)	;;
			*)	invalid_option_found=1
				save_opt="$opt"
				save_optarg="$OPTARG"
				break ;;
		esac
	done

	if [[ $invalid_option_found -eq 1 ]]; then
		
		scds_syslog -p error -t $(syslog_tag) -m \
		    "Invalid command line parameter %s %s." \
		    "$save_optarg" "$save_opt"
		return 1

	fi

	# Ensure that the resource name, type and group variables are set.
	if [[ -z "$RESOURCE_NAME" ]] || [[ -z "$RESOURCE_TYPE" ]] ||
	    [[ -z "$RESOURCE_GROUP" ]]; then

		scds_syslog -p error -t $(syslog_tag) -m \
		    "Too few arguments specified on command line."
		return 1

	fi

	if [[ $operation = "update" ]]; then

		# Expand name of variable used during update of the
		# FileSystemMountPoints property.
		eval OLDMNTS_FILE=\"$OLDMNTS_FILE\"

		# Save list of FS (this list will be odd once Validation
		# succeeded).
		# Note that this list will survive Update on non-primary
		# nodes.
		read_property_value FileSystemMountPoints | \
			$SORT > $OLDMNTS_FILE || return 1

	fi

	return 0
}


##########
#  main  ################################################################
##########								#
#									#
# Synopsis:	The validate callback itself.				#
#									#
# Arguments:	$@	Command line arguments passed by RGM.		#
#									#
# Return code:	0	If everything succeeded.			#
#		1	If something failed.				#
#									#
# Sequence:	1. Reads command line arguments.			#
#		2. Validates configuration.				#
#									#
# Comments:	Any error encountered is fatal.				#
#									#
#########################################################################
main()
{
	initialize

	write_trace "initialize completed"

	validate_read_arguments_from_cmdline "${@:-}" || exit 1

	write_trace "validate_read_arguments_from_cmdline completed"

	# Invoke the private validation executable. The private validation
	# process among other things involves the validation of the underlying
	# global devices.
	$tnf $MYDIR/hastorageplus_validate_private "${@:-}" || exit 1

	write_trace "hastorageplus_validate_private completed"

	exit 0
}

main "${@:-}"
