#!/bin/bash
#
# J.P.Boggis 25/11/2004:  Script to backup selected files/directories using
#			  'rsync' from multiple sources, creating an archive
#			  history of modified and deleted files.
#
# Last updated:  21/02/2012
#
#
# License:
# ~~~~~~~~
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# long with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# For further information, see:  http://www.gnu.org/licenses/gpl.html
#
#
# History:
# ~~~~~~~~
# J.P.Boggis 25/11/2004 v1.0  -  Initial version.
# J.P.Boggis 02/02/2005 v1.1  -  Updated to use separate config file.
# J.P.Boggis 09/02/2005 v1.2  -  Improved reporting and remote performance.
# J.P.Boggis 10/02/2005 v1.3  -  Option to compress previous archives.
# J.P.Boggis 04/03/2005 v1.4  -  Added multiplex option and pre/post commands.
# J.P.Boggis 29/03/2005 v1.5  -  Lock file added to detect script running.
# J.P.Boggis 05/04/2005 v1.6  -  Added MANUAL_[NAME].
# J.P.Boggis 19/05/2005 v1.7  -  Option added to zip previous archives.
# J.P.Boggis 20/05/2005 v1.8  -  Added EXCLUDE_[NAME]= option.
# J.P.Boggis 08/07/2005 v1.9  -  Added RSYNC_RETRIES for failed transfers.
# J.P.Boggis 11/07/2005 v2.0  -  Added RSYNC_RETRY_DELAY and RSYNC_TIMEOUT.
# J.P.Boggis 13/07/2005 v2.1  -  Added ability to do incremental backups
#                                using hardlinks.
# J.P.Boggis 05/08/2005 v2.2  -  Added DEST_CREATE_[NAME]= option.
# J.P.Boggis 16/11/2005 v2.3  -  Support for multiple hosts in SOURCEPATH.
# J.P.Boggis 23/01/2006 v2.4  -  Destination path is now recursively created
#				 (If directory structure doesn't exist) and
#				 DEST_CREATE_[NAME]=1 for source.  Default
#				 for DEST_CREATE (If not set for source)
#				 can now be set globally.
# J.P.Boggis 24/01/2006 v2.5  -  Option to purge previous archives for all
#				 sources when disk utilisation is exceeded
#				 (Rather than just the current source)
#				 added.  Oldest sources are removed first.
# J.P.Boggis 17/03/2006 v2.6  -  Added TIMEOUT_[NAME] option to set custom
#				 timeout for a source.
# J.P.Boggis 27/03/2006 v2.7  -  Purge lock files added to prevent conflicts
#				 when multiple multiplexed instances are
#				 purging previous archives at the same time.
#				 Path on remote source is now checked for
#				 accessibility and files before preceeding.
# J.P.Boggis 02/05/2006 v2.8  -  Previous archives are now purged (If
#				 necessary) while the backup is waiting for
#				 the correct start time.  This helps prevent
#				 the backup from being delayed while waiting
#				 for old files to be deleted.  To best
#				 utilise this feature, multi_backup_rsync
#				 should be called via cron several hours
#				 before the backups are due to start.
# J.P.Boggis 19/05/2006 v2.9  -  Elapsed time added to purge log messages.
# J.P.Boggis 29/06/2006 v3.0  -  Check for files in remote directory will
#                                now try other hostname (If more than one
#                                has been specified) if connection to the
#				 first picked at random fails.
# J.P.Boggis 26/07/2006 v3.1  -  expr is now used instead of $[] for
#                                arithmetic, to increase compatibility
#                                with shells other than bash.
# J.P.Boggis 11/07/2008 v3.2  -  Added PID check to lock file checks.
#				 Kills the process if the lock file
#				 hasn't been updated within LOCK_TIME.
# J.P.Boggis 11/06/2009 v3.3  -  Backup is now aborted if directory(s)
#				 specified for MOUNT_DEST are not mounted
#				 at the time the backup commences.
# J.P.Boggis 11/06/2009 v3.4  -  SYNC_OLDEST_FIRST option added. 
# J.P.Boggis 22/07/2009 v3.5  -  IGNORE_ERRORS option added.
# J.P.Boggis 11/08/2009 v3.6  -  DEST2_[SOURCE] added to allow synchronisation
#                                (Mirroring) to a secondary destination.
# J.P.Boggis 16/12/2009 v3.7  -  MOUNTSPEC_[NAME] added.
# J.P.Boggis 29/01/2010 v3.8  -  'info' and 'sources' added.
# J.P.Boggis 10/02/2010 v3.9  -  'status' added.
# J.P.Boggis 24/02/2010 v4.0  -  Removal of old logs now works correctly.
#                                Added COMPRESS_FILES_[NAME]= option.
#                                Added INCLUDE_[NAME]= option.
# J.P.Boggis 04/05/2010 v4.1  -  Added FAILOVER_[NAME]= option.
# J.P.Boggis 07/05/2010 v4.2  -  Added multiple DEST_[NAME] paths.
# J.P.Boggis 12/05/2010 v4.3  -  Added AVOID_BACKUP_HOURS
# J.P.Boggis 19/05/2010 v4.4  -  Multiple destination paths are now checked
#				 for over utilisation and purged if necessary.
# J.P.Boggis 27/01/2011 v4.5  -  Last log message shown for info/status
# J.P.Boggis 02/02/2011 v4.6  -  Next path number for source is saved in /tmp
# J.P.Boggis 14/02/2011 v4.7  -  DEST_EXISTS_[NAME] option.
# J.P.Boggis 24/03/2011 v4.8  -  Mount status of directories listed in
#                                MOUNT_DEST is now checked appropriately for
#                                each source (Non-applicable dir that is not
#                                mounted does not abort backup.)
# J.P.Boggis 12/04/2011 v4.9  -  Multiple source paths can now be specified
#                                as well as hosts for failover.
# J.P.Boggis 18/04/2011 v5.0  -  Bug fixes in FailoverSourcePath
# J.P.Boggis 21/04/2011 v5.1  -  Bug fixes in AlternativeDestPath
# J.P.Boggis 31/05/2011 v5.2  -  Bugs fixed in AlternativeDestPath that
#				 prevented first destination being chosen.
# J.P.Boggis 18/08/2011 v5.3  -  Bug in AlternativeDestPath that caused
#				 DEST_EXISTS_[NAME]=0 to be ignored fixed.
# J.P.Boggis 10/10/2011 v5.4  -  Fixed /tmp/$SOURCE.next not being incremented.
# J.P.Boggis 14/10/2011 v5.5  -  Added running time to status report.  Lock
#                                file is now removed is backup is skipped
#                                (Unable to mount source, etc.)
# J.P.Boggis 27/10/2011 v5.6  -  .next is updated after backup has completed.
# J.P.Boggis 25/01/2012 v5.7  -  Warns if no files in backup have been modified
#				 in the past 48 hours.
# J.P.Boggis 30/01/2012 v5.8  -  WARN_DAYS_[NAME] option added.
#				 SYNC_OLDEST_FIRST_[NAME] now checks each file
#				 exists before attempting to rsync.
# J.P.Boggis 31/01/2012 v5.9  -  Errors and warnings are now reported
#				 separately.
# J.P.Boggis 10/02/2012 v6.0  -  Others: section added to summary.
# J.P.Boggis 21/02/2012 v6.1  -  Warnings are no-longer logged as errors
#
#
# Usage:
# ~~~~~~
#:> USAGE:  multi_backup_rsync [start|cron|silent|multiplex] [<OPTIONS>]
#:>         multi_backup_rsync [run|info|status] [ALL|<SOURCE>[,<SOURCE>][...]]
#:?>        multi_backup_rsync sources
#:>
#:>         start       -  Start backup of all sources.
#:>         cron        -  Same as start, but for use in cronjobs.
#:>                        Only errors will be output.
#:>         silent      -  Same as cron, but with absolutely no output
#:>                        (For use with summary/errors via E-mail.)
#:>         multiplex   -  Used internally to start multiplexed sources.
#:>
#:>         run         -  Manually run backups for specified sources.
#:>                        BACKUP_DAYS and MULTIPLEX will be ignored
#:>                        (Sources specified are run sequentially
#:>                        in the order specified.)
#:>
#:>         sources     -  Show list of available sources.
#:>
#:>         status      -  Show status of source(s).
#:>
#:>         info        -  Show information about source(s).
#
#
# Backup sources are configured in /etc/multi_backup_rsync.sources, which
# should contain the following:
#
#    SOURCES="NAME [NAME] [...]"
#
#       List of defined sources that will be backed up, e.g:
#
#          SOURCES="SERVER1 SERVER2 SERVER3"
#
#
#    MOUNT_[NAME]="[mount path]"
#    MOUNTCMD_[NAME]="[mount command-line]"
#    UMOUNTCMD_[NAME]="[unmount command-line]"
#    MOUNTSPEC_[NAME]="/filespec"
#
#       If [mount path] is specified, this will be mounted prior to the
#	backup and unmounted at the end.  This is used to mount NFS,
#	Windows/Samba,	NetWare, etc. volumes locally for backup.
#
#       The mount must be configured in /etc/fstab or the command-lines
#       for mounting/unmounting must specified using MOUNTCMD_[NAME] and
#	UMOUNTCMD_[NAME] (In addition to the mount path), e.g:
#
#	   MOUNT_SERVER2="/servers/server2"
#   	   MOUNTCMD_SERVER3="ncpmount -S SERVER3 -A server3.local -U .admin.tree
#	      -C /servers/server3"
#          UMOUNTCMD_SERVER3="ncpumount /servers/server3"
#
#       NOTE:  For NetWare, use CRON.NLM to backup NDS on a daily basis via
#              'dsrepair -rc' and trustee assignments via 'trustbar volume: -B'
#
#       MOUNTSPEC_[NAME] specifies a file specification (E.g:  /*.dat) that
#       will be appended when listing files in the mount path to check whether
#       it is actually mounted or not (If no files are found, the backup will
#       be skipped.)
#
#
#    SOURCEPATH_[NAME]="[host[;host[;...]]:][path][:path][:...]"
#
#       Path to the source files that you wish to backup.  For ssh accessible
#	sources, this will generally be host.name:/ so that the source files
#	are relative to root on the remote server.  For pre-mounted volumes
#	(See above), this needs to be /path/to/mounted/volume
#
#       If a remote host has multiple links (E.g:  Two Internet connections),
#       multiple hostnames can be specified as a ; separated list.  A host
#       will be picked at random for each source file/directory, unless
#       FAILOVER_[NAME] is defined (See below.)
#
#       Multiple hosts enables fault tolerance  -  If the connection fails,
#       times out, etc., one of the other hostname(s) will be used for each
#       retry.
#
#       Multiple source paths may also be specified if the source is
#       duplicated on more than one disk (E.g:  Remotely backing up
#       another archive with alternative paths specified for DEST_[NAME])
#
#	E.g:  SOURCEPATH_SERVER1="server1.local:/"
#	      SOURCEPATH_USER1="user1@server1.local:"
#	      SOURCEPATH_MULTI="server.domain1.tld;server.domain2.tld:/"
#             SOURCEPATH_MPATH="server.domain.tld:/path1:/path2"
#	      SOURCEPATH_LOCAL="/home/user2"
#
#
#    FAILOVER_[NAME]=0|1
#
#       When multiple hosts (Separated by ;) are specified for
#       SOURCEPATH_[NAME], this option enables failover selection of the host
#       instead of random.
#
#       The first host in the list will always be used, but if the transfer
#       fails, the next host in the list will be used instead for the next
#       retry.
#
#
#    SOURCEFILES_[NAME]="[file:]name [[file:]name] [...]"
#
#       Files/directories within above source path to backup, e.g:
#
#          SOURCEFILES_SERVER1="etc root home var file:test.txt"
#
#       If the source is not prefixed with 'file:', it will be treated as a
#       directory and backed up recursively.  If prefixed with file: it will
#       be treated as a file and backed up individually (Wildcards may be
#       specified for file:, e.g:  file:/etc/*.conf
#
#
#    DEST_[NAME]="[backup path][;alternative backup path][...]"
#
#	Local path to copy backups to, e.g:
#
#          DEST_SERVER1="/backup/server1"
#
#       If more than one path is specified, then the backup will be alternated
#       between the available paths based on the current date, initial random
#       selection and the source number (If multiple sources are defined and
#       being backed up), e.g:
#
#          DEST_SERVER1="/backup/server1;/backup2/server1"
#
#       This allows backups to be distributed between multiple disks for
#       data redundancy.
#
#
#       The following will be created under the destination path directory:
#
#          incremental  -  For incremental backups, contains daily, weekly
#                          and monthly incremental backups in the format
#                          m(N)-w(N)-d(N).
#
#                          The current backup can be found in current, which
#                          is a symlink to m0-w0-d1.  This contains a full
#                          complete copy of the specified source files/
#                          directories from the source server.  The other
#                          incremental directories contain changed/deleted
#                          file and hardlinks to those that haven't changed.
#                          
#          current      -  For non-incremental backups, contains the current
#                          backup files.  This is a complete copy of the
#                          specified source files/directories from the
#                          source server.
#
#          previous     -  Contains dated archives of modified/deleted files.
#			   These are in the format YYYY-MM-DD and compressed
#			   to save disk space.
#
#			   NOTE:  These archives DO NOT contain unchanged
#                                 files.
#
#          log          -  Contains backup logs:
#
#                          summary  -  Brief summary of backup.
#                          errors   -  Summary of backup errors (If any.)
#                          results  -  Full results log of backup.
#
#
#    DEST_EXISTS_[NAME]=0|1
#
#       When using multiple destination paths for failover, this option
#       specifies that the destination directories already exist.  In
#       this case, the backup will failover to the next destination in
#       the list if it is unable to access its first choice due to it
#       not being mounted or any other error.
#
#
#    DEST2_[NAME]="[secondary backup path]"
#
#	Secondary path to synchronise the backup to after the primary backup
#       has completed.  This allows the backup to be mirrored onto a second
#       drive/partition, e.g:
#
#          DEST2_SERVER1="/backup2/server1"
#
#       NOTE:  No disk space checks are currently performed on the the
#              secondary path.  The secondary drive/partition should
#              ideally have the same or greater available free space
#              as the primary.
#
#              Multiple secondary destinations cannot be specified.
#
#
#    INCLUDE_[NAME]="pattern [pattern] [...]"
#
#       Files that match the specified patterns will be included in
#       the backup of the specified source (rsync --include)
#
#       E.g:  important.dat  Include file important.dat
#
#
#    EXCLUDE_[NAME]="pattern [pattern] [...]"
#
#       Files that match the specified patterns will be excluded from
#       the backup of the specified source (rsync --exclude)
#
#       E.g:  *.tmp mydir/   Excludes files ending in .tmp and dir mydir
#
#
#    LOCAL_[NAME]=0|1
#
#       If backing up a local source (I.e:  Local mounted disk or fast LAN
#       host), set this to 1 to use rsync options optimised for local
#       copying (No compression.)
#
#
#    MANUAL_[NAME]=0|1
#
#       If this is set to 1, the source can only be ran manually, using
#       'multi_backup_rsync run NAME'.
#
#
#    MULTIPLEX_[NAME]=0|1
#
#       If multiple sources are being backed up, setting this to 1 for each
#       source will allow these sources to be backed up simultaneously.
#       Non-multiplex sources will be backed up sequentially.
#
#
#    DELAY_[NAME]=seconds
#
#       Sets the minimum delay between each ssh/rsync command.  This is for
#       use with hosts with anti-SSH attack measures implemented, where
#       multiple SSH connections over a short period of time will result
#       in a firewall host block.
#
#
#    RETRY_DELAY_[NAME]=seconds
#
#       Sets the minimum delay between each retry.  This can be used to
#       allow sufficient time for open files to be closed.
#
#
#    TIME_[NAME]="hh:mm"
#
#       For multiplexed sources, wait until the specified time before starting
#       backup job.  This allows individual jobs to be delayed until the
#       specified time (24-hour format.)
#
#
#    BANDWIDTH_[NAME]=value
#
#       Limit bandwidth to specified value Kbps (Bits per second)
#
#
#    TIMEOUT_[NAME]=value
#
#       Set custom timeout value (Setting this to 0 will disable the default
#       (RSYNC_TIMEOUT) and use rsync's built-in default only.)
#
#
#    IGNORE_ERRORS_[NAME]="regexp"
#
#       If set, rsync errors matching this regexp will be ignored, preventing
#       automatic retry (Unless other non-matching errors occur.)
#
#
#    INCREMENTAL_BACKUPS_[NAME]=0|1
#    INCREMENTAL_DAILY_[NAME]=value
#    INCREMENTAL_WEEKLY_[NAME]=value
#    INCREMENTAL_MONTHLY_[NAME]=value
#
#       Overrides incremental backup settings for the named backup source
#       (Defaults defined below will be used otherwise.)
#
#
#    COMPRESS_FILES_[NAME]=0|1
#
#       Individually compresses each file within the archive using gzip.
#       This is useful for backups of large files that compress well and
#       are always changing (E.g:  Backup of Exchange .pst's), making
#       hardlinks pointless.
#
#       NOTE:  When using this option, hardlinks will not be created for
#              the daily/weekly/monthly archive directories.
#
#              There must be sufficient disk space to decompress each
#              SOURCEFILE. 
#
#
#    RSYNC_OPTIONS_[NAME]="[options]"
#
#       Alternative rsync options for this source.  RSYNC_OPTIONS are used
#	by default if these are not specified for a particular source.
#
#	E.g:  RSYNC_OPTIONS_SERVER1="-abzrv"
#
#
#    SSH_PORT_[NAME]=value
#
#       Specifies an alternative port to use for SSH instead of the default
#       port 22.  This is useful for hosts where SSH is listening on a
#       non-standard port, or access to the standard port has been
#       restricted to stop SSH attacks.
#
#
#    PRE_EXEC_[NAME]="command [; command][; ...]"
#    POST_EXEC_[NAME]="command [; command]; ...]"
#
#       Commands to execute before and after backup of the source.  For
#       example, this can be used to run mysqldump to backup MySQL
#       databases prior to backing up the source.
#
#       For backups of remote hosts, the command(s) will be executed on
#       the remote host using ssh.
#
#
#    KEEP_[NAME]=value
#
#       Maximum number of previous archives to keep (Providing disk
#       utilisation is not exceeded.)  If set to 0, no limit will be
#       applied.
#
#
#    BACKUP_DAYS_[NAME]="day [day] [...]"
#
#       Days when this source will be backed up (0 is Sunday, 1 is Monday,
#       etc.)  E.g:  "1 2 3 4 5" will only backup the source during weekdays.
#       If this is not specified, every day will be assumed.
#
#
#    UTILISATION_[NAME]=value
#
#       Alternative utilisation value for destination backup path.  When
#       this is exceeded, previous archives will be deleted to free up
#	disk space.  If this is not specified, the value for
#	PURGE_UTILISATION is used.
#
#       E.g:  UTILISATION_SERVER1=75
#
#
#    DEST_CREATE_[NAME]=0|1
#
#       If set to 0, the destination directory (DEST_[NAME]) will not be
#       created if it does not exist.  Useful if the destination directory
#       is on a removable external disk, preventing creation and a full
#       backup onto the local disk if it's not mounted for any reason.
#
#
#    SYNC_OLDEST_FIRST_[NAME]=0|1
#
#       If set to 1, each file will be individually syncronised in order
#       of oldest modification time first.  After this has completed, a
#       normal sync will be performed to copy new files and/or delete
#       old files.
#
#
#    PURGE_OLD_DIRS="/path/dir [/path/dir] [...]"
#
#       When PURGE_ALL_SOURCES=1, this option specifies additional directories
#       to purge in addition to the destination directories of current defined
#       sources (SOURCES=...)  This can be used to purge previous archives of
#       old sources that are no-longer backed up (E.g:  Server removed from
#       network.)
#
#
#    E-mail report options:
#    ~~~~~~~~~~~~~~~~~~~~~~
#    EMAIL_RCPT="name@domain.tld"
#
#       Send summary and/or error report to specified recipient.
#
#    EMAIL_FROM="name@domain.tld"
#
#       Sender address to use for above E-mail, e.g:  backup@domain.tld
#
#    EMAIL_SUBJECT="Subject prefix"
#
#       Subject for above E-mail, e.g:  "Backup summary"
#
#    WARN_DAYS_[NAME]=value
#
#	Sets the time period to check for modified files in the backup
#       destination post-backup.  If no files are changed within this
#       time period, a warning will be generated (Indicating possible
#       failed backup.)
#
#	The default is 2 days and the warning can be optionally disabled
#       by setting this value to 0.
#
#
#
# NOTE:  For automated and unattended backup, this script should be
#        called from a cron job.  If backing up remote servers, create
#        SSH certificates to allow rsync to copy files from the remote
#        hosts without prompting for a password.


