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