#!/bin/ksh
#
# $Id: yarss,v 1.8 2005/04/17 12:55:07 jerome Exp $
# ----------------------------------------------------------------------
# AlternC - Web Hosting System
# Copyright (C) 2003 by the AlternC Development Team.
# http://alternc.org
# ----------------------------------------------------------------------
# Based on:
# Valentin Lacambre's web hosting softwares: http://altern.org
# Mike Rubel's rsync snapshot page: http://www.mikerubel.org/computers/rsync_snapshots
# ----------------------------------------------------------------------
# LICENSE
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License (GPL)
# 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.
#
# To read the license please visit http://www.gnu.org/copyleft/gpl.html
# ----------------------------------------------------------------------
# Original Author of file: Jerome Moinet <jerome@moinet.org>
# Purpose of file: - makes incremental snapshot backups using rsync and hard links.
#                  - Must be used with a central backup server that will get
#                    data on remote servers to store them on local file system
#                  - Can be used to backup local data
# Note: this shell is intended to be used on debian woody
#
# ==> This is a beta version ! <==
#
# This shell must be called by cron, or else you will loose your snapshot period.
# Type "man yarss" to have more information about this shell.
#
# Please feel free to send feedbacks, enhancements or new translations to jerome@moinet.org
#
# ----------------------------------------------------------------------
#
PATH=""
PROG_NAME=`/usr/bin/basename $0`
[ "$?" != 0 ] && { echo "basename error, exiting." ; exit 1 ; }
# if true, gives you some execution time traces in your log file
TIME_TRACE=false
RSYNC_OPTIONS="-Wa --numeric-ids --delete --timeout 10000"
NB_ARGS=7
MAX_ROTATE=512
ADD_SLASH=""
LOCKFILE_TIMEOUT=60
LOCKFILE_TIMEOUT_COUNT=67
set -A SNAPSHOT_TYPE hourly daily weekly monthly


#-------------------------
function set_messages
#-------------------------
{
	# Language-dependent messages.
	# Uses gettext and mo files.
	# Don't change these messages, change the .po file instead.
	export TEXTDOMAINDIR=/usr/share/locale
	export TEXTDOMAIN=yarss

	DATE=`$date`
	HELP=$($gettext "Makes incremental backups.")
	USAGE=`$printf "$($gettext "Usage : %s <%s|%s|%s|%s> <snapshots number> <unix user on remote host> <remote host> <remote directory to backup> <local directory where to backup> <log file> <\"rsync exclude path 1\" \"rsync exclude path 2\" ... \"rsync exclude path n\">")" $PROG_NAME ${SNAPSHOT_TYPE[0]} ${SNAPSHOT_TYPE[1]} ${SNAPSHOT_TYPE[2]} ${SNAPSHOT_TYPE[3]}`
	NOT_FOUND_MSG=$($gettext "does not exist.")
	NON_NUM_MSG=$($gettext "Snapshot number must be a number.")
	LOW_NUM_MSG=$($gettext "Snapshot number must be greater than zero.")
	HIGH_NUM_MSG=`$printf "$($gettext "Snapshot number is too hight (> %s).")" ${MAX_ROTATE}`
	BAD_ARG1_MSG=`$printf "$($gettext "First argument must be \"%s\", \"%s\", \"%s\" or \"%s\".")" ${SNAPSHOT_TYPE[0]} ${SNAPSHOT_TYPE[1]} ${SNAPSHOT_TYPE[2]} ${SNAPSHOT_TYPE[3]}`
	TIME_NOT_FOUND_MSG=`$printf "$($gettext "You have to install the time package to use %s in time trace mode: apt-get install time")" $PROG_NAME`
	IPROUTE_NOT_FOUND_MSG=`$printf "$($gettext "You have to install the iproute package to use %s: apt-get install iproute")" $PROG_NAME`
	LOCKFILE_NOT_FOUND_MSG=`$printf "$($gettext "You have to install the lockfile-progs package to use %s: apt-get install lockfile-progs")" $PROG_NAME`
	RSYNC_NOT_FOUND_MSG=`$printf "$($gettext "You have to install the rsync package to use %s: apt-get install rsync")" $PROG_NAME`
	LOCKFILE_CREATION_FAILED_1=`$printf "$($gettext "%s is allready running. Lockfile creation will be retried for %s minutes with an one minute interval.")" $PROG_NAME $LOCKFILE_TIMEOUT`
	LOCKFILE_CREATION_FAILED_2=`$printf "$($gettext "Enable to create lockfile after %s minutes. Delete %s.lock if you are sure that %s is not running.")" $LOCKFILE_TIMEOUT $LOCK_FILE $PROG_NAME`
	NON_ROOT_MSG=$($gettext "You have to be root (uid 0) to launch this shell.")
	MISSING_PROG=$($gettext "Enable to execute")
	HOST_UNREACHABLE=`$printf "$($gettext "Enable to contact %s")" $REMOTE_HOST`
	NON_SSH_CONNECT=`$printf "$($gettext "ssh %s@%s: can't connect.")" ${REMOTE_USER} ${REMOTE_HOST}`
	NON_REMOTE_DIR=`$printf "$($gettext "\"%s:%s\" does not exist or is not a directory.")" ${REMOTE_HOST} ${REMOTE_DIR}`
	LOCAL_DIR_IS_SLASH=$($gettext "Local directory cannot be /")
	NO_LOCAL_DIR=`$printf "$($gettext "\"%s\" does not exist or is not a directory.")" $LOCAL_DIR`
	NO_LOG_FILE_DIR=`$printf "$($gettext "%s is in a non existant directory.")" $LOG_FILE`
	SAME_DIR=$($gettext "Local and remote directories are identical, wich is not possible when executing a local backup.")
	EXCUTING=$($gettext "Executing")
	BEGIN_MSG=`$printf "$($gettext -e -- "-----------------------------------\nBegining backup from \"%s:%s\" to \"%s\" on %s\nTIME_TRACE=%s, type: %s")" ${REMOTE_HOST} ${REMOTE_DIR} ${LOCAL_DIR} "$DATE" ${TIME_TRACE} ${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}`

	END_MSG=`$printf "$($gettext "Ending backup from \"%s:%s\" to \"%s\" on %s")" ${REMOTE_HOST} ${REMOTE_DIR} ${LOCAL_DIR} "$DATE"`
	NON_DEBIAN_MSG=$($gettext "You don't seem to be using Debian GNU Linux V3.0 (woody), exiting.")
}