# Shell for spawning background processes
BGSHELL="/bin/bash"


# Source definitions
SOURCEDEFS="/etc/multi_backup_rsync.sources"

# Mount destination path for archives (If external (E.g:  USB) disk and not
# currently mounted.)  If required, specify this option in above source
# definitions file.  Multiple paths can be specified separated by spaces.
#
# If one or more of the listed destination paths cannot be mounted, then
# sources which use that destination path will be aborted (Avoiding filling
# up the local disk with the backup instead.)
#
MOUNT_DEST=""

# Hours to try to avoid starting a new transfer (For non-local sources only.)
# This helps avoid backups taking place during working hours, which may slow
# down the Internet and/or WAN connectivity for users.
#
AVOID_BACKUP_HOURS="7 8 9 10 11 12 13 14 15 16 17 18"

# Number of logs from previous backups to keep (In log directory)
KEEP_LOGS=7

# Show summary at end?
SHOW_SUMMARY=1

# Show errors at end?
SHOW_ERRORS=1

# Send summary via E-mail?
EMAIL_SUMMARY=1

# Send errors by E-mail?
EMAIL_ERRORS=1

# Enable incremental backups using hardlinks?  This creates dated directories
# containing a complete snapshot of the backup source (Making restore/browsing
# considerably easier.)  Disk space is conserved by creating hardlinks to
# unchanged files.
#
INCREMENTAL_BACKUPS=1

# Number of daily incremental backups to keep
#
INCREMENTAL_DAILY=7

# Number of weekly incremental backups to keep
#
INCREMENTAL_WEEKLY=5

# Number of monthly incremental backups to keep
#
INCREMENTAL_MONTHLY=12

# Compress previous backup archive directories to save space?
# (NOTE:  Incremental backups using hardlinks cannot be compressed.)
#
COMPRESS_BACKUPS=1

# For previous archive directories, use zip instead of gzip?
#
COMPRESS_BACKUPS_ZIP=0

# Use bzip2?  This will give better compression than gzip, but at the
# expense of more CPU time (Please ensure that this is available.)
#
COMPRESS_BACKUPS_BZIP2=0

# Default rsync options
RSYNC_OPTIONS_REMOTE="-rlptgoDvz --delete --delete-excluded --hard-links --numeric-ids --stats"
RSYNC_OPTIONS_LOCAL="-rlptgoDv --delete --delete-excluded --hard-links --numeric-ids --stats"
RSYNC_OPTIONS_DEST2="-rlptgoDv --delete --hard-links --numeric-ids"

# If rsync fails, retry the transfer upto the specified number of times.
#
# rsync can sometimes fail for large transfers with 'connection unexpectedly
# closed' (Even for a local transfer across a LAN.)
#
RSYNC_RETRIES=3

# Minimum delay time between each retry, in seconds
#
RSYNC_RETRY_DELAY="300"

# Rsync timeout due to inactivity in seconds (0 = Use rsync default.)
#
RSYNC_TIMEOUT=0

# Apply failover by default to sources with multiple hosts defined,
# instead of random selection?
#
FAILOVER_DEFAULT=0

# Create destination directory structure by default?
#
DEST_CREATE=1

# Select the first random path at random?
#
DEST_ALTERNATE_RANDOM=1

# If multiple destination paths are defined, alternate the path for each
# source, based on its number in addition to the date?
#
DEST_ALTERNATE_BY_SOURCE=1

# Default number of previous archives to keep (0 = No limit.)
# (Not applicable to incremental backups.)
#
KEEP_PREVIOUS=0

# When disk utilisation on a given backup volume exceeds the percentage
# value below, previous backup archives (Oldest first) will be deleted
# to free up disk space for the pending backup.
#
PURGE_UTILISATION=90

# Minimum number of previous backup archives to keep when purging
#
PURGE_MINIMUM=2

# Search all source destination directories for previous backups and purge
# the oldest?  If not, the oldest backups in the current source will be
# purged only.
#
PURGE_ALL_SOURCES=1

# Maximum time to wait for purge of source by another instance
#
PURGE_TIMEOUT=`expr 60 \* 60 `

# Purge previous archives (If necessary) while waiting for correct backup
# start time?  This helps prevent the backup from being delayed while waiting
# for old files to be deleted.
#
PURGE_WHILE_WAITING=1

# Temporary file for purge list
#
TMP_PURGE_LIST="/tmp/.mbackup.purge.list"

# Tempory log file for purge while waiting for backup to start
#
TMP_PURGE_LOG="/tmp/.mbackup.purge.log"

# Default E-mail subject
EMAIL_SUBJECT="Backup report for \$SOURCE (\$ERRORS \$ERRORS_TITLE, \$WARNINGS \$WARNINGS_TITLE) on \$START_TIME..."

# Default E-mail sender
EMAIL_FROM="backup@localhost"

# Date format for logs
LOG_DATE="%d/%m/%Y %H:%M:%S"

# Temporary file for E-mail message
TMP_EMAIL="/tmp/.backup.report"

# Name prefix for lock files and temporary files
#
TMP_NAME="/tmp/.mbackuprsync"

# To prevent multiple instances of backing up the same source, a lock file
# is created while each source is running.  This is updated regularly
# during the backup.  The lock will be considered expired if it has
# not been updated for the time period specified below (In hours.)
#
LOCK_TIME=2


# Send message via E-mail
#
# USAGE:  SendMessage "Message" <ERROR>
#
function SendMessage {
   if [ "$2" = "ERROR" ]; then
      if [ "$ERRORS" = "" ]; then ERRORS=0; fi
      ERRORS=`expr $ERRORS + 1`
      if [ $ERRORS -eq 1 ]; then ERRORS_TITLE="error"; else ERRORS_TITLE="errors"; fi
   elif [ "$2" = "WARNING" ]; then
      if [ "$WARNINGS" = "" ]; then WARNINGS=0; fi
      WARNINGS=`expr $WARNINGS + 1`
      if [ $WARNINGS -eq 1 ]; then WARNINGS_TITLE="warning"; else WARNINGS_TITLE="warnings"; fi
   fi

   eval MAIL_SUBJECT=\"$EMAIL_SUBJECT\"
   echo "$1" |mail -a "From: <$EMAIL_FROM>" -a "To: <$EMAIL_RCPT>" -a "Reply-to: <$EMAIL_FROM>" -s "$MAIL_SUBJECT" $EMAIL_RCPT
}


# Log message to log file $LOGFILE
#
# USAGE: LogMessage "Message" [<ISERROR> <TARGET> <UPDSTAT> <TIMEDATE> <ADDSOURCE> <OPTIONS>]
#
#   <ISERROR>:  GENERAL    = General message
#		ERROR      = Error message
#               WARNING    = Warning message
#    <TARGET>:  LOG        = Send message to log file only
#               STATUS     = Update status only
#               EMAIL      = Send message via E-mail only
#		BOTH       = Send message to log file and via E-mail
#   <UPDSTAT>:  STATUS     = Update status file
#               NOSTATUS   = Do not update
#  <TIMEDATE>:  TIMEDATE   = Add time/date stamp to log file
#               NOTIMEDATE = Do not add time/date stamp
# <ADDSOURCE>:  SOURCE     = Add name of source to console/E-mail message
#               NOSOURCE   = Do not add name of source
#   <NEWLINE>:  NL         = Add trailing newline
#               NLNL       = Add leading and trailing newline
#    <ESCAPE>:  E          = Interpret escape codes
#
function LogMessage {

   LM_MESSAGE="$1"
   LM_ISERROR="$2"
   LM_TARGET="$3"
   LM_UPDSTAT="$4"
   LM_TIMEDATE="$5"
   LM_ADDSOURCE="$6"
   LM_NEWLINE="$7"

   if [ "$8" = "E" ]; then
      LM_ESCAPE="-e"
   else
      LM_ESCAPE=""
   fi


   if [ "$LM_TARGET" != "STATUS" ] && [ "$LM_TARGET" != "EMAIL" ]; then
      if [ "$LM_NEWLINE" != "" ]; then
         if [ "$LM_NEWLINE" = "NL" ]; then
            LM_MESSAGE="$LM_MESSAGE\n"
         elif [ "$LM_NEWLINE" = "NLNL" ]; then
            LM_MESSAGE="\n$LM_MESSAGE\n"
         fi
      fi

      # Log to console
      if [ "$LM_ISERROR" = "ERROR" ] || [ "$LM_ISERROR" = "WARNING" ] || [ $ERRORS_ONLY -eq 0 ]; then
         if [ "$LM_ADDSOURCE" != "NOSOURCE" ] && [ "$SOURCE" != "" ]; then
            if [ "$LM_TIMEDATE" != "NOTIMEDATE" ]; then
               echo $LM_ESCAPE "`date +"%Y-%m-%d %H:%M:%S"`: $SOURCE: $LM_MESSAGE"
            else
               echo $LM_ESCAPE "$SOURCE: $LM_MESSAGE"
            fi
         else
            if [ "$LM_TIMEDATE" != "NOTIMEDATE" ]; then
         
               echo $LM_ESCAPE "`date +"%Y-%m-%d %H:%M:%S"`: $LM_MESSAGE"
            else
               echo $LM_ESCAPE "$LM_MESSAGE"
            fi
         fi
      fi


      # Log to log file
      if [ "$LOGFILE" != "" ]; then
         if [ "$LM_TIMEDATE" != "NOTIMEDATE" ]; then
            echo $LM_ESCAPE "`date +"%Y-%m-%d %H:%M:%S"`: $LM_MESSAGE" >> $LOGFILE
         else 
            echo $LM_ESCAPE "$LM_MESSAGE" >> $LOGFILE
         fi
      fi


      LM_MESSAGE="$1"
   fi


   # Update status
   if [ "$LM_UPDSTAT" != "NOSTATUS" ] && [ "$TMP_NAME" != "" ] && [ "$SOURCE" != "" ]; then
      echo $LM_ESCAPE "`date +"%Y-%m-%d %H:%M:%S"`: $LM_MESSAGE" > $TMP_NAME.$SOURCE.status
   fi


   # Send by E-mail
   if [ "$LM_TARGET" = "EMAIL" ] || [ "$LM_TARGET" == "BOTH" ]; then
      SendMessage "$LM_MESSAGE" $LM_ISERROR
   fi
}


# Log message with trailing newline
#
function LogMessageNL {
   LogMessage "$1" $2 $3 $4 $5 $6 NL
}


# Log message with trailing newline and escape codes interpreted
#
function LogMessageNLE {
   LogMessage "$1" $2 $3 $4 $5 $6 NL E
}


# Log message with leading and trailing newline
#
function NLLogMessageNL {
   LogMessage "$1" $2 $3 $4 $5 $6 NLNL
}


# Remove old log files (Assumed 1 file = 1 day)
#
# USAGE:  RemoveOldLogs <PATH> <SUFFIX> <KEEP>
#
function RemoveOldLogs {
   if [ "$1" != "" ] && [ "$2" != "" ] && [ "$3" != "" ] && [ $3 -gt 1 ]; then
      if [ -e "$1" ] && [ -d "$1" ]; then
         pushd $1 2>&1 >/dev/null
         FILES="`ls -r *$2 2>&1 |grep -v \"No such file or directory\"`"
         FILE_COUNT=1
         for FILE in $FILES; do
             if [ $FILE_COUNT -ge $3 ]; then
                if [ -e "$FILE" ]; then
		   LogMessage "Removing old log file $FILE..."
	           rm $1/$FILE >> $LOGFILE 2>&1
	        fi
	     fi
   	     FILE_COUNT=`expr $FILE_COUNT + 1`
          done
          popd 2>&1 >/dev/null
      fi
   fi
}


# Rotate incremental backups
#
# USAGE:  RotateBackups <PATH> <DAILY> <WEEKLY> <MONTHLY>
#
function RotateBackups {
   ROTATE_PATH="$1"
   ROTATE_DAILY="$2"
   ROTATE_WEEKLY="$3"
   ROTATE_MONTHLY="$4"

   # Incremental backup directory exists?
   if [ ! -e "$ROTATE_PATH" ] || [ ! -d "$ROTATE_PATH" ]; then
      return
   fi

   # Ensure current backup contains files (Otherwise do not rotate backups)
   if [ "`ls -d -r $1/m0-w0-d1/* 2>&1 |grep -v \"No such file or directory\"`" = "" ]; then
      return
   fi
   
   # Rotate backups
   for ROTATE_TYPE in m w d; do
       case $ROTATE_TYPE in
            d)
	       ROTATE_INTERVAL=`expr 60 \* 60 \* 12`
	       ROTATE_INTFILE="$ROTATE_PATH/.last.daily"
	       ROTATE_COUNT="$ROTATE_DAILY"
	       ROTATE_PREFIX="m0-w0-d"
	       ROTATE_SUFFIX=""
	       ROTATE_ROLL_COUNT=0
	       ROTATE_ROLL_PREFIX=""
	       ROTATE_ROLL_SUFFIX=""
	       ;;
	    w)
	       ROTATE_INTERVAL=`expr 60 \* 60 \* 24 \* 7`
	       ROTATE_INTFILE="$ROTATE_PATH/.last.weekly"
	       ROTATE_COUNT="$ROTATE_WEEKLY"
	       ROTATE_PREFIX="m0-w"
	       ROTATE_SUFFIX="-d0"
	       ROTATE_ROLL_COUNT="$ROTATE_DAILY"
	       ROTATE_ROLL_PREFIX="m0-w0-d"
	       ROTATE_ROLL_SUFFIX=""
	       ;;
	    m)
	       ROTATE_INTERVAL=`expr 60 \* 60 \* 24 \* 30`
	       ROTATE_INTFILE="$ROTATE_PATH/.last.monthly"
	       ROTATE_COUNT="$ROTATE_MONTHLY"
	       ROTATE_PREFIX="m"
	       ROTATE_SUFFIX="-w0-d0"
	       ROTATE_ROLL_COUNT="$ROTATE_WEEKLY"
	       ROTATE_ROLL_PREFIX="m0-w"
	       ROTATE_ROLL_SUFFIX="-d0"
	       ;;
	    *)
	       ROTATE_COUNT=0
	       ROTATE_ROLL_COUNT=0
       esac
       
       if [ $ROTATE_COUNT -gt 0 ]; then

          # Check rotate whether interval has passed
	  if [ -e "$ROTATE_INTFILE" ]; then
	     ROTATE_LASTINT="`cat $ROTATE_INTFILE`"
	  else
	     ROTATE_LASTINT=""
	  fi
	  if [ "$ROTATE_LASTINT" = "" ] || [ "$ROTATE_LASTINT" = "0" ]; then
	     if [ "$ROTATE_TYPE" = "d" ]; then
	        ROTATE_LASTINT=0
	     else
	        ROTATE_LASTINT="`date +%s`"
	        echo "$ROTATE_LASTINT" > $ROTATE_INTFILE
             fi
	  fi
	  
	  if [ `date +%s` -ge `expr $ROTATE_LASTINT + $ROTATE_INTERVAL` ]; then
	     echo "`date +%s`" > $ROTATE_INTFILE
	     
	     # Rotate existing backups, removing oldest
	     ROTATE_COUNTER=`expr $ROTATE_COUNT + 1`
	     while [ $ROTATE_COUNTER -gt 0 ]; do
	        if [ -e "$ROTATE_PATH/${ROTATE_PREFIX}${ROTATE_COUNTER}${ROTATE_SUFFIX}" ]; then
		   if [ $ROTATE_COUNTER -ge $ROTATE_COUNT ]; then
	              rm -rf "$ROTATE_PATH/${ROTATE_PREFIX}${ROTATE_COUNTER}${ROTATE_SUFFIX}" >>$LOGERRORS 2>&1
		   else
	              mv $ROTATE_PATH/${ROTATE_PREFIX}${ROTATE_COUNTER}${ROTATE_SUFFIX} $ROTATE_PATH/${ROTATE_PREFIX}`expr $ROTATE_COUNTER + 1`${ROTATE_SUFFIX} >>$LOGERRORS 2>&1
		      if [ "${ROTATE_PREFIX}${ROTATE_COUNTER}${ROTATE_SUFFIX}" = "m0-w0-d1" ] && [ -e "$ROTATE_PATH/${ROTATE_PREFIX}`expr $ROTATE_COUNTER + 1`${ROTATE_SUFFIX}" ] && [ ! -e "$ROTATE_PATH/${ROTATE_PREFIX}${ROTATE_COUNTER}${ROTATE_SUFFIX}" ]; then
		         ln -sf $ROTATE_PATH/${ROTATE_PREFIX}`expr $ROTATE_COUNTER + 1`${ROTATE_SUFFIX} $ROTATE_PATH/current
		      fi
		   fi
		fi
		ROTATE_COUNTER=`expr $ROTATE_COUNTER - 1`
	     done

	     # Rollover oldest backup from next group
	     ROTATE_COUNTER=$ROTATE_ROLL_COUNT
	     ROTATE_FOUND=0
	     while [ $ROTATE_COUNTER -gt 0 ] && [ $ROTATE_FOUND -eq 0 ]; do
	        if [ -e "$ROTATE_PATH/${ROTATE_ROLL_PREFIX}${ROTATE_COUNTER}${ROTATE_ROLL_SUFFIX}" ]; then
	           cp -al $ROTATE_PATH/${ROTATE_ROLL_PREFIX}${ROTATE_COUNTER}${ROTATE_ROLL_SUFFIX} $ROTATE_PATH/${ROTATE_PREFIX}1${ROTATE_SUFFIX} >>$LOGERRORS 2>&1
		   ROTATE_FOUND=1
		fi
		ROTATE_COUNTER=`expr $ROTATE_COUNTER - 1`
	     done
	  fi
       fi
   done
}


# Wait for purge of source directory by another instance to complete
function WaitPurge {
   PURGE_WAITED=0
   if [ -e "$1" ]; then
      LogMessage "Purge: $2 is being purged by another instance. Waiting for purge to complete..."
      PURGE_WAITED=1
      WAIT_PURGE=1
      while [ $WAIT_PURGE -eq 1 ]; do
            sleep 1m
	    if [ ! -e "$1" ]; then
	       WAIT_PURGE=0
	    else
	       PURGE_TIME="`cat $1`"
	       if [ "$PURGE_TIME" = "" ] || [ `date +%s` -gt `expr $PURGE_TIME + $PURGE_TIMEOUT` ]; then
		  LogMessage "Purge: Timeout waiting for purge of $2 by another instance." ERROR
      		  PURGE_WAITED=0
	          WAIT_PURGE=0
	       fi
	    fi
      done
   fi
}


# Free up space by deleting previous backup archives (Oldest first) until
# disk space utilisation is less than specified utilisation
#
# USAGE:  PurgeBackups <PATH> <UTILISATION> <KEEP>
#
function PurgeBackups {

   PURGED=0
   PURGE_START_TIME="`date +%s`"

   # Temporary purge file list
   TMP_PURGE_LIST_FILE="$TMP_PURGE_LIST.`date +%s`"

   # Keep only specified number of previous backup archives
   # (Not applicable to incremental backups)
   #
   if [ "$3" != "" ] && [ $3 -gt 1 ]; then
      NOTIFIED=0
      BACKUPS="`ls -d -r $1/*-*-* 2>&1 |grep -v \"No such file or directory\"`"
      BACKUP_COUNT=1
      for DIR in $BACKUPS; do
          if [ $BACKUP_COUNT -ge $3 ]; then
	     CORRECT_PATH="`echo \"$DIR\" |grep -E \"^$1\"`"

             if [ -e "$DIR" ] && [ "$CORRECT_PATH" != "" ]; then
	        if [ $NOTIFIED -eq 0 ]; then
	 	   LogMessage "Purge: Old archive count is greater than $3, purging old archives..."
		   NOTIFIED=1
		fi

	 	LogMessage "Purge: Purging old archive $DIR..."
		DIR_TMP_PURGE="`echo "$DIR" |sed -e "s~/~-~g"`"
		WaitPurge "$TMP_NAME.$DIR_TMP_PURGE.purge" "$DIR"

 		if [ "$PURGE_WAITED" != "1" ] && [ -e "$DIR" ]; then
	 	   LogMessage "Purge: Purging old archive $DIR..."

		   echo "`date +%s`" > $TMP_NAME.$DIR_TMP_PURGE.purge
	           rm -r "$DIR" >/dev/null 2>&1
		   rm "$TMP_NAME.$DIR_TMP_PURGE.purge" >/dev/null 2>&1
		   PURGED=1	
		
                   # Update lock file
		   if [ -e "$TMP_NAME.$SOURCE.lock" ]; then
		      echo "`date +%s`" > $TMP_NAME.$SOURCE.lock
		   fi
                fi
	     fi
	  fi
          BACKUP_COUNT=`expr $BACKUP_COUNT + 1`
      done
   fi

   # Update lock file
   if [ -e "$TMP_NAME.$SOURCE.lock" ]; then
      echo "`date +%s`" > $TMP_NAME.$SOURCE.lock
   fi
  

   # Disk utilisation exceeded?  Purge previous backup
   # archives until this is below specified utilisation.
   #
   CONTINUE_PURGE=1
   PURGE_COUNT=0
   while [ $CONTINUE_PURGE -eq 1 ]; do
      PURGE_COUNT=`expr $PURGE_COUNT + 1`
      
      CONTINUE_PURGE=0
      SOURCES_PURGED=0
      BACKUPS=""

      # Purge oldest previous backup on over-utilised volume
      if [ $PURGE_ALL_SOURCES -eq 1 ]; then
	    
         # Produce list of oldest previous backups for all sources
         PURGE_DIR_LIST="$PURGE_OLD_DIRS"
         for PURGE_SRC in $ORIGINAL_SOURCES; do
             eval PURGE_SRC_DIR="\$DEST_$PURGE_SRC"
             PURGE_SRC_DIR_LIST="`echo \"$PURGE_SRC_DIR\" |sed -e \"s/;/ /g\"`"

             for PDIR in $PURGE_SRC_DIR_LIST; do

                 # Ensure that previous backup is on drive that is over utilised
                 if [ -e "$PDIR" ]; then
                    if [ -d "$PDIR" ]; then
                       UTILISATION_PDIR="`df $PDIR |grep -v "Filesystem" |sed -e "s/[[:space:]]\+/ /g" -e "s/%//g" |cut -d' ' -f5`"

                       if [ $UTILISATION_PDIR -gt $2 ]; then
                          PURGE_DIR_LIST="$PURGE_DIR_LIST $PDIR"
                       fi
                    fi
                 fi
             done
         done
         PURGE_DIR_LIST="$PURGE_DIR_LIST $MOUNT_DEST"
      else

         # Produce list of backups for current source only
         eval CHECK_DEST_SOURCE="\$DEST_$SOURCE"
         CHECK_DEST_SOURCE_LIST="`echo \"$CHECK_DEST_SOURCE\" |sed -e \"s/;/ /g\"`"
         
         for PURGE_SRC in $CHECK_DEST_SOURCE_LSIT; do
             eval PURGE_SRC_DIR="\$DEST_$PURGE_SRC"
             PURGE_SRC_DIR_LIST="`echo \"$PURGE_SRC_DIR\" |sed -e \"s/;/ /g\"`"

             for PDIR in $PURGE_SRC_DIR_LIST; do

                 # Ensure that previous backup is on drive that is over utilised
                 if [ -e "$PDIR" ]; then
                    if [ -d "$PDIR" ]; then
                       UTILISATION_PDIR="`df $PDIR |grep -v "Filesystem" |sed -e "s/[[:space:]]\+/ /g" -e "s/%//g" |cut -d' ' -f5`"

                       if [ $UTILISATION_PDIR -gt $2 ]; then
                          PURGE_DIR_LIST="$PURGE_DIR_LIST $PDIR"
                       fi
                    fi
                 fi
             done
         done
      fi

      # Find directories to purge
      echo -n "" > $TMP_PURGE_LIST_FILE
      for DIR in $PURGE_DIR_LIST; do
          if [ -e "$DIR" ] && [ -d "$DIR" ]; then
             find $DIR -iregex ^.*/incremental/m[0-9]+-w[0-9]+-d[0-9]+\$ -prune -printf "%f #-DIR-# %p\n" -o -iregex ^.*/previous/[0-9]+-[0-9]+-[0-9]+.*\$ -prune -printf "%f #-DIR-# %p\n" |grep -v "/incremental/m0-w0-d1" >> $TMP_PURGE_LIST_FILE
          fi
      done
	       
      BACKUPS="`cat $TMP_PURGE_LIST_FILE |sort -r -u |sed -e \"s/^.\+#-DIR-#[[:space:]]\+//g\"`"
      BACKUPS_COUNT="`echo \"$BACKUPS\" |wc -l`"
      if [ $BACKUPS_COUNT -eq 1 ]; then BACKUPS_COUNT=2; fi

      if [ "$BACKUPS" = "" ]; then

         # Purge oldest previous backup of current source if directory list is empty
         BACKUPS="`ls $CHECK_DEST/previous/*-*-* 2>&1 |grep -v \"No such file or directory\" ; ls -r -d $CHECK_DEST/incremental/*-*-* 2>&1 |grep -v \"No such file or directory\"`"
         BACKUPS_COUNT="`echo \"$BACKUPS\" |wc -l`"
      fi

      if [ "$BACKUPS_COUNT" != "" ] && [ $BACKUPS_COUNT -gt 1 ] && [ $BACKUPS_COUNT -gt $PURGE_MINIMUM ]; then
         PURGE_COUNT=0
         for DIR in $BACKUPS; do
             if [ -e "$DIR" ]; then
                if [ $PURGE_COUNT -lt 1 ]; then

                   UTILISATION_PDIR=0
                   if [ -e "$DIR" ]; then
                       if [ -d "$DIR" ]; then
                          UTILISATION_PDIR="`df $DIR |grep -v "Filesystem" |sed -e "s/[[:space:]]\+/ /g" -e "s/%//g" |cut -d' ' -f5`"
                       fi
                   fi

                   if [ $UTILISATION_PDIR -gt $2 ]; then
	 	      LogMessage "Purge: Purging $DIR (${UTILISATION_PDIR}%)..."
  	              DIR_TMP_PURGE="`echo "$DIR" |sed -e "s~/~-~g"`"
	  	      WaitPurge "$TMP_NAME.$DIR_TMP_PURGE.purge" "$DIR"
			 
		      if [ "$PURGE_WAITED" != "1" ] && [ -e "$DIR" ]; then
	 	         LogMessage "Purge: Purging $DIR (${UTILISATION_PDIR}%)..."

		         echo "`date +%s`" > $TMP_NAME.$DIR_TMP_PURGE.purge
    	                 rm -r "$DIR" >/dev/null 2>&1
		         rm "$TMP_NAME.$DIR_TMP_PURGE.purge" >/dev/null 2>&1
	                 PURGE_COUNT=`expr $PURGE_COUNT + 1`
		         PURGED=1
			  
                         # Update lock file
                         if [ -e "$TMP_NAME.$SOURCE.lock" ]; then
                            echo "`date +%s`" > $TMP_NAME.$SOURCE.lock
		         fi
                      fi
                   fi
	        fi
	     fi
         done
		
	 if [ $PURGE_COUNT -gt 0 ]; then
	    SOURCES_PURGED=`expr $SOURCES_PURGED + 1`
	 fi
      fi
	    
      if [ $SOURCES_PURGED -gt 0 ]; then
         CONTINUE_PURGE=1
      fi
   done
   if [ -e "$TMP_PURGE_LIST_FILE" ]; then rm $TMP_PURGE_LIST_FILE; fi
	       
   # Update lock file
   if [ -e "$TMP_NAME.$SOURCE.lock" ]; then
      echo "`date +%s`" > $TMP_NAME.$SOURCE.lock
   fi
   
   # Purge time
   if [ $PURGED = "1" ]; then
      PURGE_ELAPSED=`expr \`date +%s\` - $PURGE_START_TIME`
      echo -e "Purge: Purging took `expr $PURGE_ELAPSED / \( 60 \* 60 \* 24 \)`d `expr \( $PURGE_ELAPSED % \( 60 \* 60 \* 24 \) \) / \( 60 \* 60 \)`h `expr \( $PURGE_ELAPSED % \( 60 \* 60 \) \) / 60`m `expr $PURGE_ELAPSED % 60`s\n" >> $LOGFILE
   fi
}


# Work out percentage (Val1 / Val2 * 100)
#
# USAGE:  percent <Val1> <Val2>
#
# Result returned in $? and $PercentVal
#
function percentage {
   PercentVal=0
   if [ "$1" != "" ] && [ "$2" != "" ] && [ "$1" != "N/A" ] && [ "$2" != "N/A" ] && [ $2 -ne 0 ]; then
      if [ $1 -lt 0 ]; then PercentVal1=`expr 0 - $1`; else PercentVal1=$1; fi
      if [ $2 -lt 0 ]; then PercentVal2=`expr 0 - $2`; else PercentVal2=$2; fi
      PercentVal=`expr \( \( $PercentVal1 \* 100 \) / $PercentVal2 \)`
   fi
   return $PercentVal
}
 

# Create directories under specified location.
#
# USAGE:  CreateDirs <PATH> <DIR>[/<DIR>][...]
#
function CreateDirs {
   if [ "$1" != "" ] && [ "$2" != "" ]; then
      CREATE_DIRLIST="`echo \"$1/$2\" |sed \"s/[[:space:]]\+/_.-/g\" |sed -e \"s~/~ ~g\"`"
      CREATE_PATH=""

      for CREATE_DIR in $CREATE_DIRLIST; do
          CREATE_DIR="`echo \"$CREATE_DIR\" |sed -e \"s/_\.-/ /g\"`"
	  if [ ! -e "$CREATE_PATH/$CREATE_DIR" ]; then
	     mkdir "$CREATE_PATH/$CREATE_DIR" >>$LOGFILE 2>&1
	  fi
	  CREATE_PATH="$CREATE_PATH/$CREATE_DIR"
      done
   fi
}


# Wait for specified time
#
function Delay {
   if [ "$1" != "" ] && [ $1 -gt 0 ]; then
      if [ "$LAST_TIME" = "" ]; then LAST_TIME=`expr \`date +%s\` - $1`; fi

      TIME_NOW="`date +%s`"
      TIME_INTERVAL=`expr $TIME_NOW - $LAST_TIME`

      if [ $TIME_INTERVAL -gt $1 ]; then
         TIME_DELAY=$1
      else
         TIME_DELAY=`expr $1 - $TIME_INTERVAL`
      fi
     
      if [ $TIME_DELAY -gt 0 ]; then
	 LogMessage "Delay: Waiting for $TIME_DELAY second(s)..."
         sleep ${TIME_DELAY}s
      fi
      LAST_TIME="`date +%s`"
   fi
}


# Execute pre/post command(s)
#
# USAGE:  PrePostExec <SOURCE PATH> "command [; command][; ...]"
#
function PrePostExec {
   if [ "$1" != "" ] && [ "$2" != "" ]; then
      BACKUP_SOURCE="$1"
      BACKUP_CMD="$2"
   
      BACKUP_HOST="`echo \"$BACKUP_SOURCE\" |sed -e \"s/^\(.\+\):.*\$/\1/g\"`"

      if [ "$BACKUP_HOST" != "" ] && [ "$BACKUP_HOST" != "$BACKUP_SOURCE" ]; then
	 Delay $SOURCE_DELAY
         ssh $RndItem $BACKUP_CMD
      else
         $BACKUP_CMD
      fi
   fi
}


# Pick random value from list.
#
# USAGE:  RandomItem "item1 item2 [...]" "current_item"
#
# Result returned $RndItem
#
function RandomItem {
   RndItem=""
   if [ "$1" != "" ]; then
      ItemCnt=0
      ItemLoop=0
      ItemLast="$2"
      
      for Itm in $1; do ItemCnt=`expr $ItemCnt + 1`; done
      
      if [ "$ItemCnt" != "" ] && [ $ItemCnt -gt 1 ]; then
         while [ "$RndItem" = "" ] && [ $ItemLoop -le 100 ]; do
	    ItemNo=`expr $RANDOM % $ItemCnt`
	    ItemPos=0
	    for Itm in $1; do
	        if [ $ItemPos -le $ItemNo ] && [ "$Itm" != "$2" ]; then
		   RndItem="$Itm"
		fi
		ItemPos=`expr $ItemPos + 1`
            done
	    ItemLoop=`expr $ItemLoop + 1`
	 done
      else
         RndItem="$1"
      fi
   fi
}


# Return source path to host picked at random.
#
# USAGE:  RandomSourcePath "<SOURCEPATH>" <UNIQUE>
#
# UNIQUE:  1 = Ensure new random host is different from last picked host.
#          0 = Pick any host at random.
#
# Result returned $RANDOMPATH
#
function RandomSourcePath {

    SOURCEPATH_HOST="`echo \"$1\" |sed -e \"s/^\([^:]\+\):\(.*\)\$/\1/g\"`"
    SOURCEPATH_PATH="`echo \"$1\" |sed -e \"s/^\([^:]\+\):\(.*\)\$/\2/g\"`"
    

    # Choose host at random
    RANDOMPATH="$1"
    if [ "$SOURCEPATH_HOST" != "" ] && [ "$SOURCEPATH_HOST" != "$SOURCEPATH" ] && [ "$SOURCEPATH_PATH" != "$SOURCEPATH" ]; then
       SOURCEPATH_HOST="`echo \"$SOURCEPATH_HOST\" |sed -e \"s/;/ /g\"`"
       
       if [ "$2" = "1" ]; then
          RandomItem "$SOURCEPATH_HOST" "$LAST_HOST"
       else
          RandomItem "$SOURCEPATH_HOST" ""
       fi

       if [ "$RndItem" != "" ]; then
          LAST_HOST="$RndItem"

          # Choose path at random
          SOURCEPATH_PATHS="`echo \"$SOURCEPATH_PATH\" |sed -e \"s/:/ /g\"`"
          if [ "$SOURCEPATH_PATHS" != "$SOURCEPATH_PATH" ]; then
             if [ "$2" = "1" ]; then
                RandomItem "$SOURCEPATH_PATHS" "$LAST_PATH"
             else
                RandomItem "$SOURCEPATH_PATHS" ""
             fi
             LAST_PATH="$RndItem"
          
             if [ "$RndItem" != "" ]; then
                RANDOMPATH="$LAST_HOST:$RndItem"
             else
                RANDOMPATH="$LAST_HOST:$SOURCEPATH_PATH"
             fi
          else
             RANDOMPATH="$LAST_HOST:$SOURCEPATH_PATH"
          fi
       else
          RANDOMPATH=""
       fi
    fi
}


# Return source path to host picked using failover.
#
# USAGE:  FailoverSourcePath "<SOURCEPATH>"
#
# Result returned $RANDOMPATH
#
function FailoverSourcePath {

    SOURCEPATH_HOST="`echo \"$1\" |sed -e \"s/^\([^:]\+\):\(.*\)\$/\1/g\"`"
    SOURCEPATH_PATH="`echo \"$1\" |sed -e \"s/^\([^:]\+\):\(.*\)\$/\2/g\"`"
   

    # Choose path
    SOURCEPATH_PATHS="`echo \"$SOURCEPATH_PATH\" |sed -e \"s/:/ /g\"`"
    if [ "$SOURCEPATH_PATHS" != "$SOURCEPATH_PATH" ]; then
       if [ "$LAST_PATH" != "" ]; then
          PathFound=0
          FIRST_PATH=""
	  for Itm in $SOURCEPATH_PATHS; do
              if [ "$FIRST_PATH" = "" ]; then
		 FIRST_PATH="$Itm"
              fi

              if [ $PathFound -eq 0 ]; then
                 if [ "$Itm" != "$LAST_PATH" ]; then
	            SOURCEPATH_PATH_PATH="$Itm"
		    LAST_PATH="$Itm"
                    PathFound=1
                 fi
              fi
          done
           
          if [ $PathFound -eq 0 ]; then
             if [ "$FIRST_PATH" != "" ]; then
	         SOURCEPATH_PATH_PATH="$FIRST_PATH"
		 LAST_PATH="$FIRST_PATH"
             else
	         SOURCEPATH_PATH_PATH="$Itm"
		 LAST_PATH="$Itm"
	     fi
          fi 
       else
          SOURCEPATH_PATH_PATH=""
	  for Itm in $SOURCEPATH_PATHS; do
	      if [ "$SOURCEPATH_PATH_PATH" = "" ]; then
	         SOURCEPATH_PATH_PATH="$Itm"
		 LAST_PATH="$Itm"
	      fi
          done
       fi
    else
       SOURCEPATH_PATH_PATH="$SOURCEPATH_PATH"
    fi


    # Choose host
    RANDOMPATH="$1"
    if [ "$SOURCEPATH_HOST" != "" ] && [ "$SOURCEPATH_HOST" != "$SOURCEPATH" ] && [ "$SOURCEPATH_PATH" != "$SOURCEPATH" ]; then
       SOURCEPATH_HOST="`echo \"$SOURCEPATH_HOST\" |sed -e \"s/;/ /g\"`"

       if [ "$LAST_HOST" != "" ]; then
          HostFound=0
          FIRST_HOST=""
	  for Itm in $SOURCEPATH_HOST; do
              if [ "$FIRST_HOST" = "" ]; then
		 FIRST_HOST="$Itm"
              fi

              if [ $HostFound -eq 0 ]; then
                 if [ "$Itm" != "$LAST_HOST" ]; then
	            RANDOMPATH="$Itm:$SOURCEPATH_PATH_PATH"
		    LAST_HOST="$Itm"
                    HostFound=1
                 fi
              fi
          done
           
          if [ $HostFound -eq 0 ]; then
             if [ "$FIRST_HOST" != "" ]; then
	         RANDOMPATH="$FIRST_HOST:$SOURCEPATH_PATH_PATH"
		 LAST_HOST="$FIRST_HOST"
             else
	         RANDOMPATH="$Itm:$SOURCEPATH_PATH_PATH"
		 LAST_HOST="$Itm"
	     fi
          fi 
       else
          RANDOMPATH=""
	  for Itm in $SOURCEPATH_HOST; do
	      if [ "$RANDOMPATH" = "" ]; then
	         RANDOMPATH="$Itm:$SOURCEPATH_PATH_PATH"
		 LAST_HOST="$Itm"
	      fi
          done
       fi
    fi
}


# Choose destination path from list of alternatives.
#
# Parameter:  1 = Chosen destination folder must exist.
#
# Result returned $DEST
#
function AlternativeDestPath {
    DEST_PATHS="$DEST"
    FIRST_DEST=""
    if [ "$DEST" != "" ]; then
       DEST_LIST="`echo \"$DEST\" |sed -e \"s/;/ /g\"`"

       # Count number of defined destination paths
       ItmCnt=0
       for Itm in $DEST_LIST; do ItmCnt=`expr $ItmCnt + 1`; done
       ItemCount=$ItmCnt

       if [ $ItemCount -gt 1 ]; then

          # Choose next path based on last saved number
          if [ "$SOURCE" != "" ] && [ -e "/tmp/.mbackuprsync.$SOURCE.next" ]; then
             ItmNo="`cat /tmp/.mbackuprsync.$SOURCE.next`"
             if [ "$ItmNo" != "" ] && [ $ItmNo -gt 0 ]; then
                ItmNo=$[ $ItmNo % $ItemCount ]
             else
                ItmNo=""
             fi
          else
             ItmNo=""
          fi

          # Choose next path based on current date and source number
          if [ "$ItmNo" = "" ]; then
             ItmNo=$[ `date +%s` / 60 / 60 / 24 ]
             if [ "$DEST_ALTERNATE_RANDOM" = "1" ] && [ "$DEST_ALTERNATE_RANDOMISED" = "" ]; then
                ItmNo=$[ $ItmNo + $RANDOM ]
	        DEST_ALTERNATE_RANDOMISED=1
             fi
             if [ "$SOURCE_NUMBER" != "" ] && [ "$DEST_ALTERNATE_BY_SOURCE" = "1" ]; then
                ItmNo=$[ $ItmNo + $SOURCE_NUMBER ]
             fi
             ItmNo=$[ $ItmNo % $ItemCount ]
          fi

          if [ "$ItmNo" = "" ]; then ItmNo=0; fi
         

          # Get selected path number from list 
          Found=0
          ItmCnt=0
          Tries=$ItemCount
          FIRST_DEST_EXIST=""
          while test $Tries -gt 0; do
             for Itm in $DEST_LIST; do
                 if [ $Found -eq 0 ]; then
                    DEST="$Itm"
                 fi

                 if [ -e "$DEST" ] && [ -d "$DEST" ]; then
                    if [ "$FIRST_DEST_EXIST" = "" ]; then
                       FIRST_DEST_EXIST="$DEST"
                    fi
                 elif [ "$1" = "1" ]; then
                    LogMessage "Error: Destination path $DEST does not exist or is not currently accessible." ERROR
                 else
                    if [ "$FIRST_DEST_EXIST" = "" ]; then
                       FIRST_DEST_EXIST="$DEST"
                    fi
		 fi

                 if [ $ItmCnt -eq $ItmNo ]; then
                    if [ "$1" != "1" ]; then
                       Found=1
                       Tries=1
                    elif [ -e "$DEST" ] && [ -d "$DEST" ]; then
                       Found=1
                       Tries=1
                    fi
                 fi
                 ItmCnt=`expr $ItmCnt + 1`
             done

             Tries=$[ $Tries - 1 ]
             ItmNo=$[ $ItmNo + 1 ]
             if [ $ItmNo -ge $ItemCount ]; then
                ItmNo=0
             fi
          done


          # Chosen path was not found in list.  Use first path that was found
          if [ "$Found" != "1" ] && [ "$FIRST_DEST_EXIST" != "" ]; then
             DEST="$FIRST_DEST_EXIST"
          elif [ ! -e "$DEST" ] || [ ! -d "$DEST" ]; then
             DEST="$FIRST_DEST_EXIST"
          fi
       fi
    fi

    if [ "$DEST" = "" ]; then
       LogMessage "Error: No valid destination paths exist or none are currently accessible from the following specified list:  $DEST_LIST" ERROR
       DEST="NO_VALID_DESTS"
    fi
}


# Wait if hour is within AVOID_BACKUP_HOURS list
#
function AvoidBackupHours {
   if [ $MANUAL_RUN -ne 1 ]; then
      HOUR="`date +%k`"
      Notified=0
      for HR in $AVOID_BACKUP_HOURS; do 
          if [ $HR -eq $HOUR ]; then
             if [ $Notified -eq 0 ]; then
	        LogMessage "Delay: Avoiding backup during hours:  $AVOID_BACKUP_HOURS"
  	        Notified=1
             fi

             while [ $HR -eq $HOUR ]; do
                sleep 1m
                HOUR="`date +%k`"
             done
          fi
      done
   fi
}


# Recurse through directories and sync files individually by modification date (Oldest first)
function RecurseDirOldest {
   OLDFILE="$FILE"
   if [ -d "$1" ]; then

      # Get file list from remote host
      if [ "$RANDOMPATH_HOST" != "$RANDOMPATH" ] && [ "$RANDOMPATH_PATH" != "$RANDOMPATH" ]; then
         if [ "$SOURCE_SSH_PORT" != "" ]; then
            REMOTE_FILE_LIST="`ssh -p $SOURCE_SSH_PORT $RANDOMPATH_HOST ls $RANDOMPATH_PATH$1 2>&1 |grep -v "No such file or directory" |sed -e "s/ /_.-/g"`"
         else
            REMOTE_FILE_LIST="`ssh $RANDOMPATH_HOST ls $RANDOMPATH_PATH$1 2>&1 |grep -v "No such file or directory" |sed -e "s/ /_.-/g"`"
         fi
      else
	 REMOTE_FILE_LIST=""
      fi

      # Sync each file in directory
      for FILE in `ls "$1" -t -r |sed -e "s/ /_.-/g"`; do
          File="`echo $FILE |sed -e "s/_\.-/ /g"`"

          if [ -d "$1/$File" ]; then

             # Recurse into subdirectory
             RecurseDirOldest "$1/$File"
          else

             # Check file exists in remote file list
             if [ "$REMOTE_FILE_LIST" != "" ]; then
                FOUND=0
                for RFILE in $REMOTE_FILE_LIST; do
                    RFile="`echo $RFILE |sed -e "s/_\.-/ /g"`"
                    if [ "$File" = "$RFILE" ]; then
                       FOUND=1
                    fi
                done;
             else
		FOUND=1
             fi

             if [ $FOUND -eq 1 ]; then
                if [ "$SOURCE_SSH_PORT" != "" ]; then
  	           if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
	              rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT --rsh="ssh -p $SOURCE_SSH_PORT" "$RANDOMPATH$1/$File" "$DEST/incremental/m0-w0-d1/$1/$File" |grep -v "No such file or directory" |grep -v "some files/attrs were not transferred" >> $TMP_NAME.$SOURCE.rsync 2>&1
	           else
                      rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT --rsh="ssh -p $SOURCE_SSH_PORT" --backup --backup-dir=$DEST/previous/$BACKUP_DATE "$RANDOMPATH$1/$File" "$DEST/current/$1/$File" |grep -v "No such file or directory" |grep -v "some files/attrs were not transferred" >> $TMP_NAME.$SOURCE.rsync 2>&1
 		   fi
                else
	           if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
                      rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT "$RANDOMPATH$1/$File" "$DEST/incremental/m0-w0-d1/$1/$File" |grep -v "No such file or directory" |grep -v "some files/attrs were not transferred" >> $TMP_NAME.$SOURCE.rsync 2>&1
	           else
                      rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT --backup --backup-dir=$DEST/previous/$BACKUP_DATE "$RANDOMPATH$1/$File" "$DEST/current/$1/$File" |grep -v "No such file or directory" |grep -v "some files/attrs were not transferred" >> $TMP_NAME.$SOURCE.rsync 2>&1
	           fi
	        fi
             fi
          fi
      done
   fi
   FILE="$OLDFILE"
}


# Read sources from /etc/multi_backup_rsync.sources
if [ -e "$SOURCEDEFS" ]; then
   source $SOURCEDEFS
else
   echo -e "\nError:  Backup source defininitions file '$SOURCEDEFS' not found.\n"
   exit 1
fi

# No sources?
if [ "$SOURCES" = "" ]; then
   echo -e "\nError:  No sources (SOURCES=) to backup defined in  '$SOURCEDEFS'.\n"
   exit 1
fi

# Compression options
if [ $COMPRESS_BACKUPS_BZIP2 -eq 1 ]; then
   TAR_OPT="j"
   TAR_EXT="bz2"
   ZIP_CMD="bzip2"
   ZIP_EXT="bz2"
   UNZIP_CMD="bunzip2"
else
   TAR_OPT="z"
   TAR_EXT="gz"
   ZIP_CMD="gzip"
   ZIP_EXT="gz"
   UNZIP_CMD="gunzip"
fi

MULTIPLEX=0
ERRORS_ONLY=0
MANUAL_COMPRESS=0
MANUAL_DECOMPRESS=0
RUN_SOURCES=""
MANUAL_RUN=0
AUTO_RUN=0
RUN_ALL=0

ORIGINAL_SOURCES="$SOURCES"


if [ "$1" = "start" ] || [ "$1" = "START" ]; then

   # Automatically start backup of all sources
   AUTO_RUN=1
elif [ "$1" = "cron" ] || [ "$1" = "CRON" ]; then

   # No general output when ran in cron job?
   AUTO_RUN=1
   ERRORS_ONLY=1
   SHOW_SUMMARY=0
elif [ "$1" = "silent" ] || [ "$1" = "SILENT" ]; then

   # Totally silent (For use in cron job with E-mail report of summary/errors.)
   AUTO_RUN=1
   ERRORS_ONLY=1
   SHOW_SUMMARY=0
   SHOW_ERRORS=0
elif [ "$1" = "multiplex" ] || [ "$1" = "MULTIPLEX" ]; then

   # Multiplex source?
   MULTIPLEX=1
   if [ "$3" != "" ]; then ERRORS_ONLY="$3"; fi
   if [ "$4" != "" ]; then SHOW_SUMMARY="$4"; fi
   if [ "$5" != "" ]; then SHOW_ERRORS="$5"; fi
   if [ "$6" != "" ]; then SOURCE_NUMBER=$6; fi
elif [ "$1" = "run" ] || [ "$1" = "RUN" ] || [ "$1" = "info" ] || [ "$1" = "INFO" ] || [ "$1" = "status" ] || [ "$1" = "STATUS" ]; then

   # Manually run specified sources or show info
   RUN_SOURCES="`echo \"$2\" |sed -e \"s/,/ /g\"`"
   MANUAL_RUN=1

   if [ "$1" = "status" ] || [ "$1" = "STATUS" ]; then
      SHOW_STATUS=1
      SHOW_INFO=1
   elif [ "$1" = "info" ] || [ "$1" = "INFO" ]; then
      SHOW_STATUS=0
      SHOW_INFO=1
   else
      SHOW_INFO=0
      SHOW_INFO=0
   fi

   if [ "$RUN_SOURCES" = "" ]; then
      if [ "$SHOW_INFO" = "1" ]; then
         if [ "$SHOW_STATUS" = "1" ]; then
            echo -e "\nError:  Please specify the name(s) of sources to show status for (Or ALL for all sources.)\n"
         else
            echo -e "\nError:  Please specify the name(s) of sources to show information for (Or ALL for all sources.)\n"
         fi
      else
         echo -e "\nError:  Please specify the name(s) of sources to run (Or ALL for all sources.)\n"
      fi

      echo -e "Available sources: $SOURCES\n"
      exit 1
   fi

   if [ "$RUN_SOURCES" = "all" ] || [ "$RUN_SOURCES" = "ALL" ]; then
      RUN_ALL=1
   else
      SOURCES="$RUN_SOURCES"
   fi
elif [ "$1" = "sources" ] || [ "$1" = "SOURCES" ] || [ "$1" = "source" ] || [ "$1" = "SOURCE" ]; then

   # Show available sources
  echo "$SOURCES"
  exit 1
else

   # Show usage
   echo ""
   cat $0 |grep "#:>" |grep -v "cat \$0" |sed -e "s/#:>[[:space:]]\?//g"
   echo ""
   exit 1
fi


# Able to create temp files?
if [ "$SHOW_INFO" != "1" ] && [ "$TMP_NAME" != "" ]; then
   echo "Test" > $TMP_NAME.test
   if [ -e "$TMP_NAME.test" ]; then
      rm $TMP_NAME.test >/dev/null 2>&1
   else
      echo -e "\nError:  Unable to create temporary files ($TMP_NAME.test)\n"
      exit 1
   fi
fi


# Mount destination archive directory(s)
if [ "$SHOW_INFO" != "1" ]; then
   for DIR in $MOUNT_DEST; do
       if [ "$DIR" != "" ]; then
          MOUNTED="`mount |grep \" $DIR \"`"
          if [ "$MOUNTED" = "" ]; then
             mount $DIR > /dev/null 2>&1
          fi
       fi
   done
fi


