Skip to content

Commit daabd67

Browse files
authored
Merge pull request #36 from buildplan/help_without_sudo
Enhancement: Non-root help display, improved process hygiene, and safety checks
2 parents 39f6946 + eb475e6 commit daabd67

File tree

2 files changed

+91
-64
lines changed

2 files changed

+91
-64
lines changed

restic-backup.sh

Lines changed: 90 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
#!/usr/bin/env bash
22

33
# =================================================================
4-
# Restic Backup Script v0.41 - 2025.11.24
4+
# Restic Backup Script v0.42 - 2025.12.17
55
# =================================================================
66

7-
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
7+
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH
88
set -euo pipefail
99
umask 077
1010

1111
# --- Script Constants ---
12-
SCRIPT_VERSION="0.41"
12+
SCRIPT_VERSION="0.42"
1313
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
1414
PROG_NAME=$(basename "$0"); readonly PROG_NAME
1515
CONFIG_FILE="${SCRIPT_DIR}/restic-backup.conf"
@@ -35,6 +35,77 @@ else
3535
C_CYAN=''
3636
fi
3737

38+
# --- Ensure running as root ---
39+
display_help() {
40+
local readme_url="https://github.com/buildplan/restic-backup-script/blob/main/README.md"
41+
42+
echo -e "${C_BOLD}${C_CYAN}Restic Backup Script (v${SCRIPT_VERSION})${C_RESET}"
43+
echo "Encrypted, deduplicated backups with restic."
44+
echo
45+
echo -e "${C_BOLD}${C_YELLOW}USAGE:${C_RESET}"
46+
echo -e " sudo $PROG_NAME ${C_GREEN}[options] [command]${C_RESET}"
47+
echo
48+
echo -e "${C_BOLD}${C_YELLOW}OPTIONS:${C_RESET}"
49+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--verbose" "Show detailed live output."
50+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--fix-permissions" "Interactive only: auto-fix 600/400 on conf/secret."
51+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--help, -h" "Display this help message."
52+
echo
53+
echo -e "${C_BOLD}${C_YELLOW}COMMANDS:${C_RESET}"
54+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "[no command]" "Run a standard backup and apply the retention policy."
55+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--init" "Initialize a new restic repository (one-time setup)."
56+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--diff" "Show a summary of changes between the last two snapshots."
57+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--snapshots" "List all available snapshots in the repository."
58+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--snapshots-delete" "Interactively select and permanently delete snapshots."
59+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--stats" "Display repository size and file counts."
60+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--check" "Verify repository integrity (subset)."
61+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--check-full" "Verify all repository data (slow)."
62+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--forget" "Apply retention policy; optionally prune."
63+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--unlock" "Remove stale repository locks."
64+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--dump <id> <path>" "Dump a single file from a snapshot to stdout."
65+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--restore" "Interactive restore wizard."
66+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--ls <snapshot_id>" "List files and directories inside a specific snapshot."
67+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--find <pattern...>" "Search for files/dirs across all snapshots (e.g., --find \"*.log\" -l)."
68+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--background-restore" "Run a non-interactive restore in the background."
69+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--sync-restore" "Run a non-interactive restore in the foreground (for cron)."
70+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--dry-run" "Preview backup changes (no snapshot)."
71+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--test" "Validate config, permissions, connectivity."
72+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--install-scheduler" "Install an automated schedule (systemd/cron)."
73+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--recovery-kit" "Generate a self-contained recovery script (with embedded password)."
74+
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--uninstall-scheduler" "Remove an automated schedule."
75+
echo
76+
echo -e "${C_BOLD}${C_YELLOW}QUICK EXAMPLES:${C_RESET}"
77+
echo -e " Run a backup now: ${C_GREEN}sudo $PROG_NAME${C_RESET}"
78+
echo -e " Verbose diff summary: ${C_GREEN}sudo $PROG_NAME --verbose --diff${C_RESET}"
79+
echo -e " Fix perms (interactive): ${C_GREEN}sudo $PROG_NAME --fix-permissions --test${C_RESET}"
80+
echo -e " Background restore: ${C_GREEN}sudo $PROG_NAME --background-restore latest /mnt/restore${C_RESET}"
81+
echo -e " List snapshot contents: ${C_GREEN}sudo $PROG_NAME --ls latest /path/to/dir${C_RESET}"
82+
echo -e " Find a file everywhere: ${C_GREEN}sudo $PROG_NAME --find \"*.log\" -l${C_RESET}"
83+
echo -e " Dump one file to stdout: ${C_GREEN}sudo $PROG_NAME --dump latest /etc/hosts > hosts.txt${C_RESET}"
84+
echo
85+
echo -e "${C_BOLD}${C_YELLOW}DEPENDENCIES:${C_RESET}"
86+
echo -e " This script requires: ${C_GREEN}restic, curl, gpg, bzip2, less, jq, flock${C_RESET}"
87+
echo
88+
local display_log
89+
if [ -r "$CONFIG_FILE" ]; then
90+
# shellcheck source=/dev/null disable=SC2153
91+
display_log=$(source "$CONFIG_FILE" >/dev/null 2>&1; echo "$LOG_FILE")
92+
else
93+
display_log="(requires sudo to read config)"
94+
fi
95+
echo -e "Config: ${C_DIM}${CONFIG_FILE}${C_RESET} Log: ${C_DIM}${display_log:-"(not set)"}${C_RESET}"
96+
echo
97+
echo -e "For full details, see the online documentation: \e]8;;${readme_url}\a${C_CYAN}README.md${C_RESET}\e]8;;\a"
98+
echo -e "${C_YELLOW}Note:${C_RESET} For restic official documentation See: https://restic.readthedocs.io/"
99+
echo
100+
}
101+
# Scan arguments for help flag immediately
102+
for arg in "$@"; do
103+
if [[ "$arg" == "-h" || "$arg" == "--help" ]]; then
104+
display_help
105+
exit 0
106+
fi
107+
done
108+
38109
# --- Ensure running as root ---
39110
if [[ $EUID -ne 0 ]]; then
40111
echo -e "${C_BOLD}${C_YELLOW}This script requires root privileges.${C_RESET}"
@@ -194,7 +265,7 @@ check_and_install_restic() {
194265
echo "Decompressing and installing to /usr/local/bin/restic..."
195266
if bunzip2 -c "$temp_binary" > /usr/local/bin/restic.tmp; then
196267
chmod +x /usr/local/bin/restic.tmp
197-
mv /usr/local/bin/restic.tmp /usr/local/bin/restic
268+
mv /usr/local/bin/restic.tmp /usr/local/bin/restic
198269
if [[ "$IS_SELINUX_DISTRO" == "true" ]] && command -v restorecon &>/dev/null; then
199270
echo "Applying SELinux context to binary..."
200271
restorecon -v /usr/local/bin/restic || true
@@ -296,62 +367,6 @@ done
296367
# UTILITY FUNCTIONS
297368
# =================================================================
298369

299-
display_help() {
300-
local readme_url="https://github.com/buildplan/restic-backup-script/blob/main/README.md"
301-
302-
echo -e "${C_BOLD}${C_CYAN}Restic Backup Script (v${SCRIPT_VERSION})${C_RESET}"
303-
echo "Encrypted, deduplicated backups with restic."
304-
echo
305-
echo -e "${C_BOLD}${C_YELLOW}USAGE:${C_RESET}"
306-
echo -e " sudo $PROG_NAME ${C_GREEN}[options] [command]${C_RESET}"
307-
echo
308-
echo -e "${C_BOLD}${C_YELLOW}OPTIONS:${C_RESET}"
309-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--verbose" "Show detailed live output."
310-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--fix-permissions" "Interactive only: auto-fix 600/400 on conf/secret."
311-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--help, -h" "Display this help message."
312-
echo
313-
echo -e "${C_BOLD}${C_YELLOW}COMMANDS:${C_RESET}"
314-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "[no command]" "Run a standard backup and apply the retention policy."
315-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--init" "Initialize a new restic repository (one-time setup)."
316-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--diff" "Show a summary of changes between the last two snapshots."
317-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--snapshots" "List all available snapshots in the repository."
318-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--snapshots-delete" "Interactively select and permanently delete snapshots."
319-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--stats" "Display repository size and file counts."
320-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--check" "Verify repository integrity (subset)."
321-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--check-full" "Verify all repository data (slow)."
322-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--forget" "Apply retention policy; optionally prune."
323-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--unlock" "Remove stale repository locks."
324-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--dump <id> <path>" "Dump a single file from a snapshot to stdout."
325-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--restore" "Interactive restore wizard."
326-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--ls <snapshot_id>" "List files and directories inside a specific snapshot."
327-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--find <pattern...>" "Search for files/dirs across all snapshots (e.g., --find \"*.log\" -l)."
328-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--background-restore" "Run a non-interactive restore in the background."
329-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--sync-restore" "Run a non-interactive restore in the foreground (for cron)."
330-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--dry-run" "Preview backup changes (no snapshot)."
331-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--test" "Validate config, permissions, connectivity."
332-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--install-scheduler" "Install an automated schedule (systemd/cron)."
333-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--recovery-kit" "Generate a self-contained recovery script (with embedded password)."
334-
printf " ${C_GREEN}%-20s${C_RESET} %s\n" "--uninstall-scheduler" "Remove an automated schedule."
335-
echo
336-
echo -e "${C_BOLD}${C_YELLOW}QUICK EXAMPLES:${C_RESET}"
337-
echo -e " Run a backup now: ${C_GREEN}sudo $PROG_NAME${C_RESET}"
338-
echo -e " Verbose diff summary: ${C_GREEN}sudo $PROG_NAME --verbose --diff${C_RESET}"
339-
echo -e " Fix perms (interactive): ${C_GREEN}sudo $PROG_NAME --fix-permissions --test${C_RESET}"
340-
echo -e " Background restore: ${C_GREEN}sudo $PROG_NAME --background-restore latest /mnt/restore${C_RESET}"
341-
echo -e " List snapshot contents: ${C_GREEN}sudo $PROG_NAME --ls latest /path/to/dir${C_RESET}"
342-
echo -e " Find a file everywhere: ${C_GREEN}sudo $PROG_NAME --find \"*.log\" -l${C_RESET}"
343-
echo -e " Dump one file to stdout: ${C_GREEN}sudo $PROG_NAME --dump latest /etc/hosts > hosts.txt${C_RESET}"
344-
echo
345-
echo -e "${C_BOLD}${C_YELLOW}DEPENDENCIES:${C_RESET}"
346-
echo -e " This script requires: ${C_GREEN}restic, curl, gpg, bzip2, less, jq, flock${C_RESET}"
347-
echo
348-
echo -e "Config: ${C_DIM}${CONFIG_FILE}${C_RESET} Log: ${C_DIM}${LOG_FILE:-"(not set)"}${C_RESET}"
349-
echo
350-
echo -e "For full details, see the online documentation: \e]8;;${readme_url}\a${C_CYAN}README.md${C_RESET}\e]8;;\a"
351-
echo -e "${C_YELLOW}Note:${C_RESET} For restic official documentation See: https://restic.readthedocs.io/"
352-
echo
353-
}
354-
355370
detect_distro() {
356371
if [ -f /etc/os-release ]; then
357372
# shellcheck source=/dev/null
@@ -808,6 +823,9 @@ cleanup() {
808823
if [ -n "${LOCK_FD:-}" ]; then
809824
flock -u "$LOCK_FD"
810825
fi
826+
if [ -n "$(jobs -p)" ]; then
827+
pkill -P $$ || true
828+
fi
811829
}
812830

813831
run_preflight_checks() {
@@ -931,6 +949,18 @@ run_preflight_checks() {
931949
else
932950
if [[ "$verbosity" == "verbose" ]]; then echo -e "[${C_GREEN} OK ${C_RESET}]"; fi
933951
fi
952+
# Disk Space Check for restic cache
953+
local check_dir="${RESTIC_CACHE_DIR:-/tmp}"
954+
mkdir -p "$check_dir" 2>/dev/null || true
955+
if command -v df >/dev/null && command -v awk >/dev/null; then
956+
if [[ "$verbosity" == "verbose" ]]; then printf " %-65s" "Free space check ($check_dir)..."; fi
957+
local available_kb
958+
available_kb=$(df -k --output=avail "$check_dir" | tail -n1)
959+
if [[ "$available_kb" -lt 512000 ]]; then
960+
handle_failure "Insufficient free space in $check_dir. Need >500MB, found $((available_kb/1024))MB." "16"
961+
fi
962+
if [[ "$verbosity" == "verbose" ]]; then echo -e "[${C_GREEN} OK ${C_RESET}]"; fi
963+
fi
934964
# Backup Sources
935965
if [[ "$mode" == "backup" || "$mode" == "diff" ]]; then
936966
if [[ "$verbosity" == "verbose" ]]; then echo -e "\n ${C_DIM}- Checking Backup Sources${C_RESET}"; fi
@@ -1881,9 +1911,6 @@ case "${1:-}" in
18811911
run_preflight_checks "unlock" "quiet"
18821912
run_unlock
18831913
;;
1884-
--help | -h)
1885-
display_help
1886-
;;
18871914
*)
18881915
if [ -n "${1:-}" ]; then
18891916
echo -e "${C_RED}Error: Unknown command '$1'${C_RESET}\n" >&2

restic-backup.sh.sha256

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
13de8cc2e5874e419ddc4a10f80ab56b76c5d4a6ba28af3b05fe79e6cf51173e restic-backup.sh
1+
a7206371dd37d9803bd1df98083332c55f570da8bc6be3ac8a7e7538356d6430 restic-backup.sh

0 commit comments

Comments
 (0)