Skip to content

Commit

Permalink
Log access denied opening a service at debug level (influxdata#4674)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielnelson authored and Mathieu Lecarme committed Apr 17, 2020
1 parent 2cd5b99 commit 3cf08be
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 152 deletions.
19 changes: 10 additions & 9 deletions plugins/inputs/win_services/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Telegraf Plugin: win_services
Input plugin to report Windows services info.
# Windows Services Input Plugin

Reports information about Windows service status.

Monitoring some services may require running Telegraf with administrator privileges.

It requires that Telegraf must be running under the administrator privileges.
### Configuration:

```toml
Expand All @@ -25,15 +27,15 @@ The `state` field can have the following values:
- 3 - stop pending
- 4 - running
- 5 - continue pending
- 6 - pause pending
- 6 - pause pending
- 7 - paused

The `startup_mode` field can have the following values:
- 0 - boot start
- 1 - system start
- 2 - auto start
- 3 - demand start
- 4 - disabled
- 4 - disabled

### Tags:

Expand All @@ -43,14 +45,13 @@ The `startup_mode` field can have the following values:

### Example Output:
```
* Plugin: inputs.win_services, Collection 1
> win_services,host=WIN2008R2H401,display_name=Server,service_name=LanmanServer state=4i,startup_mode=2i 1500040669000000000
> win_services,display_name=Remote\ Desktop\ Services,service_name=TermService,host=WIN2008R2H401 state=1i,startup_mode=3i 1500040669000000000
win_services,host=WIN2008R2H401,display_name=Server,service_name=LanmanServer state=4i,startup_mode=2i 1500040669000000000
win_services,display_name=Remote\ Desktop\ Services,service_name=TermService,host=WIN2008R2H401 state=1i,startup_mode=3i 1500040669000000000
```
### TICK Scripts

A sample TICK script for a notification about a not running service.
It sends a notification whenever any service changes its state to be not _running_ and when it changes that state back to _running_.
It sends a notification whenever any service changes its state to be not _running_ and when it changes that state back to _running_.
The notification is sent via an HTTP POST call.

```
Expand Down
158 changes: 93 additions & 65 deletions plugins/inputs/win_services/win_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,52 @@ package win_services

import (
"fmt"
"log"
"os"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)

//WinService provides interface for svc.Service
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)
}

//WinServiceManagerProvider sets interface for acquiring manager instance, like mgr.Mgr
type WinServiceManagerProvider interface {
// ManagerProvider sets interface for acquiring manager instance, like mgr.Mgr
type ManagerProvider interface {
Connect() (WinServiceManager, error)
}

//WinServiceManager provides interface for mgr.Mgr
// 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
// WinSvcMgr is wrapper for mgr.Mgr implementing WinServiceManager interface
type WinSvcMgr struct {
realMgr *mgr.Mgr
}
Expand All @@ -45,7 +65,7 @@ func (m *WinSvcMgr) ListServices() ([]string, error) {
return m.realMgr.ListServices()
}

//MgProvider is an implementation of WinServiceManagerProvider interface returning WinSvcMgr
// MgProvider is an implementation of WinServiceManagerProvider interface returning WinSvcMgr
type MgProvider struct {
}

Expand All @@ -71,15 +91,14 @@ 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"`
mgrProvider WinServiceManagerProvider
mgrProvider ManagerProvider
}

type ServiceInfo struct {
ServiceName string
DisplayName string
State int
StartUpMode int
Error error
}

func (m *WinServices) Description() string {
Expand All @@ -91,93 +110,102 @@ func (m *WinServices) SampleConfig() string {
}

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()

serviceInfos, err := listServices(m.mgrProvider, m.ServiceNames)

serviceNames, err := listServices(scmgr, m.ServiceNames)
if err != nil {
return err
}

for _, service := range serviceInfos {
if service.Error == nil {
fields := make(map[string]interface{})
tags := make(map[string]string)

//display name could be empty, but still valid service
if len(service.DisplayName) > 0 {
tags["display_name"] = service.DisplayName
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)
}
tags["service_name"] = service.ServiceName
continue
}

fields["state"] = service.State
fields["startup_mode"] = service.StartUpMode
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
}

acc.AddFields("win_services", fields, tags)
} else {
acc.AddError(service.Error)
fields := map[string]interface{}{
"state": service.State,
"startup_mode": service.StartUpMode,
}
acc.AddFields("win_services", fields, tags)
}

return nil
}

//listServices gathers info about given services. If userServices is empty, it return info about all services on current Windows host. Any a critical error is returned.
func listServices(mgrProv WinServiceManagerProvider, userServices []string) ([]ServiceInfo, error) {
scmgr, err := mgrProv.Connect()
if err != nil {
return nil, fmt.Errorf("Could not open service manager: %s", err)
// listServices returns a list of services to gather.
func listServices(scmgr WinServiceManager, userServices []string) ([]string, error) {
if len(userServices) != 0 {
return userServices, nil
}
defer scmgr.Disconnect()

var serviceNames []string
if len(userServices) == 0 {
//Listing service names from system
serviceNames, err = scmgr.ListServices()
if err != nil {
return nil, fmt.Errorf("Could not list services: %s", err)
}
} else {
serviceNames = userServices
}
serviceInfos := make([]ServiceInfo, len(serviceNames))

for i, srvName := range serviceNames {
serviceInfos[i] = collectServiceInfo(scmgr, srvName)
names, err := scmgr.ListServices()
if err != nil {
return nil, fmt.Errorf("Could not list services: %s", err)
}

return serviceInfos, nil
return names, nil
}

//collectServiceInfo gathers info about a service from WindowsAPI
func collectServiceInfo(scmgr WinServiceManager, serviceName string) (serviceInfo ServiceInfo) {

serviceInfo.ServiceName = serviceName
// collectServiceInfo gathers info about a service.
func collectServiceInfo(scmgr WinServiceManager, serviceName string) (*ServiceInfo, error) {
srv, err := scmgr.OpenService(serviceName)
if err != nil {
serviceInfo.Error = fmt.Errorf("Could not open service '%s': %s", serviceName, err)
return
return nil, &ServiceErr{
Message: "could not open service",
Service: serviceName,
Err: err,
}
}
defer srv.Close()

srvStatus, err := srv.Query()
if err == nil {
serviceInfo.State = int(srvStatus.State)
} else {
serviceInfo.Error = fmt.Errorf("Could not query service '%s': %s", serviceName, err)
//finish collecting info on first found error
return
if err != nil {
return nil, &ServiceErr{
Message: "could not query service",
Service: serviceName,
Err: err,
}
}

srvCfg, err := srv.Config()
if err == nil {
serviceInfo.DisplayName = srvCfg.DisplayName
serviceInfo.StartUpMode = int(srvCfg.StartType)
} else {
serviceInfo.Error = fmt.Errorf("Could not get config of service '%s': %s", serviceName, err)
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
return serviceInfo, nil
}

func init() {
inputs.Add("win_services", func() telegraf.Input { return &WinServices{mgrProvider: &MgProvider{}} })
inputs.Add("win_services", func() telegraf.Input {
return &WinServices{
mgrProvider: &MgProvider{},
}
})
}
Loading

0 comments on commit 3cf08be

Please sign in to comment.