forked from yinchengtsinghua/golang-bitcoin-chinese
-
Notifications
You must be signed in to change notification settings - Fork 0
/
service_windows.go
315 lines (266 loc) · 8.23 KB
/
service_windows.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
//此源码被清华学神尹成大魔王专业翻译分析并修改
//尹成QQ77025077
//尹成微信18510341407
//尹成所在QQ群721929980
//尹成邮箱 [email protected]
//尹成毕业于清华大学,微软区块链领域全球最有价值专家
//https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
//版权所有(c)2013-2016 BTCSuite开发者
//此源代码的使用由ISC控制
//可以在许可文件中找到的许可证。
package main
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/btcsuite/winsvc/eventlog"
"github.com/btcsuite/winsvc/mgr"
"github.com/btcsuite/winsvc/svc"
)
const (
//svcname是btcd服务的名称。
svcName = "btcdsvc"
//svcdisplayname是将在Windows中显示的服务名称
//服务列表。不是svcname是使用的“real”名称
//控制服务。这仅用于显示目的。
svcDisplayName = "Btcd Service"
//svcdesc是服务的描述。
svcDesc = "Downloads and stays synchronized with the bitcoin block " +
"chain and provides chain services to applications."
)
//ELOG用于向Windows事件日志发送消息。
var elog *eventlog.Log
//当主服务器
//已启动到Windows事件日志。
func logServiceStartOfDay(srvr *server) {
var message string
message += fmt.Sprintf("Version %s\n", version())
message += fmt.Sprintf("Configuration directory: %s\n", defaultHomeDir)
message += fmt.Sprintf("Configuration file: %s\n", cfg.ConfigFile)
message += fmt.Sprintf("Data directory: %s\n", cfg.DataDir)
elog.Info(1, message)
}
//btcdservice包含处理所有服务的主服务处理程序
//更新并启动BTCDMAIN。
type btcdService struct{}
//execute是winsvc包在接收时调用的主要入口点。
//来自Windows服务控制管理器的信息。它启动了
//长期运行的btcmain(btcd真正的肉)处理服务
//更改请求,并将更改通知服务控制管理器。
func (s *btcdService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) {
//服务启动挂起。
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
changes <- svc.Status{State: svc.StartPending}
//在单独的goroutine中启动btcmain,以便服务可以启动
//迅速地。关闭(以及潜在错误)通过
//多尼琴ServerChan会收到一次主服务器实例的通知
//它是启动的,因此可以优雅地停止。
doneChan := make(chan error)
serverChan := make(chan *server)
go func() {
err := btcdMain(serverChan)
doneChan <- err
}()
//服务现在已启动。
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
var mainServer *server
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
//服务停止挂起。不接受任何
//挂起时有更多命令。
changes <- svc.Status{State: svc.StopPending}
//发出退出主功能的信号。
shutdownRequestChannel <- struct{}{}
default:
elog.Error(1, fmt.Sprintf("Unexpected control "+
"request #%d.", c))
}
case srvr := <-serverChan:
mainServer = srvr
logServiceStartOfDay(mainServer)
case err := <-doneChan:
if err != nil {
elog.Error(1, err.Error())
}
break loop
}
}
//服务现在已停止。
changes <- svc.Status{State: svc.Stopped}
return false, 0
}
//InstallService尝试安装BTCD服务。通常这应该
//由MSI安装程序完成,但此处提供,因为它可能有用
//为了发展。
func installService() error {
//获取当前可执行文件的路径。这是需要的,因为
//args[0]可能会因应用程序的启动方式而有所不同。
//例如,在cmd.exe下,它将仅是应用程序的名称。
//没有路径或扩展,但在mingw下它将是完整的
//包含扩展名的路径。
exePath, err := filepath.Abs(os.Args[0])
if err != nil {
return err
}
if filepath.Ext(exePath) == "" {
exePath += ".exe"
}
//连接到Windows服务管理器。
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
//确保服务不存在。
service, err := serviceManager.OpenService(svcName)
if err == nil {
service.Close()
return fmt.Errorf("service %s already exists", svcName)
}
//安装服务。
service, err = serviceManager.CreateService(svcName, exePath, mgr.Config{
DisplayName: svcDisplayName,
Description: svcDesc,
})
if err != nil {
return err
}
defer service.Close()
//使用标准“标准”窗口支持事件日志中的事件
//eventcreate.exe消息文件。这允许轻松记录自定义
//而不需要创建自己的消息目录。
eventlog.Remove(svcName)
eventsSupported := uint32(eventlog.Error | eventlog.Warning | eventlog.Info)
return eventlog.InstallAsEventCreate(svcName, eventsSupported)
}
//removeService尝试卸载BTCD服务。通常这应该
//由MSI卸载程序完成,但此处提供,因为它可以
//有助于发展。不是故意不删除事件日志条目
//因为它会使任何现有的事件日志消息失效。
func removeService() error {
//连接到Windows服务管理器。
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
//确保服务存在。
service, err := serviceManager.OpenService(svcName)
if err != nil {
return fmt.Errorf("service %s is not installed", svcName)
}
defer service.Close()
//移除服务。
return service.Delete()
}
//StartService尝试启动BTCD服务。
func startService() error {
//连接到Windows服务管理器。
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
service, err := serviceManager.OpenService(svcName)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer service.Close()
err = service.Start(os.Args)
if err != nil {
return fmt.Errorf("could not start service: %v", err)
}
return nil
}
//ControlService允许更改服务状态的命令。它
//同时等待最多10秒,让服务更改为通过的
//状态。
func controlService(c svc.Cmd, to svc.State) error {
//连接到Windows服务管理器。
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
service, err := serviceManager.OpenService(svcName)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer service.Close()
status, err := service.Control(c)
if err != nil {
return fmt.Errorf("could not send control=%d: %v", c, err)
}
//发送控制消息。
timeout := time.Now().Add(10 * time.Second)
for status.State != to {
if timeout.Before(time.Now()) {
return fmt.Errorf("timeout waiting for service to go "+
"to state=%d", to)
}
time.Sleep(300 * time.Millisecond)
status, err = service.Query()
if err != nil {
return fmt.Errorf("could not retrieve service "+
"status: %v", err)
}
}
return nil
}
//performservicecommand尝试运行受支持的服务命令之一
//通过服务命令标志在命令行上提供。适当的
//如果指定的命令无效,则返回错误。
func performServiceCommand(command string) error {
var err error
switch command {
case "install":
err = installService()
case "remove":
err = removeService()
case "start":
err = startService()
case "stop":
err = controlService(svc.Stop, svc.Stopped)
default:
err = fmt.Errorf("invalid service command [%s]", command)
}
return err
}
//ServiceMain检查是否将我们作为服务调用,如果是,则使用
//用于启动长时间运行的服务器的服务控制管理器。旗是
//返回给调用方,以便应用程序可以确定是否退出(当
//作为服务运行)或以正常交互模式启动。
func serviceMain() (bool, error) {
//如果我们以交互方式运行(或不能以交互方式运行),则不作为服务运行
//因错误而确定)。
isInteractive, err := svc.IsAnInteractiveSession()
if err != nil {
return false, err
}
if isInteractive {
return false, nil
}
elog, err = eventlog.Open(svcName)
if err != nil {
return false, err
}
defer elog.Close()
err = svc.Run(svcName, &btcdService{})
if err != nil {
elog.Error(1, fmt.Sprintf("Service start failed: %v", err))
return true, err
}
return true, nil
}
//将特定于Windows的函数设置为实际函数。
func init() {
runServiceCommand = performServiceCommand
winServiceMain = serviceMain
}