mirror of
https://github.com/fosrl/olm.git
synced 2026-03-07 03:06:44 +00:00
Working windows service
This commit is contained in:
63
README.md
63
README.md
@@ -86,6 +86,69 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
Make sure to `mv ./olm /usr/local/bin/olm`!
|
Make sure to `mv ./olm /usr/local/bin/olm`!
|
||||||
|
|
||||||
|
## Windows Service
|
||||||
|
|
||||||
|
On Windows, Olm can be installed and run as a Windows service. This allows it to start automatically at boot and run in the background.
|
||||||
|
|
||||||
|
### Service Management Commands
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
# Install the service
|
||||||
|
olm.exe install
|
||||||
|
|
||||||
|
# Start the service
|
||||||
|
olm.exe start
|
||||||
|
|
||||||
|
# Stop the service
|
||||||
|
olm.exe stop
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
olm.exe status
|
||||||
|
|
||||||
|
# Remove the service
|
||||||
|
olm.exe remove
|
||||||
|
|
||||||
|
# Run in debug mode (console output)
|
||||||
|
olm.exe debug
|
||||||
|
|
||||||
|
# Show help
|
||||||
|
olm.exe help
|
||||||
|
```
|
||||||
|
|
||||||
|
**Helper Scripts**: For easier service management, you can use the provided helper scripts:
|
||||||
|
- `olm-service.bat` - Batch script (requires Administrator privileges)
|
||||||
|
- `olm-service.ps1` - PowerShell script with better error handling
|
||||||
|
|
||||||
|
Example using the batch script:
|
||||||
|
```cmd
|
||||||
|
# Run as Administrator
|
||||||
|
olm-service.bat install
|
||||||
|
olm-service.bat start
|
||||||
|
olm-service.bat status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Configuration
|
||||||
|
|
||||||
|
When running as a service, Olm will read configuration from environment variables or you can modify the service to include command-line arguments:
|
||||||
|
|
||||||
|
1. Install the service: `olm.exe install`
|
||||||
|
2. Configure the service with your credentials using Windows Service Manager or by setting system environment variables:
|
||||||
|
- `PANGOLIN_ENDPOINT=https://example.com`
|
||||||
|
- `OLM_ID=your_olm_id`
|
||||||
|
- `OLM_SECRET=your_secret`
|
||||||
|
3. Start the service: `olm.exe start`
|
||||||
|
|
||||||
|
### Service Logs
|
||||||
|
|
||||||
|
When running as a service, logs are written to:
|
||||||
|
- Windows Event Log (Application log, source: "OlmWireguardService")
|
||||||
|
- Log files in: `%PROGRAMDATA%\Olm\logs\olm.log`
|
||||||
|
|
||||||
|
You can view the Windows Event Log using Event Viewer or PowerShell:
|
||||||
|
```powershell
|
||||||
|
Get-EventLog -LogName Application -Source "OlmWireguardService" -Newest 10
|
||||||
|
```
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
### Container
|
### Container
|
||||||
|
|||||||
112
main.go
112
main.go
@@ -1,9 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -24,6 +26,92 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Check if we're running as a Windows service
|
||||||
|
if isWindowsService() {
|
||||||
|
runService("OlmWireguardService", false)
|
||||||
|
fmt.Println("Service started successfully")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle service management commands on Windows
|
||||||
|
// print the args
|
||||||
|
for i, arg := range os.Args {
|
||||||
|
fmt.Printf("Arg %d: %s\n", i, arg)
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "windows" && len(os.Args) > 1 {
|
||||||
|
fmt.Println("Handling Windows service management command:", os.Args[1])
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "install":
|
||||||
|
err := installService()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to install service: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Service installed successfully")
|
||||||
|
return
|
||||||
|
case "remove", "uninstall":
|
||||||
|
err := removeService()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to remove service: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Service removed successfully")
|
||||||
|
return
|
||||||
|
case "start":
|
||||||
|
err := startService()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to start service: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Service started successfully")
|
||||||
|
return
|
||||||
|
case "stop":
|
||||||
|
err := stopService()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to stop service: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Service stopped successfully")
|
||||||
|
return
|
||||||
|
case "status":
|
||||||
|
status, err := getServiceStatus()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to get service status: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("Service status: %s\n", status)
|
||||||
|
return
|
||||||
|
case "debug":
|
||||||
|
runService("OlmWireguardService", true)
|
||||||
|
return
|
||||||
|
case "help", "--help", "-h":
|
||||||
|
fmt.Println("Olm WireGuard VPN Client")
|
||||||
|
fmt.Println("\nWindows Service Management:")
|
||||||
|
fmt.Println(" install Install the service")
|
||||||
|
fmt.Println(" remove Remove the service")
|
||||||
|
fmt.Println(" start Start the service")
|
||||||
|
fmt.Println(" stop Stop the service")
|
||||||
|
fmt.Println(" status Show service status")
|
||||||
|
fmt.Println(" debug Run service in debug mode")
|
||||||
|
fmt.Println("\nFor console mode, run without arguments or with standard flags.")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
fmt.Println("Unknown command:", os.Args[1])
|
||||||
|
fmt.Println("Use 'olm --help' for usage information.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run in console mode
|
||||||
|
runOlmMain(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func runOlmMain(ctx context.Context) {
|
||||||
|
// Setup Windows event logging if on Windows
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
setupWindowsEventLog()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
endpoint string
|
endpoint string
|
||||||
id string
|
id string
|
||||||
@@ -210,7 +298,7 @@ func main() {
|
|||||||
var dev *device.Device
|
var dev *device.Device
|
||||||
var wgData WgData
|
var wgData WgData
|
||||||
var holePunchData HolePunchData
|
var holePunchData HolePunchData
|
||||||
var uapi *os.File
|
var uapiListener net.Listener
|
||||||
var tdev tun.Device
|
var tdev tun.Device
|
||||||
|
|
||||||
sourcePort, err := FindAvailableUDPPort(49152, 65535)
|
sourcePort, err := FindAvailableUDPPort(49152, 65535)
|
||||||
@@ -327,7 +415,7 @@ func main() {
|
|||||||
|
|
||||||
errs := make(chan error)
|
errs := make(chan error)
|
||||||
|
|
||||||
uapi, err := uapiListen(interfaceName, fileUAPI)
|
uapiListener, err = uapiListen(interfaceName, fileUAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to listen on uapi socket: %v", err)
|
logger.Error("Failed to listen on uapi socket: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -335,7 +423,7 @@ func main() {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
conn, err := uapi.Accept()
|
conn, err := uapiListener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs <- err
|
errs <- err
|
||||||
return
|
return
|
||||||
@@ -622,10 +710,16 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer olm.Close()
|
defer olm.Close()
|
||||||
|
|
||||||
// Wait for interrupt signal
|
// Wait for interrupt signal or context cancellation
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-sigCh
|
|
||||||
|
select {
|
||||||
|
case <-sigCh:
|
||||||
|
logger.Info("Received interrupt signal")
|
||||||
|
case <-ctx.Done():
|
||||||
|
logger.Info("Context cancelled")
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-stopHolepunch:
|
case <-stopHolepunch:
|
||||||
@@ -648,6 +742,10 @@ func main() {
|
|||||||
close(stopPing)
|
close(stopPing)
|
||||||
}
|
}
|
||||||
|
|
||||||
uapi.Close()
|
if uapiListener != nil {
|
||||||
dev.Close()
|
uapiListener.Close()
|
||||||
|
}
|
||||||
|
if dev != nil {
|
||||||
|
dev.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
olm-service.bat
Normal file
52
olm-service.bat
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
REM Olm Windows Service Management Script
|
||||||
|
REM This script helps manage the Olm WireGuard service on Windows
|
||||||
|
|
||||||
|
if "%1"=="" goto :help
|
||||||
|
if "%1"=="help" goto :help
|
||||||
|
if "%1"=="/?" goto :help
|
||||||
|
if "%1"=="-h" goto :help
|
||||||
|
if "%1"=="--help" goto :help
|
||||||
|
|
||||||
|
REM Check if running as administrator
|
||||||
|
net session >nul 2>&1
|
||||||
|
if %errorLevel% neq 0 (
|
||||||
|
echo Error: This script must be run as Administrator for service management.
|
||||||
|
echo Right-click and select "Run as administrator"
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Execute the service command
|
||||||
|
olm.exe %*
|
||||||
|
if %errorLevel% neq 0 (
|
||||||
|
echo Command failed with error code %errorLevel%
|
||||||
|
pause
|
||||||
|
exit /b %errorLevel%
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Operation completed successfully.
|
||||||
|
pause
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:help
|
||||||
|
echo Olm WireGuard Service Management
|
||||||
|
echo.
|
||||||
|
echo Usage: %~nx0 [command]
|
||||||
|
echo.
|
||||||
|
echo Commands:
|
||||||
|
echo install Install the Olm service
|
||||||
|
echo remove Remove the Olm service
|
||||||
|
echo start Start the Olm service
|
||||||
|
echo stop Stop the Olm service
|
||||||
|
echo status Show service status
|
||||||
|
echo debug Run in debug mode
|
||||||
|
echo help Show this help
|
||||||
|
echo.
|
||||||
|
echo Note: This script must be run as Administrator for service management.
|
||||||
|
echo Make sure olm.exe is in your PATH or in the same directory.
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
85
olm-service.ps1
Normal file
85
olm-service.ps1
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Olm Windows Service Management Script
|
||||||
|
# This PowerShell script helps manage the Olm WireGuard service on Windows
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Position=0)]
|
||||||
|
[ValidateSet("install", "remove", "uninstall", "start", "stop", "status", "debug", "help")]
|
||||||
|
[string]$Command = "help"
|
||||||
|
)
|
||||||
|
|
||||||
|
function Test-Administrator {
|
||||||
|
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
|
||||||
|
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Show-Help {
|
||||||
|
Write-Host "Olm WireGuard Service Management" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Usage: .\olm-service.ps1 [command]" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Commands:" -ForegroundColor Yellow
|
||||||
|
Write-Host " install Install the Olm service"
|
||||||
|
Write-Host " remove Remove the Olm service"
|
||||||
|
Write-Host " start Start the Olm service"
|
||||||
|
Write-Host " stop Stop the Olm service"
|
||||||
|
Write-Host " status Show service status"
|
||||||
|
Write-Host " debug Run in debug mode"
|
||||||
|
Write-Host " help Show this help"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Note: This script must be run as Administrator for service management." -ForegroundColor Red
|
||||||
|
Write-Host "Make sure olm.exe is in your PATH or in the same directory." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-OlmCommand {
|
||||||
|
param([string]$cmd)
|
||||||
|
|
||||||
|
if (-not (Test-Administrator) -and $cmd -ne "status" -and $cmd -ne "help") {
|
||||||
|
Write-Error "This script must be run as Administrator for service management."
|
||||||
|
Write-Host "Right-click PowerShell and select 'Run as administrator'" -ForegroundColor Yellow
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$olmPath = Get-Command "olm.exe" -ErrorAction SilentlyContinue
|
||||||
|
if (-not $olmPath) {
|
||||||
|
# Try current directory
|
||||||
|
$olmPath = Join-Path $PSScriptRoot "olm.exe"
|
||||||
|
if (-not (Test-Path $olmPath)) {
|
||||||
|
Write-Error "olm.exe not found in PATH or current directory"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$olmPath = $olmPath.Source
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Executing: $olmPath $cmd" -ForegroundColor Cyan
|
||||||
|
$result = & $olmPath $cmd
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host $result -ForegroundColor Green
|
||||||
|
Write-Host "Operation completed successfully." -ForegroundColor Green
|
||||||
|
return $true
|
||||||
|
} else {
|
||||||
|
Write-Error "Command failed with exit code: $LASTEXITCODE"
|
||||||
|
Write-Host $result -ForegroundColor Red
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to execute olm.exe: $($_.Exception.Message)"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
switch ($Command.ToLower()) {
|
||||||
|
"help" {
|
||||||
|
Show-Help
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
$success = Invoke-OlmCommand -cmd $Command
|
||||||
|
if (-not $success) {
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
service_unix.go
Normal file
40
service_unix.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service management functions are not available on non-Windows platforms
|
||||||
|
func installService() error {
|
||||||
|
return fmt.Errorf("service management is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeService() error {
|
||||||
|
return fmt.Errorf("service management is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
func startService() error {
|
||||||
|
return fmt.Errorf("service management is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopService() error {
|
||||||
|
return fmt.Errorf("service management is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceStatus() (string, error) {
|
||||||
|
return "", fmt.Errorf("service management is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isWindowsService() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func runService(name string, isDebug bool) {
|
||||||
|
// No-op on non-Windows platforms
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupWindowsEventLog() {
|
||||||
|
// No-op on non-Windows platforms
|
||||||
|
}
|
||||||
309
service_windows.go
Normal file
309
service_windows.go
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
//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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user