Personal.X-Istence.com

Bert JW Regeer (畢傑龍)

Backup Script

I was working one some backup scripts and I decided I might as well generalise almost all of it (I am sorry about the hard coded paths) so almost anyone can come grab it and use it on their servers. The shell script is pretty self explanatory and I am not very good at documenting code, so the script source itself is your best bet as to why I did something some way. This script is in production use on several servers I administrate, I hope someone can use it as well.

Documentation on how to use it:

New backup script which will mount a /backup, then use rsync to copy files from /usr/home to /backup/yyyy-mm-dd. After it is done, it will remove the oldest backup, 14 days ago. All paths are hardcoded.

If a file .exclude is in a users homedir, it is looked at, and is used to exclude certain directories or files from the rsync. For example:

in /usr/home/xistence/.exclude

public_html/iso

No trailing slash, this will cause the folder /usr/home/xistence/public_html/iso to not be backed up. This might be for various reasons, but for this example it is because backing up GB sized files should not be something that is done, only because it takes ages and wastes valuable space.

Thing to keep in mind is, if you want to backup a 40 GB HD, with 22 GB used, to keep even a 3 day backup of that would require 3 * 22 GB, or 66 GB of available space on the backup drive. It is therefore in ones best interest to exclude very large files, and back them up in a different method since they are most likely not going to change much across revisions.

This script should be set to cron every 30 minutes, as rsync will do one full backup when the day changes, and then from there on do incremental backups.

Maybe removing the --delete option might help with customers that delete files and expect them to still be in the backup for that day, this way however if a file is removed it will still exist in the backup until it is deleted after 14 days.

#!/usr/local/bin/bash

# This file REQUIRES bash, and can not be run under /bin/sh!

###
 # Copyright 2006 Bert JW Regeer. All rights  reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
 # are met:
 # 1. Redistributions of source code must retain the above copyright
 #    notice, this list of conditions and the following disclaimer.
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
 #
 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
 # The views and conclusions contained in the software and  documentation are
 # those of the authors and should not be  interpreted as representing official
 # policies, either expressed  or implied, of bsdPanel project.
 #
###

for HOMEDIR in `/usr/bin/find /usr/home -type d -maxdepth 1 | grep -v '^.$'`; do
    if [ -f $HOMEDIR/.exclude ]; then
        echo "Exclude found for $HOMEDIR"
        while read line; do
            if [ "${line:0:1}" != "/" ]; then
                # They are missing a full path, we will be nice and add it
                TMPLINE="$HOMEDIR/$line"
                line=$TMPLINE
            fi
            echo "  Excluding $line"
            echo $line >> /tmp/exclude.$$
        done < $HOMEDIR/.exclude
    fi
done

mount | grep /backup

if [ $? -ne 0 ]; then
    /sbin/mount /backup
    if [ $? -ne 0 ]; then
        logger -s -p user.err -t backup "Backup drive could not be mounted! Bailing!"
        /sbin/umount -f /backup
        exit 1
    fi
fi

BACKUPDIR="/backup/`date "+%Y-%m-%d"`"
OLDDIR="/backup/`date -v-14d "+%Y-%m-%d"`"

if [ ! -d $BACKUPDIR ]; then
    mkdir $BACKUPDIR
fi

if [ $? -ne 0 ]; then
    logger -s -p user.err -t backup "Could not create backup folder $BACKUPDIR. Bailing!"
    /sbin/umount -f /backup
    exit 1
fi

# We got this far, now we have to run rsync. Rsync is very verbose on purpose

/usr/local/bin/rsync -av --relative --delete /usr/home/ $BACKUPDIR --exclude-from=/tmp/exclude.$$

if [ $? -ne 0 ]; then
    logger -s -p user.err -t backup "rsync failed. Do not trust backup ($BACKUPDIR)"
    /sbin/umount /backup
    exit 1
fi

echo "Backup complete! Deleting oldest backup (14 days ago)"

if [ -d $OLDDIR ]; then
    rm -rf $OLDDIR
fi

/sbin/umount /backup
/bin/rm -f /tmp/exclude.$$

logger -s -p user.notice -t backup "Backup complete at $BACKUPDIR."</pre>