#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 1997,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 
# @(#)29 1.290.1.9 src/avs/fs/mmfs/ts/admin/mmremote.sh, mmfs, avs_rgpfs24, rgpfs24s008a 11/25/06 00:29:43
#######################################################################

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

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

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

LOCAL_FILES=" "


# Local variables
typeset -l kword_lc
typeset -l arg3_lc
rc=0


# Local functions

####################################################################
#
# Function:  Take a snapshot of the currently-running trace.
#
# Input:     $1 - action to perform
#
# Output:    None
#
# Returns:   0
#
####################################################################
function traceSnapshot
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGtraceSnapshot ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset action=$1
  typeset tracePid

  if [[ -z $(print -- " off on offon log " | $fgrep " $action ") ]]
  then
    # Invalid value for flag
    printErrorMsg 153 $mmcmd tsnap
    return 1
  fi

  if [[ $action != off ]]
  then
    $mmfsadm showtrace >/dev/null
    if [[ $? -eq 0 ]]
    then
      $mmfsadm trace all 9
      $mmfsadm trace tm 2 thread 1 mutex 1 vnode 1 ksvfs 1 klockl 0
      $mmfsadm trace io 3 pgalloc 1 mb 1 lock 2 dfs 2 fsck 3
    fi
  fi

  tracePid=$($ps -eo "pid args" | $awk '/trace / && !/this process/ {print $1}')
  if [[ ($action = off || $action = offon) && -n $tracePid ]]
  then
    $trcstop
    $mv /var/adm/ras/trcfile  /var/adm/ras/trcfile.$(date +"%H.%M.%S")
  fi

  if [[ $action = offon || $action = on ]]
  then
    $trace -a -l -L 12000000 -T 4000000 -j 306,307,308,309  \
       -o /var/adm/ras/trcfile >/dev/null 2>&1 &
  fi

  date=$(date +"%y%m%d")
  $mkdir -p /log/$date 2>/dev/null

  for trcname in $($ls /var/adm/ras | $grep trcfile.)
  do
    time=$(print -- $trcname | $awk -F. '{print $2}')
    $cp -p /var/adm/ras/trcfile.$time  \
        /log/$date/trcfile.$date.$time."$($hostname -s)"
    $rm -f /var/adm/ras/trcfile.$time
  done

  return 0

}  #------ end of function traceSnapshot -------------------


###############################################################################
#
# Function:  Send the local mmsdrfs file to the node that is requesting it.
#            Do this only if the client's generation number is smaller than
#            the generation number of the mmsdrfs file on this node.
#            If requested, get the lock.
#
# Input:     $1 - generation number of the sdr file on the client
#            $2 - name of client requesting the file
#            $3 - name to use on the client for the retrieved file
#            $4 - lockId or nolock
#
# Output:    gpfsObject or 'fail'.
#
# Returns:   Zero if successful, non-zero otherwise.
#
###############################################################################
function sendClusterSDRFiles  # <clientGenNumber> <client> <fileName> <lockId>
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGsendClusterSDRFiles ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset clientGenNumber=$1
  typeset client=$2
  typeset fileName=$3
  typeset lockId=$4

  typeset rc=0
  typeset lockObtained=""
  typeset lockResult gpfsObjectInfo serverGenNumber junk
  typeset pid getObjectPort

  # Get the lock if requested
  if [[ $lockId != nolock ]]
  then
    lockResult=$(getLock $lockId)

    # Look at the result from the lock request.
    # If it is more than one word, we assume it is an error message.
    set -f ; set -- $lockResult ; set +f
    gpfsObjectInfo=$1
    junk=$2
    if [[ -z $gpfsObjectInfo || -n $junk ]]
    then
      # If unexpected output, give up
      [[ -n $junk ]] &&  \
        print -u2 "$lockResult"
      return 1
    fi
    if [[ $lockResult = fail ]]
    then
      print -- "fail"
      return 1
    else
      lockObtained=yes
    fi
  fi

  # If we are here, either the caller did not need the lock or the lock
  # was successfully obtained.  Make sure the caller has the latest data.

  # Get the server's generation number.
  gpfsObjectInfo=$(getGpfsObject)
  rc=$?
  IFS=':'
  set -f ; set -- $gpfsObjectInfo ; set +f
  IFS="$IFS_sv"
  serverGenNumber=$2
  if [[ $rc -ne 0 || -z $serverGenNumber ]]
  then
    # Unexpected value for the Gpfs object
    [[ $rc -eq 0 ]] &&  \
      printErrorMsg 286 sendClusterSDRFiles $gpfsObjectInfo
    [[ $lockObtained = yes ]] &&  \
      freeLock 0 > /dev/null
    print -- "fail"
    return 1
  fi

  # If the client's local generation number is smaller than
  # the one we have here, copy our version to the client.
  if [[ $clientGenNumber -lt $serverGenNumber ]]
  then
    $rcp $mmsdrfsFile ${client}:${fileName}
    rc=$?
  fi

  # If everything is OK so far, return the Gpfs object.
  if [[ $rc -eq 0 ]]
  then
    print -- "$gpfsObjectInfo"
  else
    [[ $lockObtained = yes ]] &&  \
      freeLock 0 > /dev/null
    print -- "fail"
  fi
  return $rc

}  #------ end of function sendClusterSDRFiles ------------


