From c0b385e82ba9ebfb8133f7bbdd0e8afddc9d134e Mon Sep 17 00:00:00 2001 From: Mariana Dima Date: Tue, 21 Apr 2020 16:30:19 +0200 Subject: [PATCH 1/2] Add more detailed errors and tests in the windows/service metricset (#17725) * error details, tests * changelog * work on tests * refactoring * mage fmt * mage fmt (cherry picked from commit c51a3bf29e65eda018bab7836d0bdd15f054932b) --- CHANGELOG.next.asciidoc | 2 + libbeat/common/bytes.go | 57 +++ libbeat/common/bytes_test.go | 37 ++ metricbeat/module/windows/service/reader.go | 197 +++++++++ .../module/windows/service/reader_test.go | 64 +++ metricbeat/module/windows/service/service.go | 4 +- ...ws_test.go => service_integration_test.go} | 5 +- .../{service_windows.go => service_status.go} | 392 +++++------------- .../windows/service/service_status_test.go | 36 ++ .../windows/service/zservice_windows.go | 14 +- winlogbeat/sys/strings.go | 59 --- winlogbeat/sys/strings_test.go | 56 +-- winlogbeat/sys/wineventlog/bufferpool.go | 4 +- .../sys/wineventlog/wineventlog_windows.go | 6 +- 14 files changed, 521 insertions(+), 412 deletions(-) create mode 100644 metricbeat/module/windows/service/reader.go create mode 100644 metricbeat/module/windows/service/reader_test.go rename metricbeat/module/windows/service/{service_integration_windows_test.go => service_integration_test.go} (98%) rename metricbeat/module/windows/service/{service_windows.go => service_status.go} (56%) create mode 100644 metricbeat/module/windows/service/service_status_test.go diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 4ee00ccab05..c4f2c0e945c 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -408,6 +408,8 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add final tests and move label to GA for the azure module in metricbeat. {pull}17319[17319] - Added documentation for running Metricbeat in Cloud Foundry. {pull}17275[17275] - Reference kubernetes manifests mount data directory from the host when running metricbeat as daemonset, so data persist between executions in the same node. {pull}17429[17429] +- Add more detailed error messages, system tests and small refactoring to the service metricset in windows. {pull}17725[17725] +- Stack Monitoring modules now auto-configure required metricsets when `xpack.enabled: true` is set. {issue}16471[[16471] {pull}17609[17609] *Packetbeat* diff --git a/libbeat/common/bytes.go b/libbeat/common/bytes.go index a2a7aa390a5..6239b024634 100644 --- a/libbeat/common/bytes.go +++ b/libbeat/common/bytes.go @@ -20,8 +20,22 @@ package common import ( "bytes" "crypto/rand" + "encoding/binary" "errors" "fmt" + "io" + "unicode/utf16" + "unicode/utf8" +) + +const ( + // 0xd800-0xdc00 encodes the high 10 bits of a pair. + // 0xdc00-0xe000 encodes the low 10 bits of a pair. + // the value is those 20 bits plus 0x10000. + surr1 = 0xd800 + surr2 = 0xdc00 + surr3 = 0xe000 + replacementChar = '\uFFFD' // Unicode replacement character ) // Byte order utilities @@ -76,3 +90,46 @@ func RandomBytes(length int) ([]byte, error) { return r, nil } + +func UTF16ToUTF8Bytes(in []byte, out io.Writer) error { + if len(in)%2 != 0 { + return fmt.Errorf("input buffer must have an even length (length=%d)", len(in)) + } + + var runeBuf [4]byte + var v1, v2 uint16 + for i := 0; i < len(in); i += 2 { + v1 = uint16(in[i]) | uint16(in[i+1])<<8 + // Stop at null-terminator. + if v1 == 0 { + return nil + } + + switch { + case v1 < surr1, surr3 <= v1: + n := utf8.EncodeRune(runeBuf[:], rune(v1)) + out.Write(runeBuf[:n]) + case surr1 <= v1 && v1 < surr2 && len(in) > i+2: + v2 = uint16(in[i+2]) | uint16(in[i+3])<<8 + if surr2 <= v2 && v2 < surr3 { + // valid surrogate sequence + r := utf16.DecodeRune(rune(v1), rune(v2)) + n := utf8.EncodeRune(runeBuf[:], r) + out.Write(runeBuf[:n]) + } + i += 2 + default: + // invalid surrogate sequence + n := utf8.EncodeRune(runeBuf[:], replacementChar) + out.Write(runeBuf[:n]) + } + } + return nil +} + +func StringToUTF16Bytes(in string) []byte { + var u16 []uint16 = utf16.Encode([]rune(in)) + buf := &bytes.Buffer{} + binary.Write(buf, binary.LittleEndian, u16) + return buf.Bytes() +} diff --git a/libbeat/common/bytes_test.go b/libbeat/common/bytes_test.go index d1d41c0f22f..b3738f510c0 100644 --- a/libbeat/common/bytes_test.go +++ b/libbeat/common/bytes_test.go @@ -21,8 +21,10 @@ package common import ( "bytes" + "encoding/binary" "errors" "testing" + "unicode/utf16" "github.com/stretchr/testify/assert" ) @@ -256,3 +258,38 @@ func TestRandomBytes(t *testing.T) { // unlikely to get 2 times the same results assert.False(t, bytes.Equal(v1, v2)) } + +func TestUTF16ToUTF8(t *testing.T) { + input := "abc白鵬翔\u145A6" + buf := &bytes.Buffer{} + binary.Write(buf, binary.LittleEndian, utf16.Encode([]rune(input))) + outputBuf := &bytes.Buffer{} + err := UTF16ToUTF8Bytes(buf.Bytes(), outputBuf) + assert.NoError(t, err) + assert.Equal(t, []byte(input), outputBuf.Bytes()) +} + +func TestUTF16BytesToStringTrimNullTerm(t *testing.T) { + input := "abc" + utf16Bytes := append(StringToUTF16Bytes(input), []byte{0, 0, 0, 0, 0, 0}...) + + outputBuf := &bytes.Buffer{} + err := UTF16ToUTF8Bytes(utf16Bytes, outputBuf) + if err != nil { + t.Fatal(err) + } + b := outputBuf.Bytes() + assert.Len(t, b, 3) + assert.Equal(t, input, string(b)) +} + +func BenchmarkUTF16ToUTF8(b *testing.B) { + utf16Bytes := StringToUTF16Bytes("A logon was attempted using explicit credentials.") + outputBuf := &bytes.Buffer{} + b.ResetTimer() + + for i := 0; i < b.N; i++ { + UTF16ToUTF8Bytes(utf16Bytes, outputBuf) + outputBuf.Reset() + } +} diff --git a/metricbeat/module/windows/service/reader.go b/metricbeat/module/windows/service/reader.go new file mode 100644 index 00000000000..918d59cb428 --- /dev/null +++ b/metricbeat/module/windows/service/reader.go @@ -0,0 +1,197 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build windows + +package service + +import ( + "crypto/sha256" + "encoding/base64" + "strconv" + "syscall" + + "github.com/pkg/errors" + "golang.org/x/sys/windows/registry" + + "github.com/elastic/beats/v7/libbeat/common" +) + +var ( + // errorNames is mapping of errno values to names. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681383(v=vs.85).aspx + errorNames = map[uint32]string{ + 1077: "ERROR_SERVICE_NEVER_STARTED", + } + InvalidDatabaseHandle = ^Handle(0) +) + +type Handle uintptr + +type Reader struct { + handle Handle + state ServiceEnumState + guid string // Host's MachineGuid value (a unique ID for the host). + ids map[string]string // Cache of service IDs. + protectedServices map[string]struct{} +} + +func NewReader() (*Reader, error) { + handle, err := openSCManager("", "", ScManagerEnumerateService|ScManagerConnect) + if err != nil { + return nil, errors.Wrap(err, "initialization failed") + } + + guid, err := getMachineGUID() + if err != nil { + return nil, err + } + + r := &Reader{ + handle: handle, + state: ServiceStateAll, + guid: guid, + ids: map[string]string{}, + protectedServices: map[string]struct{}{}, + } + + return r, nil +} + +func (reader *Reader) Read() ([]common.MapStr, error) { + services, err := GetServiceStates(reader.handle, reader.state, reader.protectedServices) + if err != nil { + return nil, err + } + + result := make([]common.MapStr, 0, len(services)) + + for _, service := range services { + ev := common.MapStr{ + "id": reader.getServiceID(service.ServiceName), + "display_name": service.DisplayName, + "name": service.ServiceName, + "state": service.CurrentState, + "start_type": service.StartType.String(), + "start_name": service.ServiceStartName, + "path_name": service.BinaryPathName, + } + + if service.CurrentState == "Stopped" { + ev.Put("exit_code", getErrorCode(service.ExitCode)) + } + + if service.PID > 0 { + ev.Put("pid", service.PID) + } + + 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 (reader *Reader) Close() error { + return closeHandle(reader.handle) +} + +func openSCManager(machineName string, databaseName string, desiredAccess ServiceSCMAccessRight) (Handle, error) { + var machineNamePtr *uint16 + if machineName != "" { + var err error + machineNamePtr, err = syscall.UTF16PtrFromString(machineName) + if err != nil { + return InvalidDatabaseHandle, err + } + } + + var databaseNamePtr *uint16 + if databaseName != "" { + var err error + databaseNamePtr, err = syscall.UTF16PtrFromString(databaseName) + if err != nil { + return InvalidDatabaseHandle, err + } + } + + handle, err := _OpenSCManager(machineNamePtr, databaseNamePtr, desiredAccess) + if err != nil { + return InvalidDatabaseHandle, ServiceErrno(err.(syscall.Errno)) + } + + return handle, nil +} + +// getMachineGUID returns the machine's GUID value which is unique to a Windows +// installation. +func getMachineGUID() (string, error) { + const key = registry.LOCAL_MACHINE + const path = `SOFTWARE\Microsoft\Cryptography` + const name = "MachineGuid" + + k, err := registry.OpenKey(key, path, registry.READ|registry.WOW64_64KEY) + if err != nil { + return "", errors.Wrapf(err, `failed to open HKLM\%v`, path) + } + + guid, _, err := k.GetStringValue(name) + if err != nil { + return "", errors.Wrapf(err, `failed to get value of HKLM\%v\%v`, path, name) + } + + return guid, nil +} + +// getServiceID returns a unique ID for the service that is derived from the +// machine's GUID and the service's name. +func (reader *Reader) getServiceID(name string) string { + // hash returns a base64 encoded sha256 hash that is truncated to 10 chars. + hash := func(v string) string { + sum := sha256.Sum256([]byte(v)) + base64Hash := base64.RawURLEncoding.EncodeToString(sum[:]) + return base64Hash[:10] + } + + id, found := reader.ids[name] + if !found { + id = hash(reader.guid + name) + reader.ids[name] = id + } + + return id +} + +func getErrorCode(errno uint32) string { + name, found := errorNames[errno] + if found { + return name + } + return strconv.Itoa(int(errno)) +} + +func closeHandle(handle Handle) error { + if err := _CloseServiceHandle(uintptr(handle)); err != nil { + return ServiceErrno(err.(syscall.Errno)) + } + return nil +} diff --git a/metricbeat/module/windows/service/reader_test.go b/metricbeat/module/windows/service/reader_test.go new file mode 100644 index 00000000000..0d2cb7d7e03 --- /dev/null +++ b/metricbeat/module/windows/service/reader_test.go @@ -0,0 +1,64 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build windows + +package service + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewReader(t *testing.T) { + reader, err := NewReader() + assert.NoError(t, err) + assert.NotNil(t, reader) + defer reader.Close() + assert.NotNil(t, reader.handle) +} + +func TestOpenSCManager(t *testing.T) { + handle, err := openSCManager("invalidMachine", "", ScManagerEnumerateService|ScManagerConnect) + assert.Error(t, err) + assert.Equal(t, handle, InvalidDatabaseHandle) + + handle, err = openSCManager("", "invalidDbName", ScManagerEnumerateService|ScManagerConnect) + assert.Error(t, err) + assert.Equal(t, handle, InvalidDatabaseHandle) + + handle, err = openSCManager("", "", ScManagerEnumerateService|ScManagerConnect) + assert.NoError(t, err) + assert.NotEqual(t, handle, InvalidDatabaseHandle) + closeHandle(handle) +} + +func TestGetMachineGUID(t *testing.T) { + guid, err := getMachineGUID() + assert.NoError(t, err) + assert.NotNil(t, guid) +} + +func TestRead(t *testing.T) { + reader, err := NewReader() + assert.NoError(t, err) + result, err := reader.Read() + assert.NoError(t, err) + assert.True(t, len(result) > 0) + reader.Close() +} diff --git a/metricbeat/module/windows/service/service.go b/metricbeat/module/windows/service/service.go index fce48ab6cff..4a0bf2b9e75 100644 --- a/metricbeat/module/windows/service/service.go +++ b/metricbeat/module/windows/service/service.go @@ -37,14 +37,14 @@ func init() { // multiple fetch calls. type MetricSet struct { mb.BaseMetricSet - reader *ServiceReader + reader *Reader } // 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) { - reader, err := NewServiceReader() + reader, err := NewReader() if err != nil { return nil, err } diff --git a/metricbeat/module/windows/service/service_integration_windows_test.go b/metricbeat/module/windows/service/service_integration_test.go similarity index 98% rename from metricbeat/module/windows/service/service_integration_windows_test.go rename to metricbeat/module/windows/service/service_integration_test.go index 51306fe5c37..fe1e987fc89 100644 --- a/metricbeat/module/windows/service/service_integration_windows_test.go +++ b/metricbeat/module/windows/service/service_integration_test.go @@ -15,7 +15,8 @@ // specific language governing permissions and limitations // under the License. -// +build integration windows +// +build integration +// +build windows package service @@ -53,7 +54,7 @@ func TestData(t *testing.T) { } func TestReadService(t *testing.T) { - reader, err := NewServiceReader() + reader, err := NewReader() if err != nil { t.Fatal(err) } diff --git a/metricbeat/module/windows/service/service_windows.go b/metricbeat/module/windows/service/service_status.go similarity index 56% rename from metricbeat/module/windows/service/service_windows.go rename to metricbeat/module/windows/service/service_status.go index 877be4e854f..8bef03126ad 100644 --- a/metricbeat/module/windows/service/service_windows.go +++ b/metricbeat/module/windows/service/service_status.go @@ -21,21 +21,18 @@ package service import ( "bytes" - "crypto/sha256" - "encoding/base64" "strconv" "syscall" "time" "unicode/utf16" "unsafe" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/pkg/errors" "golang.org/x/sys/windows" - "golang.org/x/sys/windows/registry" - "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" - "github.com/elastic/beats/v7/winlogbeat/sys" "github.com/elastic/gosigar" ) @@ -47,52 +44,18 @@ import ( //sys _QueryServiceConfig2(serviceHandle ServiceHandle, infoLevel ServiceConfigInformation, configBuffer *byte, bufSize uint32, bytesNeeded *uint32) (err error) [failretval==0] = advapi32.QueryServiceConfig2W //sys _CloseServiceHandle(handle uintptr) (err error) = advapi32.CloseServiceHandle -var ( - sizeofEnumServiceStatusProcess = (int)(unsafe.Sizeof(EnumServiceStatusProcess{})) -) - -type ServiceDatabaseHandle uintptr - -type ServiceHandle uintptr - -type ProcessHandle uintptr - -type ServiceConfigInformation uint32 - const ( - ServiceConfigDelayedAutoStartInfo ServiceConfigInformation = 3 - ServiceConfigDescription ServiceConfigInformation = 1 - ServiceConfigFailureActions ServiceConfigInformation = 2 - ServiceConfigFailureActionsFlag ServiceConfigInformation = 4 - ServiceConfigPreferredNode ServiceConfigInformation = 9 - ServiceConfigPreshutdownInfo ServiceConfigInformation = 7 - ServiceConfigRequiredPrivilegesInfo ServiceConfigInformation = 6 - ServiceConfigServiceSidInfo ServiceConfigInformation = 5 - ServiceConfigTriggerInfo ServiceConfigInformation = 8 - ServiceConfigLaunchProtected ServiceConfigInformation = 12 -) - -type serviceDelayedAutoStartInfo struct { - delayedAutoStart bool -} - -type serviceTriggerInfo struct { - cTriggers uint32 - pTriggers uintptr - pReserved uintptr -} - -var serviceStates = map[ServiceState]string{ - ServiceContinuePending: "Continuing", - ServicePausePending: "Pausing", - ServicePaused: "Paused", - ServiceRunning: "Running", - ServiceStartPending: "Starting", - ServiceStopPending: "Stopping", - ServiceStopped: "Stopped", -} + ConfigDelayedAutoStartInfo ConfigInformation = 3 + ConfigTriggerInfo ConfigInformation = 8 + ConfigLaunchProtected ConfigInformation = 12 + ConfigDescription ConfigInformation = 1 + ConfigFailureActions ConfigInformation = 2 + ConfigFailureActionsFlag ConfigInformation = 4 + ConfigPreferredNode ConfigInformation = 9 + ConfigPreshutdownInfo ConfigInformation = 7 + ConfigRequiredPrivilegesInfo ConfigInformation = 6 + ConfigServiceSidInfo ConfigInformation = 5 -const ( StartTypeBoot ServiceStartType = iota StartTypeSystem StartTypeAutomatic @@ -104,36 +67,33 @@ const ( StartTypeManualTriggered ) -var serviceStartTypes = map[ServiceStartType]string{ - StartTypeBoot: "Boot", - StartTypeSystem: "System", - StartTypeAutomatic: "Automatic", - StartTypeManual: "Manual", - StartTypeDisabled: "Disabled", - StartTypeAutomaticDelayed: "Automatic (Delayed)", - StartTypeAutomaticTriggered: "Automatic (Triggered)", - StartTypeAutomaticDelayedTriggered: "Automatic (Delayed, Triggered)", - StartTypeManualTriggered: "Manual (Triggered)", -} - -func (startType ServiceStartType) String() string { - return serviceStartTypes[startType] -} - -func (state ServiceState) String() string { - if val, ok := serviceStates[state]; ok { - return val +var ( + InvalidServiceHandle = ^Handle(0) + serviceStates = map[ServiceState]string{ + ServiceContinuePending: "Continuing", + ServicePausePending: "Pausing", + ServicePaused: "Paused", + ServiceRunning: "Running", + ServiceStartPending: "Starting", + ServiceStopPending: "Stopping", + ServiceStopped: "Stopped", + } + serviceStartTypes = map[ServiceStartType]string{ + StartTypeBoot: "Boot", + StartTypeSystem: "System", + StartTypeAutomatic: "Automatic", + StartTypeManual: "Manual", + StartTypeDisabled: "Disabled", + StartTypeAutomaticDelayed: "Automatic (Delayed)", + StartTypeAutomaticTriggered: "Automatic (Triggered)", + StartTypeAutomaticDelayedTriggered: "Automatic (Delayed, Triggered)", + StartTypeManualTriggered: "Manual (Triggered)", } - return "" -} +) -// errorNames is mapping of errno values to names. -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681383(v=vs.85).aspx -var errorNames = map[uint32]string{ - 1077: "ERROR_SERVICE_NEVER_STARTED", -} +type ConfigInformation uint32 -type ServiceStatus struct { +type Status struct { DisplayName string ServiceName string CurrentState string @@ -145,88 +105,28 @@ type ServiceStatus struct { BinaryPathName string } -type ServiceReader struct { - handle ServiceDatabaseHandle - state ServiceEnumState - guid string // Host's MachineGuid value (a unique ID for the host). - ids map[string]string // Cache of service IDs. - protectedServices map[string]struct{} +type serviceTriggerInfo struct { + cTriggers uint32 + pTriggers uintptr + pReserved uintptr } -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 +type serviceDelayedAutoStartInfo struct { + delayedAutoStart bool } -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 (startType ServiceStartType) String() string { + return serviceStartTypes[startType] } -func QueryServiceConfig2(serviceHandle ServiceHandle, infoLevel ServiceConfigInformation) ([]byte, error) { - var buffer []byte - - for { - var bytesNeeded uint32 - var bufPtr *byte - if len(buffer) > 0 { - bufPtr = &buffer[0] - } - - if err := _QueryServiceConfig2(serviceHandle, infoLevel, bufPtr, uint32(len(buffer)), &bytesNeeded); err != nil { - if ServiceErrno(err.(syscall.Errno)) == SERVICE_ERROR_INSUFFICIENT_BUFFER { - // Increase buffer size and retry. - buffer = make([]byte, len(buffer)+int(bytesNeeded)) - continue - } - return nil, err - } - - break +func (state ServiceState) String() string { + if val, ok := serviceStates[state]; ok { + return val } - - return buffer, nil + return "" } -func getServiceStates(handle ServiceDatabaseHandle, state ServiceEnumState, protectedServices map[string]struct{}) ([]ServiceStatus, error) { +func GetServiceStates(handle Handle, state ServiceEnumState, protectedServices map[string]struct{}) ([]Status, error) { var servicesReturned uint32 var servicesBuffer []byte @@ -243,7 +143,7 @@ func getServiceStates(handle ServiceDatabaseHandle, state ServiceEnumState, prot servicesBuffer = make([]byte, len(servicesBuffer)+int(bytesNeeded)) continue } - return nil, ServiceErrno(err.(syscall.Errno)) + return nil, errors.Wrap(ServiceErrno(err.(syscall.Errno)), "error while calling the _EnumServicesStatusEx api") } break @@ -256,9 +156,10 @@ func getServiceStates(handle ServiceDatabaseHandle, state ServiceEnumState, prot servicesBuffer = servicesBuffer[:len(servicesBuffer)-1] } - var services []ServiceStatus + var services []Status + var sizeStatusProcess = (int)(unsafe.Sizeof(EnumServiceStatusProcess{})) for i := 0; i < int(servicesReturned); i++ { - serviceTemp := (*EnumServiceStatusProcess)(unsafe.Pointer(&servicesBuffer[i*sizeofEnumServiceStatusProcess])) + serviceTemp := (*EnumServiceStatusProcess)(unsafe.Pointer(&servicesBuffer[i*sizeStatusProcess])) service, err := getServiceInformation(serviceTemp, servicesBuffer, handle, protectedServices) if err != nil { @@ -271,8 +172,8 @@ func getServiceStates(handle ServiceDatabaseHandle, state ServiceEnumState, prot return services, nil } -func getServiceInformation(rawService *EnumServiceStatusProcess, servicesBuffer []byte, handle ServiceDatabaseHandle, protectedServices map[string]struct{}) (ServiceStatus, error) { - service := ServiceStatus{ +func getServiceInformation(rawService *EnumServiceStatusProcess, servicesBuffer []byte, handle Handle, protectedServices map[string]struct{}) (Status, error) { + service := Status{ PID: rawService.ServiceStatusProcess.DwProcessId, } @@ -281,13 +182,13 @@ func getServiceInformation(rawService *EnumServiceStatusProcess, servicesBuffer displayNameOffset := uintptr(unsafe.Pointer(rawService.LpDisplayName)) - (uintptr)(unsafe.Pointer(&servicesBuffer[0])) strBuf := new(bytes.Buffer) - if err := sys.UTF16ToUTF8Bytes(servicesBuffer[displayNameOffset:], strBuf); err != nil { + if err := common.UTF16ToUTF8Bytes(servicesBuffer[displayNameOffset:], strBuf); err != nil { return service, err } service.DisplayName = strBuf.String() strBuf.Reset() - if err := sys.UTF16ToUTF8Bytes(servicesBuffer[serviceNameOffset:], strBuf); err != nil { + if err := common.UTF16ToUTF8Bytes(servicesBuffer[serviceNameOffset:], strBuf); err != nil { return service, err } service.ServiceName = strBuf.String() @@ -307,12 +208,12 @@ func getServiceInformation(rawService *EnumServiceStatusProcess, servicesBuffer service.ExitCode = rawService.ServiceStatusProcess.DwServiceSpecificExitCode } - serviceHandle, err := OpenService(handle, service.ServiceName, ServiceQueryConfig) + serviceHandle, err := openServiceHandle(handle, service.ServiceName, ServiceQueryConfig) if err != nil { - return service, err + return service, errors.Wrapf(err, "error while opening service %s", service.ServiceName) } - defer CloseServiceHandle(serviceHandle) + defer closeHandle(serviceHandle) // Get detailed information if err := getAdditionalServiceInfo(serviceHandle, &service); err != nil { @@ -341,21 +242,25 @@ func getServiceInformation(rawService *EnumServiceStatusProcess, servicesBuffer return service, nil } -// getServiceUptime returns the uptime for process -func getServiceUptime(processID uint32) (time.Duration, error) { - var processCreationTime gosigar.ProcTime +func openServiceHandle(handle Handle, serviceName string, desiredAccess ServiceAccessRight) (Handle, error) { + var serviceNamePtr *uint16 + if serviceName != "" { + var err error + serviceNamePtr, err = syscall.UTF16PtrFromString(serviceName) + if err != nil { + return InvalidServiceHandle, err + } + } - err := processCreationTime.Get(int(processID)) + serviceHandle, err := _OpenService(handle, serviceNamePtr, desiredAccess) if err != nil { - return time.Duration(processCreationTime.StartTime), err + return InvalidServiceHandle, ServiceErrno(err.(syscall.Errno)) } - uptime := time.Since(time.Unix(0, int64(processCreationTime.StartTime)*int64(time.Millisecond))) - - return uptime, nil + return serviceHandle, nil } -func getAdditionalServiceInfo(serviceHandle ServiceHandle, service *ServiceStatus) error { +func getAdditionalServiceInfo(serviceHandle Handle, service *Status) error { var buffer []byte for { @@ -371,7 +276,7 @@ func getAdditionalServiceInfo(serviceHandle ServiceHandle, service *ServiceStatu buffer = make([]byte, len(buffer)+int(bytesNeeded)) continue } - return ServiceErrno(err.(syscall.Errno)) + return errors.Wrapf(ServiceErrno(err.(syscall.Errno)), "error while querying the service configuration %s", service.ServiceName) } serviceQueryConfig := (*QueryServiceConfig)(unsafe.Pointer(&buffer[0])) service.StartType = ServiceStartType(serviceQueryConfig.DwStartType) @@ -379,13 +284,13 @@ func getAdditionalServiceInfo(serviceHandle ServiceHandle, service *ServiceStatu binaryPathNameOffset := uintptr(unsafe.Pointer(serviceQueryConfig.LpBinaryPathName)) - (uintptr)(unsafe.Pointer(&buffer[0])) strBuf := new(bytes.Buffer) - if err := sys.UTF16ToUTF8Bytes(buffer[serviceStartNameOffset:], strBuf); err != nil { + if err := common.UTF16ToUTF8Bytes(buffer[serviceStartNameOffset:], strBuf); err != nil { return err } service.ServiceStartName = strBuf.String() strBuf.Reset() - if err := sys.UTF16ToUTF8Bytes(buffer[binaryPathNameOffset:], strBuf); err != nil { + if err := common.UTF16ToUTF8Bytes(buffer[binaryPathNameOffset:], strBuf); err != nil { return err } service.BinaryPathName = strBuf.String() @@ -396,21 +301,21 @@ func getAdditionalServiceInfo(serviceHandle ServiceHandle, service *ServiceStatu return nil } -func getOptionalServiceInfo(serviceHandle ServiceHandle, service *ServiceStatus) error { +func getOptionalServiceInfo(serviceHandle Handle, service *Status) error { // Get information if the service is started delayed or triggered. Only valid for automatic or manual services. So filter them first. if service.StartType == StartTypeAutomatic || service.StartType == StartTypeManual { var delayedInfo *serviceDelayedAutoStartInfo if service.StartType == StartTypeAutomatic { - delayedInfoBuffer, err := QueryServiceConfig2(serviceHandle, ServiceConfigDelayedAutoStartInfo) + delayedInfoBuffer, err := queryServiceConfig2(serviceHandle, ConfigDelayedAutoStartInfo) if err != nil { - return err + return errors.Wrapf(err, "error while querying rhe service configuration %s", service.ServiceName) } delayedInfo = (*serviceDelayedAutoStartInfo)(unsafe.Pointer(&delayedInfoBuffer[0])) } // Get information if the service is triggered. - triggeredInfoBuffer, err := QueryServiceConfig2(serviceHandle, ServiceConfigTriggerInfo) + triggeredInfoBuffer, err := queryServiceConfig2(serviceHandle, ConfigTriggerInfo) if err != nil { return err } @@ -436,104 +341,43 @@ func getOptionalServiceInfo(serviceHandle ServiceHandle, service *ServiceStatus) 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") - } - - guid, err := getMachineGUID() - if err != nil { - return nil, err - } - - r := &ServiceReader{ - handle: hndl, - state: ServiceStateAll, - guid: guid, - ids: map[string]string{}, - protectedServices: map[string]struct{}{}, - } - - return r, nil -} - -func (reader *ServiceReader) Read() ([]common.MapStr, error) { - services, err := getServiceStates(reader.handle, reader.state, reader.protectedServices) - if err != nil { - return nil, err - } - - result := make([]common.MapStr, 0, len(services)) - - for _, service := range services { - ev := common.MapStr{ - "id": reader.getServiceID(service.ServiceName), - "display_name": service.DisplayName, - "name": service.ServiceName, - "state": service.CurrentState, - "start_type": service.StartType.String(), - "start_name": service.ServiceStartName, - "path_name": service.BinaryPathName, - } - - if service.CurrentState == "Stopped" { - ev.Put("exit_code", getErrorCode(service.ExitCode)) - } +func queryServiceConfig2(serviceHandle Handle, infoLevel ConfigInformation) ([]byte, error) { + var buffer []byte - if service.PID > 0 { - ev.Put("pid", service.PID) + for { + var bytesNeeded uint32 + var bufPtr *byte + if len(buffer) > 0 { + bufPtr = &buffer[0] } - if service.Uptime > 0 { - if _, err = ev.Put("uptime.ms", service.Uptime); err != nil { - return nil, err + if err := _QueryServiceConfig2(serviceHandle, infoLevel, bufPtr, uint32(len(buffer)), &bytesNeeded); err != nil { + if ServiceErrno(err.(syscall.Errno)) == SERVICE_ERROR_INSUFFICIENT_BUFFER { + // Increase buffer size and retry. + buffer = make([]byte, len(buffer)+int(bytesNeeded)) + continue } + return nil, err } - result = append(result, ev) + break } - return result, nil + return buffer, nil } -// getServiceID returns a unique ID for the service that is derived from the -// machine's GUID and the service's name. -func (reader *ServiceReader) getServiceID(name string) string { - // hash returns a base64 encoded sha256 hash that is truncated to 10 chars. - hash := func(v string) string { - sum := sha256.Sum256([]byte(v)) - base64Hash := base64.RawURLEncoding.EncodeToString(sum[:]) - return base64Hash[:10] - } +// getServiceUptime returns the uptime for process +func getServiceUptime(processID uint32) (time.Duration, error) { + var processCreationTime gosigar.ProcTime - id, found := reader.ids[name] - if !found { - id = hash(reader.guid + name) - reader.ids[name] = id + err := processCreationTime.Get(int(processID)) + if err != nil { + return time.Duration(processCreationTime.StartTime), err } - return id + uptime := time.Since(time.Unix(0, int64(processCreationTime.StartTime)*int64(time.Millisecond))) + + return uptime, nil } func (e ServiceErrno) Error() string { @@ -556,31 +400,3 @@ func (e ServiceErrno) Error() string { } return string(utf16.Decode(b[:n])) } - -// getMachineGUID returns the machine's GUID value which is unique to a Windows -// installation. -func getMachineGUID() (string, error) { - const key = registry.LOCAL_MACHINE - const path = `SOFTWARE\Microsoft\Cryptography` - const name = "MachineGuid" - - k, err := registry.OpenKey(key, path, registry.READ|registry.WOW64_64KEY) - if err != nil { - return "", errors.Wrapf(err, `failed to open HKLM\%v`, path) - } - - guid, _, err := k.GetStringValue(name) - if err != nil { - return "", errors.Wrapf(err, `failed to get value of HKLM\%v\%v`, path, name) - } - - return guid, nil -} - -func getErrorCode(errno uint32) string { - name, found := errorNames[errno] - if found { - return name - } - return strconv.Itoa(int(errno)) -} diff --git a/metricbeat/module/windows/service/service_status_test.go b/metricbeat/module/windows/service/service_status_test.go new file mode 100644 index 00000000000..667632abf4c --- /dev/null +++ b/metricbeat/module/windows/service/service_status_test.go @@ -0,0 +1,36 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build windows + +package service + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetServiceStates(t *testing.T) { + handle, err := openSCManager("", "", ScManagerEnumerateService|ScManagerConnect) + assert.NoError(t, err) + assert.NotEqual(t, handle, InvalidDatabaseHandle) + services, err := GetServiceStates(handle, ServiceStateAll, map[string]struct{}{}) + assert.NoError(t, err) + assert.True(t, len(services) > 0) + closeHandle(handle) +} diff --git a/metricbeat/module/windows/service/zservice_windows.go b/metricbeat/module/windows/service/zservice_windows.go index 31fdf630fe7..cb816720b15 100644 --- a/metricbeat/module/windows/service/zservice_windows.go +++ b/metricbeat/module/windows/service/zservice_windows.go @@ -64,9 +64,9 @@ var ( procCloseServiceHandle = modadvapi32.NewProc("CloseServiceHandle") ) -func _OpenSCManager(machineName *uint16, databaseName *uint16, desiredAcces ServiceSCMAccessRight) (handle ServiceDatabaseHandle, err error) { +func _OpenSCManager(machineName *uint16, databaseName *uint16, desiredAcces ServiceSCMAccessRight) (handle Handle, err error) { r0, _, e1 := syscall.Syscall(procOpenSCManagerW.Addr(), 3, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(databaseName)), uintptr(desiredAcces)) - handle = ServiceDatabaseHandle(r0) + handle = Handle(r0) if handle == 0 { if e1 != 0 { err = errnoErr(e1) @@ -77,7 +77,7 @@ func _OpenSCManager(machineName *uint16, databaseName *uint16, desiredAcces Serv 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) { +func _EnumServicesStatusEx(handle Handle, 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 { @@ -89,9 +89,9 @@ func _EnumServicesStatusEx(handle ServiceDatabaseHandle, infoLevel ServiceInfoLe return } -func _OpenService(handle ServiceDatabaseHandle, serviceName *uint16, desiredAccess ServiceAccessRight) (serviceHandle ServiceHandle, err error) { +func _OpenService(handle Handle, serviceName *uint16, desiredAccess ServiceAccessRight) (serviceHandle Handle, err error) { r0, _, e1 := syscall.Syscall(procOpenServiceW.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(serviceName)), uintptr(desiredAccess)) - serviceHandle = ServiceHandle(r0) + serviceHandle = Handle(r0) if serviceHandle == 0 { if e1 != 0 { err = errnoErr(e1) @@ -102,7 +102,7 @@ func _OpenService(handle ServiceDatabaseHandle, serviceName *uint16, desiredAcce return } -func _QueryServiceConfig(serviceHandle ServiceHandle, serviceConfig *byte, bufSize uint32, bytesNeeded *uint32) (err error) { +func _QueryServiceConfig(serviceHandle Handle, 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 { @@ -114,7 +114,7 @@ func _QueryServiceConfig(serviceHandle ServiceHandle, serviceConfig *byte, bufSi return } -func _QueryServiceConfig2(serviceHandle ServiceHandle, infoLevel ServiceConfigInformation, configBuffer *byte, bufSize uint32, bytesNeeded *uint32) (err error) { +func _QueryServiceConfig2(serviceHandle Handle, infoLevel ConfigInformation, configBuffer *byte, bufSize uint32, bytesNeeded *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procQueryServiceConfig2W.Addr(), 5, uintptr(serviceHandle), uintptr(infoLevel), uintptr(unsafe.Pointer(configBuffer)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0) if r1 == 0 { if e1 != 0 { diff --git a/winlogbeat/sys/strings.go b/winlogbeat/sys/strings.go index 2310808086a..12d91fac31d 100644 --- a/winlogbeat/sys/strings.go +++ b/winlogbeat/sys/strings.go @@ -20,71 +20,12 @@ package sys import ( "errors" "fmt" - "io" "strings" "unicode/utf16" - "unicode/utf8" -) - -// The conditions replacementChar==unicode.ReplacementChar and -// maxRune==unicode.MaxRune are verified in the tests. -// Defining them locally avoids this package depending on package unicode. - -const ( - replacementChar = '\uFFFD' // Unicode replacement character - maxRune = '\U0010FFFF' // Maximum valid Unicode code point. -) - -const ( - // 0xd800-0xdc00 encodes the high 10 bits of a pair. - // 0xdc00-0xe000 encodes the low 10 bits of a pair. - // the value is those 20 bits plus 0x10000. - surr1 = 0xd800 - surr2 = 0xdc00 - surr3 = 0xe000 - - surrSelf = 0x10000 ) var ErrBufferTooSmall = errors.New("buffer too small") -func UTF16ToUTF8Bytes(in []byte, out io.Writer) error { - if len(in)%2 != 0 { - return fmt.Errorf("input buffer must have an even length (length=%d)", len(in)) - } - - var runeBuf [4]byte - var v1, v2 uint16 - for i := 0; i < len(in); i += 2 { - v1 = uint16(in[i]) | uint16(in[i+1])<<8 - // Stop at null-terminator. - if v1 == 0 { - return nil - } - - switch { - case v1 < surr1, surr3 <= v1: - n := utf8.EncodeRune(runeBuf[:], rune(v1)) - out.Write(runeBuf[:n]) - case surr1 <= v1 && v1 < surr2 && len(in) > i+2: - v2 = uint16(in[i+2]) | uint16(in[i+3])<<8 - if surr2 <= v2 && v2 < surr3 { - // valid surrogate sequence - r := utf16.DecodeRune(rune(v1), rune(v2)) - n := utf8.EncodeRune(runeBuf[:], r) - out.Write(runeBuf[:n]) - } - i += 2 - default: - // invalid surrogate sequence - n := utf8.EncodeRune(runeBuf[:], replacementChar) - out.Write(runeBuf[:n]) - } - } - - return nil -} - // UTF16BytesToString returns a string that is decoded from the UTF-16 bytes. // The byte slice must be of even length otherwise an error will be returned. // The integer returned is the offset to the start of the next string with diff --git a/winlogbeat/sys/strings_test.go b/winlogbeat/sys/strings_test.go index 0c92c9f3c47..358e61ed6b2 100644 --- a/winlogbeat/sys/strings_test.go +++ b/winlogbeat/sys/strings_test.go @@ -19,23 +19,16 @@ package sys import ( "bytes" - "encoding/binary" "testing" - "unicode/utf16" "github.com/stretchr/testify/assert" -) -func toUTF16Bytes(in string) []byte { - var u16 []uint16 = utf16.Encode([]rune(in)) - buf := &bytes.Buffer{} - binary.Write(buf, binary.LittleEndian, u16) - return buf.Bytes() -} + "github.com/elastic/beats/v7/libbeat/common" +) func TestUTF16BytesToString(t *testing.T) { input := "abc白鵬翔\u145A6" - utf16Bytes := toUTF16Bytes(input) + utf16Bytes := common.StringToUTF16Bytes(input) output, _, err := UTF16BytesToString(utf16Bytes) if err != nil { @@ -45,7 +38,7 @@ func TestUTF16BytesToString(t *testing.T) { } func TestUTF16BytesToStringOffset(t *testing.T) { - in := bytes.Join([][]byte{toUTF16Bytes("one"), toUTF16Bytes("two"), toUTF16Bytes("three")}, []byte{0, 0}) + in := bytes.Join([][]byte{common.StringToUTF16Bytes("one"), common.StringToUTF16Bytes("two"), common.StringToUTF16Bytes("three")}, []byte{0, 0}) output, offset, err := UTF16BytesToString(in) if err != nil { @@ -72,7 +65,7 @@ func TestUTF16BytesToStringOffset(t *testing.T) { } func TestUTF16BytesToStringOffsetWithEmptyString(t *testing.T) { - in := bytes.Join([][]byte{toUTF16Bytes(""), toUTF16Bytes("two")}, []byte{0, 0}) + in := bytes.Join([][]byte{common.StringToUTF16Bytes(""), common.StringToUTF16Bytes("two")}, []byte{0, 0}) output, offset, err := UTF16BytesToString(in) if err != nil { @@ -91,7 +84,7 @@ func TestUTF16BytesToStringOffsetWithEmptyString(t *testing.T) { } func BenchmarkUTF16BytesToString(b *testing.B) { - utf16Bytes := toUTF16Bytes("A logon was attempted using explicit credentials.") + utf16Bytes := common.StringToUTF16Bytes("A logon was attempted using explicit credentials.") b.Run("simple_string", func(b *testing.B) { b.ResetTimer() @@ -111,40 +104,3 @@ func BenchmarkUTF16BytesToString(b *testing.B) { } }) } - -func TestUTF16ToUTF8(t *testing.T) { - input := "abc白鵬翔\u145A6" - utf16Bytes := toUTF16Bytes(input) - - outputBuf := &bytes.Buffer{} - err := UTF16ToUTF8Bytes(utf16Bytes, outputBuf) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, []byte(input), outputBuf.Bytes()) -} - -func TestUTF16BytesToStringTrimNullTerm(t *testing.T) { - input := "abc" - utf16Bytes := append(toUTF16Bytes(input), []byte{0, 0, 0, 0, 0, 0}...) - - outputBuf := &bytes.Buffer{} - err := UTF16ToUTF8Bytes(utf16Bytes, outputBuf) - if err != nil { - t.Fatal(err) - } - b := outputBuf.Bytes() - assert.Len(t, b, 3) - assert.Equal(t, input, string(b)) -} - -func BenchmarkUTF16ToUTF8(b *testing.B) { - utf16Bytes := toUTF16Bytes("A logon was attempted using explicit credentials.") - outputBuf := &bytes.Buffer{} - b.ResetTimer() - - for i := 0; i < b.N; i++ { - UTF16ToUTF8Bytes(utf16Bytes, outputBuf) - outputBuf.Reset() - } -} diff --git a/winlogbeat/sys/wineventlog/bufferpool.go b/winlogbeat/sys/wineventlog/bufferpool.go index 104d45f938a..90bdf825de1 100644 --- a/winlogbeat/sys/wineventlog/bufferpool.go +++ b/winlogbeat/sys/wineventlog/bufferpool.go @@ -20,7 +20,7 @@ package wineventlog import ( "sync" - "github.com/elastic/beats/v7/winlogbeat/sys" + "github.com/elastic/beats/v7/libbeat/common" ) // bufferPool contains a pool of byteBuffer objects. @@ -104,7 +104,7 @@ func UTF16BytesToString(b []byte) (string, error) { bb := newByteBuffer() defer bb.free() - if err := sys.UTF16ToUTF8Bytes(b, bb); err != nil { + if err := common.UTF16ToUTF8Bytes(b, bb); err != nil { return "", err } diff --git a/winlogbeat/sys/wineventlog/wineventlog_windows.go b/winlogbeat/sys/wineventlog/wineventlog_windows.go index 57aaf456947..3b282fd41d7 100644 --- a/winlogbeat/sys/wineventlog/wineventlog_windows.go +++ b/winlogbeat/sys/wineventlog/wineventlog_windows.go @@ -27,6 +27,8 @@ import ( "runtime" "syscall" + "github.com/elastic/beats/v7/libbeat/common" + "golang.org/x/sys/windows" "github.com/elastic/beats/v7/winlogbeat/sys" @@ -414,7 +416,7 @@ func FormatEventString( // This assumes there is only a single string value to read. This will // not work to read keys (when messageFlag == EvtFormatMessageKeyword). - return sys.UTF16ToUTF8Bytes(buffer[:bufferUsed], out) + return common.UTF16ToUTF8Bytes(buffer[:bufferUsed], out) } // offset reads a pointer value from the reader then calculates an offset from @@ -505,5 +507,5 @@ func renderXML(eventHandle EvtHandle, flag EvtRenderFlag, renderBuf []byte, out "to the buffer, but the buffer can only hold %d bytes", bufferUsed, len(renderBuf)) } - return sys.UTF16ToUTF8Bytes(renderBuf[:bufferUsed], out) + return common.UTF16ToUTF8Bytes(renderBuf[:bufferUsed], out) } From e635a2617a5370a73006e0deadee52abd3bf5dba Mon Sep 17 00:00:00 2001 From: Mariana Date: Tue, 21 Apr 2020 16:44:07 +0200 Subject: [PATCH 2/2] update changelog --- CHANGELOG.next.asciidoc | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index c4f2c0e945c..03adced55e9 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -409,7 +409,6 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Added documentation for running Metricbeat in Cloud Foundry. {pull}17275[17275] - Reference kubernetes manifests mount data directory from the host when running metricbeat as daemonset, so data persist between executions in the same node. {pull}17429[17429] - Add more detailed error messages, system tests and small refactoring to the service metricset in windows. {pull}17725[17725] -- Stack Monitoring modules now auto-configure required metricsets when `xpack.enabled: true` is set. {issue}16471[[16471] {pull}17609[17609] *Packetbeat*