2020-03-26 02:21:59 -06:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
#
|
|
|
|
## borg helper script for viewing and restoring backups
|
|
|
|
#
|
|
|
|
|
2020-03-26 02:43:00 -06:00
|
|
|
### trap
|
|
|
|
trap trapExit 1 2 3 6
|
2020-03-26 02:21:59 -06:00
|
|
|
|
|
|
|
### functions
|
2020-03-26 03:56:23 -06:00
|
|
|
cleanup () {
|
|
|
|
# remove borg temp directory, if it exists
|
|
|
|
if [ -d "${borgBaseDir}/tmp" ]; then
|
2020-03-26 04:01:15 -06:00
|
|
|
if ! rm -rf "${borgBaseDir}/tmp" > /dev/null 2>&1; then
|
2020-03-26 04:01:54 -06:00
|
|
|
consoleError 3 "Script completed successfully but could not remove temporary directory at $borgBaseDir/tmp. Sorry to be messy."
|
2020-03-26 04:01:15 -06:00
|
|
|
fi
|
2020-03-26 03:56:23 -06:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2020-03-26 02:21:59 -06:00
|
|
|
consoleError() {
|
|
|
|
printf "%s\n%s\n" "$err" "$2"
|
|
|
|
printf "Exiting.\n\n%s" "$norm"
|
|
|
|
exit "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
textblock() {
|
|
|
|
printf "%s\n" "$1" | fold -w "$width" -s
|
|
|
|
}
|
|
|
|
|
2020-03-26 02:43:00 -06:00
|
|
|
trapExit () {
|
2020-03-26 03:56:23 -06:00
|
|
|
cleanup
|
2020-03-26 02:43:00 -06:00
|
|
|
printf "%s\nScript execution terminated via signal.\n\n%s" "$err" "$norm"
|
|
|
|
exit 99
|
|
|
|
}
|
|
|
|
|
2020-03-26 02:21:59 -06:00
|
|
|
|
|
|
|
### text formatting presets
|
2020-03-26 03:25:54 -06:00
|
|
|
if command -v tput > /dev/null; then
|
|
|
|
err=$(tput bold)$(tput setaf 1)
|
|
|
|
norm=$(tput sgr0)
|
|
|
|
width=$(tput cols)
|
|
|
|
else
|
|
|
|
err="[ERROR] "
|
|
|
|
norm=""
|
|
|
|
width=80
|
|
|
|
fi
|
2020-03-26 02:21:59 -06:00
|
|
|
|
|
|
|
|
|
|
|
### pre-requisites
|
|
|
|
|
|
|
|
# is user root?
|
2020-03-26 04:33:16 -06:00
|
|
|
#if [ ! "$( id -u )" -eq 0 ]; then
|
|
|
|
# consoleError 1 'You must be root to run this script.'
|
|
|
|
#fi
|
2020-03-26 02:21:59 -06:00
|
|
|
|
|
|
|
# has a parameter been passed to this script?
|
|
|
|
if [ -z "$1" ]; then
|
|
|
|
consoleError 1 "No operation requested. Please run this script with '--help' for valid parameters."
|
|
|
|
fi
|
|
|
|
|
|
|
|
# process startup parameters
|
|
|
|
while [ $# -gt 0 ]; do
|
|
|
|
case "$1" in
|
|
|
|
-a|--archive)
|
|
|
|
# name of backup archive
|
|
|
|
if [ -z "$2" ]; then
|
|
|
|
consoleError 1 "Please provide the name of the backup archive you want to work with or use '--list-all' to get a full list."
|
|
|
|
fi
|
|
|
|
archiveName="$2"
|
|
|
|
shift
|
|
|
|
;;
|
2020-03-26 05:37:03 -06:00
|
|
|
--exclude)
|
|
|
|
# exclude files from restore
|
|
|
|
if [ -z "$2" ]; then
|
|
|
|
consoleError 1 "Please provide a list of exclusions in the proper borg format."
|
|
|
|
fi
|
|
|
|
exclusions="$2"
|
|
|
|
shift
|
|
|
|
;;
|
2020-03-26 02:21:59 -06:00
|
|
|
-f|--file)
|
|
|
|
# specific file to restore
|
|
|
|
if [ -z "$2" ]; then
|
|
|
|
consoleError 1 'Please provide the name of the specific file you want to restore.'
|
|
|
|
fi
|
|
|
|
fileName="$2"
|
|
|
|
shift
|
|
|
|
;;
|
|
|
|
-h|-\?|--help)
|
|
|
|
# display help
|
|
|
|
printf "\nStill working on the help text :-)\n\n"
|
|
|
|
exit 0
|
|
|
|
;;
|
2020-03-26 04:43:27 -06:00
|
|
|
-i|--info)
|
|
|
|
# show archive information
|
|
|
|
operation='info'
|
|
|
|
;;
|
2020-03-26 02:21:59 -06:00
|
|
|
-l|--list)
|
|
|
|
# list contents of specific backup
|
|
|
|
operation='viewarchive'
|
|
|
|
;;
|
|
|
|
|
|
|
|
-la|--list-all)
|
|
|
|
# list all backup archives
|
|
|
|
operation='listall'
|
|
|
|
;;
|
|
|
|
-p|--path)
|
|
|
|
# path to restore files
|
|
|
|
if [ -z "$2" ]; then
|
|
|
|
consoleError 1 'Please specify a path where you want files restored.'
|
|
|
|
fi
|
|
|
|
restorePath="${2%/}"
|
|
|
|
shift
|
|
|
|
;;
|
2020-03-26 04:40:36 -06:00
|
|
|
--progress)
|
|
|
|
# show progress
|
|
|
|
commonOptions="$commonOptions --progress"
|
|
|
|
;;
|
2020-03-26 02:25:51 -06:00
|
|
|
-r|--restore)
|
|
|
|
# restore archive/file
|
|
|
|
operation='restore'
|
|
|
|
;;
|
2020-03-26 02:21:59 -06:00
|
|
|
-v|--vars)
|
|
|
|
# location of borgvars file
|
|
|
|
if [ -z "$2" ]; then
|
|
|
|
consoleError 1 'Please provide the path to the file with your borg connection information.'
|
|
|
|
elif [ ! -f "$2" ]; then
|
|
|
|
consoleError 1 'The specified borg connection information file does not exist.'
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
varsFile="$2"
|
|
|
|
shift
|
|
|
|
;;
|
2020-03-26 04:40:36 -06:00
|
|
|
--verbose)
|
|
|
|
# display each file being processed
|
|
|
|
restoreOptions="$restoreOptions --list"
|
|
|
|
;;
|
2020-03-26 02:21:59 -06:00
|
|
|
*)
|
|
|
|
# invalid option
|
|
|
|
printf "%s\nUnknown option: %s\n" "$err" "$1"
|
|
|
|
printf "Use '--help' for valid options.\n\n%s" "$norm"
|
|
|
|
exit 1
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
shift
|
|
|
|
done
|
|
|
|
|
2020-03-26 02:35:30 -06:00
|
|
|
|
|
|
|
### check parameter validity
|
|
|
|
|
|
|
|
# no operation
|
|
|
|
if [ -z "$operation" ]; then
|
|
|
|
consoleError 1 'Nothing to do!'
|
|
|
|
fi
|
|
|
|
|
2020-03-26 02:49:20 -06:00
|
|
|
# no borg information file
|
|
|
|
if [ -z "$varsFile" ]; then
|
|
|
|
consoleError 1 'You must provide a valid .borgvars file with information about your borg repo.'
|
|
|
|
fi
|
|
|
|
|
2020-03-26 04:43:27 -06:00
|
|
|
# info without archive
|
|
|
|
if [ "$operation" = 'info' ] && [ -z "$archiveName" ]; then
|
|
|
|
consoleError 1 "Info operation requested but no archive name provided. Please use '--list-all' for a list of all available archives."
|
|
|
|
fi
|
|
|
|
|
2020-03-26 02:35:30 -06:00
|
|
|
# list without archive
|
2020-03-26 03:46:31 -06:00
|
|
|
if [ "$operation" = 'viewarchive' ] && [ -z "$archiveName" ]; then
|
2020-03-26 02:35:30 -06:00
|
|
|
consoleError 1 "List operation requested but no archive name provided. Please use '--list-all' for a list of all available archives."
|
|
|
|
fi
|
|
|
|
|
|
|
|
# restore with no path
|
|
|
|
if [ "$operation" = 'restore' ] && [ -z "$restorePath" ]; then
|
|
|
|
consoleError 1 "Restore operation requested but no restore path provided."
|
|
|
|
# restore with no archive
|
|
|
|
elif [ "$operation" = 'restore' ] && [ -z "$archiveName" ]; then
|
|
|
|
consoleError 1 "Restore operation requested but no archive name provided."
|
|
|
|
fi
|
|
|
|
|
|
|
|
# file provided but no archive
|
|
|
|
if [ "$fileName" ] && [ -z "$archiveName" ]; then
|
|
|
|
consoleError 1 "Filename specified without an associated archive name."
|
|
|
|
fi
|
|
|
|
|
2020-03-26 04:40:36 -06:00
|
|
|
# clean-up leading spaces in option strings
|
2020-03-26 05:37:03 -06:00
|
|
|
if [ "$commonOptions" ]; then commonOptions=${commonOptions##[[:space:]]}; fi
|
|
|
|
if [ "$restoreOptions" ]; then restoreOptions=${restoreOptions##[[:space:]]}; fi
|
2020-03-26 04:40:36 -06:00
|
|
|
|
2020-03-26 02:47:52 -06:00
|
|
|
|
|
|
|
### read borg information file
|
|
|
|
|
|
|
|
# check if file was provided as a relative or absolute path
|
|
|
|
case "${varsFile}" in
|
|
|
|
/*)
|
|
|
|
# absolute path, no need to rewrite variable
|
|
|
|
. "${varsFile}"
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
# relative path, prepend './' to create absolute path
|
|
|
|
. "./${varsFile}"
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
|
2020-03-26 03:08:22 -06:00
|
|
|
# verify borg base directory
|
|
|
|
if [ -z "${borgBaseDir}" ]; then
|
|
|
|
consoleError 2 "$varsFile: 'borgBaseDir' is not specified."
|
|
|
|
elif [ ! -d "${borgBaseDir}" ]; then
|
|
|
|
consoleError 2 "$varsFile: 'borgBaseDir' does not exist."
|
|
|
|
fi
|
|
|
|
export BORG_BASE_DIR="${borgBaseDir%/}"
|
|
|
|
|
|
|
|
## check path to SSH keyfile
|
|
|
|
if [ -z "${sshKeyFile}" ]; then
|
|
|
|
consoleError 2 "$varsFile: 'sshKeyFile' is not specified."
|
|
|
|
elif [ ! -f "${sshKeyFile}" ]; then
|
|
|
|
consoleError 2 "$varsFile: 'sshKeyFile' does not exist."
|
|
|
|
fi
|
|
|
|
export BORG_RSH="ssh -i ${sshKeyFile}"
|
|
|
|
|
|
|
|
# check borg repo connect string
|
|
|
|
if [ -z "${borgRepo}" ]; then
|
|
|
|
consoleError 2 "$varsFile: 'borgRepo' is not specified."
|
|
|
|
fi
|
|
|
|
export BORG_REPO="${borgRepo}"
|
|
|
|
|
|
|
|
# check borg repo password
|
|
|
|
if [ -n "${borgRepoPassword}" ]; then
|
|
|
|
export BORG_PASSPHRASE="${borgRepoPassword}"
|
2020-03-26 03:46:48 -06:00
|
|
|
elif [ "${borgRepoPassword}" = 'none' ]; then
|
2020-03-26 03:08:22 -06:00
|
|
|
export BORG_PASSPHRASE=""
|
|
|
|
else
|
2020-03-26 03:46:48 -06:00
|
|
|
consoleError 2 "$varsFile: 'borgRepoPassword' must be specified or must be 'none' if no password has been set (VERY INSECURE!)."
|
2020-03-26 03:08:22 -06:00
|
|
|
fi
|
|
|
|
|
|
|
|
# export borg remote path, if specified
|
|
|
|
if [ -n "${borgRemote}" ]; then export BORG_REMOTE_PATH="${borgRemote}"; fi
|
|
|
|
|
2020-03-26 02:47:52 -06:00
|
|
|
|
2020-03-26 03:56:23 -06:00
|
|
|
### create borg temp dir:
|
|
|
|
## python requires a writable temporary directory when unpacking borg and
|
|
|
|
## executing commands. This defaults to /tmp but many systems mount /tmp with
|
|
|
|
## the 'noexec' option for security. Thus, we will use/create a 'tmp' folder
|
|
|
|
## within the BORG_BASE_DIR and instruct python to use that instead of /tmp
|
|
|
|
|
|
|
|
# check if BORG_BASE_DIR/tmp exists, if not, create it
|
|
|
|
if [ ! -d "${borgBaseDir}/tmp" ]; then
|
|
|
|
if ! mkdir "${borgBaseDir}/tmp"; then
|
|
|
|
consoleError 3 'Unable to create temp working directory for borg.'
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
export TMPDIR="${borgBaseDir}/tmp"
|
2020-03-26 04:02:05 -06:00
|
|
|
|
|
|
|
|
2020-03-26 04:07:37 -06:00
|
|
|
### execute borg operations
|
|
|
|
|
2020-03-26 04:43:27 -06:00
|
|
|
# info operations
|
|
|
|
if [ "$operation" = 'info' ]; then
|
|
|
|
borg info ::"${archiveName}"
|
2020-03-26 04:07:37 -06:00
|
|
|
# list operations
|
2020-03-26 04:44:29 -06:00
|
|
|
elif [ "$operation" = 'listall' ]; then
|
2020-03-26 04:07:37 -06:00
|
|
|
borg list
|
|
|
|
elif [ "$operation" = 'viewarchive' ]; then
|
|
|
|
borg list ::"${archiveName}"
|
2020-03-26 04:33:16 -06:00
|
|
|
# restore operations
|
2020-03-26 04:44:29 -06:00
|
|
|
elif [ "$operation" = 'restore' ]; then
|
2020-03-26 04:33:16 -06:00
|
|
|
if [ -z "$fileName" ]; then
|
|
|
|
# restore entire archive
|
|
|
|
cd "$restorePath" || consoleError 4 'Could not change to restore directory.'
|
2020-03-26 05:37:03 -06:00
|
|
|
borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}"
|
2020-03-26 04:33:16 -06:00
|
|
|
elif [ "$fileName" ]; then
|
|
|
|
# restore single file
|
|
|
|
cd "$restorePath" || consoleError 4 'Could not change to restore directory.'
|
2020-03-26 05:37:03 -06:00
|
|
|
borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" "${fileName}"
|
2020-03-26 04:33:16 -06:00
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
2020-03-26 04:02:05 -06:00
|
|
|
|
|
|
|
### exit gracefully
|
|
|
|
cleanup
|
|
|
|
exit 0
|
|
|
|
|
|
|
|
|
|
|
|
### exit codes
|
|
|
|
# 0: no errors, script completed successfully
|
|
|
|
# 1: parameter error (missing, non-existant or invalid input)
|
|
|
|
# 2: parameter missing/invalid in .borgvars file
|
|
|
|
# 3: could not create/remove borg tmp directory
|
|
|
|
|
|
|
|
#EOF
|