#!/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 
# @(#)80 1.38.1.4 src/avs/fs/mmfs/ts/admin/mmauth.sh, mmfs, avs_rgpfs24, rgpfs24s005a 6/14/06 14:39:42
##############################################################################
#
# Usage:
#
#   mmauth genkey  {new | commit}
#
#   mmauth add     remoteClusterName -k keyfile [-l cipherList]
#
#   mmauth update  remoteClusterName [-C newClusterName]
#                  [-k keyfile] [-l cipherList]
#
#   mmauth delete {remoteClusterName | all}
#
#   mmauth show   [remoteClusterName | all]
#
#   mmauth grant  {remoteClusterName | all} -f {device | all}
#                 [-a {rw | ro | none}] [-r {uid:gid | no}]
#
#   mmauth deny   {remoteClusterName | all} -f {device | all}
#
# where
#
#   remoteClusterName   is the fully-qualified name for a remote cluster that
#                 will be allowed to mount file systems owned by this cluster.
#
#   -C newClusterName  is the new fully-qualified cluster name for an
#                 already-defined cluster remoteClusterName.
#
#   -k keyfile    is the authentication key file generated by the mmauth
#                 command on the remote cluster.
#
#   -l cipherList   is the cipher list to be used when establishing a connection
#                 to the remote cluster.
#
#   -f device     is the name of the file system for which access is being
#                 granted or denied.
#
#   -a {rw | ro | none}   is the type of access being granted.  Default is "rw".
#                 Specifying "none" is the same as running mmauth deny.
#
#   -r {uid:gid | no}  is a root credentials remapping (root-squash) option.
#                 The UID and GID of all processes with root credentials
#                 from the remote cluster, will be remapped to the specified
#                 values. The default is not to remap the root UID and GID.
#                 Note:  The UID and GID must be specified as an unsigned
#                        integers or as symbolic names that can be resolved
#                        by the OS to valid UID and GID.  Specifying 'no'
#                        (or "off" or DEFAULT) turns off the remapping.
#
##############################################################################

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

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

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

LOCAL_FILES=" $verifiedKey $tmpkey $fsFile "


# Local variables
usageMsg=249
contactNodesCount=0


# Local functions

##########################################################################
#
# Function:  Determine the SHA digest (fingerprint) that corresponds
#            to the public key file for the specified cluster.
#
# Input:     $1 - cluster name
#            $2 - sdrfs file to use
#
# Output:    The SHA digest string.
#
# Returns:   0 - no errors encountered
#            1 - unexpected error
#
##########################################################################
function getFingerprint  # <clusterName> <sdrfsFile>
{
  typeset sourceFile="mmauth.sh"
  [[ -n $DEBUG || -n $DEBUGgetFingerprint ]] && set -x
  typeset clusterName=$1
  typeset sdrfs=$2

  typeset sdrfsLine clusterNameField lineTypeField keyLine keyFingerprint

  [[ ! -f $sdrfs ]] && return 1
  $rm -f $tmpPublicKey

  # Go through the current mmsdrfs file and retrieve the requested information.
  IFS=":"
  exec 4<&-
  exec 4< $sdrfs
  while read -u4 sdrfsLine
  do
    # Parse the line.
    set -f ; set -- $sdrfsLine ; set +f
    clusterNameField=$1
    lineTypeField=$2

    # We are interested only in the AUTHORIZED_KEY lines for the specified cluster.
    if [[ $clusterNameField = $clusterName && $lineTypeField = $AUTHORIZED_KEY ]]
    then
      # The public key information is everything past the first 4 fields.
      shift 4
      keyLine=$*
      keyLine="${keyLine%%+(:)}"

      # Examine the lines.  If the key was generated by the cs4b version
      # of mmauth genkey, there will be stanza lines and the fingerprint
      # will be included.  If the fingerprint is missing, or if this is
      # a key generated by a cs3b version of mmauth genkey, extract the
      # public key in a temp file.
      if [[ $keyLine = "clusterName="*            ||
            $keyLine = "clusterID="*              ||
            $keyLine = "genkeyFormat="*           ||
            $keyLine = "genkeyCompatibleFormat="* ||
            $keyLine = "keyGenNumber="*           ||
            $keyLine = "publicKey="               ||
            $keyLine = "certificate="             ]]
      then
        :  # Skip the line.

      elif [[ $keyLine = "keyDigest="* ]]
      then
        # Return the key fingerprint.
        keyFingerprint="${keyLine#keyDigest=}"
        break

      else
        # Add the line to the key file.
        IFS=""     # Reset IFS to preserve blanks and tabs.
        print -- "$keyLine" >> $tmpPublicKey
        checkForErrors "writing to file $tmpPublicKey" $?
      fi  # end of if [[ $keyLine = "clusterName="* ]]
    fi  # end of if [[ $clusterNameField = $clusterName && ...

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

  done  # end while read -u4 sdrfsLine

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

  # If the fingerprint was not imbedded in the key stanza,
  # determine it with the help of the openssl command.
  if [[ -z $keyFingerprint && -s $tmpPublicKey ]]
  then
    keyFingerprint=$($openssl dgst -sha -hex < $tmpPublicKey)
    checkForErrors "openssl dgst -sha -hex $tmpPublicKey" $?
  fi
  [[ -z $keyFingerprint ]] && keyFingerprint="(undefined)"

  # Return the result.
  print -- "$keyFingerprint"
  return 0

}  #------------ end function getFingerprint -----------------


