#!/bin/ksh93
#  ALTRAN_PROLOG_BEGIN_TAG
#  This is an automatically generated prolog.
#
#  Copyright (C) Altran ACT S.A.S. 2017,2018,2021.  All rights reserved.
#
#  ALTRAN_PROLOG_END_TAG
#
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r721 src/43haes/usr/sbin/cluster/events/utils/cl_export_fs.sh 1.1.6.5 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1990,2016 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)  8153ed6 43haes/usr/sbin/cluster/events/utils/cl_export_fs.sh, 726, 2147A_aha726, Nov 23 2021 10:21 AM

#================================================
# The following, commented line enforces coding
# standards when this file is edited via vim.
#================================================
# vim:tabstop=4:shiftwidth=4:expandtab:smarttab
#================================================

#
#   COMPONENT_NAME: EVENTUTILS
#
#   FUNCTIONS: none
#
#   ORIGINS: 27
#
###############################################################################
#
#  Name:  primary
#
#  Return the first entry in a list.
#
#  Returns:
#    0 - Success
#
#  Arguments:  list of strings
#
#  Environment: None
#
###############################################################################
primary( )
{
    echo ${1:-}
}

###############################################################################
#
#  Name:  secondary
#
#  Returns all entries in a list except for the first.
#
#  Returns:
#    0 - Success
#
#  Arguments:  list of strings
#
#  Environment: None
#
###############################################################################
secondary( )
{
    [[ -n "${1:-}" ]] && shift
    echo "${@:-}"
}

###############################################################################
#
#  Name:  cl_export_fs
#
#  Given a list of filesystems, NFS export them so NFS clients can
#  continue to work.
#
#  Returns:
#    0 - Success
#    1 - Any runtime errors (unable to export, startsrc failures)
#    2 - Incorrect number of arguments was passed.
#
#  Arguments:  hostname of host given root access,
#              list of filesystems to export via nfsv3,
#              list of filesystems to export via nfsv4
#
#  Environment: PATH, VERBOSE_LOGGING
#
###############################################################################

typeset PROGNAME=${0##*/}
export PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all)"
if [[ $VERBOSE_LOGGING == "high" ]]; then
    set -x
    version='%I%'
fi

. /usr/es/sbin/cluster/events/utils/cl_nfs_utils

HOST="$1"
EXPORT_V3="$2"
EXPORT_V4="$3"

STATUS=0

integer LIMIT=60 WAIT=1 TRY=0

PROC_RES=false

#
:   If JOB_TYPE is set, and it does not equal to "GROUP", then
:   we are processing for process_resources
#
if [[ ${JOB_TYPE:-0} != 0 && $JOB_TYPE != "GROUP" ]]; then
    PROC_RES=true
fi

set -u

EXPFILE="/usr/es/sbin/cluster/etc/exports"

