golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/manager/install.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package manager 7 8 import ( 9 "errors" 10 "log" 11 "os" 12 "strings" 13 "time" 14 15 "golang.org/x/sys/windows" 16 "golang.org/x/sys/windows/svc" 17 "golang.org/x/sys/windows/svc/mgr" 18 19 "golang.zx2c4.com/wireguard/windows/conf" 20 ) 21 22 var cachedServiceManager *mgr.Mgr 23 24 func serviceManager() (*mgr.Mgr, error) { 25 if cachedServiceManager != nil { 26 return cachedServiceManager, nil 27 } 28 m, err := mgr.Connect() 29 if err != nil { 30 return nil, err 31 } 32 cachedServiceManager = m 33 return cachedServiceManager, nil 34 } 35 36 var ErrManagerAlreadyRunning = errors.New("Manager already installed and running") 37 38 func InstallManager() error { 39 m, err := serviceManager() 40 if err != nil { 41 return err 42 } 43 path, err := os.Executable() 44 if err != nil { 45 return nil 46 } 47 48 // TODO: Do we want to bail if executable isn't being run from the right location? 49 50 serviceName := "WireGuardManager" 51 service, err := m.OpenService(serviceName) 52 if err == nil { 53 status, err := service.Query() 54 if err != nil { 55 service.Close() 56 return err 57 } 58 if status.State != svc.Stopped { 59 service.Close() 60 if status.State == svc.StartPending { 61 // We were *just* started by something else, so return success here, assuming the other program 62 // starting this does the right thing. This can happen when, e.g., the updater relaunches the 63 // manager service and then invokes wireguard.exe to raise the UI. 64 return nil 65 } 66 return ErrManagerAlreadyRunning 67 } 68 err = service.Delete() 69 service.Close() 70 if err != nil { 71 return err 72 } 73 for { 74 service, err = m.OpenService(serviceName) 75 if err != nil { 76 break 77 } 78 service.Close() 79 time.Sleep(time.Second / 3) 80 } 81 } 82 83 config := mgr.Config{ 84 ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, 85 StartType: mgr.StartAutomatic, 86 ErrorControl: mgr.ErrorNormal, 87 DisplayName: "WireGuard Manager", 88 } 89 90 service, err = m.CreateService(serviceName, path, config, "/managerservice") 91 if err != nil { 92 return err 93 } 94 service.Start() 95 return service.Close() 96 } 97 98 func UninstallManager() error { 99 m, err := serviceManager() 100 if err != nil { 101 return err 102 } 103 serviceName := "WireGuardManager" 104 service, err := m.OpenService(serviceName) 105 if err != nil { 106 return err 107 } 108 service.Control(svc.Stop) 109 err = service.Delete() 110 err2 := service.Close() 111 if err != nil { 112 return err 113 } 114 return err2 115 } 116 117 func InstallTunnel(configPath string) error { 118 m, err := serviceManager() 119 if err != nil { 120 return err 121 } 122 path, err := os.Executable() 123 if err != nil { 124 return nil 125 } 126 127 name, err := conf.NameFromPath(configPath) 128 if err != nil { 129 return err 130 } 131 132 serviceName, err := conf.ServiceNameOfTunnel(name) 133 if err != nil { 134 return err 135 } 136 service, err := m.OpenService(serviceName) 137 if err == nil { 138 status, err := service.Query() 139 if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { 140 service.Close() 141 return err 142 } 143 if status.State != svc.Stopped && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { 144 service.Close() 145 return errors.New("Tunnel already installed and running") 146 } 147 err = service.Delete() 148 service.Close() 149 if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { 150 return err 151 } 152 for { 153 service, err = m.OpenService(serviceName) 154 if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { 155 break 156 } 157 service.Close() 158 time.Sleep(time.Second / 3) 159 } 160 } 161 162 config := mgr.Config{ 163 ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, 164 StartType: mgr.StartAutomatic, 165 ErrorControl: mgr.ErrorNormal, 166 Dependencies: []string{"Nsi", "TcpIp"}, 167 DisplayName: "WireGuard Tunnel: " + name, 168 SidType: windows.SERVICE_SID_TYPE_UNRESTRICTED, 169 } 170 service, err = m.CreateService(serviceName, path, config, "/tunnelservice", configPath) 171 if err != nil { 172 return err 173 } 174 175 err = service.Start() 176 go trackTunnelService(name, service) // Pass off reference to handle. 177 return err 178 } 179 180 func UninstallTunnel(name string) error { 181 m, err := serviceManager() 182 if err != nil { 183 return err 184 } 185 serviceName, err := conf.ServiceNameOfTunnel(name) 186 if err != nil { 187 return err 188 } 189 service, err := m.OpenService(serviceName) 190 if err != nil { 191 return err 192 } 193 service.Control(svc.Stop) 194 err = service.Delete() 195 err2 := service.Close() 196 if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE { 197 return err 198 } 199 return err2 200 } 201 202 func changeTunnelServiceConfigFilePath(name, oldPath, newPath string) { 203 var err error 204 defer func() { 205 if err != nil { 206 log.Printf("Unable to change tunnel service command line argument from %#q to %#q: %v", oldPath, newPath, err) 207 } 208 }() 209 m, err := serviceManager() 210 if err != nil { 211 return 212 } 213 serviceName, err := conf.ServiceNameOfTunnel(name) 214 if err != nil { 215 return 216 } 217 service, err := m.OpenService(serviceName) 218 if err == windows.ERROR_SERVICE_DOES_NOT_EXIST { 219 err = nil 220 return 221 } else if err != nil { 222 return 223 } 224 defer service.Close() 225 config, err := service.Config() 226 if err != nil { 227 return 228 } 229 exePath, err := os.Executable() 230 if err != nil { 231 return 232 } 233 args, err := windows.DecomposeCommandLine(config.BinaryPathName) 234 if err != nil || len(args) != 3 || 235 !strings.EqualFold(args[0], exePath) || args[1] != "/tunnelservice" || !strings.EqualFold(args[2], oldPath) { 236 err = nil 237 return 238 } 239 args[2] = newPath 240 config.BinaryPathName = windows.ComposeCommandLine(args) 241 err = service.UpdateConfig(config) 242 }