#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2004,2006 
# 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 
# @(#)76 1.2.3.2 src/avs/fs/mmfs/ts/admin/mmfsctl.sh, mmfs, avs_rgpfs24, rgpfs24s005a 6/14/06 14:40:21
################################################################################
#
# Usage:
#
#   mmfsctl Device {suspend | resume}
#     or
#   mmfsctl Device {exclude | include}
#                  {-d DiskList | -F DiskFile | -G FailureGroup}
#     or
#   mmfsctl Device syncFSconfig
#                  {-n RemoteNodes | -C RemoteCluster} [-S SpecFile]
#
# where
#
#   Device            The device name of the affected GPFS file system.
#
#   suspend           Suspend the execution of all new I/O requests coming
#                     from user applications, flush all pending requests
#                     on all nodes, bring the file system to a consistent
#                     state on disk.
#
#   resume            Resume normal processing of I/O requests on all nodes.
#
#   exclude           Exclude the specified disks when determining the
#                     most recent file system control data structures.
#                     This has the effect of overriding the normal GPFS
#                     quorum requirements for on-disk file system data
#                     and stops the disks from participating into normal
#                     file systems operations.
#
#   include           Restore the specified disks for normal file system
#                     operations.
#
#   -d DiskList       The names of the disks to be excluded or included.
#                     If there is more than one disk, delimit each name with
#                     a semicolon (;) and enclose the list in quotation marks.
#
#   -F DiskFile       A file that contains, one per line, the names of the
#                     disks to be excluded or included.
#
#   -G FailureGroup   Exclude or include all disks that belong to the
#                     specified failure group.
#
#   syncFSconfig      Synchronize the file system configuration information
#                     in a remote cluster with the file system information
#                     in the local cluster.
#
#   -C RemoteCluster  The name of the remote cluster where the file system
#                     information is to be imported.  The cluster must
#                     already be registered with the mmremotecluster command.
#
#   -n RemoteNodes    The path name to a file containing, one per line,
#                     hostnames or IP addresses of nodes that belong to
#                     the remote cluster.
#
#   -S SpecFile       The path name to a file with the NSD server designations
#                     in the remote cluster for all affected disks.  The file
#                     format is the same as for the -S parameter of mmimportfs.
#                     Note that SpecFile refers to a file in the remote cluster
#                     and must exist on all nodes in the contact node list.
#
################################################################################

# Include global declarations and service routines.
. /usr/lpp/mmfs/bin/mmglobfuncs
. /usr/lpp/mmfs/bin/mmsdrfsdef
. /usr/lpp/mmfs/bin/mmfsfuncs

sourceFile="mmfsctl.sh"
[[ -n $DEBUG || -n $DEBUGmmfsctl ]] && set -x
$mmTRACE_ENTER "$*"

# Local work files.  Names should be of the form:
#   fn=${tmpDir}fn.${mmcmd}.$$

tmpDiskFile=${tmpDir}tmpDiskFile.${mmcmd}.$$

LOCAL_FILES=" $tmpDiskFile "


# Local variables
usageMsg=484
typeset -l action


# Local functions


