Update dependencies
This commit is contained in:
303
vendor/tailscale.com/util/winutil/svcdiag_windows.go
generated
vendored
Normal file
303
vendor/tailscale.com/util/winutil/svcdiag_windows.go
generated
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package winutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/set"
|
||||
)
|
||||
|
||||
// LogSvcState obtains the state of the Windows service named rootSvcName and
|
||||
// all of its dependencies, and then emits that state to logf.
|
||||
func LogSvcState(logf logger.Logf, rootSvcName string) {
|
||||
logEntries := []svcStateLogEntry{}
|
||||
|
||||
walkFn := func(svc *mgr.Service, config mgr.Config) {
|
||||
status, err := svc.Query()
|
||||
if err != nil {
|
||||
logf("Failed retrieving Status for service %q: %v", svc.Name, err)
|
||||
}
|
||||
|
||||
logEntries = append(logEntries, makeLogEntry(svc, status, config))
|
||||
}
|
||||
|
||||
err := walkServices(rootSvcName, walkFn)
|
||||
if err != nil {
|
||||
logf("LogSvcState error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
json, err := json.MarshalIndent(logEntries, "", " ")
|
||||
if err != nil {
|
||||
logf("Error marshaling service log entries: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString("State of service ")
|
||||
fmt.Fprintf(&builder, "%q", rootSvcName)
|
||||
builder.WriteString(" and its dependencies:")
|
||||
builder.WriteString("\n")
|
||||
builder.Write(json)
|
||||
builder.WriteString("\n")
|
||||
|
||||
logf(builder.String())
|
||||
}
|
||||
|
||||
// walkSvcFunc is type of the callback function invoked by WalkServices.
|
||||
type walkSvcFunc func(*mgr.Service, mgr.Config)
|
||||
|
||||
// walkServices opens the service named rootSvcName and walks its dependency
|
||||
// graph, invoking callback for each service (including the root itself).
|
||||
func walkServices(rootSvcName string, callback walkSvcFunc) error {
|
||||
scm, err := ConnectToLocalSCMForRead()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connecting to Service Control Manager: %w", err)
|
||||
}
|
||||
defer scm.Disconnect()
|
||||
|
||||
rootSvc, err := OpenServiceForRead(scm, rootSvcName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening service %q: %w", rootSvcName, err)
|
||||
}
|
||||
|
||||
deps := []*mgr.Service{rootSvc}
|
||||
defer func() {
|
||||
// Any service still in deps when we return is open and must be closed.
|
||||
for _, dep := range deps {
|
||||
dep.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
seen := set.Set[string]{}
|
||||
|
||||
for err == nil && len(deps) > 0 {
|
||||
err = func() error {
|
||||
curSvc := deps[len(deps)-1]
|
||||
defer curSvc.Close()
|
||||
|
||||
deps = deps[:len(deps)-1]
|
||||
|
||||
seen.Add(curSvc.Name)
|
||||
|
||||
curCfg, err := curSvc.Config()
|
||||
if err != nil {
|
||||
return fmt.Errorf("retrieving Config for service %q: %w", curSvc.Name, err)
|
||||
}
|
||||
|
||||
callback(curSvc, curCfg)
|
||||
|
||||
for _, depName := range curCfg.Dependencies {
|
||||
if seen.Contains(depName) {
|
||||
continue
|
||||
}
|
||||
|
||||
depSvc, err := OpenServiceForRead(scm, depName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening service %q: %w", depName, err)
|
||||
}
|
||||
|
||||
deps = append(deps, depSvc)
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type svcStateLogEntry struct {
|
||||
ServiceName string `json:"serviceName"`
|
||||
ServiceType string `json:"serviceType"`
|
||||
State string `json:"state"`
|
||||
StartupType string `json:"startupType"`
|
||||
Triggers *_SERVICE_TRIGGER_INFO `json:"triggers,omitempty"`
|
||||
TriggersError error `json:"triggersError,omitempty"`
|
||||
}
|
||||
|
||||
type _SERVICE_TRIGGER_SPECIFIC_DATA_ITEM struct {
|
||||
dataType uint32
|
||||
cbData uint32
|
||||
data *byte
|
||||
}
|
||||
|
||||
type serviceTriggerSpecificDataItemJSONMarshal struct {
|
||||
DataType uint32 `json:"dataType"`
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (tsdi *_SERVICE_TRIGGER_SPECIFIC_DATA_ITEM) MarshalJSON() ([]byte, error) {
|
||||
m := serviceTriggerSpecificDataItemJSONMarshal{DataType: tsdi.dataType}
|
||||
|
||||
const maxDataLen = 128
|
||||
data := unsafe.Slice(tsdi.data, tsdi.cbData)
|
||||
if len(data) > maxDataLen {
|
||||
// Only output the first maxDataLen bytes.
|
||||
m.Data = fmt.Sprintf("%s... (truncated %d bytes)", hex.EncodeToString(data[:maxDataLen]), len(data)-maxDataLen)
|
||||
} else {
|
||||
m.Data = hex.EncodeToString(data)
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
type _SERVICE_TRIGGER struct {
|
||||
triggerType uint32
|
||||
action uint32
|
||||
triggerSubtype *windows.GUID
|
||||
cDataItems uint32
|
||||
pDataItems *_SERVICE_TRIGGER_SPECIFIC_DATA_ITEM
|
||||
}
|
||||
|
||||
type serviceTriggerJSONMarshal struct {
|
||||
TriggerType uint32 `json:"triggerType"`
|
||||
Action uint32 `json:"action"`
|
||||
TriggerSubtype string `json:"triggerSubtype,omitempty"`
|
||||
DataItems []_SERVICE_TRIGGER_SPECIFIC_DATA_ITEM `json:"dataItems"`
|
||||
}
|
||||
|
||||
func (ti *_SERVICE_TRIGGER) MarshalJSON() ([]byte, error) {
|
||||
m := serviceTriggerJSONMarshal{
|
||||
TriggerType: ti.triggerType,
|
||||
Action: ti.action,
|
||||
DataItems: unsafe.Slice(ti.pDataItems, ti.cDataItems),
|
||||
}
|
||||
if ti.triggerSubtype != nil {
|
||||
m.TriggerSubtype = ti.triggerSubtype.String()
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
type _SERVICE_TRIGGER_INFO struct {
|
||||
cTriggers uint32
|
||||
pTriggers *_SERVICE_TRIGGER
|
||||
_ *byte // pReserved
|
||||
}
|
||||
|
||||
func (sti *_SERVICE_TRIGGER_INFO) MarshalJSON() ([]byte, error) {
|
||||
triggers := unsafe.Slice(sti.pTriggers, sti.cTriggers)
|
||||
return json.Marshal(triggers)
|
||||
}
|
||||
|
||||
// getSvcTriggerInfo obtains information about any system events that may be
|
||||
// used to start svc. Only relevant for demand-start (aka manual) services.
|
||||
func getSvcTriggerInfo(svc *mgr.Service) (*_SERVICE_TRIGGER_INFO, error) {
|
||||
var desiredLen uint32
|
||||
err := queryServiceConfig2(svc.Handle, windows.SERVICE_CONFIG_TRIGGER_INFO,
|
||||
nil, 0, &desiredLen)
|
||||
if err != windows.ERROR_INSUFFICIENT_BUFFER {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := make([]byte, desiredLen)
|
||||
err = queryServiceConfig2(svc.Handle, windows.SERVICE_CONFIG_TRIGGER_INFO,
|
||||
&buf[0], desiredLen, &desiredLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return (*_SERVICE_TRIGGER_INFO)(unsafe.Pointer(&buf[0])), nil
|
||||
}
|
||||
|
||||
// makeLogEntry consolidates relevant service information into a svcStateLogEntry.
|
||||
// We record the values of various service configuration constants as strings
|
||||
// so the the log entries are easy to interpret at a glance by humans.
|
||||
func makeLogEntry(svc *mgr.Service, status svc.Status, cfg mgr.Config) (entry svcStateLogEntry) {
|
||||
entry.ServiceName = svc.Name
|
||||
|
||||
switch status.State {
|
||||
case windows.SERVICE_STOPPED:
|
||||
entry.State = "STOPPED"
|
||||
case windows.SERVICE_START_PENDING:
|
||||
entry.State = "START_PENDING"
|
||||
case windows.SERVICE_STOP_PENDING:
|
||||
entry.State = "STOP_PENDING"
|
||||
case windows.SERVICE_RUNNING:
|
||||
entry.State = "RUNNING"
|
||||
case windows.SERVICE_CONTINUE_PENDING:
|
||||
entry.State = "CONTINUE_PENDING"
|
||||
case windows.SERVICE_PAUSE_PENDING:
|
||||
entry.State = "PAUSE_PENDING"
|
||||
case windows.SERVICE_PAUSED:
|
||||
entry.State = "PAUSED"
|
||||
case windows.SERVICE_NO_CHANGE:
|
||||
entry.State = "NO_CHANGE"
|
||||
default:
|
||||
entry.State = fmt.Sprintf("Unknown constant %d", status.State)
|
||||
}
|
||||
|
||||
switch cfg.ServiceType {
|
||||
case windows.SERVICE_FILE_SYSTEM_DRIVER:
|
||||
entry.ServiceType = "FILE_SYSTEM_DRIVER"
|
||||
case windows.SERVICE_KERNEL_DRIVER:
|
||||
entry.ServiceType = "KERNEL_DRIVER"
|
||||
case windows.SERVICE_WIN32_OWN_PROCESS, windows.SERVICE_WIN32_SHARE_PROCESS:
|
||||
entry.ServiceType = "WIN32"
|
||||
default:
|
||||
entry.ServiceType = fmt.Sprintf("Unknown constant %d", cfg.ServiceType)
|
||||
}
|
||||
|
||||
switch cfg.StartType {
|
||||
case windows.SERVICE_BOOT_START:
|
||||
entry.StartupType = "BOOT_START"
|
||||
case windows.SERVICE_SYSTEM_START:
|
||||
entry.StartupType = "SYSTEM_START"
|
||||
case windows.SERVICE_AUTO_START:
|
||||
if cfg.DelayedAutoStart {
|
||||
entry.StartupType = "DELAYED_AUTO_START"
|
||||
} else {
|
||||
entry.StartupType = "AUTO_START"
|
||||
}
|
||||
case windows.SERVICE_DEMAND_START:
|
||||
entry.StartupType = "DEMAND_START"
|
||||
triggerInfo, err := getSvcTriggerInfo(svc)
|
||||
if err == nil {
|
||||
entry.Triggers = triggerInfo
|
||||
} else {
|
||||
entry.TriggersError = err
|
||||
}
|
||||
case windows.SERVICE_DISABLED:
|
||||
entry.StartupType = "DISABLED"
|
||||
default:
|
||||
entry.StartupType = fmt.Sprintf("Unknown constant %d", cfg.StartType)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// ConnectToLocalSCMForRead connects to the Windows Service Control Manager with
|
||||
// read-only access. x/sys/windows/svc/mgr/Connect requests read+write access,
|
||||
// which requires Administrative access rights.
|
||||
func ConnectToLocalSCMForRead() (*mgr.Mgr, error) {
|
||||
h, err := windows.OpenSCManager(nil, nil, windows.GENERIC_READ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgr.Mgr{Handle: h}, nil
|
||||
}
|
||||
|
||||
// OpenServiceForRead opens a service with read-only access.
|
||||
// x/sys/windows/svc/mgr/(*Mgr).OpenService requests read+write access,
|
||||
// which requires Administrative access rights.
|
||||
func OpenServiceForRead(scm *mgr.Mgr, svcName string) (*mgr.Service, error) {
|
||||
svcNamePtr, err := windows.UTF16PtrFromString(svcName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, err := windows.OpenService(scm.Handle, svcNamePtr, windows.GENERIC_READ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgr.Service{Name: svcName, Handle: h}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user