added local support to rdiff helper
[matthijs/upstream/backupninja-vserver.git] / handlers / rdiff.helper.in
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         formItem "dest_type" "$rdiff_type"
116         formDisplay
117      [ $? = 0 ] || return
118      tmp_array=($REPLY)
119      rdiff_keep=${tmp_array[0]}
120      rdiff_directory=${tmp_array[1]}
121      rdiff_host=${tmp_array[2]}
122      rdiff_user=${tmp_array[3]}  
123      rdiff_type=${tmp_array[4]}
124   done
125   set +o noglob
126   
127   _dest_done="(DONE)"
128   setDefault conn
129 }
130
131 do_rdiff_ssh_con() {
132    local remote_status="ok"
133
134    IFS=$' \t\n'
135    if [ "$_dest_done" = "" ]; then
136       msgBox "$rdiff_title: error" "You must first configure the destination."
137       return
138    elif [ "$rdiff_type" = "" ]; then
139       msgBox "$rdiff_title: error" "You must first configure the destination backup type."
140       return
141    elif [ "$rdiff_user" = "" ]; then
142       msgBox "$rdiff_title: error" "You must first configure the destination user."
143       return
144    elif [ "$rdiff_host" = "" ]; then
145       msgBox "$rdiff_title: error" "You must first configure the destination host."
146       return
147    else
148       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?"
149       [ $? = 0 ] || return
150    fi
151
152    if [ ! -f /root/.ssh/id_dsa.pub -a ! -f /root/.ssh/id_rsa.pub ]; then
153       echo "Creating local root's ssh key"
154       ssh-keygen -t dsa -f /root/.ssh/id_dsa -N ""
155       echo "Done. hit return to continue"
156       read
157    fi
158   
159    ssh -o PreferredAuthentications=publickey $rdiff_host -l $rdiff_user "exit" 2> /dev/null
160    if [ $? -ne 0 ]; then
161       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."
162       ssh-copy-id -i /root/.ssh/id_[rd]sa.pub $rdiff_user@$rdiff_host
163       if [ $? -ne 0 ]; then
164          echo "FAILED: Couldn't copy root's public ssh key to authorized_keys of $rdiff_user@$rdiff_host."
165          ssh $rdiff_user@$rdiff_host 'test -w .ssh || test -w .'
166          result=$?
167          echo "Hit return to continue."
168          read
169          case $result in
170             0 )   msgBox "$rdiff_title: error" "Directories are writable: Probably just a typo the first time." ;;
171             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." ;;
172             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." ;;
173             * )   msgBox "$rdiff_title: error" "Unexpected error." ;;
174          esac 
175          return
176       else
177          echo "Done. hit return to continue"
178          read
179       fi
180    else
181       echo "root@localhost is already in authorized_keys of $rdiff_user@$rdiff_host."
182       echo "Hit return to continue."
183       read
184    fi
185
186    # test to see if the remote rdiff backup directory exists and is writable
187    echo "Testing to see if remote rdiff backup directory exists and is writable"
188    ssh $rdiff_user@$rdiff_host "test -d ${rdiff_directory}"
189    if [ $? = 0 ]; then
190       ssh $rdiff_user@$rdiff_host "test -w $rdiff_directory"
191       if [ $? != 0 ]; then
192          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."
193          remote_status=failed
194       fi
195    else
196       booleanBox "Remote directory does not exist" "The destination backup directory does not exist, do you want me to create it for you?"
197       if [ $? = 0 ]; then
198          ssh $rdiff_user@$rdiff_host "mkdir -p ${rdiff_directory}"
199          result=$?
200          case $result in
201             0) msgBox "$rdiff_title: success" "Creation of the remote destination directory was a success!";;
202             1) msgBox "$rdiff_title: error" "Connected successfully to $rdiff_user@$rdiff_host, but was unable to create the destination directory, check the directory permissions." 
203                remote_status=failed;;
204             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." 
205                remote_status=failed;;
206             *) msgBox "$rdiff_title: error" "Unexpected error." 
207                remote_status=failed;;
208          esac
209       fi
210    fi
211   
212    if [ "$remote_status" = "ok" ]; then
213       do_rdiff_con
214    fi
215 }
216
217 do_rdiff_con() {
218    echo "Checking for local install of rdiff-backup"
219    require_packages rdiff-backup
220
221    echo "Testing to make sure destination has rdiff-backup installed and is compatible."
222    remote_result=`/usr/bin/rdiff-backup --test-server $rdiff_user@$rdiff_host::/ 2>&1 >&-`
223    if [ $? -ne 0 ]; then
224       echo $remote_result | grep -q "command not found"
225       if [ $? -eq 0 ]; then
226          if [ "$rdiff_user" = "root" ]; then
227             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?"
228             if [ $? = 0 ]; then
229                ssh $rdiff_user@$rdiff_host 'apt-get install rdiff-backup'
230                result=$?
231                echo "Hit return to continue."
232                read
233                case $result in
234                   0) msgBox "$rdiff_title: success" "Installation of rdiff-backup was a success!" 
235                      do_rdiff_con;;
236                   1) msgBox "$rdiff_title: error" "Connected successfully to $rdiff_user@$rdiff_host, but was unable to install the package for some reason.";;
237                   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.";;
238                   *) msgBox "$rdiff_title: error" "Unexpected error.";;
239                esac 
240                return
241             fi
242          else
243             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?"
244             if [ $? = 0 ]; then
245                do_rdiff_con
246             else
247                return
248             fi
249          fi
250       else
251          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?"
252          if [ $? = 0 ]; then
253             do_rdiff_con
254          else
255             return
256          fi
257       fi
258    else
259         echo "SUCCESS: Everything looks good!"
260         echo "Hit return to continue."
261         read
262    fi
263
264    _con_done="(DONE)"
265    setDefault finish
266 }
267
268 do_rdiff_finish() {
269    get_next_filename $configdirectory/90.rdiff
270    cat > $next_filename <<EOF
271 # options = --force
272 # when = everyday at 02
273
274 [source]
275 type = local
276 keep = $rdiff_keep
277
278 # A few notes about includes and excludes:
279 # 1. include, exclude and vsinclude statements support globbing with '*'
280 # 2. Symlinks are not dereferenced. Moreover, an include line whose path
281 #    contains, at any level, a symlink to a directory, will only have the
282 #    symlink backed-up, not the target directory's content. Yes, you have to
283 #    dereference yourself the symlinks, or to use 'mount --bind' instead.
284 #    Example: let's say /home is a symlink to /mnt/crypt/home ; the following
285 #    line will only backup a "/home" symlink ; neither /home/user nor
286 #    /home/user/Mail will be backed-up :
287 #      include = /home/user/Mail
288 #    A workaround is to 'mount --bind /mnt/crypt/home /home' ; another one is to
289 #    write :
290 #      include = /mnt/crypt/home/user/Mail
291 # 3. All the excludes come after all the includes. The order is not otherwise
292 #    taken into account.
293
294 # files to include in the backup
295 EOF
296    ## includes ##
297    if [ "$host_or_vservers" == host -o "$host_or_vservers" == both ]; then
298       set -o noglob
299       for ((i=0; i < ${#rdiff_includes[@]} ; i++)); do
300          echo "include = ${rdiff_includes[$i]}" >> $next_filename
301       done       
302       set +o noglob
303    fi
304
305    if [ "$host_or_vservers" == vservers -o "$host_or_vservers" == both ]; then      
306       cat >> $next_filename <<EOF
307 #
308 # If vservers = yes in /etc/backupninja.conf then the following variables can
309 # be used:
310 # vsnames = all | <vserver1> <vserver2> ... (default = all)
311 # vsinclude = <path>
312 # vsinclude = <path>
313 # ...
314 # Any path specified in vsinclude is added to the include list for each vserver
315 # listed in vsnames (or all if vsnames = all, which is the default).
316 #
317 # For example, vsinclude = /home will backup the /home directory in every
318 # vserver listed in vsnames. If you have 'vsnames = foo bar baz', this
319 # vsinclude will add to the include list /vservers/foo/home, /vservers/bar/home
320 # and /vservers/baz/home.
321 # Vservers paths are derived from $VROOTDIR.
322
323 EOF
324       set -o noglob
325       echo -e "vsnames = $selected_vservers\n" >> $next_filename
326       for i in $rdiff_vsincludes; do
327          echo "vsinclude = $i" >> $next_filename
328       done
329       set +o noglob
330    fi
331    
332    ## excludes ##
333    set -o noglob
334    for ((i=0; i < ${#rdiff_excludes[@]} ; i++)); do
335      echo exclude = ${rdiff_excludes[$i]} >> $next_filename
336    done
337    set +o noglob
338    cat >> $next_filename <<EOF
339
340 ######################################################
341 ## destination section
342 ## (where the files are copied to)
343   
344 [dest]
345 type = remote
346 directory = $rdiff_directory
347 host = $rdiff_host
348 user = $rdiff_user
349 EOF
350
351    chmod 600 $next_filename
352 }
353
354 rdiff_main_menu() {
355    while true; do
356       srcitem="choose files to include & exclude $_src_done"
357       destitem="configure backup destination $_dest_done"
358       conitem="set up ssh keys and test remote connection $_con_done"
359       advitem="edit advanced settings $_adv_done"
360       menuBox "$rdiff_title" "choose a step:" \
361          src "$srcitem" \
362          dest "$destitem" \
363          conn "$conitem" \
364          finish "finish and create config file"
365       [ $? = 0 ] || return
366       result="$REPLY"
367       case "$result" in
368          "src") do_rdiff_src;;
369          "dest") do_rdiff_dest;;
370          "conn") do_rdiff_ssh_con;;
371          "adv") do_rdiff_adv;;
372          "finish")
373             if [[ "$_con_done$_dest_done$_src_done" != "(DONE)(DONE)(DONE)" ]]; then
374                msgBox "$rdiff_title" "You cannot create the configuration file until the other steps are completed."
375             else
376                do_rdiff_finish
377                return
378             fi
379             ;;
380       esac
381    done
382 }
383
384 rdiff_wizard() {
385  
386    # Global variables
387    rdiff_title="rdiff-backup action wizard"
388    _src_done=
389    _dest_done=
390    _con_done=
391    _adv_done=
392    rdiff_keep=60D
393    rdiff_directory=/backup/`hostname`
394    rdiff_type=remote
395    rdiff_user=
396    rdiff_host=
397
398    # Global variables whose '*' shall not be expanded
399    set -o noglob
400    rdiff_includes=(/var/spool/cron/crontabs /var/backups /etc /root /home /usr/local/*bin /var/lib/dpkg/status*)
401    rdiff_excludes=(/home/*/.gnupg /home/*/.local/share/Trash /home/*/.Trash /home/*/.thumbnails /home/*/.beagle /home/*/.aMule /home/*/gtk-gnutella-downloads)
402    rdiff_vsincludes=
403    set +o noglob
404   
405    rdiff_main_menu
406 }
407