Skip to content

Commit 411554a

Browse files
authored
Merge pull request #12 from buildplan/loop_directories
Loop directories to backup location to avoid issues with overlap
2 parents fc8dfa0 + f730671 commit 411554a

File tree

2 files changed

+158
-84
lines changed

2 files changed

+158
-84
lines changed

README.md

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ END_EXCLUDES
222222

223223
# =================================================================
224224
# SCRIPT INITIALIZATION & SETUP
225-
# v0.9 - 2025.08.09
225+
# v0.10 - 2025.08.09
226226
# =================================================================
227227
set -Euo pipefail
228228
umask 077
@@ -330,8 +330,12 @@ send_notification() {
330330

331331
run_integrity_check() {
332332
local rsync_check_opts=(-ainc -c --delete --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
333-
# shellcheck disable=SC2086
334-
LC_ALL=C rsync "${rsync_check_opts[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>> "${LOG_FILE:-/dev/null}"
333+
334+
for dir in $BACKUP_DIRS; do
335+
local remote_subdir="${REMOTE_TARGET}/$(basename "$dir")/"
336+
# shellcheck disable=SC2086
337+
LC_ALL=C rsync "${rsync_check_opts[@]}" "$dir" "$remote_subdir" 2>> "${LOG_FILE:-/dev/null}"
338+
done
335339
}
336340

337341
parse_stat() {
@@ -415,15 +419,24 @@ if [[ "${1:-}" ]]; then
415419
--dry-run)
416420
trap - ERR
417421
echo "--- DRY RUN MODE ACTIVATED ---"
418-
# shellcheck disable=SC2086
419-
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 $BACKUP_DIRS "$REMOTE_TARGET"; then
420-
echo ""
421-
echo "❌ Dry run FAILED. See the rsync error message above for details."
422-
exit 1
422+
DRY_RUN_FAILED=false
423+
for dir in $BACKUP_DIRS; do
424+
remote_subdir="${REMOTE_TARGET}/$(basename "$dir")/"
425+
echo "--- Checking dry run for: $dir -> $remote_subdir"
426+
# shellcheck disable=SC2086
427+
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 "$dir" "$remote_subdir"; then
428+
DRY_RUN_FAILED=true
429+
fi
430+
done
431+
432+
if [[ "$DRY_RUN_FAILED" == "true" ]]; then
433+
echo ""
434+
echo "❌ Dry run FAILED for one or more directories. See the rsync error messages above for details."
435+
exit 1
423436
fi
424437
echo "--- DRY RUN COMPLETED ---"; exit 0 ;;
438+
425439
--checksum | --summary)
426-
# Both modes use the same check, just with different reporting
427440
echo "--- INTEGRITY CHECK MODE ACTIVATED ---"
428441
echo "Calculating differences..."
429442
FILE_DISCREPANCIES=$(run_integrity_check)
@@ -462,47 +475,71 @@ fi
462475
echo "============================================================" >> "$LOG_FILE"
463476
log_message "Starting rsync backup..."
464477

478+
# --- Execute Backup for Each Directory ---
465479
START_TIME=$(date +%s)
480+
success_dirs=()
481+
failed_dirs=()
482+
overall_exit_code=0
483+
full_rsync_output=""
484+
485+
for dir in $BACKUP_DIRS; do
486+
log_message "Backing up directory: $dir"
487+
488+
remote_subdir="${REMOTE_TARGET}/$(basename "$dir")/"
489+
490+
RSYNC_LOG_TMP=$(mktemp)
491+
RSYNC_EXIT_CODE=0
492+
RSYNC_OPTS=("${RSYNC_BASE_OPTS[@]}")
493+
494+
if [[ "$VERBOSE_MODE" == "true" ]]; then
495+
RSYNC_OPTS+=(--info=stats2,progress2)
496+
# shellcheck disable=SC2086
497+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$dir" "$remote_subdir" 2>&1 | tee "$RSYNC_LOG_TMP"
498+
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
499+
else
500+
RSYNC_OPTS+=(--info=stats2)
501+
# shellcheck disable=SC2086
502+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$dir" "$remote_subdir" > "$RSYNC_LOG_TMP" 2>&1 || RSYNC_EXIT_CODE=$?
503+
fi
466504

467-
RSYNC_LOG_TMP=$(mktemp)
468-
RSYNC_EXIT_CODE=0
469-
RSYNC_OPTS=("${RSYNC_BASE_OPTS[@]}")
505+
cat "$RSYNC_LOG_TMP" >> "$LOG_FILE"
506+
full_rsync_output+=$'\n'"$(<"$RSYNC_LOG_TMP")"
507+
rm -f "$RSYNC_LOG_TMP"
470508

471-
if [[ "$VERBOSE_MODE" == "true" ]]; then
472-
RSYNC_OPTS+=(--info=stats2,progress2)
473-
# shellcheck disable=SC2086
474-
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>&1 | tee "$RSYNC_LOG_TMP"
475-
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
476-
else
477-
RSYNC_OPTS+=(--info=stats2)
478-
# shellcheck disable=SC2086
479-
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" $BACKUP_DIRS "$REMOTE_TARGET" > "$RSYNC_LOG_TMP" 2>&1 || RSYNC_EXIT_CODE=$?
480-
fi
509+
if [[ $RSYNC_EXIT_CODE -eq 0 || $RSYNC_EXIT_CODE -eq 24 ]]; then
510+
success_dirs+=("$(basename "$dir")")
511+
if [[ $RSYNC_EXIT_CODE -eq 24 ]]; then
512+
log_message "WARNING for $dir: rsync completed with code 24 (some source files vanished)."
513+
overall_exit_code=24 # Mark the overall run as a warning
514+
fi
515+
else
516+
failed_dirs+=("$(basename "$dir")")
517+
log_message "FAILED for $dir: rsync exited with code: $RSYNC_EXIT_CODE."
518+
overall_exit_code=1 # Mark the overall run as a failure
519+
fi
520+
done
481521

482522
END_TIME=$(date +%s)
483523
DURATION=$((END_TIME - START_TIME))
484-
485-
cat "$RSYNC_LOG_TMP" >> "$LOG_FILE"
486-
RSYNC_OUTPUT=$(<"$RSYNC_LOG_TMP")
487-
488524
trap - ERR
489525

490-
case $RSYNC_EXIT_CODE in
491-
0)
492-
BACKUP_STATS=$(format_backup_stats "$RSYNC_OUTPUT")
493-
SUCCESS_MSG=$(printf "%s\n\nDuration: %dm %ds" "$BACKUP_STATS" $((DURATION / 60)) $((DURATION % 60)))
494-
log_message "SUCCESS: rsync completed."
495-
send_notification "✅ Backup SUCCESS: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "$SUCCESS_MSG" ;;
496-
24)
497-
BACKUP_STATS=$(format_backup_stats "$RSYNC_OUTPUT")
498-
WARN_MSG=$(printf "rsync completed with a warning (code 24).\nSome source files vanished during transfer.\n\n%s\n\nDuration: %dm %ds" "$BACKUP_STATS" $((DURATION / 60)) $((DURATION % 60)))
499-
log_message "WARNING: rsync completed with code 24 (some source files vanished)."
500-
send_notification "⚠️ Backup Warning: ${HOSTNAME}" "warning" "${NTFY_PRIORITY_WARNING}" "warning" "$WARN_MSG" ;;
501-
*)
502-
FAIL_MSG=$(printf "rsync failed on ${HOSTNAME} with exit code ${RSYNC_EXIT_CODE}. Check log for details.\n\nDuration: %dm %ds" $((DURATION / 60)) $((DURATION % 60)))
503-
log_message "FAILED: rsync exited with code: $RSYNC_EXIT_CODE."
504-
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "$FAIL_MSG" ;;
505-
esac
526+
# --- Final Notification Logic ---
527+
BACKUP_STATS=$(format_backup_stats "$full_rsync_output")
528+
FINAL_MESSAGE=$(printf "%s\n\nDuration: %dm %ds" "$BACKUP_STATS" $((DURATION / 60)) $((DURATION % 60)))
529+
530+
if [[ ${#failed_dirs[@]} -eq 0 ]]; then
531+
log_message "SUCCESS: All backups completed."
532+
if [[ $overall_exit_code -eq 24 ]]; then
533+
send_notification "⚠️ Backup Warning: ${HOSTNAME}" "warning" "${NTFY_PRIORITY_WARNING}" "warning" "$FINAL_MESSAGE"
534+
else
535+
send_notification "✅ Backup SUCCESS: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "$FINAL_MESSAGE"
536+
fi
537+
else
538+
printf -v FAIL_MSG "One or more backups failed.\n\nSuccessful: %s\nFailed: %s\n\n%s" \
539+
"${success_dirs[*]:-None}" "${failed_dirs[*]}" "$FINAL_MESSAGE"
540+
log_message "FAILURE: One or more backups failed."
541+
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "$FAIL_MSG"
542+
fi
506543

507544
echo "======================= Run Finished =======================" >> "$LOG_FILE"
508545
echo "" >> "$LOG_FILE"

backup_script.sh

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# =================================================================
44
# SCRIPT INITIALIZATION & SETUP
5-
# v0.9 - 2025.08.09
5+
# v0.10 - 2025.08.09
66
# =================================================================
77
set -Euo pipefail
88
umask 077
@@ -110,8 +110,12 @@ send_notification() {
110110

111111
run_integrity_check() {
112112
local rsync_check_opts=(-ainc -c --delete --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
113-
# shellcheck disable=SC2086
114-
LC_ALL=C rsync "${rsync_check_opts[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>> "${LOG_FILE:-/dev/null}"
113+
114+
for dir in $BACKUP_DIRS; do
115+
local remote_subdir="${REMOTE_TARGET}/$(basename "$dir")/"
116+
# shellcheck disable=SC2086
117+
LC_ALL=C rsync "${rsync_check_opts[@]}" "$dir" "$remote_subdir" 2>> "${LOG_FILE:-/dev/null}"
118+
done
115119
}
116120

117121
parse_stat() {
@@ -195,15 +199,24 @@ if [[ "${1:-}" ]]; then
195199
--dry-run)
196200
trap - ERR
197201
echo "--- DRY RUN MODE ACTIVATED ---"
198-
# shellcheck disable=SC2086
199-
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 $BACKUP_DIRS "$REMOTE_TARGET"; then
200-
echo ""
201-
echo "❌ Dry run FAILED. See the rsync error message above for details."
202-
exit 1
202+
DRY_RUN_FAILED=false
203+
for dir in $BACKUP_DIRS; do
204+
remote_subdir="${REMOTE_TARGET}/$(basename "$dir")/"
205+
echo "--- Checking dry run for: $dir -> $remote_subdir"
206+
# shellcheck disable=SC2086
207+
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 "$dir" "$remote_subdir"; then
208+
DRY_RUN_FAILED=true
209+
fi
210+
done
211+
212+
if [[ "$DRY_RUN_FAILED" == "true" ]]; then
213+
echo ""
214+
echo "❌ Dry run FAILED for one or more directories. See the rsync error messages above for details."
215+
exit 1
203216
fi
204217
echo "--- DRY RUN COMPLETED ---"; exit 0 ;;
218+
205219
--checksum | --summary)
206-
# Both modes use the same check, just with different reporting
207220
echo "--- INTEGRITY CHECK MODE ACTIVATED ---"
208221
echo "Calculating differences..."
209222
FILE_DISCREPANCIES=$(run_integrity_check)
@@ -242,47 +255,71 @@ fi
242255
echo "============================================================" >> "$LOG_FILE"
243256
log_message "Starting rsync backup..."
244257

258+
# --- Execute Backup for Each Directory ---
245259
START_TIME=$(date +%s)
260+
success_dirs=()
261+
failed_dirs=()
262+
overall_exit_code=0
263+
full_rsync_output=""
264+
265+
for dir in $BACKUP_DIRS; do
266+
log_message "Backing up directory: $dir"
267+
268+
remote_subdir="${REMOTE_TARGET}/$(basename "$dir")/"
269+
270+
RSYNC_LOG_TMP=$(mktemp)
271+
RSYNC_EXIT_CODE=0
272+
RSYNC_OPTS=("${RSYNC_BASE_OPTS[@]}")
273+
274+
if [[ "$VERBOSE_MODE" == "true" ]]; then
275+
RSYNC_OPTS+=(--info=stats2,progress2)
276+
# shellcheck disable=SC2086
277+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$dir" "$remote_subdir" 2>&1 | tee "$RSYNC_LOG_TMP"
278+
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
279+
else
280+
RSYNC_OPTS+=(--info=stats2)
281+
# shellcheck disable=SC2086
282+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$dir" "$remote_subdir" > "$RSYNC_LOG_TMP" 2>&1 || RSYNC_EXIT_CODE=$?
283+
fi
246284

247-
RSYNC_LOG_TMP=$(mktemp)
248-
RSYNC_EXIT_CODE=0
249-
RSYNC_OPTS=("${RSYNC_BASE_OPTS[@]}")
285+
cat "$RSYNC_LOG_TMP" >> "$LOG_FILE"
286+
full_rsync_output+=$'\n'"$(<"$RSYNC_LOG_TMP")"
287+
rm -f "$RSYNC_LOG_TMP"
250288

251-
if [[ "$VERBOSE_MODE" == "true" ]]; then
252-
RSYNC_OPTS+=(--info=stats2,progress2)
253-
# shellcheck disable=SC2086
254-
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>&1 | tee "$RSYNC_LOG_TMP"
255-
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
256-
else
257-
RSYNC_OPTS+=(--info=stats2)
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=$?
260-
fi
289+
if [[ $RSYNC_EXIT_CODE -eq 0 || $RSYNC_EXIT_CODE -eq 24 ]]; then
290+
success_dirs+=("$(basename "$dir")")
291+
if [[ $RSYNC_EXIT_CODE -eq 24 ]]; then
292+
log_message "WARNING for $dir: rsync completed with code 24 (some source files vanished)."
293+
overall_exit_code=24 # Mark the overall run as a warning
294+
fi
295+
else
296+
failed_dirs+=("$(basename "$dir")")
297+
log_message "FAILED for $dir: rsync exited with code: $RSYNC_EXIT_CODE."
298+
overall_exit_code=1 # Mark the overall run as a failure
299+
fi
300+
done
261301

262302
END_TIME=$(date +%s)
263303
DURATION=$((END_TIME - START_TIME))
264-
265-
cat "$RSYNC_LOG_TMP" >> "$LOG_FILE"
266-
RSYNC_OUTPUT=$(<"$RSYNC_LOG_TMP")
267-
268304
trap - ERR
269305

270-
case $RSYNC_EXIT_CODE in
271-
0)
272-
BACKUP_STATS=$(format_backup_stats "$RSYNC_OUTPUT")
273-
SUCCESS_MSG=$(printf "%s\n\nDuration: %dm %ds" "$BACKUP_STATS" $((DURATION / 60)) $((DURATION % 60)))
274-
log_message "SUCCESS: rsync completed."
275-
send_notification "✅ Backup SUCCESS: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "$SUCCESS_MSG" ;;
276-
24)
277-
BACKUP_STATS=$(format_backup_stats "$RSYNC_OUTPUT")
278-
WARN_MSG=$(printf "rsync completed with a warning (code 24).\nSome source files vanished during transfer.\n\n%s\n\nDuration: %dm %ds" "$BACKUP_STATS" $((DURATION / 60)) $((DURATION % 60)))
279-
log_message "WARNING: rsync completed with code 24 (some source files vanished)."
280-
send_notification "⚠️ Backup Warning: ${HOSTNAME}" "warning" "${NTFY_PRIORITY_WARNING}" "warning" "$WARN_MSG" ;;
281-
*)
282-
FAIL_MSG=$(printf "rsync failed on ${HOSTNAME} with exit code ${RSYNC_EXIT_CODE}. Check log for details.\n\nDuration: %dm %ds" $((DURATION / 60)) $((DURATION % 60)))
283-
log_message "FAILED: rsync exited with code: $RSYNC_EXIT_CODE."
284-
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "$FAIL_MSG" ;;
285-
esac
306+
# --- Final Notification Logic ---
307+
BACKUP_STATS=$(format_backup_stats "$full_rsync_output")
308+
FINAL_MESSAGE=$(printf "%s\n\nDuration: %dm %ds" "$BACKUP_STATS" $((DURATION / 60)) $((DURATION % 60)))
309+
310+
if [[ ${#failed_dirs[@]} -eq 0 ]]; then
311+
log_message "SUCCESS: All backups completed."
312+
if [[ $overall_exit_code -eq 24 ]]; then
313+
send_notification "⚠️ Backup Warning: ${HOSTNAME}" "warning" "${NTFY_PRIORITY_WARNING}" "warning" "$FINAL_MESSAGE"
314+
else
315+
send_notification "✅ Backup SUCCESS: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "$FINAL_MESSAGE"
316+
fi
317+
else
318+
printf -v FAIL_MSG "One or more backups failed.\n\nSuccessful: %s\nFailed: %s\n\n%s" \
319+
"${success_dirs[*]:-None}" "${failed_dirs[*]}" "$FINAL_MESSAGE"
320+
log_message "FAILURE: One or more backups failed."
321+
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "$FAIL_MSG"
322+
fi
286323

287324
echo "======================= Run Finished =======================" >> "$LOG_FILE"
288325
echo "" >> "$LOG_FILE"

0 commit comments

Comments
 (0)