# Be sure to use the right programs on Debian
# and be sure they are there
dpkg=/usr/bin/dpkg
awk=/usr/bin/awk
grep=/bin/grep
id=/usr/bin/id
rm=/bin/rm
sed=/bin/sed
cut=/usr/bin/cut
ssh=/usr/bin/ssh
ping=/bin/ping
dirname=/usr/bin/dirname
uname=/bin/uname
mkdir=/bin/mkdir
expr=/usr/bin/expr
touch=/usr/bin/touch
cp=/bin/cp
date=/bin/date
ls=/bin/ls
cat=/bin/cat
printf=/usr/bin/printf
gettext=/usr/bin/gettext
lockfilecreate=/usr/bin/lockfile-create
lockfiletouch=/usr/bin/lockfile-touch
lockfileremove=/usr/bin/lockfile-remove

# Must have date, gettext and printf first to display error messages
[ -x "$date" ] || { echo "Cannot execute $date"; exit 1 ; }
[ -x "$gettext" ] || { echo "Cannot execute $gettext"; exit 1 ; }
[ -x "$printf" ] || { echo "Cannot execute $printf"; exit 1 ; }

set_messages
for i in $dpkg $awk $grep $id $rm $sed $cut $ssh $ping $dirname $uname $mkdir $expr $touch $cp $ls $cat $lockfilecreate $lockfiletouch $lockfileremove; do
	[ -x "$i" ] || { $printf "$MISSING_PROG" ; echo " ${i}"; exit 1 ; }
done


#-------------------------
function my_mv
#-------------------------
{
	# mv with modification date conservation
	REF="/tmp/${PROG_NAME}.$$"
	$touch -r "$1" "$REF"
	/bin/mv "$1" "$2"
	$touch -r "$REF" "$2"
	$rm "$REF"
}
mv=my_mv


#-------------------------
function trap_EXIT
#-------------------------
{
	# Does some cleaning
	[ -f "$LOCK_FILE.lock" ] && $lockfileremove $LOCK_FILE
	$rm -f "/tmp/${PROG_NAME}.$$"
	exit ${1:-1}
}
trap trap_EXIT INT KILL TERM QUIT ABRT STOP HUP


