1- #! /bin/bash
1+ #! /usr/ bin/env bash
22
33# =================================================================
4- # Restic Backup Script v0.23 - 2025.09.09
4+ # Restic Backup Script v0.24 - 2025.09.10
55# =================================================================
66
77set -euo pipefail
88umask 077
99
1010# --- Script Constants ---
11- SCRIPT_VERSION=" 0.23 "
11+ SCRIPT_VERSION=" 0.24 "
1212SCRIPT_DIR=$( cd -- " $( dirname -- " ${BASH_SOURCE[0]} " ) " & > /dev/null && pwd)
1313CONFIG_FILE=" ${SCRIPT_DIR} /restic-backup.conf"
1414LOCK_FILE=" /tmp/restic-backup.lock"
4646
4747import_restic_key () {
4848 local fpr=" CF8F18F2844575973F79D4E191A6868BD3F7A907"
49- # Return successfully if key already exists
50- gpg --list-keys " $fpr " > /dev/null 2>&1 && return 0
51- local servers=(
52- " hkps://keyserver.ubuntu.com"
53- " hkps://keys.openpgp.org"
54- " hkps://pgpkeys.eu"
55- )
56- for ks in " ${servers[@]} " ; do
57- echo " Fetching restic release key from $ks ..."
58- if gpg --keyserver " $ks " --recv-keys " $fpr " ; then
49+ # Check local user keyring
50+ if gpg --list-keys " $fpr " > /dev/null 2>&1 ; then
51+ return 0
52+ fi
53+ # Check Debian/Ubuntu system keyring
54+ local debian_keyring=" /usr/share/keyrings/restic-archive-keyring.gpg"
55+ if [[ -f " $debian_keyring " ]]; then
56+ echo " Found debian keyring, checking for key..."
57+ if gpg --no-default-keyring --keyring " $debian_keyring " --list-keys " $fpr " > /dev/null 2>&1 ; then
58+ echo " Importing trusted key from system keyring..."
59+ gpg --no-default-keyring --keyring " $debian_keyring " --export " $fpr " | gpg --import > /dev/null 2>&1
60+ return $?
61+ fi
62+ fi
63+ # Try public keyservers fallback
64+ local servers=( " hkps://hkps.pool.sks-keyservers.net" " hkps://keys.openpgp.org" " hkps://keyserver.ubuntu.com" )
65+ for server in " ${servers[@]} " ; do
66+ echo " Attempting to fetch from $server ..."
67+ if gpg --keyserver " $server " --recv-keys " $fpr " ; then
68+ echo " Key imported successfully."
5969 return 0
6070 fi
6171 done
62- echo -e " ${C_RED} Failed to import restic PGP key from all keyservers. ${C_RESET} " >&2
72+ echo " Failed to import restic PGP key. " >&2
6373 return 1
6474}
6575
@@ -475,87 +485,101 @@ cleanup() {
475485
476486run_preflight_checks () {
477487 local mode=" ${1:- backup} "
478- echo -e " ${C_BOLD} --- Running Pre-flight Checks ---${C_RESET} "
488+ local verbosity=" ${2:- quiet} "
489+
490+ if [[ " $verbosity " == " verbose" ]]; then
491+ echo -e " ${C_BOLD} --- Running Pre-flight Checks ---${C_RESET} "
492+ fi
479493
480494 # System Dependencies
481- echo -e " \n ${C_DIM} - Checking System Dependencies${C_RESET} "
482- printf " %-65s" " Required commands (restic, curl, flock)..."
495+ if [[ " $verbosity " == " verbose" ]]; then
496+ echo -e " \n ${C_DIM} - Checking System Dependencies${C_RESET} "
497+ printf " %-65s" " Required commands (restic, curl, flock)..."
498+ fi
483499 local required_cmds=(restic curl flock)
484500 for cmd in " ${required_cmds[@]} " ; do
485501 if ! command -v " $cmd " & > /dev/null; then
486- echo -e " [${C_RED} FAIL ${C_RESET} ]"
502+ [[ " $verbosity " == " verbose " ]] && echo -e " [${C_RED} FAIL ${C_RESET} ]"
487503 echo -e " ${C_RED} ERROR: Required command '$cmd ' not found${C_RESET} " >&2
488504 exit 10
489505 fi
490506 done
491- echo -e " [${C_GREEN} OK ${C_RESET} ]"
507+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_GREEN} OK ${C_RESET} ]" ; fi
492508
493509 # Configuration Files
494- echo -e " \n ${C_DIM} - Checking Configuration Files${C_RESET} "
495- printf " %-65s" " Password file ('$RESTIC_PASSWORD_FILE ')..."
510+ if [[ " $verbosity " == " verbose" ]]; then echo -e " \n ${C_DIM} - Checking Configuration Files${C_RESET} " ; fi
511+
512+ if [[ " $verbosity " == " verbose" ]]; then printf " %-65s" " Password file ('$RESTIC_PASSWORD_FILE ')..." ; fi
496513 if [ ! -r " $RESTIC_PASSWORD_FILE " ]; then
497- echo -e " [${C_RED} FAIL ${C_RESET} ]"
514+ [[ " $verbosity " == " verbose " ]] && echo -e " [${C_RED} FAIL ${C_RESET} ]"
498515 echo -e " ${C_RED} ERROR: Password file not found or not readable: $RESTIC_PASSWORD_FILE ${C_RESET} " >&2
499516 exit 11
500517 fi
501- echo -e " [${C_GREEN} OK ${C_RESET} ]"
518+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_GREEN} OK ${C_RESET} ]" ; fi
502519
503520 if [ -n " ${EXCLUDE_FILE:- } " ]; then
504- printf " %-65s" " Exclude file ('$EXCLUDE_FILE ')..."
521+ if [[ " $verbosity " == " verbose " ]] ; then printf " %-65s" " Exclude file ('$EXCLUDE_FILE ')..." ; fi
505522 if [ ! -r " $EXCLUDE_FILE " ]; then
506- echo -e " [${C_RED} FAIL ${C_RESET} ]"
523+ [[ " $verbosity " == " verbose " ]] && echo -e " [${C_RED} FAIL ${C_RESET} ]"
507524 echo -e " ${C_RED} ERROR: The specified EXCLUDE_FILE is not readable: ${EXCLUDE_FILE}${C_RESET} " >&2
508525 exit 14
509526 fi
510- echo -e " [${C_GREEN} OK ${C_RESET} ]"
527+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_GREEN} OK ${C_RESET} ]" ; fi
511528 fi
512529
513- printf " %-65s" " Log file writability ('$LOG_FILE ')..."
530+ if [[ " $verbosity " == " verbose " ]] ; then printf " %-65s" " Log file writability ('$LOG_FILE ')..." ; fi
514531 if ! touch " $LOG_FILE " > /dev/null 2>&1 ; then
515- echo -e " [${C_RED} FAIL ${C_RESET} ]"
532+ [[ " $verbosity " == " verbose " ]] && echo -e " [${C_RED} FAIL ${C_RESET} ]"
516533 echo -e " ${C_RED} ERROR: The log file or its directory is not writable: ${LOG_FILE}${C_RESET} " >&2
517534 exit 15
518535 fi
519- echo -e " [${C_GREEN} OK ${C_RESET} ]"
536+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_GREEN} OK ${C_RESET} ]" ; fi
520537
521538 # Repository State
522- echo -e " \n ${C_DIM} - Checking Repository State${C_RESET} "
523- printf " %-65s" " Repository connectivity and credentials..."
539+ if [[ " $verbosity " == " verbose" ]]; then echo -e " \n ${C_DIM} - Checking Repository State${C_RESET} " ; fi
540+
541+ if [[ " $verbosity " == " verbose" ]]; then printf " %-65s" " Repository connectivity and credentials..." ; fi
524542 if ! restic cat config > /dev/null 2>&1 ; then
525543 if [[ " $mode " == " init" ]]; then
526- echo -e " [${C_YELLOW} SKIP ${C_RESET} ] (OK for --init mode)"
544+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_YELLOW} SKIP ${C_RESET} ] (OK for --init mode)" ; fi
527545 return 0
528546 fi
529- echo -e " [${C_RED} FAIL ${C_RESET} ]"
547+ [[ " $verbosity " == " verbose " ]] && echo -e " [${C_RED} FAIL ${C_RESET} ]"
530548 echo -e " ${C_RED} ERROR: Cannot access repository. Check credentials or run --init first.${C_RESET} " >&2
531549 exit 12
532550 fi
533- echo -e " [${C_GREEN} OK ${C_RESET} ]"
551+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_GREEN} OK ${C_RESET} ]" ; fi
534552
535- printf " %-65s" " Stale repository locks..."
553+ if [[ " $verbosity " == " verbose " ]] ; then printf " %-65s" " Stale repository locks..." ; fi
536554 local lock_info
537555 lock_info=$( restic list locks 2> /dev/null || true)
538556 if [ -n " $lock_info " ]; then
539- echo -e " [${C_YELLOW} WARN ${C_RESET} ]"
540- echo -e " ${C_YELLOW} ⚠️ Stale locks found! This may prevent backups from running.${C_RESET} "
541- echo -e " ${C_DIM} Run the --unlock command to remove them.${C_RESET} "
557+ if [[ " $verbosity " == " verbose" ]]; then
558+ echo -e " [${C_YELLOW} WARN ${C_RESET} ]"
559+ echo -e " ${C_YELLOW} ⚠️ Stale locks found! This may prevent backups from running.${C_RESET} "
560+ echo -e " ${C_DIM} Run the --unlock command to remove them.${C_RESET} "
561+ fi
542562 else
543- echo -e " [${C_GREEN} OK ${C_RESET} ]"
563+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_GREEN} OK ${C_RESET} ]" ; fi
544564 fi
545565
546566 # Backup Sources
547567 if [[ " $mode " == " backup" || " $mode " == " diff" ]]; then
548- echo -e " \n ${C_DIM} - Checking Backup Sources${C_RESET} "
568+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " \n ${C_DIM} - Checking Backup Sources${C_RESET} " ; fi
549569 for source in $BACKUP_SOURCES ; do
550- printf " %-65s" " Source directory ('$source ')..."
570+ if [[ " $verbosity " == " verbose " ]] ; then printf " %-65s" " Source directory ('$source ')..." ; fi
551571 if [ ! -d " $source " ] || [ ! -r " $source " ]; then
552- echo -e " [${C_RED} FAIL ${C_RESET} ]"
572+ [[ " $verbosity " == " verbose " ]] && echo -e " [${C_RED} FAIL ${C_RESET} ]"
553573 echo -e " ${C_RED} ERROR: Source directory not found or not readable: $source ${C_RESET} " >&2
554574 exit 13
555575 fi
556- echo -e " [${C_GREEN} OK ${C_RESET} ]"
576+ if [[ " $verbosity " == " verbose " ]] ; then echo -e " [${C_GREEN} OK ${C_RESET} ]" ; fi
557577 done
558578 fi
579+
580+ if [[ " $verbosity " == " quiet" ]]; then
581+ echo -e " ${C_GREEN} ✅ Pre-flight checks passed.${C_RESET} "
582+ fi
559583}
560584
561585rotate_log () {
@@ -888,12 +912,12 @@ rotate_log
888912# Handle different modes
889913case " ${1:- } " in
890914 --init)
891- run_preflight_checks " init"
915+ run_preflight_checks " init" " quiet "
892916 init_repository
893917 ;;
894918 --dry-run)
895919 echo -e " ${C_BOLD} --- Dry Run Mode ---${C_RESET} "
896- run_preflight_checks
920+ run_preflight_checks " backup " " quiet "
897921 backup_cmd=(restic)
898922 [ " ${LOG_LEVEL:- 1} " -le 0 ] && backup_cmd+=(--quiet)
899923 [ " ${LOG_LEVEL:- 1} " -ge 2 ] && backup_cmd+=(--verbose)
@@ -911,31 +935,31 @@ case "${1:-}" in
911935 ;;
912936 --test)
913937 echo -e " ${C_BOLD} --- Test Mode ---${C_RESET} "
914- run_preflight_checks
938+ run_preflight_checks " backup " " verbose "
915939 echo -e " ${C_GREEN} ✅ All tests passed${C_RESET} "
916940 ;;
917941 --snapshots)
918- run_preflight_checks
942+ run_preflight_checks " backup " " quiet "
919943 run_snapshots
920944 ;;
921945 --restore)
922- run_preflight_checks " restore"
946+ run_preflight_checks " restore" " quiet "
923947 run_restore
924948 ;;
925949 --check)
926- run_preflight_checks
950+ run_preflight_checks " backup " " quiet "
927951 run_check
928952 ;;
929953 --forget)
930- run_preflight_checks
954+ run_preflight_checks " backup " " quiet "
931955 run_forget
932956 ;;
933957 --diff)
934- run_preflight_checks " diff"
958+ run_preflight_checks " diff" " quiet "
935959 run_diff
936960 ;;
937961 --unlock)
938- run_preflight_checks " unlock"
962+ run_preflight_checks " unlock" " quiet "
939963 run_unlock
940964 ;;
941965 --help | -h)
@@ -949,7 +973,7 @@ case "${1:-}" in
949973 fi
950974
951975 # Default: full backup
952- run_preflight_checks
976+ run_preflight_checks " backup " " quiet "
953977
954978 log_message " === Starting backup run ==="
955979
0 commit comments