4a619f7adc
First Windows parity milestone (DESIGN-WINDOWS.md §13 build order item 1).
- Add a PlatformOps abstraction so command dispatch is OS-neutral:
- linuxPlatformOps keeps today's systemctl/journalctl/bash behavior (deliberately
untagged so shared dispatch tests run on Windows CI)
- windowsPlatformOps maps reboot/shutdown to shutdown.exe, service control to
sc.exe (stop+start for restart), fetch_logs to Get-WinEvent, arbitrary_bash to
powershell -EncodedCommand (byte-exact under arbitrary quoting), and declines
configure_ldap (Windows logon goes through OpenCredential)
- Run theta-agent as a Windows service (x/sys/windows/svc): SYSTEM auto-start,
SCM stop/shutdown handling; CLI install-service/remove-service via svc/mgr
- Add theta-agent-helper (session-0 companion): lock/display_off/logout via
user32/wtsapi32, and staged self-update (wait for service stop, swap the
locked exe, sc start)
- Self-update becomes platform-aware: Linux renames over the running binary;
Windows stages .new and hands the swap to the helper (running exe is locked)
- Platform paths: agent.yml and tray.sock under %ProgramData%\Theta42 (the
service runs as SYSTEM while the tray runs as the user, so the per-user temp
dir no longer works for tray IPC); LDAP byte-pump falls back to TCP loopback
- config: service_name, desktop_helper, public_ip_detect (air-gap: skips
external public-IP lookups in telemetry + home monitor), wireguard block
- cli: platform-aware config path + self-update artifact name + service restart
- tests: dispatch tests pin linuxPlatformOps; 0600 mode assertions gated to
POSIX so the suite is green on Windows
Rebuilds all tracked dist binaries (v2.1.0).
89 lines
1.9 KiB
Go
89 lines
1.9 KiB
Go
//go:build windows
|
|
|
|
package main
|
|
|
|
// Service management CLI (theta-agent install-service / remove-service). The
|
|
// Inno installer also drives this rather than hand-rolling `sc create`.
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"golang.org/x/sys/windows/svc/mgr"
|
|
)
|
|
|
|
func handleServiceCommand(args []string) {
|
|
action := "install"
|
|
if len(args) > 0 {
|
|
action = args[0]
|
|
}
|
|
switch action {
|
|
case "install":
|
|
installService()
|
|
case "remove":
|
|
removeService()
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "usage: theta-agent %s [install|remove]\n", action)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func installService() {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
logFatal("cannot resolve agent path: %v", err)
|
|
}
|
|
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
logFatal("cannot connect to service manager: %v", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService("theta-agent")
|
|
if err == nil {
|
|
s.Close()
|
|
fmt.Println("[+] theta-agent service already exists")
|
|
return
|
|
}
|
|
|
|
cfg := mgr.Config{
|
|
DisplayName: "Theta Agent",
|
|
Description: "Theta42 unified endpoint management agent (telemetry, remote ops, LDAP logon, WireGuard mesh)",
|
|
ServiceStartName: "LocalSystem",
|
|
StartType: mgr.StartAutomatic,
|
|
}
|
|
s, err = m.CreateService("theta-agent", exe, cfg, "is", "auto")
|
|
if err != nil {
|
|
logFatal("cannot create service: %v", err)
|
|
}
|
|
defer s.Close()
|
|
fmt.Printf("[+] Registered theta-agent service (%s)\n", filepath.Base(exe))
|
|
}
|
|
|
|
func removeService() {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
logFatal("cannot connect to service manager: %v", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService("theta-agent")
|
|
if err != nil {
|
|
fmt.Println("[!] theta-agent service not found")
|
|
return
|
|
}
|
|
defer s.Close()
|
|
|
|
if err := s.Delete(); err != nil {
|
|
logFatal("cannot delete service: %v", err)
|
|
}
|
|
fmt.Println("[+] Removed theta-agent service")
|
|
}
|
|
|
|
func logFatal(format string, args ...interface{}) {
|
|
fmt.Fprintf(os.Stderr, "[!] "+format+"\n", args...)
|
|
os.Exit(1)
|
|
}
|