mirror of
https://github.com/fosrl/olm.git
synced 2026-02-08 05:56:41 +00:00
310 lines
6.8 KiB
Go
310 lines
6.8 KiB
Go
//go:build windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"golang.org/x/sys/windows/svc"
|
|
"golang.org/x/sys/windows/svc/debug"
|
|
"golang.org/x/sys/windows/svc/eventlog"
|
|
"golang.org/x/sys/windows/svc/mgr"
|
|
)
|
|
|
|
const (
|
|
serviceName = "OlmWireguardService"
|
|
serviceDisplayName = "Olm WireGuard VPN Service"
|
|
serviceDescription = "Olm WireGuard VPN client service for secure network connectivity"
|
|
)
|
|
|
|
type olmService struct {
|
|
elog debug.Log
|
|
ctx context.Context
|
|
stop context.CancelFunc
|
|
}
|
|
|
|
func (s *olmService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) {
|
|
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
|
changes <- svc.Status{State: svc.StartPending}
|
|
|
|
// Start the main olm functionality
|
|
go s.runOlm()
|
|
|
|
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
|
|
|
for {
|
|
select {
|
|
case c := <-r:
|
|
switch c.Cmd {
|
|
case svc.Interrogate:
|
|
changes <- c.CurrentStatus
|
|
case svc.Stop, svc.Shutdown:
|
|
s.elog.Info(1, "Service stopping")
|
|
changes <- svc.Status{State: svc.StopPending}
|
|
s.stop()
|
|
return false, 0
|
|
default:
|
|
s.elog.Error(1, fmt.Sprintf("Unexpected control request #%d", c))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *olmService) runOlm() {
|
|
// Create a context that can be cancelled when the service stops
|
|
s.ctx, s.stop = context.WithCancel(context.Background())
|
|
|
|
// Run the main olm logic in a separate goroutine
|
|
go func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
s.elog.Error(1, fmt.Sprintf("Olm panic: %v", r))
|
|
}
|
|
}()
|
|
|
|
// Call the main olm function
|
|
runOlmMain(s.ctx)
|
|
}()
|
|
|
|
// Wait for context cancellation
|
|
<-s.ctx.Done()
|
|
s.elog.Info(1, "Olm service context cancelled")
|
|
}
|
|
|
|
func runService(name string, isDebug bool) {
|
|
var err error
|
|
var elog debug.Log
|
|
|
|
if isDebug {
|
|
elog = debug.New(name)
|
|
} else {
|
|
elog, err = eventlog.Open(name)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
defer elog.Close()
|
|
|
|
elog.Info(1, fmt.Sprintf("Starting %s service", name))
|
|
run := svc.Run
|
|
if isDebug {
|
|
run = debug.Run
|
|
}
|
|
|
|
service := &olmService{elog: elog}
|
|
err = run(name, service)
|
|
if err != nil {
|
|
elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err))
|
|
return
|
|
}
|
|
elog.Info(1, fmt.Sprintf("%s service stopped", name))
|
|
}
|
|
|
|
func installService() error {
|
|
exepath, err := os.Executable()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get executable path: %v", err)
|
|
}
|
|
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect to service manager: %v", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService(serviceName)
|
|
if err == nil {
|
|
s.Close()
|
|
return fmt.Errorf("service %s already exists", serviceName)
|
|
}
|
|
|
|
config := mgr.Config{
|
|
ServiceType: 0x10, // SERVICE_WIN32_OWN_PROCESS
|
|
StartType: mgr.StartAutomatic,
|
|
ErrorControl: mgr.ErrorNormal,
|
|
DisplayName: serviceDisplayName,
|
|
Description: serviceDescription,
|
|
BinaryPathName: exepath,
|
|
}
|
|
|
|
s, err = m.CreateService(serviceName, exepath, config)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create service: %v", err)
|
|
}
|
|
defer s.Close()
|
|
|
|
err = eventlog.InstallAsEventCreate(serviceName, eventlog.Error|eventlog.Warning|eventlog.Info)
|
|
if err != nil {
|
|
s.Delete()
|
|
return fmt.Errorf("failed to install event log: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func removeService() error {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect to service manager: %v", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService(serviceName)
|
|
if err != nil {
|
|
return fmt.Errorf("service %s is not installed", serviceName)
|
|
}
|
|
defer s.Close()
|
|
|
|
// Stop the service if it's running
|
|
status, err := s.Query()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query service status: %v", err)
|
|
}
|
|
|
|
if status.State != svc.Stopped {
|
|
_, err = s.Control(svc.Stop)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to stop service: %v", err)
|
|
}
|
|
|
|
// Wait for service to stop
|
|
timeout := time.Now().Add(30 * time.Second)
|
|
for status.State != svc.Stopped {
|
|
if timeout.Before(time.Now()) {
|
|
return fmt.Errorf("timeout waiting for service to stop")
|
|
}
|
|
time.Sleep(300 * time.Millisecond)
|
|
status, err = s.Query()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query service status: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
err = s.Delete()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete service: %v", err)
|
|
}
|
|
|
|
err = eventlog.Remove(serviceName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to remove event log: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func startService() error {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect to service manager: %v", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService(serviceName)
|
|
if err != nil {
|
|
return fmt.Errorf("service %s is not installed", serviceName)
|
|
}
|
|
defer s.Close()
|
|
|
|
err = s.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start service: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func stopService() error {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect to service manager: %v", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService(serviceName)
|
|
if err != nil {
|
|
return fmt.Errorf("service %s is not installed", serviceName)
|
|
}
|
|
defer s.Close()
|
|
|
|
status, err := s.Control(svc.Stop)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to stop service: %v", err)
|
|
}
|
|
|
|
timeout := time.Now().Add(30 * time.Second)
|
|
for status.State != svc.Stopped {
|
|
if timeout.Before(time.Now()) {
|
|
return fmt.Errorf("timeout waiting for service to stop")
|
|
}
|
|
time.Sleep(300 * time.Millisecond)
|
|
status, err = s.Query()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query service status: %v", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getServiceStatus() (string, error) {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to connect to service manager: %v", err)
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService(serviceName)
|
|
if err != nil {
|
|
return "Not Installed", nil
|
|
}
|
|
defer s.Close()
|
|
|
|
status, err := s.Query()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to query service status: %v", err)
|
|
}
|
|
|
|
switch status.State {
|
|
case svc.Stopped:
|
|
return "Stopped", nil
|
|
case svc.StartPending:
|
|
return "Starting", nil
|
|
case svc.StopPending:
|
|
return "Stopping", nil
|
|
case svc.Running:
|
|
return "Running", nil
|
|
case svc.ContinuePending:
|
|
return "Continue Pending", nil
|
|
case svc.PausePending:
|
|
return "Pause Pending", nil
|
|
case svc.Paused:
|
|
return "Paused", nil
|
|
default:
|
|
return "Unknown", nil
|
|
}
|
|
}
|
|
|
|
func isWindowsService() bool {
|
|
interactive, err := svc.IsWindowsService()
|
|
return err == nil && interactive
|
|
}
|
|
|
|
func setupWindowsEventLog() {
|
|
// Create log directory if it doesn't exist
|
|
logDir := filepath.Join(os.Getenv("PROGRAMDATA"), "Olm", "logs")
|
|
os.MkdirAll(logDir, 0755)
|
|
|
|
logFile := filepath.Join(logDir, "olm.log")
|
|
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
if err == nil {
|
|
log.SetOutput(file)
|
|
}
|
|
}
|