Tags
#!/bin/sh
# Version: 1.2
# Description: Automates Borg backup process with logging, error handling,
#              and repository initialization if required.

set -e

# =============================================================================
# Configuration
# =============================================================================

# Hostname setup
FULL_HOSTNAME=$(hostname)
SHORT_HOSTNAME=$(hostname -s)

# Passphrase file
BORG_PASSPHRASE_FILE=~/.borg_passphrase

# SSH key file location
SSH_KEY="/root/.ssh/id_ed25519_$SHORT_HOSTNAME"

# Backup server
REPO_SERVER="backup.servus.at"

# Backup repository location
REPO1="$SHORT_HOSTNAME@$REPO_SERVER:/./borg"

# Log file location
LOG="/var/log/borg_backup.log"

# Directories to backup
DIR_LIST_FILE="/root/scripts/backup_dirs.txt"

# Exclude file
EXCLUDE_FILE="/root/scripts/exclude_dirs.txt"

# =============================================================================
# Functions
# =============================================================================

# Function to log messages
log_message() {
  echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" >> "$LOG"
}

# Function to send an email with error message
send_error_mail() {
  ERROR_MSG=$1
  echo "$ERROR_MSG" | mail -s "Borg Backup Error on $SHORT_HOSTNAME" root
  log_message "$ERROR_MSG"
}

# Function to check if the repository exists
check_repo() {
  borg list "$REPO1" --rsh="ssh -i $SSH_KEY" > /dev/null 2>&1
}

# Function to initialize the repository if it does not exist
init_repo() {
  log_message "Repository not found. Initializing new Borg repository: $REPO1"
  if borg init --encryption=repokey "$REPO1" --rsh="ssh -i $SSH_KEY"; then
    log_message "Repository initialized successfully: $REPO1"
  else
    ERROR_MSG="Error initializing repository: $REPO1"
    send_error_mail "$ERROR_MSG"
    exit 1
  fi
}

# Function to perform backup
perform_backup() {
  log_message "Starting backup of directories: $BACKUP_DIRS"
  if borg create --progress --lock-wait 10 --read-special $EXCLUDES "$REPO1::'{hostname}-{now:%Y-%m-%d}'" $BACKUP_DIRS --rsh="ssh -i $SSH_KEY"; then
    log_message "Backup completed successfully."
  else
    ERROR_MSG="Error during backup creation."
    send_error_mail "$ERROR_MSG"
    exit 1
  fi
}

# Function to prune old backups
prune_backups() {
  log_message "Starting pruning of old backups"
  if borg prune -v --list "$REPO1" --keep-daily=7 --keep-weekly=4 --keep-monthly=6 --rsh="ssh -i $SSH_KEY"; then
    log_message "Pruning completed successfully."
  else
    ERROR_MSG="Error during pruning."
    send_error_mail "$ERROR_MSG"
    exit 1
  fi
}

# =============================================================================
# Main Process
# =============================================================================

log_message "Borg backup process started."

# Get the system's hostname and short hostname
if [ "$FULL_HOSTNAME" != "$SHORT_HOSTNAME" ]; then
  log_message "Warning: Full hostname ($FULL_HOSTNAME) differs from short hostname ($SHORT_HOSTNAME)"
fi

# Setup Borg passphrase from a secure file
if [ ! -f "$BORG_PASSPHRASE_FILE" ]; then
  log_message "Passphrase file is empty or not found: $BORG_PASSPHRASE_FILE"
  exit 1
fi

export BORG_PASSPHRASE=$(cat "$BORG_PASSPHRASE_FILE")

# Check if the repository exists
if ! check_repo; then
  init_repo
else
  log_message "Repository found: $REPO1"
fi

# Check if the directories list file exists
if [ ! -f "$DIR_LIST_FILE" ]; then
  ERROR_MSG="Backup directories list file not found: $DIR_LIST_FILE"
  send_error_mail "$ERROR_MSG"
  exit 1
fi

BACKUP_DIRS=$(cat "$DIR_LIST_FILE" | xargs)
log_message "Directories to be backed up: '$BACKUP_DIRS'"

# Check if the exclude file exists, create if not
if [ ! -f "$EXCLUDE_FILE" ]; then
  log_message "Exclude directories list file not found: $EXCLUDE_FILE, creating it."
  touch "$EXCLUDE_FILE"
fi

EXCLUDES=$(awk '{print "--exclude " $0}' "$EXCLUDE_FILE" | xargs)

# Perform backup and prune old backups
perform_backup
prune_backups

log_message "Borg backup process completed."

# Unset sensitive variables
unset BORG_PASSPHRASE