#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 1999,2005 
# 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 
# @(#)85 1.46 src/avs/fs/mmfs/ts/admin/mmlsdisk.sh, mmfs, avs_rgpfs24, rgpfs240610b 10/6/05 10:56:32
#########################################################################
#
# Usage:
#
#   mmlsdisk Device [-d "DiskName[;DiskName]"] [-e] [-L]
#
#   mmlsdisk Device [-d "DiskName[;DiskName]"] {-m | -M}
#
# where
#
#   -e    Display all of the disks that do not have an availability
#         of up and a status of ready.
#
#   -L    Display an extended list of the disk parameters, including
#         the disk id field and the remarks field.  The remarks column
#         shows the current file system descriptor quorum assignments,
#         and displays the excluded disks.
#
#   -m    Display whether I/O requests to the disk are satisfied on
#         the local node or via an NSD server.  The scope of this
#         option is the node on which the mmlsdisk command is issued.
#
#   -M    Display whether I/O requests to the disk are satisfied on
#         the local node or via an NSD server.  If the I/O is done
#         via an NSD server, show the NSD server name and the actual
#         underlying disk name on that server node.
#         Caution:  This is a slow operation!
#
#
# Undocumented options:
#
#   -i    Display the disk id values for each of the disks.
#
#########################################################################

# Include global declarations and service routines.
. /usr/lpp/mmfs/bin/mmglobfuncs
if [[ $ourUid -eq 0 ]]
then
  . /usr/lpp/mmfs/bin/mmsdrfsdef
  . /usr/lpp/mmfs/bin/mmfsfuncs
fi

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

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

tsnsdaccessResult=${tmpDir}tsnsdaccessResult.${mmcmd}.$$

LOCAL_FILES=" $tsnsdaccessResult "


# Local variables

usageMsg=301
integer rc=0
integer nodeCount=0
argList=""   # argList without quotes around disk list
argListQ=""  # argList with quotes around disk list
# underline="----------------------------------------------------------"
# underline="${underline}----------------------------------------------"
underline="------------  -----------------------  -----------------  ------------"


# Local functions


