Harden Windows service supervision

This commit is contained in:
2026-09-07 11:19:45 +08:00
parent 266f29853e
commit 44ce5e655d
9 changed files with 199 additions and 9 deletions
+5 -1
View File
@@ -5,9 +5,11 @@ cd /d "%~dp0"
set "LOG_DIR=%~dp0logs" set "LOG_DIR=%~dp0logs"
set "LOG_FILE=%LOG_DIR%\autostart.log" set "LOG_FILE=%LOG_DIR%\autostart.log"
set "DOCUTRANSLATE_NONINTERACTIVE=1"
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%" if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
:restart
echo.>> "%LOG_FILE%" echo.>> "%LOG_FILE%"
echo [%DATE% %TIME%] Starting DocuTranslate autostart runner...>> "%LOG_FILE%" echo [%DATE% %TIME%] Starting DocuTranslate autostart runner...>> "%LOG_FILE%"
@@ -15,5 +17,7 @@ call "%~dp0run.bat" >> "%LOG_FILE%" 2>&1
set "EXIT_CODE=%ERRORLEVEL%" set "EXIT_CODE=%ERRORLEVEL%"
echo [%DATE% %TIME%] DocuTranslate stopped with exit code %EXIT_CODE%.>> "%LOG_FILE%" echo [%DATE% %TIME%] DocuTranslate stopped with exit code %EXIT_CODE%.>> "%LOG_FILE%"
echo [%DATE% %TIME%] Restarting in 10 seconds...>> "%LOG_FILE%"
exit /b %EXIT_CODE% timeout /t 10 /nobreak >nul
goto restart
+17
View File
@@ -0,0 +1,17 @@
@echo off
setlocal
cd /d "%~dp0"
powershell -NoProfile -Command "if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { exit 0 } else { exit 1 }" >nul 2>&1
if not "%ERRORLEVEL%"=="0" (
set "DOCUTRANSLATE_CHECK_BAT=%~f0"
echo [INFO] Administrator permission is required. Opening the UAC prompt...
powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath $env:DOCUTRANSLATE_CHECK_BAT -Verb RunAs"
exit /b
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0check_autostart.ps1"
echo.
pause
+34
View File
@@ -0,0 +1,34 @@
$ErrorActionPreference = "Continue"
$TaskNames = @("DocuTranslate", "DocuTranslateWatchdog")
foreach ($TaskName in $TaskNames) {
Write-Host ""
Write-Host "===== $TaskName ====="
$Task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($null -eq $Task) {
Write-Host "[ERROR] Task not found. Run setup_autostart.bat first." -ForegroundColor Red
continue
}
$Info = Get-ScheduledTaskInfo -TaskName $TaskName
Write-Host "State: $($Task.State)"
Write-Host "Run as: $($Task.Principal.UserId)"
Write-Host "Last run: $($Info.LastRunTime)"
Write-Host "Last result: $($Info.LastTaskResult)"
Write-Host "Next run: $($Info.NextRunTime)"
Write-Host "Execution limit: $($Task.Settings.ExecutionTimeLimit)"
Write-Host "Action: $($Task.Actions.Execute) $($Task.Actions.Arguments)"
}
Write-Host ""
Write-Host "===== HTTP health ====="
try {
$Response = Invoke-WebRequest -UseBasicParsing -Uri "http://127.0.0.1:8010/service/meta" -TimeoutSec 8
Write-Host "[OK] DocuTranslate responded with HTTP $($Response.StatusCode)." -ForegroundColor Green
} catch {
Write-Host "[ERROR] DocuTranslate did not respond: $($_.Exception.Message)" -ForegroundColor Red
}
Write-Host ""
Write-Host "Result notes: 0 = last run succeeded; 267009 (0x41301) = task is running."
+17
View File
@@ -0,0 +1,17 @@
@echo off
setlocal
cd /d "%~dp0"
powershell -NoProfile -Command "if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { exit 0 } else { exit 1 }" >nul 2>&1
if not "%ERRORLEVEL%"=="0" (
set "DOCUTRANSLATE_DIAGNOSE_BAT=%~f0"
echo [INFO] Administrator permission is required. Opening the UAC prompt...
powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath $env:DOCUTRANSLATE_DIAGNOSE_BAT -Verb RunAs"
exit /b
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0diagnose_service.ps1"
echo.
pause
+89
View File
@@ -0,0 +1,89 @@
$ErrorActionPreference = "Continue"
$ProjectDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$LogDir = Join-Path $ProjectDir "logs"
if (!(Test-Path -LiteralPath $LogDir)) {
New-Item -ItemType Directory -Path $LogDir | Out-Null
}
$ReportPath = Join-Path $LogDir ("diagnose_" + (Get-Date -Format "yyyyMMdd_HHmmss") + ".txt")
$Lines = New-Object System.Collections.Generic.List[string]
function Add-ReportLine([string]$Text = "") {
$Lines.Add($Text)
Write-Host $Text
}
Add-ReportLine "DocuTranslate diagnostic report"
Add-ReportLine ("Generated: " + (Get-Date))
$Os = Get-CimInstance Win32_OperatingSystem
Add-ReportLine ""
Add-ReportLine "===== System uptime ====="
Add-ReportLine ("Last boot: " + $Os.LastBootUpTime)
Add-ReportLine ("Uptime: " + ((Get-Date) - $Os.LastBootUpTime))
Add-ReportLine ""
Add-ReportLine "===== Power and restart events (last 14 days) ====="
$StartTime = (Get-Date).AddDays(-14)
$Events = Get-WinEvent -FilterHashtable @{ LogName = "System"; Id = @(41, 1074, 6005, 6006, 6008); StartTime = $StartTime } -ErrorAction SilentlyContinue |
Sort-Object TimeCreated -Descending |
Select-Object -First 30
if ($Events) {
foreach ($Event in $Events) {
$Message = ($Event.Message -replace "`r?`n", " ")
Add-ReportLine ("{0:yyyy-MM-dd HH:mm:ss} ID={1} Provider={2} Message={3}" -f $Event.TimeCreated, $Event.Id, $Event.ProviderName, $Message)
}
} else {
Add-ReportLine "No matching events found."
}
Add-ReportLine ""
Add-ReportLine "===== Scheduled tasks ====="
foreach ($TaskName in @("DocuTranslate", "DocuTranslateWatchdog")) {
$Task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($null -eq $Task) {
Add-ReportLine ("${TaskName}: NOT FOUND")
continue
}
$Info = Get-ScheduledTaskInfo -TaskName $TaskName
Add-ReportLine ("{0}: State={1}; LastRun={2}; LastResult={3}; NextRun={4}; Limit={5}" -f $TaskName, $Task.State, $Info.LastRunTime, $Info.LastTaskResult, $Info.NextRunTime, $Task.Settings.ExecutionTimeLimit)
}
Add-ReportLine ""
Add-ReportLine "===== Port 8010 ====="
$Connections = Get-NetTCPConnection -LocalPort 8010 -State Listen -ErrorAction SilentlyContinue
if ($Connections) {
foreach ($Connection in $Connections) {
$Process = Get-Process -Id $Connection.OwningProcess -ErrorAction SilentlyContinue
Add-ReportLine ("Listening: {0}:{1}; PID={2}; Process={3}" -f $Connection.LocalAddress, $Connection.LocalPort, $Connection.OwningProcess, $Process.ProcessName)
}
} else {
Add-ReportLine "Nothing is listening on port 8010."
}
Add-ReportLine ""
Add-ReportLine "===== HTTP health ====="
try {
$Response = Invoke-WebRequest -UseBasicParsing -Uri "http://127.0.0.1:8010/service/meta" -TimeoutSec 8
Add-ReportLine ("HTTP status: " + $Response.StatusCode)
} catch {
Add-ReportLine ("HTTP failed: " + $_.Exception.Message)
}
foreach ($Name in @("autostart.log", "watchdog.log")) {
$Path = Join-Path $LogDir $Name
Add-ReportLine ""
Add-ReportLine ("===== Last 80 lines: " + $Name + " =====")
if (Test-Path -LiteralPath $Path) {
foreach ($Line in (Get-Content -LiteralPath $Path -Tail 80)) {
Add-ReportLine $Line
}
} else {
Add-ReportLine "Log file not found."
}
}
$Lines | Out-File -LiteralPath $ReportPath -Encoding utf8
Add-ReportLine ""
Add-ReportLine ("Report saved to: " + $ReportPath)
+2 -2
View File
@@ -7,7 +7,7 @@ cd /d "%~dp0"
REM 检查 .venv 是否存在 REM 检查 .venv 是否存在
if not exist ".venv\Scripts\activate.bat" ( if not exist ".venv\Scripts\activate.bat" (
echo [ERROR] 未找到 .venv\Scripts\activate.bat echo [ERROR] 未找到 .venv\Scripts\activate.bat
pause if not defined DOCUTRANSLATE_NONINTERACTIVE pause
exit /b 1 exit /b 1
) )
@@ -29,7 +29,7 @@ call deactivate >nul 2>nul
if not "%EXIT_CODE%"=="0" ( if not "%EXIT_CODE%"=="0" (
echo. echo.
echo [ERROR] 程序退出,返回码: %EXIT_CODE% echo [ERROR] 程序退出,返回码: %EXIT_CODE%
pause if not defined DOCUTRANSLATE_NONINTERACTIVE pause
) )
exit /b %EXIT_CODE% exit /b %EXIT_CODE%
+11
View File
@@ -3,6 +3,14 @@ setlocal
cd /d "%~dp0" cd /d "%~dp0"
powershell -NoProfile -Command "if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { exit 0 } else { exit 1 }" >nul 2>&1
if not "%ERRORLEVEL%"=="0" (
set "DOCUTRANSLATE_SETUP_BAT=%~f0"
echo [INFO] Administrator permission is required. Opening the UAC prompt...
powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath $env:DOCUTRANSLATE_SETUP_BAT -Verb RunAs"
exit /b
)
set "TASK_NAME=DocuTranslate" set "TASK_NAME=DocuTranslate"
set "WATCHDOG_TASK_NAME=DocuTranslateWatchdog" set "WATCHDOG_TASK_NAME=DocuTranslateWatchdog"
set "PROJECT_DIR=%~dp0" set "PROJECT_DIR=%~dp0"
@@ -55,4 +63,7 @@ echo.
echo You can start it now with: echo You can start it now with:
echo schtasks /Run /TN "%TASK_NAME%" echo schtasks /Run /TN "%TASK_NAME%"
echo. echo.
echo Double-click check_autostart.bat to verify tasks and HTTP health.
echo Double-click diagnose_service.bat to collect restart and failure evidence.
echo.
pause pause
+9 -2
View File
@@ -27,12 +27,16 @@ try {
$StartupSettings = New-ScheduledTaskSettingsSet ` $StartupSettings = New-ScheduledTaskSettingsSet `
-StartWhenAvailable ` -StartWhenAvailable `
-MultipleInstances IgnoreNew ` -MultipleInstances IgnoreNew `
-ExecutionTimeLimit ([TimeSpan]::Zero) `
-DontStopIfGoingOnBatteries `
-RestartCount 3 ` -RestartCount 3 `
-RestartInterval (New-TimeSpan -Minutes 1) -RestartInterval (New-TimeSpan -Minutes 1)
} catch { } catch {
$StartupSettings = New-ScheduledTaskSettingsSet ` $StartupSettings = New-ScheduledTaskSettingsSet `
-StartWhenAvailable ` -StartWhenAvailable `
-MultipleInstances IgnoreNew -MultipleInstances IgnoreNew `
-ExecutionTimeLimit ([TimeSpan]::Zero) `
-DontStopIfGoingOnBatteries
} }
Register-ScheduledTask ` Register-ScheduledTask `
@@ -51,7 +55,9 @@ $WatchdogTrigger = New-ScheduledTaskTrigger `
-RepetitionDuration (New-TimeSpan -Days 3650) -RepetitionDuration (New-TimeSpan -Days 3650)
$WatchdogSettings = New-ScheduledTaskSettingsSet ` $WatchdogSettings = New-ScheduledTaskSettingsSet `
-StartWhenAvailable ` -StartWhenAvailable `
-MultipleInstances IgnoreNew -MultipleInstances IgnoreNew `
-ExecutionTimeLimit (New-TimeSpan -Minutes 2) `
-DontStopIfGoingOnBatteries
Register-ScheduledTask ` Register-ScheduledTask `
-TaskName $WatchdogTaskName ` -TaskName $WatchdogTaskName `
@@ -66,6 +72,7 @@ Write-Host "[OK] Scheduled task `"$WatchdogTaskName`" has been created."
Write-Host "[OK] Startup runner: `"$RunnerBat`"" Write-Host "[OK] Startup runner: `"$RunnerBat`""
Write-Host "[OK] Watchdog: `"$WatchdogBat`"" Write-Host "[OK] Watchdog: `"$WatchdogBat`""
Write-Host "[OK] Log file: `"$LogFile`"" Write-Host "[OK] Log file: `"$LogFile`""
Write-Host "[OK] Startup task execution limit: disabled"
Write-Host "" Write-Host ""
Write-Host "Verify:" Write-Host "Verify:"
Write-Host "schtasks /Query /TN `"$TaskName`" /V /FO LIST" Write-Host "schtasks /Query /TN `"$TaskName`" /V /FO LIST"
+15 -4
View File
@@ -9,9 +9,20 @@ set "LOG_FILE=%LOG_DIR%\watchdog.log"
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%" if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
powershell -NoProfile -ExecutionPolicy Bypass -Command ^ powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$ErrorActionPreference = 'SilentlyContinue';" ^ "$ErrorActionPreference = 'Stop'; try { $r = Invoke-WebRequest -UseBasicParsing -Uri '%HEALTH_URL%' -TimeoutSec 8; if ($r.StatusCode -eq 200) { exit 0 } } catch {}; exit 1"
"$ok = $false;" ^
"try { $r = Invoke-WebRequest -UseBasicParsing -Uri '%HEALTH_URL%' -TimeoutSec 5; $ok = ($r.StatusCode -eq 200) } catch { $ok = $false };" ^ if "%ERRORLEVEL%"=="0" exit /b 0
"if (-not $ok) { Add-Content -Path '%LOG_FILE%' -Value ('[' + (Get-Date) + '] Health check failed, restarting %TASK_NAME%...'); schtasks /End /TN '%TASK_NAME%' | Out-Null; Start-Sleep -Seconds 2; schtasks /Run /TN '%TASK_NAME%' | Out-Null }"
timeout /t 10 /nobreak >nul
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$ErrorActionPreference = 'Stop'; try { $r = Invoke-WebRequest -UseBasicParsing -Uri '%HEALTH_URL%' -TimeoutSec 8; if ($r.StatusCode -eq 200) { exit 0 } } catch {}; exit 1"
if "%ERRORLEVEL%"=="0" exit /b 0
echo [%DATE% %TIME%] Two health checks failed. Restarting %TASK_NAME%...>> "%LOG_FILE%"
schtasks /End /TN "%TASK_NAME%" >> "%LOG_FILE%" 2>&1
timeout /t 2 /nobreak >nul
schtasks /Run /TN "%TASK_NAME%" >> "%LOG_FILE%" 2>&1
exit /b 0 exit /b 0