#! /usr/bin/ksh
#
# Copyright 1998-2003 Sun Microsystems, Inc.   All rights reserved.
# Use is subject to license terms.


#pragma ident	"@(#)hastorageplus_postnet_stop.ksh	1.2	03/02/25 SMI"
############################################################
#
# HA Storage Plus post net stop method
# 
#
###########################################################
# Copyright 1998-2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#
#pragma ident	"@(#)hastorageplus_lib.ksh	1.5	03/02/25 SMI"
#

#####################################################
# Treat undefined variables as null.
# Disable wildcard expansion
# Script is running in suid mode.
#####################################################
set -ufp

#####################################################
#
# Set defaults, which could be overridden later
#
#####################################################
MYDIR=`/bin/dirname $0`
BINDIR=`/bin/dirname $0`
ETCDIR="$BINDIR/../etc"
DEBUG_LEVEL=0  # Sri 

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

MOUNT="/usr/sbin/mount"		#Regular mount
MOUNTO="/usr/sbin/mount -O"	#Mount in overlay mode
UMOUNT="/usr/sbin/umount -f"
FSCK="/usr/sbin/fsck -o p"
RMF="/bin/rm -f"

TMPERR_FILE="/var/run/HAStoragePlus_fsck_errors_$$"

#####################################################
#
# These are currently hardcoded. Could choose to use an 
# extension property. 
#
#####################################################
DEBUG_LOGFILE="/dev/null"

#Maximum size i.e. 512 KB
DEBUG_LOG_SIZE=524288

ARGUMENTS="$0 ${@:-""}"
RESOURCE_NAME=""
RESOURCE_TYPE=""
RESOURCE_GROUP=""
SCLOGGER=/usr/cluster/lib/sc/scds_syslog


#####################################################
# Assumption: The SC 3.0 environment predefines the PATH 
# and LD_LIBRARY_PATH env variables.
#####################################################

ORIGPATH=`echo $PATH`
ORIG_LD_LIBRARY_PATH=`echo $LD_LIBRARY_PATH`

#####################################################
# Set up a signal handler to ensure that 
# any TRAPSIGNALS kill the process and 
# also exit with 1
#####################################################


TRAPSIGNALS="TERM KILL"
trap "trap - $TRAPSIGNALS; /usr/bin/pkill -g $$;exit 1" $TRAPSIGNALS


######################################################
# Function: syslog_tag
#
# Arguments: none
#
#
# Return:
#
# none
#
# Comments:
#
#	prints syslog_tag as SC[pkg.method]: GROUP:RESOURCE
#
#######################################################
syslog_tag()
{
	print "SC[${pkg:-??}.${method:-??}]:${RESOURCE_GROUP:-??}:${RESOURCE_NAME:-??}" 
}

