#!/bin/bash

############################################
#
# BCKDIR=          # destination dir
# BCKS=            # rsync shares
# MAXDAYS=         # Max. keep days
# MAXRUN=          # Max runtime (optional, default:unlimited)
# BWLIMIT=         # Bandwidth limit (optional)
# EXCLUDE=         # Exclude (optional)
# EXCLUDEFROM=     # Exclude from file (optional)
#
# RSYNC_USER=      # Rsync user
# RSYNC_PASSWORD=  # Rsync password
# RSYNC_HOST=      # Rsync host (optional, default:localhost)
# SYNC_PORT=       # Rsync port
# RSYNC_ICONV=     # Codepage convert (optional: to,from)
#
# VERBOSE=         # Verbose (optional) 0/1
#
#############################################

function dircheck() {

    [ "x$BCKDIR" == "x" ] && exit 33
    [ "x$BCKS" == "x" ] && exit 33
    [ "x$MAXDAYS" == "x" ] && exit 33

    [ "x$RSYNC_USER" == "x" ] && exit 33
    [ "x$RSYNC_PASSWORD" == "x" ] && exit 33
    [ "x$RSYNC_HOST" == "x" ] && RSYNC_HOST="127.0.0.1"
    [ "x$RSYNC_PORT" == "x" ] && exit 33

    [ "x$RSYNC_ICONV" != "x" ] && OPT_ICONV="--iconv $RSYNC_ICONV"
    [ "x$BWLIMIT" != "x" ] && OPT_BWLIMIT="--bwlimit $BWLIMIT"
    [ "x$EXCLUDE" != "x" ] && OPT_EXCLUDE="--exclude $EXCLUDE"

    if [ "x$EXCLUDEFROM" != "x" ]; then
	if [ -f $EXCLUDEFROM ]; then
	    OPT_EXCLUDEFROM="--exclude-from $EXCLUDEFROM"
	else
	    echo "ERROR: $EXCLUDEFROM exclude file doesn't exist!"
	    exit 87
	fi
    fi

    OPT_VERB="-q"
    [ "x$VERBOSE" == "x1" ] && OPT_VERB="-v"

    [ -d $BCKDIR ] || mkdir $BCKDIR

}

function dircheck_bck() {

    [ -d $BCKDIR/$BCK ] || mkdir $BCKDIR/$BCK
    [ -d $BCKDIR/$BCK/daily ] || mkdir $BCKDIR/$BCK/daily
    [ -d $BCKDIR/$BCK/weekly ] || mkdir $BCKDIR/$BCK/weekly

    [ -d $BCKDIR/$BCK/daily/$DAY ] || mkdir $BCKDIR/$BCK/daily/$DAY
    [ -d $BCKDIR/$BCK/daily/$DAYY ] || mkdir $BCKDIR/$BCK/daily/$DAYY

}

function syncok() {

    errcode=$1

    case "$errcode" in
	30)
	backup_error
	return 30
	;;
	35)
	backup_error
	return 35
	;;
	10)
	backup_error
	return 10
	;;
	11)
	backup_error
	return 11
	;;
	12)
	backup_error
	return 12
	;;
	*)
	return 0
	;;
    esac

}

function backup_error() {
    ETS=`date '+%T'`
    echo "  Error during sync at $ETS"
    sleep 5
    /etc/init.d/stunnel4 restart >/dev/null
    sleep 5
}

###########################################################

function initbackup() {

    if [ "x$CMDL" != "x" ]; then
	MAXRUN=$CMDL
    fi

    #check
    dircheck

    # sync
    export RSYNC_PASSWORD

    for BCK in $BCKS; do

	dircheck_bck


	#source, dest
	bsrc=rsync://$RSYNC_USER@$RSYNC_HOST:$RSYNC_PORT/$BCK
	bdst=$BCKDIR/$BCK/init
	[ -d $bdst ] || mkdir $bdst

	if [ -n "$MAXRUN" ]; then
	    limitexec="/usr/bin/timeout $MAXRUN"
	else
	    limitexec=
	fi

	# make the initial backup
	while : ; do
	    echo -n "- $BCK: "
	    date '+%T'
	    $limitexec /usr/bin/rsync -a -r $OPT_VERB -E $OPT_BWLIMIT $OPT_ICONV $OPT_EXCLUDE $OPT_EXCLUDEFROM --timeout=900 --contimeout=15 --old-compress --numeric-ids --delete --delete-excluded --force $bsrc/ $bdst/
	    if syncok $?; then break; fi
	    echo "  Sleeping for 5 minutes..."
	    sleep 300
	done

	#finalize
	touch $bdst

    done

}


