1
0
mirror of https://git.zx2c4.com/wireguard-windows synced 2024-11-10 16:59:18 +00:00
wireguard-windows/manager/install.go
Jason A. Donenfeld 6ed37f30f5 global: bump date
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-01-06 17:28:13 +01:00

243 lines
5.3 KiB
Go

/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package manager
import (
"errors"
"log"
"os"
"strings"
"time"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
"golang.zx2c4.com/wireguard/windows/conf"
)
var cachedServiceManager *mgr.Mgr
func serviceManager() (*mgr.Mgr, error) {
if cachedServiceManager != nil {
return cachedServiceManager, nil
}
m, err := mgr.Connect()
if err != nil {
return nil, err
}
cachedServiceManager = m
return cachedServiceManager, nil
}
var ErrManagerAlreadyRunning = errors.New("Manager already installed and running")
func InstallManager() error {
m, err := serviceManager()
if err != nil {
return err
}
path, err := os.Executable()
if err != nil {
return nil
}
// TODO: Do we want to bail if executable isn't being run from the right location?
serviceName := "WireGuardManager"
service, err := m.OpenService(serviceName)
if err == nil {
status, err := service.Query()
if err != nil {
service.Close()
return err
}
if status.State != svc.Stopped {
service.Close()
if status.State == svc.StartPending {
// We were *just* started by something else, so return success here, assuming the other program
// starting this does the right thing. This can happen when, e.g., the updater relaunches the
// manager service and then invokes wireguard.exe to raise the UI.
return nil
}
return ErrManagerAlreadyRunning
}
err = service.Delete()
service.Close()
if err != nil {
return err
}
for {
service, err = m.OpenService(serviceName)
if err != nil {
break
}
service.Close()
time.Sleep(time.Second / 3)
}
}
config := mgr.Config{
ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
StartType: mgr.StartAutomatic,
ErrorControl: mgr.ErrorNormal,
DisplayName: "WireGuard Manager",
}
service, err = m.CreateService(serviceName, path, config, "/managerservice")
if err != nil {
return err
}
service.Start()
return service.Close()
}
func UninstallManager() error {
m, err := serviceManager()
if err != nil {
return err
}
serviceName := "WireGuardManager"
service, err := m.OpenService(serviceName)
if err != nil {
return err
}
service.Control(svc.Stop)
err = service.Delete()
err2 := service.Close()
if err != nil {
return err
}
return err2
}
func InstallTunnel(configPath string) error {
m, err := serviceManager()
if err != nil {
return err
}
path, err := os.Executable()
if err != nil {
return nil
}
name, err := conf.NameFromPath(configPath)
if err != nil {
return err
}
serviceName, err := conf.ServiceNameOfTunnel(name)
if err != nil {
return err
}
service, err := m.OpenService(serviceName)
if err == nil {
status, err := service.Query()
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
service.Close()
return err
}
if status.State != svc.Stopped && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
service.Close()
return errors.New("Tunnel already installed and running")
}
err = service.Delete()
service.Close()
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
return err
}
for {
service, err = m.OpenService(serviceName)
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
break
}
service.Close()
time.Sleep(time.Second / 3)
}
}
config := mgr.Config{
ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
StartType: mgr.StartAutomatic,
ErrorControl: mgr.ErrorNormal,
Dependencies: []string{"Nsi", "TcpIp"},
DisplayName: "WireGuard Tunnel: " + name,
SidType: windows.SERVICE_SID_TYPE_UNRESTRICTED,
}
service, err = m.CreateService(serviceName, path, config, "/tunnelservice", configPath)
if err != nil {
return err
}
err = service.Start()
go trackTunnelService(name, service) // Pass off reference to handle.
return err
}
func UninstallTunnel(name string) error {
m, err := serviceManager()
if err != nil {
return err
}
serviceName, err := conf.ServiceNameOfTunnel(name)
if err != nil {
return err
}
service, err := m.OpenService(serviceName)
if err != nil {
return err
}
service.Control(svc.Stop)
err = service.Delete()
err2 := service.Close()
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
return err
}
return err2
}
func changeTunnelServiceConfigFilePath(name, oldPath, newPath string) {
var err error
defer func() {
if err != nil {
log.Printf("Unable to change tunnel service command line argument from %#q to %#q: %v", oldPath, newPath, err)
}
}()
m, err := serviceManager()
if err != nil {
return
}
serviceName, err := conf.ServiceNameOfTunnel(name)
if err != nil {
return
}
service, err := m.OpenService(serviceName)
if err == windows.ERROR_SERVICE_DOES_NOT_EXIST {
err = nil
return
} else if err != nil {
return
}
defer service.Close()
config, err := service.Config()
if err != nil {
return
}
exePath, err := os.Executable()
if err != nil {
return
}
args, err := windows.DecomposeCommandLine(config.BinaryPathName)
if err != nil || len(args) != 3 ||
!strings.EqualFold(args[0], exePath) || args[1] != "/tunnelservice" || !strings.EqualFold(args[2], oldPath) {
err = nil
return
}
args[2] = newPath
config.BinaryPathName = windows.ComposeCommandLine(args)
err = service.UpdateConfig(config)
}