#!/bin/sh
: ${SNAP_SRC:=.}
usage() {
printf >&2 "Usage: %s: [-S SNAP_SRC] [-D SNAP_DST] [-] [RSYNC_ARGS]\n" $(basename "$0")
exit 2
}
die() {
printf "%s" "$*"
exit 1
}
while getopts S:D: opt; do
case $opt in
(S) SNAP_SRC=$OPTARG;;
(D) SNAP_DST=$OPTARG;;
(?) usage;;
(h) usage;;
esac
done
shift $(($OPTIND - 1))
case $1 in
(-) shift;;
(--) shift;;
esac
if test -z "$SNAP_DST"; then
echo >&2 "SNAP_DST is required either as env var or argument"
usage
fi
: ${SNAP_WRITESRC:=$SNAP_SRC}
check_local() {
for m in $SNAP_WRITESRC/.snapshot.[0-9]*; do
test -f "$m" || continue
test -n "$mark" && die "duplicate snapshot mark"
mark=$m
done
test -f "$mark" || die "snapshot mark not found"
ts=${mark##*.}
}
check_remote() {
snap.list "$SNAP_SRC/" | while read m; do
test -n "$ts" && die "duplicate snapshot mark"
ts=$m
done
test -n "$ts" || die "snapshot mark not found"
}
rs() {
rsync -aA --delete $RSYNC_ARGS "$@"
}
push_remote() {
# remote
case "$SNAP_DST" in
(*.push) ;;
(*) SNAP_DST=${SNAP_DST}.push;;
esac
rs "$@" --exclude=/${mark##*/} "$SNAP_SRC/" "$SNAP_DST/" && \
rs "$@" "$SNAP_WRITESRC/${mark##*/}" "$SNAP_DST/"
}
push_local() {
test -d "$SNAP_DST" || die "destination not a directory: ${SNAP_DST}"
snapshot=$(snap.list "$SNAP_DST/" | tail -1)
if test -n "$snapshot"; then
link=${SNAP_DST}/${snapshot}
mkdir -p "${SNAP_DST}/batch" || exit $?
rs "$@" --write-batch="${SNAP_DST}/batch/${snapshot}-$ts" "--link-dest=$link/" "$SNAP_SRC/" "$SNAP_DST/$ts/" || exit $?
else
rs "$@" "$SNAP_SRC/" "$SNAP_DST/$ts/" || exit $?
fi
touch "${SNAP_DST}/${mark##*/}"
}
case "$SNAP_SRC" in
(*:*) check_remote;;
(*) check_local;;
esac
case "$SNAP_DST" in
(rsync:*) push_remote "$@";;
(*::*) push_remote "$@";;
(*:*) die "pushing via remote shell directly not supported, use -e with rsync:// instead";;
(*) push_local "$@";;
esac