############################################################################
#
# Function:  Determine whether this node is new to GPFS and whether the
#            GPFS code on this node can support clustering.  If it can,
#            create a skeleton version of the sdrfs file that reflects
#            the passed key attributes and a generation number of 1.
#
# Input:     cltype         - cluster and environment type (e.g., "lc/lc2")
#            primaryServer  - primary server
#            backupServer   - secondary server or _NOSECONDARY_
#            localNodeData  - hostname, node number, etc. for the node
#            rshPath        - remote shell command or _DEFAULT_
#            rcpPath        - remote file copy command or _DEFAULT_
#            clusterIdAndSt - cluster id and cluster subtype
#
# Output:    A colon-separated list of the following fields:
#              keyword         - checkNewClusterNode
#              status          - success, not_new, etc.
#              adapter type    - e.g., eth0
#              daemon version  - e.g., 800
#              product version - e.g., 2.3.0.0
#              OS name         - AIX or Linux
#
#            Note:  The adapter type and subsequent fields have meaningful
#                   information only if status is "success".
#
# Returns:   zero if success, non-zero otherwise
#
############################################################################
function checkNewClusterNode  # <cltype> <primaryServer> <backupServer>
                              # <localNodeData> <rshPath> <rcpPath>
                              # <clusterIdAndSt>
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGcheckNewClusterNode ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset cltype=$1
  typeset primary=$2
  typeset secondary=$3
  typeset localNodeData=$4
  typeset rshPath=$5
  typeset rcpPath=$6
  typeset clusterIdAndSt=$7

  typeset clusterType environmentType result hostResult
  typeset initialSystemFiles sdrfsLine nodeNumber ipa adminNodeName adminIpa
  typeset clusterId clusterSubtype adapterType nodeStatus

  typeset newGenNumber=1
  typeset rc=0

  initialSystemFiles="$mmsdrfsFile ${mmfsEnvLevel}$newGenNumber $mmfsNodeData"
  [[ $secondary = "_NOSECONDARY_" ]] && secondary=""
  [[ $rshPath = "_DEFAULT_" ]] && rshPath=""
  [[ $rcpPath = "_DEFAULT_" ]] && rcpPath=""
  export GPFS_rshPath="$rshPath"
  export GPFS_rcpPath="$rcpPath"

  # Check that no sdrfs file exists on this node;
  # report "not new" if an sdrfs file already exists.
  if [[ -f $mmsdrfsFile ]]
  then
    print -- "checkNewClusterNode:not_new:"
    return 1
  fi

  # Parse the overloaded input parameters.
  IFS='/'
  set -f ; set -- $cltype ; set +f
  IFS="$IFS_sv"
  clusterType=$1
  environmentType=$2
  [[ -z $environmentType ]] && environmentType=$clusterType

  IFS=':'
  set -f ; set -- $clusterIdAndSt ; set +f
  IFS="$IFS_sv"
  clusterId=$1
  clusterSubtype=$2
  [[ -z $clusterSubtype ]] && clusterSubtype=$environmentType

  # Check whether the prerequisite software for the specified
  # cluster type is installed.
  checkPrereqs $environmentType
  if [[ $? -ne 0 ]]
  then
    print -- "checkNewClusterNode:not_supported:"
    return 1
  fi

  # Create a skeleton sdrfs file on this node.
  $mkdir -p $mmsdrfsDir
  sdrfsLine="$GLOBAL_ID:$VERSION_LINE::$CURRENT_SDRFS_FORMAT:$CURRENT_SDRFS_VERSION"
  sdrfsLine="$sdrfsLine:$newGenNumber::$clusterType:$primary:$secondary"
  sdrfsLine="$sdrfsLine::$rshPath:$rcpPath:$clusterId:$clusterSubtype:"
  print -- "$sdrfsLine" > $mmsdrfsFile

  # Add threatening comment lines:  DO NOT MESS WITH THIS FILE!
  sdrfsLine="$GLOBAL_ID:$COMMENT_LINE::1:"
  print -- "$sdrfsLine" >> $mmsdrfsFile
  sdrfsLine="$GLOBAL_ID:$COMMENT_LINE::2:$warningText"
  print -- "$sdrfsLine" >> $mmsdrfsFile
  sdrfsLine="$GLOBAL_ID:$COMMENT_LINE::3:"
  print -- "$sdrfsLine" >> $mmsdrfsFile

  # Add the local node information.
  print -- "$localNodeData" >> $mmsdrfsFile
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    printErrorMsg 171 "checkNewClusterNode" "writing to file $mmsdrfsFile" $rc
    print -- "checkNewClusterNode:unexpected_error:"
    $rm -f $initialSystemFiles
    return 1
  fi

  # Create a file with the MEMBER_NODE line for this node.
  # It will be used later to quickly determine the node's
  # node number, reliable hostname, and other information.
  print -- "$localNodeData" > $mmfsNodeData
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    printErrorMsg 171 "checkNewClusterNode" "writing to file $mmfsNodeData" $rc
    print -- "checkNewClusterNode:unexpected_error:"
    $rm -f $initialSystemFiles
    return 1
  fi

  # Remember the current level of the system files.
  $touch ${mmfsEnvLevel}$newGenNumber

  # Flush the important files to disk.
  $mmsync $mmsdrfsFile $mmfsNodeData ${mmfsEnvLevel}$newGenNumber

  # Retrieve needed information from the local node data.
  IFS=':'
  set -f ; set -A v -- - $localNodeData ; set +f
  IFS="$IFS_sv"
  nodeNumber=${v[$NODE_NUMBER_Field]}
  ipa=${v[$IPA_Field]}
  adapterType=${v[$ADAPTER_TYPE_Field]}
  adminNodeName=${v[$REL_HOSTNAME_Field]}
  daemonNodeName=${v[$DAEMON_NODENAME_Field]}

  if [[ -z $adapterType ]]
  then
    # Get the node's adapter information.
    $ifconfig -a > $adfile
    rc=$?
    if [[ $rc -ne 0 ]]
    then
      printErrorMsg 171 "checkNewClusterNode" "ifconfig -a" $rc
      print -- "checkNewClusterNode:unexpected_error:"
      $rm -f $initialSystemFiles
      return 1
    fi

    # Find the adapter type for the primary GPFS network.
    # The assumptions are that the ifconfig output is organized in stanzas.
    # Each stanza begins with the adapter type starting in column 1.
    # All other lines are indented by at least one space or tab character.
    if [[ $osName = Linux ]]
    then
      adapterType=$($awk '                                     \
        $0 !~ /^[ 	]/      { adapterType = $1 }           \
        / addr:'$ipa' /         { print adapterType ; exit }   \
      ' $adfile)
      rc=$?
    elif [[ $osName = AIX ]]
    then
      adapterType=$($awk '                                     \
        $0 !~ /^[ 	]/      { adapterType = $1 }           \
        /[ 	]inet '$ipa' /  { print adapterType ; exit }   \
      ' $adfile)
      rc=$?
      adapterType=${adapterType%:}
    else
      # Should never get here.
      printErrorMsg 171 "checkNewClusterNode" "unsupported OS $osName" 1
      print -- "checkNewClusterNode:unexpected_error:"
      $rm -f $initialSystemFiles
      return 1
    fi
    if [[ $rc -ne 0 ]]
    then
      printErrorMsg 171 "checkNewClusterNode" "awk" $rc
      print -- "checkNewClusterNode:unexpected_error:"
      $rm -f $initialSystemFiles
      return 1
    fi

    # Verify that the adapter exists.
    if [[ -z $adapterType ]]
    then
      # Print error type and return.
      printErrorMsg 154 $mmcmd $ipa $adminNodeName
      print -- "checkNewClusterNode:ipa_missing"
      $rm -f $initialSystemFiles
      return 1
    fi

    # If this is an IP alias, use the name of the main device.
    adapterType=${adapterType%%:*}

    # Add the new information to the local node data.
    v[$ADAPTER_TYPE_Field]=$adapterType
    localNodeData=$(print_newLine)
    print -- "$localNodeData" > $mmfsNodeData
    rc=$?
    if [[ $rc -ne 0 ]]
    then
      printErrorMsg 171 "checkNewClusterNode" "writing to file $mmfsNodeData" $rc
      print -- "checkNewClusterNode:unexpected_error:"
      $rm -f $initialSystemFiles
      return 1
    fi
  fi  # end of if [[ -z $adapterType ]]


  # Check that the admin interface name is valid
  # if it is different than the one we just checked.
  if [[ $adminNodeName != $daemonNodeName ]]
  then
    # Determine the IP address for the specified admin node name.
    hostResult=$($host $adminNodeName)
    set -f ; set -- $hostResult ; set +f
    adminIpa=${3%%,*}

    # Check that the admin node name has a valid IP address.
    if [[ -z $adminIpa ]]
    then
      # An invalid node name was specified.
      printErrorMsg 54 $mmcmd $adminNodeName
      $rm -f $initialSystemFiles
      return 1
    fi

    # Find the adapter type for the admin network.
    # The assumptions are that the ifconfig output is organized in stanzas.
    # Each stanza begins with the adapter type starting in column 1.
    # All other lines are indented by at least one space or tab character.
    if [[ $osName = Linux ]]
    then
      adapterType=$($awk '                                          \
        $0 !~ /^[ 	]/           { adapterType = $1 }           \
        / addr:'$adminIpa' /         { print adapterType ; exit }   \
      ' $adfile)
      rc=$?
    elif [[ $osName = AIX ]]
    then
      adapterType=$($awk '                                          \
        $0 !~ /^[ 	]/           { adapterType = $1 }           \
        /[ 	]inet '$adminIpa' /  { print adapterType ; exit }   \
      ' $adfile)
      rc=$?
      adapterType=${adapterType%:}
    else
      # Should never get here.
      printErrorMsg 171 "checkNewClusterNode" "unsupported OS $osName" 1
      print -- "checkNewClusterNode:unexpected_error:"
      $rm -f $initialSystemFiles
      return 1
    fi

    if [[ $rc -ne 0 ]]
    then
      printErrorMsg 171 "checkNewClusterNode" "awk" $rc
      print -- "checkNewClusterNode:unexpected_error:"
      $rm -f $initialSystemFiles
      return 1
    fi

    # Verify that the adapter exists.
    if [[ -z $adapterType ]]
    then
      # Print error type and return.
      printErrorMsg 154 $mmcmd $adminIpa $adminNodeName
      print -- "checkNewClusterNode:ipa_missing"
      $rm -f $initialSystemFiles
      return 1
    fi

    # If this is an IP alias, use the name of the main device.
    adapterType=${adapterType%%:*}

  fi  # end of if [[ $adminNodeName != $daemonNodeName ]]

  # Report success and return to caller.
  result="checkNewClusterNode:success:$adapterType:$currentDaemonVersion"
  result="$result:$productVersion:$osName:"
  print -- "$result"
  return 0

}  #------ end of function checkNewClusterNode --------------


############################################################################
#
# Function:  Verify that the specified adapter exists.
#
# Input:     IP address of the adapter to check
#
# Output:    The word "success" or a keyword representing the type
#            of failure: "unexpected_error", "ipa_missing", etc.
#
# Returns:   zero if success, non-zero otherwise
#
############################################################################
function checkAdapter  # <ipa>
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGcheckAdapter ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset ipa=$1

  typeset adapterType=""
  typeset rc=0

  # Get the node's adapter information.
  $ifconfig -a > $adfile
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    printErrorMsg 171 "checkAdapter" "ifconfig -a" $rc
    print -- "unexpected_error"
    return 1
  fi

  # Find the adapter type from the ifconfig output.
  # The assumptions are that the output is organized in stanzas.
  # Each stanza begins with the adapter type starting in column 1.
  # All other lines are indented by at least one space or tab character.
  if [[ $osName = Linux ]]
  then
    adapterType=$($awk '                                      \
      $0 !~ /^[ 	]/      { adapterType = $1 }          \
      / addr:'$ipa' /           { print adapterType ; exit }  \
    ' $adfile)
    rc=$?
  elif [[ $osName = AIX ]]
  then
    adapterType=$($awk '                                      \
      $0 !~ /^[ 	]/      { adapterType = $1 }          \
      /[ 	]inet '$ipa' /  { print adapterType ; exit }  \
    ' $adfile)
    rc=$?
    adapterType=${adapterType%:}
  else
    # Should never get here.
    printErrorMsg 171 "checkAdapter" "unsupported OS $osName" 1
    print -- "unexpected_error"
    return 1
  fi
  if [[ $rc -ne 0 ]]
  then
    printErrorMsg 171 "checkAdapter" "awk" $rc
    print -- "unexpected_error"
    return 1
  fi

  # Verify that the adapter exists.
  if [[ -z $adapterType ]]
  then
    # Print error type and return.
    print -- "ipa_missing"
    return 1
  fi

  # Report success and return to caller.
  print -- "success"
  return 0

}  #------ end of function checkAdapter ---------------------


################################################################
#
# Function:  Run an administration command.
#
# Input:     Command name and arguments
#
# Output:    Depends on the command
#
# Returns:   127  Command not supported
#            Return code from the command
#
################################################################
function runCommand  #  <adminCommand> [<arguments>]
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGrunCommand ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset adminCmd=${1##*/} # get the basename part of <adminCommand>
  typeset arguments="$2"

  typeset rc=0


  # Verify the command belongs to the set of allowed commands.
  case $adminCmd in

    "cat"           ) adminCmd=$cat                 ;;
    "head"          ) adminCmd=$head                ;;
    "lspv"          ) adminCmd=$lspv                ;;
    "lssrc"         ) adminCmd=$lssrc               ;;
    "lsvsd"         ) adminCmd=$lsvsd               ;;
    "mmfsadm"       ) adminCmd=$mmfsadm             ;;
    "mmtrace"       ) adminCmd=$mmtrace             ;;
    "mount"         ) adminCmd=$mount               ;;
    "removevsd"     ) adminCmd="$removevsd -f -v "  ;;
    "touch"         ) adminCmd=$touch               ;;
    "tsaddrmap"     ) adminCmd=$tsaddrmap           ;;
    "tsctl"         ) adminCmd=$tsctl               ;;
    "tspreparedisk" ) adminCmd=$tspreparedisk       ;;
    "umount"        ) adminCmd=$umount              ;;
    "unmount"       ) adminCmd=$unmount             ;;
     *              ) return 127                    ;; # invalid command
  esac

  # Run the command and propagate back the return code.
  $adminCmd $arguments
  rc=$?

  [[ $adminCmd = ts* || $adminCmd = mm* ]] &&  \
    rc=$(remapRC $?)

  return $rc

}  #------ end of function runCommand -------------------


