github.com/Andyfoo/golang/x/sys@v0.0.0-20190901054642-57c1bf301704/windows/svc/mgr/mgr.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build windows 6 7 // Package mgr can be used to manage Windows service programs. 8 // It can be used to install and remove them. It can also start, 9 // stop and pause them. The package can query / change current 10 // service state and config parameters. 11 // 12 package mgr 13 14 import ( 15 "syscall" 16 "time" 17 "unicode/utf16" 18 "unsafe" 19 20 "github.com/Andyfoo/golang/x/sys/windows" 21 ) 22 23 // Mgr is used to manage Windows service. 24 type Mgr struct { 25 Handle windows.Handle 26 } 27 28 // Connect establishes a connection to the service control manager. 29 func Connect() (*Mgr, error) { 30 return ConnectRemote("") 31 } 32 33 // ConnectRemote establishes a connection to the 34 // service control manager on computer named host. 35 func ConnectRemote(host string) (*Mgr, error) { 36 var s *uint16 37 if host != "" { 38 s = syscall.StringToUTF16Ptr(host) 39 } 40 h, err := windows.OpenSCManager(s, nil, windows.SC_MANAGER_ALL_ACCESS) 41 if err != nil { 42 return nil, err 43 } 44 return &Mgr{Handle: h}, nil 45 } 46 47 // Disconnect closes connection to the service control manager m. 48 func (m *Mgr) Disconnect() error { 49 return windows.CloseServiceHandle(m.Handle) 50 } 51 52 type LockStatus struct { 53 IsLocked bool // Whether the SCM has been locked. 54 Age time.Duration // For how long the SCM has been locked. 55 Owner string // The name of the user who has locked the SCM. 56 } 57 58 // LockStatus returns whether the service control manager is locked by 59 // the system, for how long, and by whom. A locked SCM indicates that 60 // most service actions will block until the system unlocks the SCM. 61 func (m *Mgr) LockStatus() (*LockStatus, error) { 62 bytesNeeded := uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{}) + 1024) 63 for { 64 bytes := make([]byte, bytesNeeded) 65 lockStatus := (*windows.QUERY_SERVICE_LOCK_STATUS)(unsafe.Pointer(&bytes[0])) 66 err := windows.QueryServiceLockStatus(m.Handle, lockStatus, uint32(len(bytes)), &bytesNeeded) 67 if err == windows.ERROR_INSUFFICIENT_BUFFER && bytesNeeded >= uint32(unsafe.Sizeof(windows.QUERY_SERVICE_LOCK_STATUS{})) { 68 continue 69 } 70 if err != nil { 71 return nil, err 72 } 73 status := &LockStatus{ 74 IsLocked: lockStatus.IsLocked != 0, 75 Age: time.Duration(lockStatus.LockDuration) * time.Second, 76 Owner: windows.UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(lockStatus.LockOwner))[:]), 77 } 78 return status, nil 79 } 80 } 81 82 func toPtr(s string) *uint16 { 83 if len(s) == 0 { 84 return nil 85 } 86 return syscall.StringToUTF16Ptr(s) 87 } 88 89 // toStringBlock terminates strings in ss with 0, and then 90 // concatenates them together. It also adds extra 0 at the end. 91 func toStringBlock(ss []string) *uint16 { 92 if len(ss) == 0 { 93 return nil 94 } 95 t := "" 96 for _, s := range ss { 97 if s != "" { 98 t += s + "\x00" 99 } 100 } 101 if t == "" { 102 return nil 103 } 104 t += "\x00" 105 return &utf16.Encode([]rune(t))[0] 106 } 107 108 // CreateService installs new service name on the system. 109 // The service will be executed by running exepath binary. 110 // Use config c to specify service parameters. 111 // Any args will be passed as command-line arguments when 112 // the service is started; these arguments are distinct from 113 // the arguments passed to Service.Start or via the "Start 114 // parameters" field in the service's Properties dialog box. 115 func (m *Mgr) CreateService(name, exepath string, c Config, args ...string) (*Service, error) { 116 if c.StartType == 0 { 117 c.StartType = StartManual 118 } 119 if c.ErrorControl == 0 { 120 c.ErrorControl = ErrorNormal 121 } 122 if c.ServiceType == 0 { 123 c.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS 124 } 125 s := syscall.EscapeArg(exepath) 126 for _, v := range args { 127 s += " " + syscall.EscapeArg(v) 128 } 129 h, err := windows.CreateService(m.Handle, toPtr(name), toPtr(c.DisplayName), 130 windows.SERVICE_ALL_ACCESS, c.ServiceType, 131 c.StartType, c.ErrorControl, toPtr(s), toPtr(c.LoadOrderGroup), 132 nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), toPtr(c.Password)) 133 if err != nil { 134 return nil, err 135 } 136 if c.SidType != windows.SERVICE_SID_TYPE_NONE { 137 err = updateSidType(h, c.SidType) 138 if err != nil { 139 windows.DeleteService(h) 140 windows.CloseHandle(h) 141 return nil, err 142 } 143 } 144 if c.Description != "" { 145 err = updateDescription(h, c.Description) 146 if err != nil { 147 windows.DeleteService(h) 148 windows.CloseHandle(h) 149 return nil, err 150 } 151 } 152 if c.DelayedAutoStart { 153 err = updateStartUp(h, c.DelayedAutoStart) 154 if err != nil { 155 windows.DeleteService(h) 156 windows.CloseHandle(h) 157 return nil, err 158 } 159 } 160 return &Service{Name: name, Handle: h}, nil 161 } 162 163 // OpenService retrieves access to service name, so it can 164 // be interrogated and controlled. 165 func (m *Mgr) OpenService(name string) (*Service, error) { 166 h, err := windows.OpenService(m.Handle, syscall.StringToUTF16Ptr(name), windows.SERVICE_ALL_ACCESS) 167 if err != nil { 168 return nil, err 169 } 170 return &Service{Name: name, Handle: h}, nil 171 } 172 173 // ListServices enumerates services in the specified 174 // service control manager database m. 175 // If the caller does not have the SERVICE_QUERY_STATUS 176 // access right to a service, the service is silently 177 // omitted from the list of services returned. 178 func (m *Mgr) ListServices() ([]string, error) { 179 var err error 180 var bytesNeeded, servicesReturned uint32 181 var buf []byte 182 for { 183 var p *byte 184 if len(buf) > 0 { 185 p = &buf[0] 186 } 187 err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO, 188 windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL, 189 p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil) 190 if err == nil { 191 break 192 } 193 if err != syscall.ERROR_MORE_DATA { 194 return nil, err 195 } 196 if bytesNeeded <= uint32(len(buf)) { 197 return nil, err 198 } 199 buf = make([]byte, bytesNeeded) 200 } 201 if servicesReturned == 0 { 202 return nil, nil 203 } 204 services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned] 205 var names []string 206 for _, s := range services { 207 name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:]) 208 names = append(names, name) 209 } 210 return names, nil 211 }