Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 69 additions & 26 deletions timemachine
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ SSH_ARGS="-oStrictHostKeyChecking=no -oLogLevel=QUIET -q"
################################################################################

print_usage() {
echo "Usage: ${MY_NAME} [-vd] <source> <dest> -- [rsync opts]"
echo "Usage: ${MY_NAME} [-vd] <source>... <dest> -- [rsync opts]"
echo

echo " ${MY_NAME} [-vdpi] <source> <host>:<dest> -- [rsync opts]"
echo " ${MY_NAME} [-vdpi] <source> <user>@<host>:<dest> -- [rsync opts]"
echo " ${MY_NAME} [-vdpi] <source> <ssh-alias>:<dest> -- [rsync opts]"
echo " ${MY_NAME} [-vdpi] <source>... <host>:<dest> -- [rsync opts]"
echo " ${MY_NAME} [-vdpi] <source>... <user>@<host>:<dest> -- [rsync opts]"
echo " ${MY_NAME} [-vdpi] <source>... <ssh-alias>:<dest> -- [rsync opts]"
echo

echo " ${MY_NAME} [-vdpi] <host>:<source> <dest> -- [rsync opts]"
Expand All @@ -59,7 +59,7 @@ print_usage() {
echo

echo "Required arguments:"
echo " <source> Local source directory"
echo " <source>... One or more local source directories"
echo " <dest> Local destination directory."
echo " <host>:<dest> SSH host and source/destination directory on server"
echo " <user>@<host>:<dest> SSH user, SSH host and source/destination directory on server"
Expand All @@ -81,10 +81,12 @@ print_usage() {
echo "Examples:"
echo " Simply back up one directory recursively"
echo " timemachine /home/user /data"
echo " Back up multiple directories"
echo " timemachine /home/user /etc /var/log /data"
echo " Do the same, but be verbose"
echo " timemachine -v /home/user /data"
echo " timemachine -v /home/user /etc /data"
echo " Append rsync options and be very verbose"
echo " timemachine -d /home/user /data -- --progress --verbose"
echo " timemachine -d /home/user /etc /data -- --progress --verbose"
echo " Log to file"
echo " timemachine -v /home/user /data > /var/log/timemachine.log 2> /var/log/timemachine.err"
echo
Expand Down Expand Up @@ -355,26 +357,60 @@ done
################################################################################

if [ "${#}" -lt "2" ]; then
logerr "<source> and <destination> are required."
logerr "At least one <source> and <destination> are required."
logerr "See -h for help."
exit 1
fi

if is_remote "${1}"; then
if is_remote "${2}"; then
logerr "Source and Target cannot both be remote locations."
logerr "See -h for help."
exit 1
# Extract destination (last argument) and sources (all but last)
DEST=""
SOURCES=""
arg_count="${#}"
i=1
for arg in "${@}"; do
if [ "${i}" -eq "${arg_count}" ]; then
DEST="${arg}"
else
if [ -z "${SOURCES}" ]; then
SOURCES="${arg}"
else
SOURCES="${SOURCES} ${arg}"
fi
fi
i=$((i + 1))
done

# Validate that we don't have multiple remote sources
remote_source_count=0
for src in ${SOURCES}; do
if is_remote "${src}"; then
remote_source_count=$((remote_source_count + 1))
fi
done

if [ "${remote_source_count}" -gt 1 ]; then
logerr "Multiple remote sources are not supported."
logerr "See -h for help."
exit 1
fi

if ! dir_exists "${1}"; then
logerr "Source directory does not exist: ${1}"
if [ "${remote_source_count}" -gt 0 ] && is_remote "${DEST}"; then
logerr "Source and Target cannot both be remote locations."
logerr "See -h for help."
exit 1
fi
if ! dir_exists "${2}"; then
logerr "Target directory does not exist: ${2}"

# Validate all source directories exist
for src in ${SOURCES}; do
if ! dir_exists "${src}"; then
logerr "Source directory does not exist: ${src}"
logerr "See -h for help."
exit 1
fi
done

if ! dir_exists "${DEST}"; then
logerr "Target directory does not exist: ${DEST}"
logerr "See -h for help."
exit 1
fi
Expand All @@ -390,11 +426,8 @@ fi
# Main Entrypoint
################################################################################

# Get arguments and remove them afterwards to have ${@} contain
# all additional rsync options
SRC="${1}"
DEST="${2}"
shift 2
# Remove destination from arguments and handle -- separator
shift "${arg_count}"
[ "${#}" -ge 1 ] && [ "${1}" = "--" ] && shift

# Name of the backup directory
Expand Down Expand Up @@ -422,12 +455,22 @@ RSYNC_PARTIAL=".partial"

BTYPE=

# Build escaped source arguments
ESCAPED_SOURCES=""
for src in ${SOURCES}; do
if [ -z "${ESCAPED_SOURCES}" ]; then
ESCAPED_SOURCES="$( escape_path "${src}" )"
else
ESCAPED_SOURCES="${ESCAPED_SOURCES} $( escape_path "${src}" )"
fi
done

# Only link destination if it already exists
if link_exists "${DEST}/${BACKUP_LATEST}"; then
BTYPE="incremental"

logmsg "Starting incremental backup"
logmsg "\$ rsync $* $( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"
logmsg "\$ rsync $* ${ESCAPED_SOURCES} $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"

cmd="rsync \
-e \"ssh ${SSH_ARGS}\" \
Expand All @@ -442,12 +485,12 @@ if link_exists "${DEST}/${BACKUP_LATEST}"; then
--partial-dir=${RSYNC_PARTIAL} \
--link-dest=../${BACKUP_LATEST} \
$* \
$( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"
${ESCAPED_SOURCES} $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"
else
BTYPE="full"

logmsg "Starting full backup"
logmsg "\$ rsync $* $( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"
logmsg "\$ rsync $* ${ESCAPED_SOURCES} $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"

cmd="rsync \
-e \"ssh ${SSH_ARGS}\" \
Expand All @@ -461,7 +504,7 @@ else
--delete-excluded \
--partial-dir=${RSYNC_PARTIAL} \
$* \
$( escape_path "${SRC}" ) $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"
${ESCAPED_SOURCES} $( escape_path "${DEST}/${BACKUP_INPROGRESS}" )"
fi

if ! eval "${cmd}"; then
Expand Down