not working yet, but i am checking in changes
[matthijs/upstream/backupninja.git] / handlers / maildir
1 ###############################################################
2 #
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.
6 #
7 #  if destdir is /backup/maildir/, then it will contain the files
8 #    daily.1
9 #    daily.2
10 #    daily.3
11 #    weekly.1
12 #    weekly.2
13 #    monthly.1
14 #  if keepdaily is 3, keepweekly is 2, and keepmonthly is 1. 
15
16 ##############################################################
17
18 getconf rotate yes
19 getconf remove yes
20
21 getconf loadlimit 5
22 getconf speedlimit 0
23 getconf keepdaily 5
24 getconf keepweekly 3
25 getconf keepmonthly 1
26
27 getconf srcdir /var/maildir
28 getconf destdir
29 getconf desthost
30 getconf destport 22
31 getconf destuser
32
33 # strip trailing /
34 destdir=${destdir%/}
35 srcdir=${srcdir%/}
36
37 # used for testing
38 getconf letter
39 getconf user
40
41 [ -d $srcdir ] || fatal "source directory $srcdir doesn't exist"
42
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/*"'
48
49 # see if we can login
50 debug "ssh -o PasswordAuthentication=no $desthost -l $destuser 'echo -n 1'"
51 if [ ! $test ]; then
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."
55         fi
56 fi
57
58 ##################################################################
59 ### FUNCTIONS
60
61 # remote run the args remotely
62 function rrun() {
63         debug ssh -o PasswordAuthentication=no $desthost -l $destuser $@
64         if [ ! $test ]; then
65                 debug ssh -o PasswordAuthentication=no $desthost -l $destuser $@
66         fi
67 }
68
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"
72         done
73 }
74
75 function do_maildirs() {
76         local dir=$1
77         [ -d $dir ] || fatal "directory $dir not found."
78         for userdir in `ls -1 $dir`; do
79                 do_userdir $userdir
80         done
81 }
82
83 function do_user() {
84         local user=$1
85         local letter=${user:0:1}
86         local dir="$srcdir/$letter/$user"
87         [ -d $dir ] || fatal "maildir $dir not found".
88
89         while 1; do
90                 load=`uptime | sed 's/^.*load average: \\([^,]*\\).*$/\\1/'`
91                 if [ $load -lt $loadlimit ]; then
92                         info "load $load, sleeping..."
93                         sleep 600
94                 else
95                         break
96                 fi
97         done
98         
99         cmd="rsync $maildirrsyncflags $excludes '$dir' '$destuser@$desthost:$destdir/maildir/$letter'"
100         debug $cmd
101         # ret=`rsync $maildirrsyncflags $excludes '$dir' '$destuser@$desthost:$destdir/maildir/$letter' 2>&1`
102 }
103
104 # remove any maildirs from backup which might have been deleted
105 # and add new ones which have just been created.
106
107 function do_remove() {
108         local tmp1=/tmp/maildirtmpfile$$
109         local tmp2=/tmp/maildirtmpfile$$
110         
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/'"
116                         debug $cmd
117                 done
118         done
119         rm $tmp1
120         rm $tmp2        
121 }
122
123 function do_rotate() {
124         backuproot=$destdir
125         now=`date %s`
126         seconds_daily=86400
127         seconds_weekly=604800
128         seconds_monthly=2628000
129
130         ssh -o PasswordAuthentication=no $desthost -l $destuser <<EOF
131         keepdaily=$keepdaily
132         keepweekly=$keepweekly
133         keepmonthly=$keepmonthly
134
135         for rottype in daily weekly monthly; do
136                 seconds=\`echo seconds_\${rottype}\`
137
138                 dir="$backuproot/\$rottype"
139                 if [ ! -d \$dir.1 ]; then
140                         echo "Warning: \$dir.1 does not exist. This backup is missing, so we are skipping the rotation."
141                         continue
142                 elif [ ! -f \$dir.1/created ]; then
143                         echo "Warning: \$dir.1/created does not exist. This backup may be only partially completed, so we are skipping the rotation."
144                         continue
145                 fi
146                 
147                 # Rotate the current list of backups, if we can.
148                 oldest=\`ls -d $\dir.* | sed 's/^.*\.//' | sort -n | tail -1\`
149                 for (( i=\$oldest; i > 0; i-- )); do
150                         if [ -d \$dir.\$i ]; then
151                                 if [ -f \$dir.\$i/rotated ]; then
152                                         rotated=\`tail -1 \$dir.\$i/rotated\`
153                                 else
154                                         rotated=0
155                                 fi
156                                 cutoff_time=\$(( now - (seconds*i) ))
157                                 if [ \$rotated -gt \$cutoff_time ]; then
158                                         next=\$(( i + 1 ))
159                                         echo "mv \$dir.\$i \$dir.\$next"
160                                         mv \$dir.\$i \$dir.\$next
161                                         date +%c%n%s > \$dir.\$next/rotated
162                                 else
163                                         echo "Info: skipping rotation of \$dir.\$i because it was already rotated within the last " \$((cutoff_time/86400)) " days."
164                                 fi 
165                         fi
166                 done
167         done
168
169         max=\$((keepdaily+1))
170         if [ ( \$keepweekly -a -d $backuproot/daily.\$max ) -a ! -d $backuproot/weekly.1 ]; then
171                 echo mv $backuproot/daily.\$max $backuproot/weekly.1
172                 mv $backuproot/daily.\$max $backuproot/weekly.1
173                 date +%c%n%s > $backuproot/weekly.1/rotated
174         fi
175
176         max=\$((keepweekly+1))
177         if [ ( \$keepmonthly -a -d $backuproot/weekly.\$max ) -a ! -d $backuproot/monthly.1 ]; then
178                 echo mv $backuproot/weekly.\$max $backuproot/monthly.1
179                 mv $backuproot/weekly.\$max $backuproot/monthly.1
180                 date +%c%n%s > $backuproot/monthly.1/rotated
181         fi
182
183         for rottype in daily weekly monthly; do
184                 max=\`echo keep\${rottype}\`
185                 max=\$((max+1))
186                 dir="$backuproot/\$rottype"
187                 oldest=\`ls -d $\dir.* | sed 's/^.*\.//' | sort -n | tail -1\`
188
189                 # if we've rotated the last backup off the stack, remove it.
190                 for (( i=\$oldest; i >= \$max; i-- )); do
191                         if [ -d \$dir.\$i ]; then
192                                 echo "Info: removing \$dir.\$i"
193                                 rm -rf \$dir.\$i
194                         fi
195                 done
196         done
197 EOF
198 }
199
200
201 ###
202 ##################################################################
203
204 ### ROTATE BACKUPS ###
205
206 if [ "$rotate" == "yes" ]; then
207         do_rotate
208 fi
209
210 ### REMOVE OLD MAILDIRS ###
211
212 if [ "$remove" == "yes" ]; then
213         debug remove
214 fi
215
216 ### ROTATE BACKUPS ###
217
218 if [ "$letter" != "" ]; then
219         debug letter
220 fi
221
222 if [ "$user" != "" ]; then
223         debug user
224 fi