##############################################################################
#
# Function:  Determine the state of the GPFS daemon and other node info.
#
# Input:     $1 - (optional) Extended output (-L option)
#
# Output:
#   mmGetState:nsId:nodeNum:name:tsQuorum:up:total:state:quorumDesignation:
#
# Returns:   Zero if successful, non-zero otherwise.
#
##############################################################################
function mmGetState  #  [-L]
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGmmGetState ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset extendedOutput=$1

  typeset tsctlOutput=""
  typeset rc tsQuorum nodesUp totalNodes daemonState quorumDesignation result
  typeset gpfsInitOutput


  if [[ -n $extendedOutput ]]
  then
    # Extended output is requested.
    # Make sure that the local copies of the mmsdrfs, mmfs.cfg,
    # and the rest of the system files are up to date.
    gpfsInitOutput=$(gpfsInit nolock)
    setGlobalVar $? $gpfsInitOutput

    # Determine the total nodes value and whether or not this is a quorum node.
    totalAndType=$($awk -F: '                                \
      BEGIN {                                                \
        { total = 0 }                                        \
        { type = "" }                                        \
      }                                                      \
      /':$NODESET_HDR:'/ {                                   \
        if ( $'$NODESETID_Field' == "'$nsId'" ) {            \
          { total = $'$NODE_COUNT_Field' }                   \
        }                                                    \
      }                                                      \
      /':$MEMBER_NODE:'/ {                                   \
        if ( $'$REL_HOSTNAME_Field' == "'$ourNodeName'" ) {  \
          { type = $'$CORE_QUORUM_Field' }                   \
          { exit }                                           \
        }                                                    \
      }                                                      \
      END { print total ":" type }                           \
    ' $mmsdrfsFile)
    checkForErrors awk $?

    IFS=':'
    set -f ; set -- $totalAndType ; set +f
    totalNodes=$1
    quorumDesignation=$2
    IFS="$IFS_sv"

  else
    # Regular output is requested.  Return fake values
    # for quorum designation and total nodes.
    totalNodes=0
    quorumDesignation=unknown
  fi  # end of if [[ -n $extendedOutput ]]


  # Determine the quorum state of the daemon.
  tsctlOutput=$(LC_ALL=C $tsctl quorumState 2>$errMsg)
  rc=$(remapRC $?)

  # Interpret the results from the tsctl command.
  if [[ -z $tsctlOutput ]]
  then
    # The command failed.  Filter out "Failed to connect" messages.
    # Show any other error messages that might be there.
    $grep -v -e "6027-665" -e "Failed to connect to file system daemon" $errMsg >$errMsg2
    [[ -s $errMsg2 ]] && $cat $errMsg2 1>&2

    # Provide default values to be returned to the caller.
    tsQuorum=0
    nodesUp=0

    if [[ $rc -eq $MM_DaemonDown ]]
    then
      daemonState="down"
    else
      daemonState="unknown"
    fi

  else
    # The tsctl command seemed to work.  Parse the output.
    # We intentionally override the totalNodes value found prior to
    # the tsctl call because the daemon value may include remote nodes
    # not reflected in the value we obtained from the sdrfs file.
    IFS=':'
    set -f ; set -- $tsctlOutput ; set +f
    tsQuorum=$1
    nodesUp=$2
    totalNodes=$3
    daemonState=$4
    IFS="$IFS_sv"

    # Remap the daemon status strings.
    case $daemonState in
      "Initial" )  daemonState="arbitrating" ;;
      "Active"  )  daemonState="active" ;;
      *         )  : ;;                      # Unknown string; leave it as is.
    esac
  fi  # end of if [[ -z $tsctlOutput ]]

  # Build and print the result string.
  result="mmGetState:$nsId:$ourNodeNumber:$ourShortName:$tsQuorum"
  result="${result}:$nodesUp:$totalNodes:$daemonState:$quorumDesignation:"
  print -- "$result"

  return 0

}  #------ end of function mmGetState -------------------


###################################################################
#
# Function:  Determine the state of the specified subsystem.
#
# Input:     $1 - subsystem name
#
# Output:    mmgetSubsysState:<nodeName>:<subsystemState>:
#
# Returns:   Always zero.
#
###################################################################
function getSubsysState  # <subsystem>
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGgetSubsysState ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset subsys=$1

  typeset subsysState=""

  # Determine whether the subsystem is active.
  if [[ $subsys = rvsd ]]
  then
    subsysState=$(LC_ALL=C $lssrc -ls  $subsys | $grep 'active=1, state=idle' 2>/dev/null)
    [[ -n $subsysState ]] && subsysState=active

  elif [[ $subsys = mmfs ]]
  then
    $ps -e | $grep -w mmfsd >/dev/null 2>&1
    rc=$?
    if [[ $rc -eq 0 ]]
    then
      print -- "active"
    else
      print -- "inactive"
    fi

  else
    subsysState=$(LC_ALL=C $lssrc -s $subsys |  \
      $awk '
        BEGIN { state = "inactive" }
        $1 == "'$subsys'"  { if ( $NF == "active" ) { state = "active" } }
        END { print state }
      ')
  fi  # end if [[ $subsys = rvsd ]]

  [[ -z $subsysState ]] && subsysState=down

  # Build and print the result string.
  result="mmgetSubsysState:$ourNodeName:$subsysState:"
  print -- "$result"

  return 0

}  #------ end of function getSubsysState -------------------


###########################################################################
#
# Function:  Verifies that the RVSD subsystem is active on all nodes
#            on which the GPFS daemon is running and then sets the
#            wait4RVSD parameter to yes.
#
# Input:     none
#
# Output:    none
#
# Returns:   0 - RVSD operational on all nodes
#            1 - Error detected, or RVSD inactive on one or more nodes
#
###########################################################################
function setWait4RVSD  #
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGsetWait4RVSD ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset subsysStateLine kword nodeName rvsdState failedNodes junk
  typeset errorFound=""
  typeset rc=0

  ##################################################################
  # Use tsdsh to find the state of RVSD on all active GPFS nodes.
  ##################################################################
  $tsdsh $mmremote getSubsysState rvsd >$tmpfile 2>&1
  rc=$(remapRC $?)

  if [[ ! -s $tmpfile ]]
  then
    # If there is no output at all, something is very wrong.
    [[ $rc -eq 0 ]] && rc=1
    checkForErrors "setWait4RVSD" $rc
  else
    # If we have output, ignore the rc from tsdsh.
    rc=0
  fi

  # Parse the output from the RVSD active test.
  $rm -f $errMsg
  exec 3<&-
  exec 3< $tmpfile
  while read -u3 subsysStateLine
  do
    IFS=':'
    set -f ; set -- $subsysStateLine ; set +f
    IFS="$IFS_sv"
    junk=$1
    kword=$2
    nodeName=$3
    rvsdState=$4

    if [[ $kword = ?( )mmgetSubsysState ]]
    then
      [[ $rvsdState != active ]] &&  \
        failedNodes="$failedNodes\n\t$nodeName"
    elif [[ -n $subsysStateLine ]]
    then
      # Unexpected output - must be an error.
      # Collect the lines in a separate file for later.
      print -- "$subsysStateLine" >> $errMsg
      checkForErrors "writing to file $errMsg" $?
    else
      :  # do nothing
    fi  # end of if [[ $kword = mmgetSubsysState ]]
  done  # end of while read -u3 subsysStateLine

  if [[ -n $failedNodes ]]
  then
    # RVSD is not yet ready on at least one node.
    printErrorMsg 474 $mmcmd RVSD "$failedNodes"
    rc=1
  fi

  if [[ -s $errMsg ]]
  then
    # Show the unexpected errors.
    $cat $errMsg  1>&2
    rc=1
  fi
  $rm -f $errMsg

  # If things look OK so far, change the value of the wait4RVSD parameter.
  if [[ $rc -eq 0 ]]
  then
    $tsctl setCfgValue wait4RVSD yes
    rc=$(remapRC $?)
    if [[ $rc -ne 0 ]]
    then
      # The tsctl command failed.
      printErrorMsg 104 "$mmcmd" "tsctl setCfgValue wait4RVSD yes"
    fi
  fi

  return $rc

}  #------ end of function setWait4RVSD ------------------


