Files
fleetforge/agents/forge-agent/internal/winsvc/install.go
T

143 lines
3.3 KiB
Go

//go:build windows
package winsvc
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/fleetforge/forge-agent/internal/config"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
"golang.org/x/sys/windows/svc/mgr"
)
func InstallService(serverURL, enrollmentToken, label string) error {
exePath, err := os.Executable()
if err != nil {
return fmt.Errorf("failed to get executable path: %w", err)
}
exePath, err = filepath.EvalSymlinks(exePath)
if err != nil {
return fmt.Errorf("failed to resolve executable path: %w", err)
}
cfg := &config.Config{
ServerURL: serverURL,
EnrollmentToken: enrollmentToken,
Label: label,
HeartbeatSec: 30,
}
if err := config.Save(cfg); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
fmt.Printf("Config saved to: %s\n", config.ConfigPath())
scm, err := mgr.Connect()
if err != nil {
return fmt.Errorf("failed to connect to Service Control Manager: %w", err)
}
defer scm.Disconnect()
existing, err := scm.OpenService(ServiceName)
if err == nil {
existing.Close()
return fmt.Errorf("service already exists: %s", ServiceName)
}
s, err := scm.CreateService(ServiceName, exePath, mgr.Config{
DisplayName: ServiceDisplayName,
Description: ServiceDescription,
StartType: mgr.StartAutomatic,
ErrorControl: mgr.ErrorNormal,
}, "run", "--service")
if err != nil {
return fmt.Errorf("failed to create service: %w", err)
}
defer s.Close()
if err := eventlog.InstallAsEventCreate(ServiceName, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
s.Delete()
return fmt.Errorf("failed to install event log source: %w", err)
}
fmt.Printf("Service '%s' installed successfully.\n", ServiceDisplayName)
fmt.Println("The service will start automatically on system boot.")
if err := s.Start(); err != nil {
fmt.Printf("Warning: Could not start service: %v\n", err)
} else {
waitUntilRunning(s)
fmt.Println("Service started.")
}
return nil
}
func UninstallService() error {
scm, err := mgr.Connect()
if err != nil {
return fmt.Errorf("failed to connect to Service Control Manager: %w", err)
}
defer scm.Disconnect()
s, err := scm.OpenService(ServiceName)
if err != nil {
return fmt.Errorf("service does not exist: %s", ServiceName)
}
defer s.Close()
st, err := s.Control(svc.Stop)
if err == nil {
waitUntilStopped(s, st)
}
if err := eventlog.Remove(ServiceName); err != nil {
fmt.Printf("Warning: Could not remove event log source: %v\n", err)
}
if err := s.Delete(); err != nil {
return fmt.Errorf("failed to delete service: %w", err)
}
fmt.Printf("Service '%s' removed successfully.\n", ServiceDisplayName)
if err := config.Delete(); err != nil && !os.IsNotExist(err) {
fmt.Printf("Warning: Could not remove config file: %v\n", err)
} else {
fmt.Println("Config file removed.")
}
return nil
}
func waitUntilStopped(s *mgr.Service, initialStatus svc.Status) {
state := initialStatus.State
for i := 0; i < 30; i++ {
if state == svc.Stopped {
return
}
time.Sleep(time.Second)
st, err := s.Query()
if err != nil {
return
}
state = st.State
}
}
func waitUntilRunning(s *mgr.Service) {
for i := 0; i < 30; i++ {
st, err := s.Query()
if err != nil {
return
}
if st.State == svc.Running {
return
}
time.Sleep(time.Second)
}
}