#!/bin/sh

# For the beginning of the script,
# search forward for "The script starts here:"


# This script can be called as either
# runbackup full
# or
# runbackup incremental
# or
# runbackup inc
# (That last one is equivalent to incremental)


# If we're called as "runbackup full", then we backup the complete
# filesystem to tape.  We write the file directly to tape, since it
# probably wouldn't fit on the disk.

# If we're called as "runbackup inc", then we do a listed incremental
# backup of the complete filesystem, creating the file
# $BACKUPFILENAME, and then we copy that file to tape.  We leave that
# file where it is, so that we can make a second copy of it to another
# tape, if we so choose.

# These two jobs are so different that they should probably be done in
# different scripts, but putting them into the same script ensures
# that they both use the same filenames for the listfile, logfile,
# etc.

# We assume that /dev/tape is a symlink to /dev/nht0, the
# non-rewinding tape device (i.e., the device that does not
# automatically rewind the tape after each write), because the "mt" 
# command uses the default device /dev/tape.
# If this is not the case, edit the definition of "MTCOMMAND" below
# to include the device as an argument to mt, as in
# MTCOMMAND="/bin/mt -f /dev/nht0"
# (in which you many also need to change the path to the mt command
# if it's not in /bin).


umask 022
#DIRTOBACKUP="/home /root /var/spool/mail /etc /var/log"
DIRTOBACKUP=/
BACKUPDIR=/var/backups
LOGFILE=$BACKUPDIR/Backups.log
MESSAGEFILE=$BACKUPDIR/Backups.messages
LISTFILE=$BACKUPDIR/Listfile.lst
LASTBACKFILE=$BACKUPDIR/Lastbackup.date
EXCLUDEFILE=$BACKUPDIR/Exclude.lst
TAPEDEVICE=/dev/tape
MTCOMMAND="/bin/mt -f $TAPEDEVICE"

# The following is used only if we're doing an incremental backup:
BACKUPFILENAME=$BACKUPDIR/files/IncrBackup.`date '+%Y.%m.%d'`.tgz



MACHINE=`hostname`

# --------------------------------------------------------------------
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# Define functions:

wait_until_tape_ready () {

# We wait a maximum of 60 seconds until the tape drive
# is ready to perform another command.
# We do this by using "mt tell" to inquire into the current
# tape position, and check to see if that command completed
# successfully.
# We check this every 5 seconds and return 0 when it's ready.
# If it's not ready after 60 seconds, we return 1.

    let NUMTRIES=1
    until [ $NUMTRIES -gt 12 ]; do
      $MTCOMMAND tell > /dev/null 2>&1
      if [ $? -eq 0 ]; then
        return 0
      fi
      sleep 5
      let NUMTRIES=NUMTRIES+1
      $MTCOMMAND tell > /dev/null 2>&1
    done
    echo "  Error: Tape still not ready after 60 seconds: `date`"\
                              | tee -a $MESSAGEFILE
    echo "  Script exiting" | tee -a $MESSAGEFILE
    exit 1

} # wait_until_tape_ready

# --------------------------------------------------------------------

rewind_tape () {

    wait_until_tape_ready
    $MTCOMMAND rewind
    if [ $? -ne 0 ]; then
      echo "  Error: Can't rewind tape: `date`" | tee -a $MESSAGEFILE
      echo "  Script exiting" | tee -a $MESSAGEFILE
      exit 1
    fi
    return 0

} # rewind_tape

# --------------------------------------------------------------------

retension_tape () {

    wait_until_tape_ready
    $MTCOMMAND retension
    if [ $? -ne 0 ];then
	echo "  Error: Unable to retension the tape: `date`"\
		| tee -a $MESSAGEFILE
	echo "  (Is there a tape in the drive?)" | tee -a $MESSAGEFILE
	echo "  Script exiting" | tee -a $MESSAGEFILE
	exit 1
    fi
    return 0

} # retension_tape

# --------------------------------------------------------------------

show_usage () {

  echo "Usage:"
  echo "      Either \"$PROGNAME full\" for a full backup, or"
  echo "             \"$PROGNAME inc\" for an incremental backup, or"
  echo "             \"$PROGNAME incremental\" for an incremental backup."

} # show_usage

# --------------------------------------------------------------------

