1
2
3
4
5
6
7
8
9
10
11 package mgr
12
13 import (
14 "syscall"
15 "time"
16 "unicode/utf16"
17 "unsafe"
18
19 "golang.org/x/sys/windows"
20 )
21
22
23 type Mgr struct {
24 Handle windows.Handle
25 }
26
27
28 func Connect() (*Mgr, error) {
29 return ConnectRemote("")
30 }
31
32
33
34 func ConnectRemote(host string) (*Mgr, error) {
35 var s *uint16
36 if host != "" {
37 s = syscall.StringToUTF16Ptr(host)
38 }
39 h, err := windows.OpenSCManager(s, nil, windows.SC_MANAGER_ALL_ACCESS)
40 if err != nil {
41 return nil, err
42 }
43 return &Mgr{Handle: h}, nil
44 }
45
46
47 func (m *Mgr) Disconnect() error {
48 return windows.CloseServiceHandle(m.Handle)
49 }
50
51 type LockStatus struct {
52 IsLocked bool
53 Age time.Duration
54 Owner string
55 }
56
57
58
59
60 func (m *Mgr) LockStatus() (*LockStatus, error) {
61 bytesNeeded := uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{}) + 1024)
62 for {
63 bytes := make([]byte, bytesNeeded)
64 lockStatus := (*windows.QUERY_SERVICE_LOCK_STATUS)(unsafe.Pointer(&bytes[0]))
65 err := windows.QueryServiceLockStatus(m.Handle, lockStatus, uint32(len(bytes)), &bytesNeeded)
66 if err == windows.ERROR_INSUFFICIENT_BUFFER && bytesNeeded >= uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{})) {
67 continue
68 }
69 if err != nil {
70 return nil, err
71 }
72 status := &LockStatus{
73 IsLocked: lockStatus.IsLocked != 0,
74 Age: time.Duration(lockStatus.LockDuration) * time.Second,
75 Owner: windows.UTF16PtrToString(lockStatus.LockOwner),
76 }
77 return status, nil
78 }
79 }
80
81 func toPtr(s string) *uint16 {
82 if len(s) == 0 {
83 return nil
84 }
85 return syscall.StringToUTF16Ptr(s)
86 }
87
88
89
90 func toStringBlock(ss []string) *uint16 {
91 if len(ss) == 0 {
92 return nil
93 }
94 t := ""
95 for _, s := range ss {
96 if s != "" {
97 t += s + "\x00"
98 }
99 }
100 if t == "" {
101 return nil
102 }
103 t += "\x00"
104 return &utf16.Encode([]rune(t))[0]
105 }
106
107
108
109
110
111
112
113
114 func (m *Mgr) CreateService(name, exepath string, c Config, args ...string) (*Service, error) {
115 if c.StartType == 0 {
116 c.StartType = StartManual
117 }
118 if c.ServiceType == 0 {
119 c.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
120 }
121 s := syscall.EscapeArg(exepath)
122 for _, v := range args {
123 s += " " + syscall.EscapeArg(v)
124 }
125 h, err := windows.CreateService(m.Handle, toPtr(name), toPtr(c.DisplayName),
126 windows.SERVICE_ALL_ACCESS, c.ServiceType,
127 c.StartType, c.ErrorControl, toPtr(s), toPtr(c.LoadOrderGroup),
128 nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), toPtr(c.Password))
129 if err != nil {
130 return nil, err
131 }
132 if c.SidType != windows.SERVICE_SID_TYPE_NONE {
133 err = updateSidType(h, c.SidType)
134 if err != nil {
135 windows.DeleteService(h)
136 windows.CloseServiceHandle(h)
137 return nil, err
138 }
139 }
140 if c.Description != "" {
141 err = updateDescription(h, c.Description)
142 if err != nil {
143 windows.DeleteService(h)
144 windows.CloseServiceHandle(h)
145 return nil, err
146 }
147 }
148 if c.DelayedAutoStart {
149 err = updateStartUp(h, c.DelayedAutoStart)
150 if err != nil {
151 windows.DeleteService(h)
152 windows.CloseServiceHandle(h)
153 return nil, err
154 }
155 }
156 return &Service{Name: name, Handle: h}, nil
157 }
158
159
160
161 func (m *Mgr) OpenService(name string) (*Service, error) {
162 h, err := windows.OpenService(m.Handle, syscall.StringToUTF16Ptr(name), windows.SERVICE_ALL_ACCESS)
163 if err != nil {
164 return nil, err
165 }
166 return &Service{Name: name, Handle: h}, nil
167 }
168
169
170
171
172
173
174 func (m *Mgr) ListServices() ([]string, error) {
175 var err error
176 var bytesNeeded, servicesReturned uint32
177 var buf []byte
178 for {
179 var p *byte
180 if len(buf) > 0 {
181 p = &buf[0]
182 }
183 err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
184 windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
185 p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
186 if err == nil {
187 break
188 }
189 if err != syscall.ERROR_MORE_DATA {
190 return nil, err
191 }
192 if bytesNeeded <= uint32(len(buf)) {
193 return nil, err
194 }
195 buf = make([]byte, bytesNeeded)
196 }
197 if servicesReturned == 0 {
198 return nil, nil
199 }
200 services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0])), int(servicesReturned))
201
202 var names []string
203 for _, s := range services {
204 name := windows.UTF16PtrToString(s.ServiceName)
205 names = append(names, name)
206 }
207 return names, nil
208 }
209
View as plain text