Working star backup script

"Zoran Ljubisic" <[email protected]> Wed, 2 Nov 2005 13:19:50 +0100
Newsgroups gmane.comp.archivers.star.user
Message-ID <004301c5dfa7$f24de1d0$fd01a8c0@zoran>
Hi all,

I found out this script that use star for backup. Maybe somebody here find 
it usefull also.
Original is locate on 
http://homepage.sunrise.ch/mysunrise/joerg.hau/linux/#backup2tape

Here it comes:

#!/bin/sh

# 'backup2tape.sh'
#
# - writes data to tape drive
# - allows to specify directories & exclude patterns
# - supports full & differential backup
# - supports both GNU mt and mt-st
# -----------------------------------------------------------------
# This program is free software; you can redistribute it and/or
# modify it under the terms of the version 2 of the GNU General
# Public License as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# -----------------------------------------------------------------
# Copyright (c) 2003...2005 Joerg Hau <joerg.hau(at)dplanet.ch>.
#
# Idea(s) based on
#
# - Joerg Schilling's 'star'
# - a script from Karsten M. Self (kmself(at)ix.netcom.com)
#   http://kmself.home.netcom.com/Linux/FAQs/backups.html
# - Vincent Stemen's 'bu' script (1998)
# - the easyBackup-Script v0.1 (c) 2000 by CCONE.at
# - material from Tim Jones Linux Magazine (1999)
#
# Revisions:
#   2003-10-05, first operational version (JHa)
#   2003-10-06, cmd line parsing and more variables (JHa)
#   2003-10-07, ctrl-c handler; error log via mail (JHa)
#   2003-10-14, fixed exclude patterns, added verify (JHa)
#   2003-10-15, all msg are now logged (JHa)
#   2003-10-29, bugfix: tape positioning before verify (JHa)
#   2003-12-20, introduced a tape label (JHa)
#   2004-01-28, minor bugfix in tape label (JHa)
#   2004-03-06, minor bugfix in label assignment on cmd line (JHa)
#   2004-08-02, bugfix in redirection to STDERR (JHa)
#   2004-12-19, fixed typo (JHa)
#   2004-12-25, hostname now from environment (JHa)
#   2004-12-29, removed domain from hostname (JHa)
#   2005-01-12, introduced handling of mt-st in addition to mt (JHa)
#   2005-02-21, added check for executables (JHa)
#   2005-02-26, $TAR, $MT now set after config file (JHa)
#   2005-03-18, fix in checkfor() error message (JHa)
# -----------------------------------------------------------------
# NOTES:
# - adjust parameters, tape device etc. below.
# - All variables below can be specified in an external file
#   which is read on the cmd line. In other words: put all 'your'
#   stuff in that external file - no need to modify this script.
#
# - for differential backup, the ctime of $LASTTIME counts.
# - if you change DIRLIST, you should perform a full backup.
#
# - This must be run as root.
# -----------------------------------------------------------------

# disable expansion of wildcards in commands
#
set -f

# default list of directories to archive
# star will NOT descend mount points, so you must specify them separately
#
DIRLIST="/home /root /etc /var /usr/local /mnt/win/Home /mnt/share/archive"

