#!/bin/bash
# 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.
ionice -c Idle nice -n 19 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
