Skip to content

Instantly share code, notes, and snippets.

@jmar71n
Created May 3, 2010 10:57
Show Gist options
  • Save jmar71n/387965 to your computer and use it in GitHub Desktop.
Save jmar71n/387965 to your computer and use it in GitHub Desktop.
incremental backup script for multiple computers and efficient use of disk space. Backups are based on a backup interval and no. of backups to keep.
#!/bin/bash
## incremental rsync backup script
## run hourly via cron, will take a back up every 24hrs for up to 7days.
## efficent use of space using hard links
## can handle computers been turned on / off
## executes n backup processes in parallel
# ------------- computers to backup -----------------------------------
# IP Address or fully qualified names here + remote backup directory
LOCATION=(
location1:/home
location2:/
location3:/
location4:/var/www
)
# ------------- backup interval in hours ------------------------------
BACKUP_INTERVAL=24
# ------------- number backups to keep --------------------------------
BACKUP_NO=14
# ------------- directory to save backups -----------------------------
SNAPSHOT_DIR=/tank/backups
# ------------- excludes ----------------------------------------------
EXCLUDES=(
dev
proc
sys
tmp
[cC]ache
.gvfs
)
# ------------ No. parallel jobs to execute ---------------------------
MAX_JOBS=2
# ---------------------------------------------------------------------
# --------------------------- script --------------------------------
# ---------------------------------------------------------------------
# make sure we're running as root, if not exit
if (( `id -u` != 0 )); then { echo "`date "+%b %d %T"`: Sorry, must be root. Exiting..."; exit; } fi
# if a backup process is already running, exit
lockdir=/tmp/rsync_backup.lock
if mkdir "$lockdir"; then
# Remove lockdir when the script finishes, or when it receives a signal
trap 'rm -rf "$lockdir"' 0 # remove directory when script finishes
#trap "exit 2" 1 2 3 15 # terminate script when receiving signal
else
echo "Cannot acquire lock (Already running). exiting..."
exit 0
fi
backup ()
(
ADDRESS=${LOCATION//:*/}
BACKUP_DIR=${LOCATION//*:/}
# if backup directory already exists, continue ...
if [ -d $SNAPSHOT_DIR/$ADDRESS ]; then
# if last backup was greater that $BACKUP_INTERVAL hrs ago, continue...
if test $(date -d "-$BACKUP_INTERVAL hours" +%s) -gt $(stat -c "%Y" $SNAPSHOT_DIR/$ADDRESS/backup.0); then
ping -c 1 $ADDRESS > /dev/null
if [ $? -eq 0 ]; then
echo "`date "+%b %d %T"`: Begining backup for $ADDRESS."
# delete the oldest snapshot, if it exists
if [ -d $SNAPSHOT_DIR/$ADDRESS/backup.$BACKUP_NO ] ; then
rm -rf $SNAPSHOT_DIR/$ADDRESS/backup.$BACKUP_NO
mv $SNAPSHOT_DIR/$ADDRESS/backup.$(($BACKUP_NO-1)) $SNAPSHOT_DIR/$ADDRESS/backup.$BACKUP_NO
fi
# shift the middle snapshot(s) back by one, if they exist
COUNT=$BACKUP_NO
while [ $COUNT -gt 2 ]; do
COUNT=$(($COUNT-1))
if [ -d $SNAPSHOT_DIR/$ADDRESS/backup.$(($COUNT-1)) ] ; then
mv $SNAPSHOT_DIR/$ADDRESS/backup.$(($COUNT-1)) $SNAPSHOT_DIR/$ADDRESS/backup.$COUNT
fi
done
# make a hard-link-only (except for dirs) copy of the latest snapshot, if that exists
if [ -d $SNAPSHOT_DIR/$ADDRESS/backup.0 ] ; then
cp -al $SNAPSHOT_DIR/$ADDRESS/backup.0 $SNAPSHOT_DIR/$ADDRESS/backup.1
fi
# rsync from the system into the latest snapshot
echo "Backing up $ADDRESS"
rsync -qaAXEW `exclude` --delete --link-dest=$SNAPSHOT_DIR/$ADDRESS/backup.1 $ADDRESS:$BACKUP_DIR $SNAPSHOT_DIR/$ADDRESS/backup.0
# update the mtime of backup dir to reflect the snapshot time
touch $SNAPSHOT_DIR/$ADDRESS/backup.0
else
ELAPSED="$((($(date +%s) - $(stat -c "%Y" $SNAPSHOT_DIR/$ADDRESS/backup.0))/3600))hrs"
echo "`date "+%b %d %T"`: $ADDRESS last backup $ELAPSED ago and has failed to respod to ping attempt "
fi
fi
else
# if backup directory does not already exist..
ping -c 1 $ADDRESS > /dev/null
if [ $? -eq 0 ]; then
echo "`date "+%b %d %T"`: First backup for $ADDRESS"
echo "Creating Backup Directory: $SNAPSHOT_DIR/$ADDRESS"
mkdir -p $SNAPSHOT_DIR/$ADDRESS
echo "Backing up $ADDRESS"
rsync -qaAXEW --stats `exclude` $ADDRESS:$BACKUP_DIR $SNAPSHOT_DIR/$ADDRESS/backup.0
touch $SNAPSHOT_DIR/$ADDRESS/backup.0
else
echo "`date "+%b %d %T"`: $ADDRESS hasn't been backed up yet and failed to respod to ping attempt "
fi
fi
)
exclude ()
{
for E in ${EXCLUDES[*]}; do
echo -n "--exclude=$E "
done
}
forky() {
while [[ $(jobs | wc -l) -ge $MAX_JOBS ]] ; do
sleep 10
done
}
if [[ $MAX_JOBS -eq 1 ]]; then
for LOCATION in ${LOCATION[*]}; do
backup
done
else
for LOCATION in ${LOCATION[*]}; do
backup &
forky
done
fi
wait
## Refrences...
## http://www.mikerubel.org/computers/rsync_snapshots/ by Mike Rubel.
## http://sourcebench.com/languages/bash/simple-parallel-processing-with-bash-using-wait-and-jobs/
## http://mywiki.wooledge.org/BashFAQ/045 ensure that only one instance of a script is running at a time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment