fixed configuration files permission check
[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 # 1. include, exclude and vsinclude statements support globbing with '*'
275 # 2. Symlinks are not dereferenced. Moreover, an include line whose path
276 #    contains, at any level, a symlink to a directory, will only have the
277 #    symlink backed-up, not the target directory's content. Yes, you have to
278 #    dereference yourself the symlinks, or to use 'mount --bind' instead.
279 #    Example: let's say /home is a symlink to /mnt/crypt/home ; the following
280 #    line will only backup a "/home" symlink ; neither /home/user nor
281 #    /home/user/Mail will be backed-up :
282 #      include = /home/user/Mail
283 #    A workaround is to 'mount --bind /mnt/crypt/home /home' ; another one is to
284 #    write :
285 #      include = /mnt/crypt/home/user/Mail
286 # 3. All the excludes come after all the includes. The order is not otherwise
287 #    taken into account.
288
289 # files to include in the backup
290 EOF
291    ## includes ##
292    if [ "$host_or_vservers" == host -o "$host_or_vservers" == both ]; then
293       set -o noglob
294       for ((i=0; i < ${#rdiff_includes[@]} ; i++)); do
295          echo "include = ${rdiff_includes[$i]}" >> $next_filename
296       done       
297       set +o noglob
298    fi
299
300    if [ "$host_or_vservers" == vservers -o "$host_or_vservers" == both ]; then      
301       cat >> $next_filename <<EOF
302 #
303 # If vservers = yes in /etc/backupninja.conf then the following variables can
304 # be used:
305 # vsnames = all | <vserver1> <vserver2> ... (default = all)
306 # vsinclude = <path>
307 # vsinclude = <path>
308 # ...
309 # Any path specified in vsinclude is added to the include list for each vserver
310 # listed in vsnames (or all if vsnames = all, which is the default).
311 #
312 # For example, vsinclude = /home will backup the /home directory in every
313 # vserver listed in vsnames. If you have 'vsnames = foo bar baz', this
314 # vsinclude will add to the include list /vservers/foo/home, /vservers/bar/home
315 # and /vservers/baz/home.
316 # Vservers paths are derived from $VROOTDIR.
317
318 EOF
319       set -o noglob
320       echo -e "vsnames = $selected_vservers\n" >> $next_filename
321       for i in $rdiff_vsincludes; do
322          echo "vsinclude = $i" >> $next_filename
323       done
324       set +o noglob
325    fi
326    
327    ## excludes ##
328    set -o noglob
329    for ((i=0; i < ${#rdiff_excludes[@]} ; i++)); do
330      echo exclude = ${rdiff_excludes[$i]} >> $next_filename
331    done
332    set +o noglob
333    cat >> $next_filename <<EOF
334
335 ######################################################
336 ## destination section
337 ## (where the files are copied to)
338   
339 [dest]
340 type = remote
341 directory = $rdiff_directory
342 host = $rdiff_host
343 user = $rdiff_user
344 EOF
345
346    chmod 600 $next_filename
347 }
348
349 rdiff_main_menu() {
350    while true; do
351       srcitem="choose files to include & exclude $_src_done"
352       destitem="configure backup destination $_dest_done"
353       conitem="set up ssh keys and test remote connection $_con_done"
354       advitem="edit advanced settings $_adv_done"
355       menuBox "$rdiff_title" "choose a step:" \
356          src "$srcitem" \
357          dest "$destitem" \
358          conn "$conitem" \
359          finish "finish and create config file"
360       [ $? = 0 ] || return
361       result="$REPLY"
362       case "$result" in
363          "src") do_rdiff_src;;
364          "dest") do_rdiff_dest;;
365          "conn") do_rdiff_ssh_con;;
366          "adv") do_rdiff_adv;;
367          "finish")
368             if [[ "$_con_done$_dest_done$_src_done" != "(DONE)(DONE)(DONE)" ]]; then
369                msgBox "$rdiff_title" "You cannot create the configuration file until the other steps are completed."
370             else
371                do_rdiff_finish
372                return
373             fi
374             ;;
375       esac
376    done
377 }
378
379 rdiff_wizard() {
380  
381    # Global variables
382    rdiff_title="rdiff-backup action wizard"
383    _src_done=
384    _dest_done=
385    _con_done=
386    _adv_done=
387    rdiff_keep=60D
388    rdiff_directory=/backup/`hostname`
389    rdiff_user=
390    rdiff_host=
391
392    # Global variables whose '*' shall not be expanded
393    set -o noglob
394    rdiff_includes=(/var/spool/cron/crontabs /var/backups /etc /root /home /usr/local/*bin /var/lib/dpkg/status*)
395    rdiff_excludes=(/home/*/.gnupg /home/*/.local/share/Trash /home/*/.Trash /home/*/.thumbnails /home/*/.beagle /home/*/.aMule /home/*/gtk-gnutella-downloads)
396    rdiff_vsincludes=
397    set +o noglob
398   
399    rdiff_main_menu
400 }
401