#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2000,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 
# @(#)16 1.43.1.1 src/avs/fs/mmfs/ts/admin/mmlsnsd.sh, mmfs, avs_rgpfs24, rgpfs24s008a 11/22/06 15:43:42
#############################################################################
#
#  List information for the disks that belong to a GPFS cluster.
#
#  Usage:
#
#     mmlsnsd [ -a | -F | -f Device | -d "DiskName[;DiskName]" ]
#             [ -L | -m | -M | -X ] [-v]
#
#  where
#
#     -a          list all disks.  This is the default.
#
#     -F          list all disks that do not belong to any file system.
#
#     -f Device   list all disks that belong to file system Device.
#
#     -d "DiskName[;DiskName]"  list the disks in the specified list
#                 of ";"-separated disk names.
#
#     -L          display longer disk information.
#
#     -m          display the disk device name on the local node,
#                 or, if applicable, the disk device names on the
#                 primary and backup server nodes.
#
#     -M          display the disk device names on all nodes to which
#                 the disk is attached.  Caution:  This is a slow operation!
#
#     -v          display additional error information where available.
#
#     -X          display eXtended disk information.
#
#############################################################################

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

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

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

disksToShow=${tmpDir}disksToShow.${mmcmd}.$$
collectedDiskData=${tmpDir}collectedDiskData.${mmcmd}.$$

LOCAL_FILES=" $disksToShow $collectedDiskData "

# Local variables
usageMsg=428
headerPrinted=no
underline="----------------------------------------------------------"
underline="${underline}----------------------------------------------"
underline="${underline}----------------------------------------------"


# Local functions


