github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cmd/dockerd/service_windows.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "syscall" 12 13 "github.com/Sirupsen/logrus" 14 "github.com/spf13/pflag" 15 "golang.org/x/sys/windows" 16 "golang.org/x/sys/windows/svc" 17 "golang.org/x/sys/windows/svc/debug" 18 "golang.org/x/sys/windows/svc/eventlog" 19 "golang.org/x/sys/windows/svc/mgr" 20 ) 21 22 var ( 23 flServiceName *string 24 flRegisterService *bool 25 flUnregisterService *bool 26 flRunService *bool 27 28 setStdHandle = syscall.NewLazyDLL("kernel32.dll").NewProc("SetStdHandle") 29 oldStderr syscall.Handle 30 panicFile *os.File 31 32 service *handler 33 ) 34 35 const ( 36 // These should match the values in event_messages.mc. 37 eventInfo = 1 38 eventWarn = 1 39 eventError = 1 40 eventDebug = 2 41 eventPanic = 3 42 eventFatal = 4 43 44 eventExtraOffset = 10 // Add this to any event to get a string that supports extended data 45 ) 46 47 func installServiceFlags(flags *pflag.FlagSet) { 48 flServiceName = flags.String("service-name", "docker", "Set the Windows service name") 49 flRegisterService = flags.Bool("register-service", false, "Register the service and exit") 50 flUnregisterService = flags.Bool("unregister-service", false, "Unregister the service and exit") 51 flRunService = flags.Bool("run-service", false, "") 52 } 53 54 type handler struct { 55 tosvc chan bool 56 fromsvc chan error 57 daemonCli *DaemonCli 58 } 59 60 type etwHook struct { 61 log *eventlog.Log 62 } 63 64 func (h *etwHook) Levels() []logrus.Level { 65 return []logrus.Level{ 66 logrus.PanicLevel, 67 logrus.FatalLevel, 68 logrus.ErrorLevel, 69 logrus.WarnLevel, 70 logrus.InfoLevel, 71 logrus.DebugLevel, 72 } 73 } 74 75 func (h *etwHook) Fire(e *logrus.Entry) error { 76 var ( 77 etype uint16 78 eid uint32 79 ) 80 81 switch e.Level { 82 case logrus.PanicLevel: 83 etype = windows.EVENTLOG_ERROR_TYPE 84 eid = eventPanic 85 case logrus.FatalLevel: 86 etype = windows.EVENTLOG_ERROR_TYPE 87 eid = eventFatal 88 case logrus.ErrorLevel: 89 etype = windows.EVENTLOG_ERROR_TYPE 90 eid = eventError 91 case logrus.WarnLevel: 92 etype = windows.EVENTLOG_WARNING_TYPE 93 eid = eventWarn 94 case logrus.InfoLevel: 95 etype = windows.EVENTLOG_INFORMATION_TYPE 96 eid = eventInfo 97 case logrus.DebugLevel: 98 etype = windows.EVENTLOG_INFORMATION_TYPE 99 eid = eventDebug 100 default: 101 return errors.New("unknown level") 102 } 103 104 // If there is additional data, include it as a second string. 105 exts := "" 106 if len(e.Data) > 0 { 107 fs := bytes.Buffer{} 108 for k, v := range e.Data { 109 fs.WriteString(k) 110 fs.WriteByte('=') 111 fmt.Fprint(&fs, v) 112 fs.WriteByte(' ') 113 } 114 115 exts = fs.String()[:fs.Len()-1] 116 eid += eventExtraOffset 117 } 118 119 if h.log == nil { 120 fmt.Fprintf(os.Stderr, "%s [%s]\n", e.Message, exts) 121 return nil 122 } 123 124 var ( 125 ss [2]*uint16 126 err error 127 ) 128 129 ss[0], err = syscall.UTF16PtrFromString(e.Message) 130 if err != nil { 131 return err 132 } 133 134 count := uint16(1) 135 if exts != "" { 136 ss[1], err = syscall.UTF16PtrFromString(exts) 137 if err != nil { 138 return err 139 } 140 141 count++ 142 } 143 144 return windows.ReportEvent(h.log.Handle, etype, 0, eid, 0, count, 0, &ss[0], nil) 145 } 146 147 func getServicePath() (string, error) { 148 p, err := exec.LookPath(os.Args[0]) 149 if err != nil { 150 return "", err 151 } 152 return filepath.Abs(p) 153 } 154 155 func registerService() error { 156 p, err := getServicePath() 157 if err != nil { 158 return err 159 } 160 m, err := mgr.Connect() 161 if err != nil { 162 return err 163 } 164 defer m.Disconnect() 165 c := mgr.Config{ 166 ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, 167 StartType: mgr.StartAutomatic, 168 ErrorControl: mgr.ErrorNormal, 169 DisplayName: "Docker Engine", 170 } 171 172 // Configure the service to launch with the arguments that were just passed. 173 args := []string{"--run-service"} 174 for _, a := range os.Args[1:] { 175 if a != "--register-service" && a != "--unregister-service" { 176 args = append(args, a) 177 } 178 } 179 180 s, err := m.CreateService(*flServiceName, p, c, args...) 181 if err != nil { 182 return err 183 } 184 defer s.Close() 185 err = eventlog.Install(*flServiceName, p, false, eventlog.Info|eventlog.Warning|eventlog.Error) 186 if err != nil { 187 return err 188 } 189 190 return nil 191 } 192 193 func unregisterService() error { 194 m, err := mgr.Connect() 195 if err != nil { 196 return err 197 } 198 defer m.Disconnect() 199 200 s, err := m.OpenService(*flServiceName) 201 if err != nil { 202 return err 203 } 204 defer s.Close() 205 206 eventlog.Remove(*flServiceName) 207 err = s.Delete() 208 if err != nil { 209 return err 210 } 211 return nil 212 } 213 214 func initService(daemonCli *DaemonCli) (bool, error) { 215 if *flUnregisterService { 216 if *flRegisterService { 217 return true, errors.New("--register-service and --unregister-service cannot be used together") 218 } 219 return true, unregisterService() 220 } 221 222 if *flRegisterService { 223 return true, registerService() 224 } 225 226 if !*flRunService { 227 return false, nil 228 } 229 230 interactive, err := svc.IsAnInteractiveSession() 231 if err != nil { 232 return false, err 233 } 234 235 h := &handler{ 236 tosvc: make(chan bool), 237 fromsvc: make(chan error), 238 daemonCli: daemonCli, 239 } 240 241 var log *eventlog.Log 242 if !interactive { 243 log, err = eventlog.Open(*flServiceName) 244 if err != nil { 245 return false, err 246 } 247 } 248 249 logrus.AddHook(&etwHook{log}) 250 logrus.SetOutput(ioutil.Discard) 251 252 service = h 253 go func() { 254 if interactive { 255 err = debug.Run(*flServiceName, h) 256 } else { 257 err = svc.Run(*flServiceName, h) 258 } 259 260 h.fromsvc <- err 261 }() 262 263 // Wait for the first signal from the service handler. 264 err = <-h.fromsvc 265 if err != nil { 266 return false, err 267 } 268 return false, nil 269 } 270 271 func (h *handler) started() error { 272 // This must be delayed until daemonCli initializes Config.Root 273 err := initPanicFile(filepath.Join(h.daemonCli.Config.Root, "panic.log")) 274 if err != nil { 275 return err 276 } 277 278 h.tosvc <- false 279 return nil 280 } 281 282 func (h *handler) stopped(err error) { 283 logrus.Debugf("Stopping service: %v", err) 284 h.tosvc <- err != nil 285 <-h.fromsvc 286 } 287 288 func (h *handler) Execute(_ []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) { 289 s <- svc.Status{State: svc.StartPending, Accepts: 0} 290 // Unblock initService() 291 h.fromsvc <- nil 292 293 // Wait for initialization to complete. 294 failed := <-h.tosvc 295 if failed { 296 logrus.Debug("Aborting service start due to failure during initialization") 297 return true, 1 298 } 299 300 s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)} 301 logrus.Debug("Service running") 302 Loop: 303 for { 304 select { 305 case failed = <-h.tosvc: 306 break Loop 307 case c := <-r: 308 switch c.Cmd { 309 case svc.Cmd(windows.SERVICE_CONTROL_PARAMCHANGE): 310 h.daemonCli.reloadConfig() 311 case svc.Interrogate: 312 s <- c.CurrentStatus 313 case svc.Stop, svc.Shutdown: 314 s <- svc.Status{State: svc.StopPending, Accepts: 0} 315 h.daemonCli.stop() 316 } 317 } 318 } 319 320 removePanicFile() 321 if failed { 322 return true, 1 323 } 324 return false, 0 325 } 326 327 func initPanicFile(path string) error { 328 var err error 329 panicFile, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0) 330 if err != nil { 331 return err 332 } 333 334 st, err := panicFile.Stat() 335 if err != nil { 336 return err 337 } 338 339 // If there are contents in the file already, move the file out of the way 340 // and replace it. 341 if st.Size() > 0 { 342 panicFile.Close() 343 os.Rename(path, path+".old") 344 panicFile, err = os.Create(path) 345 if err != nil { 346 return err 347 } 348 } 349 350 // Update STD_ERROR_HANDLE to point to the panic file so that Go writes to 351 // it when it panics. Remember the old stderr to restore it before removing 352 // the panic file. 353 sh := syscall.STD_ERROR_HANDLE 354 h, err := syscall.GetStdHandle(sh) 355 if err != nil { 356 return err 357 } 358 359 oldStderr = h 360 361 r, _, err := setStdHandle.Call(uintptr(sh), uintptr(panicFile.Fd())) 362 if r == 0 && err != nil { 363 return err 364 } 365 366 return nil 367 } 368 369 func removePanicFile() { 370 if st, err := panicFile.Stat(); err == nil { 371 if st.Size() == 0 { 372 sh := syscall.STD_ERROR_HANDLE 373 setStdHandle.Call(uintptr(sh), uintptr(oldStderr)) 374 panicFile.Close() 375 os.Remove(panicFile.Name()) 376 } 377 } 378 }