Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 112 lines (94 sloc) 3.24 KB
#!/bin/sh
# This is the reverse rsync backup (rrb) utility. It has to be run on the
# backup server and will then make a backup of a specified directory of a
# client machine via ssh, rsh, or any other remote shell or the localhost using
# rsync with hardlinks. See the config.example for more information about the
# configuration of rrb for a client.
#
# Further features include an exclusion file, the resume of a failed transfer,
# customizable commands to be executed before running, after running, after a
# failure, after a success.
#
# You can use fdupes or a similar tool to hardlink between multiple machines.
#
# No backups will be done automatically. You have to run rrb by hand every time
# you want to make a backup. Use cron to make regular backups, at to make
# backups at a specific time, or a custom script using sxc, or portknocking, or
# whatever to enable the client to request a backup.
#
# If you want to automize backups via ssh, you can use public key
# authentication. So if you do full-system backups from your backup server, it
# will have root access to all the backup clients. Root access on the server
# means root access on all the clients and a loss of all the backups. Secure
# the server and save backups offline, too.
#
# The backups can be made accessible in any manner, like NFS, SMB, or a custom
# script that tars the files and sends them over the network. Please use your
# creativity.
#
# This script should be compatible to zsh and bash. Tell me if it fails in your
# favourite shell.
# -----------------------------------------------------------------------------
set -e # Exit on every fail.
HELP="Usage: $0 config"
# Exit codes
EX_USAGE=64
EX_SOFTWARE=70
EX_CONFIG=78
EX_FILE=100
EX_RUNNING=101
# Unset variables from config file.
unset SRC EXCLUDES_FILE DEST_DIR BEFORE_CMD AFTER_CMD FAIL_CMD SUCCESS_CMD \
RSYNC_OPTS
fail()
{
echo -e "$1" >&2
exit $2
}
run()
{
if [ "$*" ]; then
echo "$*" | sh
fi
}
add_rsync_opt()
{
if [ "${RSYNC_OPTS}" ]; then
RSYNC_OPTS=("${RSYNC_OPTS[@]}" "$*")
else
RSYNC_OPTS=("$*")
fi
}
cleanup()
{
[ 0 -ne $? ] && run "$FAIL_CMD"
run "$AFTER_CMD"
rm -f "$LOCK_FILE"
}
[ "$#" -eq 1 ] || fail "Invalid usage.\n$HELP" $EX_USAGE
[ "$1" ] || fail "Config file missing.\n$HELP" $EX_USAGE
source $1
[ "$SRC" -a "$DEST_DIR" ] || fail "Invalid config file, has to include at \
least \$SRC and \$DEST_DIR.\n$HELP" $EX_CONFIG
cd $DEST_DIR
LATEST_DIR="latest"
TMP_DIR=".tmp"
NEW_DIR="`date +%FT%T`"
LOCK_FILE=".lock"
[ -e "$NEW_DIR" ] && fail "Backup directory $NEW_DIR already exists." $EX_FILE
[ "$EXCLUDES_FILE" ] && add_rsync_opt --exclude-from="$EXCLUDES_FILE"
[ -L "$LATEST_DIR" ] && add_rsync_opt --link-dest="$PWD/$LATEST_DIR"
# noclobber prevents the '>' from overwriting an existing lock file.
if ! (set -o noclobber; echo $$ > "$LOCK_FILE"); then
fail "$DEST_DIR is already locked by the process with the PID \
$(cat "$LOCK_FILE"). Remove $LOCK_FILE to unlock manually." $EX_RUNNING
fi
trap cleanup EXIT HUP INT QUIT TERM # Always call, even on success.
run "$BEFORE_CMD"
rsync -avzP --delete --delete-excluded "${RSYNC_OPTS[@]}" "$SRC" "$TMP_DIR" \
|| [ 24 -eq $? ]
mv "$TMP_DIR" "$NEW_DIR"
rm -f "$LATEST_DIR"
ln -sf "$NEW_DIR" "$LATEST_DIR"
run "$SUCCESS_CMD"
# Call cleanup.