2020-03-26 02:21:59 -06:00
#!/bin/sh
#
## borg helper script for viewing and restoring backups
2020-03-26 06:49:08 -06:00
##
## script written by Asif Bacchus, last updated March 26, 2020.
##
## The author of this script is not affiliated with 'borgbackup' in any way and
## does not warrant anything about this script, its operation, suitability or
## fitness for use in any environment or under any conditions. You are using
## this script entirely at your own risk.
2020-03-26 02:21:59 -06:00
#
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 06:43:38 -06:00
consoleError 3 " Script completed successfully but could not remove temporary directory at ' $borgBaseDir /tmp'. Sorry to be messy. "
fi
fi
if [ -f " ${ restorePath } /touch.test " ] ; then
if ! rm -f " ${ restorePath } /touch.test " > /dev/null 2>& 1; then
consoleError 5 " Script completed successfully but could not remove test file at ' $restorePath /touch.test'. 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 "
}
2020-03-26 06:27:34 -06:00
scriptHelp( ) {
printf "\n"
textblock " ${ bold } Usage: borghelper.sh [parameters] ${ norm } "
printf "\n"
textblock "Simple script to read connection parameters from a flat text file and process borg 'info', 'list' and 'restore' commands without the very long command lines that are required when specifying repo names and passwords, etc."
printf "\n"
textblock " ${ magenta } The script has the following parameters: ${ norm } "
printf "\n"
textblock " ${ magenta } --- Required Parameters --- ${ norm } "
printf "\n"
ptextblock "-v|--vars"
textblock "Path to the .borgvars file from which to read borg connection information. This is not required if run with '--makevars'."
printf "\n"
textblock " ${ magenta } --- Operation Modes --- ${ norm } "
printf "\n"
ptextblock "--makevars"
textblock "Create a sample .borgvars file that you can fill in and use with this script."
ptextblock "-i|--info"
textblock "Get information about a specified borg repo archive. Requires you supply '--archive'."
ptextblock "-l|--list"
2020-03-27 00:36:35 -06:00
textblock "List contents of a specified borg repo archive. Requires you supply '--archive'. You can optionally specify a file/pattern to search for using '--file'."
2020-03-26 06:27:34 -06:00
ptextblock "-la|--list-all"
textblock "List all available archives within the repo specified in your .borgvars file."
ptextblock "-r|--restore"
textblock "Restore the specified borg repo archive/file(s). Requires you supply '--archive'."
printf "\n"
textblock " ${ magenta } --- Selector Parameters --- ${ norm } "
printf "\n"
ptextblock "-a|--archive"
textblock "The archive within your borg repo you wish to work with."
ptextblock "--exclude"
textblock "Pattern (python/borg) of files to exclude from a restore operation."
ptextblock "-f|--file"
2020-03-27 00:36:35 -06:00
textblock "Specific file/pattern (python/borg) within an archive for which you want to restore or search. Requires that you supply '--archive'."
2020-03-26 06:27:34 -06:00
ptextblock "-p|--path"
textblock "Path to which you want your archive/files restored. This script will attempt to create the directory for you if it does not already exist."
printf "\n"
textblock " ${ magenta } --- Restore Options --- ${ norm } "
printf "\n"
ptextblock "--progress"
textblock "Display progress indicator during restore operations. WARNING: This can drastically slow down operations on larger archives!"
ptextblock "--verbose"
textblock "List the individual files being processed during restore operations."
printf "\n"
textblock " ${ magenta } --- Other Parameters --- ${ norm } "
printf "\n"
ptextblock "-h|-?|--help"
textblock "This help screen."
printf "\n"
exit 0
}
2020-03-26 02:21:59 -06:00
textblock( ) {
printf "%s\n" " $1 " | fold -w " $width " -s
}
2020-03-26 06:27:34 -06:00
ptextblock( ) {
printf "%s%s%s\n" " $cyan " " $1 " " $norm "
}
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
2020-03-26 06:27:34 -06:00
bold = $( tput bold)
cyan = $( tput setaf 6)
2020-03-26 03:25:54 -06:00
err = $( tput bold) $( tput setaf 1)
2020-03-26 06:27:34 -06:00
magenta = $( tput setaf 5)
2020-03-26 03:25:54 -06:00
norm = $( tput sgr0)
width = $( tput cols)
else
2020-03-26 06:27:34 -06:00
bold = ""
cyan = ""
2020-03-26 03:25:54 -06:00
err = "[ERROR] "
2020-03-26 06:27:34 -06:00
magenta = ""
2020-03-26 03:25:54 -06:00
norm = ""
width = 80
fi
2020-03-26 02:21:59 -06:00
### pre-requisites
# is user root?
2020-03-26 05:43:10 -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)
2020-03-27 00:36:35 -06:00
# specific file/pattern to restore
2020-03-26 02:21:59 -06:00
if [ -z " $2 " ] ; then
2020-03-27 00:36:35 -06:00
consoleError 1 'Please provide the name of the specific file/pattern for which you want to restore or search.'
2020-03-26 02:21:59 -06:00
fi
fileName = " $2 "
shift
; ;
-h| -\? | --help)
# display help
2020-03-26 06:27:34 -06:00
scriptHelp
2020-03-26 02:21:59 -06:00
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'
; ;
2020-03-26 05:49:23 -06:00
--makevars)
# make a borgvars template file
operation = 'makevars'
; ;
2020-03-26 02:21:59 -06:00
-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
2020-03-26 05:49:23 -06:00
### process 'makevars' operation
if [ " $operation " = 'makevars' ] ; then
if ! printf "sshKeyFile=\nborgBaseDir=\nborgRepo=\nborgRepoPassword=\nborgRemote=\n" > ./sample.borgvars; then
consoleError 4 'Could not write sample borgvars file.'
else
exit 0
fi
fi
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 06:44:38 -06:00
# check/create restore path
2020-03-26 08:06:01 -06:00
if [ " $operation " = 'restore' ] ; then
if [ -d " $restorePath " ] ; then
2020-03-27 02:59:17 -06:00
# convert to absolute path
restorePath = $(
cd " $restorePath " || \
consoleError 5 'Cannot access specified restore directory.' ; \
pwd -P
)
2020-03-26 06:44:38 -06:00
if ! touch " ${ restorePath } /touch.test " > /dev/null 2>& 1; then
2020-03-26 08:06:01 -06:00
consoleError 5 'Cannot write to specified restore directory.'
fi
else
if ! mkdir -p " ${ restorePath } " > /dev/null 2>& 1; then
consoleError 5 'Cannot create specified restore directory.'
else
2020-03-27 02:59:17 -06:00
# convert to absolute path
restorePath = $(
cd " $restorePath " || \
consoleError 5 'Cannot access specified restore directory.' ; \
pwd -P
)
2020-03-26 08:06:01 -06:00
if ! touch " ${ restorePath } /touch.test " > /dev/null 2>& 1; then
consoleError 5 'Cannot write to specified restore directory.'
fi
2020-03-26 06:44:38 -06:00
fi
fi
fi
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
2020-03-27 00:36:35 -06:00
if [ " $fileName " ] ; then
borg list ::" ${ archiveName } " " ${ fileName } "
else
borg list ::" ${ archiveName } "
fi
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 05:40:23 -06:00
cd " $restorePath " || consoleError 4 'Could not change to restore directory.'
2020-03-26 04:33:16 -06:00
if [ -z " $fileName " ] ; then
# restore entire archive
2020-03-26 05:40:23 -06:00
if [ " $exclusions " ] ; then
borg --show-rc ${ commonOptions } extract ${ restoreOptions } ::" ${ archiveName } " --exclude " ${ exclusions } "
else
borg --show-rc ${ commonOptions } extract ${ restoreOptions } ::" ${ archiveName } "
fi
2020-03-26 04:33:16 -06:00
elif [ " $fileName " ] ; then
2020-03-26 05:40:23 -06:00
# restore file/pattern
if [ " $exclusions " ] ; then
borg --show-rc ${ commonOptions } extract ${ restoreOptions } ::" ${ archiveName } " " ${ fileName } " --exclude " ${ exclusions } "
else
borg --show-rc ${ commonOptions } extract ${ restoreOptions } ::" ${ archiveName } " " ${ fileName } "
fi
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
2020-03-26 06:44:38 -06:00
# 4: could not write sample borgvars file (permissions?)
2020-03-27 02:59:17 -06:00
# 5: cannot access/create/write to restore path or could not remove test file
2020-03-26 04:02:05 -06:00
2020-03-26 06:44:38 -06:00
#EOF