OSDN Git Service

fix commands
[bytom/shuttle.git] / vendor / github.com / bytom / vendor / golang.org / x / sys / windows / svc / service.go
diff --git a/vendor/github.com/bytom/vendor/golang.org/x/sys/windows/svc/service.go b/vendor/github.com/bytom/vendor/golang.org/x/sys/windows/svc/service.go
new file mode 100644 (file)
index 0000000..903cba3
--- /dev/null
@@ -0,0 +1,363 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+// Package svc provides everything required to build Windows service.
+//
+package svc
+
+import (
+       "errors"
+       "runtime"
+       "syscall"
+       "unsafe"
+
+       "golang.org/x/sys/windows"
+)
+
+// State describes service execution state (Stopped, Running and so on).
+type State uint32
+
+const (
+       Stopped         = State(windows.SERVICE_STOPPED)
+       StartPending    = State(windows.SERVICE_START_PENDING)
+       StopPending     = State(windows.SERVICE_STOP_PENDING)
+       Running         = State(windows.SERVICE_RUNNING)
+       ContinuePending = State(windows.SERVICE_CONTINUE_PENDING)
+       PausePending    = State(windows.SERVICE_PAUSE_PENDING)
+       Paused          = State(windows.SERVICE_PAUSED)
+)
+
+// Cmd represents service state change request. It is sent to a service
+// by the service manager, and should be actioned upon by the service.
+type Cmd uint32
+
+const (
+       Stop                  = Cmd(windows.SERVICE_CONTROL_STOP)
+       Pause                 = Cmd(windows.SERVICE_CONTROL_PAUSE)
+       Continue              = Cmd(windows.SERVICE_CONTROL_CONTINUE)
+       Interrogate           = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
+       Shutdown              = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
+       ParamChange           = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
+       NetBindAdd            = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
+       NetBindRemove         = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
+       NetBindEnable         = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
+       NetBindDisable        = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
+       DeviceEvent           = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
+       HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
+       PowerEvent            = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
+       SessionChange         = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
+)
+
+// Accepted is used to describe commands accepted by the service.
+// Note that Interrogate is always accepted.
+type Accepted uint32
+
+const (
+       AcceptStop                  = Accepted(windows.SERVICE_ACCEPT_STOP)
+       AcceptShutdown              = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN)
+       AcceptPauseAndContinue      = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE)
+       AcceptParamChange           = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)
+       AcceptNetBindChange         = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE)
+       AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
+       AcceptPowerEvent            = Accepted(windows.SERVICE_ACCEPT_POWEREVENT)
+       AcceptSessionChange         = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE)
+)
+
+// Status combines State and Accepted commands to fully describe running service.
+type Status struct {
+       State      State
+       Accepts    Accepted
+       CheckPoint uint32 // used to report progress during a lengthy operation
+       WaitHint   uint32 // estimated time required for a pending operation, in milliseconds
+}
+
+// ChangeRequest is sent to the service Handler to request service status change.
+type ChangeRequest struct {
+       Cmd           Cmd
+       EventType     uint32
+       EventData     uintptr
+       CurrentStatus Status
+}
+
+// Handler is the interface that must be implemented to build Windows service.
+type Handler interface {
+
+       // Execute will be called by the package code at the start of
+       // the service, and the service will exit once Execute completes.
+       // Inside Execute you must read service change requests from r and
+       // act accordingly. You must keep service control manager up to date
+       // about state of your service by writing into s as required.
+       // args contains service name followed by argument strings passed
+       // to the service.
+       // You can provide service exit code in exitCode return parameter,
+       // with 0 being "no error". You can also indicate if exit code,
+       // if any, is service specific or not by using svcSpecificEC
+       // parameter.
+       Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
+}
+
+var (
+       // These are used by asm code.
+       goWaitsH                       uintptr
+       cWaitsH                        uintptr
+       ssHandle                       uintptr
+       sName                          *uint16
+       sArgc                          uintptr
+       sArgv                          **uint16
+       ctlHandlerExProc               uintptr
+       cSetEvent                      uintptr
+       cWaitForSingleObject           uintptr
+       cRegisterServiceCtrlHandlerExW uintptr
+)
+
+func init() {
+       k := syscall.MustLoadDLL("kernel32.dll")
+       cSetEvent = k.MustFindProc("SetEvent").Addr()
+       cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
+       a := syscall.MustLoadDLL("advapi32.dll")
+       cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
+}
+
+// The HandlerEx prototype also has a context pointer but since we don't use
+// it at start-up time we don't have to pass it over either.
+type ctlEvent struct {
+       cmd       Cmd
+       eventType uint32
+       eventData uintptr
+       errno     uint32
+}
+
+// service provides access to windows service api.
+type service struct {
+       name    string
+       h       windows.Handle
+       cWaits  *event
+       goWaits *event
+       c       chan ctlEvent
+       handler Handler
+}
+
+func newService(name string, handler Handler) (*service, error) {
+       var s service
+       var err error
+       s.name = name
+       s.c = make(chan ctlEvent)
+       s.handler = handler
+       s.cWaits, err = newEvent()
+       if err != nil {
+               return nil, err
+       }
+       s.goWaits, err = newEvent()
+       if err != nil {
+               s.cWaits.Close()
+               return nil, err
+       }
+       return &s, nil
+}
+
+func (s *service) close() error {
+       s.cWaits.Close()
+       s.goWaits.Close()
+       return nil
+}
+
+type exitCode struct {
+       isSvcSpecific bool
+       errno         uint32
+}
+
+func (s *service) updateStatus(status *Status, ec *exitCode) error {
+       if s.h == 0 {
+               return errors.New("updateStatus with no service status handle")
+       }
+       var t windows.SERVICE_STATUS
+       t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
+       t.CurrentState = uint32(status.State)
+       if status.Accepts&AcceptStop != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP
+       }
+       if status.Accepts&AcceptShutdown != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
+       }
+       if status.Accepts&AcceptPauseAndContinue != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
+       }
+       if status.Accepts&AcceptParamChange != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE
+       }
+       if status.Accepts&AcceptNetBindChange != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE
+       }
+       if status.Accepts&AcceptHardwareProfileChange != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE
+       }
+       if status.Accepts&AcceptPowerEvent != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT
+       }
+       if status.Accepts&AcceptSessionChange != 0 {
+               t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE
+       }
+       if ec.errno == 0 {
+               t.Win32ExitCode = windows.NO_ERROR
+               t.ServiceSpecificExitCode = windows.NO_ERROR
+       } else if ec.isSvcSpecific {
+               t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR)
+               t.ServiceSpecificExitCode = ec.errno
+       } else {
+               t.Win32ExitCode = ec.errno
+               t.ServiceSpecificExitCode = windows.NO_ERROR
+       }
+       t.CheckPoint = status.CheckPoint
+       t.WaitHint = status.WaitHint
+       return windows.SetServiceStatus(s.h, &t)
+}
+
+const (
+       sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
+       sysErrNewThreadInCallback
+)
+
+func (s *service) run() {
+       s.goWaits.Wait()
+       s.h = windows.Handle(ssHandle)
+       argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
+       args := make([]string, len(argv))
+       for i, a := range argv {
+               args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:])
+       }
+
+       cmdsToHandler := make(chan ChangeRequest)
+       changesFromHandler := make(chan Status)
+       exitFromHandler := make(chan exitCode)
+
+       go func() {
+               ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
+               exitFromHandler <- exitCode{ss, errno}
+       }()
+
+       status := Status{State: Stopped}
+       ec := exitCode{isSvcSpecific: true, errno: 0}
+       var outch chan ChangeRequest
+       inch := s.c
+       var cmd Cmd
+       var evtype uint32
+       var evdata uintptr
+loop:
+       for {
+               select {
+               case r := <-inch:
+                       if r.errno != 0 {
+                               ec.errno = r.errno
+                               break loop
+                       }
+                       inch = nil
+                       outch = cmdsToHandler
+                       cmd = r.cmd
+                       evtype = r.eventType
+                       evdata = r.eventData
+               case outch <- ChangeRequest{cmd, evtype, evdata, status}:
+                       inch = s.c
+                       outch = nil
+               case c := <-changesFromHandler:
+                       err := s.updateStatus(&c, &ec)
+                       if err != nil {
+                               // best suitable error number
+                               ec.errno = sysErrSetServiceStatusFailed
+                               if err2, ok := err.(syscall.Errno); ok {
+                                       ec.errno = uint32(err2)
+                               }
+                               break loop
+                       }
+                       status = c
+               case ec = <-exitFromHandler:
+                       break loop
+               }
+       }
+
+       s.updateStatus(&Status{State: Stopped}, &ec)
+       s.cWaits.Set()
+}
+
+func newCallback(fn interface{}) (cb uintptr, err error) {
+       defer func() {
+               r := recover()
+               if r == nil {
+                       return
+               }
+               cb = 0
+               switch v := r.(type) {
+               case string:
+                       err = errors.New(v)
+               case error:
+                       err = v
+               default:
+                       err = errors.New("unexpected panic in syscall.NewCallback")
+               }
+       }()
+       return syscall.NewCallback(fn), nil
+}
+
+// BUG(brainman): There is no mechanism to run multiple services
+// inside one single executable. Perhaps, it can be overcome by
+// using RegisterServiceCtrlHandlerEx Windows api.
+
+// Run executes service name by calling appropriate handler function.
+func Run(name string, handler Handler) error {
+       runtime.LockOSThread()
+
+       tid := windows.GetCurrentThreadId()
+
+       s, err := newService(name, handler)
+       if err != nil {
+               return err
+       }
+
+       ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
+               e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
+               // We assume that this callback function is running on
+               // the same thread as Run. Nowhere in MS documentation
+               // I could find statement to guarantee that. So putting
+               // check here to verify, otherwise things will go bad
+               // quickly, if ignored.
+               i := windows.GetCurrentThreadId()
+               if i != tid {
+                       e.errno = sysErrNewThreadInCallback
+               }
+               s.c <- e
+               // Always return NO_ERROR (0) for now.
+               return 0
+       }
+
+       var svcmain uintptr
+       getServiceMain(&svcmain)
+       t := []windows.SERVICE_TABLE_ENTRY{
+               {syscall.StringToUTF16Ptr(s.name), svcmain},
+               {nil, 0},
+       }
+
+       goWaitsH = uintptr(s.goWaits.h)
+       cWaitsH = uintptr(s.cWaits.h)
+       sName = t[0].ServiceName
+       ctlHandlerExProc, err = newCallback(ctlHandler)
+       if err != nil {
+               return err
+       }
+
+       go s.run()
+
+       err = windows.StartServiceCtrlDispatcher(&t[0])
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+// StatusHandle returns service status handle. It is safe to call this function
+// from inside the Handler.Execute because then it is guaranteed to be set.
+// This code will have to change once multiple services are possible per process.
+func StatusHandle() windows.Handle {
+       return windows.Handle(ssHandle)
+}