# list of exclude patterns for 'star'
#
EXCLUDE="tmp/* var/tmp/* var/spool/wwwoffle/* proc/* */[cC]ache/* 
*/[.][tT]humbnail[s]/* *~"

# default backup mode is either "Differential" or "Full"
# This string is also used for logging.
# Attention, this is case-sensitive!
#
MODE="Differential"

# the non-rewinding tape device
#
TAPE="/dev/nst0"

# Blocksize for the tape drive (to be multiplied by 512)
# for a HP1533A (DDS-2),  125 is fine
# for a HP DLT4000 (DLT), 500 is fine
#
BS="125"

# the tar and mt executables to use
# I use Joerg Schilling's 'star' and GNU 'mt'
# note that some options used below are version-specific!
#
TAR_EXE="/usr/bin/star"
MT_EXE="/usr/bin/mt"

# Get the system name (without domain suffix)
#
NAME=${HOSTNAME%%.*}

# Backup log directory
#
LOGDIR="/var/log/backup"

# One "general" log file
# to keep track of date/time & success/failure
# (one line per event)
#
LOGFILE=${LOGDIR}/${NAME}.log

# "individual" log files, a pair for each backup process
# The filenames contain hostname & date/time, so they do not
# get overwritten when multiple backups are done the same day
#
LOGNAME=${LOGFILE}.`date +%Y%m%dT%H%M`
STDOUT=${LOGNAME}.ok
STDERR=${LOGNAME}.err
DIFF=${LOGNAME}.diff

# A file whose date/time stamp is used for incremental backups
#
LASTTIME=${LOGDIR}/${NAME}.lasttime

# Error control file (see 'star' manpage for details)
#
ERRCTLFILE=${LOGDIR}/${NAME}.errctl

VERBOSE=""      # verbose mode or not (empty string = no)
INIT_TAPE=""    # should tape be rewritten or appended? (empty = append)

ERRCNT=0        #error counter

MAILTO="root@${HOSTNAME}"   # Backup operator

#
# --- "No user adjustable parts below this line" :-) ---
#

# -----------------------------------------------------------------
# subroutine to check for some required executables
# argument: (list of) programs to test for
# will exit with rc=1 if any program was not found
# -----------------------------------------------------------------
function checkfor()
{
for i in $*; do
    PROG=`which $i`
    if [ $? != 0 ]; then
  echo "'$i' command not found, exiting."
  exit 1
    else
    # echo "'$i' command found."
  shift
    fi
done
}

# ------------------------------------------------------------
# subroutine to log stuff to file with date/time stamp
# argument: text to log
# ------------------------------------------------------------
function logging()
{
# get date/time stamp
DATETIME=`date --iso-8601=sec`

# append text ($1) to file
echo "${DATETIME}. $1" >> $LOGFILE

# if verbose mode is set, echo also to screen
if [ $VERBOSE ] ; then
    echo "${DATETIME}. $1"
fi
}


# ------------------------------------------------------------
# subroutine to check error status of last command
# argument: error message
# will exit with rc=1 if errorlevel was not 0
# ------------------------------------------------------------
function errcheck()
{
if [ $? != 0 ]; then
    logging "Abort: $1, status: $?"
    echo `date --iso-8601=sec`. $1 >> $STDERR
    mail -s "$NAME-Backup ABORT!" $MAILTO < $STDERR
    exit 1
fi
}


# ------------------------------------------------------------
# subroutine to create an "exceptions" file
# argument: filename
# see the 'star' manpage for explanations
# ------------------------------------------------------------
function makectlfile()
{
cat << eof > $1
GROW  ${LOGDIR}*
eof

if [ $? != 0 ]; then
    logging "Warning: cannot create exceptions file. Verify will report 
everything."
fi
}

# ------------------------------------------------------------
# subroutine to obtain tape position
#
# Most Linux distributions use GNU mt, but RedHat uses mt-st.
# Both have different status messages!
#
# argument: none
# sets    : tape position (in TPOS)
# ------------------------------------------------------------
function get_tape_position()
{
if ( $MT --version | grep -q 'mt-st' ) ; then
    # mt-st: "File number=1, block number=0, partition=0."
    TPOS=`$MT status | grep "File number" | cut -d "," -f 1 | cut -d "=" -f 
2`
else
    # GNU tar: "file number=1"
    TPOS=`$MT status | grep "file number" | cut -d ' ' -f 4`
fi
}


# ------------------------------------------------------------
# subroutine to print usage mode
# ------------------------------------------------------------
function usage()
{
cat << eof

${0##*/} -  Backup to Tape

A script to back up files and directories to a tape streamer.

Copyright (c) 2003...2005 Joerg Hau <joerg.hau(at)dplanet.ch>.

This program is free software; you can redistribute it and/or
modify it under the terms of version 2 of the GNU General Public
License as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

Usage: ${0##*/} [-f | --full] [-d | --diff] [-i | --init]
    [-c | --config config_file] [-l | --label text] [-v | --verbose]

    Invoking "${0##*/}" with no arguments will perform a
    differential backup of the files listed at the beginning of
    this script. Otherwise:

    -f, --full        write full backup.
    -d, --diff        write differential backup.
    -i, --init        write tape from the beginning. This is done by default
                      for a full backup, but not for incremental stuff:
                      you may use this function to "re-initialise" a tape.
    -c config_file    Specify a configuration file.
                      Default is to use the built-in values.
    -l label          Label (a descriptive text) to identify the tape.
                      At present this is provided merely for convenience.
    -v, --verbose     Verbose mode.
    -? | -h | --help  This help.

Notes:

    o   Full backup always starts at beginning of the tape.

    o   There is no incremental backup (not yet? ;-)

    o   Differential backup appends at the end of the tape (simply
        due to the fact that I do not change that tape every day ;-).
        The tape position is written to $LOGFILE,
        so that you can use "$MT_EXE -f ${TAPE} asf \$position"
        to position the tape before extracting that backup.

    o   It does not traverse mounted file systems, so you can backup
        file systems like '/' without umounting file systems you do
        not want included, such as NFS mounts.

    o   WARNING: This requires that you explicitly specify mounted fs that
        shall *be* included! Look up /etc/fstab ...

    o   To restore a subset (e.g. all in /home), use a command like
        "$TAR_EXE -f ${TAPE} -xpv -b $BS pat='home/*'".
eof
}


