#!/bin/ksh
#
# Program: fbr
# Current Revision: 1.26
# Last Modification Date: 05/03/25
# Last Modified by: %N%
#
# Copyright 2002,2003 Sun Microsystems, Inc.  All Rights Reserved
# Use is subject to license terms.
#
# FBR Script
#
# This script provides the ability to archive (backup) or
# recover (restore) critical personality files for the
# Storage Service Processor from the flash USB disk device
# attached to the Service Processor Core USB port.
# 
# The user interface requires arguments describing
# the action to be performed, backup or restore.  Verbose mode
# is activated with the -v option.
#
# fbr -b [ -f] [-v]
# fbr -r [-v]
#
# Inputs:  /opt/SUNWsefbru/files/backup_list
#          /opt/SUNWsefbru/files/hi_pri_list
#
# The hi_pri_list contains a list of high priority files that trigger a
# backup if they change.  When a backup is triggered, the files in
# the backup_list and the hi_pri_list are both archived.
#
# Before a restore is performed, a check is made to determine if the
# platform and SP version on the USB device is correct.  The requirement
# is that the SP version must be greater than or equal to the version
# on the USB device.
#
# Return Codes:
#  0 = Success
#  1 = Missing/Invalid Arguments
#  2 = Usage Error
#  3 = No Personality file on flash disk
#  4 = Invalid Platform or Version
#  5 = USB Platform could not be determined
#  6 = USB flash disk not mountable
#  7 = No device entry found for USB flash disk
#  8 = USB flash disk mount failed
#  9 = Local Platform could not be determined
# 10 = USB flash disk write protected
# 11 = USB flash disk missing
#
#  Function Definitions
#
Usage () {
  fbr_usage1=" USAGE: $PGMNAME -b [-f] [-v]"
  fbr_usage2="        $PGMNAME -r [-v]"
  fbr_usage3=" OPTIONS"
  fbr_usage4="        -b Backs up the Personality to the flash disk"
  fbr_usage5="        -f Force a backup of the Personality to the flash disk"
  fbr_usage6="        -r Restores the Personality from the flash disk"
  fbr_usage7="        -v Verbose mode"

  print ""
  print "$fbr_usage1"
  print "$fbr_usage2"
  print ""
  print "$fbr_usage3"
  print ""
  print "$fbr_usage4"
  print "$fbr_usage5"
  print "$fbr_usage6"
  print "$fbr_usage7"
}
#
#  Protect against a dead running file that would keep the fbr process
#  from running due to crash, interrupt, etc.
#
cleanup() {
  rm -f $thisFBR
  tpid=${thisFBR#*.running}
  rm -f /tmp/*$tpid.tar*
  exit $1
}
#
#  Determine the USB Flash disk device entry
#
GetUSBDevice() {
  for i in `ls /dev/rdsk`; do
    DEV=`print $i`
    if [ "$DEV" != "" ]; then
      if [ `print $i | grep -c "s2"` -gt 0 ]; then
        RDEV=/dev/rdsk/${i}  # Raw Drive for Inquiry
        FDEV=/dev/dsk/${i}   # Formatted Drive for mount
        PHYS_PATH_LINK=`ls -l $RDEV | grep usb` 
        if [ "$PHYS_PATH_LINK" !=  "" ]; then
          FLASHDRIVE_FOUND=TRUE
          break
        fi
      fi
    fi
  done
  if [ "$FLASHDRIVE_FOUND" = "FALSE" ]; then
    if (( VERBOSE )); then
      print "No device entry found for the USB flash disk"
    fi
    $LOGGER PERSISTENCE_IO_ERROR_ON_STORAGE
    logger -p local5.notice -t FBR "USB flash disk missing"
    return 7
  fi
}
#
# Mount an identified USB flash disk device
#
MountFlashDisk() {
  mountFlag=$1
  STAT=`fsck -m $RDEV >/tmp/fbr$$.out 2>&1; print $?`
#
# The USB device has a dip switch which can be set to lock position.
# When it is set the device gets a mount error on a UNIX system for
# a read/write mount.  Determine if the device is write protected so
# StorADE can report the diagnostic.
#
  let writeProtected="$(grep -c "NO WRITE" /tmp/fbr$$.out | cut -f2 -d:)"
  rm /tmp/fbr$$.out
  case $STAT in
    0|33)  ;;
    32)
      fsck -F ufs -y $FDEV ;;
    *)   
      if [ $OP = "backup" ]; then
        STAT=`mkfs -F ufs $RDEV 30720 32 64 8192 1024 16 10 3 2048 t 0 -1 8 16 >/dev/null 2>&1; print $?`
        if [ $STAT -ne 0 ]; then
          $LOGGER PERSISTENCE_IO_ERROR
          if [ $STAT -eq 32 ]; then
            if (( VERBOSE )); then
              print "USB flash disk missing"
            fi
            logger -p local5.notice -t FBR "USB flash disk missing"
            ret=11
          else
            if (( VERBOSE )); then
              print "USB device not mountable"
            fi
            logger -p local5.notice -t FBR "USB flash disk not mountable"
            ret=6
          fi
          return $ret
        fi
      fi;;
  esac

  if [ ! -d $FLASH_DIR ]; then
    mkdir $FLASH_DIR
    chmod 0755 $FLASH_DIR
  fi
#
# For restore mount the device read only and this will save some writes
# to the USB device and help increase its longevity.
#
  if [[ $mountFlag = "restore" ]]; then
    STAT=`mount -r $FDEV $FLASH_DIR >/dev/null 2>&1; print $?`
  else
    if (( $writeProtected )); then
      if (( VERBOSE )); then
        print "USB device not mountable, write protected"
      fi
      logger -p local5.notice -t FBR "USB flash disk write protected"
      return 10
    else
      STAT=`mount $FDEV $FLASH_DIR >/dev/null 2>&1; print $?`
    fi
  fi
  if [ $STAT -ne 0 ]; then 
    if (( VERBOSE )); then
      print "Mount of USB device failed: $STAT"
    fi
    if [ $STAT -eq 32 ]; then
      logger -p local5.notice -t FBR "USB flash disk mount failed"
    fi
    $LOGGER PERSISTENCE_IO_ERROR
    return 8
  fi
}
#
# main()
#
#
#  Protect against a dead running file that would keep the fbr process
#  from running due to crash, interrupt, etc.
#
fbrRunning=/opt/SUNWsefbru/bin/.running
if ls $fbrRunning* > /dev/null 2>&1 ; then
  for i in `ls -1 $fbrRunning*`; do
    tpid=${i#*.running}
    if ps -p $tpid > /dev/null 2>&1; then
      logger -p local5.notice -t FBR "fbr already in progress"
      print "fbr already in progress"
    else
      logger -p local5.notice -t FBR "Cleaning up dead fbr files.  fbr must be restarted."
      print "Cleaning up dead fbr files.  fbr must be restarted."
      rm -f $i
      rm -f /tmp/*$tpid.tar*
    fi
  done
else
  trap 'cleanup' INT TERM
  fbrRunning=/opt/SUNWsefbru/bin/.running
  thisFBR=$fbrRunning$$
  touch $thisFBR
  PKGNAME=SUNWsefbru
  PKGBASE=$INSTALL_BASE/opt
  PGMNAME=`basename $0`
  LOGGER=$INSTALL_BASE/opt/se6x20/bin/log
  IAM_CMD=/usr/bin/iam

  HP_FILE_LIST=$PKGBASE/$PKGNAME/files/hi_pri_list
  FBR_FILE_LIST=$PKGBASE/$PKGNAME/files/backup_list
  CACHE_DIR=$PKGBASE/$PKGNAME/files
  WORK_DIR=/tmp
  TARGET_FILENAME=SP_Personality
  HP_FILENAME=hi_pri_list

  OP=
  let VERBOSE=0
  let FORCE=0
  let DO_BACKUP=0

  FLASH_DIR=/flash
  FLASHDRIVE_FOUND=FALSE
#
# Verify that an argument list has been specified
#
  if [ $# -eq 0 ]; then
    DATE=`date '+%m/%d/%y %H:%M:%S'`
    print "$DATE: $PGMNAME: OPER_ERROR: Missing/Invalid Argument[s]"
    Usage
    cleanup 1
  fi
#
# Parse arg list and validate request
#
  while getopts ":bfrv" opt; do
    case $opt in
       b)  if [ -z $OP ]; then
             OP=backup
           else
             Usage
             cleanup 2
           fi;;
       f)  let FORCE=1;;
       r)  if [ -z $OP ]; then
             OP=restore
           else
             Usage
             cleanup 2
           fi;;
       v)  let VERBOSE=1;;
      \?)  Usage
           cleanup 2;;
    esac
  done
  if [ -z $OP ]; then
    Usage
    cleanup 2
#
# Do a backup
#
  elif [ $OP = "backup" ]; then
#
# Tar up the high priority files 
#
    flist=`cat $HP_FILE_LIST`
    tar -cpf $WORK_DIR/$HP_FILENAME$$.tar $flist >/dev/null 2>&1
#
# Check to see if any of the high priority files have changed since the
# last backup.  If they have, then trigger the backup to happen.
#
    if (( VERBOSE )); then
      print "Checking for personality changes"
    fi
    if [[ -a $CACHE_DIR/$HP_FILENAME.tar ]]; then
      CMPSTAT=`cmp $WORK_DIR/$HP_FILENAME$$.tar $CACHE_DIR/$HP_FILENAME.tar >/dev/null 2>&1; print $?`
      if [[ $CMPSTAT -ne 0 ]]; then
        cp $WORK_DIR/$HP_FILENAME$$.tar $CACHE_DIR/$HP_FILENAME.tar
        let DO_BACKUP=1
      else
#
# Fix for Bug ID 4982196
#
# It is possible when the USB flash is removed from the SP, that an update 
# occurs with the absence of USB flash in the SP.  In this case, only the 
# hi_pri_list.tar file is created by the fbr command and hi_pri_list.tar 
# and SP_Personality.tar.Z become out of sync.
#
# Determine if the hi_pri_list archive is newer than the cached personality.
# If it is, trigger a backup and resync the files.
#
        FINDSTAT=`find $CACHE_DIR -newer $CACHE_DIR/$TARGET_FILENAME.tar.Z 2> /dev/null | grep $HP_FILENAME.tar > /dev/null 2>&1; print $?`
        if [ $FINDSTAT -eq 0 ]; then
#
# Sync up the files
#
          cp $WORK_DIR/$HP_FILENAME$$.tar $CACHE_DIR/$HP_FILENAME.tar
          let DO_BACKUP=1
        fi
      fi
    else
#
# The high priority files were not already cached, so cache them
#
      cp $WORK_DIR/$HP_FILENAME$$.tar $CACHE_DIR/$HP_FILENAME.tar
      let DO_BACKUP=1
    fi
#
# Check to see if the personality has not been backed up for 24 hours.
# The requirement is to backup every 24 hours if nothing has changed.
# If it hasn't, then trigger the backup to happen.
#
    FINDSTAT=`find $CACHE_DIR -type f -mtime -1 | grep $TARGET_FILENAME > /dev/null 2>&1; print $?`
    if [ $FINDSTAT -ne 0 ]; then 
      let DO_BACKUP=1
    fi
    if (( FORCE )); then
      let DO_BACKUP=1
    fi
    if (( DO_BACKUP )); then
      if (( VERBOSE )); then
        print "Generating list of files that define the system's personality"
      fi
      LIST=`cat $HP_FILE_LIST; cat $FBR_FILE_LIST`
#
#  Create Personality TAR file of backup file list contents
#
      if (( VERBOSE )); then
        print "Creating personality tar file"
      fi
      tar -cpf $WORK_DIR/$TARGET_FILENAME$$.tar $LIST >/dev/null 2>&1
#
#  Compress Personality file to reduce storage space
#
      if (( VERBOSE )); then
        print "Compressing personality tar file"
      fi
      compress -f $WORK_DIR/$TARGET_FILENAME$$.tar
      if (( VERBOSE )); then
        print "Finding device entry for USB device"
      fi
      GetUSBDevice
      ret=$?
      if [ $ret -eq 0 ]; then
        if (( VERBOSE )); then
          print "Mounting USB drive"
        fi
        MountFlashDisk
        ret=$?
        if [ $ret -eq 0 ]; then
          if (( VERBOSE )); then
            print "Personality changed, updating cache"
          fi
          cp $WORK_DIR/$TARGET_FILENAME$$.tar.Z $CACHE_DIR/$TARGET_FILENAME.tar.Z
          cp $WORK_DIR/$TARGET_FILENAME$$.tar.Z $FLASH_DIR/$TARGET_FILENAME.tar.Z
          ret=$?
          if [[ $ret -ne 0 ]]; then
            if [[ ret -eq 2 ]]; then
              logger -p local5.notice -t FBR "USB flash disk file system full"
            else
              logger -p local5.notice -t FBR "USB flash disk write error"
            fi
            umount $FLASH_DIR
            print "Backup Command Failed ($ret)"
            cleanup $ret
          fi
#
# Place the current SP version information on the USB device.  This will
# be used to determine if a restore is being performed on the correct
# platform with a proper version.
#
          $IAM_CMD > $FLASH_DIR/iam.txt
          umount $FLASH_DIR
        else
          print "Backup Command Failed ($ret)"
          cleanup $ret
        fi
      else
        print "Backup Command Failed ($ret)"
        cleanup $ret
      fi
      print "Backup Command Successful"
    else
#
# If there is no backup required because the persistent file is up to
# date, the USB device has not been accessed.  If the device is not
# connected then the successful message is not really a true indication
# of the USB state.  Therefore a different messsage is necessary.
#
      print "No Backup Required"
    fi
#
# Do a restore
#
  elif [ "$OP" = "restore" ]; then
    if (( VERBOSE )); then
      print "Restoring personality from flash disk"
    fi
    if (( VERBOSE )); then
      print "Finding device entry for flash disk"
    fi
    GetUSBDevice
    ret=$?
    if [ $ret -eq 0 ]; then
      if (( VERBOSE )); then
        print "Mounting USB drive"
      fi
      MountFlashDisk restore
      ret=$?
      if [ $ret -eq 0 ]; then
#
#  Copy Personality file from flash disk to cache area
#
        if (( VERBOSE )); then
          print "Copying personality from flash disk and restoring"
        fi
        if [ -f $FLASH_DIR/$TARGET_FILENAME.tar.Z ]; then
#
# Check the persistent data to see if the restore is to the same platform and
# the release version is greater than or equal to what is on the USB device.
#
# The assumption is that the output from the iam command will not change.
#
# The format of the output for the iam command is:
#
#         Sun StorEdge(tm) 6920
#            Version: 2.0.6
#      Build Date: November  3, 2003
#
# If setup command was not run with the initialize option the output of
# the iam command is:
#
#      Unable to determine StorEdge(tm) Service Processor release!
#
#
          was_setup=`head -n 1 $FLASH_DIR/iam.txt | grep -i determine`
          if [[ -z $was_setup ]]; then
            let usb_platform=`head -n 1 $FLASH_DIR/iam.txt | awk '{print $3}'`
          else
#
# This means that the system was never initialized via setup
#
            print "Platform could not be determined"
            print "Restore Command Failed (5)"
            umount $FLASH_DIR
            cleanup 5
          fi

          usb_version=`cat $FLASH_DIR/iam.txt | grep -i version | cut -f2 -d:`
          let usb_major=`print $usb_version | cut -f1 -d.`
          let usb_mid=`print $usb_version | cut -f2 -d.`
          let usb_minor=`print $usb_version | cut -f3 -d.`

          was_setup=`$IAM_CMD | head -n 1 | grep -i determine`
          if [[ -z $was_setup ]]; then
            let local_platform=`$IAM_CMD | head -n 1 | awk '{print $3}'`
          else
#
# This means that the system was never initialized via setup
#
            print "Local Platform could not be determined"
            print "Restore Command Failed (9)"
            umount $FLASH_DIR
            cleanup 9
          fi
          local_version=`$IAM_CMD | grep -i version | cut -f2 -d:`
          let local_major=`print $local_version | cut -f1 -d.`
          let local_mid=`print $local_version | cut -f2 -d.`
          let local_minor=`print $local_version | cut -f3 -d.`


          if (( $local_major < $usb_major ||
                $local_mid < $usb_mid ||
                $local_minor < $usb_minor ||
                $local_platform != $usb_platform )); then
          
            print "Cannot restore: Invalid platform or version"
            print "Local platform is $local_platform, restore platform is $usb_platform"
            print "Local version is $local_version, restore version is $usb_version"
            print "Restore Command Failed (4)"
            umount $FLASH_DIR
            cleanup 4
          fi

          cp $FLASH_DIR/$TARGET_FILENAME.tar.Z $CACHE_DIR/$TARGET_FILENAME.tar.Z
#
#  Restore Personality files to Service Processor
#
          zcat $CACHE_DIR/$TARGET_FILENAME.tar.Z | tar -xpf - >/dev/null 2>&1
        else
          if (( VERBOSE )); then
            print "No personality file on flash disk"
          fi
          $LOGGER PERSISTENCE_IO_ERROR_ON_FILE $FLASH_DIR/$TARGET_FILENAME.tar.Z
          print "Restore Command Failed (3)"
          umount $FLASH_DIR
          cleanup 3
        fi
#
#  Unmounting flash disk device
#
        umount $FLASH_DIR
      else
        print "Restore Command Failed ($ret)"
        cleanup $ret
      fi
    else
      print "Restore Command Failed ($ret)"
      cleanup $ret
    fi
    print "Restore Command Successful"
  fi
fi
cleanup 0
