Compare commits

...

4 Commits

Author SHA1 Message Date
Asif Bacchus
c73a3b8531 style: cleanup formatting, spelling
- use project-custom dictionary
- cleanup spelling mistakes
- rename functions to conform with camelCase for spellcheck
- reformat file: spacing, indentation
2021-05-15 09:15:57 -06:00
Asif Bacchus
8b201ca047 feature(add option for separate keyfile): 2021-05-15 09:05:04 -06:00
Asif Bacchus
a86f13a1e5 perf: run help automatically as needed 2021-05-15 08:58:39 -06:00
Asif Bacchus
7b87c246c3 struct: update mod time, remove vscode dir 2021-05-15 08:58:13 -06:00
7 changed files with 216 additions and 157 deletions

View File

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/.idea.borgScripts.iml
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
# Datasource local storage ignored files
/../../../../../../../../../:\Redirected\Asif\Documents\RiderProjects\borgScripts\.idea\.idea.borgScripts.dir\.idea/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@ -0,0 +1,12 @@
<component name="ProjectDictionaryState">
<dictionary name="asif">
<words>
<w>borgbackup</w>
<w>borghelper</w>
<w>borgvars</w>
<w>listall</w>
<w>makevars</w>
<w>viewarchive</w>
</words>
</dictionary>
</component>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ContentModelUserStore">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RIDER_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../.." />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CommitMessageInspectionProfile">
<profile version="1.0">
<inspection_tool class="BodyLimit" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="SubjectBodySeparation" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="SubjectLimit" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,3 +0,0 @@
{
"bookmarks": []
}

View File

@ -3,11 +3,11 @@
# #
## borg helper script for viewing and restoring backups ## borg helper script for viewing and restoring backups
## ##
## script written by Asif Bacchus, last updated March 26, 2020. ## script written by Asif Bacchus, last updated May 15, 2021.
## ##
## The author of this script is not affiliated with 'borgbackup' in any way and ## 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 ## does not warrant anything about this script, its operation, suitability or
## fitness for use in any environment or under any conditions. You are using ## fitness for use in any environment or under any conditions. You are using
## this script entirely at your own risk. ## this script entirely at your own risk.
# #
@ -15,15 +15,16 @@
trap trapExit 1 2 3 6 trap trapExit 1 2 3 6
### functions ### functions
cleanup () { cleanup() {
# remove borg temp directory, if it exists # remove borg temp directory, if it exists
# shellcheck disable=SC2154
if [ -d "${borgBaseDir}/tmp" ]; then if [ -d "${borgBaseDir}/tmp" ]; then
if ! rm -rf "${borgBaseDir}/tmp" > /dev/null 2>&1; then if ! rm -rf "${borgBaseDir}/tmp" >/dev/null 2>&1; then
consoleError 3 "Script completed successfully but could not remove temporary directory at '$borgBaseDir/tmp'. Sorry to be messy." consoleError 3 "Script completed successfully but could not remove temporary directory at '$borgBaseDir/tmp'. Sorry to be messy."
fi fi
fi fi
if [ -f "${restorePath}/touch.test" ]; then if [ -f "${restorePath}/touch.test" ]; then
if ! rm -f "${restorePath}/touch.test" > /dev/null 2>&1; 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." consoleError 5 "Script completed successfully but could not remove test file at '$restorePath/touch.test'. Sorry to be messy."
fi fi
fi fi
@ -37,73 +38,72 @@ consoleError() {
scriptHelp() { scriptHelp() {
printf "\n" printf "\n"
textblock "${bold}Usage: borghelper.sh [parameters]${norm}" textBlock "${bold}Usage: borghelper.sh [parameters]${norm}"
printf "\n" 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." 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" printf "\n"
textblock "${magenta}The script has the following parameters:${norm}" textBlock "${magenta}The script has the following parameters:${norm}"
printf "\n" printf "\n"
textblock "${magenta}--- Required Parameters ---${norm}" textBlock "${magenta}--- Required Parameters ---${norm}"
printf "\n" printf "\n"
ptextblock "-v|--vars" textBlockParams "-v|--vars"
textblock "Path to the .borgvars file from which to read borg connection information. This is not required if run with '--makevars'." textBlock "Path to the .borgvars file from which to read borg connection information. This is not required if run with '--makevars'."
printf "\n" printf "\n"
textblock "${magenta}--- Operation Modes ---${norm}" textBlock "${magenta}--- Operation Modes ---${norm}"
printf "\n" printf "\n"
ptextblock "--makevars" textBlockParams "--makevars"
textblock "Create a sample .borgvars file that you can fill in and use with this script." textBlock "Create a sample .borgvars file that you can fill in and use with this script."
ptextblock "-i|--info" textBlockParams "-i|--info"
textblock "Get information about a specified borg repo archive. Requires you supply '--archive'." textBlock "Get information about a specified borg repo archive. Requires you supply '--archive'."
ptextblock "-l|--list" textBlockParams "-l|--list"
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'." 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'."
ptextblock "-la|--list-all" textBlockParams "-la|--list-all"
textblock "List all available archives within the repo specified in your .borgvars file." textBlock "List all available archives within the repo specified in your .borgvars file."
ptextblock "-r|--restore" textBlockParams "-r|--restore"
textblock "Restore the specified borg repo archive/file(s). Requires you supply '--archive'." textBlock "Restore the specified borg repo archive/file(s). Requires you supply '--archive'."
printf "\n" printf "\n"
textblock "${magenta}--- Selector Parameters ---${norm}" textBlock "${magenta}--- Selector Parameters ---${norm}"
printf "\n" printf "\n"
ptextblock "-a|--archive" textBlockParams "-a|--archive"
textblock "The archive within your borg repo you wish to work with." textBlock "The archive within your borg repo you wish to work with."
ptextblock "--exclude" textBlockParams "--exclude"
textblock "Pattern (python/borg) of files to exclude from a restore operation." textBlock "Pattern (python/borg) of files to exclude from a restore operation."
ptextblock "-f|--file" textBlockParams "-f|--file"
textblock "Specific file/pattern (python/borg) within an archive for which you want to restore or search. Requires that you supply '--archive'." textBlock "Specific file/pattern (python/borg) within an archive for which you want to restore or search. Requires that you supply '--archive'."
ptextblock "-p|--path" textBlockParams "-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." 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" printf "\n"
textblock "${magenta}--- Restore Options ---${norm}" textBlock "${magenta}--- Restore Options ---${norm}"
printf "\n" printf "\n"
ptextblock "--progress" textBlockParams "--progress"
textblock "Display progress indicator during restore operations. WARNING: This can drastically slow down operations on larger archives!" textBlock "Display progress indicator during restore operations. WARNING: This can drastically slow down operations on larger archives!"
ptextblock "--verbose" textBlockParams "--verbose"
textblock "List the individual files being processed during restore operations." textBlock "List the individual files being processed during restore operations."
printf "\n" printf "\n"
textblock "${magenta}--- Other Parameters ---${norm}" textBlock "${magenta}--- Other Parameters ---${norm}"
printf "\n" printf "\n"
ptextblock "-h|-?|--help" textBlockParams "-h|-?|--help"
textblock "This help screen." textBlock "This help screen."
printf "\n" printf "\n"
exit 0 exit 0
} }
textblock() { textBlock() {
printf "%s\n" "$1" | fold -w "$width" -s printf "%s\n" "$1" | fold -w "$width" -s
} }
ptextblock() { textBlockParams() {
printf "%s%s%s\n" "$cyan" "$1" "$norm" printf "%s%s%s\n" "$cyan" "$1" "$norm"
} }
trapExit () { trapExit() {
cleanup cleanup
printf "%s\nScript execution terminated via signal.\n\n%s" "$err" "$norm" printf "%s\nScript execution terminated via signal.\n\n%s" "$err" "$norm"
exit 99 exit 99
} }
### text formatting presets ### text formatting presets
if command -v tput > /dev/null; then if command -v tput >/dev/null; then
bold=$(tput bold) bold=$(tput bold)
cyan=$(tput setaf 6) cyan=$(tput setaf 6)
err=$(tput bold)$(tput setaf 1) err=$(tput bold)$(tput setaf 1)
@ -119,113 +119,110 @@ else
width=80 width=80
fi fi
### pre-requisites ### pre-requisites
# is user root? # is user root?
if [ ! "$( id -u )" -eq 0 ]; then if [ ! "$(id -u)" -eq 0 ]; then
consoleError 1 'You must be root to run this script.' consoleError 1 'You must be root to run this script.'
fi fi
# has a parameter been passed to this script? # has a parameter been passed to this script?
if [ -z "$1" ]; then if [ -z "$1" ]; then
consoleError 1 "No operation requested. Please run this script with '--help' for valid parameters." scriptHelp
fi fi
# process startup parameters # process startup parameters
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case "$1" in case "$1" in
-a|--archive) -a | --archive)
# name of backup archive # name of backup archive
if [ -z "$2" ]; then 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." 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 fi
archiveName="$2" archiveName="$2"
shift shift
;; ;;
--exclude) --exclude)
# exclude files from restore # exclude files from restore
if [ -z "$2" ]; then if [ -z "$2" ]; then
consoleError 1 "Please provide a list of exclusions in the proper borg format." consoleError 1 "Please provide a list of exclusions in the proper borg format."
fi fi
exclusions="$2" exclusions="$2"
shift shift
;; ;;
-f|--file) -f | --file)
# specific file/pattern to restore # specific file/pattern to restore
if [ -z "$2" ]; then if [ -z "$2" ]; then
consoleError 1 'Please provide the name of the specific file/pattern for which you want to restore or search.' consoleError 1 'Please provide the name of the specific file/pattern for which you want to restore or search.'
fi fi
fileName="$2" fileName="$2"
shift shift
;; ;;
-h|-\?|--help) -h | -\? | --help)
# display help # display help
scriptHelp scriptHelp
exit 0 ;;
;; -i | --info)
-i|--info) # show archive information
# show archive information operation='info'
operation='info' ;;
;; -l | --list)
-l|--list) # list contents of specific backup
# list contents of specific backup operation='viewarchive'
operation='viewarchive' ;;
;;
-la | --list-all)
-la|--list-all) # list all backup archives
# list all backup archives operation='listall'
operation='listall' ;;
;; --makevars)
--makevars) # make a borgvars template file
# make a borgvars template file operation='makevars'
operation='makevars' ;;
;; -p | --path)
-p|--path) # path to restore files
# path to restore files if [ -z "$2" ]; then
if [ -z "$2" ]; then consoleError 1 'Please specify a path where you want files restored.'
consoleError 1 'Please specify a path where you want files restored.' fi
fi restorePath="${2%/}"
restorePath="${2%/}" shift
shift ;;
;; --progress)
--progress) # show progress
# show progress commonOptions="$commonOptions --progress"
commonOptions="$commonOptions --progress" ;;
;; -r | --restore)
-r|--restore) # restore archive/file
# restore archive/file operation='restore'
operation='restore' ;;
;; -v | --vars)
-v|--vars) # location of borgvars file
# location of borgvars file if [ -z "$2" ]; then
if [ -z "$2" ]; then consoleError 1 'Please provide the path to the file with your borg connection information.'
consoleError 1 'Please provide the path to the file with your borg connection information.' elif [ ! -f "$2" ]; then
elif [ ! -f "$2" ]; then consoleError 1 'The specified borg connection information file does not exist.'
consoleError 1 'The specified borg connection information file does not exist.'
exit 1
fi
varsFile="$2"
shift
;;
--verbose)
# display each file being processed
restoreOptions="$restoreOptions --list"
;;
*)
# invalid option
printf "%s\nUnknown option: %s\n" "$err" "$1"
printf "Use '--help' for valid options.\n\n%s" "$norm"
exit 1 exit 1
;; fi
varsFile="$2"
shift
;;
--verbose)
# display each file being processed
restoreOptions="$restoreOptions --list"
;;
*)
# invalid option
printf "%s\nUnknown option: %s\n" "$err" "$1"
printf "Use '--help' for valid options.\n\n%s" "$norm"
exit 1
;;
esac esac
shift shift
done done
### process 'makevars' operation ### process 'makevars' operation
if [ "$operation" = 'makevars' ]; then if [ "$operation" = 'makevars' ]; then
if ! printf "sshKeyFile=\nborgBaseDir=\nborgRepo=\nborgRepoPassword=\nborgRemote=\n" > ./sample.borgvars; then if ! printf "sshKeyFile=\nborgBaseDir=\nborgRepo=\nborgRepoPassword=\nborgRepoKey=\nborgRemote=\n" >./sample.borgvars; then
consoleError 4 'Could not write sample borgvars file.' consoleError 4 'Could not write sample borgvars file.'
else else
exit 0 exit 0
@ -276,25 +273,25 @@ if [ "$operation" = 'restore' ]; then
if [ -d "$restorePath" ]; then if [ -d "$restorePath" ]; then
# convert to absolute path # convert to absolute path
restorePath=$( restorePath=$(
cd "$restorePath" || \ cd "$restorePath" ||
consoleError 5 'Cannot access specified restore directory.'; \ consoleError 5 'Cannot access specified restore directory.'
pwd -P pwd -P
) )
if ! touch "${restorePath}/touch.test" > /dev/null 2>&1; then if ! touch "${restorePath}/touch.test" >/dev/null 2>&1; then
consoleError 5 'Cannot write to specified restore directory.' consoleError 5 'Cannot write to specified restore directory.'
fi fi
else else
if ! mkdir -p "${restorePath}" > /dev/null 2>&1; then if ! mkdir -p "${restorePath}" >/dev/null 2>&1; then
consoleError 5 'Cannot create specified restore directory.' consoleError 5 'Cannot create specified restore directory.'
else else
# convert to absolute path # convert to absolute path
restorePath=$( restorePath=$(
cd "$restorePath" || \ cd "$restorePath" ||
consoleError 5 'Cannot access specified restore directory.'; \ consoleError 5 'Cannot access specified restore directory.'
pwd -P pwd -P
) )
if ! touch "${restorePath}/touch.test" > /dev/null 2>&1; then if ! touch "${restorePath}/touch.test" >/dev/null 2>&1; then
consoleError 5 'Cannot write to specified restore directory.' consoleError 5 'Cannot write to specified restore directory.'
fi fi
fi fi
fi fi
@ -304,14 +301,16 @@ fi
# check if file was provided as a relative or absolute path # check if file was provided as a relative or absolute path
case "${varsFile}" in case "${varsFile}" in
/*) /*)
# absolute path, no need to rewrite variable # absolute path, no need to rewrite variable
. "${varsFile}" # shellcheck disable=SC1090
;; . "${varsFile}"
*) ;;
# relative path, prepend './' to create absolute path *)
. "./${varsFile}" # relative path, prepend './' to create absolute path
;; # shellcheck disable=SC1090
. "./${varsFile}"
;;
esac esac
# verify borg base directory # verify borg base directory
@ -323,6 +322,7 @@ fi
export BORG_BASE_DIR="${borgBaseDir%/}" export BORG_BASE_DIR="${borgBaseDir%/}"
## check path to SSH keyfile ## check path to SSH keyfile
# shellcheck disable=SC2154
if [ -z "${sshKeyFile}" ]; then if [ -z "${sshKeyFile}" ]; then
consoleError 2 "$varsFile: 'sshKeyFile' is not specified." consoleError 2 "$varsFile: 'sshKeyFile' is not specified."
elif [ ! -f "${sshKeyFile}" ]; then elif [ ! -f "${sshKeyFile}" ]; then
@ -331,12 +331,14 @@ fi
export BORG_RSH="ssh -i ${sshKeyFile}" export BORG_RSH="ssh -i ${sshKeyFile}"
# check borg repo connect string # check borg repo connect string
# shellcheck disable=SC2154
if [ -z "${borgRepo}" ]; then if [ -z "${borgRepo}" ]; then
consoleError 2 "$varsFile: 'borgRepo' is not specified." consoleError 2 "$varsFile: 'borgRepo' is not specified."
fi fi
export BORG_REPO="${borgRepo}" export BORG_REPO="${borgRepo}"
# check borg repo password # check borg repo password
# shellcheck disable=SC2154
if [ -n "${borgRepoPassword}" ]; then if [ -n "${borgRepoPassword}" ]; then
export BORG_PASSPHRASE="${borgRepoPassword}" export BORG_PASSPHRASE="${borgRepoPassword}"
elif [ "${borgRepoPassword}" = 'none' ]; then elif [ "${borgRepoPassword}" = 'none' ]; then
@ -345,9 +347,15 @@ else
consoleError 2 "$varsFile: 'borgRepoPassword' must be specified or must be 'none' if no password has been set (VERY INSECURE!)." consoleError 2 "$varsFile: 'borgRepoPassword' must be specified or must be 'none' if no password has been set (VERY INSECURE!)."
fi fi
# export borg remote path, if specified # check borg keyfile if supplied
if [ -n "${borgRemote}" ]; then export BORG_REMOTE_PATH="${borgRemote}"; fi # shellcheck disable=SC2154
if [ -n "${borgRepoKey}" ]; then
export BORG_KEY_FILE="${borgRepoKey}"
fi
# export borg remote path, if specified
# shellcheck disable=SC2154
if [ -n "${borgRemote}" ]; then export BORG_REMOTE_PATH="${borgRemote}"; fi
### create borg temp dir: ### create borg temp dir:
## python requires a writable temporary directory when unpacking borg and ## python requires a writable temporary directory when unpacking borg and
@ -363,7 +371,6 @@ if [ ! -d "${borgBaseDir}/tmp" ]; then
fi fi
export TMPDIR="${borgBaseDir}/tmp" export TMPDIR="${borgBaseDir}/tmp"
### execute borg operations ### execute borg operations
# info operations # info operations
@ -384,32 +391,34 @@ elif [ "$operation" = 'restore' ]; then
if [ -z "$fileName" ]; then if [ -z "$fileName" ]; then
# restore entire archive # restore entire archive
if [ "$exclusions" ]; then if [ "$exclusions" ]; then
# shellcheck disable=SC2086
borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" --exclude "${exclusions}" borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" --exclude "${exclusions}"
else else
# shellcheck disable=SC2086
borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}"
fi fi
elif [ "$fileName" ]; then elif [ "$fileName" ]; then
# restore file/pattern # restore file/pattern
if [ "$exclusions" ]; then if [ "$exclusions" ]; then
# shellcheck disable=SC2086
borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" "${fileName}" --exclude "${exclusions}" borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" "${fileName}" --exclude "${exclusions}"
else else
# shellcheck disable=SC2086
borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" "${fileName}" borg --show-rc ${commonOptions} extract ${restoreOptions} ::"${archiveName}" "${fileName}"
fi fi
fi fi
fi fi
### exit gracefully ### exit gracefully
cleanup cleanup
exit 0 exit 0
### exit codes ### exit codes
# 0: no errors, script completed successfully # 0: no errors, script completed successfully
# 1: parameter error (missing, non-existant or invalid input) # 1: parameter error (missing, non-existent or invalid input)
# 2: parameter missing/invalid in .borgvars file # 2: parameter missing/invalid in .borgvars file
# 3: could not create/remove borg tmp directory # 3: could not create/remove borg tmp directory
# 4: could not write sample borgvars file (permissions?) # 4: could not write sample borgvars file (permissions?)
# 5: cannot access/create/write to restore path or could not remove test file # 5: cannot access/create/write to restore path or could not remove test file
#EOF #EOF