#!/usr/bin/env bash script_name="$(basename $(realpath $0))" working_dir="$(dirname $(realpath $0))" if [[ -f "${working_dir}/conf/borg-backup-nc.conf" ]] ; then conf_file="${working_dir}/conf/borg-backup-nc.conf" else conf_file="${working_dir}/conf/borg-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 warn "No configuration file '$conf_file' present!" #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_BORG_HOSTNAME="" echo -e "\033[32m--\033[m" echo "" echo "Specify the computer that hosts the borg repository." echo "" if [[ -n "${_BORG_HOST}" ]] ; then while [[ "X${FQHN_BORG_HOSTNAME}" = "X" ]]; do echononl "Borg Repository Host [${_BORG_HOST}]: " read FQHN_BORG_HOSTNAME if [[ "X${FQHN_BORG_HOSTNAME}" = "X" ]]; then FQHN_BORG_HOSTNAME="${_BORG_HOST}" break fi if [[ ${FQHN_BORG_HOSTNAME} =~ ^127 ]] || [[ "${FQHN_BORG_HOSTNAME,,}" = "localhost" ]] ; then break fi if [[ ! ${FQHN_BORG_HOSTNAME} =~ \. ]]; then echo -e "\n\tGiven Host \033[33m\033[1m${FQHN_BORG_HOSTNAME}\033[m seems not to be a full qualified hostname.\n" FQHN_BORG_HOSTNAME="" fi done else while [[ "X${FQHN_BORG_HOSTNAME}" = "X" ]]; do echononl "Full borg hostname: " read FQHN_BORG_HOSTNAME if [[ ${FQHN_BORG_HOSTNAME} =~ ^127 ]] || [[ "${FQHN_BORG_HOSTNAME,,}" = "localhost" ]] ; then break fi if [[ "X${FQHN_BORG_HOSTNAME}" = "X" ]]; then echo -e "\n\t\033[33m\033[1mFull qualified hostname is reqired\033[m\n" fi if [[ ! $FQHN_BORG_HOSTNAME =~ \. ]]; then echo -e "\n\tGiven Host \033[33m\033[1m$FQHN_BORG_HOSTNAME\033[m seems not to be a full qualified hostname.\n" FQHN_BORG_HOSTNAME="" fi done fi BORG_HOST="${FQHN_BORG_HOSTNAME,,}" FQPN_BORG_REPO="" echo -e "\033[32m--\033[m" echo "" echo "Specify the borg repository path from which files are to be extracted." echo "" if [[ -n "${_BORG_REPO}" ]] ; then echononl "Full Qualified Borg Repository Path [${_BORG_REPO}]: " read FQPN_BORG_REPO if [[ "X${FQPN_BORG_REPO}" = "X" ]]; then FQPN_BORG_REPO="${_BORG_REPO}" fi else while [[ "X${FQPN_BORG_REPO}" = "X" ]]; do echononl "Full Qualified Borg Repository Path: " read FQPN_BORG_REPO if [[ "X${FQPN_BORG_REPO}" = "X" ]]; then echo -e "\n\t\033[33m\033[1mFull qualified hostname is reqired\033[m\n" fi done fi BORG_REPO="${FQPN_BORG_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 borg 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 [[ ! ${FQHN_BORG_HOSTNAME} =~ ^127 ]] && [[ ! "${FQHN_BORG_HOSTNAME,,}" = "localhost" ]] ; then SSH_USER="" echo -e "\033[32m--\033[m" echo "" echo "Specify the ssh user to connect to the Borg 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 Borg 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 Borg 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}/borg-list-backups.sh ) BORG_BACKUP_ID="" echo "" echo -e " \033[33m---------------------------------------------\033[m" echo " Which Borg Archiv should be extracted?" echo "" echo " Give a Borg Backup ID from above list" echo "" while [[ "X${BORG_BACKUP_ID}" = "X" ]]; do echo -n " Borg 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_BACKUP_SERVER="/backup/RESTORE/$(basename "${BORG_REPO}")/${BORG_BACKUP_ID}" _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 borg 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 borg extract script." echo "" echo " Type a number or press 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 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_BACKUP_SERVER}\" cd \"${BORG_RESTORE_PATH_BACKUP_SERVER}\" borg --remote-path borg extract ${BORG_URL}::${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}" borg --remote-path borg extract ${BORG_REPO}::${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}\" borg --remote-path borg extract ${BORG_URL}::${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_BACKUP_SERVER}\" cd "${BORG_RESTORE_PATH_BACKUP_SERVER}" borg --remote-path borg extract ${BORG_REPO}::${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 [[ ${BORG_HOST} =~ ^127 ]] || [[ ${BORG_HOST,,} =~ localhost ]] ; then borg --remote-path borg extract ${BORG_REPO}::${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 borg --remote-path borg extract ${BORG_URL}::${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