#!/bin/sh

#-------------------------------------------------------------------------------
# restore.sh
# 
# This shell script is invoked to perform a restore of the HSC backup data.
# This script should be executed every time the system reboots.  If no
# "indicator" file is present, the script exits and no file restoration is done.
# 
# Usage: 'restore'
#
# Return Codes:
# 1 - Error mounting the upgrade partition
# 2 - Error Copying from the media
# 3 - Archive not present
# 4 - Error unmounting the media
# 5 - Other Errors
# 6 - DVD drive failure
# 7 - No media inserted
# 8 - Unformatted media
# 9 - Incorrect label on media
# 10 - tar error extracting relative to working directory
# 11 - unable to create staging directory
# 12 - no need to run a restore
# 13 - unable to generate the excluded shared object file list
# 14 - tar error extracting non-*.so files relative to / dir
# 15 - staged file move/copy error

#-------------------------------------------------------------------------------
# directory and filename which records the backup actions.
#-------------------------------------------------------------------------------
LOGDIR=/var/hsc/log
LOG=$LOGDIR/restore.log

LOG_ERROR_LOG=/tmp/restore.log
ARCHIVE_LOG=/tmp/archive.log

# stdout of 'mount' command directed to /tmp/mount_output+PID
MOUNT_OUTPUT=/tmp/mount_output$$

#-------------------------------------------------------------------------------
# mount point: the mount point where the backup data is contained
#-------------------------------------------------------------------------------
MOUNTPOINT=/media/cdrom

#-------------------------------------------------------------------------------
# location on the HMC where a remote archive file can be found
#-------------------------------------------------------------------------------
REM_ARCHIVE_LOCATION=/dump

#-------------------------------------------------------------------------------
# the filename of the backup archive
#-------------------------------------------------------------------------------
ARCHIVENAME=backuphdr.tgz
ARCHIVE=$MOUNTPOINT/$ARCHIVENAME

#-------------------------------------------------------------------------------
# the (hard drive) media mount point where the indicator file, created by the
# CD re-install process, is stored.  There should be an entry for this
# mountpoint in /etc/fstab.  Also set the Linux kernel mount point location
#-------------------------------------------------------------------------------
UPGRADE_MOUNTPOINT=/mnt/upgrade

#-------------------------------------------------------------------------------
# the indicator file. If it exists, execute 'tar' cmd to restore the backup data
#-------------------------------------------------------------------------------
INDFILE=iqybcrit.dat
REMOTE_INDFILE=rmtbcrit.dat
IQYBCRIT=$UPGRADE_MOUNTPOINT/$INDFILE
RMTBCRIT=$UPGRADE_MOUNTPOINT/$REMOTE_INDFILE

#-------------------------------------------------------------------------------
# Initialize internal script variables used for archive restore type
#-------------------------------------------------------------------------------
USE_REMOTE_BACKUP=0
USE_LOCAL_BACKUP=0

#-------------------------------------------------------------------------------
# Flag to indicate a critical error has occurred during 'tar' file processing
#-------------------------------------------------------------------------------
ARCHIVEFAIL=0

#-------------------------------------------------------------------------------
# Flags/variables used for error processing
#-------------------------------------------------------------------------------
let CP_INDICATOR=0
let MV_INDICATOR=0
 
#
# common exit point for script
#
exit_cleanup() {
    rm -f $MOUNT_OUTPUT
    
    if [ "$USE_REMOTE_BACKUP" = "1" ]; then
        rm -f $RMTBCRIT
        rm -f $REM_ARCHIVE_LOCATION/HMCBackup*
        echo "remote archive indicator file and archive file have been removed from this HMC" >> $LOG
    fi
    if [ "$USE_LOCAL_BACKUP" = "1" ]; then
       /sbin/hdparm -d0 $DVD_DEV >/dev/null 2>&1
    fi
    
    umount $UPGRADE_MOUNTPOINT

    if [ $ARCHIVEFAIL -ne 0 ]; then
        echo "!potential critical archive failure!" >> $LOG
        echo "archive failure error code is: $ARCHIVEFAIL" >> $LOG
        echo "task completed on `date`, return status = 99" >> $LOG
        exit 99
    else
        echo "task completed on `date`, return status = $1" >> $LOG
    fi
    exit $1
}  