##########################################################################
#
# Function:  Determine the SHA digest (fingerprint) that corresponds
#            to the specified public key file for the local cluster.
#
# Input:     $1 - key file to use
#
# Output:    The SHA digest string.
#
# Returns:   0 - no errors encountered
#            1 - unexpected error
#
##########################################################################
function getLocalFingerprint  # <keyFile>
{
  typeset sourceFile="mmauth.sh"
  [[ -n $DEBUG || -n $DEBUGgetLocalFingerprint ]] && set -x
  typeset keyFile=$1

  typeset keyLine keyFingerprint

  [[ ! -f $keyFile ]] && return 1
  $rm -f $tmpPublicKey

  # Go through the specified file and retrieve the requested information.
  exec 4<&-
  exec 4< $keyFile
  while read -u4 keyLine
  do
    # Examine the lines.  If the key was generated by the cs4b version
    # of mmauth genkey, there will be stanza lines and the fingerprint
    # will be included.  If the fingerprint is missing, or if this is
    # a key generated by a cs3b version of mmauth genkey, extract the
    # public key in a temp file.
    if [[ $keyLine = "clusterName="*            ||
          $keyLine = "clusterID="*              ||
          $keyLine = "genkeyFormat="*           ||
          $keyLine = "genkeyCompatibleFormat="* ||
          $keyLine = "keyGenNumber="*           ||
          $keyLine = "publicKey="               ||
          $keyLine = "certificate="             ]]
    then
      :  # Skip the line.

    elif [[ $keyLine = "keyDigest="* ]]
    then
      # Return the key fingerprint.
      keyFingerprint="${keyLine#keyDigest=}"
      break

    else
      # Add the line to the key file.
      print -- "$keyLine" >> $tmpPublicKey
      checkForErrors "writing to file $tmpPublicKey" $?
    fi  # end of if [[ $keyLine = "clusterName="* ]]
  done  # end while read -u4 keyLine

  # If the fingerprint was not imbedded in the key stanza,
  # determine it with the help of the openssl command.
  if [[ -z $keyFingerprint && -s $tmpPublicKey ]]
  then
    keyFingerprint=$($openssl dgst -sha -hex < $tmpPublicKey)
    checkForErrors "openssl dgst -sha -hex $tmpPublicKey" $?
  fi
  [[ -z $keyFingerprint ]] && keyFingerprint="(undefined)"

  # Return the result.
  print -- "$keyFingerprint"
  return 0

}  #------------ end function getLocalFingerprint -----------------


