Created
May 3, 2010 10:57
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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