if (( $# < 2 || $# > 3 ))
then
    cl_echo 10103 "Usage: $PROGNAME 'hostname_given_root_access' 'v3_exports' [ 'v4_exports' ]\n" $PROGNAME
    exit 2
fi

# The following code handles migration.  In 5.4.0 and earlier the nfs export
# version was specified in the HACMP exports file.  Starting in 5.4.1 it
# became a property of the resource group.  In order to make these two
# methods coexist in a backwards compatible manner, if the resource group has
# no NFSv4 exports configured, then get the version information from the
# HACMP exports file.

DARE_EVENT=reconfig_resource_acquire

#
:   Check memory to see if NFSv4 exports have been configured.
#
export_v4=${EXPORT_V4:-${EXPORT_FILESYSTEM_V4:-}}
if [[ -z $export_v4 && ${EVENT_NAME:-$DARE_EVENT} == $DARE_EVENT ]]
then
    #
    :	During DARE operations, these variables only represent the exports
    :	that are changing.  So look it up from the ODM to make sure.
    #
    export_v4=$(clodmget -q "name=EXPORT_FILESYSTEM_V4 AND group=$GROUPNAME" -f value -n HACMPresource)
fi

#
: If we do not have NFSv4 exports configured, then determine
: the protocol versions from the HACMP exports file.
#
if [[ -z $export_v4 && -r $EXPFILE ]]
then
    export_v3=

    for fs in $EXPORT_V3
    do
	
	#
        : Get the export file for the filesystem from the exports file.
        : Only look at the part of the line preceding comments.
	#

        getline_exports $fs
        export_line=$cl_exports_data	

	#
        : The line is of the format:  filesystem -option1,option2,...
        : This will give "option1 option2 ..."
	#
        options=$(echo $export_line | awk '{ for (i=2; i<=NF; i++) printf $i " "; print "" }' |               \
                  cut -d- -f2- | tr ',' ' ')

        #
        : Each option can be of the format name=value, or just name.
        : We only care about the vers option.
	#

	#
        : Have we seen the vers option?
	#
        vers_missing=1

	#
        : Loop through all of the export options for this export.
	#
        for option in $options
        do
            case $option in
              vers=*)
                vers_missing=0

		#
                : Merge in the vers option.
		#
                case $option in
                  *2*)    export_v3="$export_v3 $fs"  ;;
                  *3*)    export_v3="$export_v3 $fs"  ;;
                esac

                case $option in
                  *4*)    export_v4="$export_v4 $fs"  ;;
                esac
                ;;
            esac
        done

	#
        : If we did not find the vers option, then NFSv3 is the default.
	#
        (( vers_missing )) && export_v3="$export_v3 $fs"
    done 

    EXPORT_V3=$export_v3
    EXPORT_V4=$export_v4
fi

KERNEL_BITS=$(/usr/sbin/bootinfo -K)

subsystems="nfsd rpc.mountd"
[[ -n $EXPORT_V4 ]] && subsystems="nfsrgyd $subsystems"

