1 ###############################################################
3 # This handler slowly creates a backup of each user's maildir
4 # to a remote server. It is designed to be run with low overhead
5 # in terms of cpu and bandwidth so it runs pretty slow.
7 # if destdir is /backup/maildir/, then it will contain the files
14 # if keepdaily is 3, keepweekly is 2, and keepmonthly is 1.
16 ##############################################################
27 getconf srcdir /var/maildir
41 [ -d $srcdir ] || fatal "source directory $srcdir doesn't exist"
43 [ ! $test ] || testflags="--dry-run -v"
44 rsyncflags="$testflags -e 'ssh -p $destport'"
45 flags_mail="$rsyncflags --archive --ignore-existing --delete --numeric-ids --size-only --bwlimit=$speedlimit"
46 flags_folders="$rsyncflags --archive --delete --numeric-ids"
47 excludes='--exclude ".Trash/*" --exclude ".Mistakes/*" --exclude ".Spam/*"'
50 debug "ssh -o PasswordAuthentication=no $desthost -l $destuser 'echo -n 1'"
52 result=`ssh -o PasswordAuthentication=no $desthost -l $destuser 'echo -n 1' 2>&1`
53 if [ "$result" != "1" ]; then
54 fatal "Can't connect to $desthost as $destuser."
58 ##################################################################
61 # remote run the args remotely
63 debug ssh -o PasswordAuthentication=no $desthost -l $destuser $@
65 debug ssh -o PasswordAuthentication=no $desthost -l $destuser $@
69 function do_letters() {
70 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
71 do_maildirs "$srcdir/$i"
75 function do_maildirs() {
77 [ -d $dir ] || fatal "directory $dir not found."
78 for userdir in `ls -1 $dir`; do
85 local letter=${user:0:1}
86 local dir="$srcdir/$letter/$user"
87 [ -d $dir ] || fatal "maildir $dir not found".
90 load=`uptime | sed 's/^.*load average: \\([^,]*\\).*$/\\1/'`
91 if [ $load -lt $loadlimit ]; then
92 info "load $load, sleeping..."
99 cmd="rsync $maildirrsyncflags $excludes '$dir' '$destuser@$desthost:$destdir/maildir/$letter'"
101 # ret=`rsync $maildirrsyncflags $excludes '$dir' '$destuser@$desthost:$destdir/maildir/$letter' 2>&1`
104 # remove any maildirs from backup which might have been deleted
105 # and add new ones which have just been created.
107 function do_remove() {
108 local tmp1=/tmp/maildirtmpfile$$
109 local tmp2=/tmp/maildirtmpfile$$
111 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
112 ls -1 "$srcdir/$i" | sort > $tmp1
113 ssh -p $destport $desthost ls -1 '$destdir/maildir/$i' | sort > $tmp2
114 for deluser in `join -v 2 $tmp1 $tmp2`; do
115 cmd="ssh -p $destport $desthost rm -vr '$destdir/maildir/$i/$deluser/'"
123 function do_rotate() {
126 ssh -T -o PasswordAuthentication=no $desthost -l $destuser <<EOF
127 ##### BEGIN REMOTE SCRIPT #####
129 seconds_weekly=604800
130 seconds_monthly=2628000
132 keepweekly=$keepweekly
133 keepmonthly=$keepmonthly
136 for rottype in daily weekly monthly; do
137 seconds=\$((seconds_\${rottype}))
139 dir="$backuproot/\$rottype"
140 if [ ! -d \$dir.1 ]; then
141 echo "Info: \$dir.1 does not exist. This backup is missing, so we are skipping the rotation."
143 elif [ ! -f \$dir.1/created ]; then
144 echo "Warning: \$dir.1/created does not exist. This backup may be only partially completed. Skipping rotation."
148 # Rotate the current list of backups, if we can.
149 oldest=\`ls -d \$dir.* | sed 's/^.*\.//' | sort -n | tail -1\`
150 [ "\$oldest" == "" ] && oldest=0
151 for (( i=\$oldest; i > 0; i-- )); do
152 if [ -d \$dir.\$i ]; then
153 if [ -f \$dir.\$i/rotated ]; then
154 rotated=\`tail -1 \$dir.\$i/rotated\`
158 cutoff_time=\$(( now - (seconds*i) ))
159 if [ \$rotated -lt \$cutoff_time ]; then
161 if [ ! -d \$dir.\$next ]; then
162 echo "mv \$dir.\$i \$dir.\$next"
163 mv \$dir.\$i \$dir.\$next
164 date +%c%n%s > \$dir.\$next/rotated
166 echo "Info: skipping rotation of \$dir.\$i because \$dir.\$next already exists."
169 echo "Info: skipping rotation of \$dir.\$i because it was rotated" \$(( (now-rotated)/86400)) "days ago ("\$(( (now-cutoff_time)/86400))" needed)."
175 max=\$((keepdaily+1))
176 if [ \( \$keepweekly -gt 0 -a -d $backuproot/daily.\$max \) -a ! -d $backuproot/weekly.1 ]; then
177 echo mv $backuproot/daily.\$max $backuproot/weekly.1
178 mv $backuproot/daily.\$max $backuproot/weekly.1
179 date +%c%n%s > $backuproot/weekly.1/rotated
182 max=\$((keepweekly+1))
183 if [ \( \$keepmonthly -gt 0 -a -d $backuproot/weekly.\$max \) -a ! -d $backuproot/monthly.1 ]; then
184 echo mv $backuproot/weekly.\$max $backuproot/monthly.1
185 mv $backuproot/weekly.\$max $backuproot/monthly.1
186 date +%c%n%s > $backuproot/monthly.1/rotated
189 for rottype in daily weekly monthly; do
190 max=\$((keep\${rottype}+1))
191 dir="$backuproot/\$rottype"
192 oldest=\`ls -d \$dir.* | sed 's/^.*\.//' | sort -n | tail -1\`
193 [ "\$oldest" == "" ] && oldest=0
194 # if we've rotated the last backup off the stack, remove it.
195 for (( i=\$oldest; i >= \$max; i-- )); do
196 if [ -d \$dir.\$i ]; then
197 echo "Info: removing \$dir.\$i"
202 ####### END REMOTE SCRIPT #######
208 ##################################################################
210 ### ROTATE BACKUPS ###
212 if [ "$rotate" == "yes" ]; then
216 ### REMOVE OLD MAILDIRS ###
218 if [ "$remove" == "yes" ]; then
222 ### ROTATE BACKUPS ###
224 if [ "$letter" != "" ]; then
228 if [ "$user" != "" ]; then