################################################################
#
# Function:  Retrieve the list of authorized file systems
#            for a given cluster.
#
# Input:     $1 - cluster name
#            $2 - sdrfs file to use
#
# Output:    A line for each of the file systems that
#            the specified cluster is allowed to access.
#            The following information is returned:
#               deviceName (maxAccessAllowed, rootSquashInfo)
#
# Returns:   0 - success
#            1 - error encountered
#
################################################################
function getFileSystemList   # <clusterName> <sdrfs>
{
  typeset sourceFile="mmauth.sh"
  [[ -n $DEBUG || -n $DEBUGgetFileSystemList ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset clusterName=$1
  typeset sdrfs=$2

  typeset fsList rootAllowed rootMappedTo

  [[ ! -s $sdrfs ]] &&  \
    checkForErrors "getFileSystemList: Missing or empty file $sdrfs" 1

  # Initialize dictionary items.
  rootAllowed=$(printInfoMsg 180)
  rootMappedTo=$(printInfoMsg 181)

  # Retrieve the information from the mmsdrfs file.
  fsList=$($awk -F: '                                \
    $'$NODESETID_Field' == "'$clusterName'" &&       \
    $'$LINE_TYPE_Field' == "'$AUTHORIZED_FS'"    {   \
      { printf ("%-9s (%s, ",                        \
                 $'$DEV_NAME_Field',                 \
                 $'$ACCESS_TYPE_Field') }            \
      if ( $'$ROOTSQUASH_UID_Field' == "") {         \
        { printf ("%s)", rootAllowMsg) }             \
      } else {                                       \
        { printf ("%s %s:%s)",                       \
                  rootMapMsg,                        \
                  $'$ROOTSQUASH_UID_Field',          \
                  $'$ROOTSQUASH_GID_Field') }        \
      }                                              \
      # Indent the information on the next line.     \
      { printf ("\n%-21s", " ") }                    \
    }                                                \
    END { print fsList }                             \
  ' rootAllowMsg="$rootAllowed" rootMapMsg="$rootMappedTo" $sdrfs)
  checkForErrors awk $?

  # Remove the last (empty) line from the result.
  [[ -n $fsList ]] && fsList=${fsList%?                     }

  print -- "$fsList"
  return $rc

}  #------ end of function getFileSystemList -----------------


################################################################
#
# Function:  Verify the specified uid:god string.  Valid values
#            are symbolic names that can be traslated to legitimate
#            for the system UID and GID, or unsigned integers,
#            or one of the keywords that turn off root squashing.
#
# Input:     $1 - uid:gid | off | no | DEFAULT | DELETE
#            $2 - sdrfs file to use
#
# Output:    uid and gid converted to unsigned integers.
#
# Returns:   0 - success
#            1 - error encountered
#
################################################################
function verifyRootSquashString   # <rsquashString>
{
  typeset sourceFile="mmauth.sh"
  [[ -n $DEBUG || -n $DEBUGverifyRootSquashString ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset rsquashString=$1

  typeset rsquashUid rsquashGid uid gid rc

  # See if the string is one of the many keywords
  # indicating no root squashing.
  if [[ $rsquashString = off || $rsquashString = DEFAULT ||
        $rsquashString = no  || $rsquashString = DELETE  ]]
  then
    print -- "DEFAULT"
    return 0
  fi

  # Parse the input string.
  IFS=":"
  set -f ; set -- $rsquashString ; set +f
  rsquashUid=$1
  rsquashGid=$2
  IFS="$IFS_sv"

  if [[ $rsquashUid = +([0-9]) ]]
  then
    uid=$rsquashUid
  elif [[ -n $rsquashUid ]]
  then
    uid=$($perl -e '
      $uid = getpwnam "'$rsquashUid'";
      if ($uid ne "") { printf "%u", $uid }
    ')
  else
    uid=""
  fi

  if [[ $rsquashGid = +([0-9]) ]]
  then
    gid=$rsquashGid
  elif [[ -n $rsquashGid ]]
  then
    gid=$($perl -e '
      $gid = getgrnam "'$rsquashGid'";
      if ($gid ne "") { printf "%u", $gid }
    ')
  else
    gid=""
  fi

  if [[ -z $uid ]]
  then
    # Invalid user name.
    printErrorMsg 172 $mmcmd $rsquashUid
    rc=1
  elif [[ -z $gid  ]]
  then
    # Invalid group name.
    printErrorMsg 173 $mmcmd $rsquashGid
    rc=1
  else
    # Everything is OK.
    print -- "$uid $gid"
    rc=0
  fi

  return $rc

}  #------ end of function verifyRootSquashString -----------------



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


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

action=$arg1
[[ -z $action ]] &&  \
  syntaxError "missingArgs" $usageMsg

remoteClusterName=$arg2
checkName clusterName 255 "$remoteClusterName"
[[ $? -ne 0 ]] && cleanupAndExit

# Allow "ls" to be used in place of "show".
[[ $action = ls ]] && action=show

# Allow "revoke" to be used in place of "deny".
[[ $action = revoke ]] && action=deny

# arg2 must be specified for all subcommands with the possible
# exception of the "show" subcommand.  It is either the keyword
# "new" or "commit" for the "genkey" subcommand, or is the name
# of the remote cluster for the rest of the subcommands.
if [[ -z $arg2 ]]
then
  if [[ $action = show ]]
  then
    remoteClusterName=all
  elif [[ $action = add || $action = update || $action = delete ||
          $action = grant || $action = deny || $action = genkey ]]
  then
    syntaxError "missingArgs" $usageMsg
  else
    syntaxError "keyword" $usageMsg "$action"
  fi  # end if [[ $action = show ]]
fi  # end if [[ -z $arg2 ]]

# Continue with the rest of the parameters.
if [[ $action = genkey ]]
then
  # The second argument must be "new" or "commit".
  genkeyAction=$arg2
  [[ $genkeyAction != new && $genkeyAction != commit ]] &&  \
    syntaxError "keyword" $usageMsg "$action"

  # No other arguments are expected.
  [[ -n $arg3 ]] && syntaxError "extraArg" $usageMsg $arg3

elif [[ $action = delete || $action = show ]]
then
  # No other arguments are expected.
  [[ -n $arg3 ]] && syntaxError "extraArg" $usageMsg $arg3

elif [[ $action = add || $action = update ]]
then
  shift 2    # Move past the cluster name in the parameter list.

  # Process the individual arguments and the values associated with them.
  while getopts :C:k:l: OPT
  do
    case $OPT in

      C) [[ -n $Cflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         Cflag="-$OPT"; Carg=$OPTARG;
         newClusterName=$Carg
         [[ $action != update ]] &&  \
           syntaxError "invalidOption" $usageMsg "-$OPT"
         checkName clusterName 255 "$Carg"
         [[ $? -ne 0 ]] && cleanupAndExit
         ;;

      k) [[ -n $kflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         kflag="-$OPT"; karg=$OPTARG;
         [[ -z $karg || $karg = DEFAULT ]] && karg=DELETE
         keyfile=$karg
         ;;

      l) [[ -n $lflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         lflag="-$OPT"; larg=$OPTARG;
         [[ -z $larg || $larg = DEFAULT ]] && larg=DELETE
         # Replace any colons in the cipher list string with commas.
         cipherList=$(print -- "$larg" | $sed 's/:/,/g')
         ;;

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

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

       *) # Invalid option.
          syntaxError "invalidOption" $usageMsg $OPTARG
          ;;
     esac
  done

  shift OPTIND-1
  [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1

  # Ensure required parameters are specified.
  if [[ $action = add ]]
  then
    [[ -z $karg ]] &&  \
      syntaxError "missingArgs" $usageMsg
  else  # $action = update
    [[ -z $Carg && -z $karg && -z $larg ]] &&  \
      syntaxError "missingArgs" $usageMsg
  fi

elif [[ $action = grant || $action = deny ]]
then
  shift 2    # Move past the cluster name in the parameter list.

  # Process the individual arguments and the values associated with them.
  while getopts :a:f:r: OPT
  do
    case $OPT in

      a) [[ -n $aflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         aflag="-$OPT"; aarg=$OPTARG;
         [[ $action = deny ]] &&  \
           syntaxError "invalidOption" $usageMsg $OPT
         [[ $aarg = none ]] && aarg=deny
         [[ $aarg != rw && $aarg != ro && $aarg != deny ]] &&  \
           syntaxError "invalidOption" $usageMsg "-$OPT $OPTARG"
         accessType=$aarg
         ;;

      f) [[ -n $fflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         fflag="-$OPT"; farg=$OPTARG;
         device=$farg
         ;;

      r) [[ -n $rflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         rflag="-$OPT"; rarg=$OPTARG;
         ;;

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

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

       *) # Invalid option.
          syntaxError "invalidOption" $usageMsg $OPTARG
          ;;
     esac
  done

  shift OPTIND-1
  [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1

  # Ensure required parameters are specified.
  [[ -z $device ]] && \
    syntaxError "missingArgs" $usageMsg

  [[ -z $accessType ]] && accessType=rw

  # Verify the device name.
  deviceName=${device##+(/)dev+(/)}  # name stripped of /dev/ prefix
  fqDeviceName="/dev/$deviceName"    # fully-qualified name (with /dev/ prefix)
  if [[ $deviceName = /* ]]
  then
    printErrorMsg 169 $mmcmd $device
    cleanupAndExit
  elif [[ $deviceName = */* ]]
  then
    printErrorMsg 170 $mmcmd $device
    cleanupAndExit
  fi

  # Verify the root squash string.
  if [[ -z $rarg ]]
  then
    rsquashUid=""
    rsquashGid=""
  else
    rsquashString=$(verifyRootSquashString $rarg)
    [[ $? -ne 0 ]] &&  \
      syntaxError "invalidOption" $noUsageMsg "-r $rarg"

    if [[ $rsquashString = DEFAULT ]]
    then
      rsquashUid=""
      rsquashGid=""
    else
      # Parse the result from verifyRootSquashString.
      set -f ; set -- $rsquashString ; set +f
      rsquashUid=$1
      rsquashGid=$2
    fi  # end of if [[ $rsquashString = DEFAULT ]]
  fi  # end of if [[ -z $rsquashString ]]

  # If the specified access type is "deny" (or "none"),
  # convert the action to "deny".
  [[ $accessType = deny ]] && action=deny

else
  # Invalid action requested.
  syntaxError "keyword" $usageMsg "$action"
fi  # end if [[ $action = genkey ]]

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


#######################################################################
# 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.
#######################################################################
if [[ $action = show ]]
then
  trap pretrap2 HUP INT QUIT KILL
  gpfsInitOutput=$(gpfsInit nolock)
  rc=$?
else
  trap pretrap HUP INT QUIT KILL
  # If action is genkey, tell gpfsInit (and related functions)
  # to bypass the checking and rebuilding of the security files.
  [[ $action = genkey ]] && export MMAUTH_GENKEY_RUNNING=yes
  gpfsInitOutput=$(gpfsInit $lockId)
  rc=$?
  unset MMAUTH_GENKEY_RUNNING
fi
setGlobalVar $rc $gpfsInitOutput

# Find out the default cipher list value to be used.
if [[ $action = show ]]
then
  defaultCipherList=$(showCfgValue cipherList)
fi


#######################################################################
# Perform any remaining parameter checking that requires knowledge of
# the sdrfsFormatLevel level (set by gpfsInit above). Ensure the user
# is not trying to utilize function that has not been enabled yet.
#######################################################################
# Ensure the key file exists and has the correct format.
if [[ -n $keyfile && $keyfile != DELETE ]]
then
  verifyPublicKeyFile $keyfile $sdrfsFormatLevel $verifiedKey  \
                      $newClusterName $remoteClusterName
  [[ $? -ne 0 ]] && cleanupAndExit

  if [[ $sdrfsFormatLevel -eq 0 ]]
  then
    # Ensure that the provided key file was generated by a 2.3 version of mmauth.
    $grep -q -e "^clusterName=" $verifiedKey >/dev/null 2>&1
    rc=$?
    if [[ $rc -eq 0 ]]
    then
      print -u2 "$mmcmd:  The specified public key file cannot be handled until you"
      print -u2 "    run \"mmchconfig release=LATEST\" to activate the new function."
      cleanupAndExit
    fi
  fi  # end of if [[ $sdrfsFormatLevel -eq 0 ]]
fi  # end of if [[ -n $keyfile && $keyfile != DELETE ]]

# Ensure the version of openssl installed on this node
# understands the specified cipher list.
if [[ -n $cipherList && $cipherList != DELETE ]]
then
  if [[ $cipherList = "AUTHONLY" ]]
  then
    ciphers=$($openssl ciphers "NULL-SHA")
  elif [[ $cipherList = *"AUTHONLY"* ]]
  then
    print -u2 "$mmcmd:  AUTHONLY cannot be specified in conjunction with other ciphers."
  else
    ciphers=$($openssl ciphers rsa "$cipherList")
  fi

  if [[ $cipherList != "AUTHONLY" && $sdrfsFormatLevel -eq 0 ]]
  then
    print -u2 "$mmcmd:  Support for cipher lists other than AUTHONLY has not been enabled yet."
    print -u2 "    Run \"mmchconfig release=LATEST\" to activate the new function."
    cleanupAndExit
  fi

  if [[ -z "$ciphers" ]]
  then
    # The specified cipher list is not supported.
    printErrorMsg 483 $mmcmd "$value"
    cleanupAndExit
  else
    :  # The new cipher list is acceptable.
  fi  # end of if [[ -z "$ciphers" ]]
fi  # end of if [[ -n $cipherList && $cipherList != DELETE ]]


#######################################################################
#
# Go through the mmsdrfs file and make the necessary changes.
#
#######################################################################
$rm -f $newsdrfs $nodefile $allClusterNodes
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.
  printLine=true  # Assume the line will be printed.

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

    $VERSION_LINE )
       # Increment the generation number.
       newGenNumber=${v[$SDRFS_GENNUM_Field]}+1
       v[$SDRFS_GENNUM_Field]=$newGenNumber

       # Retrieve the current key generation numbers and other information.
       newKeyGenNumber=${v[$NEW_KEY_Field]}
       [[ -z $newKeyGenNumber ]] && newKeyGenNumber=0
       committedKeyGenNumber=${v[$COMMITTED_KEY_Field]}
       [[ -z $committedKeyGenNumber ]] && committedKeyGenNumber=0
       secLevel=${v[$SECLEVEL_Field]}
       [[ -z $secLevel ]] && secLevel=0
       ourClusterName=${v[$CLUSTER_NAME_Field]}
       ourClusterId=${v[$CLUSTERID_Field]#gpfs}

       if [[ $action = genkey ]]
       then
         # Automatically "commit" old clusters that already have
         # established private keys (with cs3b level of mmauth).
         [[ $committedKeyGenNumber -eq 0 ]] &&  \
           committedKeyGenNumber=$newKeyGenNumber

         if [[ $genkeyAction = new ]]
         then
           # Do not allow a new key to be generated if a previously
           # generated key has not been committed yet.
           if [[ $committedKeyGenNumber -lt $newKeyGenNumber ]]
           then
             # Run: mmauth genkey commit.
             printErrorMsg 157 $mmcmd "mmauth genkey commit"
             cleanupAndExit
           fi

           # Increment the generation number for the new key
           # that we are about to create.
           (( newKeyGenNumber += 1 ))

           # If this is the very first genkey request,
           # or if the new code has not been activated yet,
           # or if this the first genkey request after
           # activating the new genkey file format,
           # automatically commit the key.
           [[ $newKeyGenNumber  -eq 1           ||
              $sdrfsFormatLevel -eq 0           ||
              -z ${v[$KEYFILE_FORMAT_Field]}    ||
              ${v[$KEYFILE_FORMAT_Field]} -eq 0 ]] &&  \
             committedKeyGenNumber=$newKeyGenNumber

           # Put the key numbers in the mmsdrfs file.
           v[$NEW_KEY_Field]=$newKeyGenNumber
           v[$COMMITTED_KEY_Field]=$committedKeyGenNumber
           [[ $sdrfsFormatLevel -ge 1 ]] &&  \
             v[$KEYFILE_FORMAT_Field]=$CURRENT_KEYFILE_FORMAT

         else  # genkeyAction = commit
           # See if there is anything to commit.
           if [[ $committedKeyGenNumber -eq $newKeyGenNumber ]]
           then
             if [[ $newKeyGenNumber -eq 0 ]]
             then
               # There is nothing to commit.
               # Tell the user to run mmauth genkey new.
               printErrorMsg 155 $mmcmd "mmauth genkey new"
             else
               # The current authentication files are already committed.
               printErrorMsg 156 $mmcmd
             fi
             cleanupAndExit
           fi

           # Ensure a cipher list has been established.
           if [[ $secLevel -eq 0 ]]
           then
             # You must establish a cipher list first.  Tell the user to run
             #    mmauth update $ourClusterName -l <cipherList>
             printErrorMsg 158 $mmcmd "mmauth update $ourClusterName -l <cipherList>"
             cleanupAndExit
           fi

           # Things seem to be OK.  Commit the key.
           committedKeyGenNumber=$newKeyGenNumber
           v[$COMMITTED_KEY_Field]=$committedKeyGenNumber

         fi  # end of if [[ $genkeyAction = new ]]
       fi  # end of if [[ $action = genkey ]]

       # See whether the target of the operation is the local cluster.
       [[ $remoteClusterName = "." ]] &&  \
         remoteClusterName=$ourClusterName

       if [[ $remoteClusterName = $ourClusterName ]]
       then
         remoteClusterName=$HOME_CLUSTER
         clusterFound=yes

         if [[ $action = add || $action = delete ||
               $action = grant || $action = deny ]]
         then
           # Operation not allowed for the local cluster.
           printErrorMsg 294 $mmcmd
           cleanupAndExit
         fi

         if [[ $action = update ]]
         then
           if [[ -n $newClusterName ]]
           then
             # Operation not allowed for the local cluster.
             printErrorMsg 294 $mmcmd
             cleanupAndExit
           fi

           if [[ -n $keyfile ]]
           then
             # Run "mmauth genkey" to establish a new key.
             printErrorMsg 314 $mmcmd
             cleanupAndExit
           fi

           if [[ -n $cipherList ]]
           then
             # Ensure the security level field is set correctly.
             if [[ $cipherList = DELETE ]]
             then
               secLevel=0
             else
               if [[ $newKeyGenNumber -eq 0 ]]
               then
                 # Run "mmauth genkey" first.
                 printErrorMsg 307 $mmcmd
                 cleanupAndExit
               fi
               secLevel=1
             fi  # end of if [[ $cipherList = DELETE ]]
             v[$SECLEVEL_Field]=$secLevel
           fi  # end of if [[ -n $cipherList ]]
         fi  # end of if [[ $action = update ]]
       fi  # end of if [[ $remoteClusterName = $ourClusterName ]]
       ;;


    $NODESET_HDR )
       if [[ $action = update && -n $cipherList &&
             $remoteClusterName = $HOME_CLUSTER ]]
       then
         # Update the cipher list for the local cluster.
         currentCipherList=${v[$CIPHER_LIST_Field]}
         [[ -z $currentCipherList ]] && currentCipherList=DELETE
         if [[ $cipherList != DELETE ]]
         then
           v[$CIPHER_LIST_Field]=$cipherList
         else
           v[$CIPHER_LIST_Field]=""
         fi
       fi  # end of if [[ $action = update && -n $cipherList && ...
       ;;


    $MEMBER_NODE )
       # Add the reliable node name to the appropriate files.

       # allClusterNodes contains the names of all nodes in the cluster.
       print -- "${v[$REL_HOSTNAME_Field]}" >> $allClusterNodes
       checkForErrors "writing to file $allClusterNodes" $?

       # nodefile contains the names of all nodes except the
       # primary and backup servers and the node on which the
       # command is executed (this node).
       if [[ ${v[$REL_HOSTNAME_Field]} != $primaryServer &&
             ${v[$REL_HOSTNAME_Field]} != $backupServer  &&
             ${v[$REL_HOSTNAME_Field]} != $ourNodeName   ]]
       then
         print -- "${v[$REL_HOSTNAME_Field]}" >> $nodefile
         checkForErrors "writing to file $nodefile" $?
       fi

       # 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]}
       ;;


    $MMFSCFG )
       if [[ $action = update && -n $cipherList &&
             $remoteClusterName = $HOME_CLUSTER ]]
       then
         # If changing the cipher list for the local cluster, remove
         # the line from the mmsdrfs file.  The mmfs.cfg information
         # will be added back before committing the changes.
         printLine=false

         # Extract the mmfs.cfg information.
         # It is everything past the first 4 fields.
         cfgLine="${v[5]}:${v[6]}:${v[7]}:${v[8]}:${v[9]}:${v[10]}:${v[11]}"
         cfgLine="$cfgLine:${v[12]}:${v[13]}:${v[14]}:${v[15]}:${v[16]}:${v[17]}"
         cfgLine="$cfgLine:${v[18]}:${v[19]}:${v[20]}:${v[21]}:${v[22]}"

         # To preserve tabs, temporarily set IFS to new line only.
         IFS="
"
         # Strip trailing colons and write the line to the file.
         print -- "${cfgLine%%+(:)}" >> $tmpCfg
         checkForErrors "writing to file $tmpCfg" $?
         IFS="$IFS_sv"  # Restore the default IFS settings.
       fi  # end of if [[ $action = update && -n $cipherList && ...
       ;;


    $AUTHORIZED_CLUSTER )
       if [[ ${v[$NODESETID_Field]} = $remoteClusterName ||
             $remoteClusterName = all && ($action = show ||
             $action = delete || $action = grant || $action = deny) ]]
       then
         clusterFound=yes
         clusterNameList="$clusterNameList ${v[$NODESETID_Field]} "

         # Processing depends on the specified action:
         case $action in
           add )
              # The remote cluster is already defined.
              printErrorMsg 316 $mmcmd $remoteClusterName
              cleanupAndExit
              ;;

           update )
              if [[ -n $cipherList ]]
              then
                if [[ $cipherList != DELETE ]]
                then
                  v[$CIPHER_LIST_Field]=$cipherList
                else
                  v[$CIPHER_LIST_Field]=""
                fi
              fi

              [[ -n $newClusterName ]] &&  \
                v[$NODESETID_Field]=$newClusterName
              ;;

           delete )
              printLine=false
              ;;

           show )
              # Display the cluster name.
              header=$(printInfoMsg 442)
              printf "%-20s %s\n" "$header" "${v[$NODESETID_Field]}"

              # Display the cipher list.
              header=$(printInfoMsg 443)
              cipherList="${v[$CIPHER_LIST_Field]}"
              if  [[ -z $cipherList ]]
              then
                # If there is no explicit cipher list for this cluster,
                # we use the cipher list for the local cluster.
                cipherList=$defaultCipherList
                [[ -z $cipherList ]] && cipherList="(none specified)"
              fi  # end of if  [[ -z $cipherList ]]
              printf "%-20s %s\n" "$header" "$cipherList"

              $rm -f $tmpPublicKey $fsFile

              # Display the SHA digest (public key fingerprint).
              header=$(printInfoMsg 444)
              keyFingerprint=$(getFingerprint ${v[$NODESETID_Field]} $mmsdrfsFile)
              printf "%-20s %s\n" "$header" "$keyFingerprint"

              # Display the file systems that can be accessed by the cluster.
              header=$(printInfoMsg 445)
              fsAuthList=$(getFileSystemList ${v[$NODESETID_Field]} $mmsdrfsFile)
              checkForErrors getFileSystemList $?
              [[ -z $fsAuthList ]] && fsAuthList="(none authorized)"
              printf "%-20s %s\n\n" "$header" "$fsAuthList"
              ;;

           grant )
              : # noop
              ;;

           deny )
              : # noop
              ;;

           *) checkForErrors "unexpected action $action" 1
              ;;
         esac  # end case $action in
       fi  # end if [[ ${v[$NODESETID_Field]} = $remoteClusterName || ...
       ;;


    $AUTHORIZED_KEY )
       if [[ ${v[$NODESETID_Field]} = $remoteClusterName ||
             $remoteClusterName = all && ($action = show || $action = delete) ]]
       then
         # Processing depends on the specified action:
         case $action in
           add )
              # The remote cluster is already defined.
              printErrorMsg 316 $mmcmd $remoteClusterName
              cleanupAndExit
              ;;

           update )
              [[ -n $keyfile ]] && printLine=false

              if [[ -n $newClusterName ]]
              then
                v[$NODESETID_Field]=$newClusterName
                [[ ${v[$LINE_NUMBER_Field]} = 1 ]] &&  \
                  v[$KEY_Field]="clusterName=$newClusterName"
              fi
              ;;

           delete )
              printLine=false
              ;;

           show )
              : # noop
              ;;

           grant )
              : # noop
              ;;

           deny )
              : # noop
              ;;

           *) checkForErrors "unexpected action $action" 1
              ;;
         esac  # end case $action in
       fi  # end if [[ ${v[$NODESETID_Field]} = $remoteClusterName || ...
       ;;


    $SG_HEADR )
       if [[ ${v[$DEV_NAME_Field]} = $deviceName || $device = all ]]
       then
         if [[ ${v[$NODESETID_Field]} = $HOME_CLUSTER ]]
         then
           # We are making changes to this file system.
           fsFound=yes
           fsList="$fsList ${v[$DEV_NAME_Field]} "
         elif [[ $device != all ]]
         then
           # Command is not allowed for remote file systems.
           printErrorMsg 106 $mmcmd $device ${v[$NODESETID_Field]}
           cleanupAndExit
         fi
       fi  # end if [[ ${v[$DEV_NAME_Field]} = $deviceName || $device = all ]]
       ;;


    $AUTHORIZED_FS )
       if [[ (${v[$NODESETID_Field]} = $remoteClusterName || $remoteClusterName = all)  &&
             (${v[$DEV_NAME_Field]} = $deviceName || $device = all || $action = delete) ]]
       then
         if [[ $action = delete || $action = deny ]]
         then
           # Remove from the file the affected AUTHORIZED_FS lines.
           printLine=false
         elif [[ $action = grant ]]
         then
           # If changing an existing authorization, update the affected fields.
           if [[ -n $aarg ]]
           then
             v[$ACCESS_TYPE_Field]=$accessType
           fi
           if [[ -n $rarg ]]
           then
             v[$ROOTSQUASH_UID_Field]=$rsquashUid
             v[$ROOTSQUASH_GID_Field]=$rsquashGid
           fi

           # Tell the guy what exactly he is doing.
           if [[ -z ${v[$ROOTSQUASH_UID_Field]} ]]
           then
             printInfoMsg 182 $mmcmd ${v[$NODESETID_Field]}  \
                 ${v[$DEV_NAME_Field]} ${v[$ACCESS_TYPE_Field]}
           else
             printInfoMsg 183 $mmcmd ${v[$NODESETID_Field]}  \
                 ${v[$DEV_NAME_Field]} ${v[$ACCESS_TYPE_Field]}  \
                 ${v[$ROOTSQUASH_UID_Field]} ${v[$ROOTSQUASH_GID_Field]}
           fi

           # Add the name of the fs to the list of processed file systems.
           # Note:  The trailing blank is important.
           processedFileSystems="$processedFileSystems ${v[$NODESETID_Field]}:${v[$DEV_NAME_Field]} "
         else
           :  # should not get here.
         fi  # end of if [[ $action = delete || $action = deny ]]
       fi  # end of if [[ (${v[$NODESETID_Field]} = $remoteClusterName ...

       if [[ ${v[$NODESETID_Field]} = $remoteClusterName ]]
       then
          [[ $action = update && -n $newClusterName ]] &&  \
            v[$NODESETID_Field]=$newClusterName
       fi
       ;;


    * )  # 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.
  if [[ $printLine = true ]]
  then
    print_newLine >> $newsdrfs
    checkForErrors "writing to file $newsdrfs" $?
  fi

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

