aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--service/errors.go2
-rw-r--r--service/install.go7
-rw-r--r--service/ipc_client.go46
-rw-r--r--service/ipc_server.go72
-rw-r--r--ui/ui.go63
5 files changed, 138 insertions, 52 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
}
diff --git a/ui/ui.go b/ui/ui.go
index 30bbb905..f5815ec6 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -84,15 +84,28 @@ func RunUI() {
pb, _ := walk.NewPushButton(mw)
pb.SetText("Start")
var runningTunnel *service.Tunnel
- var lastTunnel *service.Tunnel
pb.Clicked().Attach(func() {
+ restoreState := true
+ pbE := pb.Enabled()
+ seE := se.Enabled()
+ pbT := pb.Text()
+ defer func() {
+ if restoreState {
+ pb.SetEnabled(pbE)
+ se.SetEnabled(seE)
+ pb.SetText(pbT)
+ }
+ }()
+ pb.SetEnabled(false)
+ se.SetEnabled(false)
+ pb.SetText("Requesting..")
if runningTunnel != nil {
- pb.SetEnabled(false)
- _, err := runningTunnel.Stop()
+ err := runningTunnel.Stop()
if err != nil {
walk.MsgBox(mw, "Unable to stop tunnel", err.Error(), walk.MsgBoxIconError)
return
}
+ restoreState = false
runningTunnel = nil
return
}
@@ -106,15 +119,12 @@ func RunUI() {
walk.MsgBox(mw, "Unable to create tunnel", err.Error(), walk.MsgBoxIconError)
return
}
-
- se.SetEnabled(false)
- pb.SetEnabled(false)
- lastTunnel = &tunnel
- _, err = tunnel.Start()
+ err = tunnel.Start()
if err != nil {
walk.MsgBox(mw, "Unable to start tunnel", err.Error(), walk.MsgBoxIconError)
return
}
+ restoreState = false
runningTunnel = &tunnel
})
@@ -136,10 +146,11 @@ func RunUI() {
}
})
- service.IPCClientRegisterTunnelChange(func(tunnel string, state service.TunnelState) {
- if lastTunnel == nil || tunnel != lastTunnel.Name {
+ setServiceState := func(tunnel *service.Tunnel, state service.TunnelState, showNotifications bool) {
+ if tunnel.Name != "test" {
return
}
+ //TODO: also set tray icon to reflect state
switch state {
case service.TunnelStarting:
se.SetEnabled(false)
@@ -151,21 +162,47 @@ func RunUI() {
pb.SetText("Stop")
pb.SetEnabled(true)
tray.SetToolTip("WireGuard: Activated")
+ if showNotifications {
+ //TODO: ShowCustom with right icon
+ tray.ShowInfo("WireGuard Activated", fmt.Sprintf("The %s tunnel has been activated.", tunnel.Name))
+ }
case service.TunnelStopping:
se.SetEnabled(false)
pb.SetText("Stopping...")
pb.SetEnabled(false)
tray.SetToolTip("WireGuard: Deactivating...")
case service.TunnelStopped, service.TunnelDeleting:
+ if runningTunnel != nil {
+ runningTunnel.Delete()
+ runningTunnel = nil
+ }
se.SetEnabled(true)
- lastTunnel.Delete()
- runningTunnel = nil
- lastTunnel = nil
pb.SetText("Start")
pb.SetEnabled(true)
tray.SetToolTip("WireGuard: Deactivated")
+ if showNotifications {
+ //TODO: ShowCustom with right icon
+ tray.ShowInfo("WireGuard Deactivated", fmt.Sprintf("The %s tunnel has been deactivated.", tunnel.Name))
+ }
}
+ }
+ service.IPCClientRegisterTunnelChange(func(tunnel *service.Tunnel, state service.TunnelState) {
+ setServiceState(tunnel, state, true)
})
+ go func() {
+ tunnels, err := service.IPCClientTunnels()
+ if err != nil {
+ return
+ }
+ for _, tunnel := range tunnels {
+ state, err := tunnel.State()
+ if err != nil {
+ continue
+ }
+ runningTunnel = &tunnel
+ setServiceState(&tunnel, state, false)
+ }
+ }()
mw.Run()
}