################## script starts here ;-) ####################

# ------------------------------------------------------------
# handle command line parameters (flags)
# ------------------------------------------------------------
while [ "$1" != "" ]; do
    case $1 in
        -f | --full )           MODE="Full"
                                ;;
        -d | --diff )           MODE="Differential"
                                ;;
        -v | --verbose )        VERBOSE="talkative"
                                ;;
        -i | --init )           INIT_TAPE="init"
                                ;;
        -c | --config )         shift
                                CONFIG_FILE="$1"
                                ;;
        -l | --label  )         shift
                                LABEL="$1"
                                ;;
        -h | -? | --help )      usage
                                exit 2
                                ;;
        * )                     usage
                                exit 1
    esac
    shift
done


# ------------------------------------------------------------
# see if you have the right to access all this stuff
# ------------------------------------------------------------
if [ `whoami` != 'root' ]; then
    echo "You must be root to run this script."
    exit 2
fi


# ------------------------------------------------------------
# handle Ctrl-C and other stuff
# ------------------------------------------------------------
trap 'errcheck "${0##*/} killed!" ; \
      cp -p ${LASTTIME}.previous $LASTTIME ; \
      exit 1' \
      9 15


# default label text. This variable is not used in the backup process
# "as such" but is written to the log file. It can be used e.g. to
# identify the tape in a set of backup tapes.
# FIXME: could use this in VOLHDR=$LABEL

LABEL="Backup-${NAME}-${MODE}"


# ------------------------------------------------------------
# if an external config file was specified, source it
# ------------------------------------------------------------
if [ $CONFIG_FILE ]; then
    logging "Reading configuration from $CONFIG_FILE"
    if [ -e "$CONFIG_FILE" ]; then
        source $CONFIG_FILE
    else
        logging "Configuration file $CONFIG_FILE not found!"
        exit 1
    fi
fi

# ------------------------------------------------------------
# set the tar and mt *commands* to use
# ------------------------------------------------------------
TAR="${TAR_EXE} -f ${TAPE}"
MT="${MT_EXE} -f ${TAPE}"


# ------------------------------------------------------------
# see if everything is accessible
# ------------------------------------------------------------
checkfor $TAR_EXE $MT_EXE gzip uuencode mail cp echo cat


# ------------------------------------------------------------
# Check if log directory exists, exit if problem
# ------------------------------------------------------------
if [ ! -d $LOGDIR ] ; then
 echo "Error: $LOGDIR does not exist. Exiting."
 exit 1
fi


# ------------------------------------------------------------
# See if differential or full backup is desired
# if differential, the date/time stamp file must exist
# ------------------------------------------------------------
if [ $MODE = "Full" ] ; then
    MODE_CMD="-cv"
else                #  all else
    if [ ! -e $LASTTIME ] ; then
        echo "Error: no previous backup detected! Must do a full backup 
first."
        exit 2
    fi
    MODE_CMD="-cv -newer=${LASTTIME}"
fi

# log event to file
#
logging "$MODE backup initiated."

# ------------------------------------------------------------
# concatenate list of directories & excludes
# we'll cd to '/' first, so all dirnames will start with './'
# ------------------------------------------------------------
DIRS=`for X in $DIRLIST; do echo -n ".${X} "; done`
EXCL=`for X in $EXCLUDE; do echo -n "-not pat=${X} "; done`

logging "Preparing to archive: $DIRLIST"
logging "Excluding: $EXCLUDE"

makectlfile $ERRCTLFILE


# ------------------------------------------------------------
# tape initialisation
# blocksizes are given in multiples of 512 bytes
# ------------------------------------------------------------
TAPE_BS=512
logging "Using blocksize of $BS (x${TAPE_BS} bytes)"
let TAPE_BS="${TAPE_BS}*${BS}"
$MT setblk $TAPE_BS
errcheck "Error during initialisation of $TAPE to a blocksize of $TAPE_BS!"


