From 8d10a1a3b9a9f11fd0339bb442fedf2009b9d1a2 Mon Sep 17 00:00:00 2001 From: Asif Bacchus Date: Tue, 12 Jul 2022 11:36:57 -0600 Subject: [PATCH] feature(version): add param to display version - as per user request --- .../.idea/git_toolbox_prj.xml | 15 + backup/backup.sh | 484 +++++++++--------- 2 files changed, 250 insertions(+), 249 deletions(-) diff --git a/.idea/.idea.MailcowBackup.dir/.idea/git_toolbox_prj.xml b/.idea/.idea.MailcowBackup.dir/.idea/git_toolbox_prj.xml index e69de29..02b915b 100644 --- a/.idea/.idea.MailcowBackup.dir/.idea/git_toolbox_prj.xml +++ b/.idea/.idea.MailcowBackup.dir/.idea/git_toolbox_prj.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/backup/backup.sh b/backup/backup.sh index fdad10e..12cc77d 100755 --- a/backup/backup.sh +++ b/backup/backup.sh @@ -8,9 +8,8 @@ ### 3. remote repo already set-up and configured ####### - ### text formatting presents -if command -v tput > /dev/null; then +if command -v tput >/dev/null; then bold=$(tput bold) cyan=$(tput setaf 6) err=$(tput bold)$(tput setaf 1) @@ -32,11 +31,9 @@ else yellow="" fi - ### trap trap trapExit 1 2 3 6 - ### functions # bad configuration value passed in details file @@ -75,71 +72,71 @@ cleanup() { if [ "$err503Copied" -eq 1 ]; then if ! rm -f "$webroot/$err503File" 2>>"$logFile"; then printf "%s[%s] -- [WARNING] Could not remove 503 error page." \ - "$warn" "$(stamp)" >> "$logFile" + "$warn" "$(stamp)" >>"$logFile" printf " Web interface will not function until this file is " \ - >> "$logFile" - printf "removed --%s\n" "$norm" >> "$logFile" - warnCount=$((warnCount+1)) + >>"$logFile" + printf "removed --%s\n" "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) else printf "%s[%s] -- [INFO] 503 error page removed --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" fi fi # cleanup SQL dump directory if created if [ "$sqlDumpDirCreated" -eq 1 ]; then if ! rm -rf "$sqlDumpDir" 2>>"$logFile"; then printf "%s[%s] -- [WARNING] Could not remove temporary SQL-dump directory. Sorry for the mess. --%s\n" \ - "$warn" "$(stamp)" "$norm" >> "$logFile" + "$warn" "$(stamp)" "$norm" >>"$logFile" else printf "%s[%s] -- [INFO] Temporary SQL-dump directory removed successfully --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" fi fi # start docker containers (no harm if they are already running) doDocker start postfix if [ "$dockerResultState" = "true" ]; then printf "%s[%s] -- [INFO] POSTFIX container is running --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" else exitError 102 'Could not start POSTFIX container.' fi doDocker start dovecot if [ "$dockerResultState" = "true" ]; then printf "%s[%s] -- [INFO] DOVECOT container is running --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" else exitError 102 'Could not start DOVECOT container.' fi } doDocker() { - containerName="$( docker ps -a --format '{{ .Names }}' --filter name=${COMPOSE_PROJECT_NAME}_${2}-mailcow_1 )" + containerName="$(docker ps -a --format '{{ .Names }}' --filter name=${COMPOSE_PROJECT_NAME}_${2}-mailcow_1)" # determine action to take if [ "$1" = "stop" ]; then printf "%s[%s] -- [INFO] Stopping %s-mailcow container --%s\n" \ - "$cyan" "$(stamp)" "$2" "$norm" >> "$logFile" - docker-compose -f "$mcDockerCompose" stop --timeout "$dockerStopTimeout" "$2-mailcow" 2>> "$logFile" + "$cyan" "$(stamp)" "$2" "$norm" >>"$logFile" + docker-compose -f "$mcDockerCompose" stop --timeout "$dockerStopTimeout" "$2-mailcow" 2>>"$logFile" # set result vars - dockerResultState="$( docker inspect -f '{{ .State.Running }}' $containerName )" - dockerResultExit="$( docker inspect -f '{{ .State.ExitCode }}' $containerName )" + dockerResultState="$(docker inspect -f '{{ .State.Running }}' $containerName)" + dockerResultExit="$(docker inspect -f '{{ .State.ExitCode }}' $containerName)" elif [ "$1" = "start" ]; then printf "%s[%s] -- [INFO] Starting %s-mailcow container --%s\n" \ - "$cyan" "$(stamp)" "$2" "$norm" >> "$logFile" - docker-compose -f "$mcDockerCompose" start "$2-mailcow" 2>> "$logFile" + "$cyan" "$(stamp)" "$2" "$norm" >>"$logFile" + docker-compose -f "$mcDockerCompose" start "$2-mailcow" 2>>"$logFile" # set result vars - dockerResultState="$( docker inspect -f '{{ .State.Running }}' $containerName )" + dockerResultState="$(docker inspect -f '{{ .State.Running }}' $containerName)" fi } # call cleanup and then exit with error report exitError() { printf "%s[%s] -- [ERROR] %s: %s --%s\n" \ - "$err" "$(stamp)" "$1" "$2" "$norm" >> "$logFile" + "$err" "$(stamp)" "$1" "$2" "$norm" >>"$logFile" cleanup # note script completion with error printf "%s[%s] --- %s execution completed with error ---%s\n" \ - "$err" "$(stamp)" "$scriptName" "$norm" >> "$logFile" + "$err" "$(stamp)" "$scriptName" "$norm" >>"$logFile" exit "$1" } @@ -241,24 +238,24 @@ newline() { # same as exitError but for signal captures trapExit() { printf "%s[%s] -- [ERROR] 99: Caught signal --%s\n" \ - "$err" "$(stamp)" "$norm" >> "$logFile" + "$err" "$(stamp)" "$norm" >>"$logFile" cleanup # note script completion with error printf "%s[%s] --- %s execution was terminated via signal ---%s\n" \ - "$err" "$(stamp)" "$scriptName" "$norm" >> "$logFile" + "$err" "$(stamp)" "$scriptName" "$norm" >>"$logFile" exit 99 } ### end of functions - ### default variable values ## script related # store logfile in the same directory as this script file using the same file # name as the script but with the extension '.log' -scriptPath="$( CDPATH='' \cd -- "$( dirname -- "$0" )" && pwd -P )" -scriptName="$( basename "$0" )" +scriptVersion="4.0" +scriptPath="$(CDPATH='' \cd -- "$(dirname -- "$0")" && pwd -P)" +scriptName="$(basename "$0")" logFile="$scriptPath/${scriptName%.*}.log" colourizeLogFile=1 warnCount=0 @@ -283,150 +280,153 @@ mcDockerCompose="/opt/mailcow-dockerized/docker-compose.yml" dockerStartTimeout=180 dockerStopTimeout=120 - ### process startup parameters while [ $# -gt 0 ]; do case "$1" in - -h|-\?|--help) - # display help - scriptHelp - exit 0 - ;; - -l|--log) - # set log file location - if [ -n "$2" ]; then - logFile="${2%/}" + -h | -\? | --help) + # display help + scriptHelp + exit 0 + ;; + --version) + # display script version + printf "\nMailcowBackup - Backup Mailcow using borgbackup to a remote SSH server : Version %s\n\n" ${scriptVersion} + exit 0 + ;; + -l | --log) + # set log file location + if [ -n "$2" ]; then + logFile="${2%/}" + shift + else + badParam empty "$@" + fi + ;; + --nc | --no-color | --no-colour) + # do NOT colourize log file + colourizeLogFile=0 + ;; + -b | --borg) + # specify non-default borg path + if [ -n "$2" ]; then + borgPath="${2%/}" + shift + else + badParam empty "$@" + fi + ;; + -c | --config | --details) + # location of config details file + if [ -n "$2" ]; then + if [ -f "$2" ]; then + configDetails="${2%/}" shift else - badParam empty "$@" + badParam dne "$@" fi - ;; - --nc|--no-color|--no-colour) - # do NOT colourize log file - colourizeLogFile=0 - ;; - -b|--borg) - # specify non-default borg path - if [ -n "$2" ]; then - borgPath="${2%/}" + else + badParam empty "$@" + fi + ;; + --compression) + # set borg archive compression + if [ -n "$2" ]; then + borgCompression="$2" + shift + else + badParam empty "$@" + fi + ;; + -v | --verbose) + # set verbose logging from borg + borgCreateParams='--list --stats' + borgPruneParams='--list' + ;; + -5 | --use-503) + # enable copying 503 error page to webroot + use503=1 + ;; + --503-path) + # FULL path to 503 file + if [ -n "$2" ]; then + if [ -f "$2" ]; then + err503Path="${2%/}" + err503File="${2##*/}" shift else - badParam empty "$@" + badParam dne "$@" fi - ;; - -c|--config|--details) - # location of config details file - if [ -n "$2" ]; then - if [ -f "$2" ]; then - configDetails="${2%/}" - shift - else - badParam dne "$@" - fi - else - badParam empty "$@" - fi - ;; - --compression) - # set borg archive compression - if [ -n "$2" ]; then - borgCompression="$2" + else + badParam empty "$@" + fi + ;; + -w | --webroot) + # path to webroot (copy 503) + if [ -n "$2" ]; then + if [ -d "$2" ]; then + webroot="${2%/}" shift else - badParam empty "$@" + badParam dne "$@" fi - ;; - -v|--verbose) - # set verbose logging from borg - borgCreateParams='--list --stats' - borgPruneParams='--list' - ;; - -5|--use-503) - # enable copying 503 error page to webroot - use503=1 - ;; - --503-path) - # FULL path to 503 file - if [ -n "$2" ]; then - if [ -f "$2" ]; then - err503Path="${2%/}" - err503File="${2##*/}" - shift - else - badParam dne "$@" - fi - else - badParam empty "$@" - fi - ;; - -w|--webroot) - # path to webroot (copy 503) - if [ -n "$2" ]; then - if [ -d "$2" ]; then - webroot="${2%/}" - shift - else - badParam dne "$@" - fi - else - badParam empty "$@" - fi - ;; - -d|--docker-compose) - # FULL path to mailcow docker-compose file - if [ -n "$2" ]; then - if [ -f "$2" ]; then - mcDockerCompose="$2" - shift - else - badParam dne "$@" - fi - else - badParam empty "$@" - fi - ;; - -m|--mailcow-config) - # FULL path to mailcow configuration file - if [ -n "$2" ]; then - if [ -f "$2" ]; then - mcConfig="$2" - shift - else - badParam dne "$@" - fi - else - badParam empty "$@" - fi - ;; - -t1|--timeout-start) - if [ -z "$2" ]; then - badParam empty "$@" - else - dockerStartTimeout="$2" + else + badParam empty "$@" + fi + ;; + -d | --docker-compose) + # FULL path to mailcow docker-compose file + if [ -n "$2" ]; then + if [ -f "$2" ]; then + mcDockerCompose="$2" shift - fi - ;; - -t2|--timeout-stop) - if [ -z "$2" ]; then - badParam empty "$@" else - dockerStopTimeout="$2" - shift + badParam dne "$@" fi - ;; - *) - printf "\n%sUnknown option: %s\n" "$err" "$1" - printf "%sUse '--help' for valid options.%s\n\n" "$cyan" "$norm" - exit 1 - ;; + else + badParam empty "$@" + fi + ;; + -m | --mailcow-config) + # FULL path to mailcow configuration file + if [ -n "$2" ]; then + if [ -f "$2" ]; then + mcConfig="$2" + shift + else + badParam dne "$@" + fi + else + badParam empty "$@" + fi + ;; + -t1 | --timeout-start) + if [ -z "$2" ]; then + badParam empty "$@" + else + dockerStartTimeout="$2" + shift + fi + ;; + -t2 | --timeout-stop) + if [ -z "$2" ]; then + badParam empty "$@" + else + dockerStopTimeout="$2" + shift + fi + ;; + *) + printf "\n%sUnknown option: %s\n" "$err" "$1" + printf "%sUse '--help' for valid options.%s\n\n" "$cyan" "$norm" + exit 1 + ;; esac shift done - ### check pre-requisites and default values # check if running as root, otherwise exit -if [ "$( id -u )" -ne 0 ]; then +if [ "$(id -u)" -ne 0 ]; then printf "\n%sERROR: script MUST be run as ROOT%s\n\n" "$err" "$norm" exit 2 fi @@ -435,7 +435,7 @@ if [ ! -f "$configDetails" ]; then badParam dne "(--details default)" "$configDetails" fi # is borg installed? -if ! find "$borgPath" -type f -executable > /dev/null 2>&1; then +if ! find "$borgPath" -type f -executable >/dev/null 2>&1; then printf "\n%sERROR: BORG cannot be found in the specified or default location on this system!%s\n\n" "$err" "$norm" exit 3 fi @@ -477,53 +477,49 @@ fi export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin export COMPOSE_HTTP_TIMEOUT="$dockerStartTimeout" - ### start logging printf "%s[%s] --- Start %s execution ---%s\n" \ - "$magenta" "$(stamp)" "$scriptName" "$norm" >> "$logFile" + "$magenta" "$(stamp)" "$scriptName" "$norm" >>"$logFile" printf "%s[%s] -- [INFO] Log located at %s%s%s --%s\n" \ - "$cyan" "$(stamp)" "$yellow" "$logFile" "$cyan" "$norm" >> "$logFile" - + "$cyan" "$(stamp)" "$yellow" "$logFile" "$cyan" "$norm" >>"$logFile" ### get location of docker volumes dockerVolumeMail=$(docker volume inspect -f '{{ .Mountpoint }}' ${COMPOSE_PROJECT_NAME}_vmail-vol-1) printf "%s[%s] -- [INFO] Using MAIL volume: %s --%s\n" \ - "$cyan" "$(stamp)" "$dockerVolumeMail" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$dockerVolumeMail" "$norm" >>"$logFile" dockerVolumeRspamd=$(docker volume inspect -f '{{ .Mountpoint }}' ${COMPOSE_PROJECT_NAME}_rspamd-vol-1) printf "%s[%s] -- [INFO] Using RSPAMD volume: %s --%s\n" \ - "$cyan" "$(stamp)" "$dockerVolumeRspamd" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$dockerVolumeRspamd" "$norm" >>"$logFile" dockerVolumePostfix=$(docker volume inspect -f '{{ .Mountpoint }}' ${COMPOSE_PROJECT_NAME}_postfix-vol-1) printf "%s[%s] -- [INFO] Using POSTFIX volume: %s --%s\n" \ - "$cyan" "$(stamp)" "$dockerVolumePostfix" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$dockerVolumePostfix" "$norm" >>"$logFile" dockerVolumeRedis=$(docker volume inspect -f '{{ .Mountpoint }}' ${COMPOSE_PROJECT_NAME}_redis-vol-1) printf "%s[%s] -- [INFO] Using REDIS volume: %s --%s\n" \ - "$cyan" "$(stamp)" "$dockerVolumeRedis" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$dockerVolumeRedis" "$norm" >>"$logFile" dockerVolumeCrypt=$(docker volume inspect -f '{{ .Mountpoint }}' ${COMPOSE_PROJECT_NAME}_crypt-vol-1) printf "%s[%s] -- [INFO] Using MAILCRYPT volume: %s --%s\n" \ - "$cyan" "$(stamp)" "$dockerVolumeCrypt" "$norm" >> "$logFile" - + "$cyan" "$(stamp)" "$dockerVolumeCrypt" "$norm" >>"$logFile" ### read details file to get variables needed run borg # check if config details file was provided as a relative or absolute path case "${configDetails}" in - /*) - # absolute path, no need to rewrite variable - # shellcheck source=./backup.details - . "${configDetails}" - ;; - *) - # relative path, prepend './' to create absolute path - # shellcheck source=./backup.details - . "./${configDetails}" - ;; +/*) + # absolute path, no need to rewrite variable + # shellcheck source=./backup.details + . "${configDetails}" + ;; +*) + # relative path, prepend './' to create absolute path + # shellcheck source=./backup.details + . "./${configDetails}" + ;; esac printf "%s[%s] -- [INFO] %s%s%s imported --%s\n" \ - "$cyan" "$(stamp)" "$yellow" "$configDetails" "$cyan" "$norm" >> "$logFile" - + "$cyan" "$(stamp)" "$yellow" "$configDetails" "$cyan" "$norm" >>"$logFile" ### Run borg variable checks printf "%s[%s] -- [INFO] Verifying supplied borg details --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" ## verify borg base directory if [ -z "${borgBaseDir}" ]; then @@ -532,7 +528,7 @@ elif [ ! -d "${borgBaseDir}" ]; then badDetails dne 'borgBaseDir' fi printf "%sdetails:borgBaseDir %s-- %s[OK]%s\n" \ - "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + "$magenta" "$norm" "$ok" "$norm" >>"$logFile" export BORG_BASE_DIR="${borgBaseDir%/}" ## check path to SSH keyfile @@ -542,15 +538,15 @@ elif [ ! -f "${borgSSHKey}" ]; then badDetails dne 'borgSSHKey' fi printf "%sdetails:borgSSHKey %s-- %s[OK]%s\n" \ - "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + "$magenta" "$norm" "$ok" "$norm" >>"$logFile" ## check SSH port if [ -z "${borgSSHPort}" ]; then borgSSHPort=22 printf "%sdetails:borgSSHPort %s-- %s[DEFAULT]%s\n" \ - "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + "$magenta" "$norm" "$ok" "$norm" >>"$logFile" else printf "%sdetails:borgSSHPort %s-- %s[CUSTOM]%s\n" \ - "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + "$magenta" "$norm" "$ok" "$norm" >>"$logFile" fi export BORG_RSH="ssh -i ${borgSSHKey} -p ${borgSSHPort}" @@ -559,20 +555,20 @@ if [ -z "${borgConnectRepo}" ]; then badDetails empty 'borgConnectRepo' fi printf "%sdetails:borgConnectRepo %s-- %s[OK]%s\n" \ - "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + "$magenta" "$norm" "$ok" "$norm" >>"$logFile" export BORG_REPO="${borgConnectRepo}" ## check borg repo password if [ -n "${borgRepoPassphrase}" ]; then printf "%sdetails:borgRepoPassphrase %s-- %s[OK]%s\n" \ - "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + "$magenta" "$norm" "$ok" "$norm" >>"$logFile" export BORG_PASSPHRASE="${borgRepoPassphrase}" else # if passwd is blank intentionally, this is insecure printf "%s-- [WARNING] Using a borg repo without a password is an " \ - "$warn" >> "$logFile" - printf "insecure configuration --%s\n" "$norm">> "$logFile" - warnCount=$((warnCount+1)) + "$warn" >>"$logFile" + printf "insecure configuration --%s\n" "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) # if this was an accident, we need to provide a bogus passwd so borg fails # otherwise it will sit forever just waiting for input export BORG_PASSPHRASE="DummyPasswordSoBorgFails" @@ -580,13 +576,13 @@ fi ## check borg repository keyfile location if [ -z "${borgKeyfileLocation}" ]; then - printf "%sdetails:borgKeyfileLocation %s-- %s[DEFAULT]%s\n" "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + printf "%sdetails:borgKeyfileLocation %s-- %s[DEFAULT]%s\n" "$magenta" "$norm" "$ok" "$norm" >>"$logFile" else # check if keyfile location exists if [ ! -f "${borgKeyfileLocation}" ]; then badDetails dne 'borgKeyfileLocation' fi - printf "%sdetails:borgKeyfileLocation %s-- %s[OK]%s\n" "$magenta" "$norm" "$ok" "$norm" >> "$logFile" + printf "%sdetails:borgKeyfileLocation %s-- %s[OK]%s\n" "$magenta" "$norm" "$ok" "$norm" >>"$logFile" export BORG_KEY_FILE="${borgKeyfileLocation}" fi @@ -599,7 +595,7 @@ if [ -n "${borgExcludeListPath}" ]; then if [ ! -f "${borgExcludeListPath}" ]; then badDetails dne 'borgExcludeListPath' fi -exclusions=1 + exclusions=1 fi ## read additional files @@ -618,25 +614,23 @@ else xtraList="${xtraList} ${xtraItem}" fi done <> "$logFile" + printf "%sdetails:borgXtraListPath %s-- %s[OK]%s\n" \ + "$magenta" "$norm" "$ok" "$norm" >>"$logFile" fi - ### set location of sql dump # this is done before resetting default TMP dir for borg -if ! sqlDumpDir=$( mktemp -d 2>/dev/null ); then +if ! sqlDumpDir=$(mktemp -d 2>/dev/null); then exitError 115 'Unable to create temp directory for SQL dump.' else - sqlDumpFile="backup-$( date +%Y%m%d_%H%M%S ).sql" + sqlDumpFile="backup-$(date +%Y%m%d_%H%M%S).sql" sqlDumpDirCreated=1 printf "%s[%s] -- [INFO] SQL dump file will be stored at: %s --%s\n" \ - "$cyan" "$(stamp)" "$sqlDumpDir/$sqlDumpFile" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$sqlDumpDir/$sqlDumpFile" "$norm" >>"$logFile" fi - ### 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 @@ -649,27 +643,25 @@ if [ ! -d "${borgBaseDir}/tmp" ]; then exitError 132 "Unable to create borg ${borgBaseDir}/tmp directory" else printf "%s[%s] -- [INFO] Created %s%s/tmp " \ - "$cyan" "$(stamp)" "$yellow" "${borgBaseDir}" >> "$logFile" - printf "%s--%s\n" "$cyan" "$norm">> "$logFile" + "$cyan" "$(stamp)" "$yellow" "${borgBaseDir}" >>"$logFile" + printf "%s--%s\n" "$cyan" "$norm" >>"$logFile" fi fi export TMPDIR="${borgBaseDir}/tmp" - ### 503 functionality if [ "$use503" -eq 1 ]; then printf "%s[%s] -- [INFO] Copying 503 error page to " \ - "$cyan" "$(stamp)" >> "$logFile" - printf "webroot -- %s\n" "$norm">> "$logFile" - if ! \cp --force "${err503Path}" "${webroot}/${err503File}" 2>> "$logFile" - then + "$cyan" "$(stamp)" >>"$logFile" + printf "webroot -- %s\n" "$norm" >>"$logFile" + if ! \cp --force "${err503Path}" "${webroot}/${err503File}" 2>>"$logFile"; then printf "%s[%s] -- [WARNING] Failed to copy 503 error page. " \ - "$warn" "$(stamp)" >> "$logFile" - printf "Web users will NOT be notified --%s\n" "$norm" >> "$logFile" - warnCount=$((warnCount+1)) + "$warn" "$(stamp)" >>"$logFile" + printf "Web users will NOT be notified --%s\n" "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) else printf "%s[%s] -- [SUCCESS] 503 error page copied --%s\n" \ - "$ok" "$(stamp)" "$norm" >> "$logFile" + "$ok" "$(stamp)" "$norm" >>"$logFile" # set cleanup flag err503Copied=1 fi @@ -682,33 +674,31 @@ fi doDocker stop postfix if [ "$dockerResultState" = "false" ] && [ "$dockerResultExit" -eq 0 ]; then printf "%s[%s] -- [INFO] POSTFIX container stopped --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" else exitError 101 'Could not stop POSTFIX container.' fi doDocker stop dovecot if [ "$dockerResultState" = "false" ] && [ "$dockerResultExit" -eq 0 ]; then printf "%s[%s] -- [INFO] DOVECOT container stopped --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" else exitError 101 'Could not stop DOVECOT container.' fi - ### dump SQL printf "%s[%s] -- [INFO] Dumping mailcow SQL database --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" docker-compose exec -T mysql-mailcow mysqldump --default-character-set=utf8mb4 \ - -u${DBUSER} -p${DBPASS} ${DBNAME} > "$sqlDumpDir/$sqlDumpFile" 2>> "$logFile" -dumpResult=$( docker-compose exec -T mysql-mailcow echo "$?" ) + -u${DBUSER} -p${DBPASS} ${DBNAME} >"$sqlDumpDir/$sqlDumpFile" 2>>"$logFile" +dumpResult=$(docker-compose exec -T mysql-mailcow echo "$?") if [ "$dumpResult" -eq 0 ]; then printf "%s[%s] -- [INFO] SQL database dumped successfully --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" else exitError 118 'There was an error dumping the mailcow SQL database.' fi - ### dump redis inside container # delete old redis dump if it exists if [ -f "$dockerVolumeRedis/dump.rdb" ]; then @@ -716,19 +706,18 @@ if [ -f "$dockerVolumeRedis/dump.rdb" ]; then fi # dump redis printf "%s[%s] -- [INFO] Dumping mailcow redis database --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" -docker-compose exec -T redis-mailcow redis-cli save >> "$logFile" 2>&1 -rdumpResult=$( docker-compose exec -T redis-mailcow echo "$?" ) + "$cyan" "$(stamp)" "$norm" >>"$logFile" +docker-compose exec -T redis-mailcow redis-cli save >>"$logFile" 2>&1 +rdumpResult=$(docker-compose exec -T redis-mailcow echo "$?") if [ "$rdumpResult" -eq 0 ]; then printf "%s[%s] -- [INFO] mailcow redis dumped successfully --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" else exitError 119 'There was an error dumping the mailcow redis database.' fi - ### execute borg depending on whether exclusions are defined -printf "%s[%s] -- [INFO] Pre-backup tasks completed, calling borgbackup --%s\n" "$cyan" "$(stamp)" "$norm" >> "$logFile" +printf "%s[%s] -- [INFO] Pre-backup tasks completed, calling borgbackup --%s\n" "$cyan" "$(stamp)" "$norm" >>"$logFile" ## construct the proper borg commandline # base command @@ -759,92 +748,89 @@ fi # execute borg printf "%s[%s] -- [INFO] Executing borg backup operation --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" -${borgCMD} 2>> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" +${borgCMD} 2>>"$logFile" borgResult="$?" ## check borg exit status if [ "$borgResult" -eq 0 ]; then printf "%s[%s] -- [SUCCESS] Borg backup completed --%s\n" \ - "$ok" "$(stamp)" "$norm" >> "$logFile" + "$ok" "$(stamp)" "$norm" >>"$logFile" elif [ "$borgResult" -eq 1 ]; then printf "%s[%s] -- [WARNING] Borg completed with warnings. " \ - "$warn" "$(stamp)" >> "$logFile" - printf "Review this logfile for details --%s\n" "$norm">> "$logFile" - warnCount=$((warnCount+1)) + "$warn" "$(stamp)" >>"$logFile" + printf "Review this logfile for details --%s\n" "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) elif [ "$borgResult" -ge 2 ]; then err_1="Borg exited with a critical error. Please review this log file" err_2="for details." exitError 138 "$err_1 $err_2" else printf "%s[%s] -- [WARNING] Borg exited with unknown return code. " \ - "$warn" "$(stamp)" >> "$logFile" - printf "Review this logfile for details --%s\n" "$norm">> "$logFile" - warnCount=$((warnCount+1)) + "$warn" "$(stamp)" >>"$logFile" + printf "Review this logfile for details --%s\n" "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) fi - ### execute borg prune if paramters are provided, otherwise skip with a warning if [ -n "${borgPruneSettings}" ]; then printf "%s[%s] -- [INFO] Executing borg prune operation --%s\n" \ - "$cyan" "$(stamp)" "$norm" >> "$logFile" + "$cyan" "$(stamp)" "$norm" >>"$logFile" "${borgPath}" prune --show-rc -v ${borgPruneParams} ${borgPruneSettings} \ - 2>> "$logFile" + 2>>"$logFile" borgPruneResult="$?" else printf "%s[%s] -- [WARNING] No prune parameters provided. " \ - "$warn" "$(stamp)" >> "$logFile" + "$warn" "$(stamp)" >>"$logFile" printf "Your archive will continue growing with each backup --%s\n" \ - "$norm" >> "$logFile" - warnCount=$((warnCount+1)) + "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) fi ## report on prune operation if executed if [ -n "${borgPruneResult}" ]; then if [ "${borgPruneResult}" -eq 0 ]; then printf "%s[%s] -- [SUCCESS] Borg prune completed --%s\n" \ - "$ok" "$(stamp)" "$norm" >> "$logFile" + "$ok" "$(stamp)" "$norm" >>"$logFile" elif [ "$borgPruneResult" -eq 1 ]; then printf "%s[%s] -- [WARNING] Borg prune completed with warnings. " \ - "$warn" "$(stamp)" >> "$logFile" - printf "Review this logfile for details --%s\n" "$norm" >> "$logFile" - warnCount=$((warnCount+1)) + "$warn" "$(stamp)" >>"$logFile" + printf "Review this logfile for details --%s\n" "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) elif [ "$borgPruneResult" -ge 2 ]; then err_1="Borg prune exited with a critical error. Please review this" err_2="log file for details." exitError 139 "$err_1 $err_2" else printf "%s[%s] -- [WARNING] Borg prune exited with an unknown " \ - "$warn" "$(stamp)" >> "$logFile" + "$warn" "$(stamp)" >>"$logFile" printf "return code. Review this logfile for details --%s\n" \ - "$norm" >> "$logFile" - warnCount=$((warnCount+1)) + "$norm" >>"$logFile" + warnCount=$((warnCount + 1)) fi fi - ### all processes successfully completed, cleanup and exit gracefully # note successful completion of borg commands printf "%s[%s] -- [SUCCESS] Backup operations completed --%s\n" \ - "$ok" "$(stamp)" "$norm" >> "$logFile" + "$ok" "$(stamp)" "$norm" >>"$logFile" # cleanup cleanup # note complete success, tally warnings and exit printf "%s[%s] -- [SUCCESS] All processes completed --%s\n" \ - "$ok" "$(stamp)" "$norm" >> "$logFile" + "$ok" "$(stamp)" "$norm" >>"$logFile" printf "%s[%s] --- %s execution completed ---%s\n" \ - "$magenta" "$(stamp)" "$scriptName" "$norm" >> "$logFile" + "$magenta" "$(stamp)" "$scriptName" "$norm" >>"$logFile" if [ "$warnCount" -gt 0 ]; then - printf "%s%s warnings issued!%s\n" "$warn" "${warnCount}" "$norm" >> "$logFile" + printf "%s%s warnings issued!%s\n" "$warn" "${warnCount}" "$norm" >>"$logFile" else - printf "%s0 warnings issued.%s\n" "$ok" "$norm" >> "$logFile" + printf "%s0 warnings issued.%s\n" "$ok" "$norm" >>"$logFile" fi exit 0 - ### error codes # 1: parameter error # 2: not run as root