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