# Check if the directory for the log file exists.
if [ ! -d $LOGDIR ]; then
    echo "=================================================================" > $LOG_ERROR_LOG
    echo -e "Restore task log for `date`." >> $LOG_ERROR_LOG
    echo "Restore task log directory, <$LOGDIR>, does not exist. Program exiting" >> $LOG_ERROR_LOG
    exit 5
fi


# Just in case we have NLS troubles reading system information...
LANG=en_US
export LANG


#-------------------------------------------------------------------------------
# Start log to record restore actions. Keep one archive copy since a reboot
# may be done and this current log file will be lost
#-------------------------------------------------------------------------------
if [ -f $LOG ]; then
    mv $LOG $LOG.1
fi
echo -e "Restore log for `date`\n" > $LOG

#-------------------------------------------------------------------------------
# Mount the media where the indicator file should be located
#-------------------------------------------------------------------------------
mount -v $UPGRADE_MOUNTPOINT > $MOUNT_OUTPUT 2>&1
if [ $? -ne 0 ]; then

    #----------------------------------------------------------------------------
    # already mounted?  Not an error, but a non-zero return code.  Continue
    #----------------------------------------------------------------------------
    if grep "already mounted" $MOUNT_OUTPUT; then
        echo "The mountpoint, $UPGRADE_MOUNTPOINT, is already mounted.  Continuing..." >> $LOG

    #----------------------------------------------------------------------------
    # other error?  Game over.
    #----------------------------------------------------------------------------
    else
        echo "The mountpoint, $UPGRADE_MOUNTPOINT, cannot be mounted.  Error is $?" >> $LOG
        exit_cleanup 1
    fi
fi

#-------------------------------------------------------------------------------
# good mount of output device, continue
#-------------------------------------------------------------------------------
echo "partition with indicator file, $INDFILE, mounted at $UPGRADE_MOUNTPOINT." >> $LOG


#-------------------------------------------------------------------------------
# The initial check is for the indicator file that a remote restore was was
# initiated. If this flag is set, then process the "HMCBackup_yyyymmdd.hhmmss"
# file which should now be located in the /dump directory.  Otherwise, check
# next for the HMC indicator file which says that a restore from DVD is to be
# done.
#-------------------------------------------------------------------------------
if [ ! -e $RMTBCRIT ]; then
    echo "$RMTBCRIT indicator file does not exist. Remote data restoration not requested continuing..." >> $LOG
    
    # See if a "DVD" restore is required.  This flag should only be set after an HMC
    # clean intsall (F8/F1).
    if [ ! -e $IQYBCRIT ]; then
        echo "$IQYBCRIT indicator file does not exist. Data restoration not required" >> $LOG
        echo -e "restore script exiting.\n" >> $LOG
        
        # excellent - no HMC restore required.  Exit program
        exit_cleanup 12
    else
        # ok, let's mark that we want to restore the system - the hard way...
        USE_LOCAL_BACKUP=1
    fi
else
    # Ah ha! We will be restoring the system from a remote archive file
    USE_REMOTE_BACKUP=1      
fi



#-------------------------------------------------------------------------------
# Mount the DVD media, if required
#-------------------------------------------------------------------------------
if [ "$USE_LOCAL_BACKUP" = "1" ]; then

    #-------------------------------------------------------------------------------
    # Check to see if the backup media (DVD) is already mounted
    #-------------------------------------------------------------------------------
    if grep -q "$MOUNTPOINT" /etc/mtab; then
        # Media is already mounted, unmount in preparation for (re-)'mount' cmd
        if ! umount -v $MOUNTPOINT >> $LOG 2>&1; then
            echo "Couldn't umount the media... exiting" >> $LOG
            exit_cleanup 4
        fi
    fi

    #-------------------------------------------------------------------------------
    # Mount and check for existence of archive
    #-------------------------------------------------------------------------------
