tar: Support running on multiple backends.
[matthijs/upstream/backupninja.git] / handlers / dup.helper.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 HELPERS="$HELPERS dup:incremental_encrypted_remote_filesystem_backup"
5
6 ### Functions
7
8 do_dup_host_includes() {
9    set -o noglob
10    # choose the files to backup
11    REPLY=
12    while [ -z "$REPLY" ]; do
13       formBegin "$dup_title - host system: includes"
14          [ -z "$dup_includes" ] && dup_includes="$dup_default_includes"
15          for i in $dup_includes; do
16             formItem include "$i"
17          done
18          formItem include ""
19          formItem include ""
20          formItem include ""
21          formItem include ""
22          formItem include ""
23          formItem include ""
24          formItem include ""
25          formItem include ""
26          formDisplay
27       [ $? = 0 ] || return 1
28       dup_includes="$REPLY"
29    done
30    set +o noglob
31 }
32
33 do_dup_vserver() {
34    # choose the vservers to backup (into $selected_vservers)
35    choose_one_or_more_vservers "$dup_title"
36    [ $? = 0 ] || return 1
37
38    set -o noglob
39    # choose the files to backup
40    REPLY=
41    while [ -z "$REPLY" ]; do
42       formBegin "$dup_title - vservers: vsincludes (backup these directories from every selected vserver)"
43          [ -z "$dup_vsincludes" ] && dup_vsincludes="$dup_default_includes"
44          for i in $dup_vsincludes; do
45             formItem include "$i"
46          done
47          formItem include ""
48          formItem include ""
49          formItem include ""
50          formItem include ""
51          formItem include ""
52          formItem include ""
53          formItem include ""
54          formItem include ""
55       formDisplay
56       [ $? = 0 ] || return 1
57       dup_vsincludes="$REPLY"
58    done
59    set +o noglob
60 }
61
62 do_dup_excludes() {
63    set -o noglob
64    formBegin "$dup_title: excludes"
65      [ -z "$dup_excludes" ] && dup_excludes="$dup_default_excludes"
66      for i in $dup_excludes; do
67         formItem exclude "$i"
68      done
69      formItem exclude ""
70      formItem exclude ""
71      formItem exclude ""
72      formItem exclude ""
73      formItem exclude ""
74      formItem exclude ""
75      formItem exclude ""
76      formItem exclude ""
77    formDisplay
78    [ $? = 0 ] || return 1
79    dup_excludes="$REPLY"
80    set +o noglob
81 }
82
83 do_dup_src() {
84    choose_host_or_vservers_or_both "$dup_title"
85    [ $? = 0 ] || return 1
86    case $host_or_vservers in
87       'host')
88          do_dup_host_includes
89          [ $? = 0 ] || return 1
90          ;;
91       'vservers')
92          do_dup_vserver
93          [ $? = 0 ] || return 1
94          ;;
95       'both')
96          do_dup_host_includes
97          [ $? = 0 ] || return 1
98          do_dup_vserver
99          [ $? = 0 ] || return 1
100          ;;
101       *)
102          return 1
103          ;;
104    esac
105    do_dup_excludes
106    [ $? = 0 ] || return 1
107
108    _src_done="(DONE)"
109    setDefault dest
110 }
111
112 do_dup_dest() {
113
114    local replyconverted
115    local thereply
116
117    set -o noglob
118    REPLY=
119    while [ -z "$REPLY" -o -z "$dup_destdir" -o -z "$dup_desthost" -o -z "$dup_destuser" ]; do
120       formBegin "$dup_title - destination: first three items are compulsory"
121         formItem "desthost" "$dup_desthost"
122         formItem "destuser" "$dup_destuser"
123         formItem "destdir" "$dup_destdir"
124         formItem "keep" "$dup_keep"
125         formItem "incremental" "$dup_incremental"
126         formItem "bandwidthlimit" "$dup_bandwidth"
127         formItem "sshoptions" "$dup_sshoptions"
128       formDisplay
129       [ $? = 0 ] || return 1
130
131       IFS=$''
132       replyconverted=`echo $REPLY | tr '\n' :`
133       IFS=$':'
134       thereply=($replyconverted)
135       IFS=$' \t\n'
136
137       dup_desthost=${thereply[0]}
138       dup_destuser=${thereply[1]}
139       dup_destdir=${thereply[2]}
140       dup_keep=${thereply[3]}
141       dup_incremental=${thereply[4]}
142       dup_bandwidth=${thereply[5]}
143       dup_sshoptions=${thereply[6]}
144
145    done
146    set +o noglob
147
148    _dest_done="(DONE)"
149    setDefault gpg
150 }
151
152 do_dup_gpg_encryptkey() {
153    REPLY=
154    while [ -z "$REPLY" -o -z "$dup_gpg_encryptkey" ]; do
155       inputBox "$dup_title - GnuPG" "Enter ID of the public GnuPG key to be used to encrypt the backups:" "$dup_gpg_encryptkey"
156       [ $? = 0 ] || return 1
157       dup_gpg_encryptkey="$REPLY"
158    done
159 }
160
161 do_dup_gpg_sign() {
162    # sign ?
163    booleanBox "$dup_title - GnuPG" "Sign the backups?" "$dup_gpg_sign"
164    if [ $? = 0 ]; then
165       dup_gpg_sign=yes
166    else
167       dup_gpg_sign=no
168    fi
169 }
170
171 do_dup_gpg_signkey() {
172    # one key pair ?
173    booleanBox "$dup_title - GnuPG" "Use the same GnuPG key pair for encryption and signing?" "$dup_gpg_onekeypair"
174    if [ $? = 0 ]; then
175       dup_gpg_onekeypair=yes
176    else
177       dup_gpg_onekeypair=no
178    fi
179
180    if [ "$dup_gpg_onekeypair" == "no" }; then
181       # signkey ?
182       REPLY=
183       while [ -z "$REPLY" -o -z "$dup_gpg_signkey" ]; do
184          inputBox "$dup_title - GnuPG" "Enter the ID of the private GnuPG key to be used to sign the backups:" "$dup_gpg_signkey"
185          [ $? = 0 ] || return 1
186          dup_gpg_signkey="$REPLY"
187       done
188    fi
189 }
190
191 do_dup_gpg_passphrase() {
192    local question="Enter the passphrase needed to unlock the GnuPG key:"
193    REPLY=
194    while [ -z "$REPLY" -o -z "$dup_gpg_password" ]; do
195       passwordBox "$dup_title - GnuPG" "$question"
196       [ $? = 0 ] || return 1
197       dup_gpg_password="$REPLY"
198    done
199 }
200
201 do_dup_gpg() {
202
203    # symmetric or public key encryption ?
204    booleanBox "$dup_title - GnuPG" "Use public key encryption? Otherwise, symmetric encryption will be used, and data signing will be impossible." "$dup_gpg_asymmetric_encryption"
205    if [ $? = 0 ]; then
206       dup_gpg_asymmetric_encryption=yes
207    else
208       dup_gpg_asymmetric_encryption=no
209    fi
210
211    # when using public/private key pair encryption, ask for the keys to use
212    if [ "$dup_gpg_asymmetric_encryption" == yes ]; then
213       do_dup_gpg_encryptkey ; [ $? = 0 ] || return 1
214       do_dup_gpg_sign ; [ $? = 0 ] || return 1
215       if [ "$dup_gpg_sign" == yes ]; then
216          do_dup_gpg_signkey ; [ $? = 0 ] || return 1
217       fi
218    else
219       dup_gpg_sign=no
220    fi
221
222    # a passphrase is alway needed
223    do_dup_gpg_passphrase
224
225    _gpg_done="(DONE)"
226    setDefault adv
227    # TODO: replace the above line by the following when do_dup_conn is written
228    # setDefault conn
229 }
230
231 # TODO: share rdiff.helper code in some lib, and use it here
232 do_dup_conn() {
233    _con_done="(DONE)"
234    setDefault adv
235 }
236
237 do_dup_misc_options() {
238
239    set -o noglob
240    local replyconverted
241    local thereply
242
243    formBegin "$dup_title - misc. options"
244      formItem "nicelevel" "$dup_nicelevel"
245      formItem "testconnect" "$dup_testconnect"
246      formItem "options" "$dup_options"
247    formDisplay
248    [ $? = 0 ] || return 1
249
250    IFS=$''
251    replyconverted=`echo $REPLY | tr '\n' :`
252    IFS=$':'
253    thereply=($replyconverted)
254    IFS=$' \t\n'
255
256    dup_nicelevel=${thereply[0]}
257    dup_testconnect=${thereply[1]}
258    dup_options=${thereply[2]}
259
260    set +o noglob
261 }
262
263 # (rdiff.helper compatible interface... there could be some sode to share, hmmm.)
264 do_dup_adv() {
265    do_dup_misc_options
266    [ $? = 0 ] || return 1
267    _adv_done="(DONE)"
268    setDefault finish
269 }
270
271 do_dup_finish() {
272    get_next_filename $configdirectory/90.dup
273    cat > $next_filename <<EOF
274 # passed directly to duplicity
275 #options = --verbosity 8
276 options = $dup_options
277
278 # default is 0, but set to 19 if you want to lower the priority.
279 nicelevel = $dup_nicelevel
280
281 # default is yes. set to no to skip the test if the remote host is alive
282 testconnect = $dup_testconnect
283
284 ######################################################
285 ## gpg section
286 ## (how to encrypt and optionally sign the backups)
287 ##
288 ## WARNING: old (pre-0.9.4) example.dup used to give wrong information about
289 ##          the way the following options are used. Please read the following
290 ##          carefully.
291 ##
292 ## If the encryptkey variable is set:
293 ##   - data is encrypted with the GnuPG public key specified by the encryptkey
294 ##     variable
295 ##   - if signing is enabled, data is signed with the GnuPG private
296 ##     key specified by the signkey variable
297 ##   - the password variable is used to unlock the GnuPG key(s) used
298 ##     for encryption and (optionnal) signing
299 ##
300 ## If the encryptkey option is not set:
301 ##   - data signing is not possible
302 ##   - the password variable is used to encrypt the data with symmetric
303 ##     encryption: no GnuPG key pair is needed
304
305 [gpg]
306
307 # when set to yes, encryptkey variable must be set below; if you want to use
308 # two different keys for encryption and signing, you must also set the signkey
309 # variable below.
310 # default is no, for backwards compatibility with backupninja <= 0.5.
311 sign = $dup_gpg_sign
312
313 # ID of the GnuPG public key used for data encryption.
314 # if not set, symmetric encryption is used, and data signing is not possible.
315 encryptkey = $dup_gpg_encryptkey
316
317 # ID of the GnuPG private key used for data signing.
318 # if not set, encryptkey will be used.
319 signkey = $dup_gpg_signkey
320
321 # password
322 # NB: neither quote this, nor should it include any quotes
323 password = $dup_gpg_password
324
325 ######################################################
326 ## source section
327 ## (where the files to be backed up are coming from)
328
329 [source]
330
331 # A few notes about includes and excludes:
332 # 1. include, exclude and vsinclude statements support globbing with '*'
333 # 2. Symlinks are not dereferenced. Moreover, an include line whose path
334 #    contains, at any level, a symlink to a directory, will only have the
335 #    symlink backed-up, not the target directory's content. Yes, you have to
336 #    dereference yourself the symlinks, or to use 'mount --bind' instead.
337 #    Example: let's say /home is a symlink to /mnt/crypt/home ; the following
338 #    line will only backup a "/home" symlink ; neither /home/user nor
339 #    /home/user/Mail will be backed-up :
340 #      include = /home/user/Mail
341 #    A workaround is to 'mount --bind /mnt/crypt/home /home' ; another one is to
342 #    write :
343 #      include = /mnt/crypt/home/user/Mail
344 # 3. All the excludes come after all the includes. The order is not otherwise
345 #    taken into account.
346
347 # files to include in the backup
348 EOF
349
350    if [ "$host_or_vservers" == host -o "$host_or_vservers" == both ]; then
351       set -o noglob
352       for i in $dup_includes; do
353          echo "include = $i" >> $next_filename
354       done
355       set +o noglob
356    fi
357
358    cat >> $next_filename <<EOF
359
360 # If vservers = yes in /etc/backupninja.conf then the following variables can
361 # be used:
362 # vsnames = all | <vserver1> <vserver2> ... (default = all)
363 # vsinclude = <path>
364 # vsinclude = <path>
365 # ...
366 # Any path specified in vsinclude is added to the include list for each vserver
367 # listed in vsnames (or all if vsnames = all, which is the default).
368 #
369 # For example, vsinclude = /home will backup the /home directory in every
370 # vserver listed in vsnames. If you have 'vsnames = foo bar baz', this
371 # vsinclude will add to the include list /vservers/foo/home, /vservers/bar/home
372 # and /vservers/baz/home.
373 # Vservers paths are derived from $VROOTDIR.
374
375 EOF
376
377    if [ "$host_or_vservers" == vservers -o "$host_or_vservers" == both ]; then
378       set -o noglob
379       echo -e "vsnames = $selected_vservers\n" >> $next_filename
380       for i in $dup_vsincludes; do
381          echo "vsinclude = $i" >> $next_filename
382       done
383       set +o noglob
384    fi
385
386    # excludes
387    cat >> $next_filename <<EOF
388
389 # files to exclude from the backup
390 EOF
391     set -o noglob
392     for i in $dup_excludes; do
393         echo "exclude = $i" >> $next_filename
394     done
395     set +o noglob
396
397     cat >> $next_filename <<EOF
398
399 ######################################################
400 ## destination section
401 ## (where the files are copied to)
402
403 [dest]
404
405 # perform an incremental backup? (default = yes)
406 # if incremental = no, perform a full backup in order to start a new backup set
407 incremental = $dup_incremental
408
409 # how many days of data to keep ; default is 60 days.
410 # (you can also use the time format of duplicity)
411 # 'keep = yes' means : do not delete old data, the remote host will take care of this
412 #keep = 60
413 #keep = yes
414 keep = $dup_keep
415
416 # full destination URL, in duplicity format; if set, desturl overrides
417 # sshoptions, destdir, desthost and destuser; it also disables testconnect and
418 # bandwithlimit. For details, see duplicity manpage, section "URL FORMAT".
419 #desturl = file:///usr/local/backup
420 #desturl = rsync://user@other.host//var/backup/bla
421 #desturl = s3+http://your_bucket
422
423 # Amazon Web Services Access Key ID and Secret Access Key, needed for backups
424 # to S3 buckets.
425 #awsaccesskeyid = YOUR_AWS_ACCESS_KEY_ID
426 #awssecretaccesskey = YOUR_AWS_SECRET_KEY
427
428 # bandwith limit, in kbit/s ; default is 0, i.e. no limit
429 #bandwidthlimit = 128
430 bandwidthlimit = $dup_bandwidth
431
432 # passed directly to ssh, scp (and sftp in duplicity >=0.4.2)
433 # warning: sftp does not support all scp options, especially -i; as
434 # a workaround, you can use "-o <SSHOPTION>"
435 #sshoptions = -o IdentityFile=/root/.ssh/id_dsa_duplicity
436 sshoptions = $dup_sshoptions
437
438 # put the backups under this directory
439 destdir = $dup_destdir
440
441 # the machine which will receive the backups
442 desthost = $dup_desthost
443
444 # make the files owned by this user
445 # note: you must be able to ssh backupuser@backhost
446 # without specifying a password (if type = remote).
447 destuser = $dup_destuser
448
449 EOF
450
451     chmod 600 $next_filename
452
453 }
454
455 dup_main_menu() {
456
457    while true; do
458       srcitem="choose files to include & exclude $_src_done"
459       destitem="configure backup destination $_dest_done"
460       gpgitem="configure GnuPG encryption/signing $_gpg_done"
461       conitem="set up ssh keys and test remote connection $_con_done"
462       advitem="edit advanced settings $_adv_done"
463       # TODO: add the following to the menu when do_dup_conn is written
464       # conn "$conitem" \
465       menuBox "$dup_title" "choose a step:" \
466          src "$srcitem" \
467          dest "$destitem" \
468          gpg "$gpgitem" \
469          adv "$advitem" \
470          finish "finish and create config file"
471       [ $? = 0 ] || return 1
472       result="$REPLY"
473
474       case "$result" in
475          "src") do_dup_src;;
476          "dest") do_dup_dest;;
477          "gpg") do_dup_gpg;;
478          # TODO: enable the following when do_dup_conn is written
479          # "conn") do_dup_conn;;
480          "adv") do_dup_adv;;
481          "finish")
482             if [[ "$_dest_done$_gpg_done$_src_done" != "(DONE)(DONE)(DONE)" ]]; then
483             # TODO: replace the previous test by the following when do_dup_conn is written
484             # if [[ "$_con_done$_dest_done$_gpg_done$_src_done" != "(DONE)(DONE)(DONE)(DONE)" ]]; then
485                msgBox "$dup_title" "You cannot create the configuration file until the four first steps are completed."
486             else
487                do_dup_finish
488                break
489             fi
490             ;;
491       esac
492
493    done
494 }
495
496 ### Main function
497
498 dup_wizard() {
499
500    require_packages duplicity
501
502    # Global variables
503    dup_title="Duplicity action wizard"
504    _src_done=
505    _dest_done=
506    _con_done=
507    _gpg_done=
508    _adv_done=
509    dup_includes=
510    dup_excludes=
511    dup_vsincludes=
512    dup_incremental=yes
513    dup_keep=60
514    dup_bandwidth=
515    dup_sshoptions=
516    dup_destdir="/backups/`hostname`"
517    dup_desthost=
518    dup_destuser=
519    dup_gpg_asymmetric_encryption="yes"
520    dup_gpg_encryptkey=""
521    dup_gpg_sign="yes"
522    dup_gpg_onekeypair="yes"
523    dup_gpg_signkey=""
524    dup_gpg_password=""
525    dup_nicelevel=19
526    dup_testconnect=yes
527    dup_options=
528
529    # Global variables whose '*' shall not be expanded
530    set -o noglob
531    dup_default_includes="/var/spool/cron/crontabs /var/backups /etc /root /home /usr/local/*bin /var/lib/dpkg/status*"
532    dup_default_excludes="/home/*/.gnupg /home/*/.local/share/Trash /home/*/.Trash /home/*/.thumbnails /home/*/.beagle /home/*/.aMule /home/*/gtk-gnutella-downloads"
533    set +o noglob
534
535    dup_main_menu
536 }