###########################################################################
#
# Function:  Determines the range of GPFS release levels on the
#            nodes on which the daemon is currently running.
#
# Input:     None
#
# Output:    A string with the following format:
#            getCodeRange:rc:status:maxDaemonVers:minDaemonVers
#
# Returns:   0 - Success
#            1 - Error detected
#
###########################################################################
function getCodeRange  # <nodefile> <sdrfsFile> <failedNodes>
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $getCodeRange ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset mmVersion2output nodeName nodeNumber kword cmdVersion
  typeset installedDaemonVersion installedProductVersion installedOsName
  typeset maxDaemonVersion=0
  typeset minDaemonVersion=100000
  typeset rc=0

  # Request the release level information for all of the nodes.
  $tsdsh $mmremote mmVersion2 >$tmpfile 2>&1
  rc=$(remapRC $?)

  if [[ ! -s $tmpfile ]]
  then
    # If there is no output at all, something is very wrong.
    [[ $rc -eq 0 ]] && rc=1
    print -- "getCodeRange:$rc:unexpected_failure:0:0:"
    printErrorMsg 171 $mmcmd "tsdsh mmremote mmVersion2" $rc
    return $rc
  else
    # If we have output, ignore the rc from tsdsh.
    rc=0
  fi

  # Parse the output from  mmremote mmVersion2.
  $rm -f $errMsg
  IFS=":"
  exec 3<&-
  exec 3< $tmpfile
  while read -u3 mmVersion2output
  do
    IFS=':'
    set -f ; set -- $mmVersion2output ; set +f
    nodeName=$1
    kword=$2
    cmdVersion=$3
    nodeNumber=$4
    installedDaemonVersion=$5
    installedProductVersion=$6
    installedOsName=$7
    IFS="$IFS_sv"

    if [[ $kword != *( )mmVersion2 ]]
    then
      # Unexpected output; it must be an error.
      # Collect the lines in a separate file for later.
      print -- "$mmVersion2output" >> $errMsg
      checkForErrors "writing to file $errMsg" $?
      continue
    fi

    # Keep track of highest and lowest daemon versions found.
    [[ $installedDaemonVersion -gt $maxDaemonVersion ]] &&  \
      maxDaemonVersion=$installedDaemonVersion
    [[ $installedDaemonVersion -lt $minDaemonVersion ]] &&  \
      minDaemonVersion=$installedDaemonVersion

    IFS=":"  # Change the separator back to ":" for the next iteration.
  done  # end of while read -u3 mmVersion2output
  IFS="$IFS_sv"

  # Print out the results string and exit.
  if [[ $maxDaemonVersion -eq 0 ]]
  then
    # We did not get back any release level information.
    # The output must have been only error messages.
    [[ $rc -eq 0 ]] && rc=1
    $cat $errMsg  1>&2
    print -- "getCodeRange:$rc:unexpected_failure:0:0:"
  else
    # Everything looks good.
    rc=0
    print -- "getCodeRange:$rc:complete:$maxDaemonVersion:$minDaemonVersion:"
  fi

  $rm -f $errMsg
  return $rc

}  #------ end of function getCodeRange ------------------


#####################################################################
#
# Function:  Ensure local system configuration files are up-to-date.
#
# Input:     $1   -f rebuild all files
#
# Output:    None
#
# Returns:   Zero if successful, non-zero otherwise.
#
#####################################################################
function refreshSysconfig  #  [-f]
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGrefreshSysconfig ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset option=$1

  typeset gpfsInitOutput
  typeset rc=0

  # If the -f option was specified, force mmfsNodeData
  # and the rest of the GPFS files to be rebuilt.
  if [[ $option = "-f" ]]
  then
    $rm -f $mmfscfgFile $mmfsNodeData $nsdpvol ${mmfsEnvLevel}+([0-9])  \
           ${mmfsNewKeyLevel}+([0-9]) ${mmfsCommittedKeyLevel}+([0-9])
    getLocalNodeData
  fi

  # Verify there is an mmfs entry in /etc/vfs.
  checkVfsNumber
  rc=$?
  [[ $rc -ne 0 ]] &&  \
    return $rc

  # Make sure that the local copies of the mmsdrfs, mmfs.cfg,
  # and the rest of the system files are up-to-date.
  gpfsInitOutput=$(gpfsInit nolock)
  rc=$?
  [[ $rc -ne 0 ]] && printErrorMsg 171 "$mmcmd" "gpfsInit" $rc
  setGlobalVar $rc $gpfsInitOutput

  return $rc

}  #------ end of function refreshSysconfig -------------------


###################################################################
#
# Function:  Tell the daemon to reread the authorized_keys list.
#
# Input:     None.
#
# Output:    None.
#
# Returns:   Zero if successful, non-zero otherwise.
#
###################################################################
function refreshAuth
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGrefreshAuth ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset gpfsInitOutput=""

  # Retrieve the latest mmsdrfs file and rebuild the environment.
  gpfsInitOutput=$(gpfsInit nolock)
  setGlobalVar $rc $gpfsInitOutput

  # Tell the daemon to reread the authorized_keys list.
  $tsauth

  return 0

}  #------ end of function refreshAuth -------------------


###################################################################
#
# Function:  Determine the generation number of a sdrfs file.
#
# Input:     $1 - (optional) sdrfs file.  If not specified,
#                 /var/mmfs/gen/mmsdrfs is assumed.
#
# Output:    <genNumber>:<timeStamp>
#
# Returns:   Zero if successful, non-zero otherwise.
#
###################################################################
function getGenNumber
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGgetGenNumber ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset sdrfs=$1
  typeset versionLine=""

  # If file not specified, use /var/mmfs/gen/mmsdrfs.
  [[ -z $sdrfs ]] && sdrfs=$mmsdrfsFile

  # Parse the version line of the mmsdrfs file.
  versionLine=$($head -1 $sdrfs)
  IFS=':'
  set -f ; set -A v -- - $versionLine ; set +f
  IFS="$IFS_sv"

  # Perform a quick sanity check.
  [[ ${v[$LINE_TYPE_Field]} != $VERSION_LINE ]] &&  \
    corruptedSdrFileExit 137 "$versionLine"

  # Make sure the time stamp field has a value.
  [[ -z ${v[$GENNUM_TSTAMP_Field]} ]] &&  \
    v[$GENNUM_TSTAMP_Field]=0

  # Generate and display the result.
  print "${v[$SDRFS_GENNUM_Field]}:${v[$GENNUM_TSTAMP_Field]}:${v[$PRIMARY_SERVER_Field]}"

  return 0

}  #------ end of function getGenNumber -------------------