#    if mount -v $MOUNTPOINT > $MOUNT_OUTPUT 2>&1; then
    # specifically use UDF, do not rely on filesystem "guessing" or /etc/fstab content
    if mount -t udf -v /dev/cdrom /mnt/cdrom > $MOUNT_OUTPUT 2>&1; then
        echo "successfully mounted the backup media, continuing..." >> $LOG

        # leave this test out since the HMC does not do a specific DVD format
#        if !(chkudf /dev/hdc | grep -q -i "ACTBKP" 2>&1); then # udf/dvd specific
        false=0
        if false; then
            echo "The media has an incorrect label... exiting" >> $LOG
            umount $MOUNTPOINT
            exit_cleanup 9
        elif [ ! -e $ARCHIVE ]; then
            echo "The backup archive is not on the restore media... exiting" >> $LOG
            umount $MOUNTPOINT
            exit_cleanup 3
        fi
        
        # archive file on DVD media check passed
        echo "backup archive file detected on media, continuing..." >> $LOG
    else
        echo "Could NOT mount the media..." >> $LOG

        # Check for no media inserted. If this is the case, we're going to remove the
        # iqybcrit.dat file.  That file should never remain on the system unless the
        # product recovery CD was just used. The last thing the recovery CD process
        # does is prompt to insert the backup archive media.  Hence, if no media, no
        # need for recovery indicator flag.
        #
        # Using 'grep' here seems kinda unnecessary/risky. We should just key off
        # 'mount' cmd return codes instead...

        if grep -q -i "wrong major or minor number" $MOUNT_OUTPUT; then
            echo -e "Hardware failure on the DVD RAM drive... exiting\n" >> $LOG
            exit_cleanup 6
        elif grep -q -i "No medium found" $MOUNT_OUTPUT; then
            echo -e "No media in the DVD RAM drive... exiting\n" >> $LOG

            # Here's one place where we'll remove the indicator file. This
            # will hopefully catch the manufacturing process issue whereby
            # they are installing the pHMCs from the recovery CD, which
            # contains the indicator file, during the initial HMC install.
            # That file should not initially be on the system.  The 'no media'
            # return code from 'mount' cmd is 32.
            rm -f $IQYBCRIT

            exit_cleanup 7
        elif grep -q -i "wrong fs type" $MOUNT_OUTPUT; then
            echo -e "Wrong filesystem type, or unformatted media, or wrong media type... exiting\n" >> $LOG
            exit_cleanup 8
        else
            echo -e "Unknown error... exiting\n" >> $LOG
            exit_cleanup 5
        fi
    fi
    DVD_DEV=/dev/hdc
    HD=`/opt/hsc/bin/GetHD`
    if [ "$HD" != "hda" ]; then
        DVD_DEV=/dev/hda
    fi
else

    #-------------------------------------------------------------------------------
    # Ensure there is a remote archive file to restore - there should be.
    # 
    # Note there should be one and only one HMCBackup_yyyyMMdd.HHmmss file in this
    # directory.  If not, just use the "first" one we find. All archievs will be 
    # removed when the operation completes.
    #-------------------------------------------------------------------------------
    x=`ls -1 $REM_ARCHIVE_LOCATION/HMCBackup*`
    if [ $? -ne 0 ]; then
        echo "The remotely restored archive file is not present... exiting" >> $LOG
        exit_cleanup 3
    else
        # Ok, remote archive file is present, ensure there is only one of them
        # and set the script variable to point to this file.
        #
        # Set $ARCHIVE to be the first listed file - there should be only one present
        echo "The remotely restored archive file, $x, is present" >> $LOG
        ARCHIVE=`echo $x | cut -d " " -f 1 -`
    fi