###########################################################

function makebackup() {

    if [ "x$CMDL" != "x" ]; then
	MAXRUN=$CMDL
    fi

    #check
    dircheck

    #DAY=`date +%u-%a`
    #DAYY=`date +%u-%a -d "yesterday"`

    # sync
    export RSYNC_PASSWORD

    for BCK in $BCKS; do

	dircheck_bck

	# get last successful backup
	if [ -L $BCKDIR/$BCK/current ]; then
	    blast=`readlink $BCKDIR/$BCK/current`
	    if [ "x$blast" == "xdaily/$DAY" ]; then
		blast="daily/$DAYY"
		echo "- $BCK: A backup was already done today, regenerating."
	    fi
	else
	    blast="daily/$DAYY"
	fi

	#source, dest
	bsrc=rsync://$RSYNC_USER@$RSYNC_HOST:$RSYNC_PORT/$BCK
	bdst=$BCKDIR/$BCK/daily/$DAY

	if [ -n "$MAXRUN" ]; then
	    limitexec="/usr/bin/timeout $MAXRUN"
	else
	    limitexec=
	fi

	# make the backup
	for i in 1 2 3 4 5; do
	    echo -n "- $BCK: "
	    date '+%T'
	    $limitexec /usr/bin/rsync -a -r $OPT_VERB -E $OPT_BWLIMIT $OPT_ICONV $OPT_EXCLUDE $OPT_EXCLUDEFROM --timeout=900 --contimeout=15 --old-compress --numeric-ids --delete --delete-excluded --force --link-dest=../../$blast $bsrc/ $bdst/
	    if syncok $?; then break; fi
	done

	#finalize
	touch $bdst

	[ -L $BCKDIR/$BCK/current ] && rm $BCKDIR/$BCK/current
	ln -s daily/$DAY $BCKDIR/$BCK/current

    done

}

###########################################################

function rotatebackup() {

    #check
    dircheck

    # Run with low IO
    if [ -x /usr/bin/ionice ] && /usr/bin/ionice -c3 true 2>/dev/null; then
	IONICE="/usr/bin/ionice -c3"
    fi

    if [ "`date +%u`" == "7" -o "$CMDL" == "FORCE" ]; then

	for BCK in $BCKS; do

	    dircheck_bck

	    echo -n "- $BCK copy: "
	    date '+%T'
	    src=$BCKDIR/$BCK/daily/$DAY
	    dst=$BCKDIR/$BCK/weekly/$TS
	    [ -d $dst ] || mkdir $dst
	    $IONICE cp -a -l $src/* $dst/

	    echo -n "- $BCK del old: "
	    date '+%T'
	    $IONICE find $BCKDIR/$BCK/weekly/ -mindepth 1 -maxdepth 1 -mtime +$MAXDAYS -exec rm -r {} \;
	done
    fi

}

###########################################################

function backuproute() {

    # The originating script name should be backup-* or rotate-*

    sn=${0##*/}
    func=${sn%%-*}

    echo -n "$func start: "
    date '+%T'

    TS=`date +%Y-%m-%d`
    DAY=`date +%u-%a`
    DAYY=`date +%u-%a -d "yesterday"`

    [ "$func" == "init" ] && initbackup
    [ "$func" == "backup" ] && makebackup
    [ "$func" == "rotate" ] && rotatebackup

    echo -n "$func end: "
    date '+%T'

}

###########################################################
# start
###########################################################

CMDL="$1"

###########################################################
# end
###########################################################

