#!/bin/bash # Version: 0.1.1 # Copyright (c) 2023: # Darren 'Tadgy' Austin # Licensed under the terms of the GNU General Public License version 3. # # Perform backup tasks in a generic way. # Base configuration. RSYNC_OPTIONS=( '-a' '-H' '-A' '--timeout=300' '--partial' '--partial-dir=.rsync-tmp' '--delete-delay' '--delay-updates' ) RSYNC_OPTIONS_VERBOSE=( '--verbose' '--stats' '--human-readable' ) RSYNC_LOG="/tmp/${0##*/}-$$-$RANDOM.log" LOCK_FILE="/tmp/${0##*/}-$$-$RANDOM.lock" # Sanity checks. (( $# != 1 )) || [[ -z "$1" ]] || [[ "$1" =~ ^(-h|-help|--help)$ ]] && { printf "%s: %s <%s>\\n" "Usage" "$0" "backup definition" >&2 exit 1 } # Remove temporary files upon exit. trap 'rm -f "$RSYNC_LOG"; rm -f "$LOCK_FILE"' EXIT # Get backup definition specific configuration. case "$1" in 'gv0') BACKUP_MOUNTPOINT="/localdata" BACKUP_DESTDIR="$BACKUP_MOUNTPOINT/backups/gv0" RSYNC_SOURCE="/storage/gv0/" # The / on the end is required. RSYNC_FILTER=() ;; 'mirrors') BACKUP_MOUNTPOINT="/localdata" BACKUP_DESTDIR="$BACKUP_MOUNTPOINT/backups/mirrors" RSYNC_SOURCE="slackware.uk::mirrors/" # The / on the end is required. RSYNC_FILTER=( \ '--include=/absolute/***' \ '--include=/csb/***' \ '--include=/cumulative/***' \ '--include=/freeslack/***' \ '--include=/gfs/***' \ '--include=/microlinux/***' \ '--include=/msb/***' \ '--include=/sarpi/***' \ '--include=/slackonly/***' \ '--include=/slackvirt/***' \ '--include=/slackware/' \ '--include=/slackware/slackware-pre-1.0-beta/***' \ '--include=/slackware/slackware-1.01/***' \ '--include=/slackwarearm/***' \ '--include=/slacky/***' \ '--include=/slaxbmc/***' \ '--include=/slint/***' \ '--include=/sls/***' \ '--include=/studioware/***' \ '--exclude=*' ) RSYNC_OPTIONS+=( '--contimeout=30' ) ;; 'userdir') BACKUP_MOUNTPOINT="/data/home" BACKUP_DESTDIR="$BACKUP_MOUNTPOINT/tadgy/UserDirs/${HOSTNAME%%.*}-$USER" RSYNC_SOURCE="/home/$USER/" # The / on the end is required. RSYNC_FILTER=( \ '--exclude=/.cache/mozilla/firefox/*/cache2/***' \ '--exclude=/.gnupg/private-keys-v1.d/*.key' \ '--exclude=/.gnupg/openpgp-revocs.d/*.rev' \ '--exclude=/.irssi/config' \ '--exclude=/.ssh/id_ed25519' \ '--exclude=/.ssh/id_rsa' ) ERRORS_SOURCE="userdir: ${HOSTNAME%%.*}-$USER" ;; *) printf "%s: %s: %s\\n" "${0##*/}" "$1" "unknown backup definition" >&2 exit 1 ;; esac # Only allow one copy of the script to run at any time. # shellcheck disable=SC2154 if [[ "$FLOCK" != "$0" ]]; then # shellcheck disable=SC2093 exec env FLOCK="$0" flock -E 10 -e -n "$0" "$0" "$@" ERR="$?" if (( ERR == 10 )); then # File is locked, exit now. exit 0 elif (( ERR > 0 )); then printf "%s: %s\\n" "${0##*/}" "flock execution error" >&2 exit 1 fi fi # Source the mail configuration. source /etc/mail.conf "backups" 2>/dev/null || { printf "%s: %s\\n" "${0##*/}" "Failed to source /etc/mail.conf" >&2 exit 1 } # Make sure BACKUP_MOUNTPOINT is a mountpoint. # Note: The / on the end if required for automount mountpoints. mountpoint "$BACKUP_MOUNTPOINT/" >/dev/null 2>&1 || { [[ -x /opt/bin/pushover-client ]] && /opt/bin/pushover-client "backups" -p -1 -m "Backup failure: ${ERRORS_SOURCE:-$RSYNC_SOURCE}" if [[ -n "${EMAIL_TO[*]}" ]]; then mailx "${MAILX_ARGS[@]}" -S "from=$EMAIL_FROM" -s "Backup failure: ${ERRORS_SOURCE:-$RSYNC_SOURCE}" "${EMAIL_TO[@]}" <<-EOF 2>/dev/null || \ printf "%s: %s\\n" "${0##*/}" "mailx command failed" >&2 '$BACKUP_MOUNTPOINT' is not a mountpoint. EOF exit 1 else printf "%s: %s\\n" "${0##*/}" "no recipient configured for mail delivery" >&2 exit 1 fi } # Create the BACKUP_DESTDIR if required. [[ ! -e "$BACKUP_DESTDIR" ]] && { mkdir -p "$BACKUP_DESTDIR" || { [[ -x /opt/bin/pushover-client ]] && /opt/bin/pushover-client "backups" -p -1 -m "Backup failure: ${ERRORS_SOURCE:-$RSYNC_SOURCE}" if [[ -n "${EMAIL_TO[*]}" ]]; then mailx "${MAILX_ARGS[@]}" -S "from=$EMAIL_FROM" -s "Backup failure: ${ERRORS_SOURCE:-$RSYNC_SOURCE}" "${EMAIL_TO[@]}" <<-EOF 2>/dev/null || \ printf "%s: %s\\n" "${0##*/}" "mailx command failed" >&2 Failed to mkdir '$BACKUP_DESTDIR'. EOF exit 1 else printf "%s: %s\\n" "${0##*/}" "no recipient configured for mail delivery" >&2 exit 1 fi } } # Do the backup. rsync "${RSYNC_OPTIONS[@]}" "${RSYNC_OPTIONS_VERBOSE[@]}" "${RSYNC_FILTER[@]}" "$RSYNC_SOURCE" "$BACKUP_DESTDIR" >"$RSYNC_LOG" 2>&1 ERR="$?" # Send a notification and mail a log if there were errors. (( ERR != 0 )) && (( ERR != 10 )) && (( ERR != 24 )) && { [[ -x /opt/bin/pushover-client ]] && /opt/bin/pushover-client "backups" -p -1 -m "Backup failure: ${ERRORS_SOURCE:-$RSYNC_SOURCE}" if [[ -n "${EMAIL_TO[*]}" ]]; then mailx "${MAILX_ARGS[@]}" -S "from=$EMAIL_FROM" -s "Backup failure: ${ERRORS_SOURCE:-$RSYNC_SOURCE}" "${EMAIL_TO[@]}" <<-EOF 2>/dev/null || \ printf "%s: %s\\n" "${0##*/}" "mailx command failed" >&2 Exit code: $ERR Output: $(< "$RSYNC_LOG") EOF exit 1 else printf "%s: %s\\n" "${0##*/}" "no recipient configured for mail delivery" >&2 exit 1 fi } exit 0