diff --git a/plugins/inputs/win_eventlog/README.md b/plugins/inputs/win_eventlog/README.md index 0dde264b4..33cb3e200 100644 --- a/plugins/inputs/win_eventlog/README.md +++ b/plugins/inputs/win_eventlog/README.md @@ -65,6 +65,10 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ''' + ## When true, event logs are read from the beginning; otherwise only future events + ## will be logged. + # from_beginning = false + ## System field names: ## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated", ## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName", diff --git a/plugins/inputs/win_eventlog/sample.conf b/plugins/inputs/win_eventlog/sample.conf index 1b35bb44e..099062ab7 100644 --- a/plugins/inputs/win_eventlog/sample.conf +++ b/plugins/inputs/win_eventlog/sample.conf @@ -40,6 +40,10 @@ ''' + ## When true, event logs are read from the beginning; otherwise only future events + ## will be logged. + # from_beginning = false + ## System field names: ## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated", ## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName", diff --git a/plugins/inputs/win_eventlog/syscall_windows.go b/plugins/inputs/win_eventlog/syscall_windows.go index 14c7ffe08..28beea839 100644 --- a/plugins/inputs/win_eventlog/syscall_windows.go +++ b/plugins/inputs/win_eventlog/syscall_windows.go @@ -23,7 +23,9 @@ type EvtSubscribeFlag uint32 // EVT_SUBSCRIBE_FLAGS enumeration // https://msdn.microsoft.com/en-us/library/windows/desktop/aa385588(v=vs.85).aspx const ( - EvtSubscribeToFutureEvents EvtSubscribeFlag = 1 + EvtSubscribeToFutureEvents EvtSubscribeFlag = 1 + EvtSubscribeStartAtOldestRecord EvtSubscribeFlag = 2 + EvtSubscribeStartAfterBookmark EvtSubscribeFlag = 3 ) // EvtRenderFlag uint32 @@ -32,9 +34,9 @@ type EvtRenderFlag uint32 // EVT_RENDER_FLAGS enumeration // https://msdn.microsoft.com/en-us/library/windows/desktop/aa385563(v=vs.85).aspx const ( - //revive:disable:var-naming // Render the event as an XML string. For details on the contents of the // XML string, see the Event schema. EvtRenderEventXml EvtRenderFlag = 1 - //revive:enable:var-naming + // Render bookmark + EvtRenderBookmark EvtRenderFlag = 2 ) diff --git a/plugins/inputs/win_eventlog/win_eventlog.go b/plugins/inputs/win_eventlog/win_eventlog.go index d7ebb62da..a53b2f7f3 100644 --- a/plugins/inputs/win_eventlog/win_eventlog.go +++ b/plugins/inputs/win_eventlog/win_eventlog.go @@ -29,52 +29,102 @@ var sampleConfig string // WinEventLog config type WinEventLog struct { - Locale uint32 `toml:"locale"` - EventlogName string `toml:"eventlog_name"` - Query string `toml:"xpath_query"` - ProcessUserData bool `toml:"process_userdata"` - ProcessEventData bool `toml:"process_eventdata"` - Separator string `toml:"separator"` - OnlyFirstLineOfMessage bool `toml:"only_first_line_of_message"` - TimeStampFromEvent bool `toml:"timestamp_from_event"` - EventTags []string `toml:"event_tags"` - EventFields []string `toml:"event_fields"` - ExcludeFields []string `toml:"exclude_fields"` - ExcludeEmpty []string `toml:"exclude_empty"` - subscription EvtHandle - buf []byte - Log telegraf.Logger + Locale uint32 `toml:"locale"` + EventlogName string `toml:"eventlog_name"` + Query string `toml:"xpath_query"` + FromBeginning bool `toml:"from_beginning"` + ProcessUserData bool `toml:"process_userdata"` + ProcessEventData bool `toml:"process_eventdata"` + Separator string `toml:"separator"` + OnlyFirstLineOfMessage bool `toml:"only_first_line_of_message"` + TimeStampFromEvent bool `toml:"timestamp_from_event"` + EventTags []string `toml:"event_tags"` + EventFields []string `toml:"event_fields"` + ExcludeFields []string `toml:"exclude_fields"` + ExcludeEmpty []string `toml:"exclude_empty"` + Log telegraf.Logger `toml:"-"` + + subscription EvtHandle + subscriptionFlag EvtSubscribeFlag + bookmark EvtHandle } -var bufferSize = 1 << 14 +const bufferSize = 1 << 14 func (*WinEventLog) SampleConfig() string { return sampleConfig } -// Gather Windows Event Log entries -func (w *WinEventLog) Gather(acc telegraf.Accumulator) error { - - var err error - if w.subscription == 0 { - w.subscription, err = w.evtSubscribe(w.EventlogName, w.Query) - if err != nil { - return fmt.Errorf("subscription of Windows Event Log failed: %w", err) - } +func (w *WinEventLog) Init() error { + w.subscriptionFlag = EvtSubscribeToFutureEvents + if w.FromBeginning { + w.subscriptionFlag = EvtSubscribeStartAtOldestRecord } + + bookmark, err := _EvtCreateBookmark(nil) + if err != nil { + return err + } + w.bookmark = bookmark + + return nil +} + +func (w *WinEventLog) Start(_ telegraf.Accumulator) error { + subscription, err := w.evtSubscribe() + if err != nil { + return fmt.Errorf("subscription of Windows Event Log failed: %w", err) + } + w.subscription = subscription w.Log.Debug("Subscription handle id:", w.subscription) -loop: + return nil +} + +func (w *WinEventLog) Stop() { + _ = _EvtClose(w.subscription) +} + +func (w *WinEventLog) GetState() interface{} { + bookmarkXML, err := w.renderBookmark(w.bookmark) + if err != nil { + w.Log.Errorf("State-persistence failed, cannot render bookmark: %w", err) + return "" + } + return bookmarkXML +} + +func (w *WinEventLog) SetState(state interface{}) error { + bookmarkXML, ok := state.(string) + if !ok { + return fmt.Errorf("invalid type %T for state", state) + } + + ptr, err := syscall.UTF16PtrFromString(bookmarkXML) + if err != nil { + return fmt.Errorf("convertion to pointer failed: %w", err) + } + + bookmark, err := _EvtCreateBookmark(ptr) + if err != nil { + return fmt.Errorf("creating bookmark failed: %w", err) + } + w.bookmark = bookmark + w.subscriptionFlag = EvtSubscribeStartAfterBookmark + + return nil +} + +// Gather Windows Event Log entries +func (w *WinEventLog) Gather(acc telegraf.Accumulator) error { for { events, err := w.fetchEvents(w.subscription) if err != nil { - switch { - case err == ERROR_NO_MORE_ITEMS: - break loop - case err != nil: - w.Log.Errorf("Error getting events: %v", err) - return err + if err == ERROR_NO_MORE_ITEMS { + break } + w.Log.Errorf("Error getting events: %v", err) + return err } for _, event := range events { @@ -244,27 +294,28 @@ func (w *WinEventLog) shouldExcludeEmptyField(field string, fieldType string, fi return false } -func (w *WinEventLog) evtSubscribe(logName, xquery string) (EvtHandle, error) { - var logNamePtr, xqueryPtr *uint16 - +func (w *WinEventLog) evtSubscribe() (EvtHandle, error) { sigEvent, err := windows.CreateEvent(nil, 0, 0, nil) if err != nil { return 0, err } defer windows.CloseHandle(sigEvent) - logNamePtr, err = syscall.UTF16PtrFromString(logName) + logNamePtr, err := syscall.UTF16PtrFromString(w.EventlogName) if err != nil { return 0, err } - xqueryPtr, err = syscall.UTF16PtrFromString(xquery) + xqueryPtr, err := syscall.UTF16PtrFromString(w.Query) if err != nil { return 0, err } - subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr, - 0, 0, 0, EvtSubscribeToFutureEvents) + var bookmark EvtHandle + if w.subscriptionFlag == EvtSubscribeStartAfterBookmark { + bookmark = w.bookmark + } + subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr, bookmark, 0, 0, w.subscriptionFlag) if err != nil { return 0, err } @@ -299,34 +350,55 @@ func (w *WinEventLog) fetchEvents(subsHandle EvtHandle) ([]Event, error) { return nil, err } + var evterr error for _, eventHandle := range eventHandles { - if eventHandle != 0 { - event, err := w.renderEvent(eventHandle) - if err == nil { - events = append(events, event) - } + if eventHandle == 0 { + continue } + if event, err := w.renderEvent(eventHandle); err == nil { + events = append(events, event) + } + if err := _EvtUpdateBookmark(w.bookmark, eventHandle); err != nil && evterr == nil { + evterr = err + } + + if err := _EvtClose(eventHandle); err != nil && evterr == nil { + evterr = err + } + } + return events, evterr +} + +func (w *WinEventLog) renderBookmark(bookmark EvtHandle) (string, error) { + var bufferUsed, propertyCount uint32 + + buf := make([]byte, bufferSize) + err := _EvtRender(0, bookmark, EvtRenderBookmark, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount) + if err != nil { + return "", err } - for i := 0; i < len(eventHandles); i++ { - err := _EvtClose(eventHandles[i]) - if err != nil { - return events, err - } + x, err := DecodeUTF16(buf[:bufferUsed]) + if err != nil { + return "", err } - return events, nil + if x[len(x)-1] == 0 { + x = x[:len(x)-1] + } + return string(x), err } func (w *WinEventLog) renderEvent(eventHandle EvtHandle) (Event, error) { var bufferUsed, propertyCount uint32 + buf := make([]byte, bufferSize) event := Event{} - err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(w.buf)), &w.buf[0], &bufferUsed, &propertyCount) + err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount) if err != nil { return event, err } - eventXML, err := DecodeUTF16(w.buf[:bufferUsed]) + eventXML, err := DecodeUTF16(buf[:bufferUsed]) if err != nil { return event, err } @@ -483,7 +555,6 @@ func openPublisherMetadata( func init() { inputs.Add("win_eventlog", func() telegraf.Input { return &WinEventLog{ - buf: make([]byte, bufferSize), ProcessUserData: true, ProcessEventData: true, Separator: "_", diff --git a/plugins/inputs/win_eventlog/zsyscall_windows.go b/plugins/inputs/win_eventlog/zsyscall_windows.go index e4c3570c2..9fffe416e 100644 --- a/plugins/inputs/win_eventlog/zsyscall_windows.go +++ b/plugins/inputs/win_eventlog/zsyscall_windows.go @@ -75,6 +75,8 @@ var ( procEvtNext = modwevtapi.NewProc("EvtNext") procEvtFormatMessage = modwevtapi.NewProc("EvtFormatMessage") procEvtOpenPublisherMetadata = modwevtapi.NewProc("EvtOpenPublisherMetadata") + procEvtCreateBookmark = modwevtapi.NewProc("EvtCreateBookmark") + procEvtUpdateBookmark = modwevtapi.NewProc("EvtUpdateBookmark") ) func _EvtSubscribe( @@ -231,3 +233,26 @@ func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity *uint16, log } return } + +func _EvtCreateBookmark(bookmarkXML *uint16) (EvtHandle, error) { + r0, _, e1 := syscall.Syscall(procEvtCreateBookmark.Addr(), 1, uintptr(unsafe.Pointer(bookmarkXML)), 0, 0) + handle := EvtHandle(r0) + if handle != 0 { + return handle, nil + } + if e1 != 0 { + return handle, errnoErr(e1) + } + return handle, syscall.EINVAL +} + +func _EvtUpdateBookmark(bookmark, event EvtHandle) error { + r0, _, e1 := syscall.Syscall(procEvtUpdateBookmark.Addr(), 2, uintptr(bookmark), uintptr(event), 0) + if r0 != 0 { + return nil + } + if e1 != 0 { + return errnoErr(e1) + } + return syscall.EINVAL +}