#-------------------------
# Main
#-------------------------
# Must be root
[ "`$id -u`" != "0" ] && { $printf "$NON_ROOT_MSG\n" ; exit 1 ; }

# Handle -h and --help flags
[ "$1" = "-h" ] || [ "$1" = "--help" ] && { $printf "$HELP\n" ; $printf "$USAGE\n" ; exit 0 ; }

# Must have minimum $NB_ARGS parameters
[ "$#" -lt ${NB_ARGS} ] && { $printf "$USAGE\n" ; exit 1 ; }

# Parameter 1 must be in SNAPSHOT_TYPE array
[ "$1" != "${SNAPSHOT_TYPE[0]}" -a "$1" != "${SNAPSHOT_TYPE[1]}" -a "$1" != "${SNAPSHOT_TYPE[2]}" -a "$1" != "${SNAPSHOT_TYPE[3]}" ] && { $printf "$BAD_ARG1_MSG\n" ; exit 1 ; }

# Parameter 2 must be numeric
[ `echo "$2" | $grep -c [^0-9]` != 0 ] && { $printf "$NON_NUM_MSG\n" ; exit 1 ; }

# Parameter 2 must be > 0
[ "$2" -le 0 ] && { $printf "$LOW_NUM_MSG\n" ; exit 1 ; }

# Parameter 2 must not be too high
[ "$2" -ge ${MAX_ROTATE} ] && { $printf "$HIGH_NUM_MSG\n" ; exit 1 ; }

# lockfile-progs has to be installed
#[ `$dpkg -l lockfile-progs | $grep -c "^ii"` -lt 1 ]  && { $printf "$LOCKFILE_NOT_FOUND_MSG\n" ; exit 1 ; }

# Rename arguments
# TODO: maybe test lenght of arguments...
case $1 in
	${SNAPSHOT_TYPE[0]})
		SNAPSHOT_INDEX=0
		;;
	${SNAPSHOT_TYPE[1]})
		SNAPSHOT_INDEX=1
		;;
	${SNAPSHOT_TYPE[2]})
		SNAPSHOT_INDEX=2
		;;
	${SNAPSHOT_TYPE[3]})
		SNAPSHOT_INDEX=3
		;;
	*)
		echo "Bad snapshot type while renaming arguments."
		exit 1
		;;
esac

SNAPSHOT_NUMBER=`$expr ${2} - 1`
REMOTE_USER=$3
REMOTE_HOST=$4
REMOTE_DIR=`$dirname "${5}/null" | $sed s/"\/*"/"\/"/`
LOCAL_DIR=`$dirname "${6}/null" | $sed s/"\/*"/"\/"/`
LOG_FILE=$7
LOCK_FILE=/var/run/$PROG_NAME.$REMOTE_HOST

# need to reset messages after renaming arguments
set_messages

# Prevents executing more than one shell at the same time for one host, usefull if precedent backup is not yet finished.
# TODO: make --retry timout a shell variable (compute to do...) => LOCKFILE_TIMEOUT
$lockfilecreate --retry 1 $LOCK_FILE
if [ $? != 0 ] ; then
	$printf "$LOCKFILE_CREATION_FAILED_1\n"
	# --retry: give $LOCKFILE_TIMEOUT minutes to other instance to end
	$lockfilecreate --retry $LOCKFILE_TIMEOUT_COUNT $LOCK_FILE
	if [ $? != 0 ] ; then
		$printf "$LOCKFILE_CREATION_FAILED_2\n"
		exit 1
	fi
fi
$lockfiletouch $LOCK_FILE &
BADGER="$!"

# time has to be installed if execution time traces are on
[ "$TIME_TRACE" = "true" ] && ! [ -x "/usr/bin/time" ]  && { $printf "$TIME_NOT_FOUND_MSG\n" ; kill "${BADGER}" ; trap_EXIT 1  ; } || time=/usr/bin/time
# Initializing time command
[ "$TIME_TRACE" = "true" ] && TIME_EXEC="$time -p -a -o \"$LOG_FILE\"" || TIME_EXEC=""

# iproute has to be installed
! [ -x /bin/ip ] && { $printf "$IPROUTE_NOT_FOUND_MSG\n" ; kill "${BADGER}" ; trap_EXIT 1 ; } || ip=/bin/ip

# rsync has to be installed
! [ -x /usr/bin/rsync ] && { $printf "$RSYNC_NOT_FOUND_MSG\n" ; kill "${BADGER}" ; trap_EXIT 1 ; } || rsync=/usr/bin/rsync

