From abb090fdb929d2cd372fe2adcc8113b993a306fc Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 29 Apr 2019 13:30:28 +0200 Subject: service: improve state transitions --- service/install.go | 6 +++--- service/ipc_server.go | 27 ++++++++++++++++++++++++--- service/service_tunnel.go | 11 ++++++----- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/service/install.go b/service/install.go index 95c42b2f..e20658e9 100644 --- a/service/install.go +++ b/service/install.go @@ -129,17 +129,17 @@ func InstallTunnel(configPath string) error { service, err := m.OpenService(serviceName) if err == nil { status, err := service.Query() - if err != nil { + if err != nil && err != syscall.Errno(serviceMARKED_FOR_DELETE) { service.Close() return err } - if status.State != svc.Stopped { + if status.State != svc.Stopped && err != syscall.Errno(serviceMARKED_FOR_DELETE) { service.Close() return errors.New("Tunnel already installed and running") } err = service.Delete() service.Close() - if err != nil { + if err != nil && err != syscall.Errno(serviceMARKED_FOR_DELETE) { return err } for { diff --git a/service/ipc_server.go b/service/ipc_server.go index 6d576846..4dd36b2d 100644 --- a/service/ipc_server.go +++ b/service/ipc_server.go @@ -8,10 +8,12 @@ package service import ( "bytes" "encoding/gob" + "fmt" "github.com/Microsoft/go-winio" "golang.org/x/sys/windows/svc" "golang.zx2c4.com/wireguard/windows/conf" "io/ioutil" + "log" "net/rpc" "os" "sync" @@ -74,13 +76,32 @@ func (s *ManagerService) Start(tunnelName string, unused *uintptr) error { // For now, enforce only one tunnel at a time. Later we'll remove this silly restriction. trackedTunnelsLock.Lock() tt := make([]string, 0, len(trackedTunnels)) - for t := range trackedTunnels { + var inTransition string + for t, state := range trackedTunnels { tt = append(tt, t) + if len(t) > 0 && (state == TunnelStarting || state == TunnelUnknown) { + inTransition = t + break + } } trackedTunnelsLock.Unlock() - for _, t := range tt { - s.Stop(t, unused) + if len(inTransition) != 0 { + return fmt.Errorf("Please allow the tunnel \"%s\" to finish activating", inTransition) } + go func() { + for _, t := range tt { + s.Stop(t, unused) + } + for _, t := range tt { + var state TunnelState + var unused uintptr + if s.State(t, &state) == nil && (state == TunnelStarted || state == TunnelStarting) { + log.Printf("[%s] Trying again to stop zombie tunnel", t) + s.Stop(t, &unused) + time.Sleep(time.Millisecond * 100) + } + } + }() // After that process is started -- it's somewhat asynchronous -- we install the new one. c, err := conf.LoadFromName(tunnelName) diff --git a/service/service_tunnel.go b/service/service_tunnel.go index dd681a33..12977d26 100644 --- a/service/service_tunnel.go +++ b/service/service_tunnel.go @@ -82,6 +82,12 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, logger.Info.Println("Starting wireguard-go version", device.WireGuardGoVersion) logger.Debug.Println("Debug log enabled") + uapiConf, err := conf.ToUAPI() + if err != nil { + serviceError = ErrorDNSLookup + return + } + wintun, err := tun.CreateTUN(conf.Name) if err != nil { serviceError = ErrorCreateWintun @@ -115,11 +121,6 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, }() logger.Info.Println("UAPI listener started") - uapiConf, err := conf.ToUAPI() - if err != nil { - serviceError = ErrorDNSLookup - return - } ipcErr := dev.IpcSetOperation(bufio.NewReader(strings.NewReader(uapiConf))) if ipcErr != nil { err = ipcErr -- cgit v1.2.3-59-g8ed1b