ansible/roles/rsync_backup/files/backup-daily.sh

150 lines
3.3 KiB
Bash
Executable file

#!/bin/sh
set -eu
umask 077
ROTATED=30
CONFDIR="/etc/rsync-backup"
DESTDIR="/srv/backup"
LOGDIR="/var/log/rsync-backup"
RUNDIR="/var/run/rsync-backup"
find_rotated() {
# sort dailys from oldest to newest, daily.7 daily.6 daily.5 ...
find "$1" -mindepth 1 -maxdepth 1 -type d -name "daily.*" | sort -V -r
}
rotate_dirs() {
for host in "$@"; do
# rotate dailys starting from oldest
if [ ! -d "${DESTDIR}/${host}" ]; then
continue
fi
find_rotated "${DESTDIR}/${host}" | while read -r dir; do
ext="${dir##*.}"
next="${dir%.*}.$((ext+1))"
mv "$dir" "$next"
done
done
# compress logs over 1 day old
find "$LOGDIR" -type f -name '*.log' -mtime +1 -execdir gzip -f {} ';'
}
prune_dirs() {
for host in "$@"; do
# remove oldest dailys
find_rotated "${DESTDIR}/${host}" | while read -r dir ; do
num="$(basename "$dir" | sed -e 's/^daily.//')"
if [ "$num" -gt $ROTATED ]; then
rm -rf "$dir"
fi
done
done
# remove logs over ROTATED*2 days old
find "$LOGDIR" -type f -name '*.log.gz' -mtime +$((ROTATED*2)) -delete
}
rsync_pull() {
dirs=""
opts=""
host="$1"
conf="${CONFDIR}/${host}.conf"
if [ -s "$conf" ] && [ -x "$conf" ]; then
# shellcheck source=/dev/null
. "$conf" || return
else
echo "skipped: ${1}" 1>&2
return
fi
lockdir="${RUNDIR}/${host}.lock"
mkdir -m 0755 "$lockdir" || return
if [ "$host" = "$(hostname)" ]; then
# skip ssh for localhost
set -- $dirs
else
set -- $(for d in $dirs; do echo "${host}:${d}" ; done)
fi
base="${DESTDIR}/${host}"
if [ ! -d "$base" ]; then
mkdir -m 0700 "$base" || return
fi
dest="${base}/daily.0"
last="${base}/daily.1"
if [ ! -d "$dest" ]; then
mkdir -m 0700 "$dest" || return
fi
if [ -d "$last" ]; then
# hardlink unchanged files to previous daily
opts="--ignore-existing --link-dest=${last}"
fi
logfile="${LOGDIR}/${host}.$(date +%Y%m%d-%H%M%S).log"
if ! /usr/local/bin/rsync \
-e "ssh -o BatchMode=yes -i ${CONFDIR}/id_ed25519" \
-Raqxz --no-devices $opts \
--log-file="$logfile" \
"$@" "$dest"
then
echo "rsync log: ${logfile}" 1>&2
fi
rmdir "$lockdir"
}
if [ ! -d "$DESTDIR" ]; then
echo "ERROR: ${DESTDIR} does not exist" 1>&2
exit 1
fi
if [ ! -d "$LOGDIR" ]; then
echo "ERROR: ${LOGDIR} does not exist" 1>&2
exit 1
fi
if [ ! -d "$RUNDIR" ]; then
mkdir -m 0755 "$RUNDIR"
fi
ALL=false
PRUNE=false
ROTATE=false
while getopts "apr" OPT; do
case "$OPT" in
a)
ALL=true
;;
p)
PRUNE=true
;;
r)
ROTATE=true
;;
*)
echo "Usage: $(basename "$0") [-apr] [host ...]" 1>&2
exit 1
;;
esac
done
shift $((OPTIND-1))
mkdir -m 0755 "${RUNDIR}/daily.lock"
trap 'rmdir "${RUNDIR}/daily.lock"' EXIT
if [ $ALL ]; then
for conf in "${CONFDIR}"/*.conf ; do
host="$(basename "$conf" ".conf")"
set -- "$host" "$@"
done
fi
$ROTATE && rotate_dirs "$@"
for host in "$@" ; do
rsync_pull "$host"
done
$PRUNE && prune_dirs "$@"