prepare_tape_for_write () {

    # retension the tape twice, and then go to the end of the data:
    echo "Retensioning tape, first of two times, `date`..."\
		| tee -a $MESSAGEFILE
    retension_tape
    echo "Retensioning tape, second of two times, `date`..."\
		| tee -a $MESSAGEFILE
    retension_tape


    echo "Seeking to the end of data on the tape, `date`..."\
		| tee -a $MESSAGEFILE
    wait_until_tape_ready
    $MTCOMMAND eod
    if [ $? -ne 0 ]; then
      echo "  Error: Unable to go to the end of the data: `date`"\
		| tee -a $MESSAGEFILE
      echo "  Script exiting" | tee -a $MESSAGEFILE
      exit 1
    fi

    # Find the present block number, and delete all non-digits
    # from the output of "mt tell" so that we have only the number:
    echo "Finding the block number at end of data, `date`..."\
		| tee -a $MESSAGEFILE
    wait_until_tape_ready
    BLOCKNO=`$MTCOMMAND tell | tr -d -c 0123456789`

} # prepare_tape_for_write

# --------------------------------------------------------------------

run_fullbackup () {

    # This function does a full backup, directly to tape:
    echo | tee -a $MESSAGEFILE
    echo "Beginning a full backup of $MACHINE, `date`" | tee -a $MESSAGEFILE

    prepare_tape_for_write

    NOW=`date`
    echo | tee -a $MESSAGEFILE
    echo "Beginning full backup of $MACHINE, $NOW" | tee -a $MESSAGEFILE

    # Remove the listed-incremental record file so that
    # a full backup will be done:
    rm -f $LISTFILE

    tar -czvX $EXCLUDEFILE\
      -V "Full backup of $MACHINE on $NOW"\
      --listed-incremental=$LISTFILE\
      -f $TAPEDEVICE\
      --totals\
      $DIRTOBACKUP  >> $MESSAGEFILE 2>&1

    echo >> $LOGFILE
    echo "Full backup of $MACHINE, $NOW" >> $LOGFILE
    echo "    (gzipped tarfile) beginning at block $BLOCKNO"  >> $LOGFILE

    # --------------------------------------------------------------------

    echo $NOW > $LASTBACKFILE

    # Find the block number that follows the date we've written:
    echo "Finding block number at end of new data, `date`"\
                                       | tee -a $MESSAGEFILE
    wait_until_tape_ready
    NEWBLOCKNO=`$MTCOMMAND tell | tr -d -c 0123456789`
    echo "    Next backup will begin at block $NEWBLOCKNO" >> $LOGFILE

    # --------------------------------------------------------------------

    # Go back to the beginning of the newly written file,
    # and run a compare.  
    echo "Seeking to beginning of new file before comparing, `date`..."\
		| tee -a $MESSAGEFILE
    $MTCOMMAND seek $BLOCKNO

    echo "Beginning compare of new backup: `date`" | tee -a $MESSAGEFILE
    wait_until_tape_ready

    # cd to / so that the paths will match up right:
    cd /
    /bin/tar --compare -z -f $TAPEDEVICE >> $MESSAGEFILE 2>&1

    echo "Compare of new backup completed: `date`" | tee -a $MESSAGEFILE

    # --------------------------------------------------------------------

    echo "Putting tape offline, `date`..." | tee -a $MESSAGEFILE
    wait_until_tape_ready
    $MTCOMMAND offline
    if [ $? -ne 0 ]; then
      echo "  Error: Unable to put tape offline: `date`"\
		| tee -a $MESSAGEFILE
      echo "Script exiting." | tee -a $MESSAGEFILE
      exit 1
    fi

    # --------------------------------------------------------------------

    echo "Backup script complete: `date`" | tee -a $MESSAGEFILE
    exit 0

} # run_fullbackup

# --------------------------------------------------------------------

