2017-08-07 21:36:15 +00:00
|
|
|
// +build windows
|
|
|
|
|
|
|
|
package win_services
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-09-11 23:04:16 +00:00
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
|
2017-08-07 21:36:15 +00:00
|
|
|
"github.com/influxdata/telegraf"
|
|
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
|
|
|
"golang.org/x/sys/windows/svc"
|
|
|
|
"golang.org/x/sys/windows/svc/mgr"
|
|
|
|
)
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
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
|
2017-08-07 21:36:15 +00:00
|
|
|
type WinService interface {
|
|
|
|
Close() error
|
|
|
|
Config() (mgr.Config, error)
|
|
|
|
Query() (svc.Status, error)
|
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
// ManagerProvider sets interface for acquiring manager instance, like mgr.Mgr
|
|
|
|
type ManagerProvider interface {
|
2017-08-07 21:36:15 +00:00
|
|
|
Connect() (WinServiceManager, error)
|
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
// WinServiceManager provides interface for mgr.Mgr
|
2017-08-07 21:36:15 +00:00
|
|
|
type WinServiceManager interface {
|
|
|
|
Disconnect() error
|
|
|
|
OpenService(name string) (WinService, error)
|
|
|
|
ListServices() ([]string, error)
|
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
// WinSvcMgr is wrapper for mgr.Mgr implementing WinServiceManager interface
|
2017-08-07 21:36:15 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
// MgProvider is an implementation of WinServiceManagerProvider interface returning WinSvcMgr
|
2017-08-07 21:36:15 +00:00
|
|
|
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 {
|
|
|
|
ServiceNames []string `toml:"service_names"`
|
2018-09-11 23:04:16 +00:00
|
|
|
mgrProvider ManagerProvider
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2018-09-11 23:04:16 +00:00
|
|
|
scmgr, err := m.mgrProvider.Connect()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Could not open service manager: %s", err)
|
|
|
|
}
|
|
|
|
defer scmgr.Disconnect()
|
2017-08-07 21:36:15 +00:00
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
serviceNames, err := listServices(scmgr, m.ServiceNames)
|
2017-08-07 21:36:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
for _, srvName := range serviceNames {
|
|
|
|
service, err := collectServiceInfo(scmgr, srvName)
|
|
|
|
if err != nil {
|
|
|
|
if IsPermission(err) {
|
|
|
|
log.Printf("D! Error in plugin [inputs.win_services]: %v", err)
|
|
|
|
} else {
|
|
|
|
acc.AddError(err)
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
2018-09-11 23:04:16 +00:00
|
|
|
continue
|
|
|
|
}
|
2017-08-07 21:36:15 +00:00
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
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
|
|
|
|
}
|
2017-08-07 21:36:15 +00:00
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
fields := map[string]interface{}{
|
|
|
|
"state": service.State,
|
|
|
|
"startup_mode": service.StartUpMode,
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
2018-09-11 23:04:16 +00:00
|
|
|
acc.AddFields("win_services", fields, tags)
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
// listServices returns a list of services to gather.
|
|
|
|
func listServices(scmgr WinServiceManager, userServices []string) ([]string, error) {
|
|
|
|
if len(userServices) != 0 {
|
|
|
|
return userServices, nil
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
names, err := scmgr.ListServices()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Could not list services: %s", err)
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
2018-09-11 23:04:16 +00:00
|
|
|
return names, nil
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-11 23:04:16 +00:00
|
|
|
// collectServiceInfo gathers info about a service.
|
|
|
|
func collectServiceInfo(scmgr WinServiceManager, serviceName string) (*ServiceInfo, error) {
|
2017-08-07 21:36:15 +00:00
|
|
|
srv, err := scmgr.OpenService(serviceName)
|
|
|
|
if err != nil {
|
2018-09-11 23:04:16 +00:00
|
|
|
return nil, &ServiceErr{
|
|
|
|
Message: "could not open service",
|
|
|
|
Service: serviceName,
|
|
|
|
Err: err,
|
|
|
|
}
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
srvStatus, err := srv.Query()
|
2018-09-11 23:04:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, &ServiceErr{
|
|
|
|
Message: "could not query service",
|
|
|
|
Service: serviceName,
|
|
|
|
Err: err,
|
|
|
|
}
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
srvCfg, err := srv.Config()
|
2018-09-11 23:04:16 +00:00
|
|
|
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),
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
2018-09-11 23:04:16 +00:00
|
|
|
return serviceInfo, nil
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2018-09-11 23:04:16 +00:00
|
|
|
inputs.Add("win_services", func() telegraf.Input {
|
|
|
|
return &WinServices{
|
|
|
|
mgrProvider: &MgProvider{},
|
|
|
|
}
|
|
|
|
})
|
2017-08-07 21:36:15 +00:00
|
|
|
}
|