1 #!/bin/bash
  2 
  3 #   ------------------------------------------------------------------
  4 #
  5 #   Shell program to backup specified directories as gzip'ed tar
  6 #   archives on certain days on a ftp server, keeping a number of
  7 #   older archives of the directories there (max is 10) and removing
  8 #   superfluous ones. Additionally, the list of packages on a Debian
  9 #   system is archived.
 10 #
 11 #   The name, user and password of the ftp server, the directories to
 12 #   backup, the days of the week on which a backup is created, the
 13 #   number of old copies and package lists (if any) to keep can be
 14 #   specified in three configuration files located in "/etc/backup"
 15 #   described below. On error (all errors are considered fatal and
 16 #   will lead this program to abort) mail can be send to an
 17 #   administrator.
 18 #
 19 #   Samples of the three configuration files 'ftp', 'locations' and
 20 #   'config' sourced and parsed by this script are given here:
 21 #
 22 #   ftp: stores the information this script needs to login into remote
 23 #        FTP server specified here.
 24 #
 25 #   --------------------------------
 26 #   HOST="ftp.server.org"
 27 #   USER=zippy"
 28 #   PASSWORD="ThePinHead"
 29 #   --------------------------------
 30 #
 31 #   locations: specifies which directories to backup on which days of
 32 #              the week, how the backup archive should be called and
 33 #              how many old backup archives should be kept on the FTP
 34 #              server.
 35 #
 36 #   --------------------------------
 37 #   # This files specifies the directories to backup, the days-of-week
 38 #   # setting is a comma separated list of integers from 1 to 7
 39 #   # indicating the days of the week (1 corresponding to Monday) that
 40 #   # directory should be backed up.  Name is the basename of the
 41 #   # filename to use for the resulting archive, leading to filenames
 42 #   # of the form "Name.YY-MM-DD.tar.gz". The last value specifies
 43 #   # the number of archives to keep on the ftp server (>0,
 44 #   # <=10). Additional (older) copies will be deleted!
 45 #
 46 #   # directory  days-of-week   name     copies to keep
 47 #   /home        1,2,3,4,5,6,7  home     3
 48 #   /var/lib     1,2,3,4,5,6,7  var.lib  1
 49 #   /var/www     1              var.www  7
 50 #   /etc         1,3            etc      9
 51 #   /root        1              root     10
 52 #   --------------------------------
 53 #
 54 #   conf: specifies mail address of backup administrator in case of an
 55 #         error and if (on a Debian system) the list of installed
 56 #         packages should be backed up, too.
 57 #
 58 #   --------------------------------
 59 #   # who to send mail to on error; leave empty to turn off mailing.
 60 #   MAILTO="email@address.org"
 61 #   # number of backup package list created on a Debian systems with "dpkg
 62 #   # --get-selections"; must be a number lower equal to 10. Set to 0 to
 63 #   # turn off backing up of the package list.
 64 #   PACKAGES=3
 65 #   --------------------------------
 66 #
 67 #   Copyright 2005-2006, Sebastian Ley  <sebastian.ley@withouthat.org>
 68 #                    and Thorsten Bonow <men@withouthat.org>.
 69 #
 70 #   This program is free software; you can redistribute it and/or
 71 #   modify it under the terms of the GNU General Public License as
 72 #   published by the Free Software Foundation; either version 2 of the
 73 #   License, or (at your option) any later version.
 74 #
 75 #   This program is distributed in the hope that it will be useful,
 76 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
 77 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 78 #   General Public License for more details.
 79 #
 80 #   Description:
 81 #
 82 #   NOTE: You must be the superuser to run this script.
 83 #
 84 #   Usage:
 85 #
 86 #       backup [ -h | --help ]
 87 #
 88 #   Options:
 89 #
 90 #       -h, --help   Display this help message and exit.
 91 #
 92 #   Arguments:
 93 #
 94 #       none
 95 #
 96 #   External programs:
 97 #
 98 #       mktemp   - make temporary filename (unique)
 99 #       id       - print real and effective UIDs and GIDs
