############################################################### # # This handler slowly creates a backup of each user's maildir # to a remote server. It is designed to be run with low overhead # in terms of cpu and bandwidth so it runs pretty slow. # # if destdir is /backup/maildir/, then it will contain the files # daily.1 # daily.2 # daily.3 # weekly.1 # weekly.2 # monthly.1 # if keepdaily is 3, keepweekly is 2, and keepmonthly is 1. # ############################################################## getconf rotate yes getconf remove yes getconf loadlimit 5 getconf speedlimit 0 getconf keepdaily 5 getconf keepweekly 3 getconf keepmonthly 1 getconf srcdir /var/maildir getconf destdir getconf desthost getconf destport 22 getconf destuser # strip trailing / destdir=${destdir%/} srcdir=${srcdir%/} # used for testing getconf letter getconf user [ -d $srcdir ] || fatal "source directory $srcdir doesn't exist" [ ! $test ] || testflags="--dry-run -v" rsyncflags="$testflags -e 'ssh -p $destport'" flags_mail="$rsyncflags --archive --ignore-existing --delete --numeric-ids --size-only --bwlimit=$speedlimit" flags_folders="$rsyncflags --archive --delete --numeric-ids" excludes='--exclude ".Trash/*" --exclude ".Mistakes/*" --exclude ".Spam/*"' # see if we can login debug "ssh -o PasswordAuthentication=no $desthost -l $destuser 'echo -n 1'" if [ ! $test ]; then result=`ssh -o PasswordAuthentication=no $desthost -l $destuser 'echo -n 1' 2>&1` if [ "$result" != "1" ]; then fatal "Can't connect to $desthost as $destuser." fi fi ################################################################## ### FUNCTIONS # remote run the args remotely function rrun() { debug ssh -o PasswordAuthentication=no $desthost -l $destuser $@ if [ ! $test ]; then debug ssh -o PasswordAuthentication=no $desthost -l $destuser $@ fi } function do_letters() { for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do do_maildirs "$srcdir/$i" done } function do_maildirs() { local dir=$1 [ -d $dir ] || fatal "directory $dir not found." for userdir in `ls -1 $dir`; do do_userdir $userdir done } function do_user() { local user=$1 local letter=${user:0:1} local dir="$srcdir/$letter/$user" [ -d $dir ] || fatal "maildir $dir not found". while 1; do load=`uptime | sed 's/^.*load average: \\([^,]*\\).*$/\\1/'` if [ $load -lt $loadlimit ]; then info "load $load, sleeping..." sleep 600 else break fi done cmd="rsync $maildirrsyncflags $excludes '$dir' '$destuser@$desthost:$destdir/maildir/$letter'" debug $cmd # ret=`rsync $maildirrsyncflags $excludes '$dir' '$destuser@$desthost:$destdir/maildir/$letter' 2>&1` } # remove any maildirs from backup which might have been deleted # and add new ones which have just been created. function do_remove() { local tmp1=/tmp/maildirtmpfile$$ local tmp2=/tmp/maildirtmpfile$$ for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do ls -1 "$srcdir/$i" | sort > $tmp1 ssh -p $destport $desthost ls -1 '$destdir/maildir/$i' | sort > $tmp2 for deluser in `join -v 2 $tmp1 $tmp2`; do cmd="ssh -p $destport $desthost rm -vr '$destdir/maildir/$i/$deluser/'" debug $cmd done done rm $tmp1 rm $tmp2 } function do_rotate() { backuproot=$destdir ssh -T -o PasswordAuthentication=no $desthost -l $destuser < 0; i-- )); do if [ -d \$dir.\$i ]; then if [ -f \$dir.\$i/rotated ]; then rotated=\`tail -1 \$dir.\$i/rotated\` else rotated=0 fi cutoff_time=\$(( now - (seconds*i) )) if [ \$rotated -lt \$cutoff_time ]; then next=\$(( i + 1 )) if [ ! -d \$dir.\$next ]; then echo "mv \$dir.\$i \$dir.\$next" mv \$dir.\$i \$dir.\$next date +%c%n%s > \$dir.\$next/rotated else echo "Info: skipping rotation of \$dir.\$i because \$dir.\$next already exists." fi else echo "Info: skipping rotation of \$dir.\$i because it was rotated" \$(( (now-rotated)/86400)) "days ago ("\$(( (now-cutoff_time)/86400))" needed)." fi fi done done max=\$((keepdaily+1)) if [ \( \$keepweekly -gt 0 -a -d $backuproot/daily.\$max \) -a ! -d $backuproot/weekly.1 ]; then echo mv $backuproot/daily.\$max $backuproot/weekly.1 mv $backuproot/daily.\$max $backuproot/weekly.1 date +%c%n%s > $backuproot/weekly.1/rotated fi max=\$((keepweekly+1)) if [ \( \$keepmonthly -gt 0 -a -d $backuproot/weekly.\$max \) -a ! -d $backuproot/monthly.1 ]; then echo mv $backuproot/weekly.\$max $backuproot/monthly.1 mv $backuproot/weekly.\$max $backuproot/monthly.1 date +%c%n%s > $backuproot/monthly.1/rotated fi for rottype in daily weekly monthly; do max=\$((keep\${rottype}+1)) dir="$backuproot/\$rottype" oldest=\`ls -d \$dir.* | sed 's/^.*\.//' | sort -n | tail -1\` [ "\$oldest" == "" ] && oldest=0 # if we've rotated the last backup off the stack, remove it. for (( i=\$oldest; i >= \$max; i-- )); do if [ -d \$dir.\$i ]; then echo "Info: removing \$dir.\$i" rm -rf \$dir.\$i fi done done ####### END REMOTE SCRIPT ####### EOF } ### ################################################################## ### ROTATE BACKUPS ### if [ "$rotate" == "yes" ]; then do_rotate fi ### REMOVE OLD MAILDIRS ### if [ "$remove" == "yes" ]; then debug remove fi ### ROTATE BACKUPS ### if [ "$letter" != "" ]; then debug letter fi if [ "$user" != "" ]; then debug user fi