run_incbackup () {

    # This function does an incremental backup

    if [ ! -d $BACKUPDIR/files ];then
      echo "Directory $BACKUPDIR/files doesn't exist; script exiting"\
          | tee -a $MESSAGEFILE
      exit 1
    fi

    if [ -f $BACKUPFILENAME ]
      then
      echo "$BACKUPFILENAME already exists; script exiting"\
          | tee -a $MESSAGEFILE
      exit 1;
    fi

    if [ ! -f $LISTFILE ];then
      echo "LISTFILE $LISTFILE doesn't exist; script exiting"\
          | tee -a $MESSAGEFILE
      exit 1
    fi


    # --------------------------------------------------------------------

    if [ -f $LASTBACKFILE ]; then
      THEN=`cat $LASTBACKFILE`
    else
      THEN="I don't know when"
    fi
    NOW=`date`
    echo | tee -a $MESSAGEFILE
    echo "Beginning listed incremental backup from $THEN to $NOW"\
	| tee -a $MESSAGEFILE

    tar -czvX $EXCLUDEFILE\
      --listed-incremental=$LISTFILE\
      -V "Listed incremental backupof $MACHINE from $THEN to $NOW"\
      -f $BACKUPFILENAME\
      --totals\
      $DIRTOBACKUP  >> $MESSAGEFILE 2>&1

    echo >> $LOGFILE
    echo "Incremental backup of $MACHINE (gzipped tarfile):" >> $LOGFILE
    echo "    From $THEN to $NOW" >> $LOGFILE

    echo $NOW > $LASTBACKFILE

    # --------------------------------------------------------------------

    # Do a compare to check the backup file contents:
    # cd to / so that the paths will match up right:

    cd /
    echo "Beginning compare of new backup, `date`" | tee -a $MESSAGEFILE
    /bin/tar --compare -z -f $BACKUPFILENAME >> $MESSAGEFILE 2>&1
    echo "Compare of new backup completed: `date`" | tee -a $MESSAGEFILE

    # --------------------------------------------------------------------

    # Copy the backup file to tape:

    prepare_tape_for_write

    echo "Beginning to copy $BACKUPFILENAME to tape, `date`" >> $LOGFILE
    echo "    beginning at block $BLOCKNO"  >> $LOGFILE
    echo "Beginning to copy $BACKUPFILENAME to tape, `date`"\
        | tee -a $MESSAGEFILE
    cp $BACKUPFILENAME $TAPEDEVICE


    wait_until_tape_ready
    NEWBLOCKNO=`$MTCOMMAND tell | tr -d -c 0123456789`
    echo "    Next backup will begin at block $NEWBLOCKNO" >> $LOGFILE


    # --------------------------------------------------------------------

    # Go back to the beginning of the newly written file on tape
    # and run a compare.  

    echo "Seeking to beginning of new file before comparing, `date`..."\
		| tee -a $MESSAGEFILE
    $MTCOMMAND seek $BLOCKNO

    echo "Beginning compare of new backup: `date`" | tee -a $MESSAGEFILE
    wait_until_tape_ready
    cmp $BACKUPFILENAME $TAPEDEVICE >> $MESSAGEFILE 2>&1
    echo "Compare of new backup completed." | tee -a $MESSAGEFILE


    echo "Putting tape offline, `date`..." | tee -a $MESSAGEFILE
    wait_until_tape_ready
    $MTCOMMAND offline
    if [ $? -ne 0 ]; then
      echo "  Error: Unable to put tape offline: `date`"\
	  | tee -a $MESSAGEFILE
      echo "Script exiting" | tee -a $MESSAGEFILE
      exit 1
    fi

    # --------------------------------------------------------------------

    echo "Backup script complete: `date`" | tee -a $MESSAGEFILE
    echo | tee -a $MESSAGEFILE
    exit 0

} # run_incbackup

# --------------------------------------------------------------------
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# --------------------------------------------------------------------
# The script starts here:

umask 022
PROGNAME="`basename $0`"
BACKTYPE=$1

if [ ! -d $BACKUPDIR ]; then
  echo "Directory $BACKUPDIR doesn't exist; script exiting"
  exit 1
fi

if [ ! -f $EXCLUDEFILE ]; then
  echo "EXCLUDEFILE $EXCLUDEFILE doesn't exist; script exiting"
  exit 1
fi



case $BACKTYPE in

  full)
    run_fullbackup
    exit 0
  ;;

  incremental | inc)
    run_incbackup
    exit 0
  ;;

  "")
    echo "$PROGNAME Error: Incorrect usage"\
		| tee -a $MESSAGEFILE
    show_usage
    exit 1
  ;;

  *)
    echo "$PROGNAME Error: Unrecognized backup type"\
		| tee -a $MESSAGEFILE
    show_usage
    exit 1
  ;;

esac