fi







#-------------------------------------------------------------------------------
# Now do the actual restore from the archive
#-------------------------------------------------------------------------------
echo -e "starting file archive restore...\n" >> $LOG

#
# First thing to do is determine which files are shared objects that could 
# potentially be partially paged in the system.  Hence, a recurring read
# of that same library file could panic the system - in theory, of course.
# Rather than using the 'tar' command to restore those particular archive
# files, we'll be staging those files and using the 'mv' command.
#
# Construct a working/staging directory for pre/post processing 
#
WORKING_DIR=/tmp/restore/work
mkdir -p $WORKING_DIR
if [ $? -ne 0 ]; then
     umount -v $MOUNTPOINT >> $LOG 2>&1
     exit_cleanup 11
fi

#
# start with a clean error log
#
rm -f $ARCHIVE_LOG
echo -e "begin file recovery error log for `date`\n" > $ARCHIVE_LOG
if [ "$USE_LOCAL_BACKUP" = "1" ]; then
 /sbin/hdparm -d1 $DVD_DEV >/dev/null 2>&1
fi

#
# build the list of all files that have a '.so' "extension" (except the library
# file search path cache, 'ld.so.cache' )
#
tar -tvzf $ARCHIVE | grep "\.so" | grep -v "ld.so.cache" | awk '{print $6}' > $WORKING_DIR/exclude.list 2>> $ARCHIVE_LOG
if [ $? -ne 0 ]; then
     umount -v $MOUNTPOINT >> $LOG 2>&1
     exit_cleanup 13
fi

#
# Catch any kernel updates as well
#
#tar -tvzf $ARCHIVE | grep "/mnt/dos" | awk '{print $6}' >> $WORKING_DIR/exclude.list 2>> $ARCHIVE_LOG

#
# Add the 'tar' command itself here. We'll restore file this with a cp/mv sequence
# when the 'tar' command has completed executing
#
tar -tvzf $ARCHIVE | grep "/bin/tar" | awk '{print $6}' >> $WORKING_DIR/exclude.list 2>> $ARCHIVE_LOG

#
# Another special case for Squadrons here - '/bin/bash' will also not un-tar as part of
# the main body of files to unpack.  Add to exclude processing list...
#
tar -tvzf $ARCHIVE | grep "/bin/bash" | awk '{print $6}' >> $WORKING_DIR/exclude.list 2>> $ARCHIVE_LOG

echo "exclude file processing list constructed, continuing..." >> $LOG


#
# Next step is to un-tar only the previously excluded files to the
# staging area.
#
# Bypass the cache file containg references to the prior list of libraries
# residing in the directories listed in /etc/ld.so.conf.  We will dynamically
# regenerate this file using 'ldconfig' at end of 'tar'  processing
#
tar -xvzf $ARCHIVE --files-from=$WORKING_DIR/exclude.list --directory=$WORKING_DIR >> $ARCHIVE_LOG 2>&1
tarRC=$?
if [ $tarRC -ne 0 ]; then
     ARCHIVEFAIL=$tarRC
     echo "shared object 'tar' processing to working directory failed (rc = $tarRC). Continuing..." >> $LOG
else
     echo "'tar' processing of shared objects/misc files to working directory successful." >> $LOG
fi

#
# Now un-tar all remaining files from the archive, with a few exceptions
# The file "/.journal" may exist in some archives. 'tar' cmd will throw an
# error if this file is attempted to be unpacked
#
#echo "/opt/hsc/bin/restore" >> $WORKING_DIR/special.list
#echo "/etc/init.d/hmcRestore" >> $WORKING_DIR/special.list
echo "/var/hsc/log/restore.log" > $WORKING_DIR/special.list
echo "/var/hsc/log/restore.log.1" >> $WORKING_DIR/special.list
echo "/var/hsc/log/hmcRestore.log" >> $WORKING_DIR/special.list
echo "/tmp/archive.log" >> $WORKING_DIR/special.list
echo "/.journal" >> $WORKING_DIR/special.list
echo "/" >> $WORKING_DIR/special.list

