tar: Support running on multiple backends.
[matthijs/upstream/backupninja.git] / handlers / dup.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 # duplicity script for backupninja
5 # requires duplicity
6 #
7
8 getconf options
9 getconf testconnect yes
10 getconf nicelevel 0
11 getconf tmpdir
12
13 setsection gpg
14 getconf password
15 getconf sign no
16 getconf encryptkey
17 getconf signkey
18
19 setsection source
20 getconf include
21 getconf vsnames all
22 getconf vsinclude
23 getconf exclude
24
25 setsection dest
26 getconf incremental yes
27 getconf keep 60
28 getconf desturl
29 getconf awsaccesskeyid
30 getconf awssecretaccesskey
31 getconf sshoptions
32 getconf bandwidthlimit 0
33 getconf desthost
34 getconf destdir
35 getconf destuser
36 destdir=${destdir%/}
37
38 ### SANITY CHECKS ##############################################################
39
40 [ -n "$desturl" -o -n "$destdir" ]  || fatal "The destination directory (destdir) must be set when desturl is not used."
41 [ -n "$include" -o -n "$vsinclude" ]  || fatal "No source includes specified"
42 [ -n "$password" ] || fatal "The password option must be set."
43 if [ "`echo $desturl | @AWK@ -F ':' '{print $1}'`" == "s3+http" ]; then
44    [ -n "$awsaccesskeyid" -a -n "$awssecretaccesskey" ]  || fatal "AWS access keys must be set for S3 backups."
45 fi
46
47 ### VServers
48 # If vservers are configured, check that the ones listed in $vsnames do exist.
49 local usevserver=no
50 if [ $vservers_are_available = yes ]; then
51    if [ "$vsnames" = all ]; then
52       vsnames="$found_vservers"
53    else
54       if ! vservers_exist "$vsnames" ; then
55             fatal "At least one of the vservers listed in vsnames ($vsnames) does not exist."
56       fi
57    fi
58    if [ -n "$vsinclude" ]; then
59       info "Using vservers '$vsnames'"
60       usevserver=yes
61    fi
62 else
63    [ -z "$vsinclude" ] || warning 'vservers support disabled in backupninja.conf, vsincludes configuration lines will be ignored'
64 fi
65
66 ### See if we can login on $desthost
67 if [ "$testconnect" == "yes" ]; then
68    if [ -n "$desturl" ]; then
69       warning 'testconnect can not be used when desturl is set'
70    else
71       debug "ssh $sshoptions -o PasswordAuthentication=no $desthost -l $destuser 'echo -n 1'"
72       if [ ! $test ]; then
73          result=`ssh $sshoptions -o PasswordAuthentication=no $desthost -l $destuser 'echo -n 1'`
74          if [ "$result" != "1" ]; then
75             fatal "Can't connect to $desthost as $destuser."
76          else
77             debug "Connected to $desthost as $destuser successfully"
78          fi
79       fi
80    fi
81 fi
82
83 ### COMMAND-LINE MANGLING ######################################################
84
85 ### initialize $execstr*
86 execstr_command=
87 execstr_options="$options --no-print-statistics"
88 execstr_source=
89 if [ -n "$desturl" ]; then
90    [ -z "$destuser" ] || warning 'the configured destuser is ignored since desturl is set'
91    [ -z "$desthost" ] || warning 'the configured desthost is ignored since desturl is set'
92    [ -z "$destdir" ] || warning 'the configured destdir is ignored since desturl is set'
93    execstr_serverpart="$desturl"
94 else
95    execstr_serverpart="scp://$destuser@$desthost/$destdir"
96 fi
97
98 ### duplicity version
99 duplicity_version="`duplicity --version | @AWK@ '{print $2}'`"
100 duplicity_major="`echo $duplicity_version | @AWK@ -F '.' '{print $1}'`"
101 duplicity_minor="`echo $duplicity_version | @AWK@ -F '.' '{print $2}'`"
102 duplicity_sub="`echo $duplicity_version | @AWK@ -F '.' '{print $3}'`"
103
104 ### ssh/scp/sftp options
105 # 1. duplicity >= 0.4.2 needs --sftp-command
106 #    (NB: sftp does not support the -l option)
107 # 2. duplicity 0.4.3 to 0.4.9 replace --ssh-command with --ssh-options, which is
108 #    passed to scp and sftp commands by duplicity. We don't use it: since this
109 #    version does not use the ssh command anymore, we keep compatibility with
110 #    our previous config files by passing $sshoptions to --scp-command and
111 #    --sftp-command ourselves
112
113 scpoptions="$sshoptions"
114 if [ "$bandwidthlimit" != 0 ]; then
115    [ -z "$desturl" ] || warning 'The bandwidthlimit option is not used when desturl is set.'
116    scpoptions="$scpoptions -l $bandwidthlimit"
117 fi
118
119 # < 0.4.2 : only uses ssh and scp
120 if [ "$duplicity_major" -le 0 -a "$duplicity_minor" -le 4 -a "$duplicity_sub" -lt 2 ]; then
121    execstr_options="${execstr_options} --scp-command 'scp $scpoptions' --ssh-command 'ssh $sshoptions'"
122 # >= 0.4.2 : also uses sftp, --sftp-command option is now supported
123 else
124    sftpoptions="$sshoptions"
125    # == 0.4.2 : uses ssh, scp and sftp
126    if [ "$duplicity_major" -eq 0 -a "$duplicity_minor" -eq 4 -a "$duplicity_sub" -eq 2 ]; then
127       execstr_options="${execstr_options} --scp-command 'scp $scpoptions' --sftp-command 'sftp $sftpoptions' --ssh-command 'ssh $sshoptions'"
128    # >= 0.4.3 : uses only scp and sftp, --ssh-command option is not supported anymore
129    else
130       execstr_options="${execstr_options} --scp-command 'scp $scpoptions' --sftp-command 'sftp $sftpoptions'"
131    fi
132 fi
133
134 ### Symmetric or asymmetric (public/private key pair) encryption
135 if [ -n "$encryptkey" ]; then
136    execstr_options="${execstr_options} --encrypt-key $encryptkey"
137    debug "Data will be encrypted with the GnuPG key $encryptkey."
138 else
139    debug "Data will be encrypted using symmetric encryption."
140 fi
141
142 ### Data signing (or not)
143 if [ "$sign" == yes ]; then
144    # duplicity is not able to sign data when using symmetric encryption
145    [ -n "$encryptkey" ] || fatal "The encryptkey option must be set when signing."
146    # if needed, initialize signkey to a value that is not empty (checked above)
147    [ -n "$signkey" ] || signkey="$encryptkey"
148    execstr_options="${execstr_options} --sign-key $signkey"
149    debug "Data will be signed will the GnuPG key $signkey."
150 else
151    debug "Data won't be signed."
152 fi
153
154 ### Incremental or full backup mode
155 # If incremental==yes, use the default duplicity behaviour: perform an
156 # incremental backup if old signatures can be found, else switch to
157 # full backup.
158 # If incremental==no, force a full backup anyway.
159 if [ "$incremental" == "no" ]; then
160    # before 0.4.4, full was an option and not a command
161    if [ "$duplicity_major" -le 0 -a "$duplicity_minor" -le 4 -a "$duplicity_sub" -lt 4 ]; then
162       execstr_options="${execstr_options} --full"
163    else
164       execstr_command="full"
165    fi
166 fi
167
168 ### Cleanup options
169 if [ "$duplicity_major" -ge 0 -a "$duplicity_minor" -ge 6 -a "$duplicity_sub" -ge 1 ]; then
170    execstr_options="${execstr_options} --extra-clean"
171 fi
172
173 ### Temporary directory
174 precmd=
175 if [ -n "$tmpdir" ]; then
176    if [ ! -d "$tmpdir" ]; then
177       info "Temporary directory ($tmpdir) does not exist, creating it."
178       mkdir -p "$tmpdir"
179       [ $? -eq 0 ] || fatal "Could not create temporary directory ($tmpdir)."
180       chmod 0700 "$tmpdir"
181    fi
182    info "Using $tmpdir as TMPDIR"
183    precmd="${precmd}TMPDIR=$tmpdir "
184 fi
185
186 ### Cleanup old backup sets (or not)
187 if [ "$keep" != "yes" ]; then
188    if [ "`echo $keep | tr -d 0-9`" == "" ]; then
189       keep="${keep}D"
190    fi
191    # before 0.4.4, remove-older-than was an option and not a command
192    if [ "$duplicity_major" -le 0 -a "$duplicity_minor" -le 4 -a "$duplicity_sub" -lt 4 ]; then
193       execstr_options="${execstr_options} --remove-older-than $keep"
194    fi
195 fi
196
197 ### Source
198
199 set -o noglob
200
201 # excludes
202 SAVEIFS=$IFS
203 IFS=$(echo -en "\n\b")
204 for i in $exclude; do
205    str="${i//__star__/*}"
206    execstr_source="${execstr_source} --exclude '$str'"
207 done
208 IFS=$SAVEIFS
209
210 # includes
211 SAVEIFS=$IFS
212 IFS=$(echo -en "\n\b")
213 for i in $include; do
214    [ "$i" != "/" ] || fatal "Sorry, you cannot use 'include = /'"
215    str="${i//__star__/*}"
216    execstr_source="${execstr_source} --include '$str'"
217 done
218 IFS=$SAVEIFS
219
220 # vsincludes
221 if [ $usevserver = yes ]; then
222    for vserver in $vsnames; do
223       SAVEIFS=$IFS
224       IFS=$(echo -en "\n\b")
225       for vi in $vsinclude; do
226          str="${vi//__star__/*}"
227          str="$VROOTDIR/$vserver$str"
228          execstr_source="${execstr_source} --include '$str'"
229       done
230       IFS=$SAVEIFS
231    done
232 fi
233
234 set +o noglob
235
236 ### EXECUTE ####################################################################
237
238 execstr_source=${execstr_source//\\*/\\\\\\*}
239
240 ### If desturl is an S3 URL export the AWS environment variables
241 if [ "`echo $desturl | @AWK@ -F ':' '{print $1}'`" == "s3+http" ]; then
242    export AWS_ACCESS_KEY_ID="$awsaccesskeyid"
243    export AWS_SECRET_ACCESS_KEY="$awssecretaccesskey"
244 fi
245
246 ### Cleanup commands (duplicity >= 0.4.4)
247
248 # cleanup
249 if [ "$duplicity_major" -ge 0 -a "$duplicity_minor" -ge 4 -a "$duplicity_sub" -ge 4 ]; then
250    debug "$precmd duplicity cleanup --force $execstr_options $execstr_serverpart"
251    if [ ! $test ]; then
252       export PASSPHRASE=$password
253       output=`nice -n $nicelevel \
254          su -c \
255          "$precmd duplicity cleanup --force $execstr_options $execstr_serverpart 2>&1"`
256       exit_code=$?
257       if [ $exit_code -eq 0 ]; then
258          debug $output
259          info "Duplicity cleanup finished successfully."
260       else
261          debug $output
262          warning "Duplicity cleanup failed."
263       fi
264    fi
265 fi
266
267 # remove-older-than
268 if [ "$keep" != "yes" ]; then
269    if [ "$duplicity_major" -ge 0 -a "$duplicity_minor" -ge 4 -a "$duplicity_sub" -ge 4 ]; then
270       debug "$precmd duplicity remove-older-than $keep --force $execstr_options $execstr_serverpart"
271       if [ ! $test ]; then
272          export PASSPHRASE=$password
273          output=`nice -n $nicelevel \
274                    su -c \
275                       "$precmd duplicity remove-older-than $keep --force $execstr_options $execstr_serverpart 2>&1"`
276          exit_code=$?
277          if [ $exit_code -eq 0 ]; then
278             debug $output
279             info "Duplicity remove-older-than finished successfully."
280          else
281             debug $output
282             warning "Duplicity remove-older-than failed."
283          fi
284       fi
285    fi
286 fi
287
288 ### Backup command
289 debug "$precmd duplicity $execstr_command $execstr_options $execstr_source --exclude '**' / $execstr_serverpart"
290 if [ ! $test ]; then
291    outputfile=`maketemp backupout`
292    export PASSPHRASE=$password
293    output=`nice -n $nicelevel \
294              su -c \
295                 "$precmd duplicity $execstr_command $execstr_options $execstr_source --exclude '**' / $execstr_serverpart >$outputfile 2>&1"`
296    exit_code=$?
297    debug $output
298    cat $outputfile | (while read output ; do
299                          info $output
300                       done
301    )
302    if [ $exit_code -eq 0 ]; then
303       info "Duplicity finished successfully."
304    else
305       fatal "Duplicity failed."
306    fi
307    rm $outputfile
308 fi
309
310 return 0