Files
sessiongurad/cmd/agent/platform_windows.go
jbergner 7972ed7e38
All checks were successful
release-tag / release-image (push) Successful in 2m5s
release-main / release-images (push) Successful in 5m5s
Update mit Guacamole-Extension
2026-08-22 15:19:17 +02:00

178 lines
4.1 KiB
Go

//go:build windows
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"github.com/example/sessionguard/internal/agent"
"github.com/example/sessionguard/internal/config"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)
const serviceName = "SessionGuardAgent"
func defaultConfigPath() string { return `C:\ProgramData\SessionGuard\agent.json` }
func platformMain(action, path string) error {
switch action {
case "install":
return installService(path)
case "uninstall":
return uninstallService()
case "start":
return startService()
case "stop":
return stopService()
case "run":
return runAsService(path)
case "":
isSvc, err := svc.IsWindowsService()
if err == nil && isSvc {
return runAsService(path)
}
return runConsole(path)
default:
return fmt.Errorf("unknown service action %q", action)
}
}
func setupLog(dataDir string) {
_ = os.MkdirAll(dataDir, 0o700)
f, err := os.OpenFile(filepath.Join(dataDir, "agent.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600)
if err == nil {
log.SetOutput(f)
}
log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.LUTC)
}
func loadApp(path string) (*agent.App, error) {
cfg, err := config.LoadAgent(path)
if err != nil {
return nil, err
}
setupLog(cfg.DataDir)
return agent.New(cfg)
}
func runConsole(path string) error {
app, err := loadApp(path)
if err != nil {
return err
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
return app.Run(ctx)
}
type serviceHandler struct{ path string }
func (h *serviceHandler) Execute(args []string, requests <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {
const accepts = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptSessionChange
status <- svc.Status{State: svc.StartPending}
app, err := loadApp(h.path)
if err != nil {
return false, 1
}
ctx, cancel := context.WithCancel(context.Background())
done := make(chan error, 1)
go func() { done <- app.Run(ctx) }()
status <- svc.Status{State: svc.Running, Accepts: accepts}
for {
select {
case c := <-requests:
switch c.Cmd {
case svc.Interrogate:
status <- c.CurrentStatus
case svc.SessionChange:
// Windows sends SERVICE_CONTROL_SESSIONCHANGE for logon, logoff, connect,
// disconnect, lock and unlock. Wake the agent worker without doing file or
// network I/O inside the Service Control Manager callback.
app.NotifySessionChange()
case svc.Stop, svc.Shutdown:
status <- svc.Status{State: svc.StopPending}
cancel()
select {
case <-done:
case <-time.After(10 * time.Second):
}
return false, 0
}
case err := <-done:
if err != nil {
log.Printf("service stopped: %v", err)
return false, 1
}
return false, 0
}
}
}
func runAsService(path string) error { return svc.Run(serviceName, &serviceHandler{path: path}) }
func installService(path string) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
if s, err := m.OpenService(serviceName); err == nil {
s.Close()
return fmt.Errorf("service already exists")
}
exe, err := os.Executable()
if err != nil {
return err
}
s, err := m.CreateService(serviceName, exe, mgr.Config{DisplayName: "SessionGuard Agent", Description: "SessionGuard Terminal Server Agent", StartType: mgr.StartAutomatic}, "-config", path, "-service", "run")
if err != nil {
return err
}
defer s.Close()
return nil
}
func uninstallService() error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(serviceName)
if err != nil {
return err
}
defer s.Close()
return s.Delete()
}
func startService() error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(serviceName)
if err != nil {
return err
}
defer s.Close()
return s.Start()
}
func stopService() error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(serviceName)
if err != nil {
return err
}
defer s.Close()
_, err = s.Control(svc.Stop)
return err
}