##########################################################################
#
# Display local nsd data for the desired disks.
# This function prints the local device name on all nodes
# for all disks that were specified by the user.
#
# Input:  $1  - file containing disk data collected from the nodes
#         $2  - file containing disk data from the mmsdrfs file
#         $3  - file containing a list of nodes to check
#
##########################################################################
function showLocalDeviceInfo  # <diskDataFile> <sdrfsDiskData> <nodefile>
{
  typeset sourceFile="mmlsnsd.sh"
  [[ -n $DEBUG || -n $DEBUGshowLocalDeviceInfo ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset diskDataFile=$1
  typeset sdrfsDiskData=$2
  typeset nodeFile=$3

  typeset diskLine diskFoundOnNode diskFoundOnPrimary diskFoundOnBackup
  typeset nodeName globalName pvid localDiskName diskdataLine
  typeset primaryServer backupServer nsdSubtype remarks xRemarks

  # If this is a single node cluster, prepend the node name
  # to the front of each record in the diskDataFile, since
  # mmcommon onall does not use mmdsh for single node clusters.
  if [[ $MMMODE = single ]]
  then
    $sed "s/^/$ourNodeName: /" $diskDataFile > $tmpfile2
    $mv $tmpfile2 $diskDataFile
    checkForErrors "mv $tmpfile2 $diskDataFile" $?
  fi  # end of if [[ $MMMODE = single ]]

  # Separate the valid nsd data into one file, and any error messages
  # into another.  The error messages (if any) will be displayed later.
  $grep    getLocalNsdData $diskDataFile > $tmpfile
  $grep -v getLocalNsdData $diskDataFile > $errMsg

  # Sort the collected nsd data by the NSD id (pvid).
  $sort -t : -k 3,3 $tmpfile -o $tmpfile2
  checkForErrors "$mmcmd: sort of tmpfile" $?

  # Sort the disk data from the mmsdrfs file based on the NSD id (pvid).
  $sort -t : -k 2,2 $sdrfsDiskData -o $sdrfsDiskData
  checkForErrors "$mmcmd: sort of sdrfsDiskData" $?

  # Join the file containing the nsd data collected from the nodes
  # with the file containing the mmsdrfs data for the desired nsds;
  # the join is performed based on the NSD id (pvid) value.
  # The lines in the resulting file have the following fields:
  #   nodeName:diskName:pvid:localDiskName:primaryServer:backupServer:localNsdSubtype:nsdSubtype
  $join -1 2 -2 3 -t: -o 2.1,1.1,1.2,2.4,1.3,1.4,2.5,2.6 $sdrfsDiskData $tmpfile2 > $tmpfile
  checkForErrors "$mmcmd: join" $?
  $touch $tmpfile       # Make sure the file exists, even if it is empty.

  # Sort the disk data file by disk name and the node file
  # by node name to ensure the output of the nested loops
  # below is in the order most easily read by the user.
  $sort -t : -k 1,1 $sdrfsDiskData -o $sdrfsDiskData
  checkForErrors "$mmcmd: second sort of sdrfsDiskData" $?
  $sort -t : -k 1,1 $nodefile -o $nodefile
  checkForErrors "$mmcmd: sort of nodefile" $?

  # Loop through the disks from the mmsdrfs file to print their data.
  IFS=":"               # Change the field separator to ':'.
  exec 3<&-
  exec 3< $sdrfsDiskData
  while read -u3 diskLine
  do
    # Parse the line.
    set -f ; set -- $diskLine ; set +f
    globalName=$1
    pvid=$2
    primaryServer=$3
    backupServer=$4
    nsdSubtype=$5
    IFS="$IFS_sv"       # Restore the default IFS settings.

    diskFoundOnPrimary=false
    diskFoundOnBackup=false

    # Get the lines from the joined file that match this disk.
    # The awk script takes into account that tspreparedisk treats
    # hdisk and generic as equivalent, and either nsd subtype could
    # be present if the cluster has both AIX and Linux nodes or the
    # filesystem was imported from one OS environment to the other.
    $rm -f $tmpfile2
    $awk -F: '                                           \
      $3=="'$pvid'" &&                                   \
      ( $7=="'$nsdSubtype'"                         ||   \
        ($7=="hdisk" && "'$nsdSubtype'"=="generic") ||   \
        ($7=="generic" && "'$nsdSubtype'"=="hdisk") )    \
        {print $1":"$4":"$7":"$8 >> "'$tmpfile2'"}       \
    ' $tmpfile
    checkForErrors awk $?

    # Process the lines that were found.
    if [[ -s $tmpfile2 ]]
    then
      # Print a line of output for each line extracted from the joined file.
      # For -M we report all nodes, while for -m and -X, we only report
      # the node running the command and any server nodes.
      IFS=":"               # Change the field separator to ':'.
      exec 4<&-
      exec 4< $tmpfile2
      while read -u4 diskdataLine
      do
        # Parse the line.
        set -f ; set -- $diskdataLine ; set +f
        nodeName=$1
        localDiskName=$2
        devType=$3
        extendedRemarks=$4
        IFS="$IFS_sv"       # Restore the default IFS settings.

        if [[ -n $Mflag ||
            ( (-n $mflag || -n $Xflag) && ($nodeName = $ourNodeName   ||
                                           $nodeName = $primaryServer ||
                                           $nodeName = $backupServer   ) ) ]]
        then
          # Set the remarks field appropriately.
          if [[ $nodeName = $primaryServer ]]
          then
            diskFoundOnPrimary=true
            remarks="primary node"
          elif [[ $nodeName = $backupServer ]]
          then
            diskFoundOnBackup=true
            remarks="backup node"
          else
            remarks="directly attached"
          fi  # end of if [[ $nodeName = $primaryServer ]]

          # Append extended remarks if there are any.
          [[ -n $extendedRemarks ]] &&
            remarks="$remarks,$extendedRemarks"

          # Print the line of output.
          if [[ -n $Xflag ]]
          then
            printf " %-12s %-18s %-14s %-8s %-24s %s\n" "$globalName"  \
              "$pvid" "$localDiskName" "$devType" "$nodeName" "$remarks"
          else
            printf " %-12s %-18s %-14s %-24s %s\n" "$globalName"  \
              "$pvid" "$localDiskName" "$nodeName" "$remarks"
          fi
        fi  # end of if [[ -n $Mflag || ...

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

      done  # end of while read -u4 diskdataLine

    fi  # end of if [[ -s $tmpfile2 ]]

    # Check whether the disk is missing anywhere and report it if it is.
    if [[ -z $primaryServer ]]
    then
      # Report this directly-attached disk as missing on nodes on which
      # it was not found.  For -M we report all nodes, while for -m and -X,
      # we only report the node running the command.
      exec 4<&-
      exec 4< $nodefile
      while read -u4 nodeName
      do
        if [[ -n $Mflag ||
              ( (-n $mflag || -n $Xflag) && $nodeName = $ourNodeName ) ]]
        then
          # Check whether the disk was found on this node.
          # The awk script takes into account that tspreparedisk treats
          # hdisk and generic as equivalent, and either nsd subtype could
          # be present if the cluster has both AIX and Linux nodes or the
          # filesystem was imported from one OS environment to the other.
          diskFoundOnNode=$($awk -F: '                         \
            BEGIN { foundFlag = "false" }                      \
            $1 == "'$nodeName'" && $3 == "'$pvid'" &&          \
            ( $7=="'$nsdSubtype'"                         ||   \
              ($7=="hdisk" && "'$nsdSubtype'"=="generic") ||   \
              ($7=="generic" && "'$nsdSubtype'"=="hdisk") ) {  \
              { foundFlag = "true" }                           \
              { exit }                                         \
            }                                                  \
            END { print foundFlag }                            \
          ' $tmpfile)
          checkForErrors "awk $tmpfile" $?
          if [[ $diskFoundOnNode = false ]]
          then
            localDiskName=$UNRESOLVED
            remarks="(not found) directly attached"
            if [[ -n $Xflag ]]
            then
              devType=$UNRESOLVED
              printf " %-12s %-18s %-14s %-8s %-24s %s\n" "$globalName"  \
                "$pvid" "$localDiskName" "$devType" "$nodeName" "$remarks"
            else
              printf " %-12s %-18s %-14s %-24s %s\n" "$globalName"  \
                 "$pvid" "$localDiskName" "$nodeName" "$remarks"
            fi
          fi  # end of if [[ $diskFoundOnNode = false ]]
        fi  # end of if [[ -n $Mflag || ( (-n $mflag || -n $Xflag) && ...
      done  # end of while read -u4 nodeName
    else
      # Output a line for the primary or backup server
      # if either was specified but was not found.
      if [[ -n $primaryServer && $diskFoundOnPrimary = false ]]
      then
        localDiskName=$UNRESOLVED
        nodeName=$primaryServer
        remarks="(not found) primary node"
        if [[ -n $Xflag ]]
        then
          devType=$UNRESOLVED
          printf " %-12s %-18s %-14s %-8s %-24s %s\n" "$globalName"  \
            "$pvid" "$localDiskName" "$devType" "$nodeName" "$remarks"
        else
          printf " %-12s %-18s %-14s %-24s %s\n" "$globalName"  \
             "$pvid" "$localDiskName" "$nodeName" "$remarks"
        fi
      fi  # end of if [[ -n $primaryServer && $diskFoundOnPrimary = false ]]
      if [[ -n $backupServer && $diskFoundOnBackup = false ]]
      then
        localDiskName=$UNRESOLVED
        nodeName=$backupServer
        remarks="(not found) backup node"
        if [[ -n $Xflag ]]
        then
          devType=$UNRESOLVED
          printf " %-12s %-18s %-14s %-8s %-24s %s\n" "$globalName"  \
            "$pvid" "$localDiskName" "$devType" "$nodeName" "$remarks"
        else
          printf " %-12s %-18s %-14s %-24s %s\n" "$globalName"  \
             "$pvid" "$localDiskName" "$nodeName" "$remarks"
        fi
      fi  # end of if [[ -n $backupServer && $diskFoundOnBackup = false ]]
    fi  # end of if [[ -z $primaryServer ]]

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

  done  # end of while read -u3 diskLine do

  # If -v was specified, display any error messages
  # that were produced on the remote nodes.
  [[ -n $verboseOutput ]] && $cat $errMsg
  $rm -f $errMsg

  return 0

}  #----- end of function showLocalDeviceInfo ------------------



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


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

while getopts :ad:Ff:LMmvX OPT
do
  case $OPT in

    a) [[ -n $aflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       aflag="-$OPT"
       [[ -n $dflag || -n $Fflag || -n $fflag ]] &&  \
         syntaxError "invalidCombination" $usageMsg $aflag $dflag $Fflag $fflag
       ;;

    d) [[ -n $dflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       dflag="-$OPT"
       [[ -n $aflag || -n $Fflag || -n $fflag ]] &&  \
         syntaxError "invalidCombination" $usageMsg $dflag $aflag $Fflag $fflag
       darg="$OPTARG"
       [[ "$OPTARG" = *+([-:,${BLANKchar}${TABchar}])* ]] &&  \
         syntaxError "badSeparator_notSemicolon" $noUsageMsg
       disksToDisplay=$(print -- "$OPTARG" | $sed 's/;/ /g')
       ;;

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

    f) [[ -n $fflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       fflag="-$OPT"
       [[ -n $aflag || -n $dflag || -n $Fflag ]] &&  \
         syntaxError "invalidCombination" $usageMsg $fflag $aflag $dflag $Fflag
       farg="$OPTARG"
       ;;

    L) [[ -n $Lflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Lflag="-$OPT"
       [[ -n $Mflag || -n $mflag || -n $Xflag ]] &&  \
         syntaxError "invalidCombination" $usageMsg $Lflag $Mflag $mflag $Xflag
       ;;

    M) [[ -n $Mflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Mflag="-$OPT"
       [[ -n $Lflag || -n $mflag || -n $Xflag ]] &&  \
         syntaxError "invalidCombination" $usageMsg $Mflag $Lflag $mflag $Xflag
       ;;

    m) [[ -n $mflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       mflag="-$OPT"
       [[ -n $Lflag || -n $Mflag || -n $Xflag ]] &&  \
         syntaxError "invalidCombination" $usageMsg $mflag $Lflag $Mflag $Xflag
       ;;

    v) [[ -n $vflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       vflag="-$OPT"
       verboseOutput=yes
       ;;

    X) [[ -n $Xflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Xflag="-$OPT"
       [[ -n $Lflag || -n $Mflag || -n $mflag ]] &&  \
         syntaxError "invalidCombination" $usageMsg $Xflag $Lflag $Mflag $mflag
       ;;

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

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

    *) syntaxError "invalidOption" $usageMsg $OPTARG
       ;;

  esac
done  # end of while getopts :ad:Ff:LMmv OPT

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

# If no disk selection option was specified, default to -a.
[[ -z $aflag && -z $dflag && -z $Fflag && -z $fflag ]] &&  \
  aflag="-a"


######################################################################
# 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.  No need to lock the sdr.
######################################################################
trap pretrap2 HUP INT QUIT KILL
gpfsInitOutput=$(gpfsInit nolock)
setGlobalVar $? $gpfsInitOutput

# In single mode environment -M does not make much sense.
if [[ -n $Mflag && $MMMODE = single ]]
then
  Mflag=""
  mflag="-m"
fi


#################################################################
# If a particular file system is requested, make sure it exists.
#################################################################
if [[ -n $farg ]]
then
  findFSoutput=$(findFS "$farg" $mmsdrfsFile)
  [[ -z $findFSoutput ]] && cleanupAndExit

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

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


###############################
# Select the disks to display.
###############################
$rm -f $disksToShow $nodefile $diskNamesFile
IFS=":"         # Change the field separator to ':'.
exec 3<&-
exec 3< $mmsdrfsFile
while read -u3 sdrfsLine
do
  # Parse the line.
  set -f ; set -A v -- - $sdrfsLine ; set +f
  IFS="$IFS_sv"
  printLine=false      # assume line is not needed

  if [[ ${v[$LINE_TYPE_Field]} = $SG_DISKS ]]
  then
    # This line describes a disk.  If it matches our selection
    # criteria, add the line to disksToShow.  Otherwise, skip the line.
    if [[ -n $aflag ]]
    then
      printLine=true
    elif [[ -n $Fflag && ${v[$NODESETID_Field]} = $FREE_DISK ]]
    then
      printLine=true
    elif [[ -n $fflag && ${v[$DEV_NAME_Field]} = $fsDeviceName ]]
    then
      printLine=true
    elif [[ -n $dflag ]]
    then
      # See if this disk is one of the disks to be displayed.
      tempList=""
      displayThisDisk=no
      for diskName in $disksToDisplay
      do
        if [[ ${v[$DISK_NAME_Field]} = $diskName ]]
        then
          # The disk represented by the current SG_DISKS line
          # is one of the disks that we want to display.
          if [[ $displayThisDisk = no ]]
          then
            # We are seeing this disk for the first time.
            printLine=true
            displayThisDisk=yes
          else
            # We have already seen this name during the current iteration.
            # It must be a duplicate entry in the command line list.
            : # ignore the duplicate - do not fail the command.
          fi
        else
          # diskName does not match the name of the disk in the current
          # SG_DISKS line.  Add diskName to the temporary list.
          tempList="$tempList $diskName"
        fi  # end of if [[ ${v[$DISK_NAME_Field]} = $diskName ]]
      done  # for diskName in $disksToDisplay

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

      # Initialize the disksToDisplay list for the next iteration.
      disksToDisplay=$tempList

    else
      # This is an SG_DISKS line that does not match our selection criteria.
      : # skip this line
    fi  # end of if [[ -n $aflag ]]

    if [[ $printLine = true ]]
    then
      print_newLine >> $disksToShow
      checkForErrors "writing to file $disksToShow" $?

      # If -m or -X was specified and the disk has primary or backup servers,
      # add the servers to the list of nodes to collect data from.
      if [[ -n $mflag || -n $Xflag ]]
      then
        [[ -n ${v[$NSD_PRIMARY_NODE_Field]} ]] &&  \
          print ${v[$NSD_PRIMARY_NODE_Field]} >> $nodefile
        [[ -n ${v[$NSD_BACKUP_NODE_Field]} ]]  &&  \
          print ${v[$NSD_BACKUP_NODE_Field]}  >> $nodefile
      fi

      # If -m, -M, or -X was specified, add data for the disk
      # to a file that will be used later.
      [[ -n $mflag || -n $Mflag || -n $Xflag ]] &&  \
        print ${v[$DISK_NAME_Field]}:${v[$PVID_Field]}:${v[$NSD_PRIMARY_NODE_Field]}:${v[$NSD_BACKUP_NODE_Field]}:${v[$NSD_SUBTYPE_Field]} >> $diskNamesFile
    fi  # end of if [[ $printLine = true ]]

  elif [[ ${v[$LINE_TYPE_Field]} = $MEMBER_NODE && -n $Mflag ]]
  then
     # Generate a list of all nodes in the cluster.
     print ${v[$REL_HOSTNAME_Field]} >> $nodefile

  else
    # This is not a line that we care about.
    :  # Skip this line.
  fi  # end of if [[ ${v[$LINE_TYPE_Field]} = $SG_DISKS ]]

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

done  # end while read -u3 sdrfsLine

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


# If there are no disks matching the selection criteria,
# put out a message and give up.
if [[ ! -s $disksToShow ]]
then
  # No disks were found.
  printErrorMsg 418 $mmcmd
  cleanupAndExit 0
fi

# If -d option was used and there are still entries in the
# disksToDisplay list, tell the user that not all disks were found.
if [[ -n $disksToDisplay ]]
then
  # Some disks were not found.
  for diskName in $disksToDisplay
  do
    printErrorMsg 524 $mmcmd $diskName
  done
fi

# Finish creating the node file if -m or -X was specified.
if [[ -n $mflag || -n $Xflag ]]
then
  # Add the node running the command to the list of nodes to check.
  print $ourNodeName >> $nodefile

  # Sort the file for uniqueness so that we do not have any
  # nodes listed more than once.
  $sort -u $nodefile -o $nodefile
fi

# Print the appropriate header line.
# This depends on the specified formatting option.
if [[ -n $Lflag ]]
then
  # header "File system  Disk name  NSD volumeID  Primary node  Backup node"
  header=$(printInfoMsg 501)
  printf "\n%s\n%.${#header}s\n" "$header" "$underline"

elif [[ -n $Mflag || -n $mflag || -n $Xflag ]]
then
  if [[ -n $Xflag ]]
  then
    # header "Disk name   NSD volumeID   Device   Devtype   Node name   Remarks"
    header=$(printInfoMsg 512)
  else
    # header "Disk name   NSD volumeID   Device   Node name   Remarks"
    header=$(printInfoMsg 502)
  fi
  printf "\n%s\n%.${#header}s\n" "$header" "$underline"

  # Obtain the local nsd data from the nodes.
  $mmcommon onall $nodefile $unreachedNodes getLocalNsdData $Xflag >$collectedDiskData 2>&1

else
  # header "File system   Disk name   Primary node   Backup node"
  header=$(printInfoMsg 503)
  printf "\n%s\n%.${#header}s\n" "$header" "$underline"
fi  # end of if [[ -n $Lflag ]]

# If -m, -M, or -X was specified, sort the disk data file and call routine
# to print the output for the desired disks on all of the pertinent nodes.
if [[ -n $mflag || -n $Mflag || -n $Xflag ]]
then
  # Invoke routine to display the disk data from the nodes.
  showLocalDeviceInfo $collectedDiskData $diskNamesFile $nodefile

else
  # For options other than -m, -M, or -X, loop through the selected
  # disks one by one and print the desired information.
  IFS=":"
  exec 3<&-
  exec 3< $disksToShow
  while read -u3 diskLine
  do
    # Parse the line.
    set -f ; set -A v -- - $diskLine ; set +f
    IFS="$IFS_sv"

    # Check whether this is a free disk.
    [[ ${v[$NODESETID_Field]} = $FREE_DISK ]] &&  \
      v[$DEV_NAME_Field]="(free disk)"

    if [[ -n $Lflag ]]
    then                          # Extended disk information requested.
      # If no primary node, indicate the disk is directly-attached.
      [[ -z ${v[$NSD_PRIMARY_NODE_Field]} ]] &&  \
        v[$NSD_PRIMARY_NODE_Field]="(directly attached)"

      # Print the line of output for the disk.
      printf " %-13s %-12s %-18s %-24s %s\n"  \
          "${v[$DEV_NAME_Field]}" "${v[$DISK_NAME_Field]}" "${v[$PVID_Field]}" \
          "${v[$NSD_PRIMARY_NODE_Field]}" "${v[$NSD_BACKUP_NODE_Field]}"

    else                          # Default disk information requested.
      # If no primary node, indicate the disk is directly-attached.
      [[ -z ${v[$NSD_PRIMARY_NODE_Field]} ]] &&  \
        v[$NSD_PRIMARY_NODE_Field]="(directly attached)"

      # Print the line of output for the disk.
      printf " %-13s %-12s %-24s %s\n"  \
             "${v[$DEV_NAME_Field]}" "${v[$DISK_NAME_Field]}"  \
             "${v[$NSD_PRIMARY_NODE_Field]}" "${v[$NSD_BACKUP_NODE_Field]}"
    fi  # end of if [[ -n $Lflag ]]

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

  done  # end while read -u3 diskLine

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

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

# Add a blank line for nicer formatting.
print ""

# If any nodes could not be reached, tell the user which ones.
if [[ -s $unreachedNodes ]]
then
  # The following nodes could not be reached: . . .
  printErrorMsg 270 $mmcmd
  $cat $unreachedNodes 1>&2
fi

cleanupAndExit 0

