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