183 lines
4.3 KiB
Bash
183 lines
4.3 KiB
Bash
#!/bin/sh
|
|
# Network-triggered backup daemon
|
|
# Monitors for NAS availability and triggers backups with safeguards
|
|
|
|
set -e
|
|
|
|
# Config
|
|
CONFIG_FILE="/etc/backup.conf"
|
|
LOG_DIR="${HOME}/.local/var/log"
|
|
LOG_FILE="${LOG_DIR}/backup-monitor.log"
|
|
STATE_DIR="${HOME}/.local/var/backup"
|
|
LAST_BACKUP_FILE="${STATE_DIR}/last-backup"
|
|
LAST_CHECK_FILE="${STATE_DIR}/last-check"
|
|
BACKUP_IN_PROGRESS_FILE="${STATE_DIR}/backup-in-progress"
|
|
|
|
# Ensure directories exist
|
|
mkdir -p "$LOG_DIR" "$STATE_DIR"
|
|
|
|
# Logging
|
|
log() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
|
|
}
|
|
|
|
# Load config
|
|
if [ ! -f "$CONFIG_FILE" ]; then
|
|
log "ERROR: Configuration file not found: $CONFIG_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
# shellcheck source=/dev/null
|
|
. "$CONFIG_FILE"
|
|
|
|
# Check if backup is already running
|
|
is_backup_running() {
|
|
# Check if any backup lock files exist
|
|
if ls /tmp/backup-*.lock >/dev/null 2>&1; then
|
|
return 0 # Backup is running
|
|
fi
|
|
|
|
# Check our own in-progress marker
|
|
if [ -f "$BACKUP_IN_PROGRESS_FILE" ]; then
|
|
# Check if marker is stale (older than 6 hours)
|
|
if [ -n "$(find "$BACKUP_IN_PROGRESS_FILE" -mmin +360 2>/dev/null)" ]; then
|
|
log "WARNING: Stale backup-in-progress marker found, removing"
|
|
rm -f "$BACKUP_IN_PROGRESS_FILE"
|
|
return 1 # Not running
|
|
fi
|
|
return 0 # Backup is running
|
|
fi
|
|
|
|
return 1 # Not running
|
|
}
|
|
|
|
# Check if cooldown period has passed
|
|
check_cooldown() {
|
|
if [ ! -f "$LAST_BACKUP_FILE" ]; then
|
|
return 0 # No previous backup, proceed
|
|
fi
|
|
|
|
LAST_BACKUP=$(cat "$LAST_BACKUP_FILE")
|
|
NOW=$(date +%s)
|
|
ELAPSED=$((NOW - LAST_BACKUP))
|
|
|
|
if [ "$ELAPSED" -lt "$BACKUP_COOLDOWN" ]; then
|
|
REMAINING=$((BACKUP_COOLDOWN - ELAPSED))
|
|
log "Cooldown active: ${REMAINING}s remaining until next backup allowed"
|
|
return 1 # Still in cooldown
|
|
fi
|
|
|
|
return 0 # Cooldown passed
|
|
}
|
|
|
|
# Check if we recently checked (prevent spam checks)
|
|
check_rate_limit() {
|
|
CHECK_INTERVAL=300 # 5 minutes between checks
|
|
|
|
if [ ! -f "$LAST_CHECK_FILE" ]; then
|
|
date +%s > "$LAST_CHECK_FILE"
|
|
return 0 # First check, proceed
|
|
fi
|
|
|
|
LAST_CHECK=$(cat "$LAST_CHECK_FILE")
|
|
NOW=$(date +%s)
|
|
ELAPSED=$((NOW - LAST_CHECK))
|
|
|
|
if [ "$ELAPSED" -lt "$CHECK_INTERVAL" ]; then
|
|
return 1 # Too soon since last check
|
|
fi
|
|
|
|
date +%s > "$LAST_CHECK_FILE"
|
|
return 0 # Can check now
|
|
}
|
|
|
|
# Check if NAS is available
|
|
check_nas_available() {
|
|
# Quick ping check
|
|
if ! ping -c 1 -W 2 "$NAS_HOST" >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
# SSH check
|
|
if ! ssh -p "$NAS_PORT" -o ConnectTimeout=5 -o BatchMode=yes \
|
|
"${NAS_USER}@${NAS_HOST}" "echo test" >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Trigger backup
|
|
trigger_backup() {
|
|
BACKUP_TYPE="${1:-incremental}"
|
|
|
|
log "NAS detected, triggering $BACKUP_TYPE backup..."
|
|
|
|
# Mark backup in progress
|
|
touch "$BACKUP_IN_PROGRESS_FILE"
|
|
|
|
# Run backup in background
|
|
(
|
|
if /usr/local/bin/backup-"$BACKUP_TYPE" >> "$LOG_FILE" 2>&1; then
|
|
log "Backup completed successfully"
|
|
else
|
|
log "ERROR: Backup failed"
|
|
fi
|
|
|
|
# Remove in-progress marker
|
|
rm -f "$BACKUP_IN_PROGRESS_FILE"
|
|
) &
|
|
|
|
# Don't wait for backup to complete
|
|
log "Backup started in background (PID: $!)"
|
|
}
|
|
|
|
# Main monitoring loop
|
|
main() {
|
|
log "=========================================="
|
|
log "Backup monitor started"
|
|
log "Monitoring for NAS: ${NAS_HOST}"
|
|
log "Check interval: 5 minutes"
|
|
log "Backup cooldown: ${BACKUP_COOLDOWN}s ($(( BACKUP_COOLDOWN / 3600 ))h)"
|
|
log "=========================================="
|
|
|
|
while true; do
|
|
# Rate limit checks (every 5 minutes)
|
|
if ! check_rate_limit; then
|
|
sleep 60
|
|
continue
|
|
fi
|
|
|
|
# Skip if backup already running
|
|
if is_backup_running; then
|
|
log "Backup already in progress, skipping check"
|
|
sleep 300 # Sleep 5 minutes
|
|
continue
|
|
fi
|
|
|
|
# Check if NAS is available
|
|
if check_nas_available; then
|
|
log "NAS is available at ${NAS_HOST}"
|
|
|
|
# Check cooldown before triggering
|
|
if check_cooldown; then
|
|
log "Cooldown passed, backup allowed"
|
|
trigger_backup "incremental"
|
|
else
|
|
log "Cooldown active, skipping backup"
|
|
fi
|
|
else
|
|
log "NAS not available (offline or not connected)"
|
|
fi
|
|
|
|
# Sleep before next check (5 minutes)
|
|
sleep 300
|
|
done
|
|
}
|
|
|
|
# Handle signals
|
|
trap 'log "Received shutdown signal, exiting..."; exit 0' TERM INT
|
|
|
|
# Start monitoring
|
|
main
|