sync: auto-sync from HOWARD-HOME at 2026-06-21 12:25:00

Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-06-21 12:25:00
This commit is contained in:
2026-06-21 12:25:45 -07:00
parent ef0398bc6b
commit 1836bfd34d
26 changed files with 197 additions and 36 deletions

View File

@@ -186,6 +186,83 @@ PS
echo "[OK] uploaded $(basename "$remote") ($nch chunk(s)) -> $(echo "$r"|jq -r '.stdout'|tr -d '\r')"
}
# ---------------------------------------------------------------------------
# DETACHED, NO-CAP execution. A scanner must never be killed by an RMM command
# timeout - large drives can scan for hours. So we launch GuruScan as a
# scheduled task with ExecutionTimeLimit=0 (unlimited); the launch command
# returns immediately, the scan runs to completion on its own, and we poll a
# disk done-marker with NO overall cap.
# ---------------------------------------------------------------------------
# gs_launch_detached "<invoke-guruscan-args>" <tag>
gs_launch_detached() {
local gsargs="$1" tag="$2"
local wl="$WORK_DIR/wrapper_$tag.ps1"
cat > "$wl" <<PS
\$ErrorActionPreference='Continue'
\$marker='C:\\GuruScan\\_done_${tag}.txt'
\$log='C:\\GuruScan\\_log_${tag}.txt'
Remove-Item \$marker,\$log -Force -ErrorAction SilentlyContinue
try { & C:\\GuruScan\\Invoke-GuruScan.ps1 ${gsargs} *> \$log; \$rc=\$LASTEXITCODE }
catch { \$rc=-1; \$_ | Out-String | Add-Content \$log }
"DONE rc=\$rc at \$(Get-Date -Format o)" | Set-Content \$marker
PS
upload_file "$wl" "C:\\GuruScan\\_detached_${tag}.ps1" || return 1
local sf="$WORK_DIR/launch_$tag.ps1"
cat > "$sf" <<PS
\$ErrorActionPreference='Stop'
\$tn='GuruScan-${tag}'
try{ Unregister-ScheduledTask -TaskName \$tn -Confirm:\$false -ErrorAction SilentlyContinue }catch{}
\$a=New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\\GuruScan\\_detached_${tag}.ps1'
\$pr=New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
\$st=New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -MultipleInstances IgnoreNew
Register-ScheduledTask -TaskName \$tn -Action \$a -Principal \$pr -Settings \$st -Force | Out-Null
Start-ScheduledTask -TaskName \$tn
Start-Sleep -Seconds 2
Write-Output ('LAUNCHED ' + \$tn + ' state=' + ((Get-ScheduledTask -TaskName \$tn).State))
PS
run_ps "$sf" 90 24 "launch-$tag" || return 1
}
# gs_wait_detached <tag> <label> -- polls for the done-marker; NO cap (24h safety net)
gs_wait_detached() {
local tag="$1" label="$2" i=0 maxi=2880
local sf="$WORK_DIR/poll_$tag.ps1"
cat > "$sf" <<PS
\$ErrorActionPreference='Continue'
\$marker='C:\\GuruScan\\_done_${tag}.txt'
if(Test-Path \$marker){ Write-Output ('MARKER ' + ((Get-Content \$marker -Raw) -replace '\s+',' ').Trim()) }
else {
\$act=@(); foreach(\$n in @('a2cmd','HitmanPro_x64','rkill')){ \$p=Get-Process -Name \$n -ErrorAction SilentlyContinue; if(\$p){ \$act += (\$n+' CPU='+[math]::Round((\$p|Select-Object -First 1).CPU,0)+'s') } }
if(\$act.Count){ Write-Output ('RUNNING ' + (\$act -join ', ')) } else { Write-Output 'RUNNING (between scanners / starting)' }
}
PS
echo "[INFO] waiting for '$label' to finish (NO cap; checking every 30s)..."
while [ $i -lt $maxi ]; do
run_ps "$sf" 30 8 "poll-$tag" >/dev/null 2>&1 || true
local out; out="$(jq -r '.stdout' "$WORK_DIR/last_result.json" 2>/dev/null | tr -d '\r')"
if printf '%s' "$out" | grep -q '^MARKER '; then
echo "[OK] '$label' finished -> $(printf '%s' "$out" | grep '^MARKER ')"
return 0
fi
i=$((i+1))
if [ $((i % 4)) -eq 0 ]; then echo " [$label] $((i/2)) min elapsed: $(printf '%s' "$out" | grep -E '^(RUNNING|MARKER)' | head -1)"; fi
sleep 30
done
echo "[WARN] '$label' exceeded 24h safety net"; return 1
}
# fetch the captured console log of a detached run (contains GURUSCAN_RESULT_JSON)
gs_fetch_detached_log() {
local tag="$1"
local sf="$WORK_DIR/getlog_$tag.ps1"
cat > "$sf" <<PS
\$p='C:\\GuruScan\\_log_${tag}.txt'
if(Test-Path \$p){ Get-Content \$p -Raw } else { Write-Output 'NO-LOG' }
PS
run_ps "$sf" 60 24 "fetchlog-$tag" >/dev/null 2>&1 || true
jq -r '.stdout' "$WORK_DIR/last_result.json" 2>/dev/null
}
# ===========================================================================
phase_prep() {
echo ""; echo "=== PHASE: prep ==="
@@ -283,21 +360,14 @@ PS
# ===========================================================================
phase_scan() {
echo ""; echo "=== PHASE: scan (clean mode, full chain, headless - LONG) ==="
local sf="$WORK_DIR/scan.ps1"
cat > "$sf" <<'PS'
$ErrorActionPreference='Continue'
& C:\GuruScan\Invoke-GuruScan.ps1 -Headless
PS
# Emsisoft timeout_min=120, HitmanPro=60. Give the command 2.5h and poll up to 3h.
run_ps "$sf" 9000 2160 "guruscan-run" || { _logerr "GuruScan run failed" --context "host=$AGENT_HOST"; echo "[ERROR] scan phase failed"; return 1; }
# surface the structured result line
local out; out="$(jq -r '.stdout' "$WORK_DIR/last_result.json" 2>/dev/null)"
echo ""; echo "=== PHASE: scan (clean mode, full chain, headless, DETACHED - NO CAP) ==="
gs_launch_detached "-Headless" "scan" || { _logerr "detached scan launch failed" --context "host=$AGENT_HOST"; echo "[ERROR] launch failed"; return 1; }
gs_wait_detached "scan" "full-scan" || true
local out; out="$(gs_fetch_detached_log "scan")"
echo ""
echo "$out" | grep 'GURUSCAN_RESULT_JSON:' | sed 's/^GURUSCAN_RESULT_JSON://' | jq '.' 2>/dev/null \
|| echo "[WARN] no GURUSCAN_RESULT_JSON line in stdout (see full output above)"
local cmd_id; cmd_id="$(cat "$WORK_DIR/last_cmd_id" 2>/dev/null||echo ?)"
post_alert "[RMM] GuruScan test: scan finished on $AGENT_HOST -> cmd:${cmd_id:0:8}"
printf '%s\n' "$out" | grep 'GURUSCAN_RESULT_JSON:' | sed 's/^.*GURUSCAN_RESULT_JSON://' | jq '.' 2>/dev/null \
|| echo "[WARN] no GURUSCAN_RESULT_JSON in detached log"
post_alert "[RMM] GuruScan test: detached scan finished on $AGENT_HOST"
}
# ===========================================================================
@@ -412,12 +482,10 @@ PS
ve_seed_ps > "$WORK_DIR/ve_seed.ps1"
run_ps "$WORK_DIR/ve_seed.ps1" 60 24 "seed-for-$eng" || { echo "[ERROR] seed failed for $eng"; return 1; }
# 2) run ONLY this engine in clean mode (long; Emsisoft updates+scans C:\)
cat > "$WORK_DIR/ve_run.ps1" <<PS
\$ErrorActionPreference='Continue'
& C:\\GuruScan\\Invoke-GuruScan.ps1 -Scanners $eng -Headless
PS
run_ps "$WORK_DIR/ve_run.ps1" 2700 600 "run-$eng" || echo "[WARN] $eng run reported non-zero"
# 2) run ONLY this engine in clean mode, DETACHED with NO cap (Emsisoft
# updates+scans all of C:\ - can take far longer than any RMM timeout)
gs_launch_detached "-Scanners $eng -Headless" "ve_$eng" || { echo "[ERROR] launch failed for $eng"; return 1; }
gs_wait_detached "ve_$eng" "run-$eng" || true
# 3) check which seeded copies survived + read that run's result json
cat > "$WORK_DIR/ve_check.ps1" <<'PS'