fix(startup): make watchdog task keep agent alive
- Use Task Scheduler XML instead of /TR - Run -ensure agent in-process, not child - Add console close and panic protection - Guard nil event channels after stop - Add non-Windows stubs for startup/mutex
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package startup
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const watchdogArg = "-ensure"
|
||||
|
||||
// watchdogTaskXML builds a Task Scheduler 2.0 XML action with Command and
|
||||
// Arguments as separate fields. Paths must not be wrapped in extra quotes —
|
||||
// schtasks /TR quoting is what made the watchdog flash and exit.
|
||||
func watchdogTaskXML(exe string) string {
|
||||
exe = strings.Trim(exe, `"`)
|
||||
var b strings.Builder
|
||||
b.WriteString(`<?xml version="1.0" encoding="UTF-16"?>`)
|
||||
b.WriteByte('\n')
|
||||
b.WriteString(`<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">`)
|
||||
b.WriteString(`<RegistrationInfo><Description>win64_mp watchdog</Description></RegistrationInfo>`)
|
||||
b.WriteString(`<Triggers><TimeTrigger>`)
|
||||
b.WriteString(`<Repetition><Interval>PT5M</Interval><StopAtDurationEnd>false</StopAtDurationEnd></Repetition>`)
|
||||
b.WriteString(`<StartBoundary>2000-01-01T00:00:00</StartBoundary>`)
|
||||
b.WriteString(`<Enabled>true</Enabled>`)
|
||||
b.WriteString(`</TimeTrigger></Triggers>`)
|
||||
b.WriteString(`<Principals><Principal id="Author">`)
|
||||
b.WriteString(`<LogonType>InteractiveToken</LogonType>`)
|
||||
b.WriteString(`<RunLevel>LeastPrivilege</RunLevel>`)
|
||||
b.WriteString(`</Principal></Principals>`)
|
||||
b.WriteString(`<Settings>`)
|
||||
b.WriteString(`<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>`)
|
||||
b.WriteString(`<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>`)
|
||||
b.WriteString(`<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>`)
|
||||
b.WriteString(`<Hidden>true</Hidden>`)
|
||||
b.WriteString(`<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>`)
|
||||
b.WriteString(`<AllowStartOnDemand>true</AllowStartOnDemand>`)
|
||||
b.WriteString(`<Enabled>true</Enabled>`)
|
||||
b.WriteString(`<StartWhenAvailable>true</StartWhenAvailable>`)
|
||||
b.WriteString(`<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>`)
|
||||
b.WriteString(`<Priority>7</Priority>`)
|
||||
b.WriteString(`</Settings>`)
|
||||
b.WriteString(`<Actions Context="Author"><Exec>`)
|
||||
b.WriteString(`<Command>`)
|
||||
b.WriteString(xmlEscape(exe))
|
||||
b.WriteString(`</Command>`)
|
||||
b.WriteString(`<Arguments>`)
|
||||
b.WriteString(watchdogArg)
|
||||
b.WriteString(`</Arguments>`)
|
||||
if dir := winDir(exe); dir != "" {
|
||||
b.WriteString(`<WorkingDirectory>`)
|
||||
b.WriteString(xmlEscape(dir))
|
||||
b.WriteString(`</WorkingDirectory>`)
|
||||
}
|
||||
b.WriteString(`</Exec></Actions></Task>`)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func xmlEscape(s string) string {
|
||||
var b strings.Builder
|
||||
_ = xml.EscapeText(&b, []byte(s))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func winDir(p string) string {
|
||||
p = strings.TrimRight(p, `/\`)
|
||||
i := strings.LastIndexAny(p, `/\`)
|
||||
if i <= 0 {
|
||||
return ""
|
||||
}
|
||||
return p[:i]
|
||||
}
|
||||
Reference in New Issue
Block a user