aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/service/tunneltracker.go
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-02-28 07:19:06 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2019-02-28 08:05:02 +0100
commitcabb405c67b9dae13be312198640bb10ce5a07a3 (patch)
tree243e106096b2df0f27074234d6acd56d518fb407 /service/tunneltracker.go
parentmanager: wire up config migrator (diff)
downloadwireguard-windows-cabb405c67b9dae13be312198640bb10ce5a07a3.tar.xz
wireguard-windows-cabb405c67b9dae13be312198640bb10ce5a07a3.zip
service: track tunnel service status
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to '')
-rw-r--r--service/tunneltracker.go117
1 files changed, 117 insertions, 0 deletions
diff --git a/service/tunneltracker.go b/service/tunneltracker.go
new file mode 100644
index 00000000..2545930d
--- /dev/null
+++ b/service/tunneltracker.go
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/svc/mgr"
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "runtime"
+ "unsafe"
+)
+
+//sys notifyServiceStatusChange(service windows.Handle, notifyMask uint32, notifyBuffer uintptr) (err error) [failretval!=0] = advapi32.NotifyServiceStatusChangeW
+//sys sleepEx(milliseconds uint32, alertable bool) (ret uint32, err error) = kernel32.SleepEx
+
+const (
+ serviceNotify_CREATED uint32 = 0x00000080
+ serviceNotify_CONTINUE_PENDING = 0x00000010
+ serviceNotify_DELETE_PENDING = 0x00000200
+ serviceNotify_DELETED = 0x00000100
+ serviceNotify_PAUSE_PENDING = 0x00000020
+ serviceNotify_PAUSED = 0x00000040
+ serviceNotify_RUNNING = 0x00000008
+ serviceNotify_START_PENDING = 0x00000002
+ serviceNotify_STOP_PENDING = 0x00000004
+ serviceNotify_STOPPED = 0x00000001
+)
+const serviceNotify_STATUS_CHANGE uint32 = 2
+const errorServiceMARKED_FOR_DELETE uint32 = 1072
+
+type serviceNotify struct {
+ version uint32
+ notifyCallback uintptr
+ context uintptr
+ notificationStatus uint32
+ serviceType uint32
+ currentState uint32
+ controlsAccepted uint32
+ win32ExitCode uint32
+ serviceSpecificExitCode uint32
+ checkPoint uint32
+ waitHint uint32
+ processId uint32
+ serviceFlags uint32
+ notificationTriggered uint32
+ serviceNames *uint16
+}
+
+func serviceTrackerCallback(notifier *serviceNotify) uintptr {
+ return 0
+}
+
+var serviceTrackerCallbackPtr uintptr
+
+func init() {
+ serviceTrackerCallbackPtr = windows.NewCallback(serviceTrackerCallback)
+}
+
+func trackExistingTunnels() error {
+ m, err := serviceManager()
+ if err != nil {
+ return err
+ }
+ names, err := conf.ListConfigNames()
+ if err != nil {
+ return err
+ }
+ for _, name := range names {
+ serviceName := "WireGuard Tunnel: " + name
+ service, err := m.OpenService(serviceName)
+ if err != nil {
+ continue
+ }
+ go trackTunnelService(name, service)
+ }
+ return nil
+}
+
+func trackTunnelService(tunnelName string, svc *mgr.Service) {
+ runtime.LockOSThread()
+ const serviceNotifications = serviceNotify_RUNNING | serviceNotify_START_PENDING | serviceNotify_STOP_PENDING | serviceNotify_STOPPED | serviceNotify_DELETE_PENDING
+ notifier := &serviceNotify{
+ version: serviceNotify_STATUS_CHANGE,
+ notifyCallback: serviceTrackerCallbackPtr,
+ }
+ defer svc.Close()
+ for {
+ notifier.context = 0
+ err := notifyServiceStatusChange(svc.Handle, serviceNotifications, uintptr(unsafe.Pointer(notifier)))
+ if err != nil {
+ return
+ }
+ sleepEx(windows.INFINITE, true)
+ if notifier.notificationStatus != 0 {
+ return
+ }
+ state := TunnelUnknown
+ if notifier.notificationTriggered&serviceNotify_DELETE_PENDING != 0 {
+ state = TunnelDeleting
+ } else if notifier.notificationTriggered&serviceNotify_STOPPED != 0 {
+ state = TunnelStopped
+ } else if notifier.notificationTriggered&serviceNotify_STOP_PENDING != 0 {
+ state = TunnelStopping
+ } else if notifier.notificationTriggered&serviceNotify_RUNNING != 0 {
+ state = TunnelStarted
+ } else if notifier.notificationTriggered&serviceNotify_START_PENDING != 0 {
+ state = TunnelStarting
+ }
+ IPCServerNotifyTunnelChange(tunnelName, state)
+ if state == TunnelDeleting {
+ return
+ }
+ }
+}