#!/bin/bash # # Script Name: AppBackup-restic.sh # Description: # Creates a backup of a web application and optionally its MySQL database. # The script archives the application home directory and uploads the backup # either to a remote SFTP server or to a Backblaze B2 repository using Restic, # depending on the host on which it is executed. # # Usage: # AppBackup-restic.sh --appname --appuser [--apppassword ] # # Author: Charlie Silverman # Version: 1.7 # # Revision History: # 1.0 - Initial version. # 1.1 - Added logging to file. # 1.2 - Added strict error handling, input validation, and FQDN support. # 1.3 - Added trap-based error logging and improved operational visibility. # 1.4 - Added application-specific log files, interrupt handling, # and enhanced logging. # 1.5 - Added command-line help. # 1.6 - Replaced positional parameters with named command-line options # and added support for short/long option formats. # 1.7 - Added email delivery of current execution log on success, # failure, or interruption. # set -o errexit set -o nounset set -o pipefail #------------------------------------------------------------------------------ # Help Function #------------------------------------------------------------------------------ show_help() { cat << EOF AppBackup-restic.sh v1.7 DESCRIPTION Creates a backup of a web application's files and optionally its MySQL database. The backup is uploaded either via SFTP or Restic, depending on the host. USAGE AppBackup-restic.sh --appname --appuser [--apppassword ] OPTIONS -a, --appname Name of the application and MySQL database. Required. -u, --appuser Linux account that owns the application files. Required. -p, --apppassword MySQL password and SFTP password (if applicable). Optional. -h, --help Display this help and exit. EXAMPLES AppBackup-restic.sh \ --appname KISNetManager \ --appuser kisuser \ --apppassword MySecurePassword AppBackup-restic.sh \ -a CustomerPortal \ -u portaluser LOG FILES Historical Log: /tmp/AppBackup-.log Current Run Log: /tmp/AppBackup--YYYYMMDD_HHMMSS.log EMAIL NOTIFICATIONS Current execution logs are emailed to: ackitsme3@gmail.com Success, failure, and interruption events each generate a separate email. AUTHOR Charlie Silverman VERSION 1.7 EOF } #------------------------------------------------------------------------------ # Process Command Line Arguments #------------------------------------------------------------------------------ AppName="" AppUser="" AppPassword="" while [[ $# -gt 0 ]]; do case "$1" in -a|--appname) AppName="$2" shift 2 ;; --appname=*) AppName="${1#*=}" shift ;; -u|--appuser) AppUser="$2" shift 2 ;; --appuser=*) AppUser="${1#*=}" shift ;; -p|--apppassword) AppPassword="$2" shift 2 ;; --apppassword=*) AppPassword="${1#*=}" shift ;; -h|--help) show_help exit 0 ;; *) echo "ERROR: Unknown option '$1'" echo show_help exit 1 ;; esac done #------------------------------------------------------------------------------ # Validate Required Parameters #------------------------------------------------------------------------------ if [[ -z "$AppName" || -z "$AppUser" ]]; then echo "ERROR: --appname and --appuser are required." echo show_help exit 1 fi #------------------------------------------------------------------------------ # Runtime Variables #------------------------------------------------------------------------------ DATE=$(date "+%Y%m%d_%H%M%S") HOSTNAME_FQDN="$(hostname -f)" SAFE_APPNAME=$(echo "$AppName" | tr -cs '[:alnum:]_-' '_') LOGFILE="/tmp/AppBackup-${SAFE_APPNAME}.log" RUNLOG="/tmp/AppBackup-${SAFE_APPNAME}-${DATE}.log" touch "$RUNLOG" #------------------------------------------------------------------------------ # Logging Function #------------------------------------------------------------------------------ log() { local msg msg="$(date '+%Y-%m-%d %H:%M:%S') [$$] $1" echo "$msg" | tee -a "$RUNLOG" } #------------------------------------------------------------------------------ # Email Function #------------------------------------------------------------------------------ send_email_log() { local subject="$1" if command -v mailx >/dev/null 2>&1; then mailx -s "$subject" ackitsme3@gmail.com < "$RUNLOG" elif command -v mail >/dev/null 2>&1; then mail -s "$subject" ackitsme3@gmail.com < "$RUNLOG" else log "WARNING: mailx/mail command not found. Email not sent." fi } #------------------------------------------------------------------------------ # Error Handling #------------------------------------------------------------------------------ error_handler() { local exit_code=$? local line_number=$1 log "ERROR: Backup failed on line ${line_number} with exit code ${exit_code}" send_email_log \ "FAILED: AppBackup ${AppName} (${HOSTNAME_FQDN})" exit "${exit_code}" } interrupt_handler() { log "ERROR: Backup interrupted by signal" send_email_log \ "INTERRUPTED: AppBackup ${AppName} (${HOSTNAME_FQDN})" exit 1 } trap 'error_handler $LINENO' ERR trap interrupt_handler SIGINT SIGTERM #------------------------------------------------------------------------------ # Startup Logging #------------------------------------------------------------------------------ log "=================================================================" log "Starting backup job" log "Application: $AppName" log "Application User: $AppUser" log "Host: $HOSTNAME_FQDN" log "Historical Log: $LOGFILE" log "Run Log: $RUNLOG" log "=================================================================" #------------------------------------------------------------------------------ # Restic Repository Configuration #------------------------------------------------------------------------------ export RESTIC_REPOSITORY="s3:https://s3.us-west-004.backblazeb2.com/ackitsbucket/docker" export RESTIC_PASSWORD_FILE="/root/.restic-password" #------------------------------------------------------------------------------ # Host Specific Configuration #------------------------------------------------------------------------------ HostPrefix="" if [[ "$HOSTNAME_FQDN" == "ackitserver.ackcentuate.com" ]]; then if [[ "$AppName" == "KISNetManager" ]]; then HostPrefix="dev-" fi fi ArchiveFile="${AppName}-${DATE}.tar.bz2" #------------------------------------------------------------------------------ # Prepare Backup Workspace #------------------------------------------------------------------------------ log "Changing to backup directory" cd /home/Backups #------------------------------------------------------------------------------ # Database Backup #------------------------------------------------------------------------------ if [[ -n "$AppPassword" ]]; then log "Creating MySQL database backup" mkdir -p "/home/${AppUser}/SQLBackup" mysqldump \ -u "$AppUser" \ --password="$AppPassword" \ "$AppName" \ > "/home/${AppUser}/SQLBackup/${AppName}.sqldump" log "MySQL dump completed successfully" else log "Database password not supplied. Skipping MySQL backup." fi #------------------------------------------------------------------------------ # Remove Previous Backup Archives #------------------------------------------------------------------------------ log "Removing previous backup archives" rm -f /home/Backups/"${AppName}"*.tar.bz* 2>/dev/null || true rm -f /home/Backups/"${HostPrefix}${AppName}"*.tar.bz* 2>/dev/null || true #------------------------------------------------------------------------------ # Create Archive #------------------------------------------------------------------------------ log "Creating archive ${HostPrefix}${ArchiveFile}" tar cjf \ "/home/Backups/${HostPrefix}${ArchiveFile}" \ "/home/${AppUser}/" log "Archive created successfully" #------------------------------------------------------------------------------ # Upload Backup #------------------------------------------------------------------------------ if [[ "$HOSTNAME_FQDN" != "ackitserver.ackcentuate.com" ]]; then if [[ -z "$AppPassword" ]]; then log "ERROR: SFTP upload requires --apppassword" exit 1 fi log "Uploading archive via SFTP" lftp "sftp://${AppUser}:${AppPassword}@webbuilder.ackcentuate.com" -e \ "cd /home/Backups; put /home/Backups/${HostPrefix}${ArchiveFile}; bye" log "SFTP upload completed successfully" else log "Loading Restic environment" source /root/.restic-env log "Starting Restic backup" restic backup "/home/${AppUser}/" \ --tag apache \ --tag website \ --tag "$AppName" \ --tag "$HOSTNAME_FQDN" \ >> "$RUNLOG" 2>&1 log "Restic backup completed successfully" fi #------------------------------------------------------------------------------ # Cleanup #------------------------------------------------------------------------------ if [[ -n "$AppPassword" ]]; then log "Removing temporary SQL backup files" rm -rf "/home/${AppUser}/SQLBackup" fi #------------------------------------------------------------------------------ # Completion #------------------------------------------------------------------------------ log "Backup completed successfully" log "Application: $AppName" log "Archive: ${HostPrefix}${ArchiveFile}" log "Host: $HOSTNAME_FQDN" log "=================================================================" #------------------------------------------------------------------------------ # Append Current Run Log To Historical Log #------------------------------------------------------------------------------ cat "$RUNLOG" >> "$LOGFILE" #------------------------------------------------------------------------------ # Email Current Run Log #------------------------------------------------------------------------------ send_email_log \ "SUCCESS: AppBackup ${AppName} (${HOSTNAME_FQDN})" #------------------------------------------------------------------------------ # Cleanup Run Log #------------------------------------------------------------------------------ rm -f "$RUNLOG" exit 0