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