1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/* 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, err := ServiceNameOfTunnel(name)
if err != nil {
continue
}
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()
// This hasStopped ugliness is because Windows 7 will send a STOP_PENDING after it has already sent a STOPPED
hasStopped := false
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
hasStopped = true
} else if notifier.notificationTriggered&serviceNotify_STOP_PENDING != 0 && hasStopped {
state = TunnelStopping
} else if notifier.notificationTriggered&serviceNotify_RUNNING != 0 {
state = TunnelStarted
hasStopped = false
} else if notifier.notificationTriggered&serviceNotify_START_PENDING != 0 {
state = TunnelStarting
hasStopped = false
}
IPCServerNotifyTunnelChange(tunnelName, state)
if state == TunnelDeleting {
return
}
}
}
|