borg-reverse/backup.sh

183 lines
5.1 KiB
Bash
Raw Permalink Normal View History

#!/bin/bash
# Stop on any error and provide some info if DEBUG set
set -e
[ -n "$DEBUG" ] && set -x
# NOTE:
# The repo variable is used in a few different ways - it should be the name of:
# 1. an existing borg repository
# 2. a config file in $borg_dir/server_config/
# 3. an entry in your ssh config
main()
{
local borg_old=0
while getopts "cp:h" option; do
case "$option" in
c) borg_old=1
;;
p) BORG_REPO_PATH=$OPTARG
;;
h)
display_usage 0
;;
*)
echo "Invalid option"
display_usage 1
;;
esac
done
shift "$((OPTIND - 1))"
if [ $# -ne 2 ]; then
display_usage 1
fi
if [ `id -u` -eq 0 ]; then
echo "This script must not be run as root!"
display_usage 1
fi
if [ -z ${BORG_REPO_PATH} ]; then
echo "[ERROR] BORG_REPO_PATH is empty or unset."
display_usage 1
fi
local mode=$1
local repo=$2
local borg_dir="${HOME}/.borg"
if [ $mode != "create" ] && [ $mode != "extract" ]; then
display_usage 1
fi
# Server config
# The config file is expected to provide several arrays:
# include, exclude and volatile
# TODO: Move config to more standard location
#local config_file="${borg_dir}/server_config/${repo}"
local config_file="server_config/${repo}"
if [[ ! -f ${config_file} ]]; then
echo "[ERROR] Config file missing; expected ${config_file}"
exit 1
fi
source "${config_file}"
# Make sure the config defined the required arrays
for var in include exclude; do
if [[ -z ${!var} ]]; then
echo "[ERROR] ${repo}: The config requires an '${var}' array"
exit 1
fi
done
# borg-backup requires --exclude to prepend each excluded item
exclude=( "${exclude[@]/#/--exclude }" )
## Logging
local log_dir="${borg_dir}/log"
local log_month_dir=${log_dir}/$(date --utc "+%Y-%m")
local log_file=${log_month_dir}/$(date --utc "+%Y-%m-%d_%H.%M.%SZ")_${repo}.log
if [[ ! -d "${log_month_dir}" ]]; then
echo "== Creating log directory"
mkdir --parent "${log_month_dir}" \
|| { echo "[ERROR] Failed to create log directory. Aborting."; exit 1; }
fi
touch ${log_file} \
|| { echo "[ERROR] Failed to create log file. Aborting"; exit 1; }
echo "== NOTE: To monitor progress, run this from another terminal:"
echo " $ tail -f ${log_file}"
# Temporarilly disallow glob
set -o noglob
local borg_options=(--show-rc --list --stats --one-file-system --exclude-caches)
if [[ -n $DEBUG ]]; then
borg_options+=(--verbose)
fi
if [ -z $BORG_OLD ]; then
borg_options+=(--show-version --exclude-nodump --keep-exclude-tags)
fi
local socket_local="/tmp/borg-local.sock"
local socket_remote="/tmp/borg-remote.sock"
if [ $mode == 'create' ]; then
echo "== CREATING"
# NOTE: "backup-server" is arbitrary and can be anything
# the socat-wrapper will ignore it
server_wrap $socket_local $socket_remote $mode $repo \
borg create \
"${borg_options[@]}" \
"${exclude[@]}" "${volatile[@]}" \
ssh://backup-server/$BORG_REPO_PATH/$repo::{utcnow:%Y-%m-%d} \
"${include[@]}" \
2>&1 | tee ${log_file}
# TODO: Test and fix up extract procedure
else
echo "== EXTRACTING"
local archive=$(borg info $BORG_REPO_PATH/$repo --last 1 | grep 'Archive name' | awk '{print $3}')
server_wrap $socket_local $socket_remote $mode $repo \
borg extract \
--dry-run \
ssh://backup-server/$BORG_REPO_PATH/$repo::$archive \
2>&1 | tee $log_file
fi
# Re-allow glob
set +o noglob
# Tidy up
# NOTE: socket_remote is dealt with in 'server_wrap.sh'
if [ -f $socket_local ]; then
rm $socket_local
fi
}
server_wrap()
{
if [ $# -lt 5 ]; then
echo "[ERROR] Missing argument(s)"
exit 1
fi
set -o noglob
local socket_local="$1"
local socket_remote="$2"
local mode="$3"
local repo="$4"
local borg_command="${@:5}; rm $socket_remote"
local remote_socat_command="'bash -c \"exec socat STDIO UNIX-CONNECT:$socket_remote\"'"
# Make command more robust against premature expansion
borg_command=`echo $borg_command | sed "s/--exclude\s\(\S\+\)/--exclude \'\1\'/g"`
# TODO: Handle extract
#if [ $mode == "extract" ]; then
# SH_CMD="cd /mnt"
#fi
exec socat UNIX-LISTEN:"$socket_local" \
"EXEC:borg serve --append-only --restrict-to-path $BORG_REPO_PATH --umask 077" &
ssh -t -R "$socket_remote":"$socket_local" $repo \
sudo BORG_RSH="$remote_socat_command" "$borg_command"
set +o noglob
}
display_usage()
{
echo "Usage: $(basename $0) [-ch] [-p path] create|extract repo-name"
echo " -c Use flags compatible with borg < (version tbd)"
echo " -p Repository path. Overrides BORG_REPO_PATH"
exit $1
}
main "$@"