# atocore-backup-pull.ps1 # # Pull the latest AtoCore backup snapshot from Dalidou to this Windows machine. # Designed to be run by Windows Task Scheduler. Fail-open by design -- if # Dalidou is unreachable (laptop on the road, etc.), exit cleanly without error. # # Usage (manual test): # powershell.exe -ExecutionPolicy Bypass -File atocore-backup-pull.ps1 # # Scheduled task: see docs/windows-backup-setup.md for Task Scheduler config. $ErrorActionPreference = "Continue" # --- Configuration --- $Remote = "papa@dalidou" $RemoteSnapshots = "/srv/storage/atocore/backups/snapshots" $LocalBackupDir = "$env:USERPROFILE\Documents\ATOCore_Backups" $LogDir = "$LocalBackupDir\_logs" $ReachabilityTest = 5 # seconds timeout for SSH probe # --- Setup --- if (-not (Test-Path $LocalBackupDir)) { New-Item -ItemType Directory -Path $LocalBackupDir -Force | Out-Null } if (-not (Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null } $Timestamp = Get-Date -Format "yyyy-MM-dd_HHmmss" $LogFile = "$LogDir\backup-$Timestamp.log" function Log($msg) { $line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $msg Write-Host $line Add-Content -Path $LogFile -Value $line } Log "=== AtoCore backup pull starting ===" Log "Remote: $Remote" Log "Local target: $LocalBackupDir" # --- Reachability check: fail open if Dalidou is offline --- Log "Checking Dalidou reachability..." $probe = & ssh -o ConnectTimeout=$ReachabilityTest -o BatchMode=yes ` -o StrictHostKeyChecking=accept-new ` $Remote "echo ok" 2>&1 if ($LASTEXITCODE -ne 0 -or $probe -ne "ok") { Log "Dalidou unreachable ($probe) -- fail-open exit" exit 0 } Log "Dalidou reachable." # --- Pull the entire snapshots directory --- # Dalidou's retention policy (7 daily + 4 weekly + 6 monthly) already caps # the snapshot count, so pulling the whole dir is bounded and simple. scp # will overwrite local files -- we rely on this to pick up new snapshots. Log "Pulling snapshots via scp..." $LocalSnapshotsDir = Join-Path $LocalBackupDir "snapshots" if (-not (Test-Path $LocalSnapshotsDir)) { New-Item -ItemType Directory -Path $LocalSnapshotsDir -Force | Out-Null } & scp -o BatchMode=yes -r "${Remote}:${RemoteSnapshots}/*" "$LocalSnapshotsDir\" 2>&1 | ForEach-Object { Add-Content -Path $LogFile -Value $_ } if ($LASTEXITCODE -ne 0) { Log "scp failed with exit $LASTEXITCODE" exit 0 # fail-open } # --- Stats --- $snapshots = Get-ChildItem -Path $LocalSnapshotsDir -Directory | Where-Object { $_.Name -match "^\d{8}T\d{6}Z$" } | Sort-Object Name -Descending $totalSize = (Get-ChildItem $LocalSnapshotsDir -Recurse -File | Measure-Object -Property Length -Sum).Sum $SizeMB = [math]::Round($totalSize / 1MB, 2) $latest = if ($snapshots.Count -gt 0) { $snapshots[0].Name } else { "(none)" } Log ("Pulled {0} snapshots successfully (total {1} MB, latest: {2})" -f $snapshots.Count, $SizeMB, $latest) Log "=== backup complete ===" # --- Log retention: keep last 30 log files --- Get-ChildItem -Path $LogDir -Filter "backup-*.log" | Sort-Object Name -Descending | Select-Object -Skip 30 | ForEach-Object { Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue }