borg2-backup/borg2-extract-archiv.sh

676 lines
16 KiB
Bash
Executable File

#!/usr/bin/env bash
script_name="$(basename $(realpath $0))"
working_dir="$(dirname $(realpath $0))"
if [[ -f "${working_dir}/conf/borg2-backup-nc.conf" ]] ; then
conf_file="${working_dir}/conf/borg2-backup-nc.conf"
else
conf_file="${working_dir}/conf/borg2-backup.conf"
fi
LOCK_DIR="/tmp/${script_name%%.*}.LOCK"
log_file="${LOCK_DIR}/${script_name%%.*}.log"
# ----------
# Base Function(s)
# ----------
clean_up() {
# Perform program exit housekeeping
rm -rf "$LOCK_DIR"
blank_line
exit $1
}
echononl(){
if $terminal ; then
echo X\\c > /tmp/shprompt$$
if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then
echo -e -n " $*\\c" 1>&2
else
echo -e -n " $*" 1>&2
fi
rm /tmp/shprompt$$
fi
}
fatal(){
echo ""
if $terminal ; then
echo -e " [ \033[31m\033[1mFatal\033[m ]: $*"
echo ""
echo -e " \033[31m\033[1mScript was interupted\033[m!"
else
echo " [ Fatal ]: $*"
echo ""
echo " Script was terminated...."
fi
echo ""
clean_up 1
}
error (){
echo ""
if $terminal ; then
echo -e " [ \033[31m\033[1mError\033[m ]: $*"
else
echo "[ Error ]: $*"
fi
}
warn (){
echo ""
if $terminal ; then
echo -e " [ \033[33m\033[1mWarning\033[m ]: $*"
else
echo "[ Warning ]: $*"
fi
echo ""
}
warn_only_terminal () {
if $terminal ; then
echo ""
echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*"
echo ""
fi
}
info (){
if $terminal ; then
echo ""
echo -e " [ \033[32m\033[1mInfo\033[m ] $*"
echo ""
fi
}
ok (){
if $terminal ; then
echo ""
echo -e " [ \033[32m\033[1mOk\033[m ] $*"
echo ""
fi
}
echo_done() {
if $terminal ; then
echo -e "\033[75G[ \033[32mdone\033[m ]"
fi
}
echo_ok() {
if $terminal ; then
echo -e "\033[75G[ \033[32mok\033[m ]"
fi
}
echo_warn() {
if $terminal ; then
echo -e "\033[75G[ \033[33mwarn\033[m ]"
fi
}
echo_failed(){
if $terminal ; then
echo -e "\033[75G[ \033[1;31mfailed\033[m ]"
fi
}
echo_skipped() {
if $terminal ; then
echo -e "\033[75G[ \033[90m\033[1mskipped\033[m ]"
fi
}
echo_wait(){
if $terminal ; then
echo -en "\033[75G[ \033[5m\033[1m...\033[m ]"
fi
}
trim() {
local var="$*"
var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
blank_line() {
if $terminal ; then
echo ""
fi
}
# ----------
# - Some checks ..
# ----------
# - Running in a terminal?
# -
if [[ -t 1 ]] ; then
terminal=true
else
terminal=false
fi
# -------------
# - Job is already running?
# -------------
# - If job already runs, stop execution..
# -
if mkdir "$LOCK_DIR" 2> /dev/null ; then
## - Remove lockdir when the script finishes, or when it receives a signal
trap "clean_up 1" SIGHUP SIGINT SIGTERM
else
datum="$(date +"%d.%m.%Y %H:%M")"
msg="[ Error ]: A previos instance of \"`basename $0`\" seems already be running.\n\n Exiting now.."
echo ""
echo "[ Error ]: A previos instance of that script \"`basename $0`\" seems already be running."
echo ""
echo -e " Exiting now.."
echo ""
for _email in ${alert_email_arr[@]} ; do
echo -e "To:${_email}\n${content_type}\nSubject:Error cronjob `basename $0` -- $datum\n${msg}\n" \
| sendmail -F "Error `hostname -f`" -f $sender_address $_email
done
exit 1
fi
# ==========
# - Begin Main Script
# ==========
# ----------
# - Headline
# ----------
if $terminal ; then
echo ""
echo -e "\033[1m----------\033[m"
echo -e "\033[32m\033[1mRunning script \033[m\033[1m$script_name\033[32m .. \033[m"
echo -e "\033[1m----------\033[m"
echo ""
fi
# ----------
# Default values
# ----------
DEFAULT_SSH_USER="borg"
DEFAULT_SSH_PORT=22
DEFAULT_BORG_RSH='ssh -i /root/.ssh/id_ed25519-borg-backup'
# ----------
# Read Configurations from $conf_file
# ----------
if [[ -f "$conf_file" ]]; then
source "$conf_file"
else
fatal "No configuration file '$conf_file' present!"
fi
# ----------
# Set Parameter values
# ----------
#if [[ -z "${BORG_PASSPHRASE}" ]] ; then
# fatal "Borg Passphrase (BORG_PASSPHRASE) NOT found!"
#fi
[[ -n "${BORG_PASSPHRASE}" ]] && _BORG_PASSPHRASE="${BORG_PASSPHRASE}"
[[ -n "${BORG_HOST}" ]] && _BORG_HOST="${BORG_HOST}"
[[ -n "${BORG_REPO}" ]] && _BORG_REPO="${BORG_REPO}"
if [[ -n "${SSH_USER}" ]] ; then
_SSH_USER="${SSH_USER}"
else
_SSH_USER="${DEFAULT_SSH_USER}"
fi
if [[ -n "${SSH_PORT}" ]] ; then
_SSH_PORT="${SSH_PORT}"
else
_SSH_PORT="${DEFAULT_SSH_PORT}"
fi
if [[ -n "${BORG_RSH}" ]] ; then
_BORG_RSH="${BORG_RSH}"
else
_BORG_RSH="${DEFAULT_BORG_RSH}"
fi
[[ -z "${BORG_FUSE_MOUNT_DIR}" ]] && BORG_FUSE_MOUNT_DIR="${DEFAULT_BORG_FUSE_MOUNT_DIR}"
# ---
# Some automatic “answerers” (if set, they automatically answer confirmation questions):
# ---
# For 'Warning: The repository at location … was previously located at ..'
#
BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
export BORG_RELOCATED_REPO_ACCESS_IS_OK
FQHN_BORG2_HOSTNAME=""
echo -e "\033[32m--\033[m"
echo ""
echo "Specify the computer that hosts the borg2 repository."
echo ""
if [[ -n "${_BORG_HOST}" ]] ; then
while [[ "X${FQHN_BORG2_HOSTNAME}" = "X" ]]; do
echononl "Borg2 Repository Host [${_BORG_HOST}]: "
read FQHN_BORG2_HOSTNAME
if [[ "X${FQHN_BORG2_HOSTNAME}" = "X" ]]; then
FQHN_BORG2_HOSTNAME="${_BORG_HOST}"
break
fi
if [[ ${FQHN_BORG2_HOSTNAME} =~ ^127 ]] || [[ "${FQHN_BORG2_HOSTNAME,,}" =~ "localhost" ]] ; then
break
fi
if [[ ! ${FQHN_BORG2_HOSTNAME} =~ \. ]]; then
echo -e "\n\tGiven Host \033[33m\033[1m${FQHN_BORG2_HOSTNAME}\033[m seems not to be a full qualified hostname.\n"
FQHN_BORG2_HOSTNAME=""
fi
done
else
while [[ "X${FQHN_BORG2_HOSTNAME}" = "X" ]]; do
echononl "Full borg2 hostname: "
read FQHN_BORG2_HOSTNAME
if [[ ${FQHN_BORG2_HOSTNAME} =~ ^127 ]] || [[ "${FQHN_BORG2_HOSTNAME,,}" =~ "localhost" ]] ; then
break
fi
if [[ "X${FQHN_BORG2_HOSTNAME}" = "X" ]]; then
echo -e "\n\t\033[33m\033[1mFull qualified hostname is reqired\033[m\n"
fi
if [[ ! $FQHN_BORG2_HOSTNAME =~ \. ]]; then
echo -e "\n\tGiven Host \033[33m\033[1m$FQHN_BORG2_HOSTNAME\033[m seems not to be a full qualified hostname.\n"
FQHN_BORG2_HOSTNAME=""
fi
done
fi
BORG_HOST="${FQHN_BORG2_HOSTNAME,,}"
FQPN_BORG2_REPO=""
echo -e "\033[32m--\033[m"
echo ""
echo "Specify the borg2 repository path from which files are to be extracted."
echo ""
if [[ -n "${_BORG_REPO}" ]] ; then
echononl "Full Qualified Borg2 Repository Path [${_BORG_REPO}]: "
read FQPN_BORG2_REPO
if [[ "X${FQPN_BORG2_REPO}" = "X" ]]; then
FQPN_BORG2_REPO="${_BORG_REPO}"
fi
else
while [[ "X${FQPN_BORG2_REPO}" = "X" ]]; do
echononl "Full Qualified Borg2 Repository Path: "
read FQPN_BORG2_REPO
if [[ "X${FQPN_BORG2_REPO}" = "X" ]]; then
echo -e "\n\t\033[33m\033[1mFull qualified hostname is reqired\033[m\n"
fi
done
fi
BORG_REPO="${FQPN_BORG2_REPO}"
LIST_OF_FILES_TO_RESTORE=
echo -e "\033[32m--\033[m"
echo ""
echo "Specify a space-separated list of files or directories to be restored."
echo ""
echo -e " Type \"\033[33mall\033[m\" or leave emty to restore the full archive"
echo ""
echononl "List of files or directories: "
read LIST_OF_FILES_TO_RESTORE
if [[ "X${LIST_OF_FILES_TO_RESTORE}" = "X" ]] || [[ "$(trim ${LIST_OF_FILES_TO_RESTORE,,})" = 'all' ]] ; then
LIST_OF_FILES_TO_RESTORE=""
fi
BORG_PASSPHRASE=""
echo -e "\033[32m--\033[m"
echo ""
echo "Specify the password for borg2 repository '$(basename "${BORG_REPO}")'."
echo ""
if [[ -n "${_BORG_PASSPHRASE}" ]] ; then
echononl "Repositories Passphrase [${_BORG_PASSPHRASE}]: "
read BORG_PASSPHRASE
if [[ "X${BORG_PASSPHRASE}" = "X" ]]; then
BORG_PASSPHRASE="${_BORG_PASSPHRASE}"
fi
else
while [[ "X${BORG_PASSPHRASE}" = "X" ]]; do
echononl "Repositories Passphrase: "
read BORG_PASSPHRASE
if [[ "X${BORG_PASSPHRASE}" = "X" ]]; then
echo -e "\n\t\033[33m\033[1m is reqired\033[m\n"
fi
done
fi
if [[ ! ${BORG_HOST} =~ ^127 ]] && [[ ! ${BORG_HOST,,} =~ localhost ]] ; then
SSH_USER=""
echo -e "\033[32m--\033[m"
echo ""
echo "Specify the ssh user to connect to the Borg2 host '${BORG_HOST}'."
echo ""
echononl "SSH user [${_SSH_USER}]: "
read SSH_USER
if [[ "X${SSH_USER}" = "X" ]]; then
SSH_USER="${_SSH_USER}"
fi
SSH_PORT=""
echo -e "\033[32m--\033[m"
echo ""
echo "Specify the ssh port to connect to the Borg2 host '${BORG_HOST}'."
echo ""
while [[ "X${SSH_PORT}" = "X" ]]; do
echononl "SSH user [${_SSH_PORT}]: "
read SSH_PORT
if [[ "X${SSH_PORT}" = "X" ]]; then
SSH_PORT="${_SSH_PORT}"
fi
done
BORG_RSH=""
echo -e "\033[32m--\033[m"
echo ""
echo "Specify the ssh command which is used to connect to the Borg2 host '${BORG_HOST}'."
echo ""
while [[ "X${BORG_RSH}" = "X" ]]; do
echononl "SSH command [${_BORG_RSH}]: "
read BORG_RSH
if [[ "X${BORG_RSH}" = "X" ]]; then
BORG_RSH="${_BORG_RSH}"
fi
done
export BORG_RSH
fi
blank_line
echo -e "\033[32m--\033[m"
echo ""
export BORG_HOST
export BORG_PASSPHRASE
export BORG_REPO
# List Borg Backups
#
(
source ${working_dir}/borg2-list-backups.sh
)
BORG_BACKUP_ID=""
echo ""
echo -e " \033[33m---------------------------------------------\033[m"
echo " Which Borg2 Archiv should be extracted?"
echo ""
echo " Give a Borg2 Backup ID from above list"
echo ""
while [[ "X${BORG_BACKUP_ID}" = "X" ]]; do
echo -n " Borg2 Backup ID: "
read BORG_BACKUP_ID
if [[ "X$(trim ${BORG_BACKUP_ID})" = "X" ]] ; then
echo -e "\n\t\033[33m\033[1mIncorrect entry\033[m\n"
fi
done
echo -e " \033[33m---------------------------------------------\033[m"
echo ""
_BORG_RESTORE_PATH="/data/RESTORE/$(basename "${BORG_REPO}")/${BORG_BACKUP_ID}"
BORG_RESTORE_PATH=""
echo -e "\033[32m--\033[m"
echo ""
echo "Specify the pathname to which the archive or parts of it are to be restored.."
echo ""
while [[ "X${BORG_RESTORE_PATH}" = "X" ]]; do
echononl "FQPN of restore path [${_BORG_RESTORE_PATH}]: "
read BORG_RESTORE_PATH
if [[ "X${BORG_RESTORE_PATH}" = "X" ]]; then
BORG_RESTORE_PATH="${_BORG_RESTORE_PATH}"
fi
done
if [[ ${BORG_HOST} =~ ^127 ]] || [[ ${BORG_HOST,,} =~ localhost ]] ; then
BORG_URL="${BORG_REPO}"
else
BORG_URL="ssh://${SSH_USER}@${BORG_HOST}:${SSH_PORT}${BORG_REPO}"
fi
blank_line
echo ""
echo ""
echo -e "\t\033[32mStart script for extracting borg2 backup..\033[m"
echo ""
echo -e "\tBORG_HOST................: ${BORG_HOST}"
echo -e "\tBORG_REPO................: ${BORG_REPO}"
echo ""
echo -e "\tBORG_PASSPHRASE..........: ${BORG_PASSPHRASE}"
echo ""
echo -e "\tSSH_USER ................: ${SSH_USER}"
echo -e "\tSSH_PORT.................: ${SSH_PORT}"
echo -e "\tBORG_RSH.................: ${BORG_RSH}"
echo ""
echo -e "\tBORG_URL.................: ${BORG_URL}"
echo ""
echo -e "\tBORG_RESTORE_PATH........: ${BORG_RESTORE_PATH}"
if [[ -z "${LIST_OF_FILES_TO_RESTORE}" ]]; then
echo -e "\tLIST_OF_FILES_TO_RESTORE.: \033[33mRestore the full archive\033[m"
else
echo -e "\tLIST_OF_FILES_TO_RESTORE.: ${LIST_OF_FILES_TO_RESTORE}"
fi
echo ""
echo ""
declare -i _CHOOSE
echo -e " Ho to continue?"
echo ""
echo -e " \033[1m[1] Restore files from repository $(basename "${BORG_REPO}") archive ${BORG_BACKUP_ID}\033[m"
echo -e " [2] Only print out the commands for restoring files/folders"
echo -e " [3] Exit borg2 extract script."
echo ""
echo " Type a number or press <RETURN> to choose highlighted value"
echo ""
echononl "Eingabe: "
while [[ -z "${_CHOOSE}" ]]; do
read OPTION
[[ -z "$OPTION" ]] && OPTION=1
case $OPTION in
1) _CHOOSE=1
;;
2) _CHOOSE=2
;;
3) _CHOOSE=3
;;
*)
echo ""
echo -e "\tFalsche Eingabe ! [1/2/3] or type <RETURN> for the highlited default"
echo ""
echononl "Eingabe: "
;;
esac
done
if [[ ${_CHOOSE} -gt 2 ]] ; then
echo ""
clean_up 0
elif [[ ${_CHOOSE} -gt 1 ]] ; then
echo -e "\033[40m\033[37m
-----------------------------------------------------------------------------------------
export BORG_PASSPHRASE=\"${BORG_PASSPHRASE}\"
export BORG_REPO=\"${BORG_REPO}\"
export BORG_RSH=\"${BORG_RSH}\"
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
mkdir -p \"${BORG_RESTORE_PATH}\"
cd \"${BORG_RESTORE_PATH}\"
borg2 --remote-path borg2 --repo ${BORG_URL} extract ${BORG_BACKUP_ID} ${LIST_OF_FILES_TO_RESTORE}
"
if [[ ! ${BORG_HOST} =~ ^127 ]] && [[ ! ${BORG_HOST,,} =~ localhost ]] ; then
echo -e "
On Backup Host type:
====================
export BORG_PASSPHRASE=\"${BORG_PASSPHRASE}\"
export BORG_REPO=\"${BORG_REPO}\"
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
mkdir -p \"${BORG_RESTORE_PATH}\"
cd "${BORG_RESTORE_PATH}"
borg2 --remote-path borg2 --repo ${BORG_REPO} extract ${BORG_BACKUP_ID} ${LIST_OF_FILES_TO_RESTORE}
"
fi
echo -e "-----------------------------------------------------------------------------------------\033[m\n"
clean_up 0
fi
echo ""
info "\033[32mStart restoring from archive with the above settings\033[m?"
echo -n " To continue type uppercase 'YES': "
read OK
echo ""
if [[ "$OK" != "YES" ]] ; then
fatal "Abort by user request - Answer as not 'YES'"
fi
blank_line
echononl "Create Restore Directory '${BORG_RESTORE_PATH}'."
if [ ! -d "${BORG_RESTORE_PATH}" ]; then
mkdir -p "${BORG_RESTORE_PATH}" > ${log_file} 2>&1
if [[ $? -gt 0 ]] ; then
echo_failed
error "$(cat "${log_file}")"
fatal "Creating restore directory '${BORG_RESTORE_PATH}' failed!"
else
echo_ok
fi
else
echo_skipped
fi
blank_line
echononl "Change into Restore Path '${BORG_RESTORE_PATH}'.."
cd "${BORG_RESTORE_PATH}" > ${log_file} 2>&1
if [[ $? -gt 0 ]] ; then
echo_failed
fatal "$(cat "${log_file}")"
else
echo_ok
fi
blank_line
echo -e "\033[40m\033[37m
-----------------------------------------------------------------------------------------
export BORG_PASSPHRASE=\"${BORG_PASSPHRASE}\"
export BORG_REPO=\"${BORG_REPO}\"
export BORG_RSH=\"${BORG_RSH}\"
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
mkdir -p \"${BORG_RESTORE_PATH}\"
cd \"${BORG_RESTORE_PATH}\"
borg2 --remote-path borg2 --repo ${BORG_URL} extract ${BORG_BACKUP_ID} ${LIST_OF_FILES_TO_RESTORE}
"
if [[ ! ${BORG_HOST} =~ ^127 ]] && [[ ! ${BORG_HOST,,} =~ localhost ]] ; then
echo -e "
On Backup Host type:
====================
export BORG_PASSPHRASE=\"${BORG_PASSPHRASE}\"
export BORG_REPO=\"${BORG_REPO}\"
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
mkdir -p \"${BORG_RESTORE_PATH}\"
cd "${BORG_RESTORE_PATH}"
borg2 --remote-path borg2 --repo ${BORG_REPO} extract ${BORG_BACKUP_ID} ${LIST_OF_FILES_TO_RESTORE}
"
fi
echo -e "-----------------------------------------------------------------------------------------\033[m\n"
blank_line
echo " Restore from archive '$(basename "${BORG_REPO}")::${BORG_BACKUP_ID}' to"
echononl " '${BORG_RESTORE_PATH}'.."
if [[ ${FQHN_BORG2_HOSTNAME} =~ ^127 ]] || [[ "${FQHN_BORG2_HOSTNAME,,}" =~ "localhost" ]] ; then
borg2 --remote-path borg2 --repo ${BORG_REPO} extract ${BORG_BACKUP_ID} ${LIST_OF_FILES_TO_RESTORE} > "${log_file}" 2>&1
if [[ $? -gt 0 ]] ; then
echo_failed
error "$(cat "${log_file}")"
else
echo_ok
fi
else
borg2 --remote-path borg2 --repo ${BORG_URL} extract ${BORG_BACKUP_ID} ${LIST_OF_FILES_TO_RESTORE} > "${log_file}" 2>&1
if [[ $? -gt 0 ]] ; then
echo_failed
error "$(cat "${log_file}")"
else
echo_ok
fi
fi
blank_line
info "The data restored from the archive can be found in the directory:
\033[1m${BORG_RESTORE_PATH}\033[m"
clean_up 0