#!/bin/sh
#
# backup.sh -- a simple shell script to make daily incremental backups of
# your home directory.
#
# This script is able to make two types of backups, full backups and
# incremental backups.  Normally incremental backups are made daily and full
# backups are made weekly.
# Full backups are considered level-0 backups and should create a new
# snapshot archive (snar) file.
# Incremental backups are level-[1-6] backups, using the full or incremental
# backup from the previous day for at most a level-6 backup.
#
# To have automated daily backups just add a line to your user crontab,
# that should go something like this:
#
# 03 00 * * * /path/to/this/script normal
#
# This will cause backup.sh to execute in normal mode every weekday of every
# month with every month-day, three minutes past midnight.
#
# The directory /var/snapshots must be exist and be user writable (my
# computer has drwxrwxrwt /var/snapshots)
#
# If you don't want remote backups comment out the RMT_CMD line.
#
# You can ignore certain files by adding the FULL path of the file to
# $HOME/.snap-exclude
# Wildcards are welcome, especially for directories
#
# To restore a backup you may need to extract (at most) 6 files: the full
# backup, followed by incremental backups from every day until the most
# recent.
#
# $Id$

SNAPDIR=/var/snapshots/$USER
RMT_CMD=$(which scp)
RMT_DIR="user@hostname:~/snapshots"
RMT_OPTIONS="-i $HOME/.ssh/id_dsa"

DATE=$(date +%Y%m%d)
TAR=$(which tar)
MKDIR=$(which mkdir)
CHMOD=$(which chmod)

LAST_FULL=$(stat -f "%Dc %Sc" -t "%Y%m%d" ${SNAPDIR}/full-*.tar.gz \
            2> /dev/null| sort -n | tail -n1)
LAST_TS=$(echo ${LAST_FULL} | awk '{ print $1}')
LAST_DATE=$(echo ${LAST_FULL} | awk '{ print $2}')

# Make sure the directory exists and has little access
if [ ! -d ${SNAPDIR} ]; then
	${MKDIR} ${SNAPDIR}
	${CHMOD} go-rwx ${SNAPDIR}
fi

function incr {
	${TAR} czf ${SNAPDIR}/incr-${DATE}.tar.gz \
	       --exclude-from $HOME/.snap-exclude \
	       --listed-incremental=${SNAPDIR}/$USER-${LAST_DATE}.snar \
	       $HOME > /dev/null 2> /dev/null

	${CHMOD} go-rwx ${SNAPDIR}/incr-${DATE}.tar.gz

	if [ "${RMT_CMD}" != "" ]; then
		${RMT_CMD} ${RMT_OPTIONS} ${SNAPDIR}/incr-${DATE}.tar.gz \
		       ${SNAPDIR}/$USER-${LAST_DATE}.snar \
		       ${RMT_DIR}
	fi
}

function full {
	${TAR} czf ${SNAPDIR}/full-${DATE}.tar.gz \
	       --exclude-from $HOME/.snap-exclude \
	       --listed-incremental=${SNAPDIR}/$USER-${DATE}.snar \
	       $HOME > /dev/null 2> /dev/null

	${CHMOD} go-rwx ${SNAPDIR}/full-${DATE}.tar.gz
	${CHMOD} go-rwx ${SNAPDIR}/$USER-${DATE}.snar

	if [ "${RMT_CMD}" != "" ]; then
		${CMT_CMD} ${RMT_OPTIONS} ${SNAPDIR}/full-${DATE}.tar.gz \
		       ${SNAPDIR}/$USER-${DATE}.snar \
		       ${RMT_DIR}
	fi
}

function normal {
	# Make a full backup if no backup exists
	if [ ! -f ${SNAPDIR}/$USER-${LAST_DATE}.snar ]; then
		full;
	else
		ELAPSED=$(($(date +%s) - ${LAST_TS}))
		SNAP_FRAME=$((7 * 24 * 60 * 60 - 3600))

		# Check if it has been over a week since a full snapshot
		if [ ${ELAPSED} -gt ${SNAP_FRAME} ]; then
			# make a full snapshot
			full;
			# clean up files older than 4 weeks
			clean;
		else
			incr;
		fi

		unset ELAPSED SNAP_FRAME
	fi
}

# Clean will remove all files older than 4 weeks to keep space down to a
# minimum
function clean {
	true
}

function usage {
	echo "usage: $0 [type]"
	echo "[type] can be one of the following:"
	echo "  normal - follow the daily incremental and weekly backup schedule"
	echo "  incr   - create a incremental backup of $HOME to ${SNAPDIR}"
	echo "  full   - create a full backup of $HOME to ${SNAPDIR}"
	echo "  clean  - cleanup backups older than one month"
	echo "  usage  - display this screen"
	echo "  --help - display this screen"

	exit 1
}

case "$1" in
	'normal')
		normal;
		;;
	'incr')
		incr;
		;;
	'full')
		full;
		;;
	'clean')
		clean;
		;;
	'--help')
		usage;
		;;
	'usage')
		usage;
		;;
	*)
		normal;
		;;
esac

