diff --git a/metricbeat/Makefile b/metricbeat/Makefile index 4447e80519c8..e1dd3efadfb1 100644 --- a/metricbeat/Makefile +++ b/metricbeat/Makefile @@ -47,7 +47,7 @@ configs: python-env @cat ${ES_BEATS}/metricbeat/_meta/common.reference.yml > _meta/beat.reference.yml @${PYTHON_ENV}/bin/python ${ES_BEATS}/script/config_collector.py --beat ${BEAT_NAME} --full $(PWD) >> _meta/beat.reference.yml @rm -rf modules.d && mkdir -p modules.d - @for MODULE in `find module -type d -maxdepth 1 -mindepth 1 -exec basename {} \;`; do cp -a $(PWD)/module/$$MODULE/_meta/config.yml modules.d/$$MODULE.yml.disabled; done + @for MODULE in `find module -maxdepth 1 -mindepth 1 -type d -exec basename {} \;`; do cp -a $(PWD)/module/$$MODULE/_meta/config.yml modules.d/$$MODULE.yml.disabled; done @chmod go-w modules.d/* @# Enable system by default: @if [ -f modules.d/system.yml.disabled ]; then mv modules.d/system.yml.disabled modules.d/system.yml; fi diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 508310c39646..c28bcb84e6f5 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -11304,6 +11304,56 @@ beta[] Module for Windows + +[float] +== service fields + +`service` contains the status for windows services. + + + +[float] +=== `windows.service.uptime.ms` + +type: long + +format: duration + +`uptime` contains the service uptime metric. + + +[float] +=== `windows.service.name` + +type: keyword + +The service name. + + +[float] +=== `windows.service.display_name` + +type: keyword + +The display name of the service. + + +[float] +=== `windows.service.start_type` + +type: keyword + +The start type of the service. The possible values are `ServiceAutoStart`, `ServiceBootStart`, `ServiceDemandStart`, `ServiceDisabled`, and `ServiceSystemStart`. + + +[float] +=== `windows.service.state` + +type: keyword + +The actual state of the service. The possible values are `ServiceContinuePending`, `ServicePausePending`, `ServicePaused`, `ServiceRunning`, `ServiceStartPending`, `ServiceStopPending`, and `ServiceStopped`. + + [[exported-fields-zookeeper]] == ZooKeeper fields diff --git a/metricbeat/docs/modules/windows.asciidoc b/metricbeat/docs/modules/windows.asciidoc index 6f5d4f963a3a..33a003d58af4 100644 --- a/metricbeat/docs/modules/windows.asciidoc +++ b/metricbeat/docs/modules/windows.asciidoc @@ -30,5 +30,9 @@ The following metricsets are available: * <> +* <> + include::windows/perfmon.asciidoc[] +include::windows/service.asciidoc[] + diff --git a/metricbeat/docs/modules/windows/service.asciidoc b/metricbeat/docs/modules/windows/service.asciidoc new file mode 100644 index 000000000000..5fbd1a56b71a --- /dev/null +++ b/metricbeat/docs/modules/windows/service.asciidoc @@ -0,0 +1,19 @@ +//// +This file is generated! See scripts/docs_collector.py +//// + +[[metricbeat-metricset-windows-service]] +include::../../../module/windows/service/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/windows/service/_meta/data.json[] +---- diff --git a/metricbeat/helper/privileges_windows.go b/metricbeat/helper/privileges_windows.go new file mode 100644 index 000000000000..20fa051b9399 --- /dev/null +++ b/metricbeat/helper/privileges_windows.go @@ -0,0 +1,96 @@ +package helper + +import ( + "sync" + "syscall" + + "github.com/pkg/errors" + + "github.com/elastic/gosigar/sys/windows" + + "github.com/elastic/beats/libbeat/logp" +) + +var once sync.Once + +// errMissingSeDebugPrivilege indicates that the SeDebugPrivilege is not +// present in the process's token. This is distinct from disabled. The token +// would be missing if the user does not have "Debug programs" rights. By +// default, only administrators and LocalSystem accounts have the privileges to +// debug programs. +var errMissingSeDebugPrivilege = errors.New("Metricbeat is running without " + + "SeDebugPrivilege, a Windows privilege that allows it to collect metrics " + + "from other processes. The user running Metricbeat may not have the " + + "appropriate privileges or the security policy disallows it.") + +// enableSeDebugPrivilege enables the SeDebugPrivilege if it is present in +// the process's token. +func enableSeDebugPrivilege() error { + self, err := syscall.GetCurrentProcess() + if err != nil { + return err + } + + var token syscall.Token + err = syscall.OpenProcessToken(self, syscall.TOKEN_QUERY|syscall.TOKEN_ADJUST_PRIVILEGES, &token) + if err != nil { + return err + } + + if err = windows.EnableTokenPrivileges(token, windows.SeDebugPrivilege); err != nil { + return errors.Wrap(err, "EnableTokenPrivileges failed") + } + + return nil +} + +// CheckAndEnableSeDebugPrivilege checks if the process's token has the +// SeDebugPrivilege and enables it if it is disabled. +func CheckAndEnableSeDebugPrivilege() error { + var err error + once.Do(func() { + err = checkAndEnableSeDebugPrivilege() + }) + return err +} + +func checkAndEnableSeDebugPrivilege() error { + info, err := windows.GetDebugInfo() + if err != nil { + return errors.Wrap(err, "GetDebugInfo failed") + } + logp.Info("Metricbeat process and system info: %v", info) + + seDebug, found := info.ProcessPrivs[windows.SeDebugPrivilege] + if !found { + return errMissingSeDebugPrivilege + } + + if seDebug.Enabled { + logp.Info("SeDebugPrivilege is enabled. %v", seDebug) + return nil + } + + if err = enableSeDebugPrivilege(); err != nil { + logp.Warn("Failure while attempting to enable SeDebugPrivilege. %v", err) + } + + info, err = windows.GetDebugInfo() + if err != nil { + return errors.Wrap(err, "GetDebugInfo failed") + } + + seDebug, found = info.ProcessPrivs[windows.SeDebugPrivilege] + if !found { + return errMissingSeDebugPrivilege + } + + if !seDebug.Enabled { + return errors.Errorf("Metricbeat failed to enable the "+ + "SeDebugPrivilege, a Windows privilege that allows it to collect "+ + "metrics from other processes. %v", seDebug) + } + + logp.Info("SeDebugPrivilege is now enabled. %v", seDebug) + return nil +} diff --git a/metricbeat/include/list.go b/metricbeat/include/list.go index f4dfb760b13e..693c10e71384 100644 --- a/metricbeat/include/list.go +++ b/metricbeat/include/list.go @@ -114,6 +114,7 @@ import ( _ "github.com/elastic/beats/metricbeat/module/vsphere/virtualmachine" _ "github.com/elastic/beats/metricbeat/module/windows" _ "github.com/elastic/beats/metricbeat/module/windows/perfmon" + _ "github.com/elastic/beats/metricbeat/module/windows/service" _ "github.com/elastic/beats/metricbeat/module/zookeeper" _ "github.com/elastic/beats/metricbeat/module/zookeeper/mntr" ) diff --git a/metricbeat/module/docker/helper.go b/metricbeat/module/docker/helper.go index d36cdd920c5b..3c950c95bd18 100644 --- a/metricbeat/module/docker/helper.go +++ b/metricbeat/module/docker/helper.go @@ -3,9 +3,9 @@ package docker import ( "strings" - "github.com/elastic/beats/libbeat/common" - "github.com/fsouza/go-dockerclient" + + "github.com/elastic/beats/libbeat/common" ) type Container struct { diff --git a/metricbeat/module/system/system_windows.go b/metricbeat/module/system/system_windows.go index f5b6669f1ef2..a9ba5466952a 100644 --- a/metricbeat/module/system/system_windows.go +++ b/metricbeat/module/system/system_windows.go @@ -1,90 +1,12 @@ package system import ( - "syscall" - - "github.com/pkg/errors" - "github.com/elastic/beats/libbeat/logp" - "github.com/elastic/gosigar/sys/windows" + "github.com/elastic/beats/metricbeat/helper" ) -// errMissingSeDebugPrivilege indicates that the SeDebugPrivilege is not -// present in the process's token. This is distinct from disabled. The token -// would be missing if the user does not have "Debug programs" rights. By -// default, only administrators and LocalSystem accounts have the privileges to -// debug programs. -var errMissingSeDebugPrivilege = errors.New("Metricbeat is running without " + - "SeDebugPrivilege, a Windows privilege that allows it to collect metrics " + - "from other processes. The user running Metricbeat may not have the " + - "appropriate privileges or the security policy disallows it.") - func initModule() { - if err := checkAndEnableSeDebugPrivilege(); err != nil { + if err := helper.CheckAndEnableSeDebugPrivilege(); err != nil { logp.Warn("%v", err) } } - -// checkAndEnableSeDebugPrivilege checks if the process's token has the -// SeDebugPrivilege and enables it if it is disabled. -func checkAndEnableSeDebugPrivilege() error { - info, err := windows.GetDebugInfo() - if err != nil { - return errors.Wrap(err, "GetDebugInfo failed") - } - logp.Info("Metricbeat process and system info: %v", info) - - seDebug, found := info.ProcessPrivs[windows.SeDebugPrivilege] - if !found { - return errMissingSeDebugPrivilege - } - - if seDebug.Enabled { - logp.Info("SeDebugPrivilege is enabled. %v", seDebug) - return nil - } - - if err = enableSeDebugPrivilege(); err != nil { - logp.Warn("Failure while attempting to enable SeDebugPrivilege. %v", err) - } - - info, err = windows.GetDebugInfo() - if err != nil { - return errors.Wrap(err, "GetDebugInfo failed") - } - - seDebug, found = info.ProcessPrivs[windows.SeDebugPrivilege] - if !found { - return errMissingSeDebugPrivilege - } - - if !seDebug.Enabled { - return errors.Errorf("Metricbeat failed to enable the "+ - "SeDebugPrivilege, a Windows privilege that allows it to collect "+ - "metrics from other processes. %v", seDebug) - } - - logp.Info("SeDebugPrivilege is now enabled. %v", seDebug) - return nil -} - -// enableSeDebugPrivilege enables the SeDebugPrivilege if it is present in -// the process's token. -func enableSeDebugPrivilege() error { - self, err := syscall.GetCurrentProcess() - if err != nil { - return err - } - - var token syscall.Token - err = syscall.OpenProcessToken(self, syscall.TOKEN_QUERY|syscall.TOKEN_ADJUST_PRIVILEGES, &token) - if err != nil { - return err - } - - if err = windows.EnableTokenPrivileges(token, windows.SeDebugPrivilege); err != nil { - return errors.Wrap(err, "EnableTokenPrivileges failed") - } - - return nil -} diff --git a/metricbeat/module/windows/perfmon/doc.go b/metricbeat/module/windows/perfmon/doc.go index e04267b29fda..12f593c82637 100644 --- a/metricbeat/module/windows/perfmon/doc.go +++ b/metricbeat/module/windows/perfmon/doc.go @@ -3,7 +3,7 @@ package perfmon //go:generate go run mkpdh_defs.go -//go:generate go run run.go -cmd "go tool cgo -godefs defs_pdh_windows.go" -goarch amd64 -output defs_pdh_windows_amd64.go -//go:generate go run run.go -cmd "go tool cgo -godefs defs_pdh_windows.go" -goarch 386 -output defs_pdh_windows_386.go +//go:generate go run ../run.go -cmd "go tool cgo -godefs defs_pdh_windows.go" -goarch amd64 -output defs_pdh_windows_amd64.go +//go:generate go run ../run.go -cmd "go tool cgo -godefs defs_pdh_windows.go" -goarch 386 -output defs_pdh_windows_386.go //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zpdh_windows.go pdh_windows.go //go:generate gofmt -w defs_pdh_windows_amd64.go defs_pdh_windows_386.go zpdh_windows.go diff --git a/metricbeat/module/windows/perfmon/run.go b/metricbeat/module/windows/run.go similarity index 100% rename from metricbeat/module/windows/perfmon/run.go rename to metricbeat/module/windows/run.go diff --git a/metricbeat/module/windows/service/_meta/data.json b/metricbeat/module/windows/service/_meta/data.json new file mode 100644 index 000000000000..645a547c878b --- /dev/null +++ b/metricbeat/module/windows/service/_meta/data.json @@ -0,0 +1,20 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "beat": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "metricset": { + "module": "windows", + "name": "service", + "rtt": 115 + }, + "windows": { + "service": { + "display_name": "AllJoyn-Routerdienst", + "name": "AJRouter", + "start_type": "ServiceDemandStart", + "state": "ServiceStopped" + } + } +} \ No newline at end of file diff --git a/metricbeat/module/windows/service/_meta/docs.asciidoc b/metricbeat/module/windows/service/_meta/docs.asciidoc new file mode 100644 index 000000000000..03d577a7fd4c --- /dev/null +++ b/metricbeat/module/windows/service/_meta/docs.asciidoc @@ -0,0 +1,14 @@ +=== windows service MetricSet + +The `service` metricset of the Windows module reads the status for Windows +Services. + +[float] +=== Configuration + +[source,yaml] +---- +- module: windows + metricsets: ["service"] + period: 10s +---- \ No newline at end of file diff --git a/metricbeat/module/windows/service/_meta/fields.yml b/metricbeat/module/windows/service/_meta/fields.yml new file mode 100644 index 000000000000..f46945da7209 --- /dev/null +++ b/metricbeat/module/windows/service/_meta/fields.yml @@ -0,0 +1,34 @@ +- name: service + type: group + description: > + `service` contains the status for windows services. + fields: + - name: uptime.ms + type: long + format: duration + input_format: milliseconds + description: > + `uptime` contains the service uptime metric. + + - name: name + type: keyword + description: > + The service name. + + - name: display_name + type: keyword + description: > + The display name of the service. + + - name: start_type + type: keyword + description: > + The start type of the service. + The possible values are `ServiceAutoStart`, `ServiceBootStart`, `ServiceDemandStart`, `ServiceDisabled`, and `ServiceSystemStart`. + + - name: state + type: keyword + description: > + The actual state of the service. + The possible values are `ServiceContinuePending`, `ServicePausePending`, `ServicePaused`, `ServiceRunning`, `ServiceStartPending`, + `ServiceStopPending`, and `ServiceStopped`. \ No newline at end of file diff --git a/metricbeat/module/windows/service/defs_service_windows.go b/metricbeat/module/windows/service/defs_service_windows.go new file mode 100644 index 000000000000..6ba9a52e5322 --- /dev/null +++ b/metricbeat/module/windows/service/defs_service_windows.go @@ -0,0 +1,187 @@ +// +build ignore + +package service + +/* +#include +#cgo LDFLAGS: -ladvapi32 +*/ +import "C" + +type ServiceErrno uintptr + +// Service Error Codes +const ( + SERVICE_ERROR_ACCESS_DENIED ServiceErrno = C.ERROR_ACCESS_DENIED + SERVICE_ERROR_MORE_DATA ServiceErrno = C.ERROR_MORE_DATA + SERVICE_ERROR_INVALID_PARAMETER ServiceErrno = C.ERROR_INVALID_PARAMETER + SERVICE_ERROR_INVALID_HANDLE ServiceErrno = C.ERROR_INVALID_HANDLE + SERVICE_ERROR_INVALID_LEVEL ServiceErrno = C.ERROR_INVALID_LEVEL + SERVICE_ERROR_INVALID_NAME ServiceErrno = C.ERROR_INVALID_NAME + SERVICE_ERROR_SHUTDOWN_IN_PROGRESS ServiceErrno = C.ERROR_SHUTDOWN_IN_PROGRESS + SERVICE_ERROR_DATABASE_DOES_NOT_EXIST ServiceErrno = C.ERROR_DATABASE_DOES_NOT_EXIST + SERVICE_ERROR_INSUFFICIENT_BUFFER ServiceErrno = C.ERROR_INSUFFICIENT_BUFFER + SERVICE_ERROR_SERVICE_DOES_NOT_EXIST ServiceErrno = C.ERROR_SERVICE_DOES_NOT_EXIST +) + +type ServiceErrorControl uint32 + +// Servcie Error Controls +const ( + SERVICE_ERROR_CRITICAL ServiceErrno = C.SERVICE_ERROR_CRITICAL + SERVICE_ERROR_IGNORE ServiceErrno = C.SERVICE_ERROR_IGNORE + SERVICE_ERROR_NORMAL ServiceErrno = C.SERVICE_ERROR_NORMAL + SERVICE_ERROR_SEVERE ServiceErrno = C.SERVICE_ERROR_SEVERE +) + +var serviceErrors = map[ServiceErrno]struct{}{ + SERVICE_ERROR_ACCESS_DENIED: struct{}{}, + SERVICE_ERROR_MORE_DATA: struct{}{}, + SERVICE_ERROR_INVALID_PARAMETER: struct{}{}, + SERVICE_ERROR_INVALID_HANDLE: struct{}{}, + SERVICE_ERROR_INVALID_LEVEL: struct{}{}, + SERVICE_ERROR_INVALID_NAME: struct{}{}, + SERVICE_ERROR_SHUTDOWN_IN_PROGRESS: struct{}{}, + SERVICE_ERROR_DATABASE_DOES_NOT_EXIST: struct{}{}, + SERVICE_ERROR_INSUFFICIENT_BUFFER: struct{}{}, + SERVICE_ERROR_CRITICAL: struct{}{}, + SERVICE_ERROR_IGNORE: struct{}{}, + SERVICE_ERROR_NORMAL: struct{}{}, + SERVICE_ERROR_SEVERE: struct{}{}, + SERVICE_ERROR_SERVICE_DOES_NOT_EXIST: struct{}{}, +} + +type ServiceType uint32 + +// ServiceTypes +const ( + // Services of type SERVICE_KERNEL_DRIVER and SERVICE_FILE_SYSTEM_DRIVER. + ServiceDriver ServiceType = C.SERVICE_DRIVER + // File system driver services. + ServiceFileSystemDriver ServiceType = C.SERVICE_FILE_SYSTEM_DRIVER + // Driver services. + ServiceKernelDriver ServiceType = C.SERVICE_KERNEL_DRIVER + // Services of type SERVICE_WIN32_OWN_PROCESS and SERVICE_WIN32_SHARE_PROCESS. + ServiceWin32 ServiceType = C.SERVICE_WIN32 + // Services that run in their own processes. + ServiceWin32OwnProcess ServiceType = C.SERVICE_WIN32_OWN_PROCESS + // Services that share a process with one or more other services. + ServiceWin32Shareprocess ServiceType = C.SERVICE_WIN32_SHARE_PROCESS + ServiceInteractiveProcess ServiceType = C.SERVICE_INTERACTIVE_PROCESS +) + +type ServiceState uint32 + +// ServiceStates +const ( + ServiceContinuePending ServiceState = C.SERVICE_CONTINUE_PENDING + ServicePausePending ServiceState = C.SERVICE_PAUSE_PENDING + ServicePaused ServiceState = C.SERVICE_PAUSED + ServiceRunning ServiceState = C.SERVICE_RUNNING + ServiceStartPending ServiceState = C.SERVICE_START_PENDING + ServiceStopPending ServiceState = C.SERVICE_STOP_PENDING + ServiceStopped ServiceState = C.SERVICE_STOPPED +) + +type ServiceEnumState uint32 + +//Service Enum States +const ( + // Enumerates services that are in the following states: SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING, SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, and SERVICE_PAUSED. + ServiceActive ServiceEnumState = C.SERVICE_ACTIVE + // Enumerates services that are in the SERVICE_STOPPED state. + ServiceInActive ServiceEnumState = C.SERVICE_INACTIVE + // Combines the SERVICE_ACTIVE and SERVICE_INACTIVE states. + ServiceStateAll ServiceEnumState = C.SERVICE_STATE_ALL +) + +type ServiceSCMAccessRight uint32 + +// Access Rights for the Service Control Manager +const ( + // Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table. + ScManagerAllAccess ServiceSCMAccessRight = C.SC_MANAGER_ALL_ACCESS + // Required to connect to the service control manager. + ScManagerConnect ServiceSCMAccessRight = C.SC_MANAGER_CONNECT + // Required to call the EnumServicesStatus or EnumServicesStatusEx function to list the services that are in the database. + ScManagerEnumerateService ServiceSCMAccessRight = C.SC_MANAGER_ENUMERATE_SERVICE + // Required to call the QueryServiceLockStatus function to retrieve the lock status information for the database. + ScManagerQueryLockStatus ServiceSCMAccessRight = C.SC_MANAGER_QUERY_LOCK_STATUS +) + +type ServiceAccessRight uint32 + +// Access Rights for a Service +const ( + // Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table. + ServiceAllAccess ServiceAccessRight = C.SERVICE_ALL_ACCESS + // Required to call the ChangeServiceConfig or ChangeServiceConfig2 function to change the service configuration. Because this grants the caller the right to change the executable file that the system runs, it should be granted only to administrators. + ServcieChangeConfig ServiceAccessRight = C.SERVICE_CHANGE_CONFIG + // Required to call the EnumDependentServices function to enumerate all the services dependent on the service. + ServiceEnumerateDependents ServiceAccessRight = C.SERVICE_ENUMERATE_DEPENDENTS + // Required to call the ControlService function to ask the service to report its status immediately. + ServiceInterrogate ServiceAccessRight = C.SERVICE_INTERROGATE + // Required to call the ControlService function to pause or continue the service. + ServicePauseContinue ServiceAccessRight = C.SERVICE_PAUSE_CONTINUE + // Required to call the QueryServiceConfig and QueryServiceConfig2 functions to query the service configuration. + ServiceQueryConfig ServiceAccessRight = C.SERVICE_QUERY_CONFIG + // Required to call the QueryServiceStatus or QueryServiceStatusEx function to ask the service control manager about the status of the service. + ServiceQueryStatus ServiceAccessRight = C.SERVICE_QUERY_STATUS + // Required to call the StartService function to start the service. + ServiceStart ServiceAccessRight = C.SERVICE_START + // Required to call the ControlService function to stop the service. + ServiceStop ServiceAccessRight = C.SERVICE_STOP + // Required to call the ControlService function to specify a user-defined control code. + ServiceUserDefinedControl ServiceAccessRight = C.SERVICE_USER_DEFINED_CONTROL +) + +type ServiceInfoLevel uint32 + +// Service Info Levels +const ( + ScEnumProcessInfo ServiceInfoLevel = C.SC_ENUM_PROCESS_INFO +) + +type ServiceStartType uint32 + +// Service Start Types +const ( + // A service started automatically by the service control manager during system startup. + ServiceAutoStart ServiceStartType = C.SERVICE_AUTO_START + // A device driver started by the system loader. This value is valid only for driver services. + ServiceBootStart ServiceStartType = C.SERVICE_BOOT_START + // A service started by the service control manager when a process calls the StartService function. + ServiceDemandStart ServiceStartType = C.SERVICE_DEMAND_START + // A service that cannot be started. Attempts to start the service result in the error code ERROR_SERVICE_DISABLED. + ServiceDisabled ServiceStartType = C.SERVICE_DISABLED + // A device driver started by the IoInitSystem function. This value is valid only for driver services. + ServiceSystemStart ServiceStartType = C.SERVICE_SYSTEM_START +) + +type ProcessAccessRight uint32 + +const ( + ProcessAllAccess ProcessAccessRight = C.PROCESS_ALL_ACCESS + ProcessCreateProcess ProcessAccessRight = C.PROCESS_CREATE_PROCESS + ProcessCreateThread ProcessAccessRight = C.PROCESS_CREATE_THREAD + ProcessDupHandle ProcessAccessRight = C.PROCESS_DUP_HANDLE + ProcessQueryInformation ProcessAccessRight = C.PROCESS_QUERY_INFORMATION + ProcessQueryLimitInformation ProcessAccessRight = C.PROCESS_QUERY_LIMITED_INFORMATION + ProcessSetInformation ProcessAccessRight = C.PROCESS_SET_INFORMATION + ProcessSetQuota ProcessAccessRight = C.PROCESS_SET_QUOTA + ProcessSuspendResume ProcessAccessRight = C.PROCESS_SUSPEND_RESUME + ProcessTerminate ProcessAccessRight = C.PROCESS_TERMINATE + ProcessVmOperation ProcessAccessRight = C.PROCESS_VM_OPERATION + ProcessVmRead ProcessAccessRight = C.PROCESS_VM_READ + ProcessVmWrite ProcessAccessRight = C.PROCESS_VM_WRITE + ProcessSynchronize ProcessAccessRight = C.SYNCHRONIZE +) + +// Contains process status information for a service. +type ServiceStatusProcess C.SERVICE_STATUS_PROCESS + +// Contains the name of a service in a service control manager database and information about the service. +type EnumServiceStatusProcess C.ENUM_SERVICE_STATUS_PROCESS + +//Contains configuration information for an installed service. +type QueryServiceConfig C.QUERY_SERVICE_CONFIG diff --git a/metricbeat/module/windows/service/defs_service_windows_386.go b/metricbeat/module/windows/service/defs_service_windows_386.go new file mode 100644 index 000000000000..f7acf085a946 --- /dev/null +++ b/metricbeat/module/windows/service/defs_service_windows_386.go @@ -0,0 +1,189 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo.exe -godefs defs_service_windows.go + +package service + +type ServiceErrno uintptr + +const ( + SERVICE_ERROR_ACCESS_DENIED ServiceErrno = 0x5 + SERVICE_ERROR_MORE_DATA ServiceErrno = 0xea + SERVICE_ERROR_INVALID_PARAMETER ServiceErrno = 0x57 + SERVICE_ERROR_INVALID_HANDLE ServiceErrno = 0x6 + SERVICE_ERROR_INVALID_LEVEL ServiceErrno = 0x7c + SERVICE_ERROR_INVALID_NAME ServiceErrno = 0x7b + SERVICE_ERROR_SHUTDOWN_IN_PROGRESS ServiceErrno = 0x45b + SERVICE_ERROR_DATABASE_DOES_NOT_EXIST ServiceErrno = 0x429 + SERVICE_ERROR_INSUFFICIENT_BUFFER ServiceErrno = 0x7a + SERVICE_ERROR_SERVICE_DOES_NOT_EXIST ServiceErrno = 0x424 +) + +type ServiceErrorControl uint32 + +const ( + SERVICE_ERROR_CRITICAL ServiceErrno = 0x3 + SERVICE_ERROR_IGNORE ServiceErrno = 0x0 + SERVICE_ERROR_NORMAL ServiceErrno = 0x1 + SERVICE_ERROR_SEVERE ServiceErrno = 0x2 +) + +var serviceErrors = map[ServiceErrno]struct{}{ + SERVICE_ERROR_ACCESS_DENIED: struct{}{}, + SERVICE_ERROR_MORE_DATA: struct{}{}, + SERVICE_ERROR_INVALID_PARAMETER: struct{}{}, + SERVICE_ERROR_INVALID_HANDLE: struct{}{}, + SERVICE_ERROR_INVALID_LEVEL: struct{}{}, + SERVICE_ERROR_INVALID_NAME: struct{}{}, + SERVICE_ERROR_SHUTDOWN_IN_PROGRESS: struct{}{}, + SERVICE_ERROR_DATABASE_DOES_NOT_EXIST: struct{}{}, + SERVICE_ERROR_INSUFFICIENT_BUFFER: struct{}{}, + SERVICE_ERROR_CRITICAL: struct{}{}, + SERVICE_ERROR_IGNORE: struct{}{}, + SERVICE_ERROR_NORMAL: struct{}{}, + SERVICE_ERROR_SEVERE: struct{}{}, + SERVICE_ERROR_SERVICE_DOES_NOT_EXIST: struct{}{}, +} + +type ServiceType uint32 + +const ( + ServiceDriver ServiceType = 0xb + + ServiceFileSystemDriver ServiceType = 0x2 + + ServiceKernelDriver ServiceType = 0x1 + + ServiceWin32 ServiceType = 0x30 + + ServiceWin32OwnProcess ServiceType = 0x10 + + ServiceWin32Shareprocess ServiceType = 0x20 + ServiceInteractiveProcess ServiceType = 0x100 +) + +type ServiceState uint32 + +const ( + ServiceContinuePending ServiceState = 0x5 + ServicePausePending ServiceState = 0x6 + ServicePaused ServiceState = 0x7 + ServiceRunning ServiceState = 0x4 + ServiceStartPending ServiceState = 0x2 + ServiceStopPending ServiceState = 0x3 + ServiceStopped ServiceState = 0x1 +) + +type ServiceEnumState uint32 + +const ( + ServiceActive ServiceEnumState = 0x1 + + ServiceInActive ServiceEnumState = 0x2 + + ServiceStateAll ServiceEnumState = 0x3 +) + +type ServiceSCMAccessRight uint32 + +const ( + ScManagerAllAccess ServiceSCMAccessRight = 0xf003f + + ScManagerConnect ServiceSCMAccessRight = 0x1 + + ScManagerEnumerateService ServiceSCMAccessRight = 0x4 + + ScManagerQueryLockStatus ServiceSCMAccessRight = 0x10 +) + +type ServiceAccessRight uint32 + +const ( + ServiceAllAccess ServiceAccessRight = 0xf01ff + + ServcieChangeConfig ServiceAccessRight = 0x2 + + ServiceEnumerateDependents ServiceAccessRight = 0x8 + + ServiceInterrogate ServiceAccessRight = 0x80 + + ServicePauseContinue ServiceAccessRight = 0x40 + + ServiceQueryConfig ServiceAccessRight = 0x1 + + ServiceQueryStatus ServiceAccessRight = 0x4 + + ServiceStart ServiceAccessRight = 0x10 + + ServiceStop ServiceAccessRight = 0x20 + + ServiceUserDefinedControl ServiceAccessRight = 0x100 +) + +type ServiceInfoLevel uint32 + +const ( + ScEnumProcessInfo ServiceInfoLevel = 0x0 +) + +type ServiceStartType uint32 + +const ( + ServiceAutoStart ServiceStartType = 0x2 + + ServiceBootStart ServiceStartType = 0x0 + + ServiceDemandStart ServiceStartType = 0x3 + + ServiceDisabled ServiceStartType = 0x4 + + ServiceSystemStart ServiceStartType = 0x1 +) + +type ProcessAccessRight uint32 + +const ( + ProcessAllAccess ProcessAccessRight = 0x1f0fff + ProcessCreateProcess ProcessAccessRight = 0x80 + ProcessCreateThread ProcessAccessRight = 0x2 + ProcessDupHandle ProcessAccessRight = 0x40 + ProcessQueryInformation ProcessAccessRight = 0x400 + ProcessQueryLimitInformation ProcessAccessRight = 0x1000 + ProcessSetInformation ProcessAccessRight = 0x200 + ProcessSetQuota ProcessAccessRight = 0x100 + ProcessSuspendResume ProcessAccessRight = 0x800 + ProcessTerminate ProcessAccessRight = 0x1 + ProcessVmOperation ProcessAccessRight = 0x8 + ProcessVmRead ProcessAccessRight = 0x10 + ProcessVmWrite ProcessAccessRight = 0x20 + ProcessSynchronize ProcessAccessRight = 0x100000 +) + +type ServiceStatusProcess struct { + DwServiceType uint32 + DwCurrentState uint32 + DwControlsAccepted uint32 + DwWin32ExitCode uint32 + DwServiceSpecificExitCode uint32 + DwCheckPoint uint32 + DwWaitHint uint32 + DwProcessId uint32 + DwServiceFlags uint32 +} + +type EnumServiceStatusProcess struct { + LpServiceName *int8 + LpDisplayName *int8 + ServiceStatusProcess ServiceStatusProcess +} + +type QueryServiceConfig struct { + DwServiceType uint32 + DwStartType uint32 + DwErrorControl uint32 + LpBinaryPathName *int8 + LpLoadOrderGroup *int8 + DwTagId uint32 + LpDependencies *int8 + LpServiceStartName *int8 + LpDisplayName *int8 +} diff --git a/metricbeat/module/windows/service/defs_service_windows_amd64.go b/metricbeat/module/windows/service/defs_service_windows_amd64.go new file mode 100644 index 000000000000..ad307ec2a9a5 --- /dev/null +++ b/metricbeat/module/windows/service/defs_service_windows_amd64.go @@ -0,0 +1,192 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo.exe -godefs defs_service_windows.go + +package service + +type ServiceErrno uintptr + +const ( + SERVICE_ERROR_ACCESS_DENIED ServiceErrno = 0x5 + SERVICE_ERROR_MORE_DATA ServiceErrno = 0xea + SERVICE_ERROR_INVALID_PARAMETER ServiceErrno = 0x57 + SERVICE_ERROR_INVALID_HANDLE ServiceErrno = 0x6 + SERVICE_ERROR_INVALID_LEVEL ServiceErrno = 0x7c + SERVICE_ERROR_INVALID_NAME ServiceErrno = 0x7b + SERVICE_ERROR_SHUTDOWN_IN_PROGRESS ServiceErrno = 0x45b + SERVICE_ERROR_DATABASE_DOES_NOT_EXIST ServiceErrno = 0x429 + SERVICE_ERROR_INSUFFICIENT_BUFFER ServiceErrno = 0x7a + SERVICE_ERROR_SERVICE_DOES_NOT_EXIST ServiceErrno = 0x424 +) + +type ServiceErrorControl uint32 + +const ( + SERVICE_ERROR_CRITICAL ServiceErrno = 0x3 + SERVICE_ERROR_IGNORE ServiceErrno = 0x0 + SERVICE_ERROR_NORMAL ServiceErrno = 0x1 + SERVICE_ERROR_SEVERE ServiceErrno = 0x2 +) + +var serviceErrors = map[ServiceErrno]struct{}{ + SERVICE_ERROR_ACCESS_DENIED: struct{}{}, + SERVICE_ERROR_MORE_DATA: struct{}{}, + SERVICE_ERROR_INVALID_PARAMETER: struct{}{}, + SERVICE_ERROR_INVALID_HANDLE: struct{}{}, + SERVICE_ERROR_INVALID_LEVEL: struct{}{}, + SERVICE_ERROR_INVALID_NAME: struct{}{}, + SERVICE_ERROR_SHUTDOWN_IN_PROGRESS: struct{}{}, + SERVICE_ERROR_DATABASE_DOES_NOT_EXIST: struct{}{}, + SERVICE_ERROR_INSUFFICIENT_BUFFER: struct{}{}, + SERVICE_ERROR_CRITICAL: struct{}{}, + SERVICE_ERROR_IGNORE: struct{}{}, + SERVICE_ERROR_NORMAL: struct{}{}, + SERVICE_ERROR_SEVERE: struct{}{}, + SERVICE_ERROR_SERVICE_DOES_NOT_EXIST: struct{}{}, +} + +type ServiceType uint32 + +const ( + ServiceDriver ServiceType = 0xb + + ServiceFileSystemDriver ServiceType = 0x2 + + ServiceKernelDriver ServiceType = 0x1 + + ServiceWin32 ServiceType = 0x30 + + ServiceWin32OwnProcess ServiceType = 0x10 + + ServiceWin32Shareprocess ServiceType = 0x20 + ServiceInteractiveProcess ServiceType = 0x100 +) + +type ServiceState uint32 + +const ( + ServiceContinuePending ServiceState = 0x5 + ServicePausePending ServiceState = 0x6 + ServicePaused ServiceState = 0x7 + ServiceRunning ServiceState = 0x4 + ServiceStartPending ServiceState = 0x2 + ServiceStopPending ServiceState = 0x3 + ServiceStopped ServiceState = 0x1 +) + +type ServiceEnumState uint32 + +const ( + ServiceActive ServiceEnumState = 0x1 + + ServiceInActive ServiceEnumState = 0x2 + + ServiceStateAll ServiceEnumState = 0x3 +) + +type ServiceSCMAccessRight uint32 + +const ( + ScManagerAllAccess ServiceSCMAccessRight = 0xf003f + + ScManagerConnect ServiceSCMAccessRight = 0x1 + + ScManagerEnumerateService ServiceSCMAccessRight = 0x4 + + ScManagerQueryLockStatus ServiceSCMAccessRight = 0x10 +) + +type ServiceAccessRight uint32 + +const ( + ServiceAllAccess ServiceAccessRight = 0xf01ff + + ServcieChangeConfig ServiceAccessRight = 0x2 + + ServiceEnumerateDependents ServiceAccessRight = 0x8 + + ServiceInterrogate ServiceAccessRight = 0x80 + + ServicePauseContinue ServiceAccessRight = 0x40 + + ServiceQueryConfig ServiceAccessRight = 0x1 + + ServiceQueryStatus ServiceAccessRight = 0x4 + + ServiceStart ServiceAccessRight = 0x10 + + ServiceStop ServiceAccessRight = 0x20 + + ServiceUserDefinedControl ServiceAccessRight = 0x100 +) + +type ServiceInfoLevel uint32 + +const ( + ScEnumProcessInfo ServiceInfoLevel = 0x0 +) + +type ServiceStartType uint32 + +const ( + ServiceAutoStart ServiceStartType = 0x2 + + ServiceBootStart ServiceStartType = 0x0 + + ServiceDemandStart ServiceStartType = 0x3 + + ServiceDisabled ServiceStartType = 0x4 + + ServiceSystemStart ServiceStartType = 0x1 +) + +type ProcessAccessRight uint32 + +const ( + ProcessAllAccess ProcessAccessRight = 0x1f0fff + ProcessCreateProcess ProcessAccessRight = 0x80 + ProcessCreateThread ProcessAccessRight = 0x2 + ProcessDupHandle ProcessAccessRight = 0x40 + ProcessQueryInformation ProcessAccessRight = 0x400 + ProcessQueryLimitInformation ProcessAccessRight = 0x1000 + ProcessSetInformation ProcessAccessRight = 0x200 + ProcessSetQuota ProcessAccessRight = 0x100 + ProcessSuspendResume ProcessAccessRight = 0x800 + ProcessTerminate ProcessAccessRight = 0x1 + ProcessVmOperation ProcessAccessRight = 0x8 + ProcessVmRead ProcessAccessRight = 0x10 + ProcessVmWrite ProcessAccessRight = 0x20 + ProcessSynchronize ProcessAccessRight = 0x100000 +) + +type ServiceStatusProcess struct { + DwServiceType uint32 + DwCurrentState uint32 + DwControlsAccepted uint32 + DwWin32ExitCode uint32 + DwServiceSpecificExitCode uint32 + DwCheckPoint uint32 + DwWaitHint uint32 + DwProcessId uint32 + DwServiceFlags uint32 +} + +type EnumServiceStatusProcess struct { + LpServiceName *int8 + LpDisplayName *int8 + ServiceStatusProcess ServiceStatusProcess + Pad_cgo_0 [4]byte +} + +type QueryServiceConfig struct { + DwServiceType uint32 + DwStartType uint32 + DwErrorControl uint32 + Pad_cgo_0 [4]byte + LpBinaryPathName *int8 + LpLoadOrderGroup *int8 + DwTagId uint32 + Pad_cgo_1 [4]byte + LpDependencies *int8 + LpServiceStartName *int8 + LpDisplayName *int8 +} diff --git a/metricbeat/module/windows/service/doc.go b/metricbeat/module/windows/service/doc.go new file mode 100644 index 000000000000..9c56ffd3e3b4 --- /dev/null +++ b/metricbeat/module/windows/service/doc.go @@ -0,0 +1,7 @@ +// Package service implements a Metricbeat metricset for reading Windows Services +package service + +//go:generate go run ../run.go -cmd "go tool cgo -godefs defs_service_windows.go" -goarch amd64 -output defs_service_windows_amd64.go +//go:generate go run ../run.go -cmd "go tool cgo -godefs defs_service_windows.go" -goarch 386 -output defs_service_windows_386.go +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zservice_windows.go service_windows.go +//go:generate gofmt -w defs_service_windows_amd64.go defs_service_windows_386.go diff --git a/metricbeat/module/windows/service/service.go b/metricbeat/module/windows/service/service.go new file mode 100644 index 000000000000..c83571a0dbbe --- /dev/null +++ b/metricbeat/module/windows/service/service.go @@ -0,0 +1,55 @@ +// +build windows + +package service + +import ( + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/metricbeat/mb" +) + +// init registers the MetricSet with the central registry. +// The New method will be called after the setup of the module and before starting to fetch data +func init() { + if err := mb.Registry.AddMetricSet("windows", "service", New); err != nil { + panic(err) + } +} + +// MetricSet type defines all fields of the MetricSet +// As a minimum it must inherit the mb.BaseMetricSet fields, but can be extended with +// additional entries. These variables can be used to persist data or configuration between +// multiple fetch calls. +type MetricSet struct { + mb.BaseMetricSet + reader *ServiceReader +} + +// New create a new instance of the MetricSet +// Part of new is also setting up the configuration by processing additional +// configuration entries if needed. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + logp.Warn("EXPERIMENTAL: The windows service metricset is experimental") + + reader, err := NewServiceReader() + if err != nil { + return nil, err + } + + return &MetricSet{ + BaseMetricSet: base, + reader: reader, + }, nil +} + +// Fetch methods implements the data gathering and data conversion to the right format +// It returns the event which is then forward to the output. In case of an error, a +// descriptive error must be returned. +func (m *MetricSet) Fetch() ([]common.MapStr, error) { + services, err := m.reader.Read() + if err != nil { + return nil, err + } + + return services, nil +} diff --git a/metricbeat/module/windows/service/service_integration_windows_test.go b/metricbeat/module/windows/service/service_integration_windows_test.go new file mode 100644 index 000000000000..fff23c112f0b --- /dev/null +++ b/metricbeat/module/windows/service/service_integration_windows_test.go @@ -0,0 +1,37 @@ +// +build integration windows + +package service + +import ( + "testing" + + mbtest "github.com/elastic/beats/metricbeat/mb/testing" +) + +func TestData(t *testing.T) { + config := map[string]interface{}{ + "module": "windows", + "metricsets": []string{"service"}, + } + + f := mbtest.NewEventsFetcher(t, config) + f.Fetch() + err := mbtest.WriteEvents(f, t) + if err != nil { + t.Fatal("write", err) + } +} + +func TestReadService(t *testing.T) { + reader, err := NewServiceReader() + if err != nil { + t.Fatal(err) + } + + services, err := reader.Read() + if err != nil { + t.Fatal(err) + } + + t.Log(services) +} diff --git a/metricbeat/module/windows/service/service_windows.go b/metricbeat/module/windows/service/service_windows.go new file mode 100644 index 000000000000..2c3acb0539ee --- /dev/null +++ b/metricbeat/module/windows/service/service_windows.go @@ -0,0 +1,350 @@ +// +build windows + +package service + +import ( + "bytes" + "strconv" + "syscall" + "time" + "unicode/utf16" + "unsafe" + + "github.com/pkg/errors" + "golang.org/x/sys/windows" + + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/winlogbeat/sys" + "github.com/elastic/gosigar" +) + +// Windows API calls +//sys _OpenSCManager(machineName *uint16, databaseName *uint16, desiredAcces ServiceSCMAccessRight) (handle ServiceDatabaseHandle, err error) = advapi32.OpenSCManagerW +//sys _EnumServicesStatusEx(handle ServiceDatabaseHandle, infoLevel ServiceInfoLevel, serviceType ServiceType, serviceState ServiceEnumState, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uintptr, groupName *uintptr) (err error) [failretval==0] = advapi32.EnumServicesStatusExW +//sys _OpenService(handle ServiceDatabaseHandle, serviceName *uint16, desiredAccess ServiceAccessRight) (serviceHandle ServiceHandle, err error) = advapi32.OpenServiceW +//sys _QueryServiceConfig(serviceHandle ServiceHandle, serviceConfig *byte, bufSize uint32, bytesNeeded *uint32) (err error) [failretval==0] = advapi32.QueryServiceConfigW +//sys _CloseServiceHandle(handle uintptr) (err error) = advapi32.CloseServiceHandle + +var ( + sizeofEnumServiceStatusProcess = (int)(unsafe.Sizeof(EnumServiceStatusProcess{})) +) + +type ServiceDatabaseHandle uintptr + +type ServiceHandle uintptr + +type ProcessHandle uintptr + +var serviceStates = map[ServiceState]string{ + ServiceContinuePending: "ServiceContinuePending", + ServicePausePending: "ServicePausePending", + ServicePaused: "ServicePaused", + ServiceRunning: "ServiceRunning", + ServiceStartPending: "ServiceStartPending", + ServiceStopPending: "ServiceStopPending", + ServiceStopped: "ServiceStopped", +} + +var serviceStartTypes = map[ServiceStartType]string{ + ServiceAutoStart: "ServiceAutoStart", + ServiceBootStart: "ServiceBootStart", + ServiceDemandStart: "ServiceDemandStart", + ServiceDisabled: "ServiceDisabled", + ServiceSystemStart: "ServiceSystemStart", +} + +func (state ServiceState) String() string { + if val, ok := serviceStates[state]; ok { + return val + } + return "" +} + +type ServiceStatus struct { + DisplayName string + ServiceName string + CurrentState string + StartType string + Uptime time.Duration +} + +type ServiceReader struct { + handle ServiceDatabaseHandle + state ServiceEnumState +} + +var InvalidServiceDatabaseHandle = ^ServiceDatabaseHandle(0) +var InvalidServiceHandle = ^ServiceHandle(0) + +func OpenSCManager(machineName string, databaseName string, desiredAccess ServiceSCMAccessRight) (ServiceDatabaseHandle, error) { + var machineNamePtr *uint16 + if machineName != "" { + var err error + machineNamePtr, err = syscall.UTF16PtrFromString(machineName) + if err != nil { + return InvalidServiceDatabaseHandle, err + } + } + + var databaseNamePtr *uint16 + if databaseName != "" { + var err error + databaseNamePtr, err = syscall.UTF16PtrFromString(databaseName) + if err != nil { + return InvalidServiceDatabaseHandle, err + } + } + + handle, err := _OpenSCManager(machineNamePtr, databaseNamePtr, desiredAccess) + if err != nil { + return InvalidServiceDatabaseHandle, ServiceErrno(err.(syscall.Errno)) + } + + return handle, nil +} + +func OpenService(handle ServiceDatabaseHandle, serviceName string, desiredAccess ServiceAccessRight) (ServiceHandle, error) { + var serviceNamePtr *uint16 + if serviceName != "" { + var err error + serviceNamePtr, err = syscall.UTF16PtrFromString(serviceName) + if err != nil { + return InvalidServiceHandle, err + } + } + + serviceHandle, err := _OpenService(handle, serviceNamePtr, desiredAccess) + if err != nil { + return InvalidServiceHandle, ServiceErrno(err.(syscall.Errno)) + } + + return serviceHandle, nil +} + +func getServiceStates(handle ServiceDatabaseHandle, state ServiceEnumState) ([]ServiceStatus, error) { + var servicesReturned uint32 + var servicesBuffer []byte + + for { + var bytesNeeded uint32 + var buf *byte + if len(servicesBuffer) > 0 { + buf = &servicesBuffer[0] + } + + if err := _EnumServicesStatusEx(handle, ScEnumProcessInfo, ServiceWin32, state, buf, uint32(len(servicesBuffer)), &bytesNeeded, &servicesReturned, nil, nil); err != nil { + if ServiceErrno(err.(syscall.Errno)) == SERVICE_ERROR_MORE_DATA { + // Increase buffer size and retry. + servicesBuffer = make([]byte, len(servicesBuffer)+int(bytesNeeded)) + continue + } + return nil, ServiceErrno(err.(syscall.Errno)) + } + + break + } + + // Windows appears to tack on a single byte null terminator to the UTF-16 + // strings, but we are expecting either no null terminator or \u0000 (an + // even number of bytes). + if len(servicesBuffer)%2 != 0 && servicesBuffer[len(servicesBuffer)-1] == 0 { + servicesBuffer = servicesBuffer[:len(servicesBuffer)-1] + } + + var services []ServiceStatus + for i := 0; i < int(servicesReturned); i++ { + serviceTemp := (*EnumServiceStatusProcess)(unsafe.Pointer(&servicesBuffer[i*sizeofEnumServiceStatusProcess])) + + service, err := getServiceInformation(serviceTemp, servicesBuffer, handle) + if err != nil { + return nil, err + } + + services = append(services, service) + } + + return services, nil +} + +func getServiceInformation(rawService *EnumServiceStatusProcess, servicesBuffer []byte, handle ServiceDatabaseHandle) (ServiceStatus, error) { + var service ServiceStatus + + // Read null-terminated UTF16 strings from the buffer. + serviceNameOffset := uintptr(unsafe.Pointer(rawService.LpServiceName)) - (uintptr)(unsafe.Pointer(&servicesBuffer[0])) + displayNameOffset := uintptr(unsafe.Pointer(rawService.LpDisplayName)) - (uintptr)(unsafe.Pointer(&servicesBuffer[0])) + + strBuf := new(bytes.Buffer) + if err := sys.UTF16ToUTF8Bytes(servicesBuffer[displayNameOffset:], strBuf); err != nil { + return service, err + } + service.DisplayName = strBuf.String() + + strBuf.Reset() + if err := sys.UTF16ToUTF8Bytes(servicesBuffer[serviceNameOffset:], strBuf); err != nil { + return service, err + } + service.ServiceName = strBuf.String() + + var state string + + if stat, ok := serviceStates[ServiceState(rawService.ServiceStatusProcess.DwCurrentState)]; ok { + state = stat + } else { + state = "Can not define State" + } + service.CurrentState = state + + // Get detailed information + if err := getDetailedServiceInfo(handle, service.ServiceName, ServiceQueryConfig, &service); err != nil { + return service, err + } + + //Get uptime for service + if ServiceState(rawService.ServiceStatusProcess.DwCurrentState) != ServiceStopped { + processUpTime, err := getServiceUptime(rawService.ServiceStatusProcess.DwProcessId) + if err != nil { + logp.Warn("Uptime for service %v is not available", service.ServiceName) + } + service.Uptime = processUpTime / time.Millisecond + } + + return service, nil +} + +// getServiceUptime returns the uptime for process +func getServiceUptime(processID uint32) (time.Duration, error) { + var processCreationTime gosigar.ProcTime + + err := processCreationTime.Get(int(processID)) + if err != nil { + return time.Duration(processCreationTime.StartTime), err + } + + uptime := time.Since(time.Unix(0, int64(processCreationTime.StartTime)*int64(time.Millisecond))) + + return uptime, nil +} + +func getDetailedServiceInfo(handle ServiceDatabaseHandle, serviceName string, accessRight ServiceAccessRight, service *ServiceStatus) error { + var serviceBufSize uint32 + var serviceBytesNeeded uint32 + + serviceHandle, err := OpenService(handle, service.ServiceName, ServiceQueryConfig) + if err != nil { + return err + } + + if err := _QueryServiceConfig(serviceHandle, nil, serviceBufSize, &serviceBytesNeeded); err != nil { + if ServiceErrno(err.(syscall.Errno)) != SERVICE_ERROR_INSUFFICIENT_BUFFER { + err := CloseServiceHandle(serviceHandle) + return err + } + serviceBufSize += serviceBytesNeeded + buffer := make([]byte, serviceBufSize) + + for { + if err := _QueryServiceConfig(serviceHandle, &buffer[0], serviceBufSize, &serviceBytesNeeded); err != nil { + if ServiceErrno(err.(syscall.Errno)) != SERVICE_ERROR_INSUFFICIENT_BUFFER { + err := CloseServiceHandle(serviceHandle) + return err + } + serviceBufSize += serviceBytesNeeded + } else { + serviceQueryConfig := (*QueryServiceConfig)(unsafe.Pointer(&buffer[0])) + service.StartType = serviceStartTypes[ServiceStartType(serviceQueryConfig.DwStartType)] + if err := CloseServiceHandle(serviceHandle); err != nil { + return err + } + break + } + } + } + return nil +} + +func (reader *ServiceReader) Close() error { + return CloseServiceDatabaseHandle(reader.handle) +} + +func CloseServiceDatabaseHandle(handle ServiceDatabaseHandle) error { + if err := _CloseServiceHandle(uintptr(handle)); err != nil { + return ServiceErrno(err.(syscall.Errno)) + } + + return nil +} + +func CloseServiceHandle(handle ServiceHandle) error { + if err := _CloseServiceHandle(uintptr(handle)); err != nil { + return ServiceErrno(err.(syscall.Errno)) + } + + return nil +} + +func NewServiceReader() (*ServiceReader, error) { + hndl, err := OpenSCManager("", "", ScManagerEnumerateService|ScManagerConnect) + + if err != nil { + return nil, errors.Wrap(err, "initialization failed") + } + + r := &ServiceReader{ + handle: hndl, + } + + r.state = ServiceStateAll + + return r, nil +} + +func (reader *ServiceReader) Read() ([]common.MapStr, error) { + services, err := getServiceStates(reader.handle, reader.state) + + if err != nil { + return nil, err + } + + result := make([]common.MapStr, 0, len(services)) + + for _, service := range services { + ev := common.MapStr{ + "display_name": service.DisplayName, + "name": service.ServiceName, + "state": service.CurrentState, + "start_type": service.StartType, + } + + if service.Uptime > 0 { + if _, err = ev.Put("uptime.ms", service.Uptime); err != nil { + return nil, err + } + } + + result = append(result, ev) + } + + return result, nil +} + +func (e ServiceErrno) Error() string { + // If the value is not one of the known Service errors then assume its a + // general windows error. + if _, found := serviceErrors[e]; !found { + return syscall.Errno(e).Error() + } + + // Use FormatMessage to convert the service errno to a string. + var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS + b := make([]uint16, 300) + n, err := windows.FormatMessage(flags, modadvapi32.Handle(), uint32(e), 0, b, nil) + if err != nil { + return "service error #" + strconv.Itoa(int(e)) + } + + // Trim terminating \r and \n + for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { + } + return string(utf16.Decode(b[:n])) +} diff --git a/metricbeat/module/windows/service/zservice_windows.go b/metricbeat/module/windows/service/zservice_windows.go new file mode 100644 index 000000000000..4f5c61828fcf --- /dev/null +++ b/metricbeat/module/windows/service/zservice_windows.go @@ -0,0 +1,109 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package service + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + + procOpenSCManagerW = modadvapi32.NewProc("OpenSCManagerW") + procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") + procOpenServiceW = modadvapi32.NewProc("OpenServiceW") + procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW") + procCloseServiceHandle = modadvapi32.NewProc("CloseServiceHandle") +) + +func _OpenSCManager(machineName *uint16, databaseName *uint16, desiredAcces ServiceSCMAccessRight) (handle ServiceDatabaseHandle, err error) { + r0, _, e1 := syscall.Syscall(procOpenSCManagerW.Addr(), 3, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(databaseName)), uintptr(desiredAcces)) + handle = ServiceDatabaseHandle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func _EnumServicesStatusEx(handle ServiceDatabaseHandle, infoLevel ServiceInfoLevel, serviceType ServiceType, serviceState ServiceEnumState, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uintptr, groupName *uintptr) (err error) { + r1, _, e1 := syscall.Syscall12(procEnumServicesStatusExW.Addr(), 10, uintptr(handle), uintptr(infoLevel), uintptr(serviceType), uintptr(serviceState), uintptr(unsafe.Pointer(services)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), uintptr(unsafe.Pointer(servicesReturned)), uintptr(unsafe.Pointer(resumeHandle)), uintptr(unsafe.Pointer(groupName)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func _OpenService(handle ServiceDatabaseHandle, serviceName *uint16, desiredAccess ServiceAccessRight) (serviceHandle ServiceHandle, err error) { + r0, _, e1 := syscall.Syscall(procOpenServiceW.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(serviceName)), uintptr(desiredAccess)) + serviceHandle = ServiceHandle(r0) + if serviceHandle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func _QueryServiceConfig(serviceHandle ServiceHandle, serviceConfig *byte, bufSize uint32, bytesNeeded *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procQueryServiceConfigW.Addr(), 4, uintptr(serviceHandle), uintptr(unsafe.Pointer(serviceConfig)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func _CloseServiceHandle(handle uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procCloseServiceHandle.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/metricbeat/module/windows/windows.go b/metricbeat/module/windows/windows.go new file mode 100644 index 000000000000..c5b05cc598bb --- /dev/null +++ b/metricbeat/module/windows/windows.go @@ -0,0 +1,38 @@ +// +build windows + +package windows + +import ( + "sync" + + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/beats/metricbeat/helper" + "github.com/elastic/beats/metricbeat/mb" +) + +var once sync.Once + +func init() { + // Register the ModuleFactory function for the "windows" module. + if err := mb.Registry.AddModule("windows", NewModule); err != nil { + panic(err) + } +} + +func initModule() { + if err := helper.CheckAndEnableSeDebugPrivilege(); err != nil { + logp.Warn("%v", err) + } +} + +type Module struct { + mb.BaseModule +} + +func NewModule(base mb.BaseModule) (mb.Module, error) { + once.Do(func() { + initModule() + }) + + return &Module{BaseModule: base}, nil +}