83f2fb5faed18ae29a184092aeb216beb63c9c82
[matthijs/upstream/backupninja.git] / handlers / rdiff.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 rdiff:incremental_remote_filesystem_backup"
5
6 declare -a rdiff_includes
7 declare -a rdiff_excludes
8 declare -a rdiff_vsincludes
9 declare -a rdiff_vsexcludes
10
11 # FUNCTIONS
12
13 do_rdiff_host_includes() {
14    set -o noglob
15    # choose the files to backup
16    REPLY=
17    while [ -z "$REPLY" ]; do
18       formBegin "$rdiff_title - host system: includes"
19          for ((i=0; i < ${#rdiff_includes[@]} ; i++)); do
20             formItem include ${rdiff_includes[$i]}
21          done
22          formItem include
23          formItem include
24          formItem include
25       formDisplay
26       [ $? = 0 ] || return
27       unset rdiff_includes
28       rdiff_includes=($REPLY)
29    done
30    set +o noglob
31 }
32
33 do_rdiff_vserver() {
34    # choose the vservers to backup (into $selected_vservers)
35    choose_one_or_more_vservers "$rdiff_title"
36    [ $? = 0 ] || return 1
37
38    set -o noglob
39    # choose the files to backup
40    REPLY=
41
42    while [ -z "$REPLY" ]; do
43       formBegin "$rdiff_title - vsincludes (backup these directories from every vserver)"
44          [ -z "$rdiff_vsincludes" ] && rdiff_vsincludes="$rdiff_default_includes"
45
46          for i in $rdiff_vsincludes; do
47             formItem include "$i"
48          done
49          formItem include ""
50          formItem include ""
51          formItem include ""
52       formDisplay
53       [ $? = 0 ] || return 1
54       rdiff_vsincludes=($REPLY)
55    done
56
57    set +o noglob
58 }
59
60 do_rdiff_excludes() {
61    set -o noglob
62    formBegin "$rdiff_title: excludes"
63      for ((i=0; i < ${#rdiff_excludes[@]} ; i++))
64      do
65        formItem exclude ${rdiff_excludes[$i]}
66      done
67      formItem exclude
68      formItem exclude
69    formDisplay
70    [ $? = 0 ] || return
71    unset rdiff_excludes
72    rdiff_excludes=($REPLY)
73    set +o noglob
74 }
75
76 do_rdiff_src() {
77    choose_host_or_vservers_or_both "$rdiff_title"
78    [ $? = 0 ] || return 1
79    case $host_or_vservers in
80       'host')
81          do_rdiff_host_includes
82          [ $? = 0 ] || return 1
83          ;;
84       'vservers')
85          do_rdiff_vserver
86          [ $? = 0 ] || return 1
87          ;;
88       'both')
89          do_rdiff_host_includes
90          [ $? = 0 ] || return 1
91          do_rdiff_vserver
92          [ $? = 0 ] || return 1
93          ;;
94       *)
95          return 1
96          ;;
97    esac
98    do_rdiff_excludes
99    [ $? = 0 ] || return 1
100    _src_done="(DONE)"
101    setDefault dest
102 }
103
104 do_rdiff_dest() {
105    declare -a tmp_array
106
107    set -o noglob
108    REPLY=
109    while [ -z "$REPLY" -o -z "$rdiff_directory" -o -z "$rdiff_host" -o -z "$rdiff_user" ]
110    do
111      formBegin "$rdiff_title - destination: last three items are required"
112         formItem "keep" "$rdiff_keep"
113         formItem "dest_directory" "$rdiff_directory"
114         formItem "dest_host" "$rdiff_host"
115         formItem "dest_user" "$rdiff_user"
116         formItem "dest_type" "$rdiff_type"
117         formDisplay
118      [ $? = 0 ] || return
119      tmp_array=($REPLY)
120      rdiff_keep=${tmp_array[0]}
121      rdiff_directory=${tmp_array[1]}
122      rdiff_host=${tmp_array[2]}
123      rdiff_user=${tmp_array[3]}
124      rdiff_type=${tmp_array[4]}
125   done
126   set +o noglob
127
128   _dest_done="(DONE)"
129   setDefault conn
130 }
131
132 do_rdiff_ssh_con() {
133    local remote_status="ok"
134
135    IFS=$' \t\n'
136    if [ "$_dest_done" = "" ]; then
137       msgBox "$rdiff_title: error" "You must first configure the destination."
138       return
139    elif [ "$rdiff_type" = "" ]; then
140       msgBox "$rdiff_title: error" "You must first configure the destination backup type."
141       return
142    elif [ "$rdiff_user" = "" ]; then
143       msgBox "$rdiff_title: error" "You must first configure the destination user."
144       return
145    elif [ "$rdiff_host" = "" ]; then
146       msgBox "$rdiff_title: error" "You must first configure the destination host."
147       return
148    else
149       booleanBox "$rdiff_title" "This step will create a ssh key for the local root user with no passphrase (if one does not already exist), and attempt to copy root's public ssh key to authorized_keys file of $rdiff_user@$rdiff_host. This will allow the local root to make unattended backups to $rdiff_user@$rdiff_host.\n\n\nAre you sure you want to continue?"
150       [ $? = 0 ] || return
151    fi
152
153    if [ ! -f /root/.ssh/id_dsa.pub -a ! -f /root/.ssh/id_rsa.pub ]; then
154       echo "Creating local root's ssh key"
155       ssh-keygen -t dsa -f /root/.ssh/id_dsa -N ""
156       echo "Done. hit return to continue"
157       read
158    fi
159
160    ssh -o PreferredAuthentications=publickey $rdiff_host -l $rdiff_user "exit" 2> /dev/null
161    if [ $? -ne 0 ]; then
162       echo "Copying root's public ssh key to authorized_keys of $rdiff_user@$rdiff_host. When prompted, specify the password for user $rdiff_user@$rdiff_host."
163       ssh-copy-id -i /root/.ssh/id_[rd]sa.pub $rdiff_user@$rdiff_host
164       if [ $? -ne 0 ]; then
165          echo "FAILED: Couldn't copy root's public ssh key to authorized_keys of $rdiff_user@$rdiff_host."
166          ssh $rdiff_user@$rdiff_host 'test -w .ssh || test -w .'
167          result=$?
168          echo "Hit return to continue."
169          read
170          case $result in
171             0 )   msgBox "$rdiff_title: error" "Directories are writable: Probably just a typo the first time." ;;
172             1 )   msgBox "$rdiff_title: error" "Connected successfully to $rdiff_user@$rdiff_host, but unable to write. Check ownership and modes of ~$rdiff_user on $rdiff_host." ;;
173             255 ) msgBox "$rdiff_title: error" "Failed to connect to $rdiff_user@$rdiff_host. Check hostname, username, and password. Also, make sure sshd is running on the destination host." ;;
174             * )   msgBox "$rdiff_title: error" "Unexpected error." ;;
175          esac
176          return
177       else
178          echo "Done. hit return to continue"
179          read
180       fi
181    else
182       echo "root@localhost is already in authorized_keys of $rdiff_user@$rdiff_host."
183       echo "Hit return to continue."
184       read
185    fi
186
187    # test to see if the remote rdiff backup directory exists and is writable
188    echo "Testing to see if remote rdiff backup directory exists and is writable"
189    ssh $rdiff_user@$rdiff_host "test -d ${rdiff_directory}"
190    if [ $? = 0 ]; then
191       ssh $rdiff_user@$rdiff_host "test -w $rdiff_directory"
192       if [ $? != 0 ]; then
193          msgBox "destination directory is not writable!" "The remote destination directory is not writable by the user you specified. Please fix the permissions on the directory and then try again."
194          remote_status=failed
195       fi
196    else
197       booleanBox "Remote directory does not exist" "The destination backup directory does not exist, do you want me to create it for you?"
198       if [ $? = 0 ]; then
199     ssh $rdiff_user@$rdiff_host "mkdir -p ${rdiff_directory}"
200          result=$?
201          case $result in
202             0) msgBox "$rdiff_title: success" "Creation of the remote destination directory was a success!";;
203             1) msgBox "$rdiff_title: error" "Connected successfully to $rdiff_user@$rdiff_host, but was unable to create the destination directory, check the directory permissions."
204                remote_status=failed;;
205             255) msgBox "$rdiff_title: error" "Failed to connect to $rdiff_user@$rdiff_host. Check hostname, username, and password. Also, make sure sshd is running on the destination host."
206                remote_status=failed;;
207             *) msgBox "$rdiff_title: error" "Unexpected error."
208                remote_status=failed;;
209          esac
210       fi
211    fi
212
213    if [ "$remote_status" = "ok" ]; then
214       do_rdiff_con
215    fi
216 }
217
218 do_rdiff_con() {
219    echo "Checking for local install of rdiff-backup"
220    require_packages rdiff-backup
221
222    echo "Testing to make sure destination has rdiff-backup installed and is compatible."
223    remote_result=`/usr/bin/rdiff-backup --test-server $rdiff_user@$rdiff_host::/ 2>&1 >&-`
224    if [ $? -ne 0 ]; then
225       echo $remote_result | grep -q "command not found"
226       if [ $? -eq 0 ]; then
227          if [ "$rdiff_user" = "root" ]; then
228             booleanBox "install rdiff-backup?" "It seems like the remote machine does not have rdiff-backup installed, I can attempt to install rdiff-backup on the remote machine.\n\n\nDo you want me to attempt this now?"
229             if [ $? = 0 ]; then
230                ssh $rdiff_user@$rdiff_host 'apt-get install rdiff-backup'
231                result=$?
232                echo "Hit return to continue."
233                read
234                case $result in
235                   0) msgBox "$rdiff_title: success" "Installation of rdiff-backup was a success!"
236                      do_rdiff_con;;
237                   1) msgBox "$rdiff_title: error" "Connected successfully to $rdiff_user@$rdiff_host, but was unable to install the package for some reason.";;
238                   255) msgBox "$rdiff_title: error" "Failed to connect to $rdiff_user@$rdiff_host. Check hostname, username, and password. Also, make sure sshd is running on the destination host.";;
239                   *) msgBox "$rdiff_title: error" "Unexpected error.";;
240                esac
241                return
242             fi
243          else
244             booleanBox "install rdiff-backup" "Please install rdiff-backup on the remote machine, this cannot be done automatically, as the remote user in your configuration is not root. \n\nIf you have installed rdiff-backup on the remote machine and you are getting this error, then there is a version incompatibility between that version and the local version.\n\nPlease resolve this problem and then try connecting again.\n\n\n\nTry connecting again?"
245             if [ $? = 0 ]; then
246                do_rdiff_con
247             else
248                return
249             fi
250          fi
251       else
252          msgBox "incompatible versions of rdiff-backup" "It looks like rdiff-backup is installed on the remote machine, but it may be an incompatible version with the one installed locally, or something else is amiss.\n\nPlease resolve this problem and then try connecting again.\n\n\nTry connecting again?"
253          if [ $? = 0 ]; then
254             do_rdiff_con
255          else
256             return
257          fi
258       fi
259    else
260     echo "SUCCESS: Everything looks good!"
261    echo "Hit return to continue."
262    read
263    fi
264
265    _con_done="(DONE)"
266    setDefault finish
267 }
268
269 do_rdiff_finish() {
270    get_next_filename $configdirectory/90.rdiff
271    cat > $next_filename <<EOF
272 # options = --force
273 # when = everyday at 02
274
275 [source]
276 type = local
277 keep = $rdiff_keep
278
279 # A few notes about includes and excludes:
280 # 1. include, exclude and vsinclude statements support globbing with '*'
281 # 2. Symlinks are not dereferenced. Moreover, an include line whose path
282 #    contains, at any level, a symlink to a directory, will only have the
283 #    symlink backed-up, not the target directory's content. Yes, you have to
284 #    dereference yourself the symlinks, or to use 'mount --bind' instead.
285 #    Example: let's say /home is a symlink to /mnt/crypt/home ; the following
286 #    line will only backup a "/home" symlink ; neither /home/user nor
287 #    /home/user/Mail will be backed-up :
288 #      include = /home/user/Mail
289 #    A workaround is to 'mount --bind /mnt/crypt/home /home' ; another one is to
290 #    write :
291 #      include = /mnt/crypt/home/user/Mail
292 # 3. All the excludes come after all the includes. The order is not otherwise
293 #    taken into account.
294
295 # files to include in the backup
296 EOF
297    ## includes ##
298    if [ "$host_or_vservers" == host -o "$host_or_vservers" == both ]; then
299       set -o noglob
300       for ((i=0; i < ${#rdiff_includes[@]} ; i++)); do
301          echo "include = ${rdiff_includes[$i]}" >> $next_filename
302       done
303       set +o noglob
304    fi
305
306    if [ "$host_or_vservers" == vservers -o "$host_or_vservers" == both ]; then
307       cat >> $next_filename <<EOF
308 #
309 # If vservers = yes in /etc/backupninja.conf then the following variables can
310 # be used:
311 # vsnames = all | <vserver1> <vserver2> ... (default = all)
312 # vsinclude = <path>
313 # vsinclude = <path>
314 # ...
315 # Any path specified in vsinclude is added to the include list for each vserver
316 # listed in vsnames (or all if vsnames = all, which is the default).
317 #
318 # For example, vsinclude = /home will backup the /home directory in every
319 # vserver listed in vsnames. If you have 'vsnames = foo bar baz', this
320 # vsinclude will add to the include list /vservers/foo/home, /vservers/bar/home
321 # and /vservers/baz/home.
322 # Vservers paths are derived from $VROOTDIR.
323
324 EOF
325       set -o noglob
326       echo -e "vsnames = $selected_vservers\n" >> $next_filename
327       for i in $rdiff_vsincludes; do
328          echo "vsinclude = $i" >> $next_filename
329       done
330       set +o noglob
331    fi
332
333    ## excludes ##
334    set -o noglob
335    for ((i=0; i < ${#rdiff_excludes[@]} ; i++)); do
336      echo exclude = ${rdiff_excludes[$i]} >> $next_filename
337    done
338    set +o noglob
339    cat >> $next_filename <<EOF
340
341 ######################################################
342 ## destination section
343 ## (where the files are copied to)
344
345 [dest]
346 type = remote
347 directory = $rdiff_directory
348 host = $rdiff_host
349 user = $rdiff_user
350 EOF
351
352    chmod 600 $next_filename
353 }
354
355 rdiff_main_menu() {
356    while true; do
357       srcitem="choose files to include & exclude $_src_done"
358       destitem="configure backup destination $_dest_done"
359       conitem="set up ssh keys and test remote connection $_con_done"
360       advitem="edit advanced settings $_adv_done"
361       menuBox "$rdiff_title" "choose a step:" \
362          src "$srcitem" \
363          dest "$destitem" \
364          conn "$conitem" \
365          finish "finish and create config file"
366       [ $? = 0 ] || return
367       result="$REPLY"
368       case "$result" in
369          "src") do_rdiff_src;;
370          "dest") do_rdiff_dest;;
371          "conn") do_rdiff_ssh_con;;
372          "adv") do_rdiff_adv;;
373          "finish")
374             if [[ "$_con_done$_dest_done$_src_done" != "(DONE)(DONE)(DONE)" ]]; then
375                msgBox "$rdiff_title" "You cannot create the configuration file until the other steps are completed."
376             else
377                do_rdiff_finish
378                return
379             fi
380             ;;
381       esac
382    done
383 }
384
385 rdiff_wizard() {
386
387    # Global variables
388    rdiff_title="rdiff-backup action wizard"
389    _src_done=
390    _dest_done=
391    _con_done=
392    _adv_done=
393    rdiff_keep=60D
394    rdiff_directory=/backup/`hostname`
395    rdiff_type=remote
396    rdiff_user=
397    rdiff_host=
398
399    # Global variables whose '*' shall not be expanded
400    set -o noglob
401    rdiff_includes=(/var/spool/cron/crontabs /var/backups /etc /root /home /usr/local/*bin /var/lib/dpkg/status*)
402    rdiff_excludes=(/home/*/.gnupg /home/*/.local/share/Trash /home/*/.Trash /home/*/.thumbnails /home/*/.beagle /home/*/.aMule /home/*/gtk-gnutella-downloads)
403    rdiff_vsincludes=
404    set +o noglob
405
406    rdiff_main_menu
407 }
408