# Local directory must exist
[ -d "$LOCAL_DIR" ] || { $printf "$NO_LOCAL_DIR\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }

# Must know if we are in the lowest snapshot level, wich means that for a snapshot type, the precedent snapshot type does or does not exists
# If not in the lower level, we don't execute rsync, and just do a cp -fal from the highest precedent level. We also don't have to test host or ssh access.
IS_FIRST_SNAPSHOT_LEVEL=false
INDEX_PREC=`$expr ${SNAPSHOT_INDEX} - 1`
MAX_INDEX=0
if [ "${SNAPSHOT_INDEX}" = "0" ] ; then
	IS_FIRST_SNAPSHOT_LEVEL=true
else
	if [ -d "${LOCAL_DIR}/${SNAPSHOT_TYPE[${INDEX_PREC}]}.0" ] ; then
		for i in `$ls -d "${LOCAL_DIR}/${SNAPSHOT_TYPE[${INDEX_PREC}]}."* | $sed s/" "/"@@@@@@"/g` ; do
			DIR=`echo $i | $sed s/"@@@@@@"/" "/g`
			MAX_INDEX_TMP=`echo "$DIR" | $awk -F . {'print $NF'}`
			[ "$MAX_INDEX_TMP" -gt "$MAX_INDEX" ] && MAX_INDEX=$MAX_INDEX_TMP
		done
	else
		IS_FIRST_SNAPSHOT_LEVEL=true
	fi
fi

# Is remote host reachable?
if [ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] ; then
	$ping -c 1 $REMOTE_HOST > /dev/null 2>&1
	[ "$?" != "0" ] && { $printf "$HOST_UNREACHABLE\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }
fi

# Are we backuping from localhost to localhost?
IS_IN_LOCAL_MODE=false
REMOTE_IP=`$ping -c1 $REMOTE_HOST | $grep "^PING" | $awk '{z=length($3) ; printf("%s\n", substr($3, 2, z-3))}'`
for LOCAL_IP in `$ip addr | $grep "inet " | $awk {'print $2'} | $cut -d"/" -f1` ; do
	[ "$LOCAL_IP" = "$REMOTE_IP" ] && IS_IN_LOCAL_MODE=true
done
#IS_IN_LOCAL_MODE=false

# If remote host is in fact localhost, make sure local and remote directories are not identical
[ "$IS_IN_LOCAL_MODE" = "true" ] && [ "$LOCAL_DIR" = "$REMOTE_DIR" ] && { $printf "$SAME_DIR\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }

# Log file must be in an existing directory
[ -d "`$dirname "$LOG_FILE"`" ] || { $printf "$NO_LOG_FILE_DIR\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }

# local directory must not be / (because of the rsync --delete option)
[ "$LOCAL_DIR" = "/" ] && { $printf "$LOCAL_DIR_IS_SLASH\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }

# Make sure we have a non password protected ssh access to remote host with specified user if not in local mode and in first level snapshot
if [ "$IS_IN_LOCAL_MODE" = "false" ] && [ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] ; then
	$ssh -o BatchMode=yes ${REMOTE_USER}@${REMOTE_HOST} "/bin/true"
	[ "$?" != "0" ] && { $printf "$NON_SSH_CONNECT\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }
fi

# Make sur remote directory exists
if [ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] ; then
	if [ "$IS_IN_LOCAL_MODE" = "false" ] ; then
		[ `$ssh -o BatchMode=yes ${REMOTE_USER}@${REMOTE_HOST} "! [ -d \"${REMOTE_DIR}\" ] && echo 1 || echo 0"` = "1" ] && { $printf "$NON_REMOTE_DIR\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }
	else
		[ -d "${REMOTE_DIR}" ] || { $printf "$NON_REMOTE_DIR\n" ; kill "${BADGER}" ; trap_EXIT 1 ; }
	fi
fi

# Parse last arguments as rsync --exclude options
shift ${NB_ARGS}
while [ $# -gt 0 ]
do
	RSYNC_EXCLUDE="--exclude \"${1}\" $RSYNC_EXCLUDE"
	shift
done

# TODO: if remote host is in fact localhost, make sure local directory is not a remote directory sub-dir, and if yes, verify that it is or not in --exclude rsync parameters

# Does the stuff...

# Give some start message
set_messages
$printf "$BEGIN_MSG\n\n" >> "$LOG_FILE"

# Rotate/create backup directories
[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $rm -fr \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.tmp\"" >> $LOG_FILE
eval $TIME_EXEC $rm -fr "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.tmp"

i=$SNAPSHOT_NUMBER
while [ "$i" -ge 0 ] ; do
	[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $mkdir -p \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${i}\"" >> "$LOG_FILE"
	$mkdir -p "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${i}"
	i=`$expr $i - 1`
done

if [ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] ; then
	[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $mv \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${SNAPSHOT_NUMBER}\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.tmp\"" >> "$LOG_FILE"
	$mv "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${SNAPSHOT_NUMBER}" "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.tmp"
else
	[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $rm -fr \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${SNAPSHOT_NUMBER}\"" >> "$LOG_FILE"
	[ "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${SNAPSHOT_NUMBER}" = "/." ] && { echo "PROBLEM: tried to rm -fr /. wich is not good. Exiting" ; kill "${BADGER}" ; trap_EXIT 1 ; }
	eval $TIME_EXEC $rm -fr \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${SNAPSHOT_NUMBER}\"
fi

i=`$expr $SNAPSHOT_NUMBER - 1`
while [ "$i" -ge 0 ] ; do
	NEXT_SNAPSHOT=`$expr $i + 1`
	[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $mv \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${i}\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${NEXT_SNAPSHOT}\"" >> "$LOG_FILE"
	$mv "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${i}" "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.${NEXT_SNAPSHOT}"
	i=`$expr $i - 1`
done

if [ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] ; then
	[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $mv \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.tmp\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\"" >> "$LOG_FILE"
	$mv "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.tmp" "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0"
fi

# Make hard links
if [ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] ; then
	if [ -d "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.1" ] && [ "$SNAPSHOT_NUMBER" -gt 0 ] ; then
		[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $cp -fal \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.1/.\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\"" >> "$LOG_FILE"
		eval $TIME_EXEC $cp -fal \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.1/.\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\"
	fi
else
	if [ -d "${LOCAL_DIR}/${SNAPSHOT_TYPE[${INDEX_PREC}]}.${MAX_INDEX}" ] ; then
		[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $cp -fal \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${INDEX_PREC}]}.${MAX_INDEX}/.\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\"" >> "$LOG_FILE"
		eval $TIME_EXEC $cp -fal \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${INDEX_PREC}]}.${MAX_INDEX}/.\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\"
	fi
fi

# Execute rsync if in the lowest snapshot level
if [ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] ; then
	[ "${REMOTE_DIR}" = "/" ] || ADD_SLASH="/"
	if [ "$IS_IN_LOCAL_MODE" = "true" ] ; then
		[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $rsync $RSYNC_OPTIONS $RSYNC_EXCLUDE \"${REMOTE_DIR}${ADD_SLASH}\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\"" >> "$LOG_FILE" 2>&1
		eval $TIME_EXEC $rsync $RSYNC_OPTIONS $RSYNC_EXCLUDE \"${REMOTE_DIR}${ADD_SLASH}\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\" >> "$LOG_FILE"
	else
		[ "$TIME_TRACE" = "true" ] && echo "${EXCUTING} $rsync -e $ssh $RSYNC_OPTIONS $RSYNC_EXCLUDE ${REMOTE_USER}@${REMOTE_HOST}:\"${REMOTE_DIR}${ADD_SLASH}\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\"" >> "$LOG_FILE"
		REMOTE_DIR=`echo $REMOTE_DIR | $sed s/" "/"\\\\\\ "/g`
		eval $TIME_EXEC $rsync -e $ssh $RSYNC_OPTIONS $RSYNC_EXCLUDE ${REMOTE_USER}@${REMOTE_HOST}:\"${REMOTE_DIR}${ADD_SLASH}\" \"${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0\" >> "$LOG_FILE" 2>&1
	fi
fi

# Touch last backup to have real backup date, only if in the lowest snapshot level
[ "$IS_FIRST_SNAPSHOT_LEVEL" = "true" ] && $touch "${LOCAL_DIR}/${SNAPSHOT_TYPE[${SNAPSHOT_INDEX}]}.0"

# Give some end message
set_messages
$printf "\n$END_MSG\n" >> "$LOG_FILE"

# Cleanly quit and release lock file
kill "${BADGER}"
trap_EXIT 0