# ------------------------------------------------------------
# full backup --> just rewind and shoot ;-)
# differential backup --> write at the end of the tape
# ------------------------------------------------------------
if [ $MODE = "Full" ] ; then
    $MT rewind
else
    if [ $INIT_TAPE ] ; then
        $MT rewind      # force write at the beginning
    else
        $MT eod      # end of data (eom = en of media)
        # GNU tar uses eom, mt-st uses eod, both are compatible :-)
    fi
fi
errcheck "Error during positioning of $TAPE!"


# ------------------------------------------------------------
# write tape position to log file, so that we can easily find
# which file contains which backup
# ------------------------------------------------------------
get_tape_position
errcheck "Error during status check of $TAPE!"
logging "Tape '$LABEL' is positioned at file number $TPOS"


# ------------------------------------------------------------
# For a partial backup, we rely on the date/time stamp
# of the file $LASTTIME. Two issues here:
# (1) If we create this file AFTER the backup is complete,
#     changes that occur between the start of this script and
#     the end of the backup process are missed.
# (2) If we 'touch' $LASTTIME before and an error occurs, the
#     datetime of the previous "good" backup would be lost.
# --> Solution: copy -p preserves date/time stamp :-)
# ------------------------------------------------------------
if [ $MODE = "Full" ] ; then
    if [ -e $LASTTIME ] ; then
     cp -p $LASTTIME ${LASTTIME}.previous
     errcheck "Error copying ${LASTTIME}!"
    fi
    echo "`date --iso-8601=sec`, $MODE backup was started. DO NOT TOUCH THIS 
FILE!" > $LASTTIME
fi


# ------------------------------------------------------------
# ... ah, finally, now we run $TAR :-)
# Some options used:
# -C /       start at "/" and strip off leading slash
# -acl       save ACLs if present
# H=exustar  use exustar header (this one supports acl)
# -M         do not descend mount points
# ------------------------------------------------------------
${TAR} -C / ${MODE_CMD} H=exustar -b ${BS} -acl -M errctl=${ERRCTLFILE} 
${EXCL} ${DIRS} > $STDOUT 2> $STDERR
ERRCNT=$?
logging "$MODE backup written, status: $ERRCNT"


# ------------------------------------------------------------
# verify tape
# ------------------------------------------------------------
sleep 30s         # give $MT some time to finish
$MT asf $TPOS     # position to beginning of this archive

get_tape_position
errcheck "Error during status check of $TAPE!"
logging "Starting verify at tape position (file number) $TPOS"

# compare tape with filesystem - not the other way round -,
# thus we do *not* need to specify a lot of parameters
#
${TAR} -C / -diff -b ${BS} errctl=${ERRCTLFILE} > ${DIFF} 2> ${DIFF}
ERRCNT=$?
logging "Verify complete, status: $ERRCNT"

# if something goes wrong, do NOT bail out here - the backup may still be 
useful


# ------------------------------------------------------------
# After a FULL _AND_ SUCCESSFUL backup, $LASTTIME has the
# correct date/time stamp.
# Only in case of trouble, we copy the previous file back.
# ------------------------------------------------------------
if [ $MODE = "Full" ] ; then
    if [ $ERRCNT != 0 ] ; then      # problem occurred
        cp -p ${LASTTIME}.previous $LASTTIME
    fi
fi


# ------------------------------------------------------------
# full backup --> force tape change by ejecting
# differential backup --> leave it in the drive, so that we can
#                         add at the end of the tape
# ------------------------------------------------------------
if [ $MODE = "Full" ] ; then
    $MT rewoffl
    logging "$TAPE ejected, status: $?. Please change the tape."
else
    $MT rewind
    logging "$TAPE rewound, status: $?."
fi


# ------------------------------------------------------------
# mail last few lines of logfile to backup-admin
# ------------------------------------------------------------
if [ $ERRCNT -gt 0 ] ; then
    mail -s "$NAME-Backup ERROR!" $MAILTO < $STDERR
else
    cat $STDOUT | gzip | uuencode backup-ok-`date --iso-8601=min`.gz | \
    mail -s "$NAME-Backup OK!" $MAILTO
fi

gzip $STDOUT

if [ $VERBOSE ] ; then echo `date --iso-8601=sec`", Backup script ended." ; 
fi
exit 0


I found minor bug with label option and config file. Everything else looks 
very well.

Zoran