done  # end while read -u3 mmsdrfsFile

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


#########################################
# Additional action-specific processing.
#########################################
if [[ $action = genkey ]]
then
  if [[ $genkeyAction = new ]]
  then
    # Ensure the OpenSSL code is installed.
    if [[ ! -x $openssl ]]
    then
      # openssl command not found.
      printErrorMsg 159 $mmcmd $openssl
      cleanupAndExit
    fi

    # If the sslrandfile user exit is installed, use it to determine
    # the value for the -rand paramter for the openssl calls.
    if [[ -x $sslrandfile ]]
    then
      randFile=$($sslrandfile 2>/dev/null)
      [[ -n $randFile ]] &&  \
        randOpt="-rand $randFile"
    fi

    # If the multiple keys support has not been activated yet,
    # ensure the daemon is not running anywhere.
    if [[ $sdrfsFormatLevel -eq 0 ]]
    then
      printInfoMsg 339
      verifyDaemonInactive $allClusterNodes $mmcmd
      [[ $? -ne 0 ]] && cleanupAndExit
      daemonInactive=yes
    fi

    # Regenerate the openssl.conf file.
    $sed 's/= gpfsCluster/= '$ourClusterName'/' $opensslConfFile > $certConfigFile
    checkForErrors "creating $certConfigFile" $?

    # Generate the new local private key.
    umask 077
    $openssl genrsa $randOpt -out $tmpPrivateKey 512
    checkForErrors "openssl genrsa $randOpt -out $tmpPrivateKey 512" $?

    # If the multiple keys functionality is enabled, generate
    # the corresponding private key and certificate files.
    # Otherwise, stage the private key file as is.
    if [[ $sdrfsFormatLevel -eq 0 ]]
    then
      $cp $tmpPrivateKey ${privateKey}$newKeyGenNumber
      checkForErrors "cp $tmpPrivateKey ${privateKey}$newKeyGenNumber" $?
      $mv $tmpPrivateKey $tmpkey
      checkForErrors "mv $tmpPrivateKey $tmpkey" $?
      fileToStage="${privateKey}$newKeyGenNumber"
    else
      # Generate the public key file and find its SHA digest.
      $openssl rsa -in $tmpPrivateKey -pubout -out $tmpPublicKey
      checkForErrors "openssl rsa -in $tmpPrivateKey -pubout -out $tmpPublicKey" $?
      keyFingerprint=$($openssl dgst -sha -hex < $tmpPublicKey 2>/dev/null)

      # Generate the certificate file.
      $openssl req $randOpt -new -x509 -days 16427 -key $tmpPrivateKey  \
               -out $tmpCertificate -config $certConfigFile
      checkForErrors  \
        "openssl req $randOpt -new -x509 -days 16427 -key $tmpPrivateKey -out $tmpCertificate -config $certConfigFile" $?

      # Create the combined file with all relevant key information.
      #
      # The combined file consists of several (currently 6) simple stanza
      # lines followed by several (currently 3) variable length sections.
      # The order must be preserved.  If it becomes necessary in the future
      # to add additional information, the following rules should be obeyed:
      # - Any new simple stanza lines should follow the initial 6 lines
      #   but should come before any variable length sections.
      # - Any new variable length sections should come before the current
      #   three sections: privateKey, publicKey and certificate.
      #
      print -- "clusterName=$ourClusterName" > $tmpkey
      checkForErrors "writing to $tmpkey" $?
      print -- "clusterID=$ourClusterId" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      print -- "genkeyFormat=$CURRENT_KEYFILE_FORMAT" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      print -- "genkeyCompatibleFormat=$COMPATIBLE_KEYFILE_FORMAT" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      print -- "keyGenNumber=$newKeyGenNumber" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      print -- "keyDigest=$keyFingerprint" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      print -- "privateKey=" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      $cat $tmpPrivateKey >> $tmpkey
      checkForErrors "cat $tmpPrivateKey >> $tmpkey" $?
      print -- "publicKey=" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      $cat $tmpPublicKey >> $tmpkey
      checkForErrors "cat $tmpPublicKey >> $tmpkey" $?
      print -- "certificate=" >> $tmpkey
      checkForErrors "writing to $tmpkey" $?
      $cat $tmpCertificate >> $tmpkey
      checkForErrors "cat $tmpCertificate >> $tmpkey" $?

      # If everyting worked out OK so far, copy the new file in the ssl/stage directory.
      fileToStage="${genkeyData}$newKeyGenNumber"
      $cp $tmpkey $fileToStage
      checkForErrors "cp $tmpkey $fileToStage" $?

    fi  # end of if [[ $sdrfsFormatLevel -eq 0 ]]
    umask $UMASK_sv

    # Stage the newly-generated key file on the server nodes.
    # The files will be activated after the new key generation number
    # is committed as a result of the regular commit protocol.
    if [[ $primaryServer != $ourNodeName ]]
    then
      $rcp -p $fileToStage ${primaryServer}:$fileToStage
      checkForErrors "$rcp ${primaryServer}:$fileToStage" $?
    fi

    if [[ -n $backupServer && $backupServer != $ourNodeName ]]
    then
      $rcp -p $fileToStage ${backupServer}:$fileToStage
      checkForErrors "$rcp ${backupServer}:$fileToStage" $?
    fi

  else
    :  # There is nothing to do for genkeyAction commit at this point.
  fi  # end of if [[ $genkeyAction = new ]]
