#!/bin/bash # This script makes a snapshot of several directories using rsync. # It's based on Rob Bos's snapshot script found at mikerubel.org. # Version 0.1, Geordy Kitchen, November 26, 2002 # Version 0.2 George A. Nelson, 17 Jul 07 Extensive changes # Version 0.3 George A. Nelson, 22 Apr 09 -R option # Wildcard allowed in source directories; use in place of *. # Uses oldest available backup when promoting, for initial startup # and so a missing backup won't continue to gum up the works. # Date each operation. # All directories processed in one connection. # This script is released under the terms of the GNU General Public License, # version 2.0. # Your crontab will typically look something like this: # 0 */4 * * * /bin/this_script hourly # 50 3 * * * /bin/this_script daily # 40 3 * * 0 /bin/this_script weekly # 30 3 1 * * /bin/this_script monthly # #GAN My crontab keeps a log and looks like: #55 3,7,11,15,19,23 * * * ( nice $HOME/geordy_backup hourly | grep -v ' is uptodate' | grep -v '^[^ ]\+/$' >>$HOME/geordy_backup.out ) 2>&1 #46 3 * * * ( $HOME/geordy_backup daily >>$HOME/geordy_backup.out ) 2>&1 #41 3 * * 0 ( $HOME/geordy_backup weekly >>$HOME/geordy_backup.out ) 2>&1 #36 3 1 * * ( $HOME/geordy_backup monthly >>$HOME/geordy_backup.out ) 2>&1 # Configuration # Destination directory. dst=/mnt/backups/example.com.d srcroot="root@example.com" # Use a trailing slash for directories, just the filename for files. src=' /home/ /root/ /var/www/ /var/local/ /var/log/ /var/spool/ /etc/ /boot/ /opt/ /usr/local/ ' src=`echo $src` # Rsync filters, usually - (exclude) paths of files or directories to skip. filter=' ' #-_/home/auser/growing.log #-_/home//.gvfs/ filter=`echo $filter` # Number of times to run per day. hourly=6 # Constants daily=7 weekly=4 monthly=60 now=1000 # Promote a backup to the next higher time period. # ex: promote hourly daily promote () { local i for i in `seq ${!1} -1 1` ; do if [ -d /$dst/$1.$i ] ; then mv $dst/$1.$i $dst/$2.0 return fi done } echo echo "##### Incremental backup $1 $(date)" if [ "${2:-}" = "warm" ] && /sbin/service httpd status &>/dev/null ; then echo "##### Backup server is active, skipping backup" exit 0 fi case $1 in hourly|now) echo echo "***** $src" [ "$filter" != "" ] && filter=`echo \-\-filter=$filter | sed 's||*|g' | sed 's/ / --filter=/'` rsync -aHCR --delete -vv --timeout=120 \ --one-file-system \ --link-dest=$dst/$1.1/ \ --link-dest=$dst/daily.1/ \ --link-dest=$dst/weekly.1/ \ --link-dest=$dst/monthly.1/ \ -F -F $filter \ $srcroot:"`echo $src | sed 's||*|g'`" $dst/$1.0/ #Ehh, must remove double quotes above if local backup, sigh. ;; daily) #[ -d $dst/hourly.$hourly ] && mv $dst/hourly.$hourly $dst/daily.0 promote hourly daily ;; weekly) #[ -d $dst/daily.$daily ] && mv $dst/daily.$daily $dst/weekly.0 promote daily weekly ;; monthly) #[ -d $dst/weekly.$weekly ] && mv $dst/weekly.$weekly $dst/monthly.0 promote weekly monthly ;; *) echo "usage: $0 [warm]" echo " period := hourly | daily | weekly | monthly" echo " Run '$0 hourly' $hourly times per day." echo " Keeps $hourly hourly, $daily daily, $weekly weekly, and $monthly monthly backups." echo " Warm only runs if httpd is stopped." exit 1 ;; esac # Rotate the current list of backups, if we can. # ${!1} --> indirection to the number of backups of that period. if [ -d $dst/$1.0 ]; then #oldest=`ls -d $dst/$1.* | tail -n 1 | sed 's/^.*\.//'` #for i in `seq $oldest 0`; do for i in `seq ${!1} -1 0` ; do [ -d $dst/$1.$i ] && mv $dst/$1.$i $dst/$1.$((i+1)) done # if we've rotated the last backup off the stack, remove it. [ -d $dst/$1.$((${!1}+1)) ] && rm -rf $dst/$1.$((${!1}+1)) # finally, let's make the new snapshot reflect the current date. [ -d $dst/$1.1 ] && touch $dst/$1.1 && touch $dst/$1.1/"0$1 $(date)" fi echo echo "----- Backup complete $(date)"