Make all indentation consistent.
[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