dup: added option --force to cleanup and remove-older-than commands, else they actual...
[matthijs/upstream/backupninja.git] / handlers / rsync.in
1 #
2 # backupninja handler to do incremental backups using
3 # rsync and hardlinks, based on
4 #
5 #   http://www.mikerubel.org/computers/rsync_snapshots/
6 #
7 # feedback: rhatto at riseup.net | gpl
8 # lot of enhancements grabbed from "rsnap" handler by paulv at bikkel.org
9 #
10 # Config file options
11 # -------------------
12 #
13 #   [general]
14 #   log = rsync log file
15 #   partition = partition where the backup lives
16 #   fscheck = set to 1 if fsck should run on $partition after the backup is made
17 #   read_only = set to 1 if $partition is mounted read-only
18 #   mountpoint = backup partition mountpoint or backup main folder
19 #   backupdir = folder relative do $mountpoint where the backup should be stored
20 #   days = number of backup increments (min = 5)
21 #   lockfile = lockfile to be kept during backup execution
22 #   nicelevel = rsync command nice level
23 #   enable_mv_timestamp_bug = set to "yes" if your system isnt handling timestamps correctly
24 #   tmp = temp folder
25 #
26 #   [source]
27 #   from = local or remote
28 #   host = source hostname or ip, if remote backup
29 #   testconnect = when "yes", test the connection for a remote source before backup
30 #   include = include folder on backup
31 #   exclude = exclude folder on backup
32 #   ssh = ssh command line (remote only)
33 #   rsync = rsync program
34 #   rsync_options = rsync command options
35 #   exclude_vserver = vserver-name (valid only if vservers = yes on backupninja.conf)
36 #   numericids = when set to 1, use numeric ids instead of user/group mappings on rsync
37 #   compress = if set to 1, compress data on rsync (remote source only)
38 #   bandwidthlimit = set a badnwidth limit in kbps (remote source only)
39 #   remote_rsync = remote rsync program (remote source only)
40 #
41 #   [services]
42 #   initscripts = absolute path where scripts are located
43 #   service = script name to be stoped at the begining of the backup and started at its end
44 #
45 # You can also specify some system comands if you don't want the default system values:
46 #
47 #   [system]
48 #   rm = rm command
49 #   cp = cp command
50 #   touch = touch command
51 #   mv = mv command
52 #   fsck = fsck command
53 #
54 # You dont need to manually specify vservers using "include = /vservers".
55 # They are automatically backuped if vserver is set to "yes" on you backupninja.conf.
56 #
57
58 # config file evaluation
59
60 setsection system
61 getconf rm rm
62 getconf cp cp
63 getconf touch touch
64 getconf mv mv
65 getconf fsck fsck
66
67 setsection general
68 getconf log /var/log/backup/rsync.log
69 getconf partition
70 getconf fscheck
71 getconf read_only
72 getconf mountpoint
73 getconf backupdir
74 getconf rotate
75 getconf days
76 getconf lockfile
77 getconf nicelevel 0
78 getconf enable_mv_timestamp_bug no
79 getconf tmp /tmp
80
81 setsection source
82 getconf from local
83 getconf testconnect no
84 getconf rsync $RSYNC
85 getconf rsync_options "-av --delete"
86 getconf ssh ssh
87 getconf user
88 getconf host
89 getconf include
90 getconf exclude
91 getconf exclude_vserver
92 getconf numericids 0
93 getconf compress 0
94 getconf bandwidthlimit
95 getconf remote_rsync rsync
96
97 setsection services
98 getconf initscripts
99 getconf service
100
101 # function definitions
102
103 function rotate {
104
105   if [[ "$2" < 4 ]]; then
106     error "Rotate: minimum of 4 rotations"
107     exit 1
108   fi
109
110   if [ -d $1.$2 ]; then
111     $nice $mv /$1.$2 /$1.tmp
112   fi
113
114   for ((n=`echo "$2 - 1" | bc`; n >= 0; n--)); do
115     if [ -d $1.$n ]; then
116       dest=`echo "$n + 1" | bc`
117       $nice $mv /$1.$n /$1.$dest
118       $touch /$1.$dest
119     fi
120   done
121
122   if [ -d $1.tmp ]; then
123     $nice $mv /$1.tmp /$1.0
124   fi
125
126   if [ -d $1.1 ]; then
127     $nice $cp -alf /$1.1/. /$1.0
128   fi
129
130 }
131
132 function move_files {
133
134  ref=$tmp/makesnapshot-mymv-$$;
135  $touch -r $1 $ref;
136  $mv $1 $2;
137  $touch -r $ref $2;
138  $rm $ref;
139
140 }
141
142 backupdir="$mountpoint/$backupdir"
143
144 # does $backupdir exists?
145
146 if [ ! -d "$backupdir" ]; then 
147   error "Backupdir $backupdir does not exist"
148   exit 1
149 fi
150
151 # setup number of increments
152
153 if [ -z "$days" ]; then
154   keep="4"
155 else
156   keep="`echo $days - 1 | bc -l`"
157 fi
158
159 # lockfile setup
160
161 if [ ! -z "$lockfile" ]; then
162   $touch $lockfile || warning "Could not create lockfile $lockfile"
163 fi
164
165 # nicelevel setup
166
167 if [ ! -z "$nicelevel" ]; then 
168   nice="nice -n $nicelevel"
169 else 
170   nice=""
171 fi
172
173 # connection test
174
175 if [ "$from" == "remote" ] && [ "$testconnect" == "yes" ]; then
176   debug "$ssh -o PasswordAuthentication=no $user@$host 'echo -n 1'"
177   result=`ssh -o PasswordAuthentication=no $user@$host 'echo -n 1'`
178   if [ "$result" != "1" ]; then
179     fatal "Can't connect to $host as $user."
180   else
181     debug "Connected to $srchost successfully"
182   fi
183 fi
184
185 # rsync options for local sources
186
187 if [ "$from" == "local" ]; then
188
189   rsync_local_options="$rsync_options"
190
191   if [ ! -z "$numericids" ]; then
192     rsync_local_options="$rsync_local_options --numeric-ids "
193   fi
194
195 fi
196
197 # rsync options for remote sources
198
199 if [ "$from" == "remote" ]; then
200
201   rsync_remote_options="$rsync_options --rsync-path=$remote_rsync"
202
203   if [ "$compress" == "1" ]; then
204     rsync_remote_options="$rsync_remote_options --compress"
205   fi
206
207   if [ ! -z "$bandwidthlimit" ]; then
208     rsync_remote_options="$rsync_remote_options --bwlimit=$bandwidthlimit"
209   fi
210
211   if [ ! -z "$numericids" ]; then
212     rsync_remote_options="$rsync_remote_options --numeric-ids"
213   fi
214
215 fi
216
217 # set mv procedure
218
219 if [ $enable_mv_timestamp_bug == "yes" ]; then
220   mv=move_files
221 fi
222
223 # set excludes
224
225 for path in $exclude; do
226   EXCLUDES="$EXCLUDES --exclude=$path"
227 done
228
229 # stop services
230
231 if [ ! -z "$service" ]; then
232   for daemon in $service; do
233     info "Stopping service $daemon..."
234     $initscripts/$daemon stop
235   done
236 fi
237
238 echo "Starting backup at `date`" >> $log
239
240 # mount backup destination folder as read-write
241
242 if [ "$read_only" == "1" ] || [ "$read_only" == "yes" ]; then
243   if [ -d "$mountpoint" ]; then
244     mount -o remount,rw $mountpoint
245     if (($?)); then
246       error "Could not mount $mountpoint"
247       exit 1
248     fi
249   fi
250 fi
251
252 # add vservers to included folders
253
254 if [ "$vservers_are_available" == "yes" ]; then
255
256   # sane permission on backup
257   mkdir -p $backupdir/$VROOTDIR
258   chmod 000 $backupdir/$VROOTDIR
259
260   for candidate in $found_vservers; do
261     candidate="`basename $candidate`"
262     found_excluded_vserver="0"
263     for excluded_vserver in $exclude_vserver; do
264       if [ "$excluded_vserver" == "$candidate" ]; then
265         found_excluded_vserver="1"
266         break
267       fi
268     done
269     if [ "$found_excluded_vserver" == "0" ]; then
270       include="$include $VROOTDIR/$candidate"
271     fi
272   done
273 fi
274
275 # the backup procedure
276
277 for SECTION in $include; do
278
279   section="`basename $SECTION`"
280
281   if [ ! -d "$backupdir/$SECTION/$section.0" ]; then
282     mkdir -p $backupdir/$SECTION/$section.0
283   fi
284  
285   info "Rotating $backupdir/$SECTION/$section..."
286   echo "Rotating $backupdir/$SECTION/$section..." >> $log
287   rotate $backupdir/$SECTION/$section $keep
288   info "Syncing $SECTION on $backupdir/$SECTION/$section.0..."
289
290   if [ "$from" == "local" ]; then
291     debug $rsync $rsync_local_options $EXCLUDES /$SECTION/ $backupdir/$SECTION/$section.0/ 
292     $nice $rsync $rsync_local_options $EXCLUDES /$SECTION/ $backupdir/$SECTION/$section.0/ >> $log
293     if [ "$?" != "0" ]; then
294       warning "Rsync error when trying to transfer $SECTION"
295     fi
296   elif [ "$from" == "remote" ]; then
297     if [ -z "$user" ] || [ -z "$host" ]; then
298       error "Config file error: either user or host was not specified"
299       exit 1
300     else
301       debug $nice $rsync $rsync_remote_options $EXCLUDES -e "$ssh" $user@$host:/$SECTION/ $backupdir/$SECTION/$section.0
302       $nice $rsync $rsync_remote_options $EXCLUDES -e "$ssh" $user@$host:/$SECTION/ $backupdir/$SECTION/$section.0 >> $log
303       if [ "$?" != "0" ]; then
304         warning "Rsync error when trying to transfer $SECTION"
305       fi
306     fi
307   else
308     error "Invalid source $from"
309     exit 1
310   fi
311
312   $touch $backupdir/$SECTION/$section.0
313
314 done
315
316 # remount backup destination as read-only
317
318 if [ "$read_only" == "1" ] || [ "$read_only" == "yes" ]; then
319   mount -o remount,ro $mountpoint
320 fi
321
322 # check partition for errors
323
324 if [ "$fscheck" == "1" ] || [ "$fscheck" == "yes" ]; then
325   umount $mountpoint
326   if (($?)); then
327     warning "Could not umount $mountpoint to run fsck"
328   else
329     $nice $fsck -v -y $partition >> $log
330     mount $mountpoint
331   fi
332 fi
333
334 # restart services
335
336 if [ ! -z "$service" ]; then
337   for daemon in $service; do
338     info "Starting service $daemon..."
339     $initscripts/$daemon start
340   done
341 fi
342
343 # removes the lockfile
344
345 if [ ! -z "$lockfile" ]; then
346   $rm $lockfile || warning "Could not remove lockfile $lockfile"
347 fi
348
349 echo "Finnishing backup at `date`" >> $log
350