From 943d1c551e6ee2e0f417faa82a25bddcd55a8615 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 12:38:01 +0900 Subject: [PATCH 01/15] Stop trapping errors und unset enviromant vars On error just print error, but do not reset env vars, if this is done on verify error the init afterwards will store all settings in the wrong path --- borg.backup.functions.init.sh | 13 +++++++++---- borg.backup.functions.verify.sh | 9 ++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index 687571e..e00e663 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -7,14 +7,19 @@ fi; set -ETu #-e -o pipefail trap cleanup SIGINT SIGTERM ERR +trap error_trap ERR cleanup() { # script cleanup here - echo "Some part of the script failed with an error: $? @LINE: $(caller)"; + echo "Script abort: $? @LINE: $(caller)"; # unset exported vars unset BORG_BASE_DIR BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK BORG_RELOCATED_REPO_ACCESS_IS_OK; # end trap - trap - SIGINT SIGTERM ERR + trap - SIGINT SIGTERM +} +error_trap() { + echo "Some part of the script failed with an error: $? @LINE: $(caller)"; + trap - ERR } # on exit unset any exported var trap "unset BORG_BASE_DIR BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK BORG_RELOCATED_REPO_ACCESS_IS_OK" EXIT; @@ -348,7 +353,7 @@ if [ -n "${ONE_TIME_TAG}" ] && ! [[ "${ONE_TIME_TAG}" =~ ^[A-Za-z0-9_-]+$ ]]; th echo "One time tag '${ONE_TIME_TAG}' must be alphanumeric with dashes and underscore only."; exit 1; elif [ -n "${ONE_TIME_TAG}" ]; then - # all ok, attach . at the end + # all ok, attach '.' at the end ONE_TIME_TAG=${ONE_TIME_TAG}"."; fi; # if -D, cannot be with -T, -i, -C, -I, -P @@ -359,7 +364,7 @@ fi; # -D also must be in valid backup set format # ! [[ "${DELETE_ONE_TIME_TAG}" =~ ^[A-Za-z0-9_-]+\.${MODULE},(\*-)?[0-9]{4}-[0-9]{2}-[0-9]{2}T\*$ ]] if [ -n "${DELETE_ONE_TIME_TAG}" ] && ! [[ "${DELETE_ONE_TIME_TAG}" =~ ^[A-Za-z0-9_-]+\.${MODULE},([A-Za-z0-9_-]+-)?[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$ ]] && ! [[ "${DELETE_ONE_TIME_TAG}" =~ ^[A-Za-z0-9_-]+\.${MODULE},(\*-)?[0-9]{4}-[0-9]{2}-[0-9]{2}T\*$ ]]; then - echo "Delete one time tag '${DELETE_ONE_TIME_TAG}' is in an invalid format. " + echo "Delete one time tag '${DELETE_ONE_TIME_TAG}' is in an invalid format." echo "Please verify existing tags with -P option." echo "For a globing be sure it is in the format of: TAG.MODULE,*-YYYY-MM-DDT*"; echo "Note the dash (-) after the first *, also time (T) is a globa (*) must." diff --git a/borg.backup.functions.verify.sh b/borg.backup.functions.verify.sh index 56d7fe7..990ff28 100644 --- a/borg.backup.functions.verify.sh +++ b/borg.backup.functions.verify.sh @@ -299,7 +299,14 @@ if [ "${VERIFY}" -eq 1 ] || [ "${INIT}" -eq 1 ]; then echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY} 2>&1|grep \"Repository ID:\""; fi; # use borg info and verify if it returns "Repository ID:" in the first line - REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1|grep "Repository ID:"); + REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1); + if ! $?; then + echo "[!] Repository verify error: ${REPO_VERIFY}"; + REPO_VERIFY=""; + else + REPO_VERIFY=$(echo "${REPO_VERIFY}" | grep "Repository ID:"); + fi; + # | grep "Repository ID:" # this is currently a hack to work round the error code in borg info # this checks if REPO_VERIFY holds this error message and then starts init if [[ -z "${REPO_VERIFY}" ]] || [[ "${REPO_VERIFY}" =~ ${REGEX_ERROR} ]]; then From c5d88a64e1c36cdce52fcd5ddb564b2dbf392fc3 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 12:43:52 +0900 Subject: [PATCH 02/15] Fix last command check for verify --- borg.backup.functions.verify.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/borg.backup.functions.verify.sh b/borg.backup.functions.verify.sh index 990ff28..8f3a98a 100644 --- a/borg.backup.functions.verify.sh +++ b/borg.backup.functions.verify.sh @@ -296,11 +296,11 @@ if [ "${VERIFY}" -eq 1 ] || [ "${INIT}" -eq 1 ]; then printf "${PRINTF_SUB_BLOCK}" "VERIFY" "$(date +'%F %T')" "${MODULE}"; if [ -n "${TARGET_SERVER}" ]; then if [ "${DEBUG}" -eq 1 ]; then - echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY} 2>&1|grep \"Repository ID:\""; + echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY} 2>&1"; fi; # use borg info and verify if it returns "Repository ID:" in the first line REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1); - if ! $?; then + if [ ! $? ]; then echo "[!] Repository verify error: ${REPO_VERIFY}"; REPO_VERIFY=""; else From f41dd1b7236c21a57f844e3571645fc82a981ed7 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 12:45:56 +0900 Subject: [PATCH 03/15] Change verify command error check --- borg.backup.functions.verify.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/borg.backup.functions.verify.sh b/borg.backup.functions.verify.sh index 8f3a98a..6bcd6dc 100644 --- a/borg.backup.functions.verify.sh +++ b/borg.backup.functions.verify.sh @@ -300,7 +300,8 @@ if [ "${VERIFY}" -eq 1 ] || [ "${INIT}" -eq 1 ]; then fi; # use borg info and verify if it returns "Repository ID:" in the first line REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1); - if [ ! $? ]; then + __LAST_ERROR=$? + if [ $__LAST_ERROR -ne 0 ]; then echo "[!] Repository verify error: ${REPO_VERIFY}"; REPO_VERIFY=""; else From 8a8de773df69eb7a586f4576cf69618f8ffb2c22 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 12:47:49 +0900 Subject: [PATCH 04/15] Change borg repository verify error message collection --- borg.backup.functions.verify.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/borg.backup.functions.verify.sh b/borg.backup.functions.verify.sh index 6bcd6dc..65618bc 100644 --- a/borg.backup.functions.verify.sh +++ b/borg.backup.functions.verify.sh @@ -296,10 +296,10 @@ if [ "${VERIFY}" -eq 1 ] || [ "${INIT}" -eq 1 ]; then printf "${PRINTF_SUB_BLOCK}" "VERIFY" "$(date +'%F %T')" "${MODULE}"; if [ -n "${TARGET_SERVER}" ]; then if [ "${DEBUG}" -eq 1 ]; then - echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY} 2>&1"; + echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY}"; fi; # use borg info and verify if it returns "Repository ID:" in the first line - REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1); + REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}"); __LAST_ERROR=$? if [ $__LAST_ERROR -ne 0 ]; then echo "[!] Repository verify error: ${REPO_VERIFY}"; From 9e0db8bae9235a40b9d8c3318dad78b290f7f3b7 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 13:03:46 +0900 Subject: [PATCH 05/15] Fix the error trap with exit to stop calling it again with itself --- borg.backup.functions.init.sh | 5 +++-- borg.backup.functions.verify.sh | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index e00e663..f1612f4 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -18,8 +18,9 @@ cleanup() { trap - SIGINT SIGTERM } error_trap() { - echo "Some part of the script failed with an error: $? @LINE: $(caller)"; - trap - ERR + echo "Some part of the script failed with an error: $? @COMMAND: '$BASH_COMMAND' @LINE: $(caller)"; + # exit caller so we do not catch the same error again + exit 0; } # on exit unset any exported var trap "unset BORG_BASE_DIR BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK BORG_RELOCATED_REPO_ACCESS_IS_OK" EXIT; diff --git a/borg.backup.functions.verify.sh b/borg.backup.functions.verify.sh index 65618bc..3d48382 100644 --- a/borg.backup.functions.verify.sh +++ b/borg.backup.functions.verify.sh @@ -296,10 +296,10 @@ if [ "${VERIFY}" -eq 1 ] || [ "${INIT}" -eq 1 ]; then printf "${PRINTF_SUB_BLOCK}" "VERIFY" "$(date +'%F %T')" "${MODULE}"; if [ -n "${TARGET_SERVER}" ]; then if [ "${DEBUG}" -eq 1 ]; then - echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY}"; + echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY} 2>&1 "; fi; # use borg info and verify if it returns "Repository ID:" in the first line - REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}"); + REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1); __LAST_ERROR=$? if [ $__LAST_ERROR -ne 0 ]; then echo "[!] Repository verify error: ${REPO_VERIFY}"; From 6023fac6369c47e218e197d5aca708a1817b90a4 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 13:05:11 +0900 Subject: [PATCH 06/15] Fix trap clanup that also trapped ERR --- borg.backup.functions.init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index f1612f4..cf357fa 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -6,8 +6,8 @@ if [ -z "${MODULE}" ]; then fi; set -ETu #-e -o pipefail -trap cleanup SIGINT SIGTERM ERR trap error_trap ERR +trap cleanup SIGINT SIGTERM cleanup() { # script cleanup here From e5c5df601312d4674fd3ef070edc713274b7263d Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 13:08:35 +0900 Subject: [PATCH 07/15] Fix error trap with return error to STD ERR --- borg.backup.functions.init.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index cf357fa..407a0d6 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -6,10 +6,10 @@ if [ -z "${MODULE}" ]; then fi; set -ETu #-e -o pipefail -trap error_trap ERR -trap cleanup SIGINT SIGTERM +trap _catch ERR +trap _cleanup SIGINT SIGTERM -cleanup() { +_cleanup() { # script cleanup here echo "Script abort: $? @LINE: $(caller)"; # unset exported vars @@ -17,8 +17,8 @@ cleanup() { # end trap trap - SIGINT SIGTERM } -error_trap() { - echo "Some part of the script failed with an error: $? @COMMAND: '$BASH_COMMAND' @LINE: $(caller)"; +_catch() { + echo "Some part of the script failed with ERROR: $? @COMMAND: '$BASH_COMMAND' @LINE: $(caller)" >&2; # exit caller so we do not catch the same error again exit 0; } From d0e96a82e0f9b566c9a5adc826e0a5691d9dac99 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 14:00:49 +0900 Subject: [PATCH 08/15] Clean up PostgreSQL call, clean up varify error block, ERR trap fixes ERR trap is now returning last error so $? can bet used Verify catches $? error now for INIT repository check --- borg.backup.functions.init.sh | 10 ++++++---- borg.backup.functions.verify.sh | 33 +++++++++++---------------------- borg.backup.pgsql.sh | 33 ++++++++++++++++++--------------- 3 files changed, 35 insertions(+), 41 deletions(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index 407a0d6..8854d0d 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -5,6 +5,9 @@ if [ -z "${MODULE}" ]; then exit 1; fi; +# E: inherit trap ERR +# T: DEBUG and RETURN traps are inherited +# u: unset variables ere error set -ETu #-e -o pipefail trap _catch ERR trap _cleanup SIGINT SIGTERM @@ -18,9 +21,8 @@ _cleanup() { trap - SIGINT SIGTERM } _catch() { - echo "Some part of the script failed with ERROR: $? @COMMAND: '$BASH_COMMAND' @LINE: $(caller)" >&2; - # exit caller so we do not catch the same error again - exit 0; + local last_exit_code=$?; + echo "Some part of the script failed with ERROR: $last_exit_code @COMMAND: '$BASH_COMMAND' @LINE: $(caller)" >&2; } # on exit unset any exported var trap "unset BORG_BASE_DIR BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK BORG_RELOCATED_REPO_ACCESS_IS_OK" EXIT; @@ -91,7 +93,7 @@ REGEX=""; REGEX_COMMENT="^[\ \t]*#"; REGEX_GLOB='\*'; REGEX_NUMERIC="^[0-9]{1,2}$"; -REGEX_ERROR="^Some part of the script failed with an error:"; +REGEX_ERROR="^Some part of the script failed with ERROR:"; PRUNE_DEBUG=""; INIT_REPOSITORY=0; FOLDER_OK=0; diff --git a/borg.backup.functions.verify.sh b/borg.backup.functions.verify.sh index 3d48382..76b4c68 100644 --- a/borg.backup.functions.verify.sh +++ b/borg.backup.functions.verify.sh @@ -294,26 +294,15 @@ COMMAND_INFO="${COMMAND_EXPORT}${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY}" # MARK: VERIFY / INFO if [ "${VERIFY}" -eq 1 ] || [ "${INIT}" -eq 1 ]; then printf "${PRINTF_SUB_BLOCK}" "VERIFY" "$(date +'%F %T')" "${MODULE}"; - if [ -n "${TARGET_SERVER}" ]; then - if [ "${DEBUG}" -eq 1 ]; then - echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY} 2>&1 "; - fi; - # use borg info and verify if it returns "Repository ID:" in the first line - REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1); - __LAST_ERROR=$? - if [ $__LAST_ERROR -ne 0 ]; then - echo "[!] Repository verify error: ${REPO_VERIFY}"; - REPO_VERIFY=""; - else - REPO_VERIFY=$(echo "${REPO_VERIFY}" | grep "Repository ID:"); - fi; - # | grep "Repository ID:" - # this is currently a hack to work round the error code in borg info - # this checks if REPO_VERIFY holds this error message and then starts init - if [[ -z "${REPO_VERIFY}" ]] || [[ "${REPO_VERIFY}" =~ ${REGEX_ERROR} ]]; then - INIT_REPOSITORY=1; - fi; - elif [ ! -d "${REPOSITORY}" ]; then + if [ "${DEBUG}" -eq 1 ]; then + echo "${BORG_COMMAND} info ${OPT_REMOTE} ${REPOSITORY} 2>&1 "; + fi; + # use borg info and verify if it returns "Repository ID:" in the first line + REPO_VERIFY=$(${BORG_COMMAND} info ${OPT_REMOTE} "${REPOSITORY}" 2>&1); + __last_error=$?; + # on any error in verify command force new INIT + if [[ $__last_error -ne 0 ]]; then + echo "[!] Repository verify error: ${REPO_VERIFY}"; INIT_REPOSITORY=1; fi; # if verrify but no init and repo is there but init file is missing set it @@ -370,7 +359,7 @@ if [ "${INIT}" -eq 1 ] && [ "${INIT_REPOSITORY}" -eq 1 ]; then # exit after init exit; elif [ "${INIT}" -eq 1 ] && [ "${INIT_REPOSITORY}" -eq 0 ]; then - echo "[! $(date +'%F %T')] Repository already initialized"; + echo "[!] ($(date +'%F %T')) Repository already initialized"; echo "For more information run:" echo "${COMMAND_INFO}"; . "${DIR}/borg.backup.functions.close.sh" 1; @@ -379,7 +368,7 @@ fi; # verify for init file if [ ! -f "${BASE_FOLDER}${BACKUP_INIT_FILE}" ]; then - echo "[! $(date +'%F %T')] It seems the repository has never been initialized." + echo "[!] ($(date +'%F %T')) It seems the repository has never been initialized." echo "Please run -I to initialize or if already initialzed run with -C for init update." . "${DIR}/borg.backup.functions.close.sh" 1; exit 1; diff --git a/borg.backup.pgsql.sh b/borg.backup.pgsql.sh index 8d5011d..6c96209 100755 --- a/borg.backup.pgsql.sh +++ b/borg.backup.pgsql.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# allow variables in printf format string +# shellcheck disable=SC2059 + # Backup PostgreSQL # default is per table dump, can be set to one full dump # config override set in borg.backup.pgsql.settings @@ -32,7 +35,7 @@ BACKUP_LOCK_FILE="borg.backup.${MODULE}.lock"; # if info print info and then abort run . "${DIR}/borg.backup.functions.info.sh"; -if [ ! -z "${DATABASE_USER}" ]; then +if [ -n "${DATABASE_USER}" ]; then DB_USER=${DATABASE_USER}; else DB_USER='postgres'; @@ -95,7 +98,7 @@ CONN_DB_HOST=''; # -h CONN_DB_PORT=''; # -p # ALL IN ONE FILE or PER DATABASE FLAG -if [ ! -z "${DATABASE_FULL_DUMP}" ]; then +if [ -n "${DATABASE_FULL_DUMP}" ]; then SCHEMA_ONLY=''; schema_flag='data'; if [ "${DATABASE_FULL_DUMP}" = "schema" ]; then @@ -120,7 +123,7 @@ if [ ! -z "${DATABASE_FULL_DUMP}" ]; then fi; fi; if [ ${DRYRUN} -eq 0 ]; then - $(${PG_DUMPALL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} ${SCHEMA_ONLY} -c | ${BORG_CALL}); + ${PG_DUMPALL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} ${SCHEMA_ONLY} -c | ${BORG_CALL}; _backup_error=$?; if [ $_backup_error -ne 0 ]; then echo "[! $(date +'%F %T')] Backup creation failed for full dump with error code: ${_backup_error}"; @@ -133,7 +136,7 @@ if [ ! -z "${DATABASE_FULL_DUMP}" ]; then echo "Prune repository with keep${KEEP_INFO:1}"; ${BORG_PRUNE}; fi; - DURATION=$[ $(date +'%s')-$LOCAL_START ]; + DURATION=$(( $(date +'%s') - LOCAL_START )); printf "${PRINTF_DB_RUN_TIME_SUB_BLOCK}" "DONE" "all databases" "${MODULE}" "$(convert_time ${DURATION})"; else # dump globals first @@ -170,21 +173,21 @@ else echo "Prune repository with keep${KEEP_INFO:1}"; ${BORG_PRUNE}; fi; - DURATION=$[ $(date +'%s')-$LOCAL_START ]; + DURATION=$(( $(date +'%s') - LOCAL_START )); printf "${PRINTF_DB_RUN_TIME_SUB_BLOCK}" "DONE" "${db}" "${MODULE}" "$(convert_time ${DURATION})"; # get list of tables - for owner_db in $(${PG_PSQL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -d template1 -t -A -F "," -X -q -c "SELECT pg_catalog.pg_get_userbyid(datdba) AS owner, datname, pg_catalog.pg_encoding_to_char(encoding) AS encoding FROM pg_catalog.pg_database WHERE datname "\!"~ 'template(0|1)' ORDER BY datname;"); do + for owner_db in $(${PG_PSQL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -d template1 -t -A -F "," -X -q -c "SELECT pg_catalog.pg_get_userbyid(datdba) AS owner, datname, pg_catalog.pg_encoding_to_char(encoding) AS encoding FROM pg_catalog.pg_database WHERE datname ~ 'template(0|1)' ORDER BY datname;"); do LOCAL_START=$(date +'%s'); # get the user who owns the DB too - owner=$(echo ${owner_db} | cut -d "," -f 1); - db=$(echo ${owner_db} | cut -d "," -f 2); - encoding=$(echo ${owner_db} | cut -d "," -f 3); + owner=$(echo "${owner_db}" | cut -d "," -f 1); + db=$(echo "${owner_db}" | cut -d "," -f 2); + encoding=$(echo "${owner_db}" | cut -d "," -f 3); printf "${PRINTF_DB_SUB_BLOCK}" "DB" "${db}" "${MODULE}"; printf "${PRINTF_SUBEXT_BLOCK}" "BACKUP" "${db}" "$(date +'%F %T')" "${MODULE}"; include=0; if [ -s "${BASE_FOLDER}${INCLUDE_FILE}" ]; then - while read incl_db; do + while read -r incl_db; do if [ "${db}" = "${incl_db}" ]; then include=1; break; @@ -195,7 +198,7 @@ else fi; exclude=0; if [ -f "${BASE_FOLDER}${EXCLUDE_FILE}" ]; then - while read excl_db; do + while read -r excl_db; do if [ "${db}" = "${excl_db}" ]; then exclude=1; break; @@ -211,7 +214,7 @@ else # default is data dump SCHEMA_ONLY=''; schema_flag='data'; - while read schema_db; do + while read -r schema_db; do if [ "${db}" = "${schema_db}" ]; then SCHEMA_ONLY='-s'; schema_flag='schema'; @@ -223,7 +226,7 @@ else # default to schema, unless in data list SCHEMA_ONLY='-s'; schema_flag='schema'; - while read data_db; do + while read -r data_db; do if [ "${db}" = "${data_db}" ]; then SCHEMA_ONLY=''; schema_flag='data'; @@ -256,7 +259,7 @@ else fi; fi; if [ ${DRYRUN} -eq 0 ]; then - ${PG_DUMP} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -c ${SCHEMA_ONLY} --format=c ${db} | ${BORG_CALL}; + ${PG_DUMP} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -c ${SCHEMA_ONLY} --format=c "${db}" | ${BORG_CALL}; _backup_error=$?; if [ $_backup_error -ne 0 ]; then echo "[! $(date +'%F %T')] Backup creation failed for ${db} dump with error code: ${_backup_error}"; @@ -272,7 +275,7 @@ else else echo "- [E] ${db}"; fi; - DURATION=$[ $(date +'%s')-$LOCAL_START ]; + DURATION=$(( $(date +'%s') - LOCAL_START )); printf "${PRINTF_DB_RUN_TIME_SUB_BLOCK}" "DONE" "${db}" "${MODULE}" "$(convert_time ${DURATION})"; done; fi; From 4a95049d4f6a4e749ce19a734bd99c8a5ca7b36b Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 14:12:24 +0900 Subject: [PATCH 09/15] Default config file info update --- borg.backup.settings-default | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/borg.backup.settings-default b/borg.backup.settings-default index 393a407..6f9e386 100644 --- a/borg.backup.settings-default +++ b/borg.backup.settings-default @@ -15,19 +15,17 @@ TARGET_BORG_PATH=""; TARGET_FOLDER=""; # the backup file (folder) for this host $(hostname), must end with .borg BACKUP_FILE=""; -# compression settings (empty for none, lz4, zstd, zlib, lzma) -# level, if empty then default, else number between 0 and 9, or 1 to 22 for zstd -# default is zstd, 3 +# compression settings (none, lz4, zstd, zlib, lzma) +# empty is default zstd COMPRESSION=""; +# compression level, if empty then default 3, else number between 0 and 9, or 1 to 22 for zstd COMPRESSION_LEVEL=""; # encryption settings: # SHA-256: 'none', 'authenticated', 'repokey', 'keyfile' # BLAKE2b: 'authenticated-blake2', 'repokey-blake2', 'keyfile-blake2' -# Note: none does not encrypt -# Blank passwords allowed for only key (if used, use keyfile) +# Note: none does not encrypt and is not recommended # Default is keyfile -# passwords have to be set via BORG_PASSPHRASE or BORG_PASSCOMMAND -# keyfile can have blank passwords +# Blank passwords allowed for only keyfile, else passwords have to be set via BORG_PASSPHRASE or BORG_PASSCOMMAND # See: http://borgbackup.readthedocs.io/en/stable/faq.html#how-can-i-specify-the-encryption-passphrase-programmatically ENCRYPTION="" # force repository verify, default is off, set to true for verify on every run From 69be76e34cbc378c3d3e75a72841c3f5ebc6f0eb Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 14:48:54 +0900 Subject: [PATCH 10/15] Default settings file update for BACKUP_FILE --- borg.backup.settings-default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/borg.backup.settings-default b/borg.backup.settings-default index 6f9e386..2b91f93 100644 --- a/borg.backup.settings-default +++ b/borg.backup.settings-default @@ -13,7 +13,7 @@ TARGET_PORT=""; TARGET_BORG_PATH=""; # folder where the backup folder will be created TARGET_FOLDER=""; -# the backup file (folder) for this host $(hostname), must end with .borg +# the backup file (folder) for this backup, avoid dynamic variables in here, must end with .borg BACKUP_FILE=""; # compression settings (none, lz4, zstd, zlib, lzma) # empty is default zstd From 12ff648504b665fa364befcfd7167ee7cfdc6d46 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 15:31:55 +0900 Subject: [PATCH 11/15] PostgreSQL backup hat wrong table selection --- borg.backup.pgsql.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/borg.backup.pgsql.sh b/borg.backup.pgsql.sh index 6c96209..f68a195 100755 --- a/borg.backup.pgsql.sh +++ b/borg.backup.pgsql.sh @@ -177,7 +177,7 @@ else printf "${PRINTF_DB_RUN_TIME_SUB_BLOCK}" "DONE" "${db}" "${MODULE}" "$(convert_time ${DURATION})"; # get list of tables - for owner_db in $(${PG_PSQL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -d template1 -t -A -F "," -X -q -c "SELECT pg_catalog.pg_get_userbyid(datdba) AS owner, datname, pg_catalog.pg_encoding_to_char(encoding) AS encoding FROM pg_catalog.pg_database WHERE datname ~ 'template(0|1)' ORDER BY datname;"); do + for owner_db in $(${PG_PSQL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -d template1 -t -A -F "," -X -q -c "SELECT pg_catalog.pg_get_userbyid(datdba) AS owner, datname, pg_catalog.pg_encoding_to_char(encoding) AS encoding FROM pg_catalog.pg_database WHERE datname !~ 'template(0|1)' ORDER BY datname;"); do LOCAL_START=$(date +'%s'); # get the user who owns the DB too owner=$(echo "${owner_db}" | cut -d "," -f 1); From 32a75626bb4044431a4c348a884f57f0b7f25562 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 3 Dec 2025 15:49:45 +0900 Subject: [PATCH 12/15] Version number fixes --- borg.backup.functions.init.sh | 2 +- borg.backup.gitea.sh | 2 +- borg.backup.mysql.sh | 2 +- borg.backup.pgsql.sh | 2 +- borg.backup.zabbix.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index 8854d0d..cf2fdae 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -32,7 +32,7 @@ function version { } # version for all general files -VERSION="4.5.4"; +VERSION="4.7.2"; # borg version and borg comamnd BORG_VERSION=""; diff --git a/borg.backup.gitea.sh b/borg.backup.gitea.sh index adc77fb..299e124 100755 --- a/borg.backup.gitea.sh +++ b/borg.backup.gitea.sh @@ -6,7 +6,7 @@ # Backup gitea database, all git folders and gitea settings MODULE="gitea" -MODULE_VERSION="1.2.1"; +MODULE_VERSION="1.2.2"; DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi diff --git a/borg.backup.mysql.sh b/borg.backup.mysql.sh index cf67829..669defe 100755 --- a/borg.backup.mysql.sh +++ b/borg.backup.mysql.sh @@ -10,7 +10,7 @@ # set last edit date + time MODULE="mysql" -MODULE_VERSION="1.1.4"; +MODULE_VERSION="1.1.5"; DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi diff --git a/borg.backup.pgsql.sh b/borg.backup.pgsql.sh index f68a195..c28d03f 100755 --- a/borg.backup.pgsql.sh +++ b/borg.backup.pgsql.sh @@ -10,7 +10,7 @@ # set last edit date + time MODULE="pgsql" -MODULE_VERSION="1.2.7"; +MODULE_VERSION="1.2.8"; DIR="${BASH_SOURCE%/*}" diff --git a/borg.backup.zabbix.sh b/borg.backup.zabbix.sh index f44b0a3..599ad41 100755 --- a/borg.backup.zabbix.sh +++ b/borg.backup.zabbix.sh @@ -6,7 +6,7 @@ # Backup zabbix config and settings only MODULE="zabbix" -MODULE_VERSION="1.1.3"; +MODULE_VERSION="1.1.4"; DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi From 1b52af909a3db95b97e28b0af1814fa7d24df629 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 4 Dec 2025 09:28:57 +0900 Subject: [PATCH 13/15] Update PostgreSQL module to run as normal user with sudo for PostgreSQL also clean up all PostgreSQL command calls as array type calls clean up binary finder for postgresql installations and add port/host override options --- borg.backup.functions.init.sh | 8 +- borg.backup.pgsql.settings-default | 12 +- borg.backup.pgsql.sh | 193 ++++++++++++++++++++--------- 3 files changed, 155 insertions(+), 58 deletions(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index cf2fdae..2d476c6 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -32,7 +32,7 @@ function version { } # version for all general files -VERSION="4.7.2"; +VERSION="4.7.3"; # borg version and borg comamnd BORG_VERSION=""; @@ -93,6 +93,8 @@ REGEX=""; REGEX_COMMENT="^[\ \t]*#"; REGEX_GLOB='\*'; REGEX_NUMERIC="^[0-9]{1,2}$"; +# port regex, but only approximately +REGEX_PORT="^[0-9]{2,5}$"; REGEX_ERROR="^Some part of the script failed with ERROR:"; PRUNE_DEBUG=""; INIT_REPOSITORY=0; @@ -156,6 +158,10 @@ SUB_BACKUP_SET=""; # for database backup only DATABASE_FULL_DUMP=""; DATABASE_USER=""; +DATABASE_USE_SUDO=""; +DATABASE_SUDO_USER=""; +DATABASE_PORT=""; +DATABASE_HOST=""; # only for mysql old config file MYSQL_DB_CONFIG=""; MYSQL_DB_CONFIG_PARAM=""; diff --git a/borg.backup.pgsql.settings-default b/borg.backup.pgsql.settings-default index 6c67720..49de930 100644 --- a/borg.backup.pgsql.settings-default +++ b/borg.backup.pgsql.settings-default @@ -9,5 +9,15 @@ # note that with this databases that have been dropped need to be pruned manually # if 'schema' word is used, only schema data is dumped DATABASE_FULL_DUMP=""; -# override default postgres user +# override default postgres user and sudo user for all postgres commands +# All commands must be run as the postgres admin user DATABASE_USER=""; +# disable sudo usage by setting to "0", default is to use sudo +DATABASE_USE_SUDO=""; +# set the sudo user, if not set postgres or DATABASE_USER is used +DATABASE_SUDO_USER=""; +# override port +DATABASE_PORT=""; +# override database host, if set to local, localhost or 127.0.0.1 it will use sockets to connect +# for socket connection ident or sudo has to be used +DATABASE_HOST=""; diff --git a/borg.backup.pgsql.sh b/borg.backup.pgsql.sh index c28d03f..7e9eb08 100755 --- a/borg.backup.pgsql.sh +++ b/borg.backup.pgsql.sh @@ -10,11 +10,13 @@ # set last edit date + time MODULE="pgsql" -MODULE_VERSION="1.2.8"; +MODULE_VERSION="1.3.0"; DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +DEFAULT_DATABASE_USE_SUDO="1"; +DEFAULT_DATABASE_USER="postgres"; # init system . "${DIR}/borg.backup.functions.init.sh"; @@ -35,15 +37,41 @@ BACKUP_LOCK_FILE="borg.backup.${MODULE}.lock"; # if info print info and then abort run . "${DIR}/borg.backup.functions.info.sh"; +# set db and sudo user if [ -n "${DATABASE_USER}" ]; then - DB_USER=${DATABASE_USER}; + DB_USER="${DATABASE_USER}"; else - DB_USER='postgres'; + DB_USER="${DEFAULT_DATABASE_USER}"; fi; +if [ -n "${DATABASE_SUDO_USER}" ]; then + DB_SUDO_USER="${DATABASE_SUDO_USER}"; +else + DB_SUDO_USER="${DB_USER}"; +fi; +# set flag if we should use sudo +if [ -n "${DATABASE_USE_SUDO}" ]; then + USE_SUDO="${DATABASE_USE_SUDO}"; +else + USE_SUDO="${DEFAULT_DATABASE_USE_SUDO}"; +fi; +# sudo command, or none if not requested +SUDO_COMMAND_A=("sudo" "-u" "${DB_SUDO_USER}"); +if [ "${USE_SUDO}" -eq "0" ]; then + SUDO_COMMAND_A=() +fi; + # get current pgsql version first # if first part <10 then user full, else only first part # eg 9.4 -> 9.4, 12.5 -> 12 -PG_VERSION=$(pgv=$(psql -U ${DB_USER} -d template1 -t -A -F "," -X -q -c 'select version();' | sed -e 's/^PostgreSQL \([0-9]\{1,\}\.[0-9]\{1,\}\).*/\1/'); if [[ $(echo "${pgv}" | cut -d "." -f 1) -ge 10 ]]; then echo "${pgv}" | cut -d "." -f 1; else echo "${pgv}" | cut -d "." -f 1,2; fi ); +PG_VERSION_CMD=("${SUDO_COMMAND_A[@]}" "psql" "-U" "${DB_USER}" "-d" "template1" "-t" "-A" "-F" "," "-X" "-q" "-c" "select version();"); +PG_VERSION=$( + pgv=$("${PG_VERSION_CMD[@]}" | sed -e 's/^PostgreSQL \([0-9]\{1,\}\.[0-9]\{1,\}\).*/\1/'); + if [[ $(echo "${pgv}" | cut -d "." -f 1) -ge 10 ]]; then + echo "${pgv}" | cut -d "." -f 1; + else + echo "${pgv}" | cut -d "." -f 1,2; + fi; +); _PATH_PG_VERSION=${PG_VERSION}; _backup_error=$?; if [ $_backup_error -ne 0 ] || [ -z "${PG_VERSION}" ]; then @@ -53,49 +81,75 @@ if [ $_backup_error -ne 0 ] || [ -z "${PG_VERSION}" ]; then fi; # path set per Distribution type and current running DB version +# Debian/Ubuntu: PG_BASE_PATH='/usr/lib/postgresql/'; # Redhat: PG_BASE_PATH='/usr/pgsql-'; -# AWS 1: PG_BASE_PATH='/usr/lib64/pgsql'; -# Debian: PG_BASE_PATH='/usr/lib/postgresql/'; -PG_BASE_PATH='/usr/lib/postgresql/'; -if [ ! -f "${PG_BASE_PATH}${_PATH_PG_VERSION}/bin/psql" ]; then - PG_BASE_PATH='/usr/pgsql-'; - if [ ! -f "${PG_BASE_PATH}${_PATH_PG_VERSION}/bin/psql" ]; then - PG_BASE_PATH='/usr/lib64/pgsql'; - _PATH_PG_VERSION=$(echo "${PG_VERSION}" | sed -e 's/\.//'); - if [ ! -f "${PG_BASE_PATH}${_PATH_PG_VERSION}/bin/psql" ]; then - echo "[! $(date +'%F %T')] PostgreSQL not found in any paths"; - . "${DIR}/borg.backup.functions.close.sh" 1; - exit 1; - fi; +PG_BASE_PATH_LIST=("/usr/lib/postgresql" "/usr/lib64/pgsql"); +PG_BASE_PATH=""; +for path in "${PG_BASE_PATH_LIST[@]}"; do + if [ -f "${path}/${_PATH_PG_VERSION}/bin/psql" ]; then + PG_BASE_PATH="${path}/"; + break; fi; +done; +if [ -z "${PG_BASE_PATH}" ]; then + echo "[! $(date +'%F %T')] PostgreSQL not found in any paths"; + . "${DIR}/borg.backup.functions.close.sh" 1; + exit 1; fi; -PG_PATH=${PG_BASE_PATH}${_PATH_PG_VERSION}'/bin/'; -PG_PSQL=${PG_PATH}'psql'; -PG_DUMP=${PG_PATH}'pg_dump'; -PG_DUMPALL=${PG_PATH}'pg_dumpall'; +PG_PATH="${PG_BASE_PATH}${_PATH_PG_VERSION}/bin/"; +PG_PSQL=("${PG_PATH}psql"); +PG_DUMP=("${PG_PATH}pg_dump"); +PG_DUMPALL=("${PG_PATH}pg_dumpall"); +PG_ERROR=0 # check that command are here -if [ ! -f "${PG_PSQL}" ]; then - echo "[! $(date +'%F %T')] psql binary not found in ${PG_PATH}"; - . "${DIR}/borg.backup.functions.close.sh" 1; - exit 1; +if [ ! -f "${PG_PSQL[0]}" ]; then + echo "[!] ($(date +'%F %T')) psql binary not found in ${PG_PATH}"; + PG_ERROR=1; fi; -if [ ! -f "${PG_DUMP}" ]; then - echo "[! $(date +'%F %T')] pg_dump binary not found in ${PG_PATH}"; - . "${DIR}/borg.backup.functions.close.sh" 1; - exit 1; +if [ ! -f "${PG_DUMP[0]}" ]; then + echo "[!] ($(date +'%F %T')) pg_dump binary not found in ${PG_PATH}"; + PG_ERROR=1; fi; -if [ ! -f "${PG_DUMPALL}" ]; then - echo "[! $(date +'%F %T')] pg_dumpall binary not found in ${PG_PATH}"; +if [ ! -f "${PG_DUMPALL[0]}" ]; then + echo "[!] ($(date +'%F %T')) pg_dumpall binary not found in ${PG_PATH}"; + PG_ERROR=1; +fi; +if [ ${PG_ERROR} -ne 0 ]; then . "${DIR}/borg.backup.functions.close.sh" 1; - exit 1; + exit ${PG_ERROR}; +fi; +# prefix with sudo, if sudo command is requested +if [ "${USE_SUDO}" -ne "0" ]; then + PG_PSQL=("${SUDO_COMMAND_A[@]}" "${PG_PSQL[@]}"); + PG_DUMP=("${SUDO_COMMAND_A[@]}" "${PG_DUMP[@]}"); + PG_DUMPALL=("${SUDO_COMMAND_A[@]}" "${PG_DUMPALL[@]}"); fi; DB_VERSION=${PG_VERSION}; -# TODO override port/host info -DB_PORT='5432'; -DB_HOST='local'; # or -CONN_DB_HOST=''; # -h -CONN_DB_PORT=''; # -p +# override for port or host name +if [ -n "${DATABASE_PORT}" ] && [[ "${DATABASE_PORT}" =~ ${REGEX_PORT} ]]; then + DB_PORT="${DATABASE_PORT}"; +else + DB_PORT="5432"; +fi; +if [ -n "${DATABASE_HOST}" ]; then + DB_HOST="${DATABASE_HOST}"; +else + DB_HOST="local"; +fi; +# use socket for local +if [ "${DB_HOST}" = "local" ] || [ "${DB_HOST}" = "localhost" ] || [ "${DB_HOST}" = "127.0.0.1" ]; then + DB_HOST='local'; + CONN_DB_HOST=(); + CONN_DB_PORT=(); +else + CONN_DB_HOST=("-p" "${DB_HOST}"); # -h + CONN_DB_PORT=("-h" "${DB_HOSTNAME}"); # -p +fi; +# now add user, con, host to all commands +PG_PSQL=("${PG_PSQL[@]}" "-U" "${DB_USER}" "${CONN_DB_HOST[@]}" "${CONN_DB_PORT[@]}"); +PG_DUMP=("${PG_DUMP[@]}" "-U" "${DB_USER}" "${CONN_DB_HOST[@]}" "${CONN_DB_PORT[@]}"); +PG_DUMPALL=("${PG_DUMPALL[@]}" "-U" "${DB_USER}" "${CONN_DB_HOST[@]}" "${CONN_DB_PORT[@]}"); # ALL IN ONE FILE or PER DATABASE FLAG if [ -n "${DATABASE_FULL_DUMP}" ]; then @@ -113,17 +167,25 @@ if [ -n "${DATABASE_FULL_DUMP}" ]; then BACKUP_SET_PREFIX="${MODULE},all-"; BACKUP_SET_NAME="${ONE_TIME_TAG}${BACKUP_SET_PREFIX}${schema_flag}-${BACKUP_SET}"; # borg call - BORG_CALL=$(echo "${_BORG_CALL}" | sed -e "s/##FILENAME##/${FILENAME}/" | sed -e "s/##BACKUP_SET##/${BACKUP_SET_NAME}/"); - BORG_PRUNE=$(echo "${_BORG_PRUNE}" | sed -e "s/##BACKUP_SET_PREFIX##/${BACKUP_SET_PREFIX}/"); + BORG_CALL=$( + echo "${_BORG_CALL}" | + sed -e "s/##FILENAME##/${FILENAME}/" | + sed -e "s/##BACKUP_SET##/${BACKUP_SET_NAME}/" + ); + BORG_PRUNE=$( + echo "${_BORG_PRUNE}" | + sed -e "s/##BACKUP_SET_PREFIX##/${BACKUP_SET_PREFIX}/" + ); + PG_DUMPALL_CMD=("${PG_DUMPALL[@]}" "${SCHEMA_ONLY}" "-c"); if [ ${DEBUG} -eq 1 ] || [ ${DRYRUN} -eq 1 ]; then echo "export BORG_BASE_DIR=\"${BASE_FOLDER}\";"; - echo "${PG_DUMPALL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} ${SCHEMA_ONLY} -c | ${BORG_CALL}"; + echo "${PG_DUMPALL_CMD[*]} | ${BORG_CALL}"; if [ -z "${ONE_TIME_TAG}" ]; then echo "${BORG_PRUNE}"; fi; fi; if [ ${DRYRUN} -eq 0 ]; then - ${PG_DUMPALL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} ${SCHEMA_ONLY} -c | ${BORG_CALL}; + "${PG_DUMPALL_CMD[@]}" | ${BORG_CALL}; _backup_error=$?; if [ $_backup_error -ne 0 ]; then echo "[! $(date +'%F %T')] Backup creation failed for full dump with error code: ${_backup_error}"; @@ -150,17 +212,25 @@ else BACKUP_SET_PREFIX="${MODULE},${db}-"; BACKUP_SET_NAME="${ONE_TIME_TAG}${BACKUP_SET_PREFIX}${schema_flag}-${BACKUP_SET}"; # borg call - BORG_CALL=$(echo "${_BORG_CALL}" | sed -e "s/##FILENAME##/${FILENAME}/" | sed -e "s/##BACKUP_SET##/${BACKUP_SET_NAME}/"); - BORG_PRUNE=$(echo "${_BORG_PRUNE}" | sed -e "s/##BACKUP_SET_PREFIX##/${BACKUP_SET_PREFIX}/"); + BORG_CALL=$( + echo "${_BORG_CALL}" | + sed -e "s/##FILENAME##/${FILENAME}/" | + sed -e "s/##BACKUP_SET##/${BACKUP_SET_NAME}/" + ); + BORG_PRUNE=$( + echo "${_BORG_PRUNE}" | + sed -e "s/##BACKUP_SET_PREFIX##/${BACKUP_SET_PREFIX}/" + ); + PG_DUMPALL_CMD=("${PG_DUMPALL[@]}" "--globals-only"); if [ ${DEBUG} -eq 1 ] || [ ${DRYRUN} -eq 1 ]; then echo "export BORG_BASE_DIR=\"${BASE_FOLDER}\";"; - echo "${PG_DUMPALL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} --globals-only | ${BORG_CALL}"; + echo "${PG_DUMPALL_CMD[*]} | ${BORG_CALL}"; if [ -z "${ONE_TIME_TAG}" ]; then echo "${BORG_PRUNE}"; fi; fi; if [ ${DRYRUN} -eq 0 ]; then - ${PG_DUMPALL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} --globals-only | ${BORG_CALL}; + "${PG_DUMPALL_CMD[@]}" | ${BORG_CALL}; _backup_error=$?; if [ $_backup_error -ne 0 ]; then echo "[! $(date +'%F %T')] Backup creation failed for ${db} dump with error code: ${_backup_error}"; @@ -177,7 +247,11 @@ else printf "${PRINTF_DB_RUN_TIME_SUB_BLOCK}" "DONE" "${db}" "${MODULE}" "$(convert_time ${DURATION})"; # get list of tables - for owner_db in $(${PG_PSQL} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -d template1 -t -A -F "," -X -q -c "SELECT pg_catalog.pg_get_userbyid(datdba) AS owner, datname, pg_catalog.pg_encoding_to_char(encoding) AS encoding FROM pg_catalog.pg_database WHERE datname !~ 'template(0|1)' ORDER BY datname;"); do + GET_DB_CMD=( + "${PG_PSQL[@]}" "-d" "template1" "-t" "-A" "-F" "," "-X" "-q" "-c" + "SELECT pg_catalog.pg_get_userbyid(datdba) AS owner, datname, pg_catalog.pg_encoding_to_char(encoding) AS encoding FROM pg_catalog.pg_database WHERE datname !~ 'template(0|1)' ORDER BY datname;" + ); + for owner_db in $("${GET_DB_CMD[@]}"); do LOCAL_START=$(date +'%s'); # get the user who owns the DB too owner=$(echo "${owner_db}" | cut -d "," -f 1); @@ -206,17 +280,15 @@ else done<"${BASE_FOLDER}${EXCLUDE_FILE}"; fi; if [ ${include} -eq 1 ] && [ ${exclude} -eq 0 ]; then + PG_DUMP_CMD=("${PG_DUMP[@]}" "-c" "--format=c"); # set dump type - SCHEMA_ONLY=''; schema_flag=''; # schema or data # schema exclude over data exclude, can't have both if [ -s "${BASE_FOLDER}${SCHEMA_ONLY_FILE}" ]; then # default is data dump - SCHEMA_ONLY=''; schema_flag='data'; while read -r schema_db; do if [ "${db}" = "${schema_db}" ]; then - SCHEMA_ONLY='-s'; schema_flag='schema'; # skip out break; @@ -224,11 +296,9 @@ else done<"${BASE_FOLDER}${SCHEMA_ONLY_FILE}"; elif [ -s "${BASE_FOLDER}${DATA_ONLY_FILE}" ]; then # default to schema, unless in data list - SCHEMA_ONLY='-s'; schema_flag='schema'; while read -r data_db; do if [ "${db}" = "${data_db}" ]; then - SCHEMA_ONLY=''; schema_flag='data'; # skip out break; @@ -237,9 +307,13 @@ else fi; # if nothing is set, default to data if [ -z "${schema_flag}" ]; then - SCHEMA_ONLY='' schema_flag="data"; fi; + # set schema flag if schmea only is requested + if [ $schema_flag = "schema" ]; then + PG_DUMP_CMD+=("-s"); + fi; + PG_DUMP_CMD+=("${db}"); # Filename # Database.User.Encoding.pgsql|data|schema-Version_Host_Port_YearMonthDay_HourMinute_Counter.Fromat(c).sql FILENAME="${db}.${owner}.${encoding}.${schema_flag}-${DB_VERSION}_${DB_HOST}_${DB_PORT}.c.sql" @@ -248,18 +322,25 @@ else # backup set: BACKUP_SET_NAME="${ONE_TIME_TAG}${BACKUP_SET_PREFIX}${schema_flag}-${BACKUP_SET}"; # borg call - BORG_CALL=$(echo "${_BORG_CALL}" | sed -e "s/##FILENAME##/${FILENAME}/" | sed -e "s/##BACKUP_SET##/${BACKUP_SET_NAME}/"); + BORG_CALL=$( + echo "${_BORG_CALL}" | + sed -e "s/##FILENAME##/${FILENAME}/" | + sed -e "s/##BACKUP_SET##/${BACKUP_SET_NAME}/" + ); # borg prune - BORG_PRUNE=$(echo "${_BORG_PRUNE}" | sed -e "s/##BACKUP_SET_PREFIX##/${BACKUP_SET_PREFIX}/"); + BORG_PRUNE=$( + echo "${_BORG_PRUNE}" | + sed -e "s/##BACKUP_SET_PREFIX##/${BACKUP_SET_PREFIX}/" + ); if [ ${DEBUG} -eq 1 ] || [ ${DRYRUN} -eq 1 ]; then echo "export BORG_BASE_DIR=\"${BASE_FOLDER}\";"; - echo "${PG_DUMP} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -c ${SCHEMA_ONLY} --format=c ${db} | ${BORG_CALL}"; + echo "${PG_DUMP_CMD[*]} | ${BORG_CALL}"; if [ -z "${ONE_TIME_TAG}" ]; then echo "${BORG_PRUNE}"; fi; fi; if [ ${DRYRUN} -eq 0 ]; then - ${PG_DUMP} -U ${DB_USER} ${CONN_DB_HOST} ${CONN_DB_PORT} -c ${SCHEMA_ONLY} --format=c "${db}" | ${BORG_CALL}; + "${PG_DUMP_CMD[@]}" | ${BORG_CALL}; _backup_error=$?; if [ $_backup_error -ne 0 ]; then echo "[! $(date +'%F %T')] Backup creation failed for ${db} dump with error code: ${_backup_error}"; From f9a58171ccfd59bed7416ad1f4df01d51d5a8c18 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 4 Dec 2025 09:35:08 +0900 Subject: [PATCH 14/15] Bump version to v4.8.0 --- borg.backup.functions.init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/borg.backup.functions.init.sh b/borg.backup.functions.init.sh index 2d476c6..0666ef5 100644 --- a/borg.backup.functions.init.sh +++ b/borg.backup.functions.init.sh @@ -32,7 +32,7 @@ function version { } # version for all general files -VERSION="4.7.3"; +VERSION="4.8.0"; # borg version and borg comamnd BORG_VERSION=""; From 924014688e00c3cc01fb358173b24981a17105e6 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 4 Dec 2025 09:57:54 +0900 Subject: [PATCH 15/15] Update gitignore file with backup lock files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 19770be..e6c0b7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .cache/ .config/ +# various borg files that need to be ignored borg.backup.settings borg.backup.*.settings borg.backup.*.include @@ -7,3 +8,4 @@ borg.backup.*.exclude borg.backup.*.schema-only borg.backup.*.init borg.backup.*.compact +borg.backup.*.lock