#
# Special case file - /sbin/srcmstr. That program gets run from inittab prior
# to this script on pHMC and 'tar' cmd returns "text file busy" msg, rc=2.
# If we find this file in the archive, then rename the "old"/current version
# so the 'tar' command will not return the warning error.  (Note: seems has
# to have something more to do with inodes/filesystem than the 'tar'
# program itself).
#
PHMC=0
tar -tvzf $ARCHIVE | grep "srcmstr" > /dev/null 2>&1
if [ $? -eq 0 ]; then
     PHMC=1
     rm -f /sbin/srcmstr
     
     tar -tvzf $ARCHIVE | grep "opt/hsc/bin/restore" > /dev/null 2>&1
     if [ $? -eq 0 ]; then
          rm -f /opt/hsc/bin/restore
     fi
     
     tar -tvzf $ARCHIVE | grep "etc/init.d/hmcRestore" > /dev/null 2>&1
     if [ $? -eq 0 ]; then
          rm -f /etc/init.d/hmcRestore
     fi
     
     echo "special file processing completed." >> $LOG
fi   

#
# Now do the un-tar of the majority of the archive file.
#
tar -xvzPf $ARCHIVE --overwrite --exclude-from=$WORKING_DIR/exclude.list --exclude-from=$WORKING_DIR/special.list >> $ARCHIVE_LOG 2>&1
tarRC=$?
if [ $tarRC -ne 0 ]; then
     ARCHIVEFAIL=$tarRC
     echo "remaining 'tar' archive processing failed (rc = $tarRC). Continuing..." >> $LOG
else
     echo "remaining 'tar' archive file processing completed successfully." >> $LOG
fi


#
# Flush memory to disk, just in case
#   
sync

#
# The theory is here that we should now be referncing the archived library
# shared objects. Do this so we don't reference a file that is about to be
# replaced.
#
export LD_LIBRARY_PATH=$WORKING_DIR/lib:$WORKING_DIR/usr/lib

#
# Assuming this far, all files have been restored from the archive BUT
# we have to (pseudo-atomically?) relocate the *.so files.  We do this
# by 1) 'cp' the archive file to the *same filesystem* as it's current
# existing, version, then 2) 'mv' archived version to current version
#
# example: if archive file is '/tmp/file1.ext'...
# 1) copy '/tmp/restore/work/tmp/file1.ext' to '/tmp/file1.ext.stage',
# 2) move 'tmp/file1.ext.stage' to '/tmp/file1.ext'
#
# Note: Many of the *.so files will most likely be symbolic links, so
#       be sure NOT to follow them when copying
#
for i in `cat $WORKING_DIR/exclude.list`
do
      d=`/usr/bin/dirname $i`
      mkdir -p $d
      cp -d -v $WORKING_DIR$i $i.stage >> $ARCHIVE_LOG 2>&1
      if [ $? -ne 0 ]; then
           let CP_INDICATOR=CP_INDICATOR+1
      else
           # Continue with 'mv' only if 'cp' worked
           mv -vf $i.stage $i >> $ARCHIVE_LOG 2>&1
           if [ $? -ne 0 ]; then
                let MV_INDICATOR=MV_INDICATOR+1
           fi
      fi
done

# End 'tar' processing log
echo -e "end file recovery error log on `date`\n" >> $ARCHIVE_LOG


#
# Rebuild the library search path based on the prior /etc/ld.so.conf
# information and then remove the temporary override path
#
if [ $PHMC -ne 0 ]; then
     # Temporarily relocate this directory prior to 'ldconfig' execution.  Appears
     # that 'ldconfig' will recurse into the /lib/i686 directory and there is a
     # potential for conflicting libraries residing in /lib.
     
     #
     # Turns out the for pHMC r41a release, which maps to particular kernel/RPM
     # versions, the *.so files in i686 directory shuold no longer exist.  Do not 
     # preserve them anymore, in fact remove them now! 10/08/03 - SLF
     #
