#!/bin/bash

REMOTE_HOST=localhost
REMOTE_DIR=/tmp/backmeupscotty/test
ARCHIVE_KEEPNBACKUPS=30
ARCHIVE_KEEPNDAYS=30
BACKUP_RUNEVERYNTHDAY=1

function upperme {
    echo $(basename $0) | tr '[:lower:]' '[:upper:]'
}

function scottyinfo {
    echo $(upperme): $@ 
}

function scottyerror {
    echo $(upperme): $@ >&2 
}

function ssh255 {
    ssh $@
    sshret=$?

    if [ $sshret -eq 255 ]; then
	scottyerror "SSH connection failed!"
	exit 1
    else
	return $sshret
    fi
}

function grepbackups {
    ssh255 $REMOTE_HOST "ls $REMOTE_DIR" | grep -E '[0-9]+-[0-9]+'
}

function isIncomplete {
    if ( ssh255 $REMOTE_HOST '[ -d '$REMOTE_DIR/incomplete' ]' ); then
	return 0
    else
	return 1
    fi
}

function isNthDay {
    if [ $(( ( $(date +%s) / (60*60*24) ) % $BACKUP_RUNEVERYNTHDAY )) -eq 0 ]; 
    then
	return 0
    else
	return 1
    fi	
}

function latestTooOld {
    for oldbackup in $(grepbackups); do
	tstamp=$(echo $oldbackup | cut -d'-' -f1)
	
	if [ $(( $(date +%s) - $tstamp )) -lt \
	    $(( $BACKUP_RUNEVERYNTHDAY*24*60*60 )) ]
	then
	    return 1
	fi
    done

    return 0
}

function scottysync {
    timestamp=$(date +%s)

    scottyinfo Syncing $SYNC_SRC to $REMOTE_HOST:$REMOTE_DIR @$timestamp

    if [ ! -d "$SYNC_SRC" ]; then
	scottyerror Source dir $SYNC_SRC does not exist. Not syncing!
	return 1
    fi

    if [ $(ls -A "$SYNC_SRC" | wc -l) -eq 0 ]; then
	scottyerror Source dir $SYNC_SRC is empty. Not syncing!
	return 1
    fi

    dir_current=$REMOTE_DIR/current
    dir_incomplete=$REMOTE_DIR/incomplete
    dir_timestamped=$REMOTE_DIR/$timestamp-$(date -d @$timestamp +%Y%m%d%H%M%S)

    if [ -z $SYNC_EXC ]; then
	rsync_exclude=""
    else
	rsync_exclude=$(eval echo --exclude={$SYNC_EXC} | tr -d {})
    fi

    if (ssh255 $REMOTE_HOST '[ ! -d '$REMOTE_DIR' ]'); then
	scottyinfo Creating destination directory $REMOTE_HOST:$REMOTE_DIR
	ssh255 $REMOTE_HOST "mkdir $REMOTE_DIR"
    fi

    if isIncomplete; then
	scottyerror Continuing old incomplete backup
    fi

    scottyinfo Starting rsync
    rsync -e ssh \
	-v -aHAX --numeric-ids --delete --delete-excluded \
	--link-dest=$dir_current \
	$rsync_exclude \
	$SYNC_SRC/ $REMOTE_HOST:$dir_incomplete/
    
    if [ $? -eq 0 ]; then
	scottyinfo Timestamping completed backup and linking to current backup
	ssh255 $REMOTE_HOST \
	    "mv $dir_incomplete $dir_timestamped && rm -f $dir_current && ln -s $(basename $dir_timestamped) $dir_current"
    fi

    while [ $(grepbackups | wc -l) -gt $ARCHIVE_KEEPNBACKUPS ]; do
	oldestbackup=$(grepbackups | head -1)
	oldestbackuptstamp=$(echo $oldestbackup | cut -d'-' -f1)

	if [ $oldestbackuptstamp -lt $(( $(date +%s) - $ARCHIVE_KEEPNDAYS*60*60*24 )) ]; then
	    scottyinfo Removing old backup $oldestbackup
	    ssh255 $REMOTE_HOST rm -r "$REMOTE_DIR/$oldestbackup"
	else
	    break
	fi
    done
}

function deleteLock {
    if ! rmdir /tmp/$(basename $0).lock; then
	scottyerror "Could not delete lockfile /tmp/$(basename $0).lock!"
    fi
}

function prepare {
    scottyinfo "Preparing for sync"
}

function cleanup_abort {
    scottyerror "Caught exit signal! Cleaning up..."

    cleanup ABORT

    if [ $(jobs -p) ]; then
	scottyerror TERMinating remaining child processes...
	kill $(jobs -p)
    fi

    deleteLock

    exit
}

function cleanup {
    scottyinfo "No cleanup function was defined."
}

function prepare {
    scottyinfo "No prepare function was defined."
}

function cleanup_normal {
    cleanup
    deleteLock
}

function printhelp {
    cat <<EOF
Usage: $(basename $0) [OPTION]...

Recognized options:
  -q   Only output errors
  -n   Run only on nth day
  -h   Print out this help
EOF
}

function backmeupscotty {
    while getopts "qn:h" opt; do
	case $opt in
	    q)
		exec > /dev/null
		;;
	    n)
		BACKUP_RUNEVERYNTHDAY=$OPTARG
		;;
	    h)
		printhelp
                exit 0
                ;;
	esac
    done
    
    ssh255 $REMOTE_HOST exit
    
    if isNthDay; then
	scottyinfo "This is the nth day."
    elif latestTooOld; then
	scottyinfo "The latest backup is too old."
    else
	scottyinfo "No backup has to be done. Exiting."
	exit 0
    fi

    scottyinfo "Performing backup."

    trap cleanup_abort EXIT

    prepare
    scottysync

    trap cleanup_normal EXIT

    exit 0
}

if ! mkdir /tmp/$(basename $0).lock; then
    scottyerror "Another instance of $(basename $0) is still running!"
    exit 1
else
    trap deleteLock EXIT
fi