#
:   Special processing for cross mounts of EFS keys
:   The overmount of /var/efs must be removed prior 
:   to stopping or restarting NFS, since the SRC 
:   operations will attempt to check the EFS enablement.
#
mounted_info=$(mount | grep -w /var/efs)
if [[ -n $mounted_info ]]
then
    #
    :	Check to see if /var/efs is overmounted
    #
    print -- "$mounted_info" | read IP_label mounted mounted_over rest
    if [[ $mounted_over == "/var/efs" ]]
    then
	#
	:   Yes, /var/efs is overmounted.  Check to see if we did it - if
	:   there is a resource group that does this.
	#
	rg_name=$(clodmget -q "name like 'EXPORT_FILESYSTEM*' and value = '$mounted'" -f group -n HACMPresource)
	if [[ -n $rg_name ]] &&
	   [[ -n $(clodmget -q "group = '$rg_name' and name = 'MOUNT_FILESYSTEM' and value = '/var/efs;${mounted}'" HACMPresource) ]] &&
	   [[ -n $(clodmget -q "group = '$rg_name' and name = 'SERVICE_LABEL' and value = '$IP_label'" HACMPresource) ]]
	then
	    #
	    :	Yes, there is a resource group that overmounts /var/efs.
	    :	Check to see if its served by a different node.
	    #
	    LC_ALL=C host $IP_label | read skip skip IP_addr rest
	    if [[ -n $IP_addr ]] &&
	       ifconfig -a | ! grep -qw ${IP_addr%,}
	    then
		#
		:   At this point, we know that currently /var/efs is 
		:   overmounted by an NFS mount not served from this 
		:   node.  So, forcably unmount the NFS overmount on
		:   /var/efs.
		#
		unmount -f /var/efs
		if [[ -d ${mounted} ]]
		then
		    #
		    :	Under normal processing, the file system ${mounted} 
		    :	is already present on this node, ready for the local
		    :	overmount to replace the remotely served overmount
		    :	Copy its contents to /var/efs, so that there is 
		    :	something useful there until that local overmount 
		    :	can take place.	    
		    #
		    cp -pR ${mounted}/* /var/efs
		    #
		    #	The knowledgable & experienced reader will realize 
		    #	at this point that there is still a window between
		    #	the umount and the copy.  However, without this step,
		    #	any further NFS or SRC operations will hang.
		    #
		fi
	    fi
	fi	
    fi
fi

#
:   Kill and restart everything in \"$subsystems\"
#
for subsystem in $subsystems ; do
    #
    :	Kill $subsystem, and restart it below
    #
    
    #check for nfsd subsystem.if it is in inoperative state,start the nfsd subsystem.
    #nfs4smctl administers revocation of NFS version 4 State and /etc/xtab file contains 
    #entries for directories that are currently exported.
    
    if [[ $subsystem == "nfsd" ]] && 
       [[ $KERNEL_BITS == "64" && -x /usr/sbin/nfs4smctl ]] &&
       [[ ! -s /etc/xtab ]]
    then

        if clcheck_server $subsystem
        then
             startsrc -s $subsystem
             rc=$?
             if (( $rc == 0 ))
             then
                  sleep 3
                  subsys_state=$(LC_ALL=C lssrc -s $subsystem | tail +2)
             fi
  
             if (( $rc != 0 )) || ! print -- "$subsys_state" | grep -qw "active"
             then
                  cl_log 10100 "$PROGNAME: Unable to start $subsystem via SRC.\n" $PROGNAME $subsystem
                  STATUS=1
                  exit $STATUS
             fi
        fi
        #
        :   nfsv4 daemon not stopped due to existing mounts
        :   Turn on NFSv4 grace periods and ignore any errors.
        #
        ODMDIR=/etc/objrepos chnfs -I -g on -x 1

    else
	#
	:   Friendly stop of $subsystem
	#
	LC_ALL=C lssrc -s $subsystem | tail +2 | grep -qw 'active' && stopsrc -s $subsystem

	#
	:   Now, wait for $subsystem to die
	#
	for (( TRY=0 ; $TRY < $LIMIT ; TRY++ ))
	do
	    subsys_state=$(LC_ALL=C lssrc -s $subsystem | tail +2)
	    if print -- "$subsys_state" | grep -qw "inoperative"
	    then
		[[ $VERBOSE_LOGGING == "high" ]] && set -x	
		subsys_state="inoperative"
		break
	    else
		sleep $WAIT
	    fi
	    set +x				# don't fill up trace log with loop passes
	done
	[[ $VERBOSE_LOGGING == "high" ]] && set -x	

	if [[ $subsys_state != "inoperative" ]]
	then
	    #
	    :   Friendly stop of $subsystem has not worked. 
	    :   Trying a more forceful method.
	    #
	    stopsrc -cs $subsystem
	    for (( TRY=0 ; $TRY < $LIMIT ; TRY++ ))
	    do
		subsys_state=$(LC_ALL=C lssrc -s $subsystem | tail +2)
		if print -- "$subsys_state" | grep -qw "inoperative"
		then
		    [[ $VERBOSE_LOGGING == "high" ]] && set -x	
		    subsys_state="inoperative"
		    break
		else
		    sleep $WAIT
		fi
		set +x			# don't fill up trace log with loop passes
	    done
	    [[ $VERBOSE_LOGGING == "high" ]] && set -x	
	fi

	#
	:   If stopsrc has failed to stop $subsystem, 
	:   use a real kill on the daemon
	#
	ps -eo comm,pid | grep -w "$subsystem" | grep -vw grep | read skip subsys_pid rest
	[[ $subsys_pid == +([0-9]) ]] && kill $subsys_pid

	#
	:   If $subsystem has been stopped,
	:   start it back up again.
	#
	if clcheck_server $subsystem
	then
	    if [[ $subsystem == "nfsd" ]] &&
	       [[ $KERNEL_BITS == "64" && -x /usr/sbin/nfs4smctl ]]
	    then
		#
		:   Turn on NFSv4 grace periods and ignore any errors.
		#
		ODMDIR=/etc/objrepos chnfs -I -g on -x 1
	    fi
	    #
	    :   Start $subsystem back up again
	    #
	    startsrc -s $subsystem
	    rc=$?
	    if (( $rc == 0 ))
	    then
		sleep 3
		subsys_state=$(LC_ALL=C lssrc -s $subsystem | tail +2)
	    fi

	    if (( $rc != 0 )) || ! print -- "$subsys_state" | grep -qw "active"
	    then
		cl_log 10100 "$PROGNAME: Unable to start $subsystem via SRC.\n" $PROGNAME $subsystem
		STATUS=1
		exit $STATUS
	    fi
	else
	    #
	    :	$subsystem refuses to die.  This will probably leave NFS unusable
	    #
	    cl_log 10631 "$PROGNAME: Unable to stop and restart $subsystem\n" $PROGNAME $subsystem
	    STATUS=1
	    exit $STATUS
	fi
    fi
done

#
:   Set the NFSv4 "nfsroot" parameter.  This must be set prior to any
:   NFS exports that use the "exname" option, and cannot be set to a new
:   value if any "exname" exports already exist.  This is normally done
:   at IPL, but rc.nfs is not run at boot when HACMP is installed.
#
[[ -n "$EXPORT_V4" ]] && eval "$(sed -ne '/^# Begin data management/,/^# End data management/p' /etc/rc.nfs)"

hasrv=

if [[ -z "${STABLE_STORAGE_PATH:-}" ]]
then
    query="name=STABLE_STORAGE_PATH AND group=$GROUPNAME"
    STABLE_STORAGE_PATH=$(odmget -q "$query" HACMPresource |
                          sed -n 's/^[ 	]*value = "\(.*\)"/\1/p')
fi

if [[ -z "$STABLE_STORAGE_PATH" ]]
then
    STABLE_STORAGE_PATH=/var/adm/nfsv4.hacmp/$GROUPNAME
fi

if [[ -z "${STABLE_STORAGE_COOKIE:-}" ]]
then
    query="name=STABLE_STORAGE_COOKIE AND group=$GROUPNAME"
    STABLE_STORAGE_COOKIE=$(odmget -q "$query" HACMPresource |
                            sed -n 's/^[ 	]*value = "\(.*\)"/\1/p')
fi

if [[ -n $GROUPNAME ]]
then
    SERVICE_LABEL=$(odmget -q "name = SERVICE_LABEL and group = $GROUPNAME" HACMPresource |
      sed -n '/value =/s/^.*"\(.*\)".*/\1/p')
