Harden Windows service supervision
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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."
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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%
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user