open-consul/agent/checks/os_service_windows.go

115 lines
3.0 KiB
Go

//go:build windows
// +build windows
package checks
import (
"fmt"
"syscall"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)
var (
win windowsSystem = windowsOS{}
)
type OSServiceClient struct {
scHandle windows.Handle
}
func NewOSServiceClient() (*OSServiceClient, error) {
var s *uint16
scHandle, err := win.OpenSCManager(s, nil, windows.SC_MANAGER_CONNECT)
if err != nil {
return nil, fmt.Errorf("error connecting to service manager: %w", err)
}
return &OSServiceClient{
scHandle: scHandle,
}, nil
}
func (client *OSServiceClient) Check(serviceName string) (err error) {
var isHealthy bool
m := win.getWindowsSvcMgr(client.scHandle)
defer func() {
errDisconnect := m.Disconnect()
if isHealthy || errDisconnect == nil || err != nil {
return
}
//unreachable at the moment but we might want to log this error. leaving here for code-review
err = errDisconnect
}()
svcHandle, err := win.OpenService(win.getWindowsSvcMgrHandle(m), syscall.StringToUTF16Ptr(serviceName), windows.SC_MANAGER_ENUMERATE_SERVICE)
if err != nil {
return fmt.Errorf("error accessing service: %w", err)
}
service := win.getWindowsSvc(serviceName, svcHandle)
defer func() {
errClose := service.Close()
if isHealthy || errClose == nil || err != nil {
return
}
//unreachable at the moment but we might want to log this error. leaving here for code-review
err = errClose
}()
status, err := service.Query()
if err != nil {
return fmt.Errorf("error querying service status: %w", err)
}
switch status.State {
case svc.Running:
err = nil
isHealthy = true
case svc.Paused, svc.Stopped:
err = ErrOSServiceStatusCritical
default:
err = fmt.Errorf("service status: %v", status.State)
}
return err
}
type windowsOS struct{}
func (windowsOS) OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle windows.Handle, err error) {
return windows.OpenSCManager(machineName, databaseName, access)
}
func (windowsOS) OpenService(mgr windows.Handle, serviceName *uint16, access uint32) (handle windows.Handle, err error) {
return windows.OpenService(mgr, serviceName, access)
}
func (windowsOS) getWindowsSvcMgr(h windows.Handle) windowsSvcMgr { return &mgr.Mgr{Handle: h} }
func (windowsOS) getWindowsSvcMgrHandle(sm windowsSvcMgr) windows.Handle {
return sm.(*mgr.Mgr).Handle
}
func (windowsOS) getWindowsSvc(name string, h windows.Handle) windowsSvc {
return &mgr.Service{Name: name, Handle: h}
}
type windowsSystem interface {
OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle windows.Handle, err error)
OpenService(mgr windows.Handle, serviceName *uint16, access uint32) (handle windows.Handle, err error)
getWindowsSvcMgr(h windows.Handle) windowsSvcMgr
getWindowsSvcMgrHandle(sm windowsSvcMgr) windows.Handle
getWindowsSvc(name string, h windows.Handle) windowsSvc
}
type windowsSvcMgr interface {
Disconnect() error
}
type windowsSvc interface {
Close() error
Query() (svc.Status, error)
}