fi

primary=$(primary $SERVICE_LABEL)
secondary=$(secondary $SERVICE_LABEL)

nfs_node_state=

#
:   Determine if grace periods are enabled
#
if ps -eo 'args' | grep -w nfsd | grep -qw -- '-gp on' ; then
    gp=on
else
    gp=off
fi

#
:   We can use an NFSv4 node if grace periods are enabled, we are running a
:   64-bit kernel, and the nfs4smctl command exists.
#
if [[ $gp == "on" && $KERNEL_BITS == "64" && -x /usr/sbin/nfs4smctl ]]
then
    hasrv=$primary
else
    rm -f $STABLE_STORAGE_PATH/* 2> /dev/null
fi

#
:   If we have NFSv4 exports, then we need to configure our NFS node so that
:   we can use stable storage.  Note, NFS only supports this functionality in
:   its 64-bit kernels.
#
if [[ -n $EXPORT_V4 && -n $hasrv ]]
then
    #
    :	Get the stable storage cookie that is stored in the stable storage and
    :	see if it matches.
    #
    stable_storage_cookie=$(cat $STABLE_STORAGE_PATH/hacmp/stable_storage_cookie 2> /dev/null)

    #
    :	Are we using temporary stable storage?  or is the stable storage stale?
    #
    if [[ "$STABLE_STORAGE_PATH" == "/var/adm/nfsv4.hacmp/$GROUPNAME" ||
          "$STABLE_STORAGE_COOKIE" != "$stable_storage_cookie" ]]
    then
	#
        :   Remove the temporary or stale stable storage if it is
        :   not already in use.
	#
        cl_nfs4smctl -Q -N $GROUPNAME
        case $? in
          1)  # Runtime error (ENOENT)
              # The NFS node is not registered, so we should delete this
              # temporary stable storage since it could be stale.
              rm -f $STABLE_STORAGE_PATH/* 2> /dev/null

              # Update the stable storage cookie file
              mkdir -p $STABLE_STORAGE_PATH/hacmp
              echo $STABLE_STORAGE_COOKIE > $STABLE_STORAGE_PATH/hacmp/stable_storage_cookie
              ;;
          0)  # SUCCESS
              # The NFS node is already registered, continue using the
              # temporary stable storage (i.e., do nothing).
              ;;
        esac
    fi

    cl_RMupdate resource_acquiring NFSv4_Node $PROGNAME
    nfs_node_state=acquiring

    #
    :	Register an nfs node for this resource group with the stable storage.
    #
    if ! cl_nfs4smctl -R -N $GROUPNAME -P $STABLE_STORAGE_PATH -s $primary
    then
        #
        : Unable to register NFSv4 node. This error can be caused by the following
        :   The stable storage is corrupt.
        :     use lsfs -q to verify block size is printed
        :   nfso -H enable_ha needs to be run.
        :     This should be performed by harc.net during boot.
        :     harc.net should have a run level 2 entry in inittab
        #
        cl_log 10101 "$PROGNAME: Unable to register NFSv4 node instance $GROUPNAME.\n" $PROGNAME $GROUPNAME

        cl_RMupdate resource_error NFSv4_Node $PROGNAME
        nfs_node_state=error
        STATUS=1
        hasrv=
        EXPORT_V4=
        EXPORT_V3=
    fi

    #
    :	Register additional network addresses.
    #
    for addr in $secondary
    do
        if ! cl_nfs4smctl -A -N $GROUPNAME -n $addr
        then
            cl_log 10102 "$PROGNAME: Unable to register service label $addr with NFSv4 node instance $GROUPNAME.\n" $PROGNAME $addr $GROUPNAME

            if (( STATUS == 0 ))
            then
                cl_RMupdate resource_error NFSv4_Node $PROGNAME
                nfs_node_state=error
                STATUS=1
            fi

            hasrv=
            EXPORT_V4=
            EXPORT_V3=
        fi
    done
fi

if [[ $nfs_node_state == acquiring ]]
then
    cl_RMupdate resource_up NFSv4_Node $PROGNAME
fi

ALLEXPORTS="All_exports"
#
:   update resource manager with this action
#
cl_RMupdate resource_acquiring $ALLEXPORTS $PROGNAME

#
:   Build a list of all filesystems that need to be exported, irrespective of
:   the protocol version.  Since some filesystems may be exported with multiple
:   versions, remove any duplicates.
#
FILESYSTEM_LIST=$(echo $EXPORT_V3 $EXPORT_V4 | tr " " "\n" | sort -u)

#
:   Loop through all of the filesystems we need to export ...
#
for fs in $FILESYSTEM_LIST
do
    v3=
    v4=
    root=$HOST
    new_options=
    export_file_line=
    USING_EXPORTS_FILE=0
    export_lines=
    otheroption=

    #
    :	Get the export line from exportfs for this export
    #
    export_line=$(exportfs | grep "^[[:space:]]*${fs}[[:space:]]")

    if [[ -r $EXPFILE ]]
    then
	#
        :   Get the export file for the filesystem from the exports file.
        :   Only look at the part of the line preceding comments.
	#

        getline_exports $fs
        export_file_line=$cl_exports_data	

	#
        :   If the administrator provides an entry for the filesystem in the
        :   exports file then ignore the root option that was passed in on the
        :   command line.
	#
        [[ -n $export_file_line ]] && root=
    fi

    #
    : If the filesystem currently is not exported, then get the options from
    : the exports file.  We will merge these options with options specified
    : through resource group attributes to produce the actual options we will
    : provide to exportfs.
    #
    if [[ -z $export_line ]]
    then
        export_line="$export_file_line"
        USING_EXPORTS_FILE=1
    fi

    #
    : In case of multiple exports for same filesystem
    : Process them line by line
    #
    set +u 
    oldifs="$IFS"
    IFS=$'\n'; export_lines=($export_line)
    IFS="$oldifs"

    if [ -n "$export_lines" ]; then
        for exportline in "${export_lines[@]}"
        do

            #
            : The line is of the format:  filesystem -option1,option2,...
            : This will give "option1 option2 ..."
            #
            old_options=$(echo $exportline | awk '{ for (i=2; i<=NF; i++) printf $i " "; print "" }' |               \
                        cut -d- -f2- | tr ',' ' ')

            #
            : Each option can be of the format name=value, or just name.
            : We care about the hasrv, vers, and root options.
            #

            #
            : Loop through all of the export options for this export.
            #
            for option in $old_options
            do
                case $option in
                  hasrv=*)
	            #
                    : Squash the hasrv option.  We will provide our own later.
	            #
                    ;;
                  vers=*)
	            #
                    : Merge in the vers option.
	            #
                    case $option in
                      *2*)    v3=":2:3"  ;;
                      *3*)    v3=":2:3"  ;;
                      *4*)    v4=":4"    ;;
                    esac
                    ;;
                  root=*)
	            #
                    : Merge in the root option.
	            #
                    root=$(echo $option | cut -d= -f2-)
                    ;;
                  *)
                    if [[ $option == "-"* ]];
                    then
                        #
                        : Removeing the prefix - in option
                        #
                        option=$(echo $option | cut -d- -f 2)
                    fi

                    if [[ $option == $fs ]];
                    then
                        #
                        : Ignore the option contains fs.
                        #
                        continue
                    fi

                    otheroption=$(echo $option | cut -d= -f 1)
                    if [[ $new_options == *"$otheroption"* ]];
                    then
                        #
                        : Ignore the repeated option
                        #
                    else
                        #
                        : Merge in all remaining options.
                        #
                        new_options="$new_options,$option"
                    fi
                    ;;
                esac
            done
        done
    fi
    set -u

    #
    : At this point, v3 and v4 are set based on what is actually exported
    : or what is configured to be exported in the exports file.
    #
    # Ignore version information coming from the exports file.
    if (( USING_EXPORTS_FILE ))
    then 
        v3=
        v4=
    fi

    #
    :	At this point, v3 and v4 are set based on what is actually exported.
    :	Now add additional versions if the resource group has them configured.
    #
    for fs3 in $EXPORT_V3
    do
        [[ "$fs" == "$fs3" ]] && v3=":2:3" && break
    done

    for fs4 in $EXPORT_V4
    do
        [[ "$fs" == "$fs4" ]] && v4=":4" && break
    done

    #
    : Versions 2 and 3 are the default versions.  Some versions of AIX do
    : not support the vers export option, so only use the option if we are
    : exporting a non-default value such as '4'
    #
    if [[ -n $v4 ]]
    then
	#
        : Strip off the leading colon
	#
        vers=$(echo "$v3$v4" | cut -d: -f2-)

	#
        : '$vers' has the versions that we want to export.  Add it to our
        : export options.
	#
        new_options="$new_options,vers=$vers"
    fi

    if [[ -n $root ]]
    then
	#
	:   If we have root priveliged clients, 
	:   then add them to the option list.
	#
        new_options="$new_options,root=$root"
    fi

    if [[ -n $v4 && -n $hasrv ]]
    then
	#
	:   If this is an NFSv4 export and grace periods are enabled, 
	:   then add the hasrv option to the list.
	#
        new_options="$new_options,hasrv=$hasrv"
    fi

    #
    :	Strip off the leading comma
    #
    new_options=$(echo "$new_options" | cut -d, -f2-)

    if [[ -z $new_options ]]
    then
	#
	:   Exporting filesystem $fs with no options
	#
        exportfs -i $fs
        RC=$?
    else
	#
	:   Exporting filesystem $fs with options $new_options 
	#
    	exportfs -i -o $new_options $fs
        RC=$?
    fi

    if (( $RC != 0 ))
    then
        cl_log 43 "$PROGNAME: Unable to export $fs.\n" $PROGNAME $fs

        if [[ $PROC_RES == true ]]; then
            STATUS=11
        else
            STATUS=1    # note error and keep going
        fi
        
	#
        :   update resource manager with results
	#
        cl_RMupdate resource_error $fs $PROGNAME
    fi
done

ALLNOERREXPORT="All_nonerror_exports"
#
:   update resource manager with results
#
cl_RMupdate resource_up $ALLNOERREXPORT $PROGNAME

exit $STATUS
