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