100 #       sed      - stream editor
101 #       basename - strip directory and suffix from filenames
102 #       stat     - display file or filesystem status
103 #       dpkg     - package manager for Debian
104 #       cat      - concatenate files and print on the standard
105 #                  output
106 #       awk      - pattern scanning and processing language
107 #       tar      - the GNU version of the tar archiving utility
108 #       gzip     - compress or expand files
109 #       sort     - sort lines of text files
110 #       ftp      - Internet file transfer program
111 #       grep     - print lines matching a pattern
112 #       mail     - send and receive mail
113 #
114 #   CAVEATS:
115 #
116 #       - No error checking is done
117 #         - if MAILTO specifies a valid eMail address
118 #         - if HOST specifies a valid ftp server
119 #         - if USER specifies a valid user on ftp server
120 #         - if PASSWORD is valid for the user on ftp server
121 #
122 #         - if there is enough space available on ftp server
123 #         - if there is enough space available in the temp dir
124 #
125 #       - Current day timestamp for archives is set once at beginning
126 #         of this script. If this script is started before midnight
127 #         but continues to create backup archives still after
128 #         midnight, these too will have the same timestamp as the
129 #         archives created before midnight.
130 #
131 #       - if PACKAGES option is specified, it is not possible to
132 #         create an backup archive with the basename "packages".
133 #
134 #       - since we grep explicitly for a year with four digits, this
135 #         script is *not* Year 10000 save ;-)
136 #
137 #   Revision History:
138 #
139 #   2005-03-10      Initial version
140 #   2005-10-05      New version implementing deletion of backups on
141 #                   the ftp server
142 #
143 #                   See ChangeLog for additional changes
144 #
145 #   CVS: $Id: backup,v 1.5 2006-08-29 14:01:57 toto Exp $
146 #
147 #   ------------------------------------------------------------------
148 
149 ##### Preamble #####
150 
151 #   ------------------------------------------------------------------
152 #   Constants
153 #   ------------------------------------------------------------------
154 
155     declare -r PROGNAME=$(basename $0)
156     declare -r VERSION="0.3"
157 
158     declare -r TEMP_DIR=/tmp
159     declare -r CONF_DIR=/etc/backup
160     declare -r CONF_FILE=$CONF_DIR/conf
161     declare -r LOCATIONS_FILE=$CONF_DIR/locations
162     declare -r FTP_CONF_FILE=$CONF_DIR/ftp
163 
164     declare -r PIDFILE_DIR=/var/run
165     declare -r PIDFILE=$PIDFILE_DIR/$PROGNAME.pid
166 
167     declare -r CURDATE=$(date --iso-8601)
168     # don't change the suffixes, because in ftp_delete grep looks for
169     # them and has to specify them as regexp, changing them here will
170     # make everything fall apart later...
171     declare -r BACKUP_SUFFIX=".$CURDATE.tar.gz"
172     declare -r PACKAGE_LIST_SUFFIX=".$CURDATE.gz"
173 
174     declare -i MAX_COPIES=10
175 
176 ##### Functions #####
177 
178 #   ------------------------------------------------------------------
179 #   Functions
180 #   ------------------------------------------------------------------
181 
182 
183 function clean_up() {
184 
185 #   ------------------------------------------------------------------
186 #   Function to remove temporary files and other housekeeping
187 #       No arguments
188 #   ------------------------------------------------------------------
189 
190     rm -f -r ${TEMP_DIR1}
191     rm -f ${PIDFILE}
192     return
193 
194 }   # end of clean_up
195 
196 
197 function error_exit() {
198 
199 #   ------------------------------------------------------------------
200 #   Function for exit due to fatal program error
201 #   (All errors are considered fatal!)
202 #       Arguments:
203 #           1 (optional) string containing descriptive error message
204 #   ------------------------------------------------------------------
205 
206     echo "${PROGNAME}: ${1:-"Unknown Error."}" >&2
207     if [ -n "$MAILTO" ]; then
208         echo "${PROGNAME}: ${1:-"Unknown Error."}" | \
209             mail -s "Backup fault!" ${MAILTO}
210     fi
211     clean_up
212     exit 1
213 
214 }   # end of error_exit
215 
216 
217 function graceful_exit() {
218 
219 #   ------------------------------------------------------------------
220 #   Function called for a graceful exit
221 #       No arguments
222 #   ------------------------------------------------------------------
223 
224     clean_up
225     exit
226 
227 }   # end of graceful_exit
228 
229 
230 function signal_exit() {
231 
232 #   ------------------------------------------------------------------
233 #   Function to handle termination signals
234 #       Arguments:
235 #           1 (optional) signal_spec
236 #   ------------------------------------------------------------------
237 
238     case $1 in
239         INT)
240             echo "$PROGNAME: Program aborted by user." >&2
241             clean_up
242             exit
243             ;;
244         TERM)
245             echo "$PROGNAME: Program terminated." >&2
246             clean_up
247             exit
248             ;;
249         *)
250             error_exit "$PROGNAME: Terminating on unknown signal."
251             ;;
252 
253     esac
254 
255 }   # end of signal_exit
256 
257 
258 function make_temp_files() {
259 
260 #   ------------------------------------------------------------------
261 #   Function to create temporary file and directory
262 #       No arguments
263 #   ------------------------------------------------------------------
264 
265     # Temp dir and file for this script, using paranoid method of
266     # creation to insure that file name is not predictable.  This is
267     # for security to avoid "tmp race" attacks.
268 
269     TEMP_DIR1=$(mktemp -d -q "${TEMP_DIR}/${PROGNAME}.$$.XXXXXX")
270     if [ "$TEMP_DIR1" = "" ]; then
271         error_exit "Cannot create temp dir!"
272     fi
273     TEMP_FILE1=$(mktemp -q "${TEMP_DIR1}/${PROGNAME}.$$.XXXXXX")
274     if [ "$TEMP_FILE1" = "" ]; then
275         error_exit "Cannot create temp file!"
276     fi
277 
278 }   # end of make_temp_files
279 
280 
281 function usage() {
282 
283 #   ------------------------------------------------------------------
284 #   Function to display usage message (does not exit)
285 #       No arguments
286 #   ------------------------------------------------------------------
287 
288     echo "Usage: ${PROGNAME} [-h | --help]"
289 
290 }   # end of usage
291 
292 
293 function helptext() {
294 
295 #   ------------------------------------------------------------------
296 #   Function to display help message for program
297 #       No arguments
298 #   ------------------------------------------------------------------
299 
300     cat <<EOF
301 
302     ${PROGNAME} ${VERSION}
303 
304     This is a program to backup specified directories as gzip'ed tar
305     archives on certain days on a ftp server, keeping a number of
306     older archives of the directories there (max is 10) and removing
307     superfluous ones. Additionally, the list of packages on a Debian
308     system is archived.
309  
310     The name, user and password of the ftp server, the directories to
311     backup, the days of the week on which a backup is created, the
312     number of old copies and package lists (if any) to keep can be
313     specified in three configuration files located in "/etc/backup"
314     described below. On error (all errors are considered fatal and
315     will lead this program to abort) mail can be send to an
316     administrator.
317  
318     Samples of the three configuration files 'ftp', 'locations' and
319     'config' sourced and parsed by this script are given here:
320  
321     ftp: stores the information this script needs to login into remote
322          FTP server specified here.
323  
324     --------------------------------
325     HOST="ftp.server.org"
326     USER=zippy"
327     PASSWORD="ThePinHead"
328     --------------------------------
329  
330     locations: specifies which directories to backup on which days of
331                the week, how the backup archive should be called and
332                how many old backup archives should be kept on the FTP
333                server.
334  
335     --------------------------------
336     # This files specifies the directories to backup, the days-of-week
337     # setting is a comma separated list of integers from 1 to 7
338     # indicating the days of the week (1 corresponding to Monday) that
339     # directory should be backed up.  Name is the basename of the
340     # filename to use for the resulting archive, leading to filenames
341     # of the form "Name.YY-MM-DD.tar.gz". The last value specifies
342     # the number of archives to keep on the ftp server (>0,
343     # <=10). Additional (older) copies will be deleted!
344  
345     # directory  days-of-week   name     copies to keep
346     /home        1,2,3,4,5,6,7  home     3
347     /var/lib     1,2,3,4,5,6,7  var.lib  1
348     /var/www     1              var.www  7
349     /etc         1,3            etc      9
350     /root        1              root     10
351     --------------------------------
352  
353     conf: specifies mail address of backup administrator in case of an
354           error and if (on a Debian system) the list of installed
355           packages should be backed up, too.
356  
357     --------------------------------
358     # who to send mail to on error; leave empty to turn off mailing.
359     MAILTO="email@address.org"
360     # number of backup package list created on a Debian systems with "dpkg
361     # --get-selections"; must be a number lower equal to 10. Set to 0 to
362     # turn off backing up of the package list.
363     PACKAGES=3
364     --------------------------------
365  
366     Copyright 2005, Sebastian Ley <sebastian.ley@withouthat.org> and
367                     Thorsten Bonow <men@withouthat.org>.
368  
369     This program is free software; you can redistribute it and/or
370     modify it under the terms of the GNU General Public License as
371     published by the Free Software Foundation; either version 2 of the
372     License, or (at your option) any later version.
373  
374     This program is distributed in the hope that it will be useful,
375     but WITHOUT ANY WARRANTY; without even the implied warranty of
376     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
377     General Public License for more details.
378 
379     $(usage)
380 
381     Options:
382 
383         -h, --help   Display this help message and exit.
384 
385     Arguments:
386 
387         none
388     
389     NOTE: You must be the superuser to run this script.
390 
391 EOF
392 
393 }   # end of helptext
394 
395 
396 function root_check() {
397 
398 #   ------------------------------------------------------------------
399 #   Function to check if user is root
400 #       No arguments
401 #   ------------------------------------------------------------------
402 
403     if [ "$(id | sed 's/uid=\([0-9]*\).*/\1/')" != "0" ]; then
404         error_exit "You must be the superuser to run this script."
405     fi
406 
407 }   # end of root_check
408 
409 
410 function check_config() {
411 
412 #   ------------------------------------------------------------------
413 #   Function to check if configuration files are valid, have the
414 #   correct permissions etc.
415 #       No arguments
416 #   ------------------------------------------------------------------
417 
418     local file
419 
420     echo -n "Checking configuration..."
421     for file in "$LOCATIONS_FILE" "$FTP_CONF_FILE" "$CONF_FILE"; do
422         if [ ! -f $file ]; then
423             error_exit "Missing configuration file '$file'."
424         fi
425         if [ "$(stat -c '%U %a' $file 2>/dev/null)" != "root 600" ]; then
426             error_exit "File '$file' isn't owned by root:root with permissions set to 0600."
427         fi
428         if [ "$file" != "$LOCATIONS_FILE" ]; then
429             source $file || error_exit "Couldn't source file '$file'."
430         fi
431     done
432     for variable in "$HOST" "$USER" "$PASSWORD" "$PACKAGES"; do
433         if [ -z "$variable" ]; then
434             error_exit "Variable '$variable' not set correctly."
435         fi
436     done
437     if [[ $PACKAGES -lt 0 || $PACKAGES -gt $MAX_COPIES ]]; then
438         error_exit  "PACKAGES option set to invalid value '$PACKAGES'."
439     fi
440     echo "done."
441     return
442 
443 }   # end of check_config
444 
445 
446 function create_tarballs() {
447 
448 #   ------------------------------------------------------------------
449 #   Function to create the tar archives from the backup directories
450 #       No arguments
451 #   ------------------------------------------------------------------
452 
453     # The 'locations' file is parsed for all data needed to create the
454     # archives; only the number of old copies to keep is error
455     # checked. Tar archives are created from directories schedules for
456     # backup today and their names are added to an archive list. A
457     # corresponding array is filled with the number of old copies to
458     # preserve on the server. The archive list is checked for
459     # duplicates.
460 
461     local directory
462     local days
463     local name
464     local copies
465     local i=1
466 
467     # parse the locations file
468     while read; do
469     if [ -n "$REPLY" ] && ! echo "$REPLY" | grep -q ^\ *\# ; then
470         directory=$(echo "$REPLY" | awk '{print $1}')
471         days=$(echo "$REPLY" | awk '{print $2}')
472         name=$(echo "$REPLY" | awk '{print $3}')$BACKUP_SUFFIX
473         copies=$(echo "$REPLY" | awk '{print $4}')
474         if [[ $copies -le 0 || $copies -gt $MAX_COPIES ]]; then
475             error_exit "Invalid number of backup copies '$copies' for file '$name'."
476         fi
477         if echo $days | grep -q $(date +%u); then
478             echo -n "Creating tarball for directory '$directory'..."
479             /usr/bin/nice -n 19 tar -c -z -f "$TEMP_DIR1/$name" "$directory" > /dev/null 2>&1
480             if [ $? -ne 0 ]; then
481                 error_exit "Error creating tar archieve '$name'."
482             fi
483             if [ -z "$archive_list" ]; then
484                 archive_list="$name"
485             else
486                 # check for duplicates
487                 echo "$archive_list" | grep $name > /dev/null 2>&1
488                 if [ $? -eq 0 ]; then
489                     error_exit "Archive '$name' already backed up."
490                 else
491                     archive_list="$archive_list $name"
492                 fi
493             fi
494             number_of_backup_copies_array[$i]="$copies"
495             i=$[ i + 1 ]
496             echo "done."
497         fi
498     fi
499     done < $LOCATIONS_FILE
500     if [ $? -ne 0 ]; then
501         error_exit "Error reading file '$LOCATIONS_FILE'."
502     fi
503     return
504 
505 }   # end of create_tarballs
506 
507 
508 function dump_packages_list() {
509 
510 #   ------------------------------------------------------------------
511 #   Function to create a file list of all installed Debian packages
512 #       No arguments
513 #   ------------------------------------------------------------------
514 
515     # A list of all installed Debian packages is stuffed into a
516     # gziped archive and it's name is added *up front* into the
517     # archive list; the number of old copies to keep on the FTP server
518     # therefore is added at the beginning of the corresponding array,
519     # too. Again the archive list is checked for duplicates.
520 
521     echo -n "Dumping packages list..."
522     dpkg --get-selections | gzip - > ${TEMP_DIR1}/packages$PACKAGE_LIST_SUFFIX
523     if [ $? -ne 0 ]; then
524         error_exit "Error creating packages list '${TEMP_DIR1}/packages$PACKAGE_LIST_SUFFIX'."
525     fi
526     if [ -z "$archive_list" ]; then
527         archive_list="packages$PACKAGE_LIST_SUFFIX"
528     else
529         # check for duplicates
530         echo "$archive_list" | grep "packages$PACKAGE_LIST_SUFFIX" > /dev/null 2>&1
531         if [ $? -eq 0 ]; then
532             error_exit "Archive 'packages$PACKAGE_LIST_SUFFIX' already backed up."
533         else
534             # package list always first element in list!
535             archive_list="packages$PACKAGE_LIST_SUFFIX $archive_list"
536         fi
537     fi
538     # number of backup copies of package list alwasy first element of array!
539     number_of_backup_copies_array[0]="$PACKAGES"
540     echo "done."
541     return
542 
543 }   # end of dump_packages_list
544 
545 
546 function ftp_remote_command() {
547 
548 #   ------------------------------------------------------------------
549 #   Function to execute an ftp command on the ftp server
550 #       Arguments:
551 #           1 (required) ftp_command
552 #   ------------------------------------------------------------------
553 
554     # Fatal error if required arguments are missing
555 
556     if [ "$1" = "" ]; then
557         error_exit "ftp_remote_command: missing argument 1"
558     fi
559 
560     ftp -n $HOST > /dev/null 2>&1 <<End-Of-FTP-Session
561 
562 user $USER $PASSWORD
563 binary
564 prompt
565 lcd $TEMP_DIR1
566 $1
567 bye
568 End-Of-FTP-Session
569 
570     return $?
571 
572 }   # end of ftp_remote_command
573 
574 
575 function ftp_get_file_list() {
576 
577 #   ------------------------------------------------------------------
578 #   Function to get a list of files already backed up on FTP server
579 #       No arguments
580 #   ------------------------------------------------------------------
581 
582     echo -n "Getting file list over FTP..."
583     ftp_remote_command "ls . $TEMP_FILE1"
584     if [ $? -ne 0 ]; then
585         error_exit "Error getting FTP file list."
586     fi
587     echo "done."
588     return
589 
590 }   # end of ftp_get_file_list
591 
592 
593 function ftp_upload() {
594 
595 #   ------------------------------------------------------------------
596 #   Function to upload backup archives to ftp server
597 #       No arguments
598 #   ------------------------------------------------------------------
599 
600     local archive
601 
602     echo -n "Starting FTP upload..."
603     for archive in ${archive_list}; do
604         grep "$archive" $TEMP_FILE1 >/dev/null 2>&1 && \
605             error_exit "Archive '$archive' already on FTP server."
606     done
607     ftp_remote_command "mput $archive_list"
608     if [ $? -ne 0 ]; then
609         error_exit "Error uploading archives."
610     fi
611     echo "done."
612     return
613 
614 }   # end of ftp_upload
615 
616 
617 function ftp_delete() {
618 
619 #   ------------------------------------------------------------------
620 #   Function to delete old backup archives on ftp server
621 #       No arguments
622 #   ------------------------------------------------------------------
623 
624     # (for archive loop:) Walk through all elements of the archive
625     # list and check if there are old superfluous copies backed up on
626     # the FTP server; these are deleted.
627     #
628     # Before doing anything else, the file list is updated from the
629     # FTP server because hopefully the new backup archives created
630     # today have been successfully uploaded.
631     #
632     # Since the archive list stores the complete names of local
633     # archives backed up today, the time-stamp suffix first has to be
634     # removed in order to compare the remaining basename with files
635     # stored on the FTP server (which have similar suffixes, but with
636     # older dates).
637     # 
638     # (for file loop:) The older copies of an archive file are listed
639     # in reverse order (newest first) and counted; if their number is
640     # higher than the maximum number of old copies stored on the FTP
641     # server, the filenames of the surplus files are added to a delete
642     # list.
643     #
644     # After checking the delete list for duplicates, an FTP connection
645     # is established and the files are deleted.
646 
647     # elements of the archive list
648     local archive
649     # archived file stored on the FTP server
650     local file
651     # index variable for the archives stored in archive variable
652     local i
653     # index variable for the archive files stored in files variable
654     local j=1
655 
656     # update the file list from the FTP server
657     ftp_get_file_list
658 
659     if [ $PACKAGES -ne 0 ]; then
660         # if PACKAGES option is specified, our array has a zeroth
661         # element
662         i=0
663     else
664         i=1
665     fi
666     echo -n "Start deleting old backup archives..."
667     for archive in $archive_list; do
668         if [ "$archive" == "packages$PACKAGE_LIST_SUFFIX" ]; then
669             archive=$(basename $archive $PACKAGE_LIST_SUFFIX)
670         else
671             archive=$(basename $archive $BACKUP_SUFFIX)
672         fi
673         # since we check for a year with 4 digits, this is not Year
674         # 10000 save :-)
675         #
676         # the $(cat...) command works on FTP directory listings in
677         # this form, the filename being the ninth element:
678         # -rw----r--   1 b007210  500  2290 Oct  7 16:03 foo.bar.2005-10-07.gz
679         for file in $(cat $TEMP_FILE1 | awk '{print $9}' | grep "$archive.[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\(.tar\)\{0,1\}.gz" | sort -r); do
680             if [ $j -gt $((${number_of_backup_copies_array[$i]})) ]; then
681                 if [ -z "$delete_list" ]; then
682                     delete_list="$file"
683                 else
684                     # check for duplicates
685                     echo "$delete_list" | grep $file > /dev/null 2>&1
686                     if [ $? -eq 0 ]; then
687                         error_exit "Archive '$file' already selected for deletion."
688                     else
689                         delete_list="$delete_list $file"
690                     fi
691                 fi
692             fi
693             j=$[ j + 1 ]
694         done
695         j=1
696         i=$[ i + 1 ]
697     done
698     ftp_remote_command "mdelete $delete_list"
699     if [ $? -ne 0 ]; then
700         error_exit "Error deleting old archives."
701     fi
702     echo "done."
703     return
704 
705 }   # end of ftp_delete
706 
707 
708 function ftp_verify() {
709 
710 #   ------------------------------------------------------------------
711 #   Function to verify if all new archives have been uploaded to FTP
712 #   server correctly and old archives have been deleted from it
713 #       No arguments
714 #   ------------------------------------------------------------------
715 
716     local archive
717     local error_flag=""
718     local missing_file_list
719     local not_deleted_file_list
720 
721     # update the file list from the FTP server
722     ftp_get_file_list
723 
724     echo -n "Start verifying backup transactions..."
725     # check if all archives have been put on FTP server 
726     for archive in ${archive_list}; do
727         grep "$archive" $TEMP_FILE1 >/dev/null 2>&1
728         if [ $? -ne 0 ]; then
729             echo "Error: Could not find archive '$archive' on FTP server!"
730             error_flag="raised"
731             if [ -z "$missing_file_list" ]; then
732                 missing_file_list="$archive"
733             else
734                 missing_file_list="$missing_file_list $archive"
735             fi
736         fi
737     done
738     # check if all old archives supposed to be deleted actually have
739     # been deleted
740     for archive in ${delete_list}; do
741         grep "$archive" $TEMP_FILE1 >/dev/null 2>&1
742         if [ $? -eq 0 ]; then
743             echo "Error: Archive '$archive' still found on FTP server!"
744             error_flag="raised"
745             if [ -z "$not_deleted_file_list" ]; then
746                 not_deleted_file_list="$archive"
747             else
748                 not_deleted_file_list="$not_deleted_file_list $archive"
749             fi
750         fi
751     done
752     if [ -n "$error_flag" ]; then
753         error_exit "Archives '$missing_file_list' not found on FTP server, archives '$not_deleted_file_list' still there."
754     fi
755     echo "done."
756     return
757 
758 }   # end of ftp_verify
759 
760 
761 #   ------------------------------------------------------------------
762 #   Program starts here
763 #   ------------------------------------------------------------------
764 
765 ##### Initialization And Setup #####
766 
767 root_check
768 
769 # Trap TERM, HUP, and INT signals and properly exit
770 
771 trap "signal_exit TERM" TERM HUP
772 trap "signal_exit INT"  INT
773 
774 ## Create temporary file(s)
775 #
776 make_temp_files
777 
778 ## Create pid file
779 # 
780 if [ -f "$PIDFILE" ]; then
781     error_exit "PID file '$PIDFILE' already exists."
782 else
783     echo $$ > $PIDFILE || error_exit "Couldn't create PID file '$PIDFILE'."
784 fi
785 
786 
787 ##### Command Line Processing #####
788 
789 # simple command line parsing with getopts buildin instead of external
790 # getopt if only -h|--help option is needed
791 if [ "$1" = "--help" ]; then
792     helptext
793     graceful_exit
794 fi
795 
796 while getopts :h OPT; do
797     case $OPT in
798       h)
799           helptext
800           graceful_exit
801           ;;
802       *)
803           usage
804           clean_up
805           exit 1
806     esac
807 done
808 
809 
810 ##### Main Logic #####
811 
812 # list of backup archives
813 # "basename1.YYYY-MM-DD.tar.gz basename2.YYYY-MM-DD.tar.gz
814 #  basename3.YYYY-MM-DD.tar.gz ..."
815 declare archive_list
816 # array storing the numbers which specify how many copies of the
817 # backed up archives should be kept on the FTP server. The first
818 # number (stored in number_of_backup_copies_array[1]!) corresponds to
819 # the first archive from archive_list. 0 element is reserved for the
820 # package list.
821 declare -a number_of_backup_copies_array
822 # list of files to be removed from remote FTP server (looks like
823 # archive_list, but stores old archives about to be deleted)
824 declare delete_list
825 
826 echo "Starting new backup on $CURDATE."
827 check_config
828 create_tarballs
829 if [ $PACKAGES -ne 0 ]; then
830     dump_packages_list
831 fi
832 ftp_get_file_list
833 ftp_upload
834 ftp_delete
835 ftp_verify
836 echo "Backup finished. Exiting..."
837 
838 graceful_exit
839 
840 # end of backup
841 
842 # Local Variables:
843 # mode: outline-minor
844 # outline-regexp: "\\(function\\)\\|\\(##### \\)"
845 # outline-heading-end-regexp: "\\(() {\n\\)\\|\\( #####\n\\)"
846 # fill-column: 70
847 # End: