#!/bin/sh # Full system backup script # Backs up entire system except excluded directories set -e # Config CONFIG_FILE="/etc/backup.conf" LOG_DIR="${HOME}/.local/var/log" LOG_FILE="${LOG_DIR}/backup.log" LOCK_FILE="/tmp/backup-full.lock" # Ensure log directory exists mkdir -p "$LOG_DIR" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE" echo "$*" } error() { printf "${RED}✖${NC} %s\n" "$1" log "ERROR: $1" } info() { printf "${BLUE}ℹ${NC} %s\n" "$1" log "INFO: $1" } success() { printf "${GREEN}✓${NC} %s\n" "$1" log "SUCCESS: $1" } # Load config if [ ! -f "$CONFIG_FILE" ]; then error "Configuration file not found: $CONFIG_FILE" exit 1 fi # shellcheck source=/dev/null . "$CONFIG_FILE" # Create lock file if [ -f "$LOCK_FILE" ]; then error "Backup already running (lock file exists: $LOCK_FILE)" exit 1 fi trap 'rm -f "$LOCK_FILE"' EXIT touch "$LOCK_FILE" # Check cooldown if [ -f "$LAST_BACKUP_FILE" ]; then LAST_BACKUP=$(cat "$LAST_BACKUP_FILE") NOW=$(date +%s) ELAPSED=$((NOW - LAST_BACKUP)) if [ "$ELAPSED" -lt "$BACKUP_COOLDOWN" ]; then REMAINING=$((BACKUP_COOLDOWN - ELAPSED)) error "Backup cooldown active. Wait ${REMAINING}s before next backup" exit 0 fi fi # Start backup log "==========================================" log "Starting FULL SYSTEM BACKUP" log "==========================================" # Check NAS connectivity info "Checking NAS connectivity..." if ! ping -c 1 -W 2 "$NAS_HOST" >/dev/null 2>&1; then error "NAS host $NAS_HOST is not reachable" exit 1 fi if ! ssh -p "$NAS_PORT" -o ConnectTimeout=5 -o BatchMode=yes \ "${NAS_USER}@${NAS_HOST}" "echo test" >/dev/null 2>&1; then error "SSH connection to NAS failed" exit 1 fi success "NAS is reachable" # Create backup directory name with timestamp BACKUP_NAME="full-$(date +%Y%m%d-%H%M%S)" BACKUP_DEST="${NAS_USER}@${NAS_HOST}:${NAS_PATH}/${BACKUP_NAME}" info "Backup destination: ${BACKUP_DEST}" # Build exclude arguments for rsync EXCLUDE_ARGS="" for pattern in "${EXCLUDE_PATTERNS[@]}"; do EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude=$pattern" done # Run rsync backup info "Starting rsync (this may take a while)..." # shellcheck disable=SC2086 if rsync -avz --delete --numeric-ids \ --rsync-path="mkdir -p ${NAS_PATH}/${BACKUP_NAME} && rsync" \ -e "ssh -p ${NAS_PORT}" \ $EXCLUDE_ARGS \ / \ "$BACKUP_DEST" >> "$LOG_FILE" 2>&1; then success "Backup completed successfully!" # Update last backup timestamp LAST_BACKUP_FILE="${HOME}/.local/var/backup/last-backup" mkdir -p "$(dirname "$LAST_BACKUP_FILE")" date +%s > "$LAST_BACKUP_FILE" # Log backup size BACKUP_SIZE=$(ssh -p "$NAS_PORT" "${NAS_USER}@${NAS_HOST}" \ "du -sh '${NAS_PATH}/${BACKUP_NAME}' 2>/dev/null | cut -f1" || echo "unknown") info "Backup size: $BACKUP_SIZE" # Clean up old backups (keep last N) info "Cleaning up old backups (keeping last ${RETENTION_COUNT})..." ssh -p "$NAS_PORT" "${NAS_USER}@${NAS_HOST}" \ "cd '${NAS_PATH}' && ls -t | grep '^full-' | tail -n +$((RETENTION_COUNT + 1)) | xargs -r rm -rf" \ >> "$LOG_FILE" 2>&1 || true else error "Backup failed! Check $LOG_FILE for details" exit 1 fi log "==========================================" log "Backup completed at $(date)" log "=========================================="