telegraf/plugins/inputs/win_services/win_services.go

213 lines
4.6 KiB
Go

// +build windows
package win_services
import (
"fmt"
"os"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)
type ServiceErr struct {
Message string
Service string
Err error
}
func (e *ServiceErr) Error() string {
return fmt.Sprintf("%s: '%s': %v", e.Message, e.Service, e.Err)
}
func IsPermission(err error) bool {
if err, ok := err.(*ServiceErr); ok {
return os.IsPermission(err.Err)
}
return false
}
// WinService provides interface for svc.Service
type WinService interface {
Close() error
Config() (mgr.Config, error)
Query() (svc.Status, error)
}
// ManagerProvider sets interface for acquiring manager instance, like mgr.Mgr
type ManagerProvider interface {
Connect() (WinServiceManager, error)
}
// WinServiceManager provides interface for mgr.Mgr
type WinServiceManager interface {
Disconnect() error
OpenService(name string) (WinService, error)
ListServices() ([]string, error)
}
// WinSvcMgr is wrapper for mgr.Mgr implementing WinServiceManager interface
type WinSvcMgr struct {
realMgr *mgr.Mgr
}
func (m *WinSvcMgr) Disconnect() error {
return m.realMgr.Disconnect()
}
func (m *WinSvcMgr) OpenService(name string) (WinService, error) {
return m.realMgr.OpenService(name)
}
func (m *WinSvcMgr) ListServices() ([]string, error) {
return m.realMgr.ListServices()
}
// MgProvider is an implementation of WinServiceManagerProvider interface returning WinSvcMgr
type MgProvider struct {
}
func (rmr *MgProvider) Connect() (WinServiceManager, error) {
scmgr, err := mgr.Connect()
if err != nil {
return nil, err
} else {
return &WinSvcMgr{scmgr}, nil
}
}
var sampleConfig = `
## Names of the services to monitor. Leave empty to monitor all the available services on the host
service_names = [
"LanmanServer",
"TermService",
]
`
var description = "Input plugin to report Windows services info."
//WinServices is an implementation if telegraf.Input interface, providing info about Windows Services
type WinServices struct {
Log telegraf.Logger
ServiceNames []string `toml:"service_names"`
mgrProvider ManagerProvider
}
type ServiceInfo struct {
ServiceName string
DisplayName string
State int
StartUpMode int
}
func (m *WinServices) Description() string {
return description
}
func (m *WinServices) SampleConfig() string {
return sampleConfig
}
func (m *WinServices) Gather(acc telegraf.Accumulator) error {
scmgr, err := m.mgrProvider.Connect()
if err != nil {
return fmt.Errorf("Could not open service manager: %s", err)
}
defer scmgr.Disconnect()
serviceNames, err := listServices(scmgr, m.ServiceNames)
if err != nil {
return err
}
for _, srvName := range serviceNames {
service, err := collectServiceInfo(scmgr, srvName)
if err != nil {
if IsPermission(err) {
m.Log.Debug(err.Error())
} else {
m.Log.Error(err.Error())
}
continue
}
tags := map[string]string{
"service_name": service.ServiceName,
}
//display name could be empty, but still valid service
if len(service.DisplayName) > 0 {
tags["display_name"] = service.DisplayName
}
fields := map[string]interface{}{
"state": service.State,
"startup_mode": service.StartUpMode,
}
acc.AddFields("win_services", fields, tags)
}
return nil
}
// listServices returns a list of services to gather.
func listServices(scmgr WinServiceManager, userServices []string) ([]string, error) {
if len(userServices) != 0 {
return userServices, nil
}
names, err := scmgr.ListServices()
if err != nil {
return nil, fmt.Errorf("Could not list services: %s", err)
}
return names, nil
}
// collectServiceInfo gathers info about a service.
func collectServiceInfo(scmgr WinServiceManager, serviceName string) (*ServiceInfo, error) {
srv, err := scmgr.OpenService(serviceName)
if err != nil {
return nil, &ServiceErr{
Message: "could not open service",
Service: serviceName,
Err: err,
}
}
defer srv.Close()
srvStatus, err := srv.Query()
if err != nil {
return nil, &ServiceErr{
Message: "could not query service",
Service: serviceName,
Err: err,
}
}
srvCfg, err := srv.Config()
if err != nil {
return nil, &ServiceErr{
Message: "could not get config of service",
Service: serviceName,
Err: err,
}
}
serviceInfo := &ServiceInfo{
ServiceName: serviceName,
DisplayName: srvCfg.DisplayName,
StartUpMode: int(srvCfg.StartType),
State: int(srvStatus.State),
}
return serviceInfo, nil
}
func init() {
inputs.Add("win_services", func() telegraf.Input {
return &WinServices{
mgrProvider: &MgProvider{},
}
})
}