fi  # end of if [[ $action = genkey ]]


if [[ $action = add ]]
then
  # Generate the needed information for the mmsdrfs file.
  newLine="$remoteClusterName:$AUTHORIZED_CLUSTER:::$accessGranted::$cipherList:"
  print -- "$newLine" >> $newsdrfs
  checkForErrors "writing to file $newsdrfs" $?

  # Process the -k keyfile option.
  if [[ -n $keyfile ]]
  then
    appendFile $remoteClusterName $verifiedKey $AUTHORIZED_KEY $newsdrfs
    checkForErrors  \
        "appendFile $remoteClusterName $verifiedKey $AUTHORIZED_KEY $newsdrfs" $?
  fi
fi  # end of if [[ $action = add ]]


if [[ $action = update ]]
then
  if [[ -z $clusterFound ]]
  then
    # The remote cluster is not authorized to access this cluster.
    printErrorMsg 259 $mmcmd $remoteClusterName
    cleanupAndExit
  fi

  # Process the -k keyfile option.
  if [[ -n $keyfile && $keyfile != DELETE ]]
  then
    if [[ -n $newClusterName ]]
    then
      appendFile $newClusterName $verifiedKey $AUTHORIZED_KEY $newsdrfs
      checkForErrors  \
        "appendFile $newClusterName $verifiedKey $AUTHORIZED_KEY $newsdrfs" $?
    else
      appendFile $remoteClusterName $verifiedKey $AUTHORIZED_KEY $newsdrfs
      checkForErrors  \
        "appendFile $remoteClusterName $verifiedKey $AUTHORIZED_KEY $newsdrfs" $?
    fi
  fi  # end of if [[ -n $keyfile && $keyfile != DELETE ]]

  # Process the -l cipherList option for the local cluster.
  if [[ -n $cipherList && $remoteClusterName = $HOME_CLUSTER ]]
  then
    # If changing the cipher list for the local cluster,
    # make changes to the mmfs.cfg file.
    $mmfixcfg "cipherList" "$cipherList" < $tmpCfg > $newcfg 2>/dev/null
    checkForErrors "mmfixcfg cipherList" $?

    # Put the updated mmfs.cfg information back into the mmsdrfs file.
    appendCfgFile $HOME_CLUSTER $newcfg $newsdrfs
    checkForErrors "appendCfgFile" $?

    # The daemon must be down on all nodes if we are about to switch
    # from a non-secure to a secure environment or vice-versa.
    if [[ $currentCipherList != $cipherList &&
          ( $currentCipherList = DELETE || $cipherList = DELETE ) ]]
    then
      printInfoMsg 339
      verifyDaemonInactive $allClusterNodes $mmcmd
      [[ $? -ne 0 ]] && cleanupAndExit
      daemonInactiveVerified=yes
    fi
  fi  # end of if [[ -n $cipherList && $remoteClusterName = $HOME_CLUSTER ]]