###################################################################
#
# Function:  Move and rename the specified file.
#            Verify its checksum.
#
# Input:     $1 - source file name
#            $2 - target file name
#            $3 - expected checksum
#            $4 - source host name
#
# Output:    None
#
# Returns:   Zero if successful, non-zero otherwise.
#
###################################################################
function mvSGDescFile  #  <source> <target> <checksum> <sourceNode>
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGmvSGDescFile ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset source=$1
  typeset target=$2
  typeset checksum=$3
  typeset sourceNodeName=$4

  typeset sumOutput newSum
  typeset rc=0

  # Make sure the target directory exists.
  $mkdir -p ${target%/*}

  # Retrieve the file.
  if [[ $sourceNodeName = $ourNodeName ]]
  then
    $cp $source $target
    rc=$?
  else
    $rcp ${sourceNodeName}:$source $target
    rc=$?
  fi

  if [[ $rc -ne 0 ]]
  then
    print -- "mvSGDescFile:error:copyfile:"
    return 1
  fi

  $mmsync $target

  # Verify the checksum.
  sumOutput=$($sum $target)
  checkForErrors "sum $target" $?
  set -f ; set -- $sumOutput ; set +f
  newSum=$1
  if [[ $checksum -ne $newSum ]]
  then
    print -- "mvSGDescFile:error:checksum:"
    return 1
  fi

  print -- "mvSGDescFile:success::"
  return 0

}  #------ end of function mvSGDescFile -------------------


###################################################################
#
# Function:  Mount the specified file system(s).
#
# Input:     $1 - file system device name or all, all_local, or all_remote
#            $2 - mount point or DEFAULT
#            $3 - default mount options
#            $4 - new mount options or DEFAULT
#            $5 - do not remount indicator (optional)
#
# Output:    None
#
# Returns:    0 - mount command issued
#             non-zero - unexpected error
#
###################################################################
function mountFileSystems  #  <device> <mountPoint> <defaultOptions> <newOptions>
{
  typeset sourceFile="mmremote.sh"
  [[ -n $DEBUG || -n $DEBUGmountFileSystems ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset deviceName=$1
  typeset mountPoint=$2
  typeset defaultOptions=$3
  typeset newOptions=$4
  typeset doNotRemount=$5

  typeset nodeIndex=0
  typeset ourNodeIndex=0
  typeset fsNumber=0
  typeset n=0
  typeset rc=0

  typeset gpfsInitOutput fsType fsName tsstatusOutput mountedFileSystems
  typeset mountDeviceList mountDeviceList_A mountDeviceList_B vfsType
  typeset nodeOptions mountAllRequested


  # Ensure the GPFS system data is up to date.
  # Note:  To avoid undesired side effects, tell gpfsInit to restrict mount
  #        point checking only to the file system(s) that we care about.
  export MOUNT_POINT_CHECK="$deviceName"
  gpfsInitOutput=$(gpfsInit nolock)
  setGlobalVar $? $gpfsInitOutput
  unset MOUNT_POINT_CHECK

  # See if there are additional mount options to specify.
  if [[ $newOptions = DEFAULT ]]
  then
    newOptions=""
  else
    newOptions="-o $newOptions"
  fi

  if [[ $deviceName = all || $deviceName = all_local || $deviceName = all_remote ]]
  then
    mountPoint=""
    mountAllRequested=yes
  fi

  # Process the mount point parameter.  Its value will also determine
  # whether it will be necessary to explicitly specify on the mount command
  # the vfs type and options string or if it is OK to let the mount
  # command figure out those things on its own.
  [[ $mountPoint = DEFAULT ]] && mountPoint=""
  if [[ -n $mountPoint ]]
  then
    defaultOptions="-o $defaultOptions"
    if [[ $osName = AIX ]]
    then
      vfsType="-v mmfs"
    elif [[ $osName = Linux ]]
    then
      vfsType="-t gpfs"
    else
      # Should never get here.
      printErrorMsg 171 "mountFileSystems" "unsupported OS $osName" 1
      return 1
    fi  # end of if [[ $osName = AIX ]]
  else
    defaultOptions=""
  fi  # end of if [[ -n $mountPoint ]]

  # Go through the local mmsdrfs file and create
  # a list of the file systems to be mounted.
  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"

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

      $MEMBER_NODE )
         # Determine the index of this node relative
         # to the rest of the nodes in the cluster.
         [[ ${v[$NODE_NUMBER_Field]} = $ourNodeNumber ]] &&  \
           ourNodeIndex=$nodeIndex
         (( nodeIndex += 1 ))
         ;;

      $SG_HEADR )
         # See if we want to mount this file system.
         if [[ ${v[$DEV_NAME_Field]} = $deviceName || $deviceName = all     ||
               $deviceName = all_local  && ${v[$FS_TYPE_Field]} = $localfs  ||
               $deviceName = all_remote && ${v[$FS_TYPE_Field]} = $remotefs ]]
         then
           mountDeviceList="$mountDeviceList /dev/${v[$DEV_NAME_Field]}"
           (( fsNumber += 1 ))
         fi
         ;;

      * )  # Not interested in any other lines.
         ;;

    esac  # end of Change some of the fields

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

  done  # end while read -u3 mmsdrfsFile
  IFS="$IFS_sv"

  # Ensure the requested file systems were found.
  # This should always be the case at this point.
  if [[ $fsNumber -eq 0 ]]
  then
    if [[ $deviceName = all || $deviceName = all_local ]]
    then
      # No file systems found in the cluster.
      printErrorMsg 200 $mmcmd
    elif [[ $deviceName = all_remote ]]
    then
      # No remote file systems found in the cluster.
      printErrorMsg 193 $mmcmd
    else
      # The requested file system was not found.
      printErrorMsg 288 $mmcmd $deviceName
    fi

    # Give up; nothing more to do.
    return $MM_FsNotFound
  fi  # end of if [[ $fsNumber -eq 0 ]]

  # If more than one file system will be mounted, order the list
  # based on the index of the local node within the cluster.
  # This is done to avoid the simultaneous mounting of the same
  # file system from all of the nodes in the cluster.
  if [[ $fsNumber -gt 1 ]]
  then
    # Calculate our node's index in the file system list.
    (( ourNodeIndex = ourNodeIndex % fsNumber ))

    # Break the list of file systems into two parts
    # based on the above calculated index.
    for fsName in $mountDeviceList
    do
      if [[ $n -ge $ourNodeIndex ]]
      then
        mountDeviceList_A="$mountDeviceList_A $fsName"
      else
        mountDeviceList_B="$mountDeviceList_B $fsName"
      fi
      (( n += 1 ))
    done  # end of for fsName in $mountDeviceList

    # Combine the two parts of the mount list.
    mountDeviceList="$mountDeviceList_A $mountDeviceList_B"
  fi  # end of if [[ $fsNumber -gt 1 ]]

  # Create a list of the currently mounted file systems on this node.
  if [[ $osName = AIX ]]
  then
    $mount >$tmpfile 2>/dev/null
    mountedFileSystems=$tmpfile
  elif [[ $osName = Linux ]]
  then
    mountedFileSystems=/proc/mounts
  else
    # Should never get here.
    printErrorMsg 171 "mountFileSystems" "unsupported OS $osName" 1
    return 1
  fi  # end of if [[ $osName = AIX ]]

  # Ensure the local GPFS daemon is accepting commands.
  tsstatusOutput=$(LC_ALL=C $tsstatus -1 2>&1)
  print -- "$tsstatusOutput" | $grep -qe 'file system daemon is running' >/dev/null 2>&1
  if [[ $? -ne 0 ]]
  then
    # GPFS daemon is not ready for commands yet.
    printErrorMsg 110 $mmcmd
    return $MM_DaemonDown
  fi

  # Mount the file systems.
  for fsName in $mountDeviceList
  do
    # See if this file system is already mounted.
    $grep -qw $fsName $mountedFileSystems > /dev/null 2>&1
    if [[ $? -eq 0 ]]
    then
      # The file system appears to be already mounted.  Verify that
      # everythig is OK by doing an internal remount if the stat on
      # mount point returns with a non-zero errno.
      activeMountPoint=$(findMountpoint $fsName)
      $perl -e ' $! = 0; stat "'$activeMountPoint'"; exit $!;'
      rc=$?
      if [[ $rc -ne 0 && -z $doNotRemount ]]
      then
        $tsremount ${fsName#/dev/}
        rc=$?
        if [[ $rc -ne 0 ]]
        then
          # The remount attempt failed.
          printErrorMsg 399 $mmcmd $fsName $rc
          [[ -n $mountAllRequested ]] && rc=0
        fi
      fi  # end of if [[ $rc -ne 0 && -z $doNotRemount ]]

      # If a specific mount point has been requested, see if it matches
      # the currently active mount point for the file system.
      if [[ -n $mountPoint ]]
      then
        [[ $mountPoint != $activeMountPoint ]] &&  \
          printErrorMsg 280 $mmcmd $fsName $activeMountPoint
      fi

      # There is nothing more to do for this file system.
      continue
    fi  # end of if [[ $? -eq 0 ]]

    # See if there are local override mount options for this file system.
    if [[ -s ${localMountOptions}.${fsName#/dev/} ]]
    then
      nodeOptions="-o $($tail -n -1 ${localMountOptions}.${fsName#/dev/} 2>/dev/null)"
    else
      nodeOptions=""
    fi

    # Mount the file system.  If the overall request is to mount a single
    # file system, show all messages from the mount command.  If the request
    # is to mount all GPFS file systems, hide the "already mounted" messages.
    if [[ -n $mountAllRequested ]]
    then
      $mount $vfsType $defaultOptions $nodeOptions $newOptions $fsName $mountPoint 2>&1 |  \
        $grep -i -v "already mounted"
      rc=0  # ignore errors
    else
      $mount $vfsType $defaultOptions $nodeOptions $newOptions $fsName $mountPoint
      rc=$?
    fi
  done  # end of for fsName in $mountDeviceList

  return $rc

}  #------ end of function mountFileSystems -------------------


###########################################################
#
#  Mainline processing
#
###########################################################
kword=$arg1
kword_lc=$arg1
arg3_lc=$arg3


if [[ -z $kword ]]
then
  # Missing keyword
  printErrorMsg 133 mmremote NULL
  cleanupAndExit
fi

# Set up silent trap exception handling.
trap pretrap3 HUP INT QUIT KILL

# Parse the caller's version information.  If missing,
# assume the request comes from a 3.1 compatible node.
[[ -z $mmrpc ]] && mmrpc="mmrpc:1:1:901:"
IFS=':'
set -f ; set -- $mmrpc ; set +f
rpcVersion=$2
rpcMinVersion=$3
daemonVersion=$4
IFS="$IFS_sv"

# Determine the execution environment and set needed global variables.
if [[ $kword_lc = checknewclusternode ]]
then
  # The request is for a function that runs prior to the node becoming
  # a member of the GPFS cluster.  Use the function arguments to set
  # some global variables.
  if [[ -z $MMMODE ]]
  then
    IFS='/'
    set -f ; set -- $arg2 ; set +f
    export MMMODE=$1
    IFS="$IFS_sv"
  fi
  [[ -z $primaryServer ]] &&  \
    primaryServer=$arg3
elif [[ $arg3_lc = checknewclusternode* ||
        $arg3_lc = removefromclustercr  ]]
then
  # If the node is not yet a member of the GPFS cluster,
  # the functions to determine the local data do not work.
  [[ -z $ourNodeName ]] && ourNodeName=$($hostname)
  [[ -z $MMMODE ]] && export MMMODE=$arg4
else
  # In all other cases, file mmsdrfs should already exist
  # and we can use it as a starting point.
  [[ -z $MMMODE || -z $environmentType || -z $primaryServer ]] &&  \
    determineMode
  getLocalNodeData
fi

# Restore the original positional parameters.
set -f ; set -- $cmdline ; set +f

# Initialize the local version information string.
rpcMinSupported=1
rpcMaxSupported=1
localVersionInfo="$MM_Version3:$ourNodeNumber:$currentDaemonVersion:$productVersion"
localVersionInfo="${localVersionInfo}:$osName:$rpcMinSupported:$rpcMaxSupported"

# Ensure we know how to interpret the request.
if [[ $rpcMinVersion -gt $rpcMaxSupported ]]
then
  # The caller is running incompatible GPFS version.
  # Tell him what our capabilities are and let him decide what to do.
  print -u2 "mmremote:incompatibleGPFScode:$ourNodeName:$localVersionInfo:"
  cleanupAndExit $MM_IncompatibleCode
fi

# Make sure we have the proper credentials.
[[ $getCredCalled = no ]] && getCred

# Reset the remote commands if necessary.
[[ -n $GPFS_rshPath ]] && rsh=$GPFS_rshPath
[[ -n $GPFS_rcpPath ]] && rcp=$GPFS_rcpPath

# Depending on the keyword, perform the requested action.
case $kword_lc in

                   #----------------------------------------
  mmversion )      # mmremote mmVersion  [<fileset>]
                   #----------------------------------------
    # Determine whether the daemon is running.
    tsstatusOutput=$(LC_ALL=C $tsstatus -1 2>&1)
    print -- "$tsstatusOutput" | $grep -e 'file system daemon is running' >/dev/null
    if [[ $? -eq 0 ]]
    then
      daemonStatus=active
    else
      print -- "$tsstatusOutput" | $grep -e 'Waiting for quorum' >/dev/null
      if [[ $? -eq 0 ]]
      then
        daemonStatus=waitingForQuorum
      else
        daemonStatus=down
      fi
    fi

    # Get the level of the installed fileset.
    # If not specified by the caller, look for mmfs.gpfs.rte.
    if [[ -n $arg2 ]]
    then
      fileset=$arg2
    else
      fileset="mmfs.gpfs.rte"
    fi

    # The output of the lslpp -c -Lq command has the following format:
    # #Package Name:Fileset:Level:State:PTF Id:Fix State:Type:Description:
    lslppOutput=$(LC_ALL=C $lslpp -c -Lq $fileset 2>/dev/null)
    IFS=':'
    set -f ; set -- $lslppOutput ; set +f
    package=$1
    fileset=$2
    level=$3
    IFS="$IFS_sv"
    [[ $package != mmfs* ]] && level=""

    # Return the string 'mmVersion', the version of the commands (2), the level
    # of the currently-installed fileset, and the status of the daemon.
    print -- "mmVersion:$MM_Version3:$level:$daemonStatus"
    rc=0
    ;;

                   #---------------------
  mmversion2 )     # mmremote mmVersion2
                   #---------------------
    # Return the keyword 'mmVersion2', the version of the commands (2),
    # the level of the currently-installed fileset (must match prod.h),
    # the OS name, and the fileset or rpm release string identifier for
    # the current level of the code.
    print -- "mmVersion2:$localVersionInfo"
    rc=0
    ;;

                   #--------------------------------------------------
  unlock )         # mmremote unlock [<nodeNumber>] [<lockServer>]
                   #--------------------------------------------------
    # Find our node number.
    if [[ -z $arg2 ]]
    then
      nodeNumber=$ourNodeNumber
    else
      nodeNumber=$arg2
    fi

    # Find the lock server.
    if [[ -z $arg3 ]]
    then
      lockServer=$primaryServer
    else
      lockServer=$arg3
    fi

    # Release the lock.
    freeLock $nodeNumber $lockServer
    rc=0
    ;;

                    #------------------------------------------------------
  copyremotefile )  # mmremote copyRemoteFile <remoteNode> <remoteFile>
                    #                               <localFile> <checksum>
                    #------------------------------------------------------
    if [[ $argc -lt 5 ]]
    then
      operands=" <remoteNode> <remoteFile> <localFile> <checksum>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    copyRemoteFileOutput=$(copyRemoteFile "$arg2" "$arg3" "$arg4" "$arg5")
    rc=$?
    print -- "$copyRemoteFileOutput"
    ;;

                           #-------------------------------------------------
  committoprimaryserver )  # mmremote commitToPrimaryServer <newsdrfs> <sum>
                           #            <genNumber> <client> <primaryServer>
                           #            <commitOption> <backupServer>
                           #-------------------------------------------------
    if [[ $argc -lt 7 ]]
    then
      operands=" <newsdrfs> <checksum> <genNumber> <client> <primaryServer>"
      operands=$operands" <commitOption> <backupServer>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Check required system files.
    commitToPrimaryServer "$arg2" "$arg3" "$arg4" "$arg5" "$arg6" "$arg7" "$arg8"
    rc=$?
    ;;

                          #----------------------------------------
                          # mmremote vfs
  vfs | checkvfsnumber )  # mmremote checkVfsNumber
                          #----------------------------------------
    # Verify there is an mmfs entry in /etc/vfs.
    checkVfsNumber
    rc=$?
    ;;

                    #----------------------------------------
  fs )              # mmremote fs [optionalTasks]
                    #----------------------------------------
    # Verify there is an mmfs entry in /etc/vfs.
    checkVfsNumber
    rc=$?
    [[ $rc -ne 0 ]] && cleanupAndExit $rc

    # Make sure that the local copies of the mmsdrfs, mmfs.cfg,
    # and the rest of the system files are up-to-date.
    gpfsInitOutput=$(gpfsInit nolock)
    checkForErrors gpfsInit $?

    # Perform additional tasks as per the propagate options parameter.
    [[ -n $arg2 ]] && handlePropagateOptions $arg2

    rc=0
    ;;

                    #----------------------------------------
  cfg )             # mmremote cfg [-f]
                    #----------------------------------------
    # Ensure local config data is up-to-date.
    refreshSysconfig $arg2
    rc=$?
    ;;


                        #----------------------------------------------------
  upgradesystemfiles )  # mmremote upgradeSystemFiles <sdrfsFile> <checksum>
                        #                             [<optionalTasks>]
                        #----------------------------------------------------
    if [[ $argc -lt 3 ]]
    then
      operands="<sdrfsFile> <checksum> [<optionalTasks>]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    upgradeSystemFiles $arg2 $arg3 NULL 0
    rc=$?

    # Perform additional tasks as per the propagate options parameter.
    [[ -n $arg4 ]] && handlePropagateOptions $arg4
    ;;


                        #----------------------------------------------------
  upgradesystemfiles2 ) # mmremote upgradeSystemFiles2 <sdrfsFile> <checksum>
                        #                              <keyFile> <checksum2>
                        #----------------------------------------------------
    if [[ $argc -lt 5 ]]
    then
      operands="<sdrfsFile> <checksum> <keyFile> <checksum2>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    upgradeSystemFiles $arg2 $arg3 $arg4 $arg5
    rc=$?
    ;;


                      #---------------------------------------------
  updategpfsobject )  # mmremote updateGpfsObject <chng1> [<chng2>]
                      #---------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<change1> [<change2>] - Specify at least one change"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    updateGpfsObject "$arg2" "$arg3"
    rc=$?
    ;;

                           #----------------------------------------
  getgpfsobjectfromfile )  # mmremote getGpfsObjectFromFile
                           #----------------------------------------

    # Retrieve the information.
    gpfsObjectInfo=$(getGpfsObjectFromFile)
    rc=$?

    print -- "$gpfsObjectInfo"
    ;;

                         #------------------------------------------------
  sendclustersdrfiles )  # mmremote sendClusterSDRFiles <clientGenNumber>
                         #    <client> <fileName> <lockId>
                         #------------------------------------------------
    if [[ $argc -lt 5 ]]
    then
      operands="<generationNumber> <client> <fileName> <lockId>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    sendClusterSDRFiles "$arg2" "$arg3" "$arg4" "$arg5"
    rc=$?
    ;;

                  #-------------------------------------------------------------
  createshadow )  # mmremote createShadow <oldGenNumber> <newSum> <commitOption>
                  #-------------------------------------------------------------
    if [[ $argc -lt 4 ]]
    then
      operands="<oldGenNumber> <newSum> <commitOption>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    createShadow "$arg2" "$arg3" "$arg4"
    rc=$?
    ;;

                       #--------------------------------------------------
  removeuncommitted )  # mmremote removeUncommitted <genNumber> <version>
                       #--------------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<genNumber> <version>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    $rm -f $uncommitted.$arg3  ${mmsdrfsPrev}.$arg2
    rc=$?
    ;;

                                              #----------------------------
  removefromcluster | removefromclustercr )   # mmremote removeFromCluster
                                              #----------------------------
    # Remove (almost) all traces.
    removeFromCluster
    rc=0
    ;;

                   #-------------------------------------------
  rmfs )           # mmremote rmfs <fqDeviceName> <mountPoint>
                   #-------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<fqDeviceName> <mountPoint>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Remove the file system stanza from /etc/filesystems.
    # Remove the device and mount point.  Ignore errors.
    removeStanza  $arg2 >/dev/null 2>&1
    removeMountPoint $arg2 $arg3 0 >/dev/null 2>&1

    rc=0
    ;;

                   #----------------------------------------
  pid )            # mmremote pid <pid>
                   #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<pid>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # See if the specified process still exists.
    $ps "$arg2" >/dev/null 2>/dev/null
    rc=$?
    if [[ $rc -eq 0 ]]
    then
      print -- "alive"
    else
      print -- "died"
    fi

    rc=0
    ;;

                   #----------------------------------------
  active )         # mmremote active
                   #----------------------------------------
    $ps -e | $grep mmfsd >/dev/null 2>&1
    rc=$?
    if [[ $rc -eq 0 ]]
    then
      print -- "active"
    else
      print -- "down"
    fi

    rc=0
    ;;

                   #----------------------------------------
  quorum )         # mmremote quorum <quorumValue>
                   #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<quorumValue>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Set the node quorum to the specified value.
    $mmfsadm quorum $arg2
    rc=$?
    ;;

                   #-----------------------------------------
  tschpool )       # mmremote tschpool <tschpool_arguments>
                   #-----------------------------------------
    if [[ $argc -lt 3 ]]
    then
      operands="<tschpool_arguments>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Collect the input parameters and invoke the command.
    shift 1
    commandArguments=$*

    $tschpool $commandArguments
    rc=$(remapRC $?)
    ;;

                   #----------------------------------------
  tsctl )          # mmremote tsctl <tsctl_arguments>
                   #----------------------------------------
    if [[ $argc -lt 3 ]]
    then
      operands="<tsctl_arguments>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Collect the input parameters and invoke the command.
    shift 1
    commandArguments=$*

    $tsctl $commandArguments
    rc=$(remapRC $?)
    ;;

                   #----------------------------------------
  tsnap )          # mmremote tsnap <action>
                   #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<action>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Take a trace snapshot.
    traceSnapshot $arg2
    rc=$?
    ;;

                         #------------------------------------------------------
  checknewclusternode )  # mmremote checkNewClusterNode <clusterType> <primary>
                         #          <secondary | _NOSECONDARY_> <localNodeData>
                         #          <rshPath | _DEFAULT_> <rcpPath | _DEFAULT_>
                         #          <clusterId>
                         #------------------------------------------------------
    if [[ $argc -lt 8 ]]
    then
      operands="<clusterType> <primaryServer> <secondaryServer | _NOSECONDARY_> "
      operands=${operands}"<localNodeData> <rshPath | _DEFAULT_> <rcpPath | _DEFAULT_> "
      operands=${operands}"<clusterId> "
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine whether the node is new and supports clusters.
    checkNewClusterNode "$arg2" "$arg3" "$arg4" "$arg5" "$arg6" "$arg7" "$arg8"
    rc=$?
    ;;

                   #-----------------------------
  checkadapter )   # mmremote checkAdapter <ipa>
                   #-----------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<ipa>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Verify the adapter exists.
    checkAdapter $arg2
    rc=0
    ;;

                   #-----------------------------------------------
  admincmd )       # mmremote adminCmd <adminCommand> [<arg ... >]
                   #-----------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<adminCommand> [<arg ... >]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Get the command name and collect its arguments.
    adminCommand=$2
    shift 2
    argList=$@

    # Run the requested command.
    runCommand $adminCommand "$argList"
    rc=$?
    ;;

                   #---------------------------------------
  exectsmcommand ) # mmremote execTSMcommand [<arg ... >]
                   #---------------------------------------
    if [[ $argc -lt 10 ]]
    then
      operands="<execTSMcommand> [<arg ... >]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Shift the command's arguments to remove the execTSMcommand verb.
    shift 1
    argList=$@

    # Run the requested command.
    $mmexectsmcmd $argList
    rc=$?
    ;;

                   #----------------------------------------------------
  startsubsys )    # mmremote startSubsys [-e <expTime>] [-E <envData>]
                   #            [-f] [-t <tracefile>] [-T <traceOpt>]
                   #----------------------------------------------------
    # Parse the optional parameters.
    shift 1
    while getopts :e:E:fT:t: OPT
    do
      case $OPT in
        e) expirationData="$OPTARG"  ;;
        E) envString=${envString}" $OPTARG " ;;
        f) argString='-a "-f"' ;;
        t) envString=${envString}" mmScriptTrace=$OPTARG " ;;
        T) envString=${envString}" MMTRACE=$OPTARG " ;;
        *) printErrorMsg 13 "$mmcmd" $OPTARG ;;
      esac
    done

    # Process the expirationData parameter.
    if [[ -n $expirationData && ! -s mmSdrLockExp ]]
    then
      # Create a file with the sdr expiration data.
      print -- "$expirationData" > $mmSdrLockExp

      # Start the background process that will remove the file.
      $mmcommon expirationDataCleanup $expirationData doNotUnlock >/dev/null 2>&1 &
    fi

    # Check whether GPFS is running.
    pid=$($ps -eo "pid args" |  \
          $awk '/\/runmmfs/ && !/this process/ {print $1}')
    if [[ -n $pid ]]
    then
      # GPFS has already been started.
      printInfoMsg 586 GPFS
    else
      # Start GPFS.
      if [[ -n ${envString} ]]
      then
        $daemonize -e "$envString" -c $runmmfs $argString
      else
        $daemonize -c $runmmfs $argString
      fi
    fi  # end if [[ -n $pid ]]
    ;;

                      #------------------------------------------
  startautomounter )  # mmremote startAutomounter <automountDir>
                      #------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<automountDir>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Run the OS automount command.
    startAutomounter $arg2
    rc=$?
    ;;

                      #-----------------------------------------------
  getlocaldiskname )  # mmremote getLocalDiskName <pvid> <nsdSubtype>
                      #-----------------------------------------------
    if [[ $argc -lt 3 ]]
    then
      operands="<pvid> <nsdSubtype>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the physical disk with the specified pvid and nsdSubtype.
    getLocalDiskName $arg2 $arg3

    rc=0
    ;;

                        #--------------------------------------
  getlocaldisknamevg )  # mmremote getLocalDiskNameVG <vgname>
                        #--------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<vgname>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the physical disk with the specified volume group name.
    getLocalDiskNameVG $arg2
    rc=0
    ;;

                      #----------------------------------------
  findpvid )          # mmremote findPvid <lvname>
                      #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<lvname>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the pvid of the disk with the specified lvname.
    findPvid $arg2
    rc=$?
    ;;

                      #----------------------------------------
  findvgpvid )        # mmremote findVgPvid <vgname>
                      #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<vgname>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the pvid of the disk with the specified vgname.
    findVgPvid $arg2
    rc=$?
    ;;

                      #----------------------------------------
  findhdiskpvid )     # mmremote findHdiskPvid <hdisk_name>
                      #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<hdisk name>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the pvid of the specified physical disk.
    findHdiskPvid $arg2
    rc=$?
    ;;

                    #----------------------------------------
  mmgetstate )      # mmremote mmGetState
                    #----------------------------------------
    # Determine the quorum state.
    mmGetState $arg2
    rc=$?
    ;;

                    #----------------------------------------
  getsubsysstate )  # mmremote getSubsysState <subsystem>
                    #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<subsystem>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the state of the specified subsystem.
    getSubsysState $arg2
    rc=$?
    ;;

                    #----------------------------------------
  killsdrserv )     # mmremote killSdrServ
                    #----------------------------------------
    # Kill the currently-running mmsdrserv daemon.
    killSdrServ
    rc=$?
    ;;

                    #----------------------------------------
  startsdrserv )    # mmremote startSdrServ <sdrservPort>
                    #----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<sdrservPort>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # If this is a server node, start the mmsdrserv daemon.
    if [[ $ourNodeName = $primaryServer || $ourNodeName = $backupServer ]]
    then
      startSdrServ $arg2
      rc=$?
    else
      rc=132  # ESDR_NOT_SERVER  "Node is not a server"
    fi
    ;;

                    #----------------------------------------
  setwait4rvsd )    # mmremote setWait4RVSD
                    #----------------------------------------
    # Set the wait4RVSD parm across the currently-active GPFS nodes.
    setWait4RVSD
    rc=$?
    ;;

                    #----------------------------------------
  refreshauth )     # mmremote refreshAuth
                    #----------------------------------------
    # Refresh the auth key files.
    refreshAuth
    rc=$?
    ;;

                    #----------------------------------------
  getcoderange )    # mmremote getCodeRange
                    #----------------------------------------
    # Determines the range of GPFS release levels on the
    # nodes on which the daemon is currently running.
    getCodeRange
    rc=$?
    ;;

                   #----------------------------------------
  postmount )      # mmremote postMount
                   #----------------------------------------
    # Perform post-mount processing.  Currently, this is a no-op.
    rc=0
    ;;

                   #----------------------------------------
  getgennumber )   # mmremote getGenNumber  [<filename>]
                   #----------------------------------------
    # Retrieve the generation number of the specified sdrfs file.
    getGenNumber $arg2
    rc=$?
    ;;

                   #----------------------------------------
  rmdeventry )     # mmremote rmDevEntry <deviceNameList>
                   #----------------------------------------
    # Get the list of devices to delete.
    shift 1
    deviceNameList=$@

    # Loop through the list and delete the /dev entries.
    for devName in $deviceNameList
    do
      removeMountPoint $devName _NO_MOUNT_POINT_DELETE_ 0
    done
    ;;

                         #------------------------------------------------------
  determinedisksubtype ) # mmremote determineDiskSubtype <lvname> [<reevaluate>]
                         #------------------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<lvname> [<reevaluate>]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the disk type.
    determineDiskSubtype $arg2 $arg3
    rc=$?
    ;;

                         #-----------------------------------------
  determinensdsubtype )  # mmremote determineNsdSubtype <diskName>
                         #-----------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<diskName>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the disk type.
    determineNsdSubtype $arg2
    rc=$?
    ;;

                         #------------------------------------
  getlocalnsddata )      # mmremote getLocalNsdData [<Xflag>]
                         #------------------------------------
    # Send the colon-delimited local nsd data from tspreparedisk -s
    # back to the requestor via stdout.
    getLocalNsdData $arg2
    rc=0
    ;;

                         #---------------------------------------
  creatensd )            # mmremote createNsd <diskName> <vflag>
                         #        <invokingCommand> <nsdSubtype>
                         #---------------------------------------
    if [[ $argc -lt 5 ]]
    then
      operands="<diskName> <vflag> <invokingCommand> <nsdSubtype>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Make an NSD out of the disk.
    createNsd $arg2 $arg3 $arg4 $arg5
    rc=$?
    ;;

                  #----------------------------------------------------
  mvsgdescfile )  # mmremote mvSGDescFile <sourceFile> <targetFile>
                  #                       <checksum> <sourceNode>
                  #----------------------------------------------------
    if [[ $argc -lt 5 ]]
    then
      operands="<sourceFile> <targetFile> <checksum> <sourceNode>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Move the specified file and verify its checksum.
    mvSGDescFile $arg2 $arg3 $arg4 $arg5
    rc=$?
    ;;

                  #----------------------------------------------------
  lssgdescfile )  # mmremote lsSGDescFile
                  #----------------------------------------------------
    # Return a list of all disk descriptor recovery files.
    [[ -d ${tmpDir}mmimportfs ]] &&  \
      $ls -1 ${tmpDir}mmimportfs/tspreparedisk.diskDesc.* 2>/dev/null
    rc=0
    ;;

                  #----------------------------------------------------
  rmsgdescfile )  # mmremote rmSGDescFile <diskName>
                  #----------------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<diskName>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Remove all disk descriptor recovery files for the specified disk.
    $rm -f ${tmpDir}mmimportfs/tspreparedisk.diskDesc.${arg2}.*
    rc=0
    ;;

                      #-------------------------------------------
  getgpfsdiskname )   # mmremote getGpfsDiskName <rawDiskName>
                      #-------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<rawDiskName>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Determine the GPFS disk name of the specified physical disk.
    gpfsDiskName=$(getGpfsDiskName $arg2)
    rc=$?
    print -- "$gpfsDiskName"
    ;;

                      #-------------------------------------------
  unfencedisks )      # mmremote unfenceDisks <diskFile>
                      #-------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<diskNameFile> | <diskNameList>"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Unfence the specified disks and clear the PR registrations.
    unfenceDisksOutput=$(unfenceDisks $arg2)
    rc=$?
    print -- "$unfenceDisksOutput"

    # If this is invoked by mmimportfs, cleanup the input file.
    # Otherwise, leave the file alone.  The decision is based on
    # the input file's name.
    [[ $arg2 = ${tmpDir}disksToUnfence.mmimportfs.*tmp ]] &&  \
      $rm -f $arg2
    ;;

                        #--------------------------------------------------
  mountfilesystems )    # mmremote mountFileSystems <device> <mountPoint>
                        #     <defaultOptions> <newOptions> [doNotRemount]
                        #--------------------------------------------------
    if [[ $argc -lt 5 ]]
    then
      operands="<device> {<mountPoint>|DEFAULT} <defaultOptions>"
      operands="${operands} {<newOptions>|DEFAULT} [doNotRemount]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Mount the specified file systems.
    mountFileSystems $arg2 "$arg3" "$arg4" "$arg5" $arg6
    rc=$?
    ;;

                        #---------------------------------------------
  unmountfilesystems )  # mmremote unmountFileSystems <device> [-f]
                        #---------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<device> [-f]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Invoke the preunmount user exit.
    [[ -x ${mmfscfgDir}preunmount ]] &&  \
      $mmcommon preunmount $arg2 umount

    # Unmount the specified file systems.
    unmountFileSystems $arg2 "$arg3"
    rc=$?
    ;;

                        #----------------------------------------------
  unmountmountpoint )   # mmremote unmountMountpoint <mountPoint> [-f]
                        #----------------------------------------------
    if [[ $argc -lt 2 ]]
    then
      operands="<mountPoint> [-f]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Unmount the specified file systems.
    unmountMountpoint $arg2 "$arg3"
    rc=$?
    ;;

             #-------------------------------------------------------------
  lsmount )  # mmremote lsmount <fsToShow> <scope> <showNodes> [norefresh]
             #-------------------------------------------------------------
    if [[ $argc -lt 4 ]]
    then
      operands="<fsToShow> <scope> <showNodes> [norefresh]"
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Find out who has the file systems mounted.
    lsmount "$arg2" "$arg3" $arg4 $arg5
    rc=$?
    ;;

                       #---------------------------------
  shutdowndaemon )     # mmremote shutdownDaemon
                       #---------------------------------
    # Remove the rerunmmfsFile file.
    # If not done, runmmfs will restart mmfsd.
    $rm -f $rerunmmfsFile

    # Shut down the GPFS daemon.
    $mmfsadm shutdown 2>&1 | $grep -v -e "mmfs daemon not running"
    $sleep 3

    # Kill the runmmfs and mmfsenv processes if they are still running.
    # This would be the case if we were in a backoff respawn
    # or the daemon was never started in the first place.
    pidNumbers=$($ps -eo "pid args" |  \
        $awk '( /\/runmmfs/ || /\/mmfsenv/ ) && !/this process/ {print $1}')
    [[ -n $pidNumbers ]] && $kill -s KILL $pidNumbers
    $rm -f "${respawnlog}".*

    # If this is a server node, start the mmsdrserv daemon.
    [[ $ourNodeName = $primaryServer || $ourNodeName = $backupServer ]] &&  \
      startSdrServ CURRENT >> $mmsdrservLog 2>&1

    # Unload the kernel extensions (in Linux only).
    [[ $osName = Linux ]] && $mmfsenv -u

    # Always return OK.
    rc=0
    ;;

                      #---------------------------------
  cleanupdaemon )     # mmremote cleanupDaemon
                      #---------------------------------
    # If this file is present, GPFS will be respawned.
    $rm -f $rerunmmfsFile >/dev/null 2>&1

    # Shut down the daemon.
    $mmfsadm cleanup 2>&1 | $grep -v -e "mmfs daemon not running"
    $sleep 3

    # If this is a server node, start the mmsdrserv daemon.
    [[ $ourNodeName = $primaryServer || $ourNodeName = $backupServer ]] &&  \
      startSdrServ CURRENT >> $mmsdrservLog 2>&1

    # Unload the kernel extensions.
    [[ $osName = Linux ]] && $mmfsenv -u

    # Always return OK.
    rc=0
    ;;


             #------------------------------------------------------------------
  onbehalf)  # mmremote onbehalf <reqNode> <rcFilename> <mmmode> <fileToCopy>
             #        <fsToCheck> <scope> <link> <remoteCommand> [<arg ... >]
             #------------------------------------------------------------------
    if [[ $argc -lt 9 ]]
    then
      operands="<requestingNode> <rcFilename> <mmmode> <fileToCopy>"
      operands="$operands <fsToCheck> <scope> <link> <remoteCommand> [<arg ... >] "
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Shift past all positional parameters and collect the command arguments.
    shift 9
    arguments="$@"

    # Setup the environment.
    export MMMODE=$arg4

    # If there is a file to copy, get it.
    fileCopied=no
    if [[ $arg5 != $NO_FILE_COPY ]]
    then
      rcpResult=$(LC_ALL=C $rcp ${arg2}:${arg5} $arg5 2>&1)
      rc=$?
      if [[ $rc -eq 0 ]]
      then
        fileCopied=yes
      else
        # If there was no need to copy the file, that's fine.
        print -- "$rcpResult" | $grep -e "refer to the same file" >/dev/null
        if [[ $? -ne 0 ]]
        then
          # Show the error from the rcp command and issue a message
          # that the rcp command failed.
          print -u2 "$rcpResult"
          printErrorMsg 171 $mmcmd "$rcp ${arg2}:${arg5} $arg5" $rc
        else
          fileCopied=yes
          rc=0
        fi
      fi  # end of if [[ $rc -eq 0 ]]
    fi  # end of if [[ $arg5 != $NO_FILE_COPY ]]

    # If the copy worked, execute the command and return the return code.
    # The return code is reported by creating a temporary file in a scratch
    # directory on the caller's machine.  This temporary "return code" file
    # has the return code as the trailing part of its name.  It is the
    # caller's responsibility to look for this file to get the return code.
    # This mechanism is used because rsh does not return return codes.
    if [[ $rc -eq 0 ]]
    then
      runLocalCommand $arg6 $arg7 $arg8 $arg9 $arguments
      rc=$?
      rshResult=$($rsh $arg2 $mmremote adminCmd touch ${tmpDir}${arg3}.${rc} 2>&1)
      ec=$?
      if [[ $ec -ne 0 ]]
      then
        # Show the error from the rsh command and issue a message
        # that the rsh command failed.
        print -u2 $rshResult
        printErrorMsg 171 $mmcmd  \
          "$rsh ${arg2} mmremote adminCmd touch ${tmpDir}${arg3}.${rc}" $ec
      fi

      # Remove the input file if it was not created on this node.
      [[ $fileCopied = yes && $arg2 != $ourNodeName ]] && $rm -f $arg5
    fi  # end of if [[ $rc -eq 0 ]]
    ;;

              #-----------------------------------------------------------
  onbehalf2)  # mmremote onbehalf2 <reqNode> <rcFilename> <mmmode> <link>
              #                    <remoteCommand> [<arg ... >]
              #-----------------------------------------------------------
    if [[ $argc -lt 6 ]]
    then
      operands="<requestingNode> <rcFilename> <mmmode> <link>"
      operands="$operands <remoteCommand> [<arg ... >] "
      printErrorMsg 260 mmremote $kword "$operands"
      cleanupAndExit
    fi

    # Shift past all positional parameters and collect the command arguments.
    shift 6
    arguments="$@"

    # Setup the environment.
    export MMMODE=$arg4

    # Execute the command and return the return code.
    # The return code is reported by creating a temporary file in a scratch
    # directory on the caller's machine.  This temporary "return code" file
    # has the return code as the trailing part of its name.  It is the
    # caller's responsibility to look for this file to get the return code.
    # This mechanism is used because rsh does not return return codes.
    runLocalCommand2 $arg5 $arg6 $arguments
    rc=$?
    rshResult=$($rsh $arg2 $mmremote adminCmd touch ${tmpDir}${arg3}.${rc} 2>&1)
    ec=$?
    if [[ $ec -ne 0 ]]
    then
      # Show the error from the rsh command and issue a message
      # that the rsh command failed.
      print -u2 -- $rshResult
      printErrorMsg 171 $mmcmd  \
        "$rsh ${arg2} mmremote adminCmd touch ${tmpDir}${arg3}.${rc}" $ec
    fi
    ;;

                   #----------------------------------------
  * )              # Unknown action requested
                   #----------------------------------------
    # Invalid keyword. The caller is running incompatible GPFS version.
    # Tell him what our capabilities are and let him decide what to do.
    printErrorMsg 133 mmremote $kword
    print -u2 "mmremote:incompatibleGPFScode:$ourNodeName:$localVersionInfo:"
    cleanupAndExit $MM_IncompatibleCode
    ;;

esac  # end first case $kword_lc in

cleanupAndExit $rc doNotUnlock