# Loop through sources and backup
if [ $ERRORS_ONLY -eq 0 ]; then echo ""; fi
Processed=0
for SOURCE in $SOURCES; do
    if [ $RUN_ALL -eq 1 ] || [ $MULTIPLEX -ne 1 ] || [ "$SOURCE" = "$2" ]; then

       # Set variables
       Processed=`expr $Processed + 1`
       START_TIME="`date`"
       BACKUP_DATE="`date +%Y-%m-%d`"
       BACKUP_SECS="`date +%s`"
       eval SOURCEPATH="\$SOURCEPATH_$SOURCE"
       eval FAILOVER="\$FAILOVER_$SOURCE"
       if [ "$FAILOVER" = "" ]; then
          FAILOVER=$FAILOVER_DEFAULT
       fi
       eval SOURCEFILES="\$SOURCEFILES_$SOURCE"
       eval MOUNT="\$MOUNT_$SOURCE"
       eval MOUNTCMD="\$MOUNTCMD_$SOURCE"
       eval UMOUNTCMD="\$UMOUNT_$SOURCE"
       eval MOUNTSPEC="\$MOUNTSPEC_$SOURCE"
       eval DEST="\$DEST_$SOURCE"
       eval DEST_EXISTS="\$DEST_EXISTS_$SOURCE"
       AlternativeDestPath $DEST_EXISTS
       eval DEST2="\$DEST2_$SOURCE"
       eval SOURCE_EXCLUDE="\$EXCLUDE_$SOURCE"
       eval SOURCE_INCLUDE="\$INCLUDE_$SOURCE"
       eval SOURCE_COMPRESS="\$COMPRESS_$SOURCE"
       eval SOURCE_BANDWIDTH="\$BANDWIDTH_$SOURCE"
       eval SOURCE_TIMEOUT="\$TIMEOUT_$SOURCE"
       eval SOURCE_MANUAL="\$MANUAL_$SOURCE"
       eval SOURCE_MULTIPLEX="\$MULTIPLEX_$SOURCE"
       eval SOURCE_KEEP="\$KEEP_$SOURCE"
       eval SOURCE_DAYS="\$BACKUP_DAYS_$SOURCE"
       eval SOURCE_LOCAL="\$LOCAL_$SOURCE"
       eval SOURCE_DELAY="\$DELAY_$SOURCE"
       eval SOURCE_RETRY_DELAY="\$RETRY_DELAY_$SOURCE"
       eval SOURCE_IGNORE_ERRORS="\$IGNORE_ERRORS_$SOURCE"
       eval SOURCE_TIME="\$TIME_$SOURCE"
       eval SOURCE_INCREMENTAL_BACKUPS="\$INCREMENTAL_BACKUPS_$SOURCE"
       eval SOURCE_INCREMENTAL_DAILY="\$INCREMENTAL_DAILY_$SOURCE"
       eval SOURCE_INCREMENTAL_WEEKLY="\$INCREMENTAL_WEEKLY_$SOURCE"
       eval SOURCE_INCREMENTAL_MONTHLY="\$INCREMENTAL_MONTHLY_$SOURCE"
       eval SOURCE_RSYNC_OPTIONS="\$RSYNC_OPTIONS_$SOURCE"
       eval SOURCE_SSH_PORT="\$SSH_PORT_$SOURCE"
       eval SOURCE_UTILISATION=\$UTILISATION_$SOURCE
       eval SOURCE_RECURSE_DIRS="\$RECURSE_DIRS_$SOURCE"
       eval SOURCE_PRE_EXEC="\$PRE_EXEC_$SOURCE"
       eval SOURCE_POST_EXEC="\$POST_EXEC_$SOURCE"
       eval SOURCE_SYNC_OLDEST_FIRST="\$SYNC_OLDEST_FIRST_$SOURCE"
       eval SOURCE_DEST_CREATE="\$DEST_CREATE_$SOURCE"
       eval SOURCE_COMPRESS_FILES="\$COMPRESS_FILES_$SOURCE"
       eval SOURCE_WARN_DAYS="\$WARN_DAYS_$SOURCE"
       if [ "$SOURCE_WARN_DAYS" = "" ]; then
          SOURCE_WARN_DAYS=2
       fi

       DECOMPRESS_TIME=0
       RSYNC_TIME=0
       COMPRESS_TIME=0

       ERRORS=0
       ERRORS_TITLE="errors"
       WARNINGS=0
       WARNINGS_TITLE="warnings"
       MISC=0
       MISC_TITLE="miscellaneous"
      
 
       # Use default incremental backup options?
       if [ "$SOURCE_INCREMENTAL_BACKUPS" = "" ]; then
          SOURCE_INCREMENTAL_BACKUPS="$INCREMENTAL_BACKUPS"
       fi
       if [ "$SOURCE_INCREMENTAL_DAILY" = "" ]; then
          SOURCE_INCREMENTAL_DAILY="$INCREMENTAL_DAILY"
       fi
       if [ "$SOURCE_INCREMENTAL_WEEKLY" = "" ]; then
          SOURCE_INCREMENTAL_WEEKLY="$INCREMENTAL_WEEKLY"
       fi
       if [ "$SOURCE_INCREMENTAL_MONTHLY" = "" ]; then
          SOURCE_INCREMENTAL_MONTHLY="$INCREMENTAL_MONTHLY"
       fi

       
       # Inclusions list
       RSYNC_INCLUDE=""
       INCLUDE_MODIFIER=""
       if [ "$SOURCE_INCLUDE" != "" ]; then
          for INCLUDE in $SOURCE_INCLUDE; do
	      if [ "$INCLUDE" != "" ]; then
	         if [ "$INCLUDE" = "+" ]; then
		    INCLUDE_MODIFIER="+"
		 elif [ "$EXCLUDE" = "-" ]; then
		    INCLUDE_MODIFIER="-"
 	         else
		    if [ "$INCLUDE_MODIFIER" = "" ]; then
		       RSYNC_INCLUDE="$RSYNC_INCLUDE --include=$INCLUDE"
		    else
		       RSYNC_INCLUDE="$RSYNC_INCLUDE --include='$INCLUDE_MODIFIER $INCLUDE'"
		    fi
		    
		    INCLUDE_MODIFIER=""
		 fi
              fi
	  done
       fi


       # Exclusions list
       RSYNC_EXCLUDE=""
       EXCLUDE_MODIFIER=""
       if [ "$SOURCE_EXCLUDE" != "" ]; then
          for EXCLUDE in $SOURCE_EXCLUDE; do
	      if [ "$EXCLUDE" != "" ]; then
	         if [ "$EXCLUDE" = "+" ]; then
		    EXCLUDE_MODIFIER="+"
		 elif [ "$EXCLUDE" = "-" ]; then
		    EXCLUDE_MODIFIER="-"
 	         else
		    if [ "$EXCLUDE_MODIFIER" = "" ]; then
		       RSYNC_EXCLUDE="$RSYNC_EXCLUDE --exclude=$EXCLUDE"
		    else
		       RSYNC_EXCLUDE="$RSYNC_EXCLUDE --exclude='$EXCLUDE_MODIFIER $EXCLUDE'"
		    fi
		    
		    EXCLUDE_MODIFIER=""
		 fi
              fi
	  done
       fi


       # Limit bandwidth?
       if [ "$SOURCE_BANDWIDTH" != "" ] && [ $SOURCE_BANDWIDTH -gt 0 ]; then
          SOURCE_BANDWIDTH=" --bwlimit=$SOURCE_BANDWIDTH"
       else
          SOURCE_BANDWIDTH=""
       fi

       # Alternative port for SSH?
       if [ "$SOURCE_SSH_PORT" = "" ] || [ $SOURCE_SSH_PORT -lt 1 ]; then
	  SOURCE_SSH_PORT=""
       fi

       # Alternative list of directories to sync recursively
       if [ "$SOURCE_RECURSE_DIRS" = "" ]; then
	  SOURCE_RECURSE_DIRS="$RECURSE_DIRS"
       fi

       # Create destination directory structure if it doesn't exist? 
       if [ "$SOURCE_DEST_CREATE" = "" ]; then
	  SOURCE_DEST_CREATE="$DEST_CREATE"
       fi
       
       # Use default keep previous archives value?
       if [ "$SOURCE_KEEP" = "" ]; then
          SOURCE_KEEP="$KEEP_PREVIOUS"
       fi

       # Use default purge disk utilisation?
       if [ "$SOURCE_UTILISATION" = "" ]; then
          SOURCE_UTILISATION="$PURGE_UTILISATION"
       fi

       # Manual run backup or multiplexed source?
       if [ $MANUAL_RUN -eq 1 ]; then SOURCE_MULTIPLEX=0; fi
       if [ "$SOURCE_MULTIPLEX" = "" ]; then SOURCE_MULTIPLEX=0; fi
       if [ "$SOURCE_MANUAL" = "" ]; then SOURCE_MANUAL=0; fi

       # Custom timeout?
       IO_TIMEOUT=" --timeout=$RSYNC_TIMEOUT"
       if [ "$SOURCE_TIMEOUT" != "" ]; then
          if [ "$SOURCE_TIMEOUT" = "0" ]; then
             IO_TIMEOUT=""
	  else
             IO_TIMEOUT=" --timeout=$SOURCE_TIMEOUT"
	  fi
       elif [ "$RSYNC_TIMEOUT" = "0" ]; then
          IO_TIMEOUT=""
       fi

       # Backup this source today?
       BACKUP_SOURCE=0
       if [ "$SOURCE_DAYS" != "" ]; then
          TODAY="`date +%w`"
	  for DAY in $SOURCE_DAYS; do
	      if [ "$DAY" = "$TODAY" ]; then
	         BACKUP_SOURCE=1
	      fi
	  done
       else
          BACKUP_SOURCE=1
       fi
       if [ $MANUAL_RUN -eq 1 ]; then BACKUP_SOURCE=1; fi

       # Wait until specified time before backing up source?
       if [ "$SHOW_INFO" != "1" ] && [ "$SOURCE_TIME" != "" ] && [ $BACKUP_SOURCE -eq 1 ] && [ $MULTIPLEX -eq 1 ] && [ $SOURCE_MULTIPLEX -eq 1 ]; then
          ORIGINAL_TIME="$SOURCE_TIME"
          SOURCE_TIME="`echo \"$SOURCE_TIME\" |sed -e \"s/:/ /g\" |sed -e \"s/0\+\([0-9]\+\)/\1/g\"`"
	  SOURCE_HOUR="`echo \"$SOURCE_TIME\" |sed -e \"s/^\([0-9]\+\) \([0-9]\+\)\$/\1/g\"`"
	  SOURCE_MIN="`echo \"$SOURCE_TIME\" |sed -e \"s/^\([0-9]\+\) \([0-9]\+\)\$/\2/g\"`"

	  if [ "$SOURCE_HOUR" = "" ] || [ $SOURCE_HOUR -lt 0 ] || [ $SOURCE_HOUR -gt 23 ]; then
             echo -e "\nError:  Invalid hour '$SOURCE_HOUR' specified for delay time '$ORIGINAL_TIME'. Please specify 0-23.\n"
	     BACKUP_SOURCE=0
	  fi
	  if [ "$SOURCE_MIN" = "" ] || [ $SOURCE_MIN -lt 0 ] || [ $SOURCE_MIN -gt 59 ]; then
             echo -e "\nError:  Invalid minute '$SOURCE_MIN' specified for delay time '$ORIGINAL_TIME'. Please specify 0-59.\n"
	     BACKUP_SOURCE=0
	  fi

          WAIT_TIME=0
	  CURRENT_HOUR=-1
	  CURRENT_MIN=-1
	  WAIT_TIME_PURGE=$PURGE_WHILE_WAITING
	  while [ $CURRENT_HOUR -ne $SOURCE_HOUR ] || [ $CURRENT_MIN -lt $SOURCE_MIN ]; do
	     if [ $CURRENT_HOUR -eq -1 ] && [ $CURRENT_MIN -eq -1 ]; then
	        LogMessage "Delay: Waiting until $ORIGINAL_TIME before starting backup..."
	     else
	        sleep 1m 
	        WAIT_TIME=`expr $WAIT_TIME + 1`
		if [ $WAIT_TIME -gt 1500 ]; then 
	           NLLogMessageNL "Error: Start time $ORIGINAL_TIME not reached within 25 hours. Backup aborted." ERROR BOTH
		   SOURCE_HOUR="$CURRENT_HOUR"
		   SOURCE_MIN="$CURRENT_MIN"
  	           BACKUP_SOURCE=0
		fi
	     fi

	     if [ $WAIT_TIME_PURGE -eq 1 ]; then
	     
	        # Purge previous backups while waiting for correct backup time?
		LOGFILE="$TMP_PURGE_LOG.$BACKUP_SECS"
	        if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
		   PurgeBackups "$DEST/incremental" $SOURCE_UTILISATION 0
		else
		   PurgeBackups "$DEST/previous" $SOURCE_UTILISATION $SOURCE_KEEP
		fi
	     fi

	     CURRENT_HOUR="`date +%H |sed -e \"s/0\+\([0-9]\+\)/\1/g\"`"
    	     CURRENT_MIN="`date +%M |sed -e \"s/0\+\([0-9]\+\)/\1/g\"`"
	  done;
       fi
       
       # Manual run only
       if [ $SOURCE_MANUAL -eq 1 ] && [ $MANUAL_RUN -eq 0 ]; then
          BACKUP_SOURCE=0
       fi
       
       # Small random delay at start
       if [ "$SHOW_INFO" != "1" ] && [ $MANUAL_RUN -ne 1 ]; then
          sleep `expr $RANDOM % 60`s
       fi
	

       # Check for hung process, or killed process leaving lock file behind
       if [ "$SHOW_INFO" != "1" ] && [ -e "$TMP_NAME.$SOURCE.lock" ]; then
          LOCK="`cat $TMP_NAME.$SOURCE.lock`"

          if [ -e "$TMP_NAME.$SOURCE.pid" ]; then
             _PID="`cat $TMP_NAME.$SOURCE.pid`"

	     if [ "$_PID" != "" ]; then
		_P_PID="`ps -p ${_PID} |grep ${_PID}`"

		if [ "$_P_PID" != "" ]; then
		   _P_PID="`ps -p ${_PID} |grep ${_PID} |grep $0`"

		   if [ "$LOCK" = "" ]; then
		      LOCK="0"
		   fi

	           NOW="`date +%s`"

    	           if [ `expr $NOW - $LOCK` -ge `expr $LOCK_TIME \* 60 \* 60` ]; then
	              LogMessage "Warning: Active lock file exists and has expired - Killing process ${_PID}." WARNING BOTH

		      kill $_PID
		      _P_PID="`ps l -p ${_PID} |grep ${_PID} |grep $0`"
		      if [ "$_P_PID" != "" ]; then
		         kill -9 $_PID
		         _P_PID="`ps l -p ${_PID} |grep ${_PID} |grep $0`"
		         if [ "$_P_PID" != "" ]; then
		            kill -11 $_PID
                         fi
                      fi

		      # Check process has gone
		      _P_PID="`ps l -p ${_PID} |grep ${_PID} |grep $0`"

		      if [ "$_P_PID" != "" ]; then
       	                 LogMessage "Error: Process is still present after attempt to kill ${_PID}: ${_P_PID}" ERROR BOTH
                         BACKUP_SOURCE=0
		      else
	                 rm -f $TMP_NAME.$SOURCE.*
		      fi
		   fi
		else
	           LogMessage "Error: Lock file exists, but no process with PID ${_PID} was found.  Removing lock file." ERROR BOTH
	           rm -f $TMP_NAME.$SOURCE.*
		fi
             else
	        LogMessage "Error: Lock file exists, but PID is blank.  Removing lock file." ERROR BOTH
	        rm -f $TMP_NAME.$SOURCE.*
	     fi
	  else
	     LogMessage "Error: Lock file exists, but there is no PID file.  Removing lock file." ERROR BOTH
	     rm -f $TMP_NAME.$SOURCE.*
	  fi
       fi


       # Check lock file for active process
       if [ "$SHOW_INFO" != "1" ] && [ -e "$TMP_NAME.$SOURCE.lock" ]; then
          LOCK="`cat $TMP_NAME.$SOURCE.lock`"

	  if [ "$LOCK" != "" ] && [ $LOCK -gt 0 ]; then
	     NOW="`date +%s`"
	     if [ `expr $NOW - $LOCK` -lt `expr $LOCK_TIME \* 60 \* 60` ]; then
	        LogMessage "Warning: Active lock file exists - Backup of source skipped." WARNING BOTH NOSTATUS
		BACKUP_SOURCE=0
	     fi
	  fi
       fi


       # Check that appropriate destination archive directory is mounted
       if [ "$SHOW_INFO" != "1" ]; then
          for DIR in $MOUNT_DEST; do
             if [ "$DIR" != "" ]; then
                MOUNTED="`mount |grep \" $DIR \"`"
                if [ "$MOUNTED" = "" ]; then
                   mount $DIR > /dev/null 2>&1
                   MOUNTED="`mount |grep \" $DIR \"`"
                   if [ "$MOUNTED" = "" ]; then
                      MATCH_DEST="`echo "$DEST" |grep "^\`echo "$DIR/" |sed -e "s~/\+\$~/~g"\`"`"
		      if [ "$MATCH_DEST" != "" ]; then
                         LogMessageNL "Error: Destination archive directory '$DIR' is not mounted.  Backup aborted." ERROR BOTH
                         BACKUP_SOURCE=0
                      fi
                   fi
                fi
             fi
          done
       fi


       # Show information
       if [ "$SHOW_INFO" == "1" ]; then
          if [ "$SHOW_STATUS" != "1" ]; then
             if [ $Processed -gt 1 ]; then
                echo ""
             fi

             echo "Source #${Processed}: $SOURCE"
             echo ""
          fi

          if [ -e "$TMP_NAME.$SOURCE.lock" ]; then
             if [ "$SHOW_STATUS" != "1" ]; then
                echo "Status: In progress."
    	        if [ -e "/tmp/$SOURCE.start" ]; then
	           RUNTIME="`cat /tmp/$SOURCE.start`"
	           if [ "$RUNTIME" != "" ] && [ $RUNTIME -gt 0 ]; then
	   	      RUNTIME="$[ `date +%s` - $RUNTIME ]"
		      if [ "$RUNTIME" != "" ] && [ $RUNTIME -gt 0 ]; then
                         echo "Run-time: $[ $RUNTIME / 86400 ] days(s), $[ ( $RUNTIME % 86400 ) / 3600 ] hour(s), $[ ($RUNTIME % 86400 % 3600) / 60 ] minute(s)"
		      fi
                   fi
                fi
                echo "Lock file: $TMP_NAME.$SOURCE.lock"
                TIMESTAMP="`cat $TMP_NAME.$SOURCE.lock`"
                echo "Last refresh: `date -d @$TIMESTAMP`"

                if [ -e "$TMP_NAME.$SOURCE.pid" ]; then
                   echo "PID: `cat $TMP_NAME.$SOURCE.pid`"
                fi

                if [ -e "$TMP_NAME.$SOURCE.status" ]; then
                   echo ""
		   echo "Last message: `cat $TMP_NAME.$SOURCE.status`"
                fi

                if [ -e "$TMP_NAME.$SOURCE.rsync" ]; then
                   echo -e "\nRsync progress:\n"
		   tail -n 10 $TMP_NAME.$SOURCE.rsync
                fi
             else
                if [ -e "$TMP_NAME.$SOURCE.status" ]; then
		   if [ -e "/tmp/$SOURCE.start" ]; then
		      RUNTIME="`cat /tmp/$SOURCE.start`"
		      if [ "$RUNTIME" != "" ] && [ $RUNTIME -gt 0 ]; then
			 RUNTIME="$[ `date +%s` - $RUNTIME ]"
		         if [ "$RUNTIME" != "" ] && [ $RUNTIME -gt 0 ]; then
                            echo "$SOURCE: Running ($[ $RUNTIME / 86400 ]d $[ ( $RUNTIME % 86400 ) / 3600 ]h $[ ($RUNTIME % 86400 % 3600) / 60 ]m):"
			 else
                            echo "$SOURCE: Running:"
			 fi
                      else
                         echo "$SOURCE: Running:"
		      fi
                   else
                      echo "$SOURCE: Running:"
                   fi
                   
		   echo "`cat $TMP_NAME.$SOURCE.status`"

                   if [ -e "$TMP_NAME.$SOURCE.rsync" ]; then
		      tail -n 1 $TMP_NAME.$SOURCE.rsync
                   fi
                else
                   echo "$SOURCE: In progress."
                fi
             fi
          else
             if [ "$SHOW_STATUS" != "1" ]; then
                echo "Status: Not running."
                if [ -e "$TMP_NAME.$SOURCE.status" ]; then
                   echo ""
		   echo "Last message: `cat $TMP_NAME.$SOURCE.status`"
                fi
             else
                if [ -e "$TMP_NAME.$SOURCE.status" ]; then
                   echo "$SOURCE: Not running: `cat $TMP_NAME.$SOURCE.status`"
                else
                   echo "$SOURCE: Not running."
                fi
             fi
          fi

          if [ "$SHOW_STATUS" != "1" ]; then
             echo ""

             echo "Local source: $SOURCE_LOCAL"
             echo "Manual run only: $SOURCE_MANUAL"
             echo "Backup time: $SOURCE_TIME"
             echo "Backup days: $SOURCE_DAYS"
             echo ""
 
	     if [ "$FAILOVER" = "1" ]; then
                echo "Source path(s): $SOURCEPATH (Failover enabled)"
             else
                echo "Source path(s): $SOURCEPATH"
             fi
             echo "Source files: $SOURCEFILES"

	     if [ "$DEST_PATHS" != "" ] && [ "$DEST" != "$DEST_PATHS" ]; then
                echo "Destinations: $DEST_PATHS"
                echo "Current dest: $DEST"
             else
                echo "Destination: $DEST"
             fi
             if [ "$DEST2" != "" ]; then
                echo "Alt. dest: $DEST2"
             fi
             echo ""

             if [ "$MOUNT" != "" ]; then
                echo "Mount source: $MOUNT"
             fi
             echo "Create dest: $SOURCE_DEST_CREATE"
             if [ "$SOURCE_RECURSE_DIRS" != "" ]; then
                echo "Recurse dirs: $SOURCE_RECURSE_DIRS"
             fi
             echo "Sync oldest first: $SOURCE_SYNC_OLDEST_FIRST"
             if [ "$SOURCE_EXCLUDE" != "" ]; then
                echo "Exclusions: $SOURCE_EXCLUDE"
             fi
             if [ "$SOURCE_INCLUDE" != "" ]; then
                echo "Inclusions: $SOURCE_INCLUDE"
             fi
             if [ "$SOURCE_IGNORE_ERRORS" != "" ]; then
                echo "Ignore errors: $SOURCE_IGNORE_ERRORS"
             fi
             echo "Max. utilisation: ${SOURCE_UTILISATION}%"
             echo ""

             echo "Compress: $SOURCE_COMPRESS"
             if [ "$SOURCE_BANDWIDTH" != "" ]; then
                echo "Bandwidth limit: $SOURCE_BANDWIDTH"
             fi
             if [ "$SOURCE_TIMEOUT" != "" ]; then
                echo "Timeout: $SOURCE_TIMEOUT"
             fi
             echo "Multiplex: $SOURCE_MULTIPLEX"
             if [ "$SOURCE_DELAY" != "" ]; then
                echo "Delay: $SOURCE_DELAY"
             fi
             echo ""

             echo "Keep archives: $SOURCE_KEEP"
             echo "Incremental: $SOURCE_INCREMENTAL_BACKUPS"
             echo ""

             if [ "$SOURCE_INCREMENTAL_BACKUPS" = "1" ]; then
                echo "Daily archives: $SOURCE_INCREMENTAL_DAILY"
                echo "Weekly archives: $SOURCE_INCREMENTAL_WEEKLY"
                echo "Monthly archives: $SOURCE_INCREMENTAL_MONTHLY"
                echo ""

                if [ -e "$DEST/incremental/.last.daily" ]; then
                   TIMESTAMP="`cat $DEST/incremental/.last.daily`"
                   echo "Last daily: `date -d @$TIMESTAMP`"
	        else
                   echo "Last daily: Never"
                fi

                if [ -e "$DEST/incremental/.last.weekly" ]; then
                   TIMESTAMP="`cat $DEST/incremental/.last.weekly`"
                   echo "Last weekly: `date -d @$TIMESTAMP`"
	        else
                   echo "Last weekly: Never"
                fi

                if [ -e "$DEST/incremental/.last.monthly" ]; then
                   TIMESTAMP="`cat $DEST/incremental/.last.monthly`"
                   echo "Last monthly: `date -d @$TIMESTAMP`"
	        else
                   echo "Last monthly: Never"
                fi

                echo ""
             fi

             if [ "$SOURCE_RSYNC_OPTIONS" != "" ] || [ "$SOURCE_SSH_PORT" != "" ] || [ "$SOURCE_PRE_EXEC" != "" ] || [ "$SOURCE_POST_EXEC" != "" ]; then
                echo "Rsync options: $SOURCE_RSYNC_OPTIONS"
                if [ "$SOURCE_SSH_PORT" != "" ]; then
                   echo "Alternative SSH port: $SOURCE_SSH_PORT"
                fi
                if [ "$SOURCE_PRE_EXEC" != "" ]; then
                   echo "Pre exec: $SOURCE_PRE_EXEC"
                fi
                if [ "$SOURCE_POST_EXEC" != "" ]; then
                   echo "Post exec: $SOURCE_POST_EXEC"
                fi
                echo ""
             fi
          else
             echo ""
          fi

          BACKUP_SOURCE=0
       fi


       if [ $BACKUP_SOURCE -ne 1 ] || [ "$SHOW_INFO" = "1" ]; then

          # Don't backup this source
          BACKUP_SOURCE=0
       elif [ $MULTIPLEX -ne 1 ] && [ $SOURCE_MULTIPLEX -eq 1 ]; then
       
          # Multiplex this source
          LogMessage "Initiating multiplexed backup..."
  	  $BGSHELL $0 multiplex "$SOURCE" "$ERRORS_ONLY" "$SHOW_SUMMARY" "$SHOW_ERRORS" "$Processed" &
       else

          # Backup this source
          if [ "$SOURCEFILES" = "" ]; then
             LogMessageNL "Error: No source files/directories (SOURCEFILES_$SOURCE=) specified for source $SOURCE in '$SOURCEDEFS' (Source doesn't exist?)  Valid sources:  $ORIGINAL_SOURCES" ERROR BOTH
          elif [ "$DEST" = "" ]; then
             LogMessageNL "Error: No backup destination path (DEST_$SOURCE=) specified for source $SOURCE in '$SOURCEDEFS'." ERROR BOTH
          elif [ "$DEST" != "NO_VALID_DESTS" ]; then

             # Create lock file
             echo "`date +%s`" > $TMP_NAME.$SOURCE.lock
             echo "$$" > $TMP_NAME.$SOURCE.pid


             # Randomise source host in source path
	     if [ "$FAILOVER" != "1" ]; then
                RandomSourcePath "$SOURCEPATH" 0
             else
		LAST_HOST=""
		LAST_PATH=""
		FailoverSourcePath "$SOURCEPATH"
	     fi


             # Create destination directories
             DirError=0
	     if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
	        DIR_LIST="$DEST/incremental $DEST/log"
             else
	        DIR_LIST="$DEST/current $DEST/previous $DEST/log"
             fi
	     if [ "$SOURCE_DEST_CREATE" = "1" ]; then
	        DEST_DIRS=""
  	        DEST_PATH=""
	        for DIRS in `echo "$DEST" |sed -e "s/[[:space:]]\+/_.-/g" |sed -e "s~/~ ~g"`; do
		    DIRNAME="`echo \"$DIRS\" |sed -e \"s/_.-/ /g\"`"

  	            DEST_PATH="$DEST_PATH/$DIRNAME"
		    DEST_DIRS="$DEST_DIRS $DEST_PATH"
	        done;

		DIR_LIST="$DEST_DIRS $DIR_LIST"
	     fi
             for DIRS in $DIR_LIST; do
                 if [ ! -e "$DIRS" ]; then
                    mkdir $DIRS
                    if [ ! -e "$DIRS" ]; then
                       LogMessage "Error: Unable to create destination directory '$DIRS' for source $SOURCE." ERROR
  		       DirError=1
                    fi
                 fi
             done

             # Create secondary destination directories
	     DIR_LIST=""
	     if [ "$SOURCE_DEST_CREATE" = "1" ] && [ "$DEST2" != "" ]; then
	        DEST2_DIRS=""
  	        DEST2_PATH=""
	        for DIRS in `echo "$DEST2" |sed -e "s/[[:space:]]\+/_.-/g" |sed -e "s~/~ ~g"`; do
		    DIRNAME="`echo \"$DIRS\" |sed -e \"s/_.-/ /g\"`"

  	            DEST2_PATH="$DEST2_PATH/$DIRNAME"
		    DEST2_DIRS="$DEST2_DIRS $DEST2_PATH"
	        done;

		DIR_LIST="$DEST2_DIRS $DIR_LIST"
	     fi
             for DIRS in $DIR_LIST; do
                 if [ ! -e "$DIRS" ]; then
                    mkdir $DIRS
                    if [ ! -e "$DIRS" ]; then
                       LogMessage "Error: Unable to create secondary destination directory '$DIRS' for source $SOURCE." ERROR
  		       DirError=1
                    fi
                 fi
             done


             # Update lock file
             echo "`date +%s`" > $TMP_NAME.$SOURCE.lock

             if [ $DirError -eq 0 ]; then
       
                # Set log file
                LOGFILE="$DEST/log/$BACKUP_DATE-results.log"
                LOGERRORS="$DEST/log/$BACKUP_DATE-errors.log"
                LOGSUMMARY="$DEST/log/$BACKUP_DATE-summary.log"
                if [ -e "$LOGFILE" ]; then rm $LOGFILE; fi
                if [ -e "$LOGERRORS" ]; then rm $LOGERRORS; fi
                if [ -e "$LOGSUMMARY" ]; then rm $LOGSUMMARY; fi
		
		TOTAL_FILE_COUNT=0
		TOTAL_DIR_COUNT=0

		CHANGED_FILE_COUNT=0
		CHANGED_DIR_COUNT=0

		MODIFIED_FILE_COUNT=0
	
	
                # Log start time
                START_TIME="`date`"
		if [ $MULTIPLEX -eq 1 ]; then
                   LogMessageNL "Multiplexed backup of '$SOURCEFILES' from $RANDOMPATH ($SOURCE) to $DEST started at $START_TIME..."
		else
                   LogMessageNL "Backup of '$SOURCEFILES' from $RANDOMPATH ($SOURCE) to $DEST started at $START_TIME..."
		fi
		echo "`date +%s`" >/tmp/$SOURCE.start


		# Remove old logs
                RemoveOldLogs $DEST/log -results.log $KEEP_LOGS
                RemoveOldLogs $DEST/log -errors.log $KEEP_LOGS
                RemoveOldLogs $DEST/log -summary.log $KEEP_LOGS
		
                # Update lock file
                echo "`date +%s`" > $TMP_NAME.$SOURCE.lock

		# Execute pre command(s)
	        if [ "$SOURCE_PRE_EXEC" != "" ]; then
		   LogMessage "Executing pre-source command(s): $SOURCE_PRE_EXEC"
	           PrePostExec "$RANDOMPATH" "$SOURCE_PRE_EXEC"
	        fi
		
                # Update lock file
                echo "`date +%s`" > $TMP_NAME.$SOURCE.lock


                # Use default rsync options?
                if [ "$SOURCE_RSYNC_OPTIONS" = "" ]; then
   	           if [ "$SOURCE_LOCAL" != "" ] && [ $SOURCE_LOCAL -eq 1 ]; then
                      SOURCE_RSYNC_OPTIONS="$RSYNC_OPTIONS_LOCAL"
                   else
                      SOURCE_RSYNC_OPTIONS="$RSYNC_OPTIONS_REMOTE"
	           fi
                fi
	  
		# Append any purge log messages to log file
		if [ -e "$TMP_PURGE_LOG.$BACKUP_SECS" ]; then
		   cat "$TMP_PURGE_LOG.$BACKUP_SECS" >> $LOGFILE
		   rm "$TMP_PURGE_LOG.$BACKUP_SECS" > /dev/null 2>&1
		fi

		
                if [ -e "$DEST" ]; then

                   # Mount specified source volume
	           IsMounted=1
                   if [ "$MOUNT" != "" ]; then
	              LogMessage "Mounting $MOUNT..."
		
	   	      if [ "$UMOUNTCMD" = "" ]; then
                         umount $MOUNT > /dev/null 2>&1
  		      else
		         $UMOUNTCMD > /dev/null 2>&1
		      fi
		
  		      if [ "$MOUNTCMD" = "" ]; then
                         mount $MOUNT >> $LOGFILE 2>&1
		      else
                         mount $MOUNTCMD >> $LOGFILE 2>&1
		      fi
		
		      MOUNTED_VOLUME="`mount |grep " $MOUNT " |grep -v grep`"
	  	      MOUNTED_FILES="`ls $MOUNT$MOUNTSPEC 2>&1 |grep -v "No such file or directory"`"

		      if [ "$MOUNTED_VOLUME" = "" ] || [ "$MOUNTED_FILES" = "" ]; then
		         LogMessage "Error: Unable to mount $MOUNT or mounted volume contains no files/directories." ERROR BOTH
		         IsMounted=0
		      fi
                   fi

                   if [ $IsMounted -eq 1 ]; then
	     
                      # Backup source files
  	  	      SOURCECOUNT=1
                      SOURCELIST=""

		      for FILE in $SOURCEFILES; do
			  if [ "$FILE" != "" ]; then
			     LAST_FILE="$FILE"
                          fi
		      done

                      for FILE in $SOURCEFILES; do
			  CURRENT_FILE="$FILE"

 	                  if [ "$FILE" != "" ]; then
			     FILE_TYPE="`echo \"$FILE\" |sed -e \"s/^\(.\+\):\(.\+\)\$/\1/g\"`"
			     FILE_NAME="`echo \"$FILE\" |sed -e \"s/^\(.\+\):\(.\+\)\$/\2/g\"`"
			     if [ "$FILE_TYPE" != "$FILE" ] && [ "$FILE_NAME" != "$FILE" ]; then
			        if [ "$FILE_TYPE" = "dir" ] || [ "$FILE_TYPE" = "DIR" ]; then
				   FILE_IS_DIR=1
				   FILE="$FILE_NAME"
				elif [ "$FILE_TYPE" = "file" ] || [ "$FILE_TYPE" = "FILE" ]; then
				   FILE_IS_DIR=0
				   FILE="$FILE_NAME"
				else
		                   LogMessage "Error: Unknown file type '$FILE_TYPE' - Please specify either dir:$FILE_NAME or file:$FILE_NAME" ERROR
				   FILE=""
				fi
		             else
			        FILE_IS_DIR=1
			     fi
			  fi 
			    
 	                  if [ "$FILE" != "" ]; then
		             if [ $SOURCECOUNT -gt 1 ] && [ $ERRORS_ONLY -eq 0 ]; then echo ""; fi

		             if [ "$RANDOMPATH" != "" ]; then
			        RANDOMPATH_HOST=""
			        RANDOMPATH_LAST="(NULL)"
			     
			        IsMounted=0
			        RetryCount=$RSYNC_RETRIES

			        while [ $IsMounted -eq 0 ] && [ $RetryCount -gt 0 ] && [ "$RANDOMPATH" != "" ] && [ "$RANDOMPATH_LAST" != "$RANDOMPATH" ]; do
		                      if [ "$SOURCE_COMPRESS" != "" ] && [ $SOURCE_COMPRESS -eq 2 ]; then
                                         FILE="`echo \"$FILE\" |sed -e \"s~^/\+~~g\"`"
		                      else
                                         FILE="/`echo \"$FILE\" |sed -e \"s~^/\+~~g\"`"
			              fi

			              IsMounted=1
				      
 	      	                      # Check path exists on remote host and contains files
				      RANDOMPATH_HOST="`echo "$RANDOMPATH" |sed -e "s/:.\+\$//g"`"
				      RANDOMPATH_PATH="`echo "$RANDOMPATH" |sed -e "s/^.\+://g"`"

				      if [ "$RANDOMPATH_HOST" != "$RANDOMPATH" ] && [ "$RANDOMPATH_PATH" != "$RANDOMPATH" ]; then
		                         if [ "$SOURCE_SSH_PORT" != "" ]; then
	  	                            MOUNTED_FILES="`ssh -p $SOURCE_SSH_PORT $RANDOMPATH_HOST ls $RANDOMPATH_PATH$FILE 2>&1 |grep -v "No such file or directory"`"
 	                                 else
	  	                            MOUNTED_FILES="`ssh $RANDOMPATH_HOST ls $RANDOMPATH_PATH$FILE 2>&1 |grep -v "No such file or directory"`"
		                         fi
				         MOUNTED_FILES_SSH="`echo "$MOUNTED_FILES" |grep "^ssh:"`"

				         if [ "$MOUNTED_FILES_SSH" != "" ]; then
					    
					    if [ $RetryCount -gt 1 ]; then
		                               LogMessage "(Retry `expr $RSYNC_RETRIES - $RetryCount + 1` of $RSYNC_RETRIES)  Unable to backup $FILE:  $MOUNTED_FILES_SSH" GENERAL
					    else
		                               LogMessage "Error: (Retry `expr $RSYNC_RETRIES - $RetryCount + 1` of $RSYNC_RETRIES) Unable to backup $FILE:  $MOUNTED_FILES_SSH" ERROR
					    fi
		                            IsMounted=0
		                         elif [ "$MOUNTED_FILES" = "" ]; then
		                            LogMessage "Error: $RANDOMPATH_PATH$FILE contains no files/directories." ERROR
					    if [ "$FAILOVER" != "1" ]; then 
					       RetryCount=1
					    fi
		                            IsMounted=0
				         fi
		                      fi

				      if [ $IsMounted -eq 0 ]; then
				
             			         # Randomise or failover source host in source path
	                                 if [ "$FAILOVER" != "1" ]; then
				            RANDOMPATH_LAST="$RANDOMPATH"
		                            RandomSourcePath "$SOURCEPATH" 1
             				 else
					    FailoverSourcePath "$SOURCEPATH"
	                                 fi
				      fi

				      RetryCount=`expr $RetryCount - 1`
			        done;
		             fi
		
                             if [ $IsMounted -eq 1 ]; then
		                if [ $SOURCECOUNT -gt 1 ]; then
		                   NLLogMessageNL "Backing up $FILE via $RANDOMPATH on `date`..."
				else
		                   LogMessageNL "Backing up $FILE via $RANDOMPATH on `date`..."
				fi

		                SOURCE_START="`date`"

                                # Update lock file
                                echo "`date +%s`" > $TMP_NAME.$SOURCE.lock


			        # Setup background job to update lock file
			        # while rsync is taking place
			        #
			        echo "1" > $TMP_NAME.$SOURCE.updatelock
			        $BGSHELL -c "while [ -e $TMP_NAME.$SOURCE.updatelock ]; do echo \"\`date +%s\`\" > $TMP_NAME.$SOURCE.lock; sleep 1s; done" &

                                # Rotate incremental backups?
			        if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ] && [ $SOURCECOUNT -eq 1 ]; then
			           RotateBackups "$DEST/incremental" $SOURCE_INCREMENTAL_DAILY $SOURCE_INCREMENTAL_WEEKLY $SOURCE_INCREMENTAL_MONTHLY
				
                                   # Create new current backup directory
                                   if [ ! -e "$DEST/incremental/m0-w0-d1" ]; then
				
			  	      # Non-incremental backup converted to incremental?
                                      if [ -e "$DEST/current" ] && [ -d "$DEST/current" ] && [ ! -e "$DEST/incremental/m0-w0-d2" ]; then
				         mv $DEST/current $DEST/incremental/m0-w0-d2 >>$LOGERRORS 2>&1
				      fi
	
                                      mkdir "$DEST/incremental/m0-w0-d1" >>$LOGERRORS 2>&1
                                   fi

		 	   	   # Hard-link or copy previous backup files into new current backup directory
				   if [ -e "$DEST/incremental/m0-w0-d2" ]; then
                                      if [ "$SOURCE_COMPRESS_FILES" = "1" ]; then
				         # Copy files (No hardlinks)
			     	         cp -a $DEST/incremental/m0-w0-d2/* $DEST/incremental/m0-w0-d1 >>$LOGERRORS 2>&1
				      else

					 # Hard-link files
			     	         cp -al $DEST/incremental/m0-w0-d2/* $DEST/incremental/m0-w0-d1 >>$LOGERRORS 2>&1
                                      fi
				   fi
		                fi


                                # Purge previous backups?
			        if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
                                   PurgeBackups "$DEST/incremental" $SOURCE_UTILISATION 0
			        else
                                   PurgeBackups "$DEST/previous" $SOURCE_UTILISATION $SOURCE_KEEP
			        fi


				# Uncompress files
			        RSYNC_RETRY=1
				if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ] && [ "$SOURCE_COMPRESS_FILES" = "1" ] && [ -e "$DEST/incremental/m0-w0-d1/$FILE" ]; then
		                   LogMessageNL "Uncompressing files:  $FILE"
			     	   UNZIP_ERRORS="`gunzip -r $DEST/incremental/m0-w0-d1/$FILE 2>&1`"
				   if [ "$UNZIP_ERRORS" != "" ]; then
		                      LogMessage "Error: Unable to uncompress files: $FILE" ERROR
                                      echo "$UNZIP_ERRORS" >> $LOGERRORS
				      RSYNC_RETRY=0
				      UNZIP_ERRORS=""
                                   fi
				fi


		                # Copy file(s) via rsync
	                        Delay $SOURCE_DELAY
                                AvoidBackupHours
			     
		                LogMessageNL "Initiating rsync transfer: $FILE"

		                TIME_START="`date +%s`"
			        RSYNC_RETRY_COUNT=1
			        while [ $RSYNC_RETRY -eq 1 ]; do

				   # Delay next retry time?
                                   if [ "$SOURCE_RETRY_DELAY" != "" ]; then
				      NEXT_RETRY="$[ `date +%s` + $SOURCE_RETRY_DELAY ]"
                                   else
			              NEXT_RETRY="`date +%s`"
                                   fi

				   # Individually sync oldest files first?
	  		           if [ "$SOURCE_SYNC_OLDEST_FIRST" != "" ]; then
				      pushd $DEST/incremental/m0-w0-d1 >/dev/null
				      SFILE="`echo \"$FILE\" |sed \"s~^/~~g\"`"
				      RecurseDirOldest $SFILE
				      popd >/dev/null
				   fi

			           # Synchronise files via rsync
	           		   RSYNC_RETRY=0
		  	           if [ "$SOURCE_SSH_PORT" != "" ]; then
			              if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
	                                 rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT --rsh="ssh -p $SOURCE_SSH_PORT" "$RANDOMPATH$FILE" "$DEST/incremental/m0-w0-d1" >> $TMP_NAME.$SOURCE.rsync 2>&1
				      else
                                         rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT --rsh="ssh -p $SOURCE_SSH_PORT" --backup --backup-dir=$DEST/previous/$BACKUP_DATE "$RANDOMPATH$FILE" "$DEST/current" >> $TMP_NAME.$SOURCE.rsync 2>&1
				      fi
		  		   else
			              if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
                                         rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT "$RANDOMPATH$FILE" "$DEST/incremental/m0-w0-d1" >> $TMP_NAME.$SOURCE.rsync 2>&1
				      else
                                         rsync $SOURCE_RSYNC_OPTIONS$SOURCE_BANDWIDTH$RSYNC_INCLUDE$RSYNC_EXCLUDE$IO_TIMEOUT --backup --backup-dir=$DEST/previous/$BACKUP_DATE "$RANDOMPATH$FILE" "$DEST/current" >> $TMP_NAME.$SOURCE.rsync 2>&1
				      fi
				   fi
		  	  	   if [ -e "$TMP_NAME.$SOURCE.rsync" ]; then
				      cat $TMP_NAME.$SOURCE.rsync >> $LOGFILE
			 	   fi

                                   # Transfer failed/error - Retry?
				   if [ "$SOURCE_IGNORE_ERRORS" != "" ]; then
                                      RETRY_ERRORS="`grep -i -E \"^.*(rsync.*connection[[:space:]]+unexpectedly[[:space:]]+closed|rsync[[:space:]]+error.*some[[:space:]]+files[[:space:]]+could[[:space:]]+not[[:space:]]+be[[:space:]]+transferred|rsync[[:space:]]+warning.*some[[:space:]]+files[[:space:]]+vanished[[:space:]]+before[[:space:]]+they[[:space:]]+could[[:space:]]+be[[:space:]]+transferred|rsync[[:space:]]+error.*timeout[[:space:]]+in[[:space:]]+data[[:space:]]+send/receive|connection[[:space:]]+timed[[:space:]]+out|file[[:space:]]+has[[:space:]]+vanished:[[:space:]]+|WARNING:.*failed[[:space:]]+verification[[:space:]]+-+[[:space:]]+update[[:space:]]+discarded|rsync[[:space:]]+error:[[:space:]]+unexplained[[:space:]]+error).*\$\" $TMP_NAME.$SOURCE.rsync |grep -v -e \"$SOURCE_IGNORE_ERRORS\" |grep -v \"some files could not be transferred\"`"
				   else
                                      RETRY_ERRORS="`grep -i -E \"^.*(rsync.*connection[[:space:]]+unexpectedly[[:space:]]+closed|rsync[[:space:]]+error.*some[[:space:]]+files[[:space:]]+could[[:space:]]+not[[:space:]]+be[[:space:]]+transferred|rsync[[:space:]]+warning.*some[[:space:]]+files[[:space:]]+vanished[[:space:]]+before[[:space:]]+they[[:space:]]+could[[:space:]]+be[[:space:]]+transferred|rsync[[:space:]]+error.*timeout[[:space:]]+in[[:space:]]+data[[:space:]]+send/receive|connection[[:space:]]+timed[[:space:]]+out|file[[:space:]]+has[[:space:]]+vanished:[[:space:]]+|WARNING:.*failed[[:space:]]+verification[[:space:]]+-+[[:space:]]+update[[:space:]]+discarded|rsync[[:space:]]+error:[[:space:]]+unexplained[[:space:]]+error).*\$\" $TMP_NAME.$SOURCE.rsync`"
				   fi
				   if [ "$RETRY_ERRORS" != "" ]; then
				      if [ $RSYNC_RETRY_COUNT -le $RSYNC_RETRIES ]; then
	                                 if [ "$FAILOVER" != "1" ]; then
                                            RandomSourcePath "$SOURCEPATH" 1
             				 else
					    FailoverSourcePath "$SOURCEPATH"
	                                 fi

				         LogMessage "Retry: $RSYNC_RETRY_COUNT of $RSYNC_RETRIES - Restarting transfer of $FILE from $SOURCE via $RANDOMPATH at `date`..."
				         RSYNC_RETRY=1
			                 sleep $RSYNC_RETRY_DELAY
					 if [ "$NEXT_RETRY" != "" ]; then
					    DELAY_NOTIFIED=0
                                            while [ `date +%s` -lt $NEXT_RETRY ]; do
					       if [ "$DELAY_NOTIFIED" != "1" ]; then
				                  LogMessage "Retry: $RSYNC_RETRY_COUNT of $RSYNC_RETRIES - Next retry delayed until `date --date="@$NEXT_RETRY"`..."
					          DELAY_NOTIFIED=1
                			       fi
					       sleep 1m
					    done
					 fi
                                
                                         # Purge previous backups before retry in case of insufficient disk space
			                 if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
                                            PurgeBackups "$DEST/incremental" $SOURCE_UTILISATION 0
	    	             	         else
                                            PurgeBackups "$DEST/previous" $SOURCE_UTILISATION $SOURCE_KEEP
			                 fi

	                                 AvoidBackupHours
				      else
				         LogMessage "Retry: $RSYNC_RETRY_COUNT of $RSYNC_RETRIES - Maximum number of retries exceeded for $FILE from $SOURCE at `date`." ERROR
				      fi
				   fi
			  	   if [ -e "$TMP_NAME.$SOURCE.rsync" ]; then
				      rm $TMP_NAME.$SOURCE.rsync
				   fi
				   RSYNC_RETRY_COUNT=`expr $RSYNC_RETRY_COUNT + 1`
				   RETRY_ERRORS=""
			        done


                                # Warn if there are no files in backup modified in last 48 hours
				CHECK_DEST_FILE="`echo \"$FILE\" |sed -e \"s~^.*/~~g\"`"
			        if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
                                   CHECK_DEST_DIR="$DEST/incremental/m0-w0-d1/$CHECK_DEST_FILE"
			        else
                                   CHECK_DEST_DIR="$DEST/current/$CHECK_DEST_FILE"
			        fi

				if [ -e "$CHECK_DEST_DIR" ]; then

				   # Total files/dirs
				   DEST_FILE_COUNT="`find $CHECK_DEST_DIR/. -type f |wc -l`"
				   DEST_DIR_COUNT="`find $CHECK_DEST_DIR/. -type d |grep -v \"^.\$\" |wc -l`"
			   	   TOTAL_FILE_COUNT=$[ $DEST_FILE_COUNT + $TOTAL_FILE_COUNT ]
				   TOTAL_DIR_COUNT=$[ $DEST_DIR_COUNT + $TOTAL_DIR_COUNT ]

			  	   # Recently changed files/dirs
			  	   DEST_FILE_COUNT="`find $CHECK_DEST_DIR/. -type f -ctime -$SOURCE_WARN_DAYS |wc -l`"
				   DEST_DIR_COUNT="`find $CHECK_DEST_DIR/. -type d -ctime -$SOURCE_WARN_DAYS |grep -v \"^.\$\" |wc -l`"
			 	   CHANGED_FILE_COUNT=$[ $DEST_FILE_COUNT + $CHANGED_FILE_COUNT ]
				   CHANGED_DIR_COUNT=$[ $DEST_DIR_COUNT + $CHANGED_DIR_COUNT ]

				   # Recently modified files
                                   DEST_FILE_COUNT="`find $CHECK_DEST_DIR/. -type f -mtime -$SOURCE_WARN_DAYS |wc -l`"
				   MODIFIED_FILE_COUNT=$[ $DEST_FILE_COUNT + $MODIFIED_FILE_COUNT ]
				fi


				# Compress files
				if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ] && [ "$SOURCE_COMPRESS_FILES" = "1" ] && [ -e "$DEST/incremental/m0-w0-d1/$FILE" ]; then
		                   LogMessageNL "Compressing files:  $FILE"
			     	   ZIP_ERRORS="`gzip -r --rsyncable $DEST/incremental/m0-w0-d1/$FILE 2>&1 |grep -v \"Too many levels of symbolic links\"`"
				   if [ "$ZIP_ERRORS" != "" ]; then
		                      LogMessage "Error: Unable to compress files: $FILE" ERROR
                                      echo "$ZIP_ERRORS" >> $LOGERRORS
				      ZIP_ERRORS=""
                                   fi
				fi


				# Synchronise to secondary location
				if [ "$DEST2" != "" ] && [ "$CURRENT_FILE" = "$LAST_FILE" ]; then
		                   LogMessageNL "Synchronising to secondary archive $DEST2 on `date`..."

                                   rsync $RSYNC_OPTIONS_DEST2 "$DEST/" "$DEST2/" > $TMP_NAME.$SOURCE.rsync 2>&1

				   # Errors occured?
                                   if [ "`grep -i -E \"^.*(rsync.*connection[[:space:]]+unexpectedly[[:space:]]+closed|rsync[[:space:]]+error.*some[[:space:]]+files[[:space:]]+could[[:space:]]+not[[:space:]]+be[[:space:]]+transferred|rsync[[:space:]]+warning.*some[[:space:]]+files[[:space:]]+vanished[[:space:]]+before[[:space:]]+they[[:space:]]+could[[:space:]]+be[[:space:]]+transferred|rsync[[:space:]]+error.*timeout[[:space:]]+in[[:space:]]+data[[:space:]]+send/receive|connection[[:space:]]+timed[[:space:]]+out|file[[:space:]]+has[[:space:]]+vanished:[[:space:]]+|WARNING:.*failed[[:space:]]+verification[[:space:]]+-+[[:space:]]+update[[:space:]]+discarded|rsync[[:space:]]+error:[[:space:]]+unexplained[[:space:]]+error).*\$\" $TMP_NAME.$SOURCE.rsync`" != "" ]; then
				      LogMessage "Error: Errors occured synchronising to secondary destination:  $DEST2" ERROR
				   fi
			  	   if [ -e "$TMP_NAME.$SOURCE.rsync" ]; then
				      rm $TMP_NAME.$SOURCE.rsync
				   fi
				fi

			        # Log end time
		                TIME_END="`date +%s`"
      	      	                RSYNC_TIME=`expr $RSYNC_TIME + \( $TIME_END - $TIME_START \)`
				if [ -e "/tmp/$SOURCE.start" ]; then rm /tmp/$SOURCE.start; fi

		                SOURCECOUNT=`expr $SOURCECOUNT + 1`

			        # Stop background lock file updating process
                                if [ -e $TMP_NAME.$SOURCE.updatelock ]; then
			           rm $TMP_NAME.$SOURCE.updatelock
			           sleep 1s
			        fi

                                # Remove lock and PID files
			        rm $TMP_NAME.$SOURCE.lock >/dev/null 2>&1

		                # Source end time and duration
		                SOURCE_END="`date`"
	                        SOURCE_START_INT="`date --date=\"$SOURCE_START\" +%s`"
	  	                SOURCE_END_INT="`date --date=\"$SOURCE_END\" +%s`"
	  	                SOURCE_ELAPSED=`expr $SOURCE_END_INT - $SOURCE_START_INT`
                                LogMessageNL "Backup of $FILE completed on $SOURCE_END (`expr $SOURCE_ELAPSED / \( 60 \* 60 \* 24 \)`d `expr \( $SOURCE_ELAPSED % \( 60 \* 60 \* 24 \) \) / \( 60 \* 60 \)`h `expr \( $SOURCE_ELAPSED % \( 60 \* 60 \) \) / 60`m `expr $SOURCE_ELAPSED % 60`s)."
	                     fi
			  
                             # Randomise source host in source path
	                     if [ "$FAILOVER" != "1" ]; then
                                RandomSourcePath "$SOURCEPATH" 0
	                     fi
			  fi
   	              done

		      if [ $SOURCECOUNT -gt 1 ]; then
		      
                         # Update current symlink for incremental backups
                         if [ $SOURCE_INCREMENTAL_BACKUPS -eq 1 ]; then
		            if [ -e "$DEST/incremental/m0-w0-d1" ]; then
		 	       rm $DEST/incremental/current >>$LOGERRORS 2>&1
                               ln -sf $DEST/incremental/m0-w0-d1 $DEST/incremental/current >>$LOGERRORS 2>&1
			    fi
		         fi
		      fi

		      # Unmount specified volume
                      if [ "$MOUNT" != "" ]; then
		         cd /
		         if [ "$UMOUNTCMD" = "" ]; then
                            umount $MOUNT >> $LOGFILE 2>&1
   		         else
		            $UMOUNTCMD >> $LOGFILE 2>&1
		         fi
                      fi

		      if [ $SOURCECOUNT -gt 1 ]; then
		      
                         # Compress previous archives
	                 if [ $COMPRESS_BACKUPS -eq 1 ] && [ $SOURCE_INCREMENTAL_BACKUPS -ne 1 ]; then
	                    if [ -e "$DEST/previous/$BACKUP_DATE" ] && [ -d "$DEST/previous/$BACKUP_DATE" ]; then
                               LogMessage "Compressing $DEST/previous/$BACKUP_DATE..."
	    	               cd $DEST/previous/$BACKUP_DATE
			       if [ "`pwd`" = "$DEST/previous/$BACKUP_DATE" ]; then
		                  ZIP_FILE_SIZE="`du -s \"$DEST/previous/$BACKUP_DATE\" |cut -f1`"
			       
			          if [ $COMPRESS_BACKUPS_ZIP -eq 1 ] && [ "$ZIP_FILE_SIZE" != "" ] && [ $ZIP_FILE_SIZE -gt 2000000 ]; then
			       
			              # Use gzip/bzip if file >2Gb
				      # (Too big for zip)
				      #
			              COMPRESS_BACKUPS_ZIP=0
			          fi
			       
			          if [ $COMPRESS_BACKUPS_ZIP -eq 1 ]; then
			             zip -b /tmp -r $DEST/previous/$BACKUP_DATE.zip * >> $LOGFILE 2>&1
				     if [ -e $DEST/previous/$BACKUP_DATE.zip ]; then
				        cd ..
				        rm -r $DEST/previous/$BACKUP_DATE
				     fi
			          else
		                     tar c${TAR_OPT}f $DEST/previous/$BACKUP_DATE.tar.${TAR_EXT} * >> $LOGFILE 2>&1
			          fi

	  	                  if [ -e "$DEST/previous/$BACKUP_DATE.tar.$TAR_EXT" ] && [ -e "$DEST/previous/$BACKUP_DATE" ] && [ -d "$DEST/previous/$BACKUP_DATE" ]; then
		                     rm -r $DEST/previous/$BACKUP_DATE >> $LOGFILE 2>&1
		                  fi
			       fi
		            fi
		         fi
		      fi

		      # Execute post command(s)
		      if [ "$SOURCE_POST_EXEC" != "" ]; then
		         LogMessage "Executing post-source command(s): $SOURCE_POST_EXEC"
		         PrePostExec "$RANDOMPATH" "$SOURCE_POST_EXEC"
		      fi
		      
                      # Remove lock file
		      if [ -e "$TMP_NAME.$SOURCE.lock" ]; then
		         rm $TMP_NAME.$SOURCE.lock >/dev/null 2>&1
		      fi

                      # Remove PID file
		      if [ -e "$TMP_NAME.$SOURCE.pid" ]; then
		         rm $TMP_NAME.$SOURCE.pid >/dev/null 2>&1
		      fi

                      # Remove lock fie update process file
		      if [ -e "$TMP_NAME.$SOURCE.updatelock" ]; then
		         rm $TMP_NAME.$SOURCE.updatelock >/dev/null 2>&1
		      fi

                      # Remove status file
		      if [ -e "$TMP_NAME.$SOURCE.status" ]; then
		         rm $TMP_NAME.$SOURCE.status >/dev/null 2>&1
		      fi
		   fi
                fi
	     fi


	     # Warn if no files changed/modified
             if [ "$MODIFIED_FILE_COUNT" = "" ] || [ "$MODIFIED_FILE_COUNT" = "0" ]; then
                LogMessage "Warning: No files have been modified in the past $SOURCE_WARN_DAYS day(s)." WARNING
             fi


             # Remove lock file if it still exists (Backup skipped)
             if [ -e "$TMP_NAME.$SOURCE.lock" ]; then
                rm $TMP_NAME.$SOURCE.lock >/dev/null 2>&1
             fi

             # Remove PID file
             if [ -e "$TMP_NAME.$SOURCE.pid" ]; then
                rm $TMP_NAME.$SOURCE.pid >/dev/null 2>&1
             fi

             # Remove lock fie update process file
             if [ -e "$TMP_NAME.$SOURCE.updatelock" ]; then
                rm $TMP_NAME.$SOURCE.updatelock >/dev/null 2>&1
             fi

             # Remove status file
             if [ -e "$TMP_NAME.$SOURCE.status" ]; then
                rm $TMP_NAME.$SOURCE.status >/dev/null 2>&1
             fi
                      
	     # Remove start time file
	     if [ -e "$TMP_NAME.$SOURCE.start" ]; then
	        rm $TMP_NAME.$SOURCE.start >/dev/null 2>&1
	     fi


             # Update next number
             if [ "$SOURCE" != "" ] && [ -e "/tmp/.mbackuprsync.$SOURCE.next" ]; then
                ItmNo="`cat /tmp/.mbackuprsync.$SOURCE.next`"
             else
		ItmNo=""
             fi
             if [ "$ItmNo" = "" ]; then ItmNo="1"; fi
             echo "$[ $ItmNo + 1 ]" > /tmp/.mbackuprsync.$SOURCE.next


             # Log finish time
  	     END_TIME="`date`"
             LogMessage "Summary for backup of '$SOURCEFILES' from $RANDOMPATH ($SOURCE) to $DEST..." GENERAL LOG NOSTATUS NOTIMEDATE NOSOURCE NLNL E

	     # Summary:  Script start time/date
	     LogMessage "Start time: \t$START_TIME" GENERAL LOG NOSTATUS NOTIMEDATE NOSOURCE NL E

	     # Summary:  Time spent rsync'ing files
	     LogMessage "Rsync time: \t`expr $RSYNC_TIME / \( 60 \* 60 \* 24 \)`d `expr \( $RSYNC_TIME % \( 60 \* 60 \* 24 \) \) / \( 60 \* 60 \)`h `expr \( $RSYNC_TIME % \( 60 \* 60 \) \) / 60`m `expr $RSYNC_TIME % 60`s" GENERAL LOG NOSTATUS NOTIMEDATE NOSOURCE "" E

	     # Determine total time
       	     START_INT="`date --date=\"$START_TIME\" +%s`"
  	     END_INT="`date --date=\"$END_TIME\" +%s`"
	     ELAPSED_TIME=`expr $END_INT - $START_INT`
	     
             # Determine script processing time
	     SCRIPT_TIME=`expr $ELAPSED_TIME - $RSYNC_TIME`

	     # Summary:  Time spent processing script
	     LogMessage "Script time:\t`expr $SCRIPT_TIME / \( 60 \* 60 \* 24 \)`d `expr \( $SCRIPT_TIME % \( 60 \* 60 \* 24 \) \) / \( 60 \* 60 \)`h `expr \( $SCRIPT_TIME % \( 60 \* 60 \) \) / 60`m `expr $SCRIPT_TIME % 60`s" GENERAL LOG NOSTATUS NOTIMEDATE NOSOURCE "" E

	     # Summary:  Total overall time
	     LogMessage "Total time: \t`expr $ELAPSED_TIME / \( 60 \* 60 \* 24 \)`d `expr \( $ELAPSED_TIME % \( 60 \* 60 \* 24 \) \) / \( 60 \* 60 \)`h `expr \( $ELAPSED_TIME % \( 60 \* 60 \) \) / 60`m `expr $ELAPSED_TIME % 60`s" GENERAL LOG NOSTATUS NOTIMEDATE NOSOURCE NL E

	     # Summary:  Finish time/date
	     LogMessage "Finish time:\t$END_TIME" GENERAL LOG NOSTATUS NOTIMEDATE NOSOURCE "" E

      	     # Generate summary/errors
  	     if [ -e "$LOGFILE" ]; then
	  
	        # Get errors/warnings
	        grep -E -i "((bunzip2?|bzip2?|error|retry|gunzip|gzip|mv|rm|rsync|tar|warning):|failed\\sto\\sopen|file\\shas\\svanished|permission\\sdenied|send_files\\sfailed|unable\\sto\\sbackup|timed\\sout)" $LOGFILE |grep -v -E -i "^.*rsync.*mkdir.*previous.*failed.*File.*exist" > $LOGERRORS
	        if [ -e "$LOGERRORS" ] && [ `wc -l $LOGERRORS |cut -f1 -d' '` -lt 1 ]; then
	           rm $LOGERRORS > /dev/null 2>&1
	        fi


                # Summary:  Backup session
	        echo "Backup session summary:" >> $LOGSUMMARY
	        echo "~~~~~~~~~~~~~~~~~~~~~~~" >> $LOGSUMMARY
	        echo -e "Source Name: \t$SOURCE" >> $LOGSUMMARY
	        echo -e "Source Path: \t$SOURCEPATH" >> $LOGSUMMARY
	        echo -e "Source Files:\t$SOURCEFILES" >> $LOGSUMMARY
	        echo -e "Destination: \t$DEST" >> $LOGSUMMARY
	        echo -e "File count:  \t$TOTAL_FILE_COUNT file(s)\t$TOTAL_DIR_COUNT dir(s)" >> $LOGSUMMARY
	        echo -e "Updated:     \t$CHANGED_FILE_COUNT file(s)\t$CHANGED_DIR_COUNT dir(s)\t" >> $LOGSUMMARY
	        echo -e "Modified:    \t$MODIFIED_FILE_COUNT file(s)" >> $LOGSUMMARY
		echo -e "Time period: \t$SOURCE_WARN_DAYS day(s)" >> $LOGSUMMARY
   

	        # Summary:  Transfer
                grep -E "^[[:digit:][:space:]:-]\+:[[:space:]]\+(Backing up .+\.\.\.|Downloading file list from|[0-9]+Kb reduced to [0-9]+Kb|Purging .+\.\.\.|Disk utilisation of .+ is greater than|Removing old log file .+\.\.\.)" $LOGFILE |sed -e "s/^\(Backing \)/\n\1/g" -e "s/^\(Backup of \)/\n\1/g" >> $LOGSUMMARY

                TOTAL_SENT=0
		TOTAL_RCVD=0
		TOTAL_RATE=0
		TOTAL_SIZE=0
		TOTAL_SPEED=0
		TOTAL_FILES=0
		
  	  	COUNTER=0
		for VAL in `cat $LOGFILE |grep -E "^(sent [0-9]+ byte|total size is [0-9]+)" |sed -e "s/[^0-9[:space:]]//g" |sed -e "s/^[[:space:]]\+//g" |sed -e "s/[[:space:]]\+/ /g"`; do
		    VAL="`echo \"$VAL\" |sed -e \"s/^0\{1,2\}//g\" |sed -e \"s/^000\+[0-9]\+/0/g\"`"
		    if [ "$VAL" = "" ]; then VAL=0; fi
		       
		    case $COUNTER in
		         0)
		            TOTAL_SENT=`expr $TOTAL_SENT + $VAL`
			    ;;
		  	 1)
			    TOTAL_RCVD=`expr $TOTAL_RCVD + $VAL`
			    ;;
			 2)
			    TOTAL_RATE=`expr $TOTAL_RATE + $VAL`
			    ;;
			 3)
			    TOTAL_SIZE=`expr $TOTAL_SIZE + $VAL`
			    ;;
			 4)
			    TOTAL_SPEED=`expr $TOTAL_SPEED + $VAL`
                            TOTAL_FILES=`expr $TOTAL_FILES + 1`
			    ;;
		    esac

		    COUNTER=`expr $COUNTER + 1`
		    if [ $COUNTER -gt 4 ]; then COUNTER=0; fi
	  	done

                if [ $TOTAL_FILES -gt 0 ]; then
  	  	   echo -e "\nRsync Transfer:" >> $LOGSUMMARY
		   echo      "~~~~~~~~~~~~~~~" >> $LOGSUMMARY
		   echo -e "Bytes Sent:  \t$TOTAL_SENT" >> $LOGSUMMARY
	  	   echo -e "Bytes Rcvd:  \t$TOTAL_RCVD" >> $LOGSUMMARY
		    
		   RATE_DECIMAL=`expr \( $TOTAL_RATE / $TOTAL_FILES \) / 100`
		   RATE_FRACTION=`expr \( $TOTAL_RATE / $TOTAL_FILES \) % 100`
		   if [ $RATE_FRACTION -lt 10 ]; then RATE_FRACTION="0$RATE_FRACTION"; fi
		   echo -e "Avg. Rate:   \t$RATE_DECIMAL.$RATE_FRACTION bytes/sec" >> $LOGSUMMARY
		    
  		   echo -e "Total Xfer:  \t`expr $TOTAL_SENT + $TOTAL_RCVD`" >> $LOGSUMMARY
  		   echo -e "Total Size:  \t$TOTAL_SIZE" >> $LOGSUMMARY
		  
		   SPEED_DECIMAL=`expr \( $TOTAL_SPEED / $TOTAL_FILES \) / 100`
		   SPEED_FRACTION=`expr \( $TOTAL_SPEED / $TOTAL_FILES \) % 100`
		   if [ $SPEED_FRACTION -lt 10 ]; then SPEED_FRACTION="0$SPEED_FRACTION"; fi
		   echo -e "Avg. Speedup:\t$SPEED_DECIMAL.$SPEED_FRACTION" >> $LOGSUMMARY
		fi


                # Summary:  Duration
                echo -e "\nDuration:" >> $LOGSUMMARY
                echo      "~~~~~~~~~" >> $LOGSUMMARY
                grep -E "^[[:space:]]*(Start|Finish|Total|Compr\\.|Decmp\\.|Rsync|Script) time:" $LOGFILE >> $LOGSUMMARY


                # Summary:  Archives purged?
	        PURGED="`cat $LOGFILE |grep -E -i \"Purge:\"`"
                if [ "$PURGED" != "" ]; then
                   echo -e "\n\nPurge:" >> $LOGSUMMARY
                   echo      "~~~~~~" >> $LOGSUMMARY
		   echo "$PURGED" >> $LOGSUMMARY
		fi
		

	        # Show summary?
                if [ $SHOW_SUMMARY -eq 1 ] && [ -e "$LOGSUMMARY" ]; then
	   	   if [ $ERRORS_ONLY -eq 0 ]; then echo ""; fi
                   cat $LOGSUMMARY
                fi


		# Errors/warnings/others count
                if [ -e "$LOGERRORS" ]; then
		   ERRORS_GREP="(warning:|retry:|file\\shas\\svanished)"
		   WARNINGS_GREP="warning:"
		   MISC_GREP="(retry:|file\\shas\\svanished)"

		   ERRORS="`cat $LOGERRORS |grep -v -E -i \"$ERRORS_GREP\" |wc -l |cut -d' ' -f1`"
		   WARNINGS="`cat $LOGERRORS |grep -E -i \"$WARNINGS_GREP\" |wc -l |cut -d' ' -f1`"
		   MISC="`cat $LOGERRORS |grep -E -i \"$MISC_GREP\" |wc -l |cut -d' ' -f1`"

                   if [ "$ERRORS" = "" ]; then ERRORS=0; fi
                   if [ "$WARNINGS" = "" ]; then WARNINGS=0; fi
                   if [ "$MISC" = "" ]; then MISC=0; fi

		   if [ $ERRORS -eq 1 ]; then ERRORS_TITLE="error"; else ERRORS_TITLE="errors"; fi
		   if [ $WARNINGS -eq 1 ]; then WARNINGS_TITLE="warning"; else WARNINGS_TITLE="warnings"; fi
		   if [ $MISC -eq 1 ]; then MISC_TITLE="miscellaneous"; else MISC_TITLE="miscellaneous"; fi
		fi


                # Show errors/warnings summary?
                if [ $SHOW_ERRORS -eq 1 ] && [ -e "$LOGERRORS" ] && [ `wc -l $LOGERRORS |cut -d' ' -f1` -gt 0 ]; then

                   # Errors
	           if [ $ERRORS_ONLY -eq 0 ]; then
                      if [ $ERRORS -gt 0 ]; then
 		         echo -e "\nErrors:"
		         echo      "~~~~~~~"
                         cat $LOGERRORS |grep -v -E -i "$ERRORS_GREP"
		         echo ""
                      fi
		   else
		      echo -e "$SOURCE errors/warnings:\n"
                      cat $LOGERRORS
                   fi

                   # Warnings
	           if [ $ERRORS_ONLY -eq 0 ] && [ $WARNINGS -gt 0 ]; then
		      echo -e "\nWarnings:"
		      echo      "~~~~~~~~~"
		   
                      cat $LOGERRORS |grep -E -i "$WARNINGS_GREP"
   		      echo ""
                   fi

                   # Others
	           if [ $ERRORS_ONLY -eq 0 ] && [ $MISC -gt 0 ]; then
		      echo -e "\nMiscellaneous:"
		      echo      "~~~~~~~~~~~~~~"
		   
                      cat $LOGERRORS |grep -E -i "$MISC_GREP"
   		      echo ""
                   fi
		else
		   if [ $SHOW_ERRORS -eq 1 ]; then echo ""; fi
                fi

	        # E-mail summary and/or errors/warnings?
	        if [ $EMAIL_SUMMARY -eq 1 ] || [ $EMAIL_ERRORS -eq 1 ]; then
	           if [ "$EMAIL_RCPT" != "" ] && [ "$EMAIL_FROM" != "" ]; then
	  	      if [ -e $TMP_EMAIL ]; then rm $TMP_EMAIL >/dev/null 2>&1; fi
		      NEWLINE=0
		
		      # Summary
                      if [ $EMAIL_SUMMARY -eq 1 ] && [ -e "$LOGSUMMARY" ]; then
                         cat $LOGSUMMARY >> $TMP_EMAIL
		         NEWLINE=1
                      fi

                      # Errors
	  	      if [ $NEWLINE -eq 1 ]; then echo "" >> $TMP_EMAIL; fi
                      if [ $EMAIL_ERRORS -eq 1 ] && [ -e "$LOGERRORS" ]; then
                         if [ $ERRORS -gt 0 ]; then
		            if [ $NEWLINE -eq 1 ]; then echo "" >> $TMP_EMAIL; fi
		            echo -e "\nErrors:" >> $TMP_EMAIL
		            echo      "~~~~~~~" >> $TMP_EMAIL
                            cat $LOGERRORS |grep -v -E -i "$ERRORS_GREP" >> $TMP_EMAIL
			    NEWLINE=1
			 fi

                         if [ $WARNINGS -gt 0 ]; then
		            if [ $NEWLINE -eq 1 ]; then echo "" >> $TMP_EMAIL; fi
		            echo -e "\nWarnings:" >> $TMP_EMAIL
		            echo      "~~~~~~~~~" >> $TMP_EMAIL
                            cat $LOGERRORS |grep -E -i "$WARNINGS_GREP" >> $TMP_EMAIL
			    NEWLINE=1
			 fi

                         if [ $MISC -gt 0 ]; then
		            if [ $NEWLINE -eq 1 ]; then echo "" >> $TMP_EMAIL; fi
		            echo -e "\nMiscellaneous:" >> $TMP_EMAIL
		            echo      "~~~~~~~~~~~~~~" >> $TMP_EMAIL
                            cat $LOGERRORS |grep -E -i "$MISC_GREP" >> $TMP_EMAIL
			    NEWLINE=1
			 fi
		      fi

		      # Send E-mail
		      if [ -e "$TMP_EMAIL" ]; then
		         eval MAIL_SUBJECT=\"$EMAIL_SUBJECT\"
		         mail -a "From: <$EMAIL_FROM>" -a "To: <$EMAIL_RCPT>" -a "Reply-to: <$EMAIL_FROM>" -s "$MAIL_SUBJECT" $EMAIL_RCPT < $TMP_EMAIL
		         rm $TMP_EMAIL >/dev/null 2>&1
		      fi
		   fi
		fi
             fi
	  fi
       fi
    fi
done
exit 0
