224 lines
9.1 KiB
Text
224 lines
9.1 KiB
Text
|
#!/usr/bin/env bash
|
||
|
|
||
|
# called before backup
|
||
|
resticwrapper_pre_backup() {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
# called after backup
|
||
|
resticwrapper_post_backup() {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
# usage
|
||
|
usage() {
|
||
|
USAGE=$(cat <<EOF
|
||
|
Usage: resticwrapper [CONFIG_FILE (absolute path)]
|
||
|
|
||
|
A script which wraps the most common use case for restic: creating a backup and sending a notification.
|
||
|
|
||
|
If no CONFIG_FILE is given, \$HOME/.resticwrapper.conf is used. This fallback option
|
||
|
has to exist if no other CONFIG_FILE exists. Otherwise the script exits.
|
||
|
|
||
|
Configuration can be done in any file and any pre-defined variable can be overwritten.
|
||
|
|
||
|
The following are at least required for the script to work:
|
||
|
- RESTICWRAPPER_BACKUP_NAME // [a-z09], should be unique for multiple backups (with the same remote user)
|
||
|
- RESTICWRAPPER_BACKUP_FILES // an array of files and directories to backup, e.g. =('/home', '/root')
|
||
|
- RESTICWRAPPER_BACKUP_REPOSITORY // trailing slash, add "user@host:/dir/" if remote
|
||
|
|
||
|
The following are optional or have reasonable defaults:
|
||
|
- RESTICWRAPPER_BACKUP_PASSWORD='' // the password if needed
|
||
|
- RESTICWRAPPER_BACKUP_PASSWORD_FILE='' // a password file if needed
|
||
|
- RESTICWRAPPER_BACKUP_PRUNE=true // prune the repository (cleans old backups; uses \$RESTICWRAPPER_BACKUP_KEEP_IN_DAYS)? false|true
|
||
|
- RESTICWRAPPER_BACKUP_KEEP_IN_DAYS=60 // in days, integer only (used for prune)
|
||
|
- RESTICWRAPPER_BACKUP_CHECK=true // checks entire repository, this is slow! false|true
|
||
|
- RESTICWRAPPER_BACKUP_CHECK_MAX_AGE_IN_SECONDS=604800 // determine when "restic check" has to run by comparing current time with latest run check (determined by last mod time of \$RESTICWRAPPER_BACKUP_CHECK_FILE). default is 7 days, 0 will always run the check
|
||
|
- RESTICWRAPPER_BACKUP_CHECK_FILE="~/.resticwrapper-\$RESTICWRAPPER_BACKUP_NAME.check" // used for time comparison
|
||
|
- RESTICWRAPPER_BACKUP_LOG=true // log? false|true
|
||
|
- RESTICWRAPPER_BACKUP_LOG_PRUNE=true // overwrite old log for each execution? false|true
|
||
|
- RESTICWRAPPER_BACKUP_LOG_FILE="~/.resticwrapper-\$RESTICWRAPPER_BACKUP_NAME.log" // log file to save output to to
|
||
|
- RESTICWRAPPER_BACKUP_NOTIFY_VIA_MAIL=false // send email (\$RESTICWRAPPER_BACKUP_LOG has to be set to true)? false|true
|
||
|
- RESTICWRAPPER_BACKUP_NOTIFY_MAIL_ADDRESS="" // mail.rc has to be configured
|
||
|
- RESTICWRAPPER_BACKUP_NOTIFY_UI=false // use notify-send for UI notification? false|true
|
||
|
|
||
|
Borg specific values can also be overwritten and have reasonable defaults:
|
||
|
- RESTIC_BINARY=restic; // defaults to /usr/bin/restic (determined by 'which restic'), adjust to a virtualenv if needed
|
||
|
- RESTICWRAPPER_RESTIC_INIT_PARAMS="init -r=\${RESTICWRAPPER_BACKUP_REPOSITORY}\${RESTICWRAPPER_BACKUP_NAME}";
|
||
|
- RESTICWRAPPER_RESTIC_CREATE_PARAMS="backup --verbose";
|
||
|
- RESTICWRAPPER_RESTIC_CHECK_PARAMS="check";
|
||
|
- RESTICWRAPPER_RESTIC_PRUNE_PARAMS="forget --prune --keep-within 0y0m\${RESTICWRAPPER_BACKUP_KEEP_IN_DAYS}d0h";
|
||
|
- RESTICWRAPPER_RESTIC_LIST_PARAMS="snapshots";
|
||
|
- RESTICWRAPPER_RESTIC_INFO_PARAMS="stats";
|
||
|
|
||
|
More information can be found in the docs: https://restic.readthedocs.io
|
||
|
|
||
|
The following functions are executed before and after the backup and can also be overwritten in the config file by redefining them:
|
||
|
- resticwrapper_pre_backup
|
||
|
- resticwrapper_post_backup
|
||
|
|
||
|
You can copy this script to '/usr/local/bin' and use create a custom CONFIG_FILE as user. Examples can be found in '/usr/share/doc/<scriptname>'.
|
||
|
EOF
|
||
|
)
|
||
|
echo "$USAGE";
|
||
|
}
|
||
|
|
||
|
set -e;
|
||
|
|
||
|
# helper functions
|
||
|
source_config() {
|
||
|
local config=$1;
|
||
|
local configFallback=$2;
|
||
|
|
||
|
if [[ ! -f "$config" ]]; then
|
||
|
if [[ ! -f "$configFallback" ]]; then
|
||
|
echo "No config file specified and could not find default in '${configFallback}'!";
|
||
|
echo "";
|
||
|
usage;
|
||
|
exit 1;
|
||
|
else
|
||
|
config=${configFallback};
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
set -a;
|
||
|
source "${config}";
|
||
|
set +a;
|
||
|
}
|
||
|
apply_defaults() {
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_PASSWORD} ]] && RESTICWRAPPER_BACKUP_PASSWORD='';
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_PASSWORD_FILE} ]] && RESTICWRAPPER_BACKUP_PASSWORD_FILE='';
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_PRUNE} ]] && RESTICWRAPPER_BACKUP_PRUNE=true;
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_KEEP_IN_DAYS} ]] && RESTICWRAPPER_BACKUP_KEEP_IN_DAYS=60;
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_CHECK} ]] && RESTICWRAPPER_BACKUP_CHECK=true;
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_CHECK_MAX_AGE_IN_SECONDS} ]] && RESTICWRAPPER_BACKUP_CHECK_MAX_AGE_IN_SECONDS=604800;
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_CHECK_FILE} ]] && RESTICWRAPPER_BACKUP_CHECK_FILE="$HOME/.resticwrapper-${RESTICWRAPPER_BACKUP_NAME}.check";
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_LOG} ]] && RESTICWRAPPER_BACKUP_LOG=true;
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_LOG_PRUNE} ]] && RESTICWRAPPER_BACKUP_LOG_PRUNE=true;
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_LOG_FILE} ]] && RESTICWRAPPER_BACKUP_LOG_FILE="$HOME/.resticwrapper-${RESTICWRAPPER_BACKUP_NAME}.log";
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_NOTIFY_VIA_MAIL} ]] && RESTICWRAPPER_BACKUP_NOTIFY_VIA_MAIL=false;
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_NOTIFY_MAIL_ADDRESS} ]] && RESTICWRAPPER_BACKUP_NOTIFY_MAIL_ADDRESS="";
|
||
|
[[ -z ${RESTICWRAPPER_BACKUP_NOTIFY_UI} ]] && RESTICWRAPPER_BACKUP_NOTIFY_UI=false;
|
||
|
[[ -z ${RESTIC_BINARY} ]] && RESTIC_BINARY=$(which restic);
|
||
|
[[ -z ${RESTICWRAPPER_RESTIC_INIT_PARAMS} ]] && RESTICWRAPPER_RESTIC_INIT_PARAMS="init -r ${RESTIC_REPOSITORY}";
|
||
|
[[ -z ${RESTICWRAPPER_RESTIC_CREATE_PARAMS} ]] && RESTICWRAPPER_RESTIC_CREATE_PARAMS="backup --verbose";
|
||
|
[[ -z ${RESTICWRAPPER_RESTIC_CHECK_PARAMS} ]] && RESTICWRAPPER_RESTIC_CHECK_PARAMS="check";
|
||
|
[[ -z ${RESTICWRAPPER_RESTIC_PRUNE_PARAMS} ]] && RESTICWRAPPER_RESTIC_PRUNE_PARAMS="forget --prune --keep-within 0y0m${RESTICWRAPPER_BACKUP_KEEP_IN_DAYS}d0h";
|
||
|
[[ -z ${RESTICWRAPPER_RESTIC_LIST_PARAMS} ]] && RESTICWRAPPER_RESTIC_LIST_PARAMS="snapshots";
|
||
|
[[ -z ${RESTICWRAPPER_RESTIC_INFO_PARAMS} ]] && RESTICWRAPPER_RESTIC_INFO_PARAMS="stats";
|
||
|
}
|
||
|
check_required() {
|
||
|
if [[ -z ${RESTICWRAPPER_BACKUP_REPOSITORY} ]]; then
|
||
|
echo "RESTICWRAPPER_BACKUP_REPOSITORY is required"
|
||
|
exit 1;
|
||
|
fi
|
||
|
if [[ -z ${RESTICWRAPPER_BACKUP_NAME} ]]; then
|
||
|
echo "RESTICWRAPPER_BACKUP_NAME is required"
|
||
|
exit 1;
|
||
|
fi
|
||
|
type ${RESTIC_BINARY} >/dev/null 2>&1 || { echo >&2 "Require '${RESTIC_BINARY}' (binary) but it's not installed. Aborting."; exit 1; }
|
||
|
}
|
||
|
|
||
|
# define wrapper functions
|
||
|
backup() {
|
||
|
echo "Using ${RESTIC_BINARY} as binary";
|
||
|
resticwrapper_pre_backup;
|
||
|
|
||
|
set -a;
|
||
|
RESTIC_REPOSITORY="${RESTICWRAPPER_BACKUP_REPOSITORY}${RESTICWRAPPER_BACKUP_NAME}";
|
||
|
set +a;
|
||
|
|
||
|
if [ ! -z "${RESTICWRAPPER_BACKUP_PASSWORD}" ]; then
|
||
|
export RESTIC_PASSWORD=${RESTICWRAPPER_BACKUP_PASSWORD};
|
||
|
fi
|
||
|
|
||
|
if [ ! -z "${RESTICWRAPPER_BACKUP_PASSWORD_FILE}" ]; then
|
||
|
export RESTIC_PASSWORD_FILE=${RESTICWRAPPER_BACKUP_PASSWORD};
|
||
|
fi
|
||
|
|
||
|
# init hint
|
||
|
echo "Hint: If you haven't, you need to create the restic repository manually before first run if it doesn't exist:";
|
||
|
echo "${RESTIC_BINARY} ${RESTICWRAPPER_RESTIC_INIT_PARAMS} ${RESTICWRAPPER_BACKUP_REPOSITORY}${RESTICWRAPPER_BACKUP_NAME}";
|
||
|
|
||
|
# create
|
||
|
${RESTIC_BINARY} ${RESTICWRAPPER_RESTIC_CREATE_PARAMS} "${RESTICWRAPPER_BACKUP_FILES[@]}"
|
||
|
|
||
|
# notify UI
|
||
|
if [ "${RESTICWRAPPER_BACKUP_NOTIFY_UI}" = true ]; then
|
||
|
notify-send "Started backup" ${RESTIC_REPOSITORY};
|
||
|
fi
|
||
|
|
||
|
# check
|
||
|
if [ "${RESTICWRAPPER_BACKUP_CHECK}" = true ]; then
|
||
|
|
||
|
# use for first time creation
|
||
|
doCheck=false
|
||
|
|
||
|
if [ ! -f "${RESTICWRAPPER_BACKUP_CHECK_FILE}" ]; then
|
||
|
touch ${RESTICWRAPPER_BACKUP_CHECK_FILE}
|
||
|
doCheck=true
|
||
|
fi
|
||
|
|
||
|
# compare date
|
||
|
lastMod=$(date -r ${RESTICWRAPPER_BACKUP_CHECK_FILE} +%s)
|
||
|
age=$(($(date "+%s") - ${lastMod}))
|
||
|
|
||
|
if [ "$age" -gt "${RESTICWRAPPER_BACKUP_CHECK_MAX_AGE_IN_SECONDS}" ]; then
|
||
|
doCheck=true
|
||
|
fi
|
||
|
|
||
|
|
||
|
if [ "$doCheck" = true ]; then
|
||
|
${RESTIC_BINARY} ${RESTICWRAPPER_RESTIC_CHECK_PARAMS};
|
||
|
touch ${RESTICWRAPPER_BACKUP_CHECK_FILE}
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
# prune
|
||
|
if [ "${RESTICWRAPPER_BACKUP_PRUNE}" = true ]; then
|
||
|
${RESTIC_BINARY} ${RESTICWRAPPER_RESTIC_PRUNE_PARAMS};
|
||
|
fi
|
||
|
|
||
|
# log
|
||
|
if [ "${RESTICWRAPPER_BACKUP_LOG}" = true ]; then
|
||
|
|
||
|
if [ "${RESTICWRAPPER_BACKUP_LOG_PRUNE}" = true ] && [ -f "${RESTICWRAPPER_BACKUP_LOG_FILE}" ]; then
|
||
|
rm ${RESTICWRAPPER_BACKUP_LOG_FILE};
|
||
|
fi
|
||
|
|
||
|
touch ${RESTICWRAPPER_BACKUP_LOG_FILE};
|
||
|
${RESTIC_BINARY} ${RESTICWRAPPER_RESTIC_LIST_PARAMS} >> ${RESTICWRAPPER_BACKUP_LOG_FILE};
|
||
|
echo "---" >> ${RESTICWRAPPER_BACKUP_LOG_FILE};
|
||
|
${RESTIC_BINARY} ${RESTICWRAPPER_RESTIC_INFO_PARAMS} >> ${RESTICWRAPPER_BACKUP_LOG_FILE};
|
||
|
|
||
|
if [ "${RESTICWRAPPER_BACKUP_NOTIFY_VIA_MAIL}" = true ]; then
|
||
|
cat ${RESTICWRAPPER_BACKUP_LOG_FILE}|mailx -Ssendwait -s "[resticwrapper ${RESTICWRAPPER_BACKUP_NAME}]" ${RESTICWRAPPER_BACKUP_NOTIFY_MAIL_ADDRESS};
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
# notify UI
|
||
|
if [ "${RESTICWRAPPER_BACKUP_NOTIFY_UI}" = true ]; then
|
||
|
notify-send "Finished backup" ${RESTIC_REPOSITORY};
|
||
|
fi
|
||
|
|
||
|
resticwrapper_post_backup;
|
||
|
}
|
||
|
|
||
|
# check requirements
|
||
|
CONFIG_FILE="$1";
|
||
|
FALLBACK_CONFIG_FILE="${HOME}/.resticwrapper.conf";
|
||
|
source_config "${CONFIG_FILE}" "${FALLBACK_CONFIG_FILE}";
|
||
|
apply_defaults;
|
||
|
check_required;
|
||
|
|
||
|
COMMAND="backup"
|
||
|
case "$COMMAND" in
|
||
|
backup)
|
||
|
backup;
|
||
|
;;
|
||
|
*)
|
||
|
echo "Unsupported command or no command given" >&2
|
||
|
usage;
|
||
|
exit 1
|
||
|
;;
|
||
|
esac
|