#!/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 
# @(#)08 1.67.1.2 src/avs/fs/mmfs/ts/admin/mmcrnsd.sh, mmfs, avs_rgpfs24, rgpfs24s005a 6/14/06 14:40:06
###############################################################################
#
#  Usage:
#    mmcrnsd -F DescFile [-v {yes | no}]
#
#  where
#    -F DescFile    specifies a file containing the disk descriptors for
#                     the network shared disks to be created
#    -v {yes | no}  verification flag; a value of "yes" specifies that the
#                     disks in DescFile should only be created if they have
#                     not been previously formatted as an NSD, while a value
#                     of "no" specifies the disks should be created regardless
#                     of whether they have been previously formatted or not.
#                     The default value is "yes".
#
###############################################################################

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

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

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

tdescfile=${tmpDir}tdescfile.${mmcmd}.$$    # temp file for descriptors

LOCAL_FILES=" $tdescfile "


# Local variables
integer highestGpfsDiskNbr
integer seqNo=0
usageMsg=410
noUsageMsg=0
hadSuccess=false


# Local functions



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


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

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

while getopts :F:v: OPT
do
  case $OPT in

    F) # node names file
       [[ -n $Farg ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Farg=$OPTARG
       if [[ ! -r $Farg ]]
       then
         # Cannot open the file
         printErrorMsg 43 $mmcmd $Farg
         cleanupAndExit
       fi
       ;;

    v) [[ -n $vflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       if [[ $OPTARG = yes || $OPTARG = no ]]
       then
         vflag="$OPTARG"
       else
         syntaxError "YesNoValue" $noUsageMsg "-$OPT"
       fi
       ;;

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

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

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

done  # end while getopts :F:v: OPT

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

[[ -z $vflag ]] && vflag=yes

# Verify the mandatory -F parameter.
[[ -z $Farg ]] && syntaxError "missingArgs" $usageMsg
descfile=$Farg
checkUserFile $descfile $descfile
[[ $? -ne 0 ]] && cleanupAndExit


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


#########################################################################
# Create a new version of the mmsdrfs file with a new generation number.
# Generate a file containing the names of the nodes in the cluster.
#########################################################################
$rm -f $nodefile $newsdrfs
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"    # 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 )  # this is the global header line
      # Increment the generation number and save the line for later output.
      newGenNumber=${v[$SDRFS_GENNUM_Field]}+1
      v[$SDRFS_GENNUM_Field]=$newGenNumber
      versionLine_A="${v[1]}:${v[2]}:${v[3]}:${v[4]}:${v[5]}:${v[6]}"
      versionLine_A="$versionLine_A:${v[7]}:${v[8]}:${v[9]}:${v[10]}"
      highestGpfsDiskNbr=${v[$HIGHEST_GPFS_DISK_NBR_Field]}
      versionLine_B="${v[12]}:${v[13]}:${v[14]}:${v[15]}:${v[16]}"
      versionLine_B="$versionLine_B:${v[17]}:${v[18]}:${v[19]}:${v[20]}"
      versionLine_B="$versionLine_B:${v[21]}:${v[22]}:${v[23]}:${v[24]}"
      printLine=false
      ;;

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

       [[ ${v[$NODE_NUMBER_Field]} = $ourNodeNumber ]] &&  \
         preferredNode=${v[$REL_HOSTNAME_Field]}
       ;;

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

  esac  # end 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 sdrfsLine

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


############################################################################
# Process the descriptors for the new disks.  Each descriptor is validated.
# If the descriptor appears to be ok, tspreparedisk is invoked to generate
# a unique physical id on the disk.  For each disk that is successfully
# processed by tspreparedisk, an SG_DISKS line is added to the sdrfs file.
############################################################################
exec 3<&-
exec 3< $descfile
$rm -f $tdescfile
while read -u3 mmDiskDesc
do
  # Skip empty lines and comment lines ('#' is the first non-white char).
  if [[ $mmDiskDesc = *([$BLANKchar$TABchar])#* ||
        $mmDiskDesc = *([$BLANKchar$TABchar])   ]]
  then
    # Preserve the line "as is" in the user's input file.
    print -- "$mmDiskDesc" >> $tdescfile
    continue
  fi

  # Append the commented-out descriptor to the temporary descriptor file.
  # If the descriptor passes all error checking, this line will be followed
  # by the converted descriptor with the assigned global name.
  print -- "# $mmDiskDesc" >> $tdescfile
  checkForErrors "writing to file $tdescfile" $?

  # Parse the disk descriptor.
  IFS=':'
  set -f ; set -- $mmDiskDesc ; set +f
  device=$1
  diskName=${device##+(/)dev+(/)}    # name stripped of /dev/ prefix
  server=$2
  backup=$3
  diskUsage=$4
  failureGroup=$5
  desiredName=$6
  storagePool=$7
  IFS="$IFS_sv"

  # Issue an informational progress message (processing disk xyz ...).
  printInfoMsg 251 $mmcmd $diskName

  # If a server node was specified, check that it is valid
  # and convert it if necessary to an admin adapter name.
  if [[ -n $server ]]
  then
    server=$(checkAndConvertNodeValue $server $REL_HOSTNAME_Field)
    [[ $? -ne 0 ]] && cleanupAndExit
  fi
  if [[ -n $backup ]]
  then
    backup=$(checkAndConvertNodeValue $backup $REL_HOSTNAME_Field)
    [[ $? -ne 0 ]] && cleanupAndExit
  fi

  # Generate a template SG_DISK line for the new disk.
  # For now, we assume that everything will check out.
  diskSize=0
  [[ -z $failureGroup ]] && failureGroup=-1
  [[ -z $diskUsage ]] && diskUsage=dataAndMetadata
  pvid=""
  primaryNsdNode=""
  backupNsdNode=""
  diskType="nsd"

  # Determine the name the nsd will be called.
  if [[ -z $desiredName ]]
  then
    highestGpfsDiskNbr=highestGpfsDiskNbr+1
    nsdName="gpfs${highestGpfsDiskNbr}nsd"
    nameSource="$cmdGenerated"
  else
    # Check whether the desired disk name conforms to naming rules.
    # If it does not, skip to the next disk descriptor.
    checkName diskName 255 "$desiredName"
    [[ $? -ne 0 ]] && continue

    # Issue an error and skip to the next descriptor
    # if the desired name begins with "gpfs".
    if [[ $desiredName = gpfs* ]]
    then
      # The desired disk name begins with "gpfs".
      printErrorMsg 505 $mmcmd $desiredName
      continue                      # Skip to the next disk descriptor.
    fi

    # Issue an error and skip to the next descriptor
    # if the desired name is already in use.
    dn=$(getDiskInfo $DISK_NAME_Field $desiredName $newsdrfs)
    rc=$?
    if [[ $rc -ne 0 && -z $dn ]]
    then
      nsdName="$desiredName"
    else
      # The desired disk name is already in use by GPFS.
      printErrorMsg 432 $mmcmd $desiredName
      continue                      # Skip to the next disk descriptor.
    fi
    nameSource="$userSpecified"
  fi

  # Create a free disk line as a starting point for the validate routine.
  freeDiskLine="$FREE_DISK:$SG_DISKS:$NO_DEVICE:$seqNo:$nsdName:$diskSize"
  freeDiskLine="${freeDiskLine}:$failureGroup:$diskUsage:$pvid:$diskType"
  freeDiskLine="${freeDiskLine}:$primaryNsdNode:$backupNsdNode::::$nameSource"
  freeDiskLine="${freeDiskLine}::$includedDisk:"

  # Check the correctness of the disk descriptor.  If everything is OK,
  # validateAndConvertNsdDescriptor will return an updated SG_DISKS line.
  validateDescriptorOutput=$(validateAndConvertNsdDescriptor "$mmDiskDesc"  \
                               $freeDiskLine $NO_DEVICE $FREE_DISK NULL)
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    # If an error was found, the validate routine issued a message.
    # We will now print the entire descriptor to help the guy some more.
    printErrorMsg 386 $mmcmd "$mmDiskDesc"
    [[ -z $desiredName ]] && highestGpfsDiskNbr=highestGpfsDiskNbr-1
    continue                        # Skip to the next disk descriptor.
  fi

  # If the descriptor seems to be OK, parse the output
  # from the validateAndConvertNsdDescriptor routine.
  set -f ; set -- $validateDescriptorOutput ; set +f
  updatedDiskLine="$1"

  # Generate and write a unique pvid on sector 2 of the disk.
  # If an NSD server is specified, invoke the function on that node.
  # This call will also return the type of the underlying disk.
  if [[ -z $server || $server = $ourNodeName ]]
  then
    invokedNode=$ourNodeName
    createNsdOutput=$(createNsd $diskName $vflag mmcrnsd NULL)
    rc=$?
  else
    invokedNode=$server
    createNsdOutput=$(run on1 $server  \
                      createNsd $diskName $vflag mmcrnsd NULL)
    rc=$?
  fi

  # The output of createNsd consists of a one-word NSD subtype value,
  # followed by the output from tspreparedisk.  Separate the two parts.
  nsdSubtype=${createNsdOutput%% *}
  tspreparediskOutput=${createNsdOutput#* }

  # Set the value for the nsd subtype diskname if the nsd subtype
  # is lv, vsd or file; this name is persistent across the nodes.
  nsdSubtypeDiskname=""
  [[ $nsdSubtype = lv || $nsdSubtype = vsd || $nsdSubtype = file ]] &&  \
    nsdSubtypeDiskname=$diskName

  # Parse the output from the tspreparedisk command.
  IFS=":"
  set -f ; set -- $tspreparediskOutput ; set +f
  magicWord=$1
  rc2=$2
  pvid=$3
  primaryLocalName=$4
  status=$5
  diskSize=$6
  oldPvid=$8
  IFS="$IFS_sv"

  if [[ $rc  -ne 0 || $nsdSubtype = error ||
        $rc2 -ne 0 || $magicWord != tspreparedisk ]]
  then
    # Try to make sense of the error.
    if [[ $nsdSubtype = error ]]
    then
      : # determineNsdSubtype failed.  Messages were already issued.

    elif [[ $magicWord = tspreparedisk ]]
    then
      # tspreparedisk was executed but was not happy about something.
      if [[ $rc2 = 1 ]]
      then
        # Permission was denied for disk.
        printErrorMsg 523 $mmcmd $device
      elif [[ $rc2 = 2 ]]
      then
        # The disk was not found.
        printErrorMsg 524 $mmcmd $device
      elif [[ $rc2 = 5 ]]
      then
        # I/O error
        printErrorMsg 525 $mmcmd $device
      elif [[ $rc2 = $MM_DeviceNotFound ]]
      then
        # A return code of ENODEV was returned for the disk.
        printErrorMsg 187 $mmcmd $invokedNode $device
      elif [[ $rc2 = 27 ]]
      then
        # The disk is too large.
        printErrorMsg 508 $mmcmd $device
      elif [[ $rc2 = 50 ]]
      then
        # The disk is fenced out.
        printErrorMsg 395 $mmcmd $invokedNode $device
      else
        # Show the result from tspreparedisk.
        print -u2 "$tspreparediskOutput"
        # Unexpected error from tspreparedisk
        printErrorMsg 171 "$mmcmd" "tspreparedisk $diskName" $rc2
      fi  # end if [[ $rc2 = 1 ]]
    else
      # Something is really wrong.  Output error information if any.
      [[ -n $createNsdOutput ]] && print -u2 "$createNsdOutput"
    fi  # end if [[ $nsdSubtype = error ]]

    # Issue "failed while processing disk descriptor xyz" message.
    [[ -z $server ]] && server=$ourNodeName
    printErrorMsg 411 $mmcmd "$mmDiskDesc" $server

    # We will process the rest of the descriptors to create
    # NSDs for those that are good and to flush out as many
    # errors with the descriptors as possible.
    [[ -z $desiredName ]] && highestGpfsDiskNbr=highestGpfsDiskNbr-1
    continue                        # Skip to the next disk descriptor.
  fi  # end if [[ $rc  -ne 0 || $nsdSubtype = error ||  ...

  if [[ $status = old ]]
  then
    # The tspreparedisk command discovered that this is an already-defined NSD.

    # Find the SG_DISKS line for the disk and remove it from the mmsdrfs file.
    # Note that it is possible that there is no SG_DISKS line for this disk.
    # This can happen if a disk is forcefully deleted (mmdelnsd -p) from the
    # mmsdrfs file but the pvid was not destroyed because the disk was not
    # available, or a previous mmcrnsd could have failed after tspreparedisk
    # successfully completed but before the information could be committed.
    $rm -f $tmpfile
    existingDiskLine=$($awk -F: '                                    \
      # If the line is for our disk, assign it to existingDiskLine.  \
      # Do not put the line in the temp file.                        \
      /':$SG_DISKS:'/ {                                              \
        if ($'$PVID_Field' == "'$oldPvid'") {                        \
          { print $0 }                                               \
          { next }                                                   \
        }                                                            \
      }                                                              \
      # Write all other lines to tmpfile.                            \
      { print $0 >> "'$tmpfile'" }                                   \
    ' $newsdrfs)

    # Prepare the newsdrfs file for the next iteration.
    # It will contain all of its original lines except the line
    # that describes the disk that is currently being processed.
    $mv $tmpfile $newsdrfs
    checkForErrors "mv $tmpfile $newsdrfs" $?

    # Find the global name of the disk.
    IFS=":"
    set -f ; set -- $existingDiskLine ; set +f
    IFS="$IFS_sv"
    existingGlobalName=$5

    if [[ $vflag = yes ]]
    then
      # The user requested existing disks to be flagged as an error.
      printErrorMsg 412 $mmcmd "$mmDiskDesc" $existingGlobalName
      [[ -z $desiredName ]] && highestGpfsDiskNbr=highestGpfsDiskNbr-1

      # Put the old line back in the file and go to the next descriptor.
      if [[ -n $existingDiskLine ]]
      then
        print -- $existingDiskLine >> $newsdrfs
        checkForErrors "writing to file $newsdrfs" $?
      fi
      continue
    fi

    # The user specified that existing disks are acceptable (-v no).
    # Was a line for the disk found in the mmsdrfs file?
    if [[ -n $existingDiskLine ]]
    then
      # An SG_DISKS line for the disk was found.
      # Did the user specify a name for the nsd?
      if [[ -n $desiredName ]]
      then
        # Use the new nsd name specified by the user.
        nsdName=$desiredName
      else
        # Reuse the old nsd name and un-increment the highest disk number.
        nsdName=$existingGlobalName
        highestGpfsDiskNbr=highestGpfsDiskNbr-1
      fi
    else
      # No SG_DISKS line was found for this disk; treat the disk as new.
      status=new
    fi  # end of if [[ -n $existingDiskLine ]]

  fi  # end of if [[ $status = old ]]

  # At this point updatedDiskLine contains the SG_DISKS information
  # for a disk which is successfully defined as an NSD by tspreparedisk.
  IFS=":"
  set -f ; set -A v -- - $updatedDiskLine ; set +f
  IFS="$IFS_sv"

  # Update descriptor values that might have been set by the validate function.
  failureGroup=${v[$FAILURE_GROUP_Field]}
  diskUsage=${v[$DISK_USAGE_Field]}

  # Add the missing or updated information.
  v[$DISK_NAME_Field]=$nsdName
  v[$DISK_SIZE_Field]=$diskSize
  v[$PVID_Field]=$pvid
  v[$NSD_SUBTYPE_Field]=$nsdSubtype
  v[$NSD_SUBTYPE_DISKNAME_Field]=$nsdSubtypeDiskname
  updatedDiskLine=$(print_newLine)

  # If a backup NSD node is specified, run the tspreparedisk command
  # on that node to verify that the disk is properly connected.
  if [[ -n $backup ]]
  then
    if [[ $nsdSubtype = vsd ]]
    then
      # Attempt to write to sector 2 of the disk from the backup
      # server in order to determine whether the disk is accessible.
      # (This will cause the backup server to be unfenced from the
      # vsd if necessary.)
      if [[ $backup = $ourNodeName ]]
      then
        invokedNode=$ourNodeName
        createNsdOutput=$(createNsd $diskName yes mmcrnsd vsd)
        rc=$?
      else
        invokedNode=$backup
        createNsdOutput=$(run on1 $backup  \
                          createNsd $diskName yes mmcrnsd vsd)
        rc=$?
      fi

      # The output of createNsd consists of a one-word NSD subtype value
      # followed by the tspreparedisk output.  Extract the tspreparedisk output.
      tspreparediskOutput=${createNsdOutput#* }
    else
      if [[ $backup = $ourNodeName ]]
      then
        invokedNode=$ourNodeName
        tspreparediskOutput=$($tspreparedisk -p $pvid -t $nsdSubtype)
        rc=$?
      else
        invokedNode=$backup
        tspreparediskOutput=$(run on1 $backup adminCmd  \
                               tspreparedisk -p $pvid -t $nsdSubtype)
        rc=$?
      fi
    fi

    # Parse the output from the tspreparedisk command.
    IFS=":"
    set -f ; set -- $tspreparediskOutput ; set +f
    magicWord=$1
    rc2=$2
    backupPvid=$3
    backupLocalName=$4
    status=$5
    IFS="$IFS_sv"

    if [[ $rc  -ne 0 || $magicWord != tspreparedisk ||
          $rc2 -ne 0 || $pvid != $backupPvid ]]
    then
      # Try to make sense of the error.
      if [[ $magicWord = tspreparedisk ]]
      then
        # tspreparedisk was executed but was not happy about something.
        if [[ $rc2 = 1 ]]
        then
          # Permission was denied for disk.
          printErrorMsg 523 $mmcmd $device
        elif [[ $rc2 = 2 ]]
        then
          # The disk was not found.
          printErrorMsg 524 $mmcmd $device
        elif [[ $rc2 = 5 ]]
        then
          # I/O error
          printErrorMsg 525 $mmcmd $device
        elif [[ $rc2 = $MM_DeviceNotFound ]]
        then
          # A return code of ENODEV was returned for the disk.
          printErrorMsg 187 $mmcmd $invokedNode $device
        elif [[ $rc2 = 50 ]]
        then
          # The disk is fenced out.
          printErrorMsg 395 $mmcmd $invokedNode $device
        elif [[ $pvid != $backupPvid ]]
        then
          print -u2 "$mmcmd: Mismatched disk PVIDs"
        else
          # Show the result from tspreparedisk.
          print -u2 "$tspreparediskOutput"
          # Unexpected error from tspreparedisk -p
          printErrorMsg 171 "$mmcmd"  \
            "tspreparedisk -p $pvid -t $nsdSubtype" $rc2
        fi  # end if [[ $rc2 = 1 ]]
      else
        # Something is really wrong.  Output error information if any.
        [[ -n $tspreparediskOutput ]] && print -u2 "$tspreparediskOutput"
      fi  # end if [[ $magicWord = tspreparedisk ]]

      # Failed while processing disk descriptor
      printErrorMsg 411 $mmcmd "$mmDiskDesc" $backup
      [[ -z $desiredName ]] && highestGpfsDiskNbr=highestGpfsDiskNbr-1
      continue                        # Skip to the next disk descriptor.
    fi  # end if [[ $rc -ne 0 || $magicWord != tspreparedisk || ... ]]

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

  # At this point, the descriptor passed all checks successfully,
  # the disk was formatted as an NSD, and updatedDiskLine contains
  # the SG_DISKS line that needs to go in the mmsdrfs file.
  print -- $updatedDiskLine >> $newsdrfs
  checkForErrors "writing to file $newsdrfs" $?

  # Append the good modified descriptor to the temporary descriptor file.
  print -- "$nsdName:::$diskUsage:$failureGroup::$storagePool" >> $tdescfile
  checkForErrors "writing to file $tdescfile" $?

  # There is at least one good disk descriptor.
  hadSuccess=true

done  # end of while read -u3 mmDiskDesc


# If all of the descriptors had some kind of error and not a single
# disk was created, give up.  Messages have already been issued.
[[ $hadSuccess != true ]] &&  \
  cleanupAndExit


####################################################################
# We have now finished processing descriptors.
# Create a new version line with an updated HIGHEST_GPFS_DISK_NBR
# value that reflects the highest disk number currently assigned
# and add it back to the mmsdrfs file.  Commit the changes.
####################################################################
newVersionLine="$versionLine_A:$highestGpfsDiskNbr:$versionLine_B"
print -- $newVersionLine >> $newsdrfs
checkForErrors "writing to file $newsdrfs" $?

# Sort the new sdrfs file.
LC_ALL=C $SORT_MMSDRFS $newsdrfs -o $newsdrfs

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


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


#############################################################################
# Move the updated (and any commented out) descriptors into the user's file.
#############################################################################
$mv $tdescfile $descfile
checkForErrors "writing to file $descfile" $?


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


###################################################
# If installed, invoke the syncfsconfig user exit.
###################################################
if [[ -x $syncfsconfig ]]
then
   print -- "$mmcmd:  Starting $syncfsconfig ..."
   $syncfsconfig
   print -- "$mmcmd:  $syncfsconfig finished."
fi

cleanupAndExit 0