###########################################################################
#
# Function:  Show whether disk I/O takes place on this node or
#            over an NSD server.  If requested, try to determine
#            the actual disk names to which the I/O is directed.
#
#            Notes:
#
#            - The file system must be mounted on this node.
#              The scope of the command is the current node only.
#
#            - If the file system is owned by some other cluster,
#              the underlying disk names cannot be determined;
#              we cannot run commands on nodes that do not belong
#              to this cluster.
#
# Input:     $1 - device name as provided by the user
#            $2 - fully qualified file system device name
#            $3 - file system home cluster name
#            $4 - disk name mapping indicator:  yes or no
#            $5 - disk name list (optional); if not specified,
#                 all disks in the file system are assumed
#
# Output:    Formatted disk access information
#
# Returns:   0 - no errors encountered
#            19 (ENODEV) - options line not found
#            22 (EINVAL) - device name is not valid
#            1 - some other unexpected error
#
###########################################################################
function displayDiskAccessInfo  # device fqDeviceName fsHomeCluster
                                #   Moption [diskList]
{
  typeset sourceFile="mmlsdisk.sh"
  [[ -n $DEBUG || -n $DEBUGdisplayDiskAccessInfo ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset device=$1
  typeset fqDeviceName=$2
  typeset fsHomeCluster=$3
  typeset Moption=$4
  typeset diskList="$5"

  typeset rc=0
  typeset displayAll=""

  typeset disksToDisplay diskName diskLine tsnsdaccessLine
  typeset magicWord nodeIPaddr localDiskName nodeName
  typeset hostResult availability nodeBelongsToTheCluster
  typeset diskInfo nsdId nsdSubtype getLocalDiskNameOutput

  [[ $Moption != M ]] && Moption=""

  # If disk list is specified, replace the ';' with blanks.
  if [[ -n $diskList ]]
  then
    disksToDisplay=$(print -- "$diskList" | $sed 's/;/ /g')
  else
    displayAll=yes
  fi

  # Call tsnsdaccess to get the information for all disks in the file system.
  $tsnsdaccess $fqDeviceName > $tsnsdaccessResult 2> $errMsg
  rc=$(remapRC $?)

  # Issue an appropriate message if tsnsdaccess failed.
  if [[ $rc -eq $MM_DaemonDown ]]
  then
    # GPFS is down on this node.
    printErrorMsg 109 $mmcmd
  elif [[ $rc -eq $MM_QuorumWait ]]
  then
    # GPFS is not ready for commands.
    printErrorMsg 110 $mmcmd
  elif [[ $rc -eq $MM_FsNotFound || $rc -eq $MM_RemoteNotFound ]]
  then
    # The file system is not mounted on this node.
    printErrorMsg 563 $mmcmd $device $ourNodeName
  elif [[ $rc -eq $MM_Remotefs && $fsHomeCluster != $HOME_CLUSTER ]]
  then
    # The file system is not owned by the remote cluster.
    [[ $device != *:* ]] &&  \
      printErrorMsg 111 $mmcmd $device $remoteDevice $fsHomeCluster
    printErrorMsg 112 $mmcmd $remoteDevice $fsHomeCluster
  elif [[ ($rc -eq $MM_HostDown    ||
           $rc -eq $MM_TimedOut    ||
           $rc -eq $MM_SecurityCfg ||
           $rc -eq $MM_AuthorizationFailed ||
           $rc -eq $MM_UnknownCluster)    &&
          $fsHomeCluster != $HOME_CLUSTER ]]
  then
    # Failed to connect to the remote cluster.
    [[ $rc -eq $MM_SecurityCfg ]] &&  \
      printErrorMsg 150 $mmcmd
    [[ $rc -eq $MM_AuthorizationFailed ]] &&  \
      printErrorMsg 151 $mmcmd
    printErrorMsg 105 $mmcmd $fsHomeCluster
  elif [[ $rc -eq $MM_ConnectionReset ]]
  then
    # An internode connection was reset.
    printErrorMsg 257 $mmcmd
  elif [[ $rc -ne 0 ]]
  then
    # tsnsdaccess failed.
    [[ -s $errMsg ]] && $cat $errMsg 1>&2
    printErrorMsg 104 "$mmcmd" "tsnsdaccess $fqDeviceName"
  else
    :  # The command must have worked.
  fi  # end of if [[ $rc -eq $MM_DaemonDown ]]
  $rm -f $errMsg

  # Give up if tsnsdaccess failed.
  [[ $rc -ne 0 ]] &&  \
    return $rc

  # Extract from the tsnsdaccess output the information for the disks
  # that we are interested in.  Store the result in a separate file.
  if [[ -n $displayAll ]]
  then
    $grep -e "tsnsdaccess:" -e "tsnsdaccess64:" $tsnsdaccessResult > $tmpfile
  else
    $rm -rf $tmpfile
    for diskName in $disksToDisplay
    do
      diskLine=$($grep -e "tsnsdaccess:$diskName:" -e "tsnsdaccess64:$diskName:" $tsnsdaccessResult)
      if [[ -z $diskLine ]]
      then
        printErrorMsg 315 $mmcmd $diskName $device
        return 1
      else
        print -- "$diskLine" >> $tmpfile
        checkForErrors "writing to file $tmpfile" $?
      fi
    done  # end for diskName in $disksToDisplay
  fi  # end of if [[ -n $displayAll ]]

  if [[ ! -s $tmpfile ]]
  then
    # Unexpected error.  This should not happen at this point.
    print -u2 "$mmcmd:  There is no disk information to display."
    return 1
  fi

  # Print the header line:
  #   "Disk name   IO performed on node   Device   Availability"
  header=$(printInfoMsg 506)
  printf "\n%s\n%.${#header}s\n" "$header" "$underline"

  # Process the information from the tsnsdaccess call and print the results.
  exec 3<&-
  exec 3< $tmpfile
  while read -u3 tsnsdaccessLine
  do
    # Parse the line.
    IFS=":"
    set -f ; set -- $tsnsdaccessLine ; set +f
    IFS="$IFS_sv"
    magicWord=$1
    diskName=$2
    localDiskName=$3
    nodeIPaddr=$4
    nsdSubtype=$5
    nsdId=$6

    # Determine the node name, local device name, and availability values.
    if [[ $localDiskName = "unused" ]]
    then
      # The disk is not being used on any of the nodes.  The only
      # thing that we know is that (most likely) the disk is down.
      availability="down"
      nodeName=$UNRESOLVED
      localDiskName=$UNRESOLVED

    elif [[ $localDiskName = "unknown" ]]
    then
      # The disk is active but not on this node.  The IP address refers
      # to the NSD server for the disk.  That server may or may not be
      # part of our cluster.  If it is not, we will attempt to get its
      # name using the host command.
      availability="up"

      nodeName=$(getNodeInfo  \
        $REL_HOSTNAME_Field $IPA_Field $nodeIPaddr $HOME_CLUSTER $mmsdrfsFile)

      if [[ -n $nodeName ]]
      then
        nodeBelongsToTheCluster=yes
      else
        hostResult=$($host $nodeIPaddr 2>/dev/null)
        set -f ; set -- $hostResult ; set +f
        nodeName=$1
        nodeBelongsToTheCluster=""
      fi
      [[ -z $nodeName ]] && nodeName=$nodeIPaddr

      if [[ -n $Moption && -n $nodeBelongsToTheCluster ]]
      then
        # -M is specified and the NSD server is a member of the cluster.
        # Find the local device name for the disk on the NSD server node.
        getLocalDiskNameOutput=$($mmcommon on1 $nodeName  \
             getLocalDiskName $nsdId $nsdSubtype 2>$errMsg)
        rc=$?

        # Parse the output from the getLocalDiskName function.
        IFS=":"
        set -f ; set -- $getLocalDiskNameOutput ; set +f
        magicWord=$1
        localDiskName=$2
        IFS="$IFS_sv"

        if [[ -z $localDiskName || $magicWord != getLocalDiskName ]]
        then
          localDiskName=$UNRESOLVED
        fi
        $rm -f $errMsg

      else
        # -M is not specified, or the NSD server is not a member
        # of our cluster;  we do not know the name of the disk.
        localDiskName=$UNRESOLVED

      fi  # end of if [[ -n $Moption && -n $nodeBelongsToTheCluster ]]

    else
      # The I/O takes place on the local node.
      availability="up"
      nodeName="localhost"

      # If this is an AIX environment, replace the /dev/r prefix with /dev/.
      if [[ $osName = AIX && $localDiskName = +(/)dev+(/)r* ]]
      then
        localDiskName=${localDiskName##+(/)dev+(/)r}
        localDiskName=/dev/$localDiskName
      fi

    fi  # end of if [[ $localDiskName = "unused" ]]

    # Put out the results.
    printf "%-13s %-24s %-18s %s\n" \
           "$diskName" "$nodeName" "$localDiskName" "$availability"

  done  # end of while read -u3 tsnsdaccessLine

  return $rc

}  #----- end of function displayDiskAccessInfo  ----------------



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


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

[[ $argc -lt 1  ]] &&  \
  syntaxError "missingArgs" $usageMsg

device=$arg1     # Save stripe group device (always the first parameter).
shift 1          # Drop the device name from the parameter list.

# Parse the optional parameters.  Since the disk names in the -d option
# are separated with ';', special care must be taken when creating argList.
# For invocations via mmcommon, the disk list must be enclosed in quotes.
# For direct (local) invocations of tslsdisk, the disk list must not have
# surrounding quotes.
if [[ $argc -gt 1 ]]
then
  while getopts :eiLmMd: OPT
  do
    case $OPT in
      e) # Display all disks that are not up and ready.
         [[ -n $eflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         eflag="-$OPT"
         [[ -n $mflag || -n $Mflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $eflag $mflag $Mflag
         argList="$argList -$OPT"
         argListQ="$argListQ -$OPT"
         ;;

      i) # Display disk ids.
         [[ -n $iflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         iflag="-$OPT"
         [[ -n $mflag || -n $Mflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $iflag $mflag $Mflag
         argList="$argList -$OPT"
         argListQ="$argListQ -$OPT"
         ;;

      d) # Display information for the specified disks.
         [[ -n $dflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         diskList="$OPTARG"
         dflag="-$OPT"
         [[ "$diskList" = *+([-:,${BLANKchar}${TABchar}])* ]] &&  \
           syntaxError "badSeparator_notSemicolon" $noUsageMsg
         argList="$argList -$OPT $OPTARG "
         argListQ="$argListQ -$OPT \"$OPTARG\" "
         ;;

      L) # Display disk ids and other information.
         [[ -n $Lflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         Lflag="-$OPT"
         [[ -n $mflag || -n $Mflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $Lflag $mflag $Mflag
         argList="$argList -$OPT"
         argListQ="$argListQ -$OPT"
         ;;

      m) # Display disk I/O path (localhost vs. NSD server).
         [[ -n $mflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         mflag="-$OPT"
         Moption="$OPT"
         [[ -n $Mflag || -n $eflag || -n $iflag || -n $Lflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $mflag $Mflag $eflag $iflag $Lflag
         ;;

      M) # Display disk I/O path (localhost vs. NSD server) and hdisk name.
         [[ -n $Mflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
         Mflag="-$OPT"
         Moption="$OPT"
         [[ -n $mflag || -n $eflag || -n $iflag || -n $Lflag ]] &&  \
           syntaxError "invalidCombination" $usageMsg $Mflag $mflag $eflag $iflag $Lflag
         ;;

      +[eidLmM]) # invalid option specified
         syntaxError "invalidOption" $usageMsg $OPT
         ;;

      :) # missing required value after an option
         syntaxError "missingValue" $usageMsg $OPTARG
         ;;

      *) # invalid option specified
         syntaxError "invalidOption" $usageMsg $OPTARG
         ;;
     esac
  done  # end of while getopts :eiLmMd: OPT
  shift OPTIND-1
  [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1
fi  # end of if [[ $argc -gt 1 ]]


###################################
# Set up trap exception handling.
###################################
trap pretrap2 HUP INT QUIT KILL


####################################################################
# If invoked by a root user, call the gpfsInit function to ensure
# that the local copy of the mmsdrfs file and the rest of the GPFS
# system files are up-to-date.  There is no need to lock the sdr.
# Non-root users are not allowed to invoke commands on other nodes.
####################################################################
if [[ $ourUid -eq 0 ]]
then
  gpfsInitOutput=$(gpfsInit nolock)
  setGlobalVar $? $gpfsInitOutput

elif [[ -n $Mflag ]]
then
  # This option requires root authority.
  printErrorMsg 324 $mmcmd
  cleanupAndExit
fi  # end of if [[ $ourUid -eq 0 ]]


####################################
# Make sure the file system exists.
####################################

# If the invocation is not for an explicitly-remote device, obtain the
# needed information about the filesystem from the mmsdrfs file.
if [[ $device != *:* ]]
then
  findFSoutput=$(findFS "$device" $mmsdrfsFile)
  [[ -z $findFSoutput ]] && cleanupAndExit

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

  # If this is a remote file system, set fqDeviceName appropriately.
  [[ $fsHomeCluster != $HOME_CLUSTER ]] &&  \
    fqDeviceName="$fsHomeCluster:/dev/$remoteDevice"
else
  fqDeviceName=$device
  deviceName=${fqDeviceName##*:}
  fsHomeCluster=${fqDeviceName%%:*}
  remoteDevice=$deviceName
fi  # end of if [[ $device != *:* ]]


############################################
# Process the -m and -M options separately.
############################################
if [[ -n $mflag || -n $Mflag ]]
then
  displayDiskAccessInfo $device $fqDeviceName $fsHomeCluster $Moption "$diskList"
  cleanupAndExit $?
fi  # end of if [[ -n $mflag || -n $Mflag ]]


########################################################################
# Invoke the command on the local node.
# Display any error messages and exit if any of the following are true:
#   - the command completed successfully
#   - there is an unacceptable error
#       (anything other than daemon down or quorum wait)
#   - the file system is remote
#   - we are not running as UID 0
#   - this is a single node cluster
########################################################################
${mmcmdDir}/${links}/mmlsdisk $fqDeviceName $argList 2>$errMsg
rc=$(remapRC $?)
if [[ ($rc -ne $MM_DaemonDown && $rc -ne $MM_QuorumWait) ||
      $fsHomeCluster != $HOME_CLUSTER                    ||
      $ourUid -ne 0                                      ||
      $MMMODE = single ]]
then
  if [[ $rc -eq $MM_FsNotFound ]]
  then
    if [[ $fsHomeCluster != $HOME_CLUSTER ]]
    then
      # The remote cluster does not know anything about this file system.
      printErrorMsg 108 $mmcmd $remoteDevice $fsHomeCluster
    else
      # Unexpected error.
      printErrorMsg 171 $mmcmd "file system $deviceName not found" $rc
    fi
  elif [[ ($rc -eq $MM_HostDown    ||
           $rc -eq $MM_TimedOut    ||
           $rc -eq $MM_SecurityCfg ||
           $rc -eq $MM_AuthorizationFailed ||
           $rc -eq $MM_UnknownCluster)    &&
          $fsHomeCluster != $HOME_CLUSTER ]]
  then
    # Failed to connect to the remote cluster.
    [[ $rc -eq $MM_SecurityCfg ]] &&  \
      printErrorMsg 150 $mmcmd
    [[ $rc -eq $MM_AuthorizationFailed ]] &&  \
      printErrorMsg 151 $mmcmd
    printErrorMsg 105 $mmcmd $fsHomeCluster
  elif [[ $rc -eq $MM_Remotefs && $fsHomeCluster != $HOME_CLUSTER ]]
  then
    # The file system is not owned by the remote cluster.
    [[ $device != *:* ]] &&  \
      printErrorMsg 111 $mmcmd $device $remoteDevice $fsHomeCluster
    printErrorMsg 112 $mmcmd $remoteDevice $fsHomeCluster
  elif [[ $rc -eq $MM_DaemonDown ]]
  then
    # GPFS is down on this node.
    printErrorMsg 109 $mmcmd
  elif [[ $rc -eq $MM_QuorumWait ]]
  then
    # GPFS is not ready for commands.
    printErrorMsg 110 $mmcmd
  elif [[ $rc -eq $MM_ConnectionReset ]]
  then
    # An internode connection was reset.
    printErrorMsg 257 $mmcmd
  else
    # Either the command worked, or it is an unexpected error.
    if [[ -s $errMsg ]]
    then
      # Show the error messages from the daemon.
      $cat $errMsg 1>&2
    elif [[ $rc -ne 0 ]]
    then
      # tslsdisk failed.
      printErrorMsg 104 "$mmcmd" "tslsdisk $fqDeviceName"
    else
      :  # The command must have worked.
    fi  # end of if [[ -s $errMsg ]]
  fi  # end of if [[ $rc -eq $MM_FsNotFound ]]
  cleanupAndExit $rc
fi  # end of if [[ ($rc -ne $MM_DaemonDown && ... ]]
$rm -f $errMsg


#########################################################################
# We come here if the command was invoked for a local file system but
# the local daemon is not available; send the command to an active node.
#########################################################################

# Create a file with the reliable names of the nodes in the cluster.
nodeCount=$(getNodeFile $REL_HOSTNAME_Field $fsHomeCluster $mmsdrfsFile $nodefile)
if [[ $nodeCount -eq 0 ]]
then
  # The cluster is empty; there is nobody to run the command.
  printErrorMsg 171 $mmcmd "getNodeFile (nodeCount=0)" 1
  cleanupAndExit
fi

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

cleanupAndExit $rc

