#!/usr/bin/env bash ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## restic-backup.sh ## ## ## ## Simple script to help setup and run ## ## periodic Restic backup jobs ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## Information ## NAME="restic-backup.sh" VERSION="0.2.0" AUTHOR="david@socialnerds.org" LICENSE="MIT" DESCRIPTION="A simple script to help setup and run periodic Restic backup jobs." WEBSITE="https://git.socialnerds.org/david/scripts" CHANGELOG=("[2023-10-26][v0.2.0] Complete rewrite" "[2022-01-01][v0.1.0] Initial version") ## ## Configuration ## EXECUTABLE="$(basename $0)" LIBRARIES="lib.sh" DEPENDENCIES="basename dirname readlink tr head chmod curl restic" REQUIRE_ROOT=0 # Files PASSWORD="$HOME/.restic-password" REPOSITORY="$HOME/.restic-repository" HEALTHCHECKS="$HOME/.restic-healthchecks" # Build the Restic command RESTIC_BINARY="$(command -v restic)" RESTIC_OPTIONS="-q" RESTIC_COMMAND="$RESTIC_BINARY --password-file $PASSWORD --repository-file $REPOSITORY $RESTIC_OPTIONS" # How many snapshots should be kept? SNAPSHOTS=32 ## ## Functions ## # Print help information function print_help() { printf "%s\n\n%s\n%b\n\n%s\n %-15s %s\n %-15s %s\n %-15s %s\n %-15s %s\n %-15s %s\n" \ "$DESCRIPTION" "Usage:" "$LIB_BOLD$EXECUTABLE $LIB_CLEAR" "Options:" \ "-c, --checks" "Enable Healthchecks" \ "-h, --help" "Print help screen and exit" \ "-i, --info" "Print script information and exit" \ "-v, --verbose" "More verbose output" \ "-q, --quiet" "No output except errors (overrides -v)" } ## ## Preflight ## # Load BASH libraries SCRIPT_PATH=$(readlink -f "$0") SCRIPT_DIR=$(dirname "$SCRIPT_PATH") for LIBRARY in $LIBRARIES; do if [[ -r "$SCRIPT_DIR/$LIBRARY" ]]; then #echo "Loading library file [$SCRIPT_DIR/$LIBRARY]" source "$SCRIPT_DIR/$LIBRARY" else echo "Error: Cannot load library file [$SCRIPT_DIR/$LIBRARY]" exit 1 fi done # Check for root privileges if ! lib_amiroot && [[ $REQUIRE_ROOT -eq 1 ]]; then lib_print "!You need to have root privileges" exit 1 fi # Check for dependencies MISSING_COMMANDS=$(lib_missing_commands $DEPENDENCIES) if [ -n "$MISSING_COMMANDS" ]; then lib_print "!One or more commands missing [$MISSING_COMMANDS]" lib_print "Try installing them with your package manager" exit 1 fi ## ## Liftoff ## while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in -c|--checks) C=1 ;; -h|--help) H=1 ;; -i|--info) I=1 ;; -v|--verbose) V=1 ;; -q|--quiet) Q=1 ;; *) lib_print "!Unknown option [$1]" lib_print "Try --help or -h for available options" exit 1 ;; esac shift done if [[ "$1" == '--' ]]; then shift fi if [[ $I -eq 1 ]]; then lib_print_info exit 0 elif [[ $H -eq 1 ]]; then print_help exit 0 fi if [[ -z "$1" ]]; then print_help exit 0 else # Generate a new password file if it is missing or empty if [[ ! -s $PASSWORD ]]; then lib_gen_string 32 > $PASSWORD chmod 600 $PASSWORD lib_print "New password file generated since it did not exist or was empty [$PASSWORD]" fi # Generate a new repo file if it is missing or empty if [[ ! -s $REPOSITORY ]]; then echo -n "Repository URL: "; read -r INPUT echo $INPUT > $REPOSITORY chmod 600 $REPOSITORY lib_print "New repository file generated since it did not exist or was empty [$REPOSITORY]" fi # Generate a new healthchecks file if it is missing or empty if [[ ! -s $HEALTHCHECKS ]] && [[ $C -eq 1 ]]; then echo -n "Healthchecks URL: "; read -r INPUT if [[ -n "$INPUT" ]]; then echo "$INPUT" > $HEALTHCHECKS chmod 600 $HEALTHCHECKS lib_print "New healthchecks file generated [$HEALTHCHECKS]" fi fi # Initialize a new repo if the URL has no config file if ! $($RESTIC_COMMAND snapshots >/dev/null 2>&1); then if ! $($RESTIC_COMMAND init >/dev/null 2>&1); then lib_print "!A problem occured while initializing a new repository" exit 1 fi lib_print "New repository initialized" fi # Run the Restic repository check if ! $($RESTIC_COMMAND check >/dev/null 2>&1); then lib_print "!Repository check failed" exit 1 fi if [[ "$1" =~ ^/ && -r "$1" ]]; then # Signal Healthchecks start if enabled if [[ "$C" -eq 1 ]]; then HEALTHCHECKS_URL=$(cat $HEALTHCHECKS) curl -fsS -m 10 --retry 5 -o /dev/null $HEALTHCHECKS_URL/start fi # Run the actual backup if ! $($RESTIC_COMMAND backup --one-file-system ${@:2} "$1" >/dev/null 2>&1); then lib_print "!Something went wrong while running backup [$1]" # Signal Healthchecks fail if enabled if [[ "$C" -eq 1 ]]; then curl -fsS -m 10 --retry 5 -o /dev/null $HEALTHCHECKS_URL/fail >/dev/null 2>&1 fi exit 1 fi # Delete old snapshots if ! $($RESTIC_COMMAND forget --keep-last $SNAPSHOTS --path "$1" --prune >/dev/null 2>&1); then lib_print "!Something went wrong while deleting old snapshots [$1]" # Signal Healthchecks fail if enabled if [[ "$C" -eq 1 ]]; then curl -fsS -m 10 --retry 5 -o /dev/null $HEALTHCHECKS_URL/fail >/dev/null 2>&1 fi exit 1 fi # Signal Healthchecks success if enabled if [[ "$C" -eq 1 ]]; then curl -fsS -m 10 --retry 5 -o /dev/null $HEALTHCHECKS_URL >/dev/null 2>&1 fi elif [[ "$1" =~ ^(backup|cache|cat|check|copy|diff|dump|find|forget|generate|help|init|key|list|ls|migrate|mount|prune|recover|repair|restore|rewrite|snapshots|stats|tag|unlock|version)$ ]]; then # Wrapper mode $RESTIC_COMMAND $@ else lib_print "!The given path must be absolute and readable [$1]" lib_print "Or you can try a Restic keyword directly for wrapper mode" exit 1 fi fi ## ## Here be dragons ## exit 0