#     mv -f /lib/i686/ /tmp/i686
     rm -fr /lib/i686
fi

/sbin/ldconfig -v >> $LOG 2>&1
/sbin/ldconfig /usr/X11R6/lib/
export LD_LIBRARY_PATH=

#
# Comment out i686 file manupulation. See prior comment above.  10/08/03
#
#if [ $PHMC -ne 0 ]; then
#     # Restore the i686 directory
#     mv -f /tmp/i686/ /lib/i686
#fi

#
# Cleanup any staged *.so files
#
rm -fr $WORKING_DIR


#
# Additional cleanup - there is the possibility that the directories under /home
# (all the users' dirs) may have been re-generated via this restore process.
# Unfortunately, if the directory itself was not part of the archive, it will
# have been created with uid/gid of root/root as part of un-taring the actual
# files in those directories. This is not good since the users will not be able
# to access their home dirs! Change ownership of those subdirectories to the
# corresponding userID.
#
cd /home/
for user in *
do
     if [ -d $user ]
     then
       g=`/usr/bin/id -g -n $user`
       chown $user.$g $user
       chmod 755 $user
       
       # add per new subdirectories in users' home directories
       find $user -name "*" -type d ! -path $user/.ssh -exec chown ${user##/home/} {} \;
       
       echo "chown/chmod of $user /home directory completed" >> $ARCHIVE_LOG
     fi
done
cd /

#
# And yet more cleanup.  Check to see if there are duplicate 'startlangDialog' links
# in the run level scripts using different sequence numbers.  If so, remove them, and
# re-add it properly.
#
if [[ -L /etc/rc.d/rc5.d/S91startlangDialog && -L /etc/rc.d.rc5.d/S99startLangDialog ]]; then
    /sbin/chkconfig --del startlangDialog
    
    # assuming 'startlangDialog' is setup properly, we don't have to mess around with
    # setting the run level and start/kill link number
    /sbin/chkconfig --add startlangDialog
fi


#
# New requirement for RMC/MegaMouth. Set an indicator file so their 'ctrestore'
# script can be executed later (*after* SRC is statrted, do in hmcpotscfg) to unpack
# their data saved away in /var/ct.backup directory
#
if [ -d /var/ct.backup ]; then
    touch /var/hsc/log/restoreRMC
    echo -e "RMC indicator file set for file restore.\n" >> $LOG
fi


if [[ $CP_INDICATOR -eq 0 && $MV_INDICATOR -eq 0 ]]; then
     echo -e "\n restore exclude list files was successful, ARCHIVEFAIL is $ARCHIVEFAIL.\n" >> $LOG
   
     rm -f $IQYBCRIT
     echo "restore indicator file removed from HMC" >> $LOG
else
     echo -e "\n restore from archive was NOT successful.\n" >> $LOG
     echo "cp indicator is: $CP_INDICATOR" >> $LOG
     echo -e "mv indicator is: $MV_INDICATOR\n" >> $LOG
   
     ARCHIVEFAIL=127
     if [ "$USE_LOCAL_BACKUP" = "1" ]; then
         umount -v $MOUNTPOINT >> $LOG 2>&1
     fi
     exit_cleanup 15
fi


#-------------------------------------------------------------------------------
# unmount the restore media and exit
#-------------------------------------------------------------------------------
if [ "$USE_LOCAL_BACKUP" = "1" ]; then
    if umount -v $MOUNTPOINT >> $LOG 2>&1; then
        exit_cleanup 0
    else
        echo "Couldn't umount the media" >> $LOG
        exit_cleanup 4
    fi
else    
    exit_cleanup 0
fi