#######################################################
# Function: scds_syslog
#
# Arguments:
#
#  <priority> <formattext> <parameters> 
#
#
# Return: 
# 
# none
#
# Comments:
#
#######################################################
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`

	msg=$(/usr/bin/printf "${msg_format}" ${@:-} )

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

	[ "${DEBUG_LOGFILE}"  != "/dev/console" ] && \
	    print -r -- "$(date '+%b %d %T') $tag: $msg" >> $DEBUG_LOGFILE
}

#######################################################
# Function: write_trace
#
# Arguments:
#
# <text> <parameters> 
#
# Return:
#
# none
#
# Comments:
#
#  write_trace <text> <parameters> 
#
#############################################################
write_trace()
{

	typeset msg_text=${1:-""}
	typeset msg

	shift

	msg=$(/usr/bin/printf "${msg_text}" ${@:-} )
	[ -z "${TRACE_INIT:-""}" ] && init_trace
	[ "${DEBUG_LOGFILE}"  != "/dev/console" ] && \
	    print -r -- "$(date '+%b %d %T') $(syslog_tag): $msg" >> $DEBUG_LOGFILE
}
#######################################################
# Function: read_arguments
#
# Arguments: Command line arguments
#
#
# Return: 0 if arguments are read and set.
#
#	  1 if there was an error.
#
# Comments:
#
#  Set Globals variables:
#     RESOURCE_NAME, RESOURCE_TYPE RESOURCE_GROUP
#######################################################
read_arguments()
{

	typeset rc=0
	typeset invalid_option=""
	typeset save_opt=""
	typeset save_optarg=""

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

	# Checks the existence of resource name, type and
	# group.

	if [ -z "$RESOURCE_NAME" \
		-o -z "$RESOURCE_TYPE" \
		-o -z "$RESOURCE_GROUP"  \
		-o -n "$invalid_option" ]; then

		scds_syslog -p error -t $(syslog_tag) -m \
	 		"Initialization failed. Invalid command line  %s %s." \
			"$save_optarg" "$save_opt"
		rc=1;
	fi
	
	return $rc

}
############################################################
# Function: check_resource_setup
#
# Arguments:
#
#
# Return:
#
#
# Comments:
#
# Checks the existence of the resource name, resource type
# and resource group.
#############################################################
check_resource_setup()
{



        if [ -z "$RESOURCE_NAME" \
                -o -z "$RESOURCE_TYPE" \
                -o -z "$RESOURCE_GROUP" ]; then
		return 1
	else
		return 0
	fi

}
#############################################################
# Function: error_exit
#
# Arguments: exit code 
#
# Return:
#
#
# Comments:
#
#############################################################
error_exit()
{
	typeset exit_code="${1:-0}"
	exit $exit_code
}

#############################################################
# Function: extract_property_value
#
# Arguments:
#
#
# Return: a return code for the print operation
#
#
# Comments:
#
#  For extended properties, 
#  scha_resource_get returns data type followed by value.
#  This function discards first line (data type) and prints 
#	remaining lines.
#############################################################
extract_property_value()
{
	typeset type
	typeset value
	typeset rc=0

	
	read type 
	rc=$?
	[ $rc -ne 0 ] && return $rc

	while read value
	do

		print $value
	done
		
	rc=$?
	return $rc
}

#######################################################
# Function: read_property_value
#
# Arguments: 
# 
# property name 
#
#
# Return: 
#
# prints out the value for the property
# status code of the print operation
#
#
# Comments:
# 
# Used to read system/extension properties via
# encapsulation of the scha_resource_get invocation.
# 
# This function encapsulates the logic  to parse the 
# name value pairs.
#######################################################
read_property_value()
{

	typeset api_command
	typeset rc
	typeset log_flag="YES"
	typeset option_tag
	typeset extension=""

	case "$1" 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="$1"
		extension=""
		;;
	DEBUG_LOGFILE| \
	DEBUG_LOG_SIZE| \
	DEBUG_LEVEL) 
		log_flag=""
		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_property_value
	fi

	rc=$?
	if [[ $rc -ne 0 ]]; then
                 if [[ "$log_flag" == "YES" ]]; then
			scds_syslog -p error -t $(syslog_tag) -m \
                	    "Error (%s) when reading property %s." "$rc" "$1" 
		fi
	fi	
	return $rc
}
#######################################################
# Function: init_trace
#
# Arguments:
#
#
# Return:
#
#
# Comments:
#
# Sets up the debug log environment. 
#############################################################
init_trace()
{
	typeset debugdir

        #[ ${DEBUG_LEVEL} -eq -1 ] && DEBUG_LEVEL=$(read_property_value DEBUG_LEVEL)

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

	TRACE_INIT="done"

	if [[ -z "$DEBUG_LOGFILE" ]]; then
		DEBUG_LOGFILE="/dev/console"
		return
	fi

	debugdir=$(/bin/dirname "$DEBUG_LOGFILE") 

	[ -n "$debugdir" ] && /bin/mkdir -p "$debugdir"

	if [[ "$DEBUG_LOGFILE" == *\. ]];then
   		DEBUG_LOGFILE="$DEBUG_LOGFILE$RESOURCE_NAME:-"""
	fi

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

	touch $DEBUG_LOGFILE 2> /dev/null

       	exec 2>> $DEBUG_LOGFILE

	rotate_log 
}

#######################################################
# Function: rotate_log
#
# Arguments:
#
#
# Return:
#
#
# Comments:
#
# Renames debug log and creates a new one.
# Same as syslog files.  
#############################################################
rotate_log()
{
        typeset log_size=0

	[ ! -f "$DEBUG_LOGFILE" ] && return
	( 
        log_size=$(/bin/ls -l "$DEBUG_LOGFILE" | /bin/awk '{print $5}')
        [ $log_size -lt $DEBUG_LOG_SIZE ] && return

        [ -f ${DEBUG_LOGFILE}.1 ] &&  /bin/rm -f "${DEBUG_LOGFILE}.1"
        [ -f ${DEBUG_LOGFILE}.0 ] && /bin/mv ${DEBUG_LOGFILE}.0 ${DEBUG_LOGFILE}.1
        /bin/mv "${DEBUG_LOGFILE}" "${DEBUG_LOGFILE}.0"
        /bin/touch "${DEBUG_LOGFILE}"
	) &
}

#######################################################
# Function: set_status
#
# Arguments:
#
#
# Return:
#
# Comments:
#   Sets resource status
#	Parameter 1: <status> (not validated)
#		 OK,  DEGRADED,  FAULTED, UNKNOWN, or OFFLINE. 
#	Parameter 2: <message> (optional)
#############################################################
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
	
	if [[ "$prev_status" = "$rstatus" ]]; then
		return 0
	fi

	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
}
#######################################################
# Function: initialize
#
# Arguments:
#
#
# Return:
#
# Comments:
############################################################
initialize()
{

	#
	# uncomment to turn on tracing in all functions
	# typeset -ft $(typeset +f)
	#
	

	# Needed for syslog messages 
	pkg=SUNWscstp


	DEBUG_LOG_SIZE=300000


}

#############################################################
# Function: read_parameters
#
# Arguments:
#
#
# Return:
#
#	0 if all ext properties read.
#
#
# Comments:
#
# Read extension properties into shell variables.
# 
# Note: The variable names are identical to their ext
# property names.
#############################################################
read_parameters()
{


        GLOBALDEVICEPATHS=$(read_property_value GlobalDevicePaths)

        FILESYSTEMMOUNTPOINTS=$(read_property_value FileSystemMountPoints)

        AFFINITYON=$(read_property_value AffinityOn)

	return 0

}

#######################################################
# Function: validate_parameters
#
# Arguments:
#
#
# Return:
#
# return 0 if validation was successful
# return 1 if there were errors
#
# Comments:
#
#############################################################
validate_parameters()
{

	#Invoke the "private" validation program and 
	#report its status. The private validation 
	#process among other things involves the validation 
	#of the underlying global devices.

	$MYDIR/hastorageplus_validate_private "${@:-}"

    	return $? 

}

#######################################################
# Function: validate_parameters_quick
#
#
# Return:
#
# return 0 if validation was successful
#
#	 0 if device paths and mount points are
#	   absent
#
# return 1 if there were errors
#
# Comments:
# 
# A "quick" validate method which skips the invocation
# of the private validation program.  
#
# Stop methods are potential callers of this 
# functions. 
#############################################################
validate_parameters_quick()
{

	# Check if GlobalDevicePaths and FilesystemMountPoints are both
	# null. If both are null, log a warning message and return 
	# without an error. 
	if  [[ "$GLOBALDEVICEPATHS" == "" ]] && [[ "$FILESYSTEMMOUNTPOINTS" == ""  ]] ; then

		scds_syslog -p warning -t $(syslog_tag)  -m \
        	"Extension properties GlobalDevicePaths and FilesystemMountPoints are both NULL."

		return 0

	fi

	# Ensure that the vfstab file is there.

	if [[ ! -f "$VFSTAB_FILE" ]]; then

		scds_syslog -p error -t $(syslog_tag)  -m \
		"$VFSTAB_FILE is missing."

		return 1

	fi

	# Ensure that the mnttab file is there.

	if [[ ! -f "$MNTTAB_FILE" ]]; then

		scds_syslog -p error -t $(syslog_tag)  -m \
		"$MNTTAB_FILE is missing."

		return 1

	fi


	return 0

}

#######################################################
# Function: set_var
#
# Arguments: 
# 'name=value' pair
#
#
# Return:
#
#
# Comments:
#
# Sets the shell variable 'name' =  'value'
#
#########################################################
set_var()
{

        typeset -u param
        typeset val
        [ -z ${1:-""} ] && return

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


        write_trace "Set var - ${param} = ${val}"


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

        return;
}

#######################################################
# Function: build_fs_list
#
# Arguments:
#
#
# Return:
#
# 0 	Always
#
#
# Comments:
# 
# $FILESYSTEMMOUNTPOINTS  returned from RGM is a 
# "," separated list of mount points.  
# The assumption made here is that file system mount points 
# do not have ',' in them. 
#
# Its safe to assume that ',' is the separator for RGM 
# extension properties. 
#############################################################
build_fs_list()
{

	
	#The RGM returns a string array ext property list with a 
	#',' as the separator. Substitute this separator with a
	# space. 

	FILESYSTEMMOUNTPOINTS="`echo $FILESYSTEMMOUNTPOINTS|/usr/bin/sed -e 's/,/ /g'`"

	return 0

}
############################################################
# Function: is_mounted
#
#
# is_mounted()
#
# Indicates whether a file system is mounted or not. 
#
# Argument: mount point
#
# Return:
#
# returns 0 if the mount point argument is absent   
# in the mnttab file i.e. it is mounted 
#
# returns 1 if the mount point argument is present
# in the mnttab file i.e. it is mounted 
#
# Comments:
# 
# MNTTAB file is assumed to have updated mount info
#############################################################
is_mounted()
{

	typeset special 
	typeset mountp 
	typeset remains


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

	while read special mountp remains
	do

		if [[ "$mountp" == "$1" ]];
		then

			write_trace "$1 is already mounted"

			return 1

		fi

	done < $MNTTAB_FILE

	write_trace "$1 is absent in $MNTTAB_FILE"

	return 0
}

#############################################################
#
# do_fsck()
#
# Function inspects a given vfstab entry ( for a mount point)
# and indicates whether to do the fsck operation 
# or to skip it. 
#
# Arguments:
#
# Arguments  passed in the following sequence -  
#
# Mountpoint Fstype  fsckpass mountatboot mountopts
#
# Return:
#
# returns 0 if the fsck operation should be skipped 
# i.e. the FS is of type UFS and logging is disabled
#
# returns 1 if the fsck operation should be done 
#
# Comments:
#
# fsck's are skipped only if the file system is of type ufs
# and logging is disabled.
#
# Keywords "logging" and "nologging" are mentioned in the 
# mount_ufs man page as the means to specify/unspecify 
# a logging ufs file system. Checks use this specification.
# 
#############################################################
do_fsck()
{


	# Arguments are not being individually broken down 
	# and searched because white spaces may get in 
	# the way. Instead, pattern matches are being performed
	# on the string.
	#
	if [[ "$2" == "ufs" ]]; then 

		# Do a pattern match 

		if [[ "$@" == *logging* ]]; then

			if [[ "$@" == *nologging* ]]; then 

				# type == ufs with no logging 
				write_trace "$1 is of type ufs with no logging"
				write_trace "Should skip fsck"
				return 0 

			else 

				write_trace "$1 is of type ufs with logging"
				write_trace "Should do fsck"
				return 1 

			fi
		
		else 
			# type == ufs with no logging 
			write_trace "$1 is of type ufs with no logging"
			write_trace "Should skip fsck"

			return  0

		fi 

	else 

		write_trace "$1 is not of type ufs. Doing fsck"

		return 1 

	fi 
	
}
############################################################
#
# Function: is_local_mountpoint
#
# Function inspects the vfstab file 
# and indicates whether a given mount point is  
# is local. 
#
# Argument: mount point
#
# Return:
#
# returns 0 
# If the mount point is not a local mount point or
# if the entry is absent in the vfstab file 
#
# returns 1 
# If the mount point is a local mount point
# 
#
# Comments:
# 
# It is assumed that checks for the existence of this 
# mount point in the vfstab file is done elsewhere.
#############################################################
is_local_mountpoint()
{
	typeset special 
	typeset fsckdev 
	typeset mountp 
	typeset type 
	typeset remains

	while read special fsckdev mountp type remains
	do

		case $special in
		'#'* | '')
			continue
			;;
		esac

		if  [[ "$mountp" = "$1" ]]; then

			# vfstab entry for the mount point
			# matched. 
			#

			# Check if "global" or "noglobal"
			# is specified.

			# K shell wild card match syntax
			#
			if [[ "$remains" == *global* ]]; then

				if [[ "$remains" == *noglobal* ]]; then

					write_trace "$mountp is a local mount point"
					return 1

				else 

					write_trace "$mountp is globally mounted"

					return 0

				fi

			else 

				# Both keywords "global"  & "noglobal"
				# absent
				write_trace "$mountp is a local mount point"

				return 1

			fi


		fi


	done < $VFSTAB_FILE

	write_trace "$mountp is not present in $VFSTAB_FILE"

	return 0

}
#include_hastorageplus_lib

method=postnet_stop

############################################################
#
# Function: umount_local_fs
#
# Arguments: none 
#
# Return:
#
# returns 0 if all local file systems have been unmounted 
# ( forcibly ) Also verifies them to be unmounted.
#
# returns 1 if an error occured 
#
# 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 rc=0
	typeset fsp
	typeset fdone

	for fsp in $FILESYSTEMMOUNTPOINTS
        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 \
				"$fsp is locally mounted. Unmounting it."

				$UMOUNT $fsp >$TMPERR_FILE 2>&1

				rc=$?

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

					scds_syslog -p error -t $(syslog_tag) -m \
					"Error when unmounting $fsp. ($rc)"

					scds_syslog -p error -t \
					$(syslog_tag) -m "`cat $TMPERR_FILE`"

					$RMF $TMPERR_FILE > /dev/null 2>&1 

					return 1

				else
					scds_syslog -p info -t $(syslog_tag) -m \
					"Successfully unmounted $fsp."

					write_trace  "`cat $TMPERR_FILE`"

					$RMF $TMPERR_FILE >/dev/null 2>&1

				fi

				fdone="FALSE"

				# Now ensure that $fsp is unmounted
				# Sleep wait till the unmount is confirmed
                		while [ "$fdone" ==  "FALSE" ]; do

					is_mounted $fsp

					rc=$?

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

						scds_syslog -p info -t $(syslog_tag) -m \
						"$fsp is not unmounted. Waiting..."

						sleep 5
						
					else 

						scds_syslog -p info -t $(syslog_tag) -m \
						"Confirmed $fsp as unmounted." 

						fdone="TRUE"

					fi


				done 

			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 \
				"$fsp is erroneously found to be unmounted. "

			fi 



		else 

			write_trace "$fsp is a global mountpoint. Not unmounting it"


		fi



	done

	return 0

}
#######################################################
# Function: main
#
# Arguments: command line
#
#
# Return: 0 if al local file systems were 
#	  successfully unmounted 
#
#	  1 if an error occured
#
#
# Comments:
#
# Forced unmounts are performed. 
#######################################################
main()
{

        typeset rc=0

        initialize

        read_arguments "${@:-}" || error_exit $?

        write_trace "read_method_arguments completed"

        read_parameters "${@:-}" || error_exit $?

        write_trace "read_parameters completed"

	#Quick validation is sufficient i.e. no 
	# involved valiadtion of device services 
	# etc.
 
        validate_parameters_quick || error_exit $?
        write_trace "validate_parameters_quick completed"


	if  [[ "$FILESYSTEMMOUNTPOINTS" != "" ]]; then

		# Unmount all local file systems. 
		#
		# Check if each file system has been
		# unmounted.
		umount_local_fs || error_exit $? 

		write_trace "umount_local_fs completed"

	fi

        exit $rc

}
main "${@:-}"
