2
2
3
3
# =================================================================
4
4
# SCRIPT INITIALIZATION & SETUP
5
- # v0.8 - 2025.08.09
5
+ # v0.9 - 2025.08.09
6
6
# =================================================================
7
7
set -Euo pipefail
8
8
umask 077
47
47
fi
48
48
49
49
# --- Validate that all required configuration variables are set ---
50
- for var in LOCAL_DIR BOX_DIR HETZNER_BOX SSH_OPTS_STR LOG_FILE \
50
+ for var in BACKUP_DIRS BOX_DIR HETZNER_BOX SSH_OPTS_STR LOG_FILE \
51
51
NTFY_PRIORITY_SUCCESS NTFY_PRIORITY_WARNING NTFY_PRIORITY_FAILURE \
52
52
LOG_RETENTION_DAYS; do
53
53
if [ -z " ${! var:- } " ]; then
@@ -110,7 +110,8 @@ send_notification() {
110
110
111
111
run_integrity_check () {
112
112
local rsync_check_opts=(-ainc -c --delete --exclude-from=" $EXCLUDE_FILE_TMP " --out-format=" %n" -e " ssh ${SSH_OPTS_STR:- } " )
113
- LC_ALL=C rsync " ${rsync_check_opts[@]} " " $LOCAL_DIR " " $REMOTE_TARGET " 2>> " ${LOG_FILE:-/ dev/ null} "
113
+ # shellcheck disable=SC2086
114
+ LC_ALL=C rsync " ${rsync_check_opts[@]} " $BACKUP_DIRS " $REMOTE_TARGET " 2>> " ${LOG_FILE:-/ dev/ null} "
114
115
}
115
116
116
117
parse_stat () {
@@ -170,10 +171,12 @@ if ! ssh ${SSH_OPTS_STR:-} -o BatchMode=yes -o ConnectTimeout=10 "$HETZNER_BOX"
170
171
trap - ERR; exit 6
171
172
fi
172
173
173
- if [[ ! -d " $LOCAL_DIR " ]] || [[ " $LOCAL_DIR " != * / ]]; then
174
- send_notification " ❌ Backup FAILED: ${HOSTNAME} " " x" " ${NTFY_PRIORITY_FAILURE} " " failure" " FATAL: LOCAL_DIR must exist and end with a trailing slash ('/')."
175
- trap - ERR; exit 2
176
- fi
174
+ for dir in $BACKUP_DIRS ; do
175
+ if [[ ! -d " $dir " ]] || [[ " $dir " != * / ]]; then
176
+ send_notification " ❌ Backup FAILED: ${HOSTNAME} " " x" " ${NTFY_PRIORITY_FAILURE} " " failure" " FATAL: A directory in BACKUP_DIRS ('$dir ') must exist and end with a trailing slash ('/')."
177
+ trap - ERR; exit 2
178
+ fi
179
+ done
177
180
178
181
179
182
# =================================================================
@@ -192,37 +195,38 @@ if [[ "${1:-}" ]]; then
192
195
--dry-run)
193
196
trap - ERR
194
197
echo " --- DRY RUN MODE ACTIVATED ---"
195
- if ! rsync " ${RSYNC_BASE_OPTS[@]} " --dry-run --info=progress2 " $LOCAL_DIR " " $REMOTE_TARGET " ; then
198
+ # shellcheck disable=SC2086
199
+ if ! rsync " ${RSYNC_BASE_OPTS[@]} " --dry-run --info=progress2 $BACKUP_DIRS " $REMOTE_TARGET " ; then
196
200
echo " "
197
201
echo " ❌ Dry run FAILED. See the rsync error message above for details."
198
202
exit 1
199
203
fi
200
204
echo " --- DRY RUN COMPLETED ---" ; exit 0 ;;
201
- --checksum)
205
+ --checksum | --summary)
206
+ # Both modes use the same check, just with different reporting
202
207
echo " --- INTEGRITY CHECK MODE ACTIVATED ---"
203
- echo " Comparing checksums... this may take a while ."
208
+ echo " Calculating differences.. ."
204
209
FILE_DISCREPANCIES=$( run_integrity_check)
205
- if [ -z " $FILE_DISCREPANCIES " ]; then
206
- echo " ✅ Checksum validation passed. No discrepancies found."
207
- log_message " Checksum validation passed. No discrepancies found."
208
- send_notification " ✅ Backup Integrity OK: ${HOSTNAME} " " white_check_mark" " ${NTFY_PRIORITY_SUCCESS} " " success" " Checksum validation passed. No discrepancies found."
209
- else
210
- log_message " Backup integrity check FAILED. Found discrepancies."
211
- ISSUE_LIST=$( echo " ${FILE_DISCREPANCIES} " | head -n 10)
212
- printf " ❌ Backup integrity check FAILED. First 10 differing files:\n%s\n" " ${ISSUE_LIST} "
213
- printf -v FAILURE_MSG " Backup integrity check FAILED.\n\nFirst 10 differing files:\n%s\n\nCheck log for full details." " ${ISSUE_LIST} "
214
- send_notification " ❌ Backup Integrity FAILED: ${HOSTNAME} " " x" " ${NTFY_PRIORITY_FAILURE} " " failure" " ${FAILURE_MSG} "
210
+
211
+ if [[ " $1 " == " --summary" ]]; then
212
+ MISMATCH_COUNT=$( echo " $FILE_DISCREPANCIES " | wc -l)
213
+ printf " 🚨 Total files with checksum mismatches: %d\n" " $MISMATCH_COUNT "
214
+ log_message " Summary mode check found $MISMATCH_COUNT mismatched files."
215
+ send_notification " 📊 Backup Summary: ${HOSTNAME} " " bar_chart" " ${NTFY_PRIORITY_SUCCESS} " " success" " Mismatched files found: $MISMATCH_COUNT "
216
+ else # --checksum
217
+ if [ -z " $FILE_DISCREPANCIES " ]; then
218
+ echo " ✅ Checksum validation passed. No discrepancies found."
219
+ log_message " Checksum validation passed. No discrepancies found."
220
+ send_notification " ✅ Backup Integrity OK: ${HOSTNAME} " " white_check_mark" " ${NTFY_PRIORITY_SUCCESS} " " success" " Checksum validation passed. No discrepancies found."
221
+ else
222
+ log_message " Backup integrity check FAILED. Found discrepancies."
223
+ ISSUE_LIST=$( echo " ${FILE_DISCREPANCIES} " | head -n 10)
224
+ printf " ❌ Backup integrity check FAILED. First 10 differing files:\n%s\n" " ${ISSUE_LIST} "
225
+ printf -v FAILURE_MSG " Backup integrity check FAILED.\n\nFirst 10 differing files:\n%s\n\nCheck log for full details." " ${ISSUE_LIST} "
226
+ send_notification " ❌ Backup Integrity FAILED: ${HOSTNAME} " " x" " ${NTFY_PRIORITY_FAILURE} " " failure" " ${FAILURE_MSG} "
227
+ fi
215
228
fi
216
229
exit 0 ;;
217
-
218
- --summary)
219
- echo " --- INTEGRITY SUMMARY MODE ---"
220
- echo " Calculating differences..."
221
- MISMATCH_COUNT=$( run_integrity_check | wc -l)
222
- printf " 🚨 Total files with checksum mismatches: %d\n" " $MISMATCH_COUNT "
223
- log_message " Summary mode check found $MISMATCH_COUNT mismatched files."
224
- send_notification " 📊 Backup Summary: ${HOSTNAME} " " bar_chart" " ${NTFY_PRIORITY_SUCCESS} " " success" " Mismatched files found: $MISMATCH_COUNT "
225
- exit 0 ;;
226
230
esac
227
231
fi
228
232
@@ -232,7 +236,6 @@ flock -n 200 || { echo "Another instance is running, exiting."; exit 5; }
232
236
if [ -f " $LOG_FILE " ] && [ " $( stat -c%s " $LOG_FILE " ) " -gt " $MAX_LOG_SIZE " ]; then
233
237
mv " $LOG_FILE " " ${LOG_FILE} .$( date +%Y%m%d_%H%M%S) "
234
238
touch " $LOG_FILE "
235
- # Clean up old log files
236
239
find " $( dirname " $LOG_FILE " ) " -name " $( basename " $LOG_FILE " ) .*" -type f -mtime +" $LOG_RETENTION_DAYS " -delete
237
240
fi
238
241
@@ -247,11 +250,13 @@ RSYNC_OPTS=("${RSYNC_BASE_OPTS[@]}")
247
250
248
251
if [[ " $VERBOSE_MODE " == " true" ]]; then
249
252
RSYNC_OPTS+=(--info=stats2,progress2)
250
- nice -n 19 ionice -c 3 rsync " ${RSYNC_OPTS[@]} " " $LOCAL_DIR " " $REMOTE_TARGET " 2>&1 | tee " $RSYNC_LOG_TMP "
253
+ # shellcheck disable=SC2086
254
+ nice -n 19 ionice -c 3 rsync " ${RSYNC_OPTS[@]} " $BACKUP_DIRS " $REMOTE_TARGET " 2>&1 | tee " $RSYNC_LOG_TMP "
251
255
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
252
256
else
253
257
RSYNC_OPTS+=(--info=stats2)
254
- nice -n 19 ionice -c 3 rsync " ${RSYNC_OPTS[@]} " " $LOCAL_DIR " " $REMOTE_TARGET " > " $RSYNC_LOG_TMP " 2>&1 || RSYNC_EXIT_CODE=$?
258
+ # shellcheck disable=SC2086
259
+ nice -n 19 ionice -c 3 rsync " ${RSYNC_OPTS[@]} " $BACKUP_DIRS " $REMOTE_TARGET " > " $RSYNC_LOG_TMP " 2>&1 || RSYNC_EXIT_CODE=$?
255
260
fi
256
261
257
262
END_TIME= $( date +%s)
0 commit comments