fi  # end of if [[ $action = update ]]


if [[ $action = delete ]]
then
  if [[ -z $clusterFound ]]
  then
    if [[ $remoteClusterName = all ]]
    then
      # There are no remote cluster authorizations.
      printErrorMsg 258 $mmcmd
    else
      # The remote cluster is not authorized to access this cluster.
      printErrorMsg 259 $mmcmd $remoteClusterName
    fi
    cleanupAndExit
  fi  # end of if [[ -z $clusterFound ]]
fi  # end of if [[ $action = delete ]]


if [[ $action = show ]]
then
  # Add the information for the local cluster at the end of the output.
  if [[ $remoteClusterName = all || $remoteClusterName = $HOME_CLUSTER ]]
  then
    clusterFound=yes

    # Display the cluster name.
    header=$(printInfoMsg 442)
    printf "%-20s %s\n" "$header" "$ourClusterName (this cluster)"

    # Display the cipher list.
    header=$(printInfoMsg 443)
    cipherList=$defaultCipherList
    [[ -z $cipherList ]] && cipherList="(none specified)"
    printf "%-20s %s\n" "$header" "$cipherList"


    # Display the SHA digest for the committed public key.
    header=$(printInfoMsg 444)
    keyFingerprint=$(getLocalFingerprint $committedPublicKey)
    printf "%-20s %s\n" "$header" "$keyFingerprint"

    # Display the SHA digest for the uncommitted public key.
    if [[ -s $newPublicKey ]]
    then
      header=$(printInfoMsg 427)
      keyFingerprint=$(getLocalFingerprint $newPublicKey)
      printf "%-20s %s\n" "$header" "$keyFingerprint"
    fi

    # Display the file systems that can be accessed by the cluster.
    header=$(printInfoMsg 445)
    fsAuthList="(all rw)"
    printf "%-20s %s\n\n" "$header" "$fsAuthList"
  fi  # end if [[ $remoteClusterName = all || $remoteClusterName = $HOME_CLUSTER ]]

  if [[ -z $clusterFound ]]
  then
    # The remote cluster is not authorized to access this cluster.
    printErrorMsg 259 $mmcmd $remoteClusterName
    cleanupAndExit
  fi  # end of if [[ -z $clusterFound ]]

  # Nothing more to do.
  cleanupAndExit 0
fi  # end of if [[ $action = show ]]


if [[ $action = grant ]]
then
  if [[ -z $clusterFound ]]
  then
    if [[ $remoteClusterName = all ]]
    then
      # There are no remote cluster definitions.
      printErrorMsg 262 $mmcmd
    else
      # The remote cluster is not defined.
      printErrorMsg 259 $mmcmd $remoteClusterName
    fi
    cleanupAndExit
  fi  # end of if [[ -z $clusterFound ]]

  if [[ -z $fsFound ]]
  then
    if [[ $device = all ]]
    then
      # No file systems were found.
      printErrorMsg 200 $mmcmd
    else
      # The requested file system was not found.
      printErrorMsg 288 $mmcmd $device
    fi
    cleanupAndExit
  fi  # end of if [[ -z $fsFound ]]

  # Generate the appropriate AUTHORIZED_FS lines.
  print -- ""  # put a blank separator line
  for fsName in $fsList
  do
    for remCluster in $clusterNameList
    do
      [[ $processedFileSystems = *" $remCluster:$fsName "* ]] && continue

      newLine=$remCluster:$AUTHORIZED_FS:$fsName::$accessType:$rsquashUid:$rsquashGid:
      print -- "$newLine" >> $newsdrfs
      checkForErrors "writing to file $newsdrfs" $?
      if [[ -z $rsquashUid ]]
      then
        printInfoMsg 182 $mmcmd $remCluster $fsName $accessType
      else
        printInfoMsg 183 $mmcmd $remCluster $fsName $accessType $rsquashUid $rsquashGid
      fi
      print -- ""  # put a blank separator line
    done  # end of for remCluster in $clusterNameList
  done  # end of for fsName in $fsList
fi  # end of if [[ $action = grant ]]


if [[ $action = deny ]]
then
  if [[ -z $clusterFound ]]
  then
    if [[ $remoteClusterName = all ]]
    then
      # There are no remote cluster definitions.
      printErrorMsg 262 $mmcmd
    else
      # The remote cluster is not defined.
      printErrorMsg 259 $mmcmd $remoteClusterName
    fi
    cleanupAndExit
  fi  # end of if [[ -z $clusterFound ]]

  if [[ -z $fsFound ]]
  then
    if [[ $device = all ]]
    then
      # No file systems were found.
      printErrorMsg 200 $mmcmd
    else
      # The requested file system was not found.
      printErrorMsg 288 $mmcmd $device
    fi
    cleanupAndExit
  fi  # end of if [[ -z $fsFound ]]

  # Nothing more to do.  The appropriate AUTHORIZED_FS lines have been removed.

fi  # end of if [[ $action = deny ]]


####################################################
# Sort the new mmsdrfs file and commit the changes.
####################################################
LC_ALL=C $SORT_MMSDRFS $newsdrfs -o $newsdrfs
checkForErrors "sorting $newsdrfs" $?

trap "" HUP INT QUIT KILL
gpfsObjectInfo=$(commitChanges  \
   $nsId $nsId $gpfsObjectInfo $newGenNumber $newsdrfs $primaryServer)
rc=$?
if [[ $rc -ne 0 ]]
then
  # The commit step failed; we cannot replace the file in the sdr.
  printErrorMsg 381 $mmcmd
  cleanupAndExit
fi


##################
# Unlock the sdr.
##################
freeLockOnServer $primaryServer $ourNodeNumber >/dev/null
sdrLocked=no
trap posttrap HUP INT QUIT KILL

# Issue "command was successful" message.
printErrorMsg 272 $mmcmd


#####################################################
# Asynchronously propagate the changes to all nodes.
#####################################################
if [[ $action = genkey && $genkeyAction = new ]]
then
  propagateKeyFile async $nodefile  \
    $newsdrfs $newGenNumber $fileToStage $newKeyGenNumber
else
  propagateSdrfsFile async $nodefile $newsdrfs $newGenNumber
fi

if [[ $secLevel -gt 0 ]]
then
  # Notify all currently-running nodes to refresh their key files.
  $mmcommon onactive $preferredNode $allClusterNodes  \
    $NO_FILE_COPY $NO_MOUNT_CHECK NULL $NO_LINK       \
    tsdsh $mmremote refreshAuth >$tmpfile 2>&1
fi

cleanupAndExit 0

