1
2
3 package main
4
5 import (
6 "log"
7 "os"
8 "time"
9 "unsafe"
10
11 "golang.org/x/sys/windows"
12 "golang.org/x/sys/windows/svc"
13 "golang.org/x/sys/windows/svc/debug"
14 "golang.org/x/sys/windows/svc/mgr"
15 )
16
17 const serviceName = "ncproxy"
18
19 var (
20 panicFile *os.File
21 oldStderr windows.Handle
22 )
23
24 type handler struct {
25 fromsvc chan error
26 done chan struct{}
27 }
28
29 type serviceFailureActions struct {
30 ResetPeriod uint32
31 RebootMsg *uint16
32 Command *uint16
33 ActionsCount uint32
34 Actions uintptr
35 }
36
37 type scAction struct {
38 Type uint32
39 Delay uint32
40 }
41
42
43 const (
44 scActionNone = 0
45 scActionRestart = 1
46
47 serviceConfigFailureActions = 2
48 )
49
50 func initPanicFile(path string) error {
51 panicFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
52 if err != nil {
53 return err
54 }
55
56 st, err := panicFile.Stat()
57 if err != nil {
58 return err
59 }
60
61
62
63 if st.Size() > 0 {
64 panicFile.Close()
65 _ = os.Rename(path, path+".old")
66 panicFile, err = os.Create(path)
67 if err != nil {
68 return err
69 }
70 }
71
72
73
74
75 sh := uint32(windows.STD_ERROR_HANDLE)
76 h, err := windows.GetStdHandle(sh)
77 if err != nil {
78 return err
79 }
80 oldStderr = h
81
82 if err := windows.SetStdHandle(sh, windows.Handle(panicFile.Fd())); err != nil {
83 return err
84 }
85
86
87 os.Stderr = os.NewFile(panicFile.Fd(), "/dev/stderr-ncproxy")
88
89
90 log.SetOutput(os.Stderr)
91 return nil
92 }
93
94 func removePanicFile() {
95 if st, err := panicFile.Stat(); err == nil {
96
97 if st.Size() == 0 {
98 sh := uint32(windows.STD_ERROR_HANDLE)
99 _ = windows.SetStdHandle(sh, oldStderr)
100 _ = panicFile.Close()
101 _ = os.Remove(panicFile.Name())
102 }
103 }
104 }
105
106 func registerService() error {
107 p, err := os.Executable()
108 if err != nil {
109 return err
110 }
111 m, err := mgr.Connect()
112 if err != nil {
113 return err
114 }
115 defer func() {
116 _ = m.Disconnect()
117 }()
118
119 c := mgr.Config{
120 ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
121 StartType: mgr.StartAutomatic,
122 ErrorControl: mgr.ErrorNormal,
123 DisplayName: "Ncproxy",
124 Description: "Network configuration proxy",
125 }
126
127
128 args := []string{"--run-service"}
129 for _, a := range os.Args[1:] {
130 if a != "--register-service" {
131 args = append(args, a)
132 }
133 }
134
135 s, err := m.CreateService(serviceName, p, c, args...)
136 if err != nil {
137 return err
138 }
139 defer s.Close()
140
141 t := []scAction{
142 {Type: scActionRestart, Delay: uint32(15 * time.Second / time.Millisecond)},
143 {Type: scActionRestart, Delay: uint32(15 * time.Second / time.Millisecond)},
144 {Type: scActionNone},
145 }
146 lpInfo := serviceFailureActions{ResetPeriod: uint32(24 * time.Hour / time.Second), ActionsCount: uint32(3), Actions: uintptr(unsafe.Pointer(&t[0]))}
147 return windows.ChangeServiceConfig2(s.Handle, serviceConfigFailureActions, (*byte)(unsafe.Pointer(&lpInfo)))
148 }
149
150 func unregisterService() error {
151 m, err := mgr.Connect()
152 if err != nil {
153 return err
154 }
155 defer func() {
156 _ = m.Disconnect()
157 }()
158
159 s, err := m.OpenService(serviceName)
160 if err != nil {
161 return err
162 }
163 defer s.Close()
164
165 return s.Delete()
166 }
167
168
169 func launchService(done chan struct{}) error {
170 h := &handler{
171 fromsvc: make(chan error),
172 done: done,
173 }
174
175 interactive, err := svc.IsAnInteractiveSession()
176 if err != nil {
177 return err
178 }
179
180 go func() {
181 if interactive {
182 err = debug.Run(serviceName, h)
183 } else {
184 err = svc.Run(serviceName, h)
185 }
186 h.fromsvc <- err
187 }()
188
189
190 return <-h.fromsvc
191 }
192
193 func (h *handler) Execute(_ []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) {
194 s <- svc.Status{State: svc.StartPending, Accepts: 0}
195
196 h.fromsvc <- nil
197
198 s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)}
199
200 Loop:
201 for c := range r {
202 switch c.Cmd {
203 case svc.Interrogate:
204 s <- c.CurrentStatus
205 case svc.Stop, svc.Shutdown:
206 s <- svc.Status{State: svc.StopPending, Accepts: 0}
207 break Loop
208 }
209 }
210
211 removePanicFile()
212 close(h.done)
213 return false, 0
214 }
215
View as plain text