diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-03-02 05:54:25 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-03-02 07:07:05 +0100 |
commit | f1b9349dadce2920187ea7def7397b514d801b38 (patch) | |
tree | 3e15404826ea98d13b938417cc7fcd145b9d7f42 /service | |
parent | syntax: flat border (diff) | |
download | wireguard-windows-f1b9349dadce2920187ea7def7397b514d801b38.tar.xz wireguard-windows-f1b9349dadce2920187ea7def7397b514d801b38.zip |
ipc: work out service state transitions
Diffstat (limited to 'service')
-rw-r--r-- | service/errors.go | 2 | ||||
-rw-r--r-- | service/install.go | 7 | ||||
-rw-r--r-- | service/ipc_client.go | 46 | ||||
-rw-r--r-- | service/ipc_server.go | 72 |
4 files changed, 88 insertions, 39 deletions
diff --git a/service/errors.go b/service/errors.go index e2d17ab9..133617ea 100644 --- a/service/errors.go +++ b/service/errors.go @@ -18,4 +18,6 @@ const ( ERROR_NETWORK_BUSY uint32 = 0x00000036 ERROR_NO_TRACKING_SERVICE uint32 = 0x00000494 ERROR_OBJECT_ALREADY_EXISTS uint32 = 0x00001392 + ERROR_SERVICE_DOES_NOT_EXIST uint32 = 0x00000424 + ERROR_SERVICE_MARKED_FOR_DELETE uint32 = 0x00000430 ) diff --git a/service/install.go b/service/install.go index 9a57504f..95a419de 100644 --- a/service/install.go +++ b/service/install.go @@ -13,6 +13,7 @@ import ( "golang.org/x/sys/windows/svc/mgr" "golang.zx2c4.com/wireguard/windows/conf" "os" + "syscall" "time" ) @@ -141,11 +142,11 @@ func InstallTunnel(configPath string) error { } for { service, err = m.OpenService(serviceName) - if err != nil { + if err != nil && err != syscall.Errno(ERROR_SERVICE_MARKED_FOR_DELETE) { break } service.Close() - time.Sleep(time.Second) + time.Sleep(time.Second / 3) } } @@ -177,7 +178,7 @@ func UninstallTunnel(name string) error { service.Control(svc.Stop) err = service.Delete() err2 := service.Close() - if err != nil { + if err != nil && err != syscall.Errno(ERROR_SERVICE_MARKED_FOR_DELETE) { return err } return err2 diff --git a/service/ipc_client.go b/service/ipc_client.go index f2ae2b22..6834e10e 100644 --- a/service/ipc_client.go +++ b/service/ipc_client.go @@ -36,17 +36,17 @@ const ( var rpcClient *rpc.Client -type tunnelChangeCallback struct { - cb func(tunnel string, state TunnelState) +type TunnelChangeCallback struct { + cb func(tunnel *Tunnel, state TunnelState) } -var tunnelChangeCallbacks = make(map[*tunnelChangeCallback]bool) +var tunnelChangeCallbacks = make(map[*TunnelChangeCallback]bool) -type tunnelsChangeCallback struct { +type TunnelsChangeCallback struct { cb func() } -var tunnelsChangeCallbacks = make(map[*tunnelsChangeCallback]bool) +var tunnelsChangeCallbacks = make(map[*TunnelsChangeCallback]bool) func InitializeIPCClient(reader *os.File, writer *os.File, events *os.File) { rpcClient = rpc.NewClient(&pipeRWC{reader, writer}) @@ -70,8 +70,9 @@ func InitializeIPCClient(reader *os.File, writer *os.File, events *os.File) { if err != nil || state == TunnelUnknown { continue } + t := &Tunnel{tunnel} for cb := range tunnelChangeCallbacks { - cb.cb(tunnel, state) + cb.cb(t, state) } case TunnelsChangeNotificationType: for cb := range tunnelsChangeCallbacks { @@ -92,19 +93,20 @@ func (t *Tunnel) RuntimeConfig() (c conf.Config, err error) { return } -func (t *Tunnel) Start() (TunnelState, error) { - var state TunnelState - return state, rpcClient.Call("ManagerService.Start", t.Name, &state) +func (t *Tunnel) Start() error { + return rpcClient.Call("ManagerService.Start", t.Name, nil) } -func (t *Tunnel) Stop() (TunnelState, error) { - var state TunnelState - return state, rpcClient.Call("ManagerService.Stop", t.Name, &state) +func (t *Tunnel) Stop() error { + return rpcClient.Call("ManagerService.Stop", t.Name, nil) } -func (t *Tunnel) Delete() (TunnelState, error) { - var state TunnelState - return state, rpcClient.Call("ManagerService.Delete", t.Name, &state) +func (t *Tunnel) WaitForStop() error { + return rpcClient.Call("ManagerService.WaitForStop", t.Name, nil) +} + +func (t *Tunnel) Delete() error { + return rpcClient.Call("ManagerService.Delete", t.Name, nil) } func (t *Tunnel) State() (TunnelState, error) { @@ -119,7 +121,7 @@ func IPCClientNewTunnel(conf *conf.Config) (Tunnel, error) { func IPCClientTunnels() ([]Tunnel, error) { var tunnels []Tunnel - return tunnels, rpcClient.Call("ManagerService.Tunnels", 0, &tunnels) + return tunnels, rpcClient.Call("ManagerService.Tunnels", uintptr(0), &tunnels) } func IPCClientQuit(stopTunnelsOnQuit bool) (bool, error) { @@ -127,19 +129,19 @@ func IPCClientQuit(stopTunnelsOnQuit bool) (bool, error) { return alreadyQuit, rpcClient.Call("ManagerService.Quit", stopTunnelsOnQuit, &alreadyQuit) } -func IPCClientRegisterTunnelChange(cb func(tunnel string, state TunnelState)) *tunnelChangeCallback { - s := &tunnelChangeCallback{cb} +func IPCClientRegisterTunnelChange(cb func(tunnel *Tunnel, state TunnelState)) *TunnelChangeCallback { + s := &TunnelChangeCallback{cb} tunnelChangeCallbacks[s] = true return s } -func IPCClientUnregisterTunnelChange(cb *tunnelChangeCallback) { +func IPCClientUnregisterTunnelChange(cb *TunnelChangeCallback) { delete(tunnelChangeCallbacks, cb) } -func IPCClientRegisterTunnelsChange(cb func()) *tunnelsChangeCallback { - s := &tunnelsChangeCallback{cb} +func IPCClientRegisterTunnelsChange(cb func()) *TunnelsChangeCallback { + s := &TunnelsChangeCallback{cb} tunnelsChangeCallbacks[s] = true return s } -func IPCClientUnregisterTunnelsChange(cb *tunnelsChangeCallback) { +func IPCClientUnregisterTunnelsChange(cb *TunnelsChangeCallback) { delete(tunnelsChangeCallbacks, cb) } diff --git a/service/ipc_server.go b/service/ipc_server.go index 3e4c7fd3..c79748db 100644 --- a/service/ipc_server.go +++ b/service/ipc_server.go @@ -8,12 +8,13 @@ package service import ( "bytes" "encoding/gob" - "errors" + "golang.org/x/sys/windows/svc" "golang.zx2c4.com/wireguard/windows/conf" "net/rpc" "os" "sync" "sync/atomic" + "syscall" "time" ) @@ -41,7 +42,7 @@ func (s *ManagerService) RuntimeConfig(tunnelName string, config *conf.Config) e return nil } -func (s *ManagerService) Start(tunnelName string, state *TunnelState) error { +func (s *ManagerService) Start(tunnelName string, unused *uintptr) error { c, err := conf.LoadFromName(tunnelName) if err != nil { return err @@ -51,30 +52,73 @@ func (s *ManagerService) Start(tunnelName string, state *TunnelState) error { return err } return InstallTunnel(path) - //TODO: write out *state } -func (s *ManagerService) Stop(tunnelName string, state *TunnelState) error { - return UninstallTunnel(tunnelName) - //TODO: This function should do nothing if the tunnel is already stopped - //TODO: write out *state +func (s *ManagerService) Stop(tunnelName string, unused *uintptr) error { + err := UninstallTunnel(tunnelName) + if err == syscall.Errno(ERROR_SERVICE_DOES_NOT_EXIST) { + _, notExistsError := conf.LoadFromName(tunnelName) + if notExistsError == nil { + return nil + } + } + return err } -func (s *ManagerService) Delete(tunnelName string, state *TunnelState) error { - err := s.Stop(tunnelName, state) +func (s *ManagerService) WaitForStop(tunnelName string, unused *uintptr) error { + serviceName := "WireGuard Tunnel: " + tunnelName + m, err := serviceManager() if err != nil { return err } - //TODO: wait for stopped somehow - if *state != TunnelStopped { - return errors.New("Unable to stop tunnel before deleting") + for { + service, err := m.OpenService(serviceName) + if err == nil || err == syscall.Errno(ERROR_SERVICE_MARKED_FOR_DELETE) { + service.Close() + time.Sleep(time.Second / 3) + } else { + return nil + } + } +} + +func (s *ManagerService) Delete(tunnelName string, unused *uintptr) error { + err := s.Stop(tunnelName, nil) + if err != nil { + return err } return conf.DeleteName(tunnelName) } func (s *ManagerService) State(tunnelName string, state *TunnelState) error { - //TODO - + serviceName := "WireGuard Tunnel: " + tunnelName + m, err := serviceManager() + if err != nil { + return err + } + service, err := m.OpenService(serviceName) + if err != nil { + *state = TunnelStopped + return nil + } + defer service.Close() + status, err := service.Query() + if err != nil { + *state = TunnelUnknown + return err + } + switch status.State { + case svc.Stopped: + *state = TunnelStopped + case svc.StopPending: + *state = TunnelStopping + case svc.Running: + *state = TunnelStarted + case svc.StartPending: + *state = TunnelStarting + default: + *state = TunnelUnknown + } return nil } |