fixed configuration files permission check
[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 #   fsck = 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:
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 fsck
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   # TODO: force to an absolute path
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   fi [ ! -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 `ls $VROOTDIR`; do
261     found_excluded_vserver="0"
262     if [ "$candidate" != "lost+found" ]; then
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     fi
273   done
274 fi
275
276 # the backup procedure
277
278 for SECTION in $include; do
279
280   section="`basename $SECTION`"
281
282   if [ ! -d "$backupdir/$SECTION/$section.0" ]; then
283     mkdir -p $backupdir/$SECTION/$section.0
284   fi
285  
286   info "Rotating $backupdir/$SECTION/$section..."
287   echo "Rotating $backupdir/$SECTION/$section..." >> $log
288   rotate $backupdir/$SECTION/$section $keep
289   info "Syncing $SECTION on $backupdir/$SECTION/$section.0..."
290
291   if [ "$from" == "local" ]; then
292     debug $rsync $rsync_local_options $EXCLUDES /$SECTION/ $backupdir/$SECTION/$section.0/ 
293     $nice $rsync $rsync_local_options $EXCLUDES /$SECTION/ $backupdir/$SECTION/$section.0/ >> $log
294     if [ "$?" != "0" ]; then
295       warning "Rsync error when trying to transfer $SECTION"
296     fi
297   elif [ "$from" == "remote" ]; then
298     if [ -z "$user" ] || [ -z "$host" ]; then
299       error "Config file error: either user or host was not specified"
300       exit 1
301     else
302       debug $nice $rsync $rsync_remote_options $EXCLUDES -e "$ssh" $user@$host:/$SECTION/ $backupdir/$SECTION/$section.0
303       $nice $rsync $rsync_remote_options $EXCLUDES -e "$ssh" $user@$host:/$SECTION/ $backupdir/$SECTION/$section.0 >> $log
304       if [ "$?" != "0" ]; then
305         warning "Rsync error when trying to transfer $SECTION"
306       fi
307     fi
308   else
309     error "Invalid source $from"
310     exit 1
311   fi
312
313   $touch $backupdir/$SECTION/$section.0
314
315 done
316
317 # remount backup destination as read-only
318
319 if [ "$read_only" == "1" ] || [ "$read_only" == "yes" ]; then
320   mount -o remount,ro $mountpoint
321 fi
322
323 # check partition for errors
324
325 if [ "$fsck" == "1" ] || [ "$fsck" == "yes" ]; then
326   umount $mountpoint
327   if (($?)); then
328     warning "Could not umount $mountpoint to run fsck"
329   else
330     $nice $fsck -v -y $partition >> $log
331     mount $mountpoint
332   fi
333 fi
334
335 # restart services
336
337 if [ ! -z "$service" ]; then
338   for daemon in $service; do
339     info "Starting service $daemon..."
340     $initscripts/$daemon start
341   done
342 fi
343
344 # removes the lockfile
345
346 if [ ! -z "$lockfile" ]; then
347   $rm $lockfile || warning "Could not remove lockfile $lockfile"
348 fi
349
350 echo "Finnishing backup at `date`" >> $log
351