#####################################################################
#
# Function:  Depending on the specified action, either suspend
#            or resume I/O activity for a given file system.
#
# Input:     $1 - file system device name
#            $2 - action:  suspend, suspendandflush, or resume
#
# Output:    None.
#
# Returns:   0 - no errors encountered
#            1 - unexpected error
#
#####################################################################
function fsSuspendOrResume   # <device> <action>
{
  typeset sourceFile="mmfsctl.sh"
  [[ -n $DEBUG || -n $DEBUGfsSuspendOrResume ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset device=$1
  typeset action=$2

  typeset gpfsInitOutput findFSoutput fqDeviceName fsHomeCluster
  typeset nodeCount preferredNode
  typeset rc=0

  [[ $action = suspend ]] && action=suspendandflush


  #####################################################################
  # Set up trap exception handling and ensure that the local copy of
  # the mmsdrfs is up-to-date.  There is no need to lock mmsdrfs file.
  #####################################################################
  trap pretrap2 HUP INT QUIT KILL
  gpfsInitOutput=$(gpfsInit nolock)
  setGlobalVar $? $gpfsInitOutput


  ###########################################################
  # Make sure the specified file system exists and is local.
  ###########################################################
  findFSoutput=$(findFS "$device" $mmsdrfsFile)
  [[ -z $findFSoutput ]] && cleanupAndExit

  # Parse the output from the findFS function.
  set -f ; set -- $findFSoutput ; set +f
  fqDeviceName=$1
  fsHomeCluster=$3

  # Exit with a message if the command was invoked for a remote file system.
  if [[ $fsHomeCluster != $HOME_CLUSTER ]]
  then
    # Command is not allowed for remote file systems.
    printErrorMsg 106 $mmcmd $device $fsHomeCluster
    cleanupAndExit 1
  fi


  ##################################################
  # First try to run the command on the local node.
  ##################################################
  ${mmcmdDir}/${links}/mmfsctl $fqDeviceName $action
  rc=$(remapRC $?)

  # If acceptable error (daemon not running, waiting for quorum),
  # try some other node.  Otherwise, get out; the command either
  # worked or the errors are not acceptable.
  [[ $rc -ne $MM_DaemonDown &&
     $rc -ne $MM_QuorumWait ||
     $MMMODE = single       ]] &&  \
    return $rc


  ############################################################################
  # If the local daemon is not available, send the command to an active node.
  ############################################################################

  # Create a file with the reliable names of the nodes in the cluster.
  nodeCount=$(getNodeFile $REL_HOSTNAME_Field $GLOBAL_ID $mmsdrfsFile $nodefile)
  [[ $nodeCount = 0 ]] &&  \
    checkForErrors "$mmcmd:  No nodes found in the cluster." 1

  # Try the nodes one by one until you find a node that can execute the command.
  preferredNode=0     # We have no idea where to go first; let mmcommon decide.
  $mmcommon linkCommand $preferredNode $nodefile mmfsctl $fqDeviceName $action
  rc=$?

  return $rc

}  #-------- end of function fsSuspendOrResume ----------------------


#####################################################################
#
# Function:  Mark the specified disks as excluded from stripe
#            group descriptor operations.
#
# Input:     None explicit.  This function uses the global variables
#            device, diskList, diskFile, and failureGroup.
#
# Output:    None.
#
# Returns:   0 - no errors encountered
#            1 - unexpected error
#
#####################################################################
function fsExcludeDisks  # (parms passed via global variables)
{
  typeset sourceFile="mmfsctl.sh"
  [[ -n $DEBUG || -n $DEBUGfsExcludeDisks ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset gpfsInitOutput findFSoutput fqDeviceName fsHomeCluster deviceName
  typeset diskName disksToExclude sdrfsLine preferredNode tempList
  typeset excludeThisDisk
  typeset includedDisksFound=""
  typeset rc=0


  #############################################################
  # Create a blank-separated list of the disks to be excluded.
  #############################################################
  if [[ -n $diskList ]]
  then
    # The input is specified in the form of a list of disk names.
    IFS=';'
    for diskName in $diskList
    do
      disksToExclude="${disksToExclude}${diskName}${BLANKchar}"
    done
    IFS="$IFS_sv"
    disksToExclude="${disksToExclude% }"

  elif [[ -n $diskFile ]]
  then
    # The disks are specified in a file.
    exec 3< $diskFile
    while read -u3 diskName
    do
      # Skip empty and comment lines.
      [[ $diskName = *([$BLANKchar$TABchar])   ]] && continue
      [[ $diskName = *([$BLANKchar$TABchar])#* ]] && continue

      # Put the disk name in the list.
      disksToExclude="${disksToExclude}${diskName}${BLANKchar}"
    done  # end while read -u3 diskDesc
    disksToExclude="${disksToExclude% }"

  elif [[ -n $failureGroup ]]
  then
    : # The input must be a failure group; nothing to do for now.

  else
    # Something is very wrong.
    checkForErrors "fsExcludeDisks: Missing input parameters" 1
  fi  # end if [[ -n $diskList ]]

  # Is there anything to do?
  if [[ -z $disksToExclude && -z $failureGroup ]]
  then
    # No disks were specified.
    printErrorMsg 264 $mmcmd
    cleanupAndExit
  fi


  #######################################################################
  # Set up trap exception handling and call the gpfsInit function.
  # It will ensure that the local copy of the mmsdrfs and the rest of
  # the GPFS system files are up-to-date and will obtain the sdr lock.
  #######################################################################
  trap pretrap HUP INT QUIT KILL
  gpfsInitOutput=$(gpfsInit $lockId)
  setGlobalVar $? $gpfsInitOutput

  # Determine the lookup order for resolving host names.
  [[ $osName != AIX ]] && resolveOrder=$(setHostResolveOrder)


  ###########################################################
  # Make sure the specified file system exists and is local.
  ###########################################################
  findFSoutput=$(findFS "$device" $mmsdrfsFile)
  [[ -z $findFSoutput ]] && cleanupAndExit

  # Parse the output from the findFS function.
  set -f ; set -- $findFSoutput ; set +f
  fqDeviceName=$1
  deviceName=$2
  fsHomeCluster=$3

  # Exit with a message if the command was invoked for a remote file system.
  if [[ $fsHomeCluster != $HOME_CLUSTER ]]
  then
    # Command is not allowed for remote file systems.
    printErrorMsg 106 $mmcmd $device $fsHomeCluster
    cleanupAndExit 1
  fi


  ##############################################
  # Create the new version of the mmsdrfs file.
  ##############################################
  $rm -f $newsdrfs $nodefile
  IFS=":"
  exec 3<&-
  exec 3< $mmsdrfsFile
  while read -u3 sdrfsLine
  do
    # Parse the line.
    set -f ; set -A v -- - $sdrfsLine ; set +f

    IFS="$IFS_sv"   # Restore the default IFS settings.

    # Change some of the fields depending on the type of line.
    case ${v[$LINE_TYPE_Field]} in

      $VERSION_LINE )  # This is the global header line.
        # Increment the generation number.
        newGenNumber=${v[$SDRFS_GENNUM_Field]}+1
        v[$SDRFS_GENNUM_Field]=$newGenNumber
        ;;

      $MEMBER_NODE )
        # Add the reliable node name to nodefile.
        print -- "${v[$REL_HOSTNAME_Field]}" >> $nodefile
        checkForErrors "writing to file $nodefile" $?

        # If this is the line for the node that is executing
        # this command, set the preferredNode variable.
        [[ ${v[$NODE_NUMBER_Field]} = $ourNodeNumber ]] &&  \
          preferredNode=${v[$REL_HOSTNAME_Field]}
        ;;

      $SG_DISKS )

        if [[ ${v[$DEV_NAME_Field]} = $deviceName ]]
        then
          # This is an SG_DISKS line that belongs to our filesystem.
          # See if this disk is one of the disks to be excluded.
          if [[ -n $failureGroup ]]
          then
            # Disks are to be excluded based on failure group.
            [[ ${v[$FAILURE_GROUP_Field]} = $failureGroup ]] &&  \
              v[$EXCLUDE_Field]=$excludedDisk

          else
            # The disks to be excluded are specified in the disksToExclude list.
            tempList=""
            excludeThisDisk=no
            for diskName in $disksToExclude
            do
              if [[ $diskName = ${v[$DISK_NAME_Field]} ]]
              then
                # The disk represented by the current SG_DISKS line
                # is one of the disks that must be excluded.
                if [[ $excludeThisDisk = no ]]
                then
                  # Mark the disk as excluded.
                  v[$EXCLUDE_Field]=$excludedDisk
                  excludeThisDisk=yes
                else
                  # We have already seen this name during the current iteration.
                  # It must be a duplicate entry in the command line list.
                  printErrorMsg 88 $mmcmd $diskName
                  cleanupAndExit
                fi  # end of if [[ $excludeThisDisk = no ]]

              else
                # diskName does not match the name of the disk in the current
                # SG_DISKS line.  Add diskName to the temporary list.
                tempList="$tempList $diskName"
              fi  # end of if [[ $diskName = ${v[$DISK_NAME_Field]} ]]
            done  # end of for diskName in $disksToExclude

            # If this disk will be excluded, its name does not appear in
            # tempList.  In other words, tempList contains only the names of
            # the disks that are to be excluded but for which the corresponding
            # SG_LINES have not been encountered yet.  Initialize the list
            # for the next iteration.
            disksToExclude=$tempList

          fi  # end of if [[ -n $failureGroup ]]

          # Keep an overall track of the EXCLUDE field to prevent
          # marking all disks as excluded.
          [[ ${v[$EXCLUDE_Field]} = $includedDisk ]] &&
            includedDisksFound=yes

        fi  # end of if [[ ${v[$DEV_NAME_Field]} = $deviceName ]]
        ;;

      * )  # Pass all other lines without a change.
        ;;

    esac  # end of "Change some of the fields . . . "

    # Build and write the line to the new mmsdrfs file.
    print_newLine >> $newsdrfs
    checkForErrors "writing to file $newsdrfs" $?

    IFS=":"  # Change the separator back to ":" for the next iteration.

  done  # end while read -u3 sdrfsLine

  IFS="$IFS_sv"  # Restore the default IFS settings.

  # If there are still entries left in the disksToExclude list, this means
  # that the user-specified disks that do not belong to the file system.
  if [[ -n $disksToExclude ]]
  then
    for diskName in $disksToExclude
    do
      printErrorMsg 315 $mmcmd $diskName $device
    done
    cleanupAndExit
  fi

  # Make sure that at least one disk will remain included.
  if [[ -z $includedDisksFound ]]
  then
    # All of the disks cannot be excluded.
    print -u2 "$mmcmd: You cannot exclude all of the disks in a file system."
    cleanupAndExit
  fi

  # Make sure the file system is not mounted on any of the nodes.
  $mmcommon onactive $preferredNode $nodefile  \
     $NO_FILE_COPY $fqDeviceName $CHECK_ALL $NO_LINK $MOUNT_CHECK_ONLY 2>$errMsg
  rc=$?
  if [[ $rc -eq $MM_FsMounted ]]
  then
    # The file system is still mounted; messages were issued by mmcommon.
    cleanupAndExit
  elif [[ $rc -eq $MM_DaemonDown ]]
  then
    # GPFS is down on all nodes that we care about; that's just fine.
    rc=0
  elif [[ $rc -eq $MM_ConnectionReset ]]
  then
    # An internode connection was reset.
    printErrorMsg 257 $mmcmd
    cleanupAndExit $rc
  elif [[ $rc -ne 0 ]]
  then
    # An unexpected error occurred during the mount check.
    if [[ -s $errMsg ]]
    then
      # Show the error messages from the daemon.
      $cat $errMsg 1>&2
    else
      # The mount check failed and there were no messages from the daemon.
      printErrorMsg 171 $mmcmd "mount check for $fqDeviceName" $rc
    fi
    # The command was unable to determine whether the file system is mounted.
    printErrorMsg 564 $mmcmd $fqDeviceName
    # The command failed.
    printErrorMsg 389 $mmcmd
    cleanupAndExit $rc
  fi
  $rm -f $errMsg


  ###############################
  # Commit the new mmsdrfs file.
  ###############################
  trap "" HUP INT QUIT KILL
  gpfsObjectInfo=$(commitChanges  \
     $nodesetId $nsId $gpfsObjectInfo $newGenNumber $newsdrfs $primaryServer)
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    # We were unable to replace the file in the sdr.
    printErrorMsg 381 $mmcmd
    cleanupAndExit
  fi


  ##################
  # Unlock the sdr.
  ##################
  [[ $sdrLocked = yes ]] &&  \
    freeLockOnServer $primaryServer $ourNodeNumber >/dev/null
  sdrLocked=no
  trap localPosttrap HUP INT QUIT KILL


  ##################################################################
  # Propagate the new mmsdrfs file.  This process is asynchronous.
  ##################################################################
  propagateSdrfsFile async $nodefile $newsdrfs $newGenNumber

  return $rc

}  #-------- end of function fsExcludeDisks ----------------------


#####################################################################
#
# Function:  Mark the specified disks as included for stripe group
#            descriptor operations.
#
# Input:     None explicit.  This function uses the global variables
#            device, diskList, diskFile, and failureGroup.
#
# Output:    None.
#
# Returns:   0 - no errors encountered
#            1 - unexpected error
#
#####################################################################
function fsIncludeDisks  # (parms passed via global variables)
{
  typeset sourceFile="mmfsctl.sh"
  [[ -n $DEBUG || -n $DEBUGfsIncludeDisks ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset gpfsInitOutput findFSoutput fqDeviceName fsHomeCluster deviceName
  typeset diskName disksToInclude sdrfsLine preferredNode tempList
  typeset includeThisDisk
  typeset rc=0

  tsDiskFile=${tmpDir}tsDiskFile.${mmcmd}.$$
  LOCAL_FILES="$LOCAL_FILES $tsDiskFile"


  #############################################################
  # Create a blank-separated list of the disks to be included.
  #############################################################
  if [[ -n $diskList ]]
  then
    # The input is specified in the form of a list of disk names.
    IFS=';'
    for diskName in $diskList
    do
      disksToInclude="${disksToInclude}${diskName}${BLANKchar}"
    done
    IFS="$IFS_sv"
    disksToInclude="${disksToInclude% }"

  elif [[ -n $diskFile ]]
  then
    # The disks are specified in a file.
    exec 3< $diskFile
    while read -u3 diskName
    do
      # Skip empty and comment lines.
      [[ $diskName = *([$BLANKchar$TABchar])   ]] && continue
      [[ $diskName = *([$BLANKchar$TABchar])#* ]] && continue

      # Put the disk name in the list.
      disksToInclude="${disksToInclude}${diskName}${BLANKchar}"
    done  # end while read -u3 diskDesc
    disksToInclude="${disksToInclude% }"

  elif [[ -n $failureGroup ]]
  then
    : # The input must be a failure group; nothing to do for now.

  else
    # Something is very wrong.
    checkForErrors "fsIncludeDisks: Missing input parameters" 1
  fi  # end if [[ -n $diskList ]]

  # Is there anything to do?
  if [[ -z $disksToInclude && -z $failureGroup ]]
  then
    # No disks were specified.
    printErrorMsg 264 $mmcmd
    cleanupAndExit
  fi


  #######################################################################
  # Set up trap exception handling and call the gpfsInit function.
  # It will ensure that the local copy of the mmsdrfs and the rest of
  # the GPFS system files are up-to-date and will obtain the sdr lock.
  #######################################################################
  trap pretrap HUP INT QUIT KILL
  gpfsInitOutput=$(gpfsInit $lockId)
  setGlobalVar $? $gpfsInitOutput

  # Determine the lookup order for resolving host names.
  [[ $osName != AIX ]] && resolveOrder=$(setHostResolveOrder)


  ###########################################################
  # Make sure the specified file system exists and is local.
  ###########################################################
  findFSoutput=$(findFS "$device" $mmsdrfsFile)
  [[ -z $findFSoutput ]] && cleanupAndExit

  # Parse the output from the findFS function.
  set -f ; set -- $findFSoutput ; set +f
  fqDeviceName=$1
  deviceName=$2
  fsHomeCluster=$3

  # Exit with a message if the command was invoked for a remote file system.
  if [[ $fsHomeCluster != $HOME_CLUSTER ]]
  then
    # Command is not allowed for remote file systems.
    printErrorMsg 106 $mmcmd $device $fsHomeCluster
    cleanupAndExit 1
  fi


  ##############################################
  # Create the new version of the mmsdrfs file.
  ##############################################
  $rm -f $newsdrfs $nodefile $tsDiskFile
  IFS=":"
  exec 3<&-
  exec 3< $mmsdrfsFile
  while read -u3 sdrfsLine
  do
    # Parse the line.
    set -f ; set -A v -- - $sdrfsLine ; set +f

    IFS="$IFS_sv"   # Restore the default IFS settings.

    # Change some of the fields depending on the type of line.
    case ${v[$LINE_TYPE_Field]} in

      $VERSION_LINE )  # This is the global header line.
        # Increment the generation number.
        newGenNumber=${v[$SDRFS_GENNUM_Field]}+1
        v[$SDRFS_GENNUM_Field]=$newGenNumber
        ;;

      $MEMBER_NODE )
        # Add the reliable node name to nodefile.
        print -- "${v[$REL_HOSTNAME_Field]}" >> $nodefile
        checkForErrors "writing to file $nodefile" $?

        # If this is the line for the node that is executing
        # this command, set the preferredNode variable.
        [[ ${v[$NODE_NUMBER_Field]} = $ourNodeNumber ]] &&  \
          preferredNode=${v[$REL_HOSTNAME_Field]}
        ;;

      $SG_DISKS )

        if [[ ${v[$DEV_NAME_Field]} = $deviceName ]]
        then
          # This is an SG_DISKS line that belongs to our filesystem.
          # See if this disk is one of the disks to be included.
          if [[ -n $failureGroup ]]
          then
            # Disks are to be included based on failure group.
            if [[ ${v[$FAILURE_GROUP_Field]} = $failureGroup ]]
            then
              # Mark the disk as included.
              v[$EXCLUDE_Field]=$includedDisk

              # Add the disk to the list of disks for tsfsctl.
              print -- "${v[$DISK_NAME_Field]}" >> $tsDiskFile
              checkForErrors "writing to file $tsDiskFile" $?
            fi  # end if [[ ${v[$FAILURE_GROUP_Field]} = $failureGroup ]]

          else
            # The disks to be included are specified in the disksToInclude list.
            tempList=""
            includeThisDisk=no
            for diskName in $disksToInclude
            do
              if [[ $diskName = ${v[$DISK_NAME_Field]} ]]
              then
                # The disk represented by the current SG_DISKS line
                # is one of the disks that must be included.
                if [[ $includeThisDisk = no ]]
                then
                  # Mark the disk as included.
                  v[$EXCLUDE_Field]=$includedDisk
                  includeThisDisk=yes

                  # Add the disk to the list of disks for tsfsctl.
                  print -- "${v[$DISK_NAME_Field]}" >> $tsDiskFile
                  checkForErrors "writing to file $tsDiskFile" $?
                else
                  # We have already seen this name during the current iteration.
                  # It must be a duplicate entry in the command line list.
                  printErrorMsg 88 $mmcmd $diskName
                  cleanupAndExit
                fi  # end of if [[ $includeThisDisk = no ]]

              else
                # diskName does not match the name of the disk in the current
                # SG_DISKS line.  Add diskName to the temporary list.
                tempList="$tempList $diskName"
              fi  # end of if [[ $diskName = ${v[$DISK_NAME_Field]} ]]
            done  # end of for diskName in $disksToInclude

            # If this disk will be included, its name does not appear in
            # tempList.  In other words, tempList contains only the names of
            # the disks that are to be included but for which the corresponding
            # SG_LINES have not been encountered yet.  Initialize the list
            # for the next iteration.
            disksToInclude=$tempList

          fi  # end of if [[ -n $failureGroup ]]

        fi  # end of if [[ ${v[$DEV_NAME_Field]} = $deviceName ]]
        ;;

      * )  # Pass all other lines without a change.
        ;;

    esac  # end of "Change some of the fields . . . "

    # Build and write the line to the new mmsdrfs file.
    print_newLine >> $newsdrfs
    checkForErrors "writing to file $newsdrfs" $?

    IFS=":"  # Change the separator back to ":" for the next iteration.

  done  # end while read -u3 sdrfsLine

  IFS="$IFS_sv"  # Restore the default IFS settings.

  # If there are still entries left in the disksToInclude list, this means
  # that the user-specified disks that do not belong to the file system.
  if [[ -n $disksToInclude ]]
  then
    for diskName in $disksToInclude
    do
      printErrorMsg 315 $mmcmd $diskName $device
    done
    cleanupAndExit
  fi


  ##############################
  # Invoke the tsfsctl command.
  ##############################
  $mmcommon onactive $preferredNode $nodefile                       \
                     $tsDiskFile $NO_MOUNT_CHECK NULL $NO_LINK      \
                     tsfsctl "$fqDeviceName include -F $tsDiskFile"
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    # tsfsctl failed.
    printErrorMsg 104 "$mmcmd" "tsfsctl include"
    cleanupAndExit $rc
  fi


  ###############################
  # Commit the new mmsdrfs file.
  ###############################
  trap "" HUP INT QUIT KILL
  gpfsObjectInfo=$(commitChanges  \
     $nodesetId $nsId $gpfsObjectInfo $newGenNumber $newsdrfs $primaryServer)
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    # We were unable to replace the file in the sdr.
    printErrorMsg 381 $mmcmd
    cleanupAndExit
  fi


  ##################
  # Unlock the sdr.
  ##################
  [[ $sdrLocked = yes ]] &&  \
    freeLockOnServer $primaryServer $ourNodeNumber >/dev/null
  sdrLocked=no
  trap localPosttrap HUP INT QUIT KILL


  ##################################################################
  # Propagate the new mmsdrfs file.  This process is asynchronous.
  ##################################################################
  propagateSdrfsFile async $nodefile $newsdrfs $newGenNumber

  return $rc

}  #-------- end of function fsIncludeDisks ----------------------


###########################################################################
#
# Function:  Synchronize the file system configuration information
#            in the target cluster with the file system config data
#            in the local cluster.
#
# Input:     None explicit.  This function uses the global variables
#            remoteNodes, remoteCluster, and Sflag.
#
# Output:    None.
#
# Returns:   0 - no errors encountered
#            1 - unexpected error
#
###########################################################################
function syncFSconfig  # (parms passed via global variables)
{
  typeset sourceFile="mmfsctl.sh"
  [[ -n $DEBUG || -n $DEBUGsyncFSconfig ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset contactNodes nodeName
  typeset rc=0
  typeset activeNodeFound=""

  fsExportData=${tmpDir}fsExportData.${mmcmd}.$$
  LOCAL_FILES="$LOCAL_FILES $fsExportData"

  # Determine where to send the data.
  if [[ -n $remoteCluster ]]
  then
    # If a cluster name was specified, get the contact nodes
    # from the mmsdrfs file.
    contactNodes=$($mmcommon getContactNodes $remoteCluster)
    $rm -f $nodefile
    IFS=","
    for nodeName in $contactNodes
    do
      [[ $nodeName != tcpPort* ]] &&  \
        print -- "$nodeName" >> $nodefile
    done
    IFS="$IFS_sv"  # Restore the default IFS settings.

    if [[ ! -s $nodefile ]]
    then
      # No contact nodes were provided for the cluster.
      printErrorMsg 177 $mmcmd $remoteCluster
      cleanupAndExit
    fi
  else
    : # The contact nodes are in $nodefile (created by checkUserFile).
  fi  # end if [[ -n $remoteCluster ]]

  # Extract the requested file system information from the local cluster.
  # Collect the output in a file and show it only if there is an error.
# printInfoMsg xyz $mmcmd
  print -u2 "$mmcmd:  Exporting file system information from the source cluster . . ."
  $mmexportfs $device -o $fsExportData -P
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    printErrorMsg 389 "$mmcmd: mmexportfs"
    cleanupAndExit
  fi

  # Import the file system information into the remote cluster.
  # Collect the output in a file and show it only if there is an error.
  exec 3<&-
  exec 3< $nodefile
  while read -u3 nodeName
  do
    isNodeReachable $nodeName
    [[ $? -ne 0 ]] && continue

    activeNodeFound=yes
#   printInfoMsg wxy $mmcmd
    print -u2 "$mmcmd:  Importing file system information into the target cluster on node $nodeName . . ."
    $mmdsh -svL $nodeName -I $fsExportData  \
       "$mmimportfs $device -i $fsExportData $Sflag -R ; $rm -f $fsExportData"
    rc=$?
    [[ $rc -eq 0 ]] && break
  done  # end of while read -u3 nodeName do

  # Did we fail to run mmimportfs succesfully anywhere?
  if [[ $rc -ne 0 ]]
  then
    # The command failed.  Examine prior error messages.
    printErrorMsg 389 "syncFSconfig: mmimportfs"
    cleanupAndExit
  fi

  if [[ -z $activeNodeFound ]]
  then
    if [[ -n $remoteCluster ]]
    then
      # None of the contact nodes in the cluster can be reached.
      printErrorMsg 178 $mmcmd $remoteCluster
    else
      print -u2 "$mmcmd:  None of the nodes in the peer cluster can be reached."
    fi
    cleanupAndExit
  fi

  return $rc

}  #------ end of function syncFSconfig ---------------------



#######################
# Mainline processing
#######################


######################################
# Process the command line arguments.
######################################
[[ $arg1 = '-?' || $arg1 = '-h' || $arg1 = '--help' || $arg1 = '--' ]] &&  \
  syntaxError "help" $usageMsg

# Process the positional parameters.
device=$arg1
kword=$arg2
action=$arg2   # action keyword in lower case only

[[ -z $action ]] && syntaxError "missingArgs" $usageMsg
shift 2    # Move past the fs name and action parameters.


# Process the rest of the parameters.
if [[ $action = suspend || $action = suspendandflush || $action = resume ]]
then
  [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1

elif [[ $action = exclude || $action = include ]]
then
  while getopts :d:F:G: OPT
  do
    case $OPT in

      d) [[ -n $dflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         dflag="-$OPT"
         [[ -n $Fflag || -n $Gflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $dflag $Fflag $Gflag
         diskList="$OPTARG"
         ;;

      F) [[ -n $Fflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         Fflag="-$OPT"
         [[ -n $dflag || -n $Gflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $dflag $Fflag $Gflag
         Farg="$OPTARG"
         ;;

      G) [[ -n $Gflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         Gflag="-$OPT"
         [[ -n $dflag || -n $Fflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $dflag $Fflag $Gflag
         failureGroup=$(checkIntRange "-G" "$OPTARG")
         [[ $? -ne 0 ]] && cleanupAndExit
         ;;

       +[dfG]) # Invalid option.
          syntaxError "invalidOption" $usageMsg $OPT
          ;;

       :) # Missing argument.
          syntaxError "missingValue" $usageMsg $OPTARG
          ;;

       *) # Invalid option.
          syntaxError "invalidOption" $usageMsg $OPTARG
          ;;
     esac
  done
  shift OPTIND-1

  [[ -z $dflag && -z $Fflag && -z $Gflag ]] &&  \
    syntaxError "missingArgs" $usageMsg
  [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1

  # If -F was specified, ensure the disk names file exists and is not empty.
  if [[ -n $Fflag ]]
  then
    checkUserFile $Farg $tmpDiskFile
    [[ $? -ne 0 ]] && cleanupAndExit
    diskFile=$tmpDiskFile
  fi  # end of if [[ -n $Fflag ]]

elif [[ $action = syncfsconfig ]]
then
  while getopts :C:n:S: OPT
  do
    case $OPT in
      C) # Name of the target cluster.
         [[ -n $Cflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         remoteCluster=$OPTARG
         Cflag=yes
         ;;

      n) # Name of contact nodes file.
         [[ -n $nflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         narg=$OPTARG
         nflag=yes
         ;;

      S) # Name of disk specifications file.
         [[ -n $Sflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         Sflag="-S $OPTARG"
         ;;

      :) syntaxError "missingValue" $usageMsg $OPTARG
         ;;

      +[CnS]) syntaxError "invalidOption" $usageMsg $OPT
         ;;

      *) # Invalid option specified.
         syntaxError "invalidOption" $usageMsg $OPTARG
         ;;
    esac
  done
  shift OPTIND-1

  [[ -n $Cflag && -n $nflag ]] &&  \
    syntaxError "invalidCombination"  $usageMsg "-C" "-n"
  [[ -z $Cflag && -z $nflag ]] &&  \
    syntaxError "missingArgs" $usageMsg
  [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1

  # If -n was specified, ensure the contact nodes file exist and is not empty.
  # This will also copy the names into $nodefile (needed by function syncfsconfig).
  if [[ -n $nflag ]]
  then
    checkUserFile $narg $nodefile
    [[ $? -ne 0 ]] && cleanupAndExit
  fi  # end of if [[ -n $nflag ]]

else
  syntaxError "keyword" $usageMsg $kword

fi  # end if [[ $action = suspend || $action = suspendandflush || $action = resume ]]


##################################################
# Invoke the appropriate function to do the work.
##################################################

case $action in
  suspend | suspendandflush | resume )
    fsSuspendOrResume $device $action
    rc=$?
    ;;

  exclude )
    fsExcludeDisks  # Parms are passed via global variables.
    rc=$?
    ;;

  include )
    fsIncludeDisks  # Parms are passed via global variables.
    rc=$?
    ;;

  syncfsconfig )
    syncFSconfig    # Parms are passed via global variables.
    rc=$?
    ;;

  *) syntaxError "keyword" $usageMsg $kword
    ;;
esac  # end case $action in

cleanupAndExit $rc

