Avoiding Disaster with a Remote Archive

Whether or not you have a good backup strategy, with tape rotation and so forth, it’s still a nice insurance policy to identify a half-dozen critical files and have them sent to a separate off-site archive system. Even if it’s just that one key file that contains customer addresses, invoices, or even email from your sweetheart, having an occasional off-site archive can save your life when you least expect it.

This sounds more complex than it really is, because as you’ll see in this script, the archive is just a file emailed to a remote mailbox and could even be pointed to a Yahoo! or Hotmail mailbox. The list of files is kept in a separate data file, with shell wildcards allowed therein. Filenames can contain spaces too, something that rather complicates the script, as you’ll see.

#!/bin/sh

# remotebackup – Takes a list of files and directories,
# builds a single archive, compressed, then emails it off to a
# remote archive site for safekeeping. It’s intended to be run
# every night for critical user files, but not intended to
# replace a more rigorous backup scheme. You should strongly
# consider using unpacker, Script #88, on the remote end too.

uuencode=”/usr/bin/uuencode”
outfile=”/tmp/rb.$$.tgz”
outfname=”backup.$(date +%y%m%d).tgz”
infile=”/tmp/rb.$$.in”

trap “/bin/rm -f $outfile $infile” 0

if [ $# -ne 2 -a $# -ne 3 ] ; then
echo “Usage: $(basename $0) backup-file-list remoteaddr {targetdir}” >&2
exit 1
fi

if [ ! -s “$1” ] ; then
echo “Error: backup list $1 is empty or missing” >&2
exit 1
fi

# Scan entries and build fixed infile list. This expands wildcards
# and escapes spaces in filenames with a backslash, producing a
# change: “this file” becomes this\ file so quotes are not needed.

while read entry; do
echo “$entry” | sed -e ‘s/ /\\ /g’ >> $infile
done < “$1”

# The actual work of building the archive, encoding it, and sending it

tar czf – $(cat $infile) | \
$uuencode $outfname | \
mail -s “${3:-Backup archive for $(date)}” “$2”

echo “Done. $(basename $0) backed up the following files:”
sed ‘s/^/ /’ $infile
echo -n “and mailed them to $2 ”
if [ ! -z “$3” ] ; then
echo “with requested target directory $3”
else
echo “”
fi

exit 0

After the basic validity checks, the script processes the file containing the list of critical files, which is supplied as the first command argument, to ensure that spaces embedded in its filenames will work in the while loop (remember, by default spaces delimit arguments, so without some additional help, the shell will think that “test file” is two arguments, not one). It does this by prefacing every space with a backslash. Then it builds the archive with the primitive but useful tar command, which lacks the ability to read standard input for its file list and thus must be fed the filenames via a cat invocation.

tar czf – $(cat $infile)

The tar invocation automatically compresses the archive, and uuencode is then utilized to ensure that the resultant archive data file can be successfully emailed without corruption. The end result is that the remote address receives an email message with the uuencoded tar archive as an attachment. This should be a straightforward script.

This script expects two arguments: the name of a file that contains a list of files to archive and back up, and the destination email address for the compressed, uuencoded archive file. The file list can be as simple as

$ cat filelist
*.sh
*.html