summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/plugins/in_winevtlog
diff options
context:
space:
mode:
Diffstat (limited to 'src/fluent-bit/plugins/in_winevtlog')
-rw-r--r--src/fluent-bit/plugins/in_winevtlog/CMakeLists.txt6
-rw-r--r--src/fluent-bit/plugins/in_winevtlog/in_winevtlog.c279
-rw-r--r--src/fluent-bit/plugins/in_winevtlog/pack.c625
-rw-r--r--src/fluent-bit/plugins/in_winevtlog/winevtlog.c840
-rw-r--r--src/fluent-bit/plugins/in_winevtlog/winevtlog.h134
5 files changed, 1884 insertions, 0 deletions
diff --git a/src/fluent-bit/plugins/in_winevtlog/CMakeLists.txt b/src/fluent-bit/plugins/in_winevtlog/CMakeLists.txt
new file mode 100644
index 000000000..8f4c0e233
--- /dev/null
+++ b/src/fluent-bit/plugins/in_winevtlog/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(src
+ in_winevtlog.c
+ pack.c
+ winevtlog.c)
+
+FLB_PLUGIN(in_winevtlog "${src}" "wevtapi")
diff --git a/src/fluent-bit/plugins/in_winevtlog/in_winevtlog.c b/src/fluent-bit/plugins/in_winevtlog/in_winevtlog.c
new file mode 100644
index 000000000..60c5bcecb
--- /dev/null
+++ b/src/fluent-bit/plugins/in_winevtlog/in_winevtlog.c
@@ -0,0 +1,279 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed 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.
+ */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_input_plugin.h>
+#include <fluent-bit/flb_kernel.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_sqldb.h>
+#include "winevtlog.h"
+
+#define DEFAULT_INTERVAL_SEC 1
+#define DEFAULT_INTERVAL_NSEC 0
+#define DEFAULT_THRESHOLD_SIZE 0x7ffff /* Default reading buffer size (512kb) */
+
+static int in_winevtlog_collect(struct flb_input_instance *ins,
+ struct flb_config *config, void *in_context);
+
+static int in_winevtlog_init(struct flb_input_instance *in,
+ struct flb_config *config, void *data)
+{
+ int ret;
+ const char *tmp;
+ int read_existing_events = FLB_FALSE;
+ struct mk_list *head;
+ struct winevtlog_channel *ch;
+ struct winevtlog_config *ctx;
+
+ /* Initialize context */
+ ctx = flb_calloc(1, sizeof(struct winevtlog_config));
+ if (ctx == NULL) {
+ flb_errno();
+ return -1;
+ }
+ ctx->ins = in;
+
+ ctx->log_encoder = flb_log_event_encoder_create(FLB_LOG_EVENT_FORMAT_DEFAULT);
+
+ if (ctx->log_encoder == NULL) {
+ flb_plg_error(in, "could not initialize event encoder");
+ flb_free(ctx);
+
+ return NULL;
+ }
+
+ /* Load the config map */
+ ret = flb_input_config_map_set(in, (void *) ctx);
+ if (ret == -1) {
+ flb_log_event_encoder_destroy(ctx->log_encoder);
+ flb_free(ctx);
+ return -1;
+ }
+
+ /* Set up total reading size threshold */
+ ctx->total_size_threshold = DEFAULT_THRESHOLD_SIZE;
+
+ /* Open channels */
+ tmp = flb_input_get_property("channels", in);
+ if (!tmp) {
+ flb_plg_debug(ctx->ins, "no channel provided. listening to 'Application'");
+ tmp = "Application";
+ }
+
+ ctx->active_channel = winevtlog_open_all(tmp, ctx);
+ if (!ctx->active_channel) {
+ flb_plg_error(ctx->ins, "failed to open channels");
+ flb_log_event_encoder_destroy(ctx->log_encoder);
+ flb_free(ctx);
+ return -1;
+ }
+
+ /* Initialize SQLite DB (optional) */
+ tmp = flb_input_get_property("db", in);
+ if (tmp) {
+ ctx->db = flb_sqldb_open(tmp, in->name, config);
+ if (!ctx->db) {
+ flb_plg_error(ctx->ins, "could not open/create database");
+ winevtlog_close_all(ctx->active_channel);
+ flb_log_event_encoder_destroy(ctx->log_encoder);
+ flb_free(ctx);
+ return -1;
+ }
+
+ ret = flb_sqldb_query(ctx->db, SQL_CREATE_CHANNELS, NULL, NULL);
+ if (ret != FLB_OK) {
+ flb_plg_error(ctx->ins, "could not create 'channels' table");
+ flb_sqldb_close(ctx->db);
+ winevtlog_close_all(ctx->active_channel);
+ flb_log_event_encoder_destroy(ctx->log_encoder);
+ flb_free(ctx);
+ return -1;
+ }
+
+ mk_list_foreach(head, ctx->active_channel) {
+ ch = mk_list_entry(head, struct winevtlog_channel, _head);
+ winevtlog_sqlite_load(ch, ctx->db);
+ flb_plg_debug(ctx->ins, "load channel<%s time=%u>",
+ ch->name, ch->time_created);
+ }
+ }
+
+ /* Set the context */
+ flb_input_set_context(in, ctx);
+
+ /* Set the collector */
+ ret = flb_input_set_collector_time(in,
+ in_winevtlog_collect,
+ ctx->interval_sec,
+ ctx->interval_nsec,
+ config);
+ if (ret == -1) {
+ flb_plg_error(ctx->ins, "could not set up a collector");
+ }
+ ctx->coll_fd = ret;
+
+ return 0;
+}
+
+static int in_winevtlog_read_channel(struct flb_input_instance *ins,
+ struct winevtlog_config *ctx,
+ struct winevtlog_channel *ch)
+{
+ unsigned int read;
+
+ if (winevtlog_read(ch, ctx, &read)) {
+ flb_plg_error(ctx->ins, "failed to read '%s'", ch->name);
+ return -1;
+ }
+ if (read == 0) {
+ return 0;
+ }
+ flb_plg_debug(ctx->ins, "read %u bytes from '%s'", read, ch->name);
+
+ if (ctx->db) {
+ ch->time_updated = time(NULL);
+ flb_plg_debug(ctx->ins, "save channel<%s time=%u>",
+ ch->name, ch->time_updated);
+ winevtlog_sqlite_save(ch, ctx->db);
+ }
+
+ if (ctx->log_encoder->output_length > 0) {
+ flb_input_log_append(ctx->ins, NULL, 0,
+ ctx->log_encoder->output_buffer,
+ ctx->log_encoder->output_length);
+ }
+
+ flb_log_event_encoder_reset(ctx->log_encoder);
+
+ return 0;
+}
+
+static int in_winevtlog_collect(struct flb_input_instance *ins,
+ struct flb_config *config, void *in_context)
+{
+ struct winevtlog_config *ctx = in_context;
+ struct mk_list *head;
+ struct winevtlog_channel *ch;
+
+ mk_list_foreach(head, ctx->active_channel) {
+ ch = mk_list_entry(head, struct winevtlog_channel, _head);
+ in_winevtlog_read_channel(ins, ctx, ch);
+ }
+ return 0;
+}
+
+static void in_winevtlog_pause(void *data, struct flb_config *config)
+{
+ struct winevtlog_config *ctx = data;
+ flb_input_collector_pause(ctx->coll_fd, ctx->ins);
+}
+
+static void in_winevtlog_resume(void *data, struct flb_config *config)
+{
+ struct winevtlog_config *ctx = data;
+ flb_input_collector_resume(ctx->coll_fd, ctx->ins);
+}
+
+static int in_winevtlog_exit(void *data, struct flb_config *config)
+{
+ struct winevtlog_config *ctx = data;
+
+ if (!ctx) {
+ return 0;
+ }
+
+ winevtlog_close_all(ctx->active_channel);
+
+ if (ctx->db) {
+ flb_sqldb_close(ctx->db);
+ }
+ flb_free(ctx);
+
+ return 0;
+}
+
+static struct flb_config_map config_map[] = {
+ {
+ FLB_CONFIG_MAP_STR, "channels", NULL,
+ 0, FLB_FALSE, 0,
+ "Specify a comma-separated list of channels to read from"
+ },
+ {
+ FLB_CONFIG_MAP_STR, "db", NULL,
+ 0, FLB_FALSE, 0,
+ "Specify DB file to save read offsets"
+ },
+ {
+ FLB_CONFIG_MAP_TIME, "interval_sec", "1s",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, interval_sec),
+ "Set the polling interval for each channel"
+ },
+ {
+ FLB_CONFIG_MAP_INT, "interval_nsec", "0",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, interval_nsec),
+ "Set the polling interval for each channel (sub seconds)"
+ },
+ {
+ FLB_CONFIG_MAP_BOOL, "string_inserts", "true",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, string_inserts),
+ "Whether to include StringInserts in output records"
+ },
+ {
+ FLB_CONFIG_MAP_BOOL, "read_existing_events", "false",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, read_existing_events),
+ "Whether to consume at oldest records in channels"
+ },
+ {
+ FLB_CONFIG_MAP_BOOL, "render_event_as_xml", "false",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, render_event_as_xml),
+ "Whether to consume at oldest records in channels"
+ },
+ {
+ FLB_CONFIG_MAP_BOOL, "use_ansi", "false",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, use_ansi),
+ "Use ANSI encoding on eventlog messages"
+ },
+ {
+ FLB_CONFIG_MAP_BOOL, "ignore_missing_channels", "false",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, ignore_missing_channels),
+ "Whether to ignore channels missing in eventlog"
+ },
+ {
+ FLB_CONFIG_MAP_STR, "event_query", "*",
+ 0, FLB_TRUE, offsetof(struct winevtlog_config, event_query),
+ "Specify XML query for filtering events"
+ },
+ /* EOF */
+ {0}
+};
+
+struct flb_input_plugin in_winevtlog_plugin = {
+ .name = "winevtlog",
+ .description = "Windows EventLog using winevt.h API",
+ .cb_init = in_winevtlog_init,
+ .cb_pre_run = NULL,
+ .cb_collect = in_winevtlog_collect,
+ .cb_flush_buf = NULL,
+ .cb_pause = in_winevtlog_pause,
+ .cb_resume = in_winevtlog_resume,
+ .cb_exit = in_winevtlog_exit,
+ .config_map = config_map
+};
diff --git a/src/fluent-bit/plugins/in_winevtlog/pack.c b/src/fluent-bit/plugins/in_winevtlog/pack.c
new file mode 100644
index 000000000..28075436b
--- /dev/null
+++ b/src/fluent-bit/plugins/in_winevtlog/pack.c
@@ -0,0 +1,625 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed 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.
+ */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_input_plugin.h>
+#include <msgpack.h>
+#include <sddl.h>
+#include <locale.h>
+#include "winevtlog.h"
+
+#define FORMAT_ISO8601 "%Y-%m-%d %H:%M:%S %z"
+
+#define BINDATA(evt) ((unsigned char *) (evt) + (evt)->DataOffset)
+
+static int pack_nullstr(struct winevtlog_config *ctx)
+{
+ flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "");
+}
+
+static int pack_wstr(struct winevtlog_config *ctx, const wchar_t *wstr)
+{
+ int size;
+ char *buf;
+ UINT code_page = CP_UTF8;
+ LPCSTR defaultChar = L" ";
+
+ if (ctx->use_ansi) {
+ code_page = CP_ACP;
+ }
+
+ /* Compute the buffer size first */
+ size = WideCharToMultiByte(code_page, 0, wstr, -1, NULL, 0, NULL, NULL);
+ if (size == 0) {
+ return -1;
+ }
+
+ buf = flb_malloc(size);
+ if (buf == NULL) {
+ flb_errno();
+ return -1;
+ }
+
+ /* Convert UTF-16 into UTF-8 */
+ size = WideCharToMultiByte(code_page, 0, wstr, -1, buf, size, defaultChar, NULL);
+ if (size == 0) {
+ flb_free(buf);
+ return -1;
+ }
+
+ /* Pack buf except the trailing '\0' */
+ flb_log_event_encoder_append_body_string(ctx->log_encoder, buf, size - 1);
+
+ flb_free(buf);
+ return 0;
+}
+
+static int pack_binary(struct winevtlog_config *ctx, PBYTE bin, size_t length)
+{
+ const char *HEX_TABLE = "0123456789ABCDEF";
+ char *buffer;
+ int size = length * 2;
+ size_t i, j;
+ unsigned int idx = 0;
+
+ if (length == 0) {
+ pack_nullstr(ctx->log_encoder);
+ return 0;
+ }
+
+ buffer = flb_malloc(size);
+ if (buffer == NULL) {
+ flb_errno();
+ return -1;
+ }
+
+ for (i = 0; i < length; i++) {
+ for (j = 0; j < 2; j++) {
+ idx = (unsigned int)(bin[i] >> (j * 4) & 0x0F);
+ buffer[2*i+(1-j)] = HEX_TABLE[idx];
+ }
+ }
+
+ flb_log_event_encoder_append_body_string(ctx->log_encoder, buffer, size);
+
+ flb_free(buffer);
+
+ return 0;
+}
+
+static int pack_guid(struct winevtlog_config *ctx, const GUID *guid)
+{
+ LPOLESTR p = NULL;
+
+ if (FAILED(StringFromCLSID(guid, &p))) {
+ return -1;
+ }
+ if (pack_wstr(ctx, p)) {
+ CoTaskMemFree(p);
+ return -1;
+ }
+
+ CoTaskMemFree(p);
+
+ return 0;
+}
+
+static int pack_hex32(struct winevtlog_config *ctx, int32_t hex)
+{
+ CHAR buffer[32];
+ size_t size = _countof(buffer);
+
+ _snprintf_s(buffer,
+ size,
+ _TRUNCATE,
+ "0x%lx",
+ hex);
+ size = strlen(buffer);
+ if (size > 0) {
+ flb_log_event_encoder_append_body_cstring(ctx->log_encoder, buffer);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int pack_hex64(struct winevtlog_config *ctx, int64_t hex)
+{
+ CHAR buffer[32];
+ size_t size = _countof(buffer);
+
+ _snprintf_s(buffer,
+ size,
+ _TRUNCATE,
+ "0x%llx",
+ hex);
+
+ size = strlen(buffer);
+ if (size > 0) {
+ flb_log_event_encoder_append_body_cstring(ctx->log_encoder, buffer);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int pack_keywords(struct winevtlog_config *ctx, uint64_t keywords)
+{
+ CHAR buffer[32];
+ size_t size = _countof(buffer);
+
+ _snprintf_s(buffer,
+ size,
+ _TRUNCATE,
+ "0x%llx",
+ keywords);
+
+ size = strlen(buffer);
+
+ flb_log_event_encoder_append_body_cstring(ctx->log_encoder, buffer);
+
+ return 0;
+}
+
+static int pack_systemtime(struct winevtlog_config *ctx, SYSTEMTIME *st)
+{
+ CHAR buf[64];
+ size_t len = 0;
+ _locale_t locale;
+ TIME_ZONE_INFORMATION tzi;
+ SYSTEMTIME st_local;
+
+ GetTimeZoneInformation(&tzi);
+
+ locale = _get_current_locale();
+ if (locale == NULL) {
+ return -1;
+ }
+ if (st != NULL) {
+ SystemTimeToTzSpecificLocalTime(&tzi, st, &st_local);
+
+ struct tm tm = {st_local.wSecond,
+ st_local.wMinute,
+ st_local.wHour,
+ st_local.wDay,
+ st_local.wMonth-1,
+ st_local.wYear-1900,
+ st_local.wDayOfWeek, 0, 0};
+ len = _strftime_l(buf, 64, FORMAT_ISO8601, &tm, locale);
+ if (len == 0) {
+ flb_errno();
+ _free_locale(locale);
+ return -1;
+ }
+ _free_locale(locale);
+
+ flb_log_event_encoder_append_body_string(ctx->log_encoder, buf, len);
+ }
+ else {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pack_filetime(struct winevtlog_config *ctx, ULONGLONG filetime)
+{
+ LARGE_INTEGER timestamp;
+ CHAR buf[64];
+ size_t len = 0;
+ FILETIME ft, ft_local;
+ SYSTEMTIME st;
+ _locale_t locale;
+
+ locale = _get_current_locale();
+ if (locale == NULL) {
+ return -1;
+ }
+ timestamp.QuadPart = filetime;
+ ft.dwHighDateTime = timestamp.HighPart;
+ ft.dwLowDateTime = timestamp.LowPart;
+ FileTimeToLocalFileTime(&ft, &ft_local);
+ if (FileTimeToSystemTime(&ft_local, &st)) {
+ struct tm tm = {st.wSecond, st.wMinute, st.wHour, st.wDay, st.wMonth-1, st.wYear-1900, st.wDayOfWeek, 0, 0};
+ len = _strftime_l(buf, 64, FORMAT_ISO8601, &tm, locale);
+ if (len == 0) {
+ flb_errno();
+ _free_locale(locale);
+ return -1;
+ }
+ _free_locale(locale);
+
+ flb_log_event_encoder_append_body_string(ctx->log_encoder, buf, len);
+ }
+ else {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pack_sid(struct winevtlog_config *ctx, PSID sid)
+{
+ size_t size;
+ LPWSTR wide_sid = NULL;
+ int ret = -1;
+
+ if (ConvertSidToStringSidW(sid, &wide_sid)) {
+ ret = pack_wstr(ctx, wide_sid);
+
+ LocalFree(wide_sid);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void pack_string_inserts(struct winevtlog_config *ctx, PEVT_VARIANT values, DWORD count)
+{
+ int i;
+ int ret;
+
+ ret = flb_log_event_encoder_body_begin_array(ctx->log_encoder);
+
+ for (i = 0; i < count; i++) {
+ if (values[i].Type & EVT_VARIANT_TYPE_ARRAY) {
+ continue;
+ }
+
+ switch (values[i].Type & EVT_VARIANT_TYPE_MASK) {
+ case EvtVarTypeNull:
+ pack_nullstr(ctx);
+ break;
+ case EvtVarTypeString:
+ if (pack_wstr(ctx, values[i].StringVal)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeAnsiString:
+ if (pack_wstr(ctx, values[i].AnsiStringVal)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeSByte:
+ flb_log_event_encoder_append_body_int8(ctx->log_encoder, values[i].SByteVal);
+ break;
+ case EvtVarTypeByte:
+ flb_log_event_encoder_append_body_uint8(ctx->log_encoder, values[i].ByteVal);
+ break;
+ case EvtVarTypeInt16:
+ flb_log_event_encoder_append_body_int16(ctx->log_encoder, values[i].Int16Val);
+ break;
+ case EvtVarTypeUInt16:
+ flb_log_event_encoder_append_body_uint16(ctx->log_encoder, values[i].UInt16Val);
+ break;
+ case EvtVarTypeInt32:
+ flb_log_event_encoder_append_body_int32(ctx->log_encoder, values[i].Int32Val);
+ break;
+ case EvtVarTypeUInt32:
+ flb_log_event_encoder_append_body_uint32(ctx->log_encoder, values[i].UInt32Val);
+ break;
+ case EvtVarTypeInt64:
+ flb_log_event_encoder_append_body_int64(ctx->log_encoder, values[i].Int64Val);
+ break;
+ case EvtVarTypeUInt64:
+ flb_log_event_encoder_append_body_uint64(ctx->log_encoder, values[i].UInt64Val);
+ break;
+ case EvtVarTypeSingle:
+ flb_log_event_encoder_append_body_double(ctx->log_encoder, values[i].SingleVal);
+ break;
+ case EvtVarTypeDouble:
+ flb_log_event_encoder_append_body_double(ctx->log_encoder, values[i].DoubleVal);
+ break;
+ case EvtVarTypeBoolean:
+ flb_log_event_encoder_append_body_boolean(ctx->log_encoder, (int) values[i].BooleanVal);
+ break;
+ case EvtVarTypeGuid:
+ if (pack_guid(ctx, values[i].GuidVal)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeSizeT:
+ flb_log_event_encoder_append_body_uint64(ctx->log_encoder, values[i].SizeTVal);
+ break;
+ case EvtVarTypeFileTime:
+ if (pack_filetime(ctx, values[i].FileTimeVal)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeSysTime:
+ if (pack_systemtime(ctx, values[i].SysTimeVal)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeSid:
+ if (pack_sid(ctx, values[i].SidVal)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeHexInt32:
+ if (pack_hex32(ctx, values[i].Int32Val)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeHexInt64:
+ if (pack_hex64(ctx, values[i].Int64Val)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeEvtXml:
+ if (pack_wstr(ctx, values[i].XmlVal, ctx)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ case EvtVarTypeBinary:
+ if (pack_binary(ctx, values[i].BinaryVal, values[i].Count)) {
+ pack_nullstr(ctx);
+ }
+ break;
+ default:
+ flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "?");
+ }
+ }
+
+ if (ret == FLB_EVENT_ENCODER_SUCCESS) {
+ ret = flb_log_event_encoder_body_commit_array(ctx->log_encoder);
+ }
+
+}
+
+void winevtlog_pack_xml_event(WCHAR *system_xml, WCHAR *message,
+ PEVT_VARIANT string_inserts, UINT count_inserts, struct winevtlog_channel *ch,
+ struct winevtlog_config *ctx)
+{
+ int ret;
+
+ ret = flb_log_event_encoder_begin_record(ctx->log_encoder);
+
+ if (ret == FLB_EVENT_ENCODER_SUCCESS) {
+ ret = flb_log_event_encoder_set_current_timestamp(ctx->log_encoder);
+ }
+
+
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "System");
+
+ if (pack_wstr(ctx, system_xml)) {
+ pack_nullstr(ctx);
+ }
+
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Message");
+
+ if (pack_wstr(ctx, message)) {
+ pack_nullstr(ctx);
+ }
+
+ if (ctx->string_inserts) {
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "StringInserts");
+
+ pack_string_inserts(ctx, string_inserts, count_inserts);
+ }
+
+ if (ret == FLB_EVENT_ENCODER_SUCCESS) {
+ ret = flb_log_event_encoder_commit_record(ctx->log_encoder);
+ }
+}
+
+void winevtlog_pack_event(PEVT_VARIANT system, WCHAR *message,
+ PEVT_VARIANT string_inserts, UINT count_inserts, struct winevtlog_channel *ch,
+ struct winevtlog_config *ctx)
+{
+ int ret;
+ size_t len;
+ int count = 19;
+
+ if (ctx->string_inserts) {
+ count++;
+ }
+
+ ret = flb_log_event_encoder_begin_record(ctx->log_encoder);
+
+ if (ret == FLB_EVENT_ENCODER_SUCCESS) {
+ ret = flb_log_event_encoder_set_current_timestamp(ctx->log_encoder);
+ }
+
+ /* ProviderName */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "ProviderName");
+
+ if (pack_wstr(ctx, system[EvtSystemProviderName].StringVal)) {
+ pack_nullstr(ctx);
+ }
+
+ /* ProviderGuid */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "ProviderGuid");
+
+ if (EvtVarTypeNull != system[EvtSystemProviderGuid].Type) {
+ if (pack_guid(ctx, system[EvtSystemProviderGuid].GuidVal)) {
+ pack_nullstr(ctx);
+ }
+ }
+ else {
+ pack_nullstr(ctx);
+ }
+
+ /* Qualifiers */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Qualifiers");
+
+ if (EvtVarTypeNull != system[EvtSystemQualifiers].Type) {
+ flb_log_event_encoder_append_body_uint16(ctx->log_encoder, system[EvtSystemQualifiers].UInt16Val);
+ }
+ else {
+ pack_nullstr(ctx);
+ }
+
+ /* EventID */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "EventID");
+
+ if (EvtVarTypeNull != system[EvtSystemEventID].Type) {
+ flb_log_event_encoder_append_body_uint16(ctx->log_encoder, system[EvtSystemEventID].UInt16Val);
+ }
+ else {
+ pack_nullstr(ctx);
+ }
+
+ /* Version */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Version");
+
+ if (EvtVarTypeNull != system[EvtSystemVersion].Type) {
+ flb_log_event_encoder_append_body_uint8(ctx->log_encoder, system[EvtSystemVersion].ByteVal);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint8(ctx->log_encoder, 0);
+ }
+
+ /* Level */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Level");
+
+ if (EvtVarTypeNull != system[EvtSystemLevel].Type) {
+ flb_log_event_encoder_append_body_uint8(ctx->log_encoder, system[EvtSystemLevel].ByteVal);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint8(ctx->log_encoder, 0);
+ }
+
+ /* Task */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Task");
+
+ if (EvtVarTypeNull != system[EvtSystemTask].Type) {
+ flb_log_event_encoder_append_body_uint16(ctx->log_encoder, system[EvtSystemTask].UInt16Val);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint16(ctx->log_encoder, 0);
+ }
+
+ /* Opcode */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Opcode");
+
+ if (EvtVarTypeNull != system[EvtSystemOpcode].Type) {
+ flb_log_event_encoder_append_body_uint8(ctx->log_encoder, system[EvtSystemOpcode].ByteVal);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint8(ctx->log_encoder, 0);
+ }
+
+ /* Keywords */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Keywords");
+
+ if (EvtVarTypeNull != system[EvtSystemKeywords].Type) {
+ pack_keywords(ctx, system[EvtSystemKeywords].UInt64Val);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint64(ctx->log_encoder, 0);
+ }
+
+ /* TimeCreated */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "TimeCreated");
+
+ if (pack_filetime(ctx, system[EvtSystemTimeCreated].FileTimeVal)) {
+ pack_nullstr(ctx);
+ }
+
+ /* EventRecordID */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "EventRecordID");
+
+ if (EvtVarTypeNull != system[EvtSystemEventRecordId].Type) {
+ flb_log_event_encoder_append_body_uint64(ctx->log_encoder, system[EvtSystemEventRecordId].UInt64Val);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint64(ctx->log_encoder, 0);
+ }
+
+ /* ActivityID */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "ActivityID");
+
+ if (pack_guid(ctx, system[EvtSystemActivityID].GuidVal)) {
+ pack_nullstr(ctx);
+ }
+
+ /* Related ActivityID */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "RelatedActivityID");
+
+ if (pack_guid(ctx, system[EvtSystemRelatedActivityID].GuidVal)) {
+ pack_nullstr(ctx);
+ }
+
+ /* ProcessID */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "ProcessID");
+
+ if (EvtVarTypeNull != system[EvtSystemProcessID].Type) {
+ flb_log_event_encoder_append_body_uint32(ctx->log_encoder, system[EvtSystemProcessID].UInt32Val);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint32(ctx->log_encoder, 0);
+ }
+
+ /* ThreadID */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "ThreadID");
+
+ if (EvtVarTypeNull != system[EvtSystemThreadID].Type) {
+ flb_log_event_encoder_append_body_uint32(ctx->log_encoder, system[EvtSystemThreadID].UInt32Val);
+ }
+ else {
+ flb_log_event_encoder_append_body_uint32(ctx->log_encoder, 0);
+ }
+
+ /* Channel */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Channel");
+
+ if (pack_wstr(ctx, system[EvtSystemChannel].StringVal)) {
+ pack_nullstr(ctx);
+ }
+
+ /* Computer */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Computer");
+
+ if (pack_wstr(ctx, system[EvtSystemComputer].StringVal)) {
+ pack_nullstr(ctx);
+ }
+
+ /* UserID */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "UserID");
+
+ if (pack_sid(ctx, system[EvtSystemUserID].SidVal)) {
+ pack_nullstr(ctx);
+ }
+
+ /* Message */
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "Message");
+
+ if (pack_wstr(ctx, message)) {
+ pack_nullstr(ctx);
+ }
+
+ /* String Inserts */
+ if (ctx->string_inserts) {
+ ret = flb_log_event_encoder_append_body_cstring(ctx->log_encoder, "StringInserts");
+
+ pack_string_inserts(ctx, string_inserts, count_inserts);
+ }
+
+ if (ret == FLB_EVENT_ENCODER_SUCCESS) {
+ ret = flb_log_event_encoder_commit_record(ctx->log_encoder);
+ }
+}
diff --git a/src/fluent-bit/plugins/in_winevtlog/winevtlog.c b/src/fluent-bit/plugins/in_winevtlog/winevtlog.c
new file mode 100644
index 000000000..09d3f9624
--- /dev/null
+++ b/src/fluent-bit/plugins/in_winevtlog/winevtlog.c
@@ -0,0 +1,840 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed 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.
+ */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_sqldb.h>
+#include <fluent-bit/flb_input.h>
+#include "winevtlog.h"
+
+#define EVENT_PROVIDER_NAME_LENGTH 256
+
+static char* convert_wstr(wchar_t *wstr, UINT codePage);
+static wchar_t* convert_str(char *str);
+
+struct winevtlog_channel *winevtlog_subscribe(const char *channel, int read_existing_events,
+ EVT_HANDLE stored_bookmark, const char *query)
+{
+ struct winevtlog_channel *ch;
+ EVT_HANDLE bookmark = NULL;
+ HANDLE signal_event = NULL;
+ DWORD len;
+ DWORD flags = 0L;
+ PWSTR wide_channel = NULL;
+ PWSTR wide_query = NULL;
+ void *buf;
+
+ ch = flb_calloc(1, sizeof(struct winevtlog_channel));
+ if (ch == NULL) {
+ flb_errno();
+ return NULL;
+ }
+
+ ch->name = flb_strdup(channel);
+ if (!ch->name) {
+ flb_errno();
+ flb_free(ch);
+ return NULL;
+ }
+ ch->query = NULL;
+
+ signal_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ // channel : To wide char
+ len = MultiByteToWideChar(CP_UTF8, 0, channel, -1, NULL, 0);
+ wide_channel = flb_malloc(sizeof(PWSTR) * len);
+ MultiByteToWideChar(CP_UTF8, 0, channel, -1, wide_channel, len);
+ if (query != NULL) {
+ // query : To wide char
+ len = MultiByteToWideChar(CP_UTF8, 0, query, -1, NULL, 0);
+ wide_query = flb_malloc(sizeof(PWSTR) * len);
+ MultiByteToWideChar(CP_UTF8, 0, query, -1, wide_query, len);
+ ch->query = flb_strdup(query);
+ }
+
+ if (stored_bookmark) {
+ flags |= EvtSubscribeStartAfterBookmark;
+ } else if (read_existing_events) {
+ flags |= EvtSubscribeStartAtOldestRecord;
+ } else {
+ flags |= EvtSubscribeToFutureEvents;
+ }
+
+ /* The wide_query parameter can handle NULL as `*` for retrieving all events.
+ * ref. https://learn.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtsubscribe
+ */
+ ch->subscription = EvtSubscribe(NULL, signal_event, wide_channel, wide_query,
+ stored_bookmark, NULL, NULL, flags);
+ if (!ch->subscription) {
+ flb_error("[in_winevtlog] cannot subscribe '%s' (%i)", channel, GetLastError());
+ flb_free(ch->name);
+ if (ch->query != NULL) {
+ flb_free(ch->query);
+ }
+ flb_free(ch);
+ return NULL;
+ }
+ ch->signal_event = signal_event;
+
+ if (stored_bookmark) {
+ ch->bookmark = stored_bookmark;
+ }
+ else {
+ bookmark = EvtCreateBookmark(NULL);
+ if (bookmark) {
+ ch->bookmark = bookmark;
+ }
+ else {
+ if (ch->subscription) {
+ EvtClose(ch->subscription);
+ }
+ if (signal_event) {
+ CloseHandle(signal_event);
+ }
+ flb_error("[in_winevtlog] cannot subscribe '%s' (%i)", channel, GetLastError());
+ flb_free(wide_channel);
+ flb_free(ch->name);
+ if (ch->query != NULL) {
+ flb_free(ch->query);
+ }
+ flb_free(ch);
+ return NULL;
+ }
+ }
+
+ flb_free(wide_channel);
+ if (wide_query != NULL) {
+ flb_free(wide_query);
+ }
+
+ return ch;
+}
+
+BOOL cancel_subscription(struct winevtlog_channel *ch)
+{
+ return EvtCancel(ch->subscription);
+}
+
+static void close_handles(struct winevtlog_channel *ch)
+{
+ int i;
+
+ if (ch->subscription) {
+ EvtClose(ch->subscription);
+ ch->subscription = NULL;
+ }
+ if (ch->signal_event) {
+ CloseHandle(ch->signal_event);
+ ch->signal_event = NULL;
+ }
+ if (ch->bookmark) {
+ EvtClose(ch->bookmark);
+ ch->bookmark = NULL;
+ }
+ for (i = 0; i < ch->count; i++) {
+ if (ch->events[i]) {
+ EvtClose(ch->events[i]);
+ ch->events[i] = NULL;
+ }
+ }
+ ch->count = 0;
+}
+
+
+void winevtlog_close(struct winevtlog_channel *ch)
+{
+ flb_free(ch->name);
+ if (ch->query != NULL) {
+ flb_free(ch->query);
+ }
+ close_handles(ch);
+
+ flb_free(ch);
+}
+
+// Render the event as an XML string and print it.
+PWSTR render_event(EVT_HANDLE hEvent, DWORD flags, unsigned int *event_size)
+{
+ DWORD status = ERROR_SUCCESS;
+ DWORD buffer_size = 0;
+ DWORD buffer_used = 0;
+ DWORD count = 0;
+ LPWSTR event_xml = NULL;
+
+ if (flags != EvtRenderEventXml && flags != EvtRenderBookmark) {
+ flb_error("Invalid flags is specified: %d", flags);
+ return NULL;
+ }
+
+ if (!EvtRender(NULL, hEvent, flags, buffer_size, event_xml, &buffer_used, &count)) {
+ status = GetLastError();
+ if (status == ERROR_INSUFFICIENT_BUFFER) {
+ buffer_size = buffer_used;
+ /* return buffer size */
+ *event_size = buffer_size;
+ event_xml = (LPWSTR)flb_malloc(buffer_size);
+ if (event_xml) {
+ EvtRender(NULL, hEvent, flags, buffer_size, event_xml, &buffer_used, &count);
+ }
+ else {
+ flb_error("malloc failed");
+ goto cleanup;
+ }
+ }
+
+ status = GetLastError();
+ if (status != ERROR_SUCCESS) {
+ flb_error("EvtRender failed with %d", GetLastError());
+ goto cleanup;
+ }
+ }
+
+ return event_xml;
+
+cleanup:
+
+ if (event_xml) {
+ flb_free(event_xml);
+ }
+
+ return NULL;
+}
+
+DWORD render_system_event(EVT_HANDLE event, PEVT_VARIANT *system, unsigned int *system_size)
+{
+ DWORD status = ERROR_SUCCESS;
+ EVT_HANDLE context = NULL;
+ DWORD buffer_size = 0;
+ DWORD buffer_used = 0;
+ DWORD count = 0;
+ PEVT_VARIANT rendered_system = NULL;
+
+ context = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
+ if (NULL == context) {
+ status = GetLastError();
+ flb_error("failed to create RenderContext with %d", status);
+
+ goto cleanup;
+ }
+ if (!EvtRender(context,
+ event,
+ EvtRenderEventValues,
+ buffer_size,
+ rendered_system,
+ &buffer_used,
+ &count)) {
+ status = GetLastError();
+
+ if (status == ERROR_INSUFFICIENT_BUFFER) {
+ buffer_size = buffer_used;
+ rendered_system = (PEVT_VARIANT)flb_malloc(buffer_size);
+ if (rendered_system) {
+ EvtRender(context,
+ event,
+ EvtRenderEventValues,
+ buffer_size,
+ rendered_system,
+ &buffer_used,
+ &count);
+ status = GetLastError();
+ *system_size = buffer_used;
+ } else {
+ if (rendered_system)
+ flb_free(rendered_system);
+
+ flb_error("failed to malloc memory with %d", status);
+
+ goto cleanup;
+ }
+ }
+
+ if (ERROR_SUCCESS != status) {
+ EvtClose(context);
+ flb_free(rendered_system);
+
+ return status;
+ }
+ }
+
+ *system = rendered_system;
+
+cleanup:
+
+ if (context) {
+ EvtClose(context);
+ }
+
+ return status;
+}
+
+
+PWSTR get_message(EVT_HANDLE metadata, EVT_HANDLE handle, unsigned int *message_size)
+{
+ WCHAR* buffer = NULL;
+ DWORD status = ERROR_SUCCESS;
+ DWORD buffer_size = 0;
+ DWORD buffer_used = 0;
+ LPVOID format_message_buffer;
+ WCHAR* message = NULL;
+ char *error_message = NULL;
+
+ // Get the size of the buffer
+ if (!EvtFormatMessage(metadata, handle, 0, 0, NULL,
+ EvtFormatMessageEvent, buffer_size, buffer, &buffer_used)) {
+ status = GetLastError();
+ if (ERROR_INSUFFICIENT_BUFFER == status) {
+ buffer_size = buffer_used;
+ buffer = flb_malloc(sizeof(WCHAR) * buffer_size);
+ if (!buffer) {
+ flb_error("failed to malloc message buffer");
+
+ goto cleanup;
+ }
+ if (!EvtFormatMessage(metadata,
+ handle,
+ 0xffffffff,
+ 0,
+ NULL,
+ EvtFormatMessageEvent,
+ buffer_size,
+ buffer,
+ &buffer_used)) {
+ status = GetLastError();
+ *message_size = buffer_used;
+
+ if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
+ switch (status) {
+ case ERROR_EVT_MESSAGE_NOT_FOUND:
+ case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
+ case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
+ case ERROR_RESOURCE_DATA_NOT_FOUND:
+ case ERROR_RESOURCE_TYPE_NOT_FOUND:
+ case ERROR_RESOURCE_NAME_NOT_FOUND:
+ case ERROR_RESOURCE_LANG_NOT_FOUND:
+ case ERROR_MUI_FILE_NOT_FOUND:
+ case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT:
+ {
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ status,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (WCHAR*)(&format_message_buffer),
+ 0,
+ NULL) == 0)
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ status,
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+ (WCHAR*)(&format_message_buffer),
+ 0,
+ NULL);
+ error_message = convert_wstr((WCHAR*)format_message_buffer, CP_ACP);
+ flb_error("Failed to get message with %d, err = %s", status, error_message);
+ flb_free(error_message);
+
+ message = _wcsdup((WCHAR*)format_message_buffer);
+ LocalFree(format_message_buffer);
+
+ goto cleanup;
+ }
+ }
+
+ if (status != ERROR_INSUFFICIENT_BUFFER) {
+ flb_error("failed with %d", status);
+ goto cleanup;
+ }
+ }
+ }
+ }
+ }
+
+ message = _wcsdup(buffer);
+
+cleanup:
+ if (buffer) {
+ flb_free(buffer);
+ }
+
+ return message;
+}
+
+PWSTR get_description(EVT_HANDLE handle, LANGID langID, unsigned int *message_size)
+{
+ WCHAR *buffer[EVENT_PROVIDER_NAME_LENGTH];
+ PEVT_VARIANT values = NULL;
+ DWORD buffer_used = 0;
+ DWORD status = ERROR_SUCCESS;
+ DWORD count = 0;
+ WCHAR *message = NULL;
+ EVT_HANDLE metadata = NULL;
+
+ PCWSTR properties[] = { L"Event/System/Provider/@Name" };
+ EVT_HANDLE context =
+ EvtCreateRenderContext(1, properties, EvtRenderContextValues);
+ if (context == NULL) {
+ flb_error("Failed to create renderContext");
+ goto cleanup;
+ }
+
+ if (EvtRender(context,
+ handle,
+ EvtRenderEventValues,
+ EVENT_PROVIDER_NAME_LENGTH,
+ buffer,
+ &buffer_used,
+ &count) != FALSE){
+ status = ERROR_SUCCESS;
+ }
+ else {
+ status = GetLastError();
+ }
+
+ if (status != ERROR_SUCCESS) {
+ flb_error("failed to query RenderContextValues");
+ goto cleanup;
+ }
+ values = (PEVT_VARIANT)buffer;
+
+ metadata = EvtOpenPublisherMetadata(
+ NULL, // TODO: Remote handle
+ values[0].StringVal,
+ NULL,
+ MAKELCID(langID, SORT_DEFAULT),
+ 0);
+ if (metadata == NULL) {
+ goto cleanup;
+ }
+
+ message = get_message(metadata, handle, message_size);
+
+cleanup:
+ if (context) {
+ EvtClose(context);
+ }
+
+ if (metadata) {
+ EvtClose(metadata);
+ }
+
+ return message;
+}
+
+int get_string_inserts(EVT_HANDLE handle, PEVT_VARIANT *string_inserts_values,
+ UINT *prop_count, unsigned int *string_inserts_size)
+{
+ PEVT_VARIANT values;
+ DWORD buffer_size = 0;
+ DWORD buffer_size_used = 0;
+ DWORD count = 0;
+ BOOL succeeded = FLB_TRUE;
+
+ EVT_HANDLE context = EvtCreateRenderContext(0, NULL, EvtRenderContextUser);
+ if (context == NULL) {
+ flb_error("Failed to create renderContext");
+ succeeded = FLB_FALSE;
+ goto cleanup;
+ }
+
+ // Get the size of the buffer
+ EvtRender(context, handle, EvtRenderEventValues, 0, NULL, &buffer_size, &count);
+ values = (PEVT_VARIANT)flb_malloc(buffer_size);
+
+ succeeded = EvtRender(context,
+ handle,
+ EvtRenderContextValues,
+ buffer_size,
+ values,
+ &buffer_size_used,
+ &count);
+
+ if (!succeeded) {
+ flb_error("Failed to get string inserts with %d\n", GetLastError());
+ goto cleanup;
+ }
+
+ *prop_count = count;
+ *string_inserts_values = values;
+ *string_inserts_size = buffer_size;
+
+cleanup:
+
+ if (context != NULL) {
+ EvtClose(context);
+ }
+
+ return succeeded;
+}
+
+static int winevtlog_next(struct winevtlog_channel *ch, int hit_threshold)
+{
+ EVT_HANDLE events[SUBSCRIBE_ARRAY_SIZE];
+ DWORD count = 0;
+ DWORD status = ERROR_SUCCESS;
+ BOOL has_next = FALSE;
+ int i;
+
+ /* If subscription handle is NULL, it should return false. */
+ if (!ch->subscription) {
+ flb_error("Invalid subscription is passed");
+ return FLB_FALSE;
+ }
+
+ if (hit_threshold) {
+ return FLB_FALSE;
+ }
+
+ has_next = EvtNext(ch->subscription, SUBSCRIBE_ARRAY_SIZE,
+ events, INFINITE, 0, &count);
+
+ if (!has_next) {
+ status = GetLastError();
+ if (ERROR_CANCELLED == status) {
+ return FLB_FALSE;
+ }
+ if (ERROR_NO_MORE_ITEMS != status) {
+ return FLB_FALSE;
+ }
+ }
+
+ if (status == ERROR_SUCCESS) {
+ ch->count = count;
+ for (i = 0; i < count; i++) {
+ ch->events[i] = events[i];
+ EvtUpdateBookmark(ch->bookmark, ch->events[i]);
+ }
+
+ return FLB_TRUE;
+ }
+
+ return FLB_FALSE;
+}
+
+/*
+ * Read from an open Windows Event Log channel.
+ */
+int winevtlog_read(struct winevtlog_channel *ch, struct winevtlog_config *ctx,
+ unsigned int *read)
+{
+ DWORD status = ERROR_SUCCESS;
+ PWSTR system_xml = NULL;
+ unsigned int system_size = 0;
+ unsigned int message_size = 0;
+ unsigned int string_inserts_size = 0;
+ int hit_threshold = FLB_FALSE;
+ unsigned int read_size = 0;
+ PWSTR message = NULL;
+ PEVT_VARIANT rendered_system = NULL;
+ PEVT_VARIANT string_inserts = NULL;
+ UINT count_inserts = 0;
+ DWORD i = 0;
+
+ while (winevtlog_next(ch, hit_threshold)) {
+ for (i = 0; i < ch->count; i++) {
+ if (ctx->render_event_as_xml) {
+ system_xml = render_event(ch->events[i], EvtRenderEventXml, &system_size);
+ message = get_description(ch->events[i], LANG_NEUTRAL, &message_size);
+ get_string_inserts(ch->events[i], &string_inserts, &count_inserts, &string_inserts_size);
+ if (system_xml) {
+ /* Caluculate total allocated size: system + message + string_inserts */
+ read_size += (system_size + message_size + string_inserts_size);
+ winevtlog_pack_xml_event(system_xml, message, string_inserts,
+ count_inserts, ch, ctx);
+
+ flb_free(string_inserts);
+ flb_free(system_xml);
+ if (message)
+ flb_free(message);
+ }
+ }
+ else {
+ render_system_event(ch->events[i], &rendered_system, &system_size);
+ message = get_description(ch->events[i], LANG_NEUTRAL, &message_size);
+ get_string_inserts(ch->events[i], &string_inserts, &count_inserts, &string_inserts_size);
+ if (rendered_system) {
+ /* Caluculate total allocated size: system + message + string_inserts */
+ read_size += (system_size + message_size + string_inserts_size);
+ winevtlog_pack_event(rendered_system, message, string_inserts,
+ count_inserts, ch, ctx);
+
+ flb_free(string_inserts);
+ flb_free(rendered_system);
+ if (message)
+ flb_free(message);
+ }
+ }
+ }
+
+ /* Closes any events in case an error occurred above. */
+ for (i = 0; i < ch->count; i++) {
+ if (NULL != ch->events[i]) {
+ EvtClose(ch->events[i]);
+ ch->events[i] = NULL;
+ }
+ }
+
+ if (read_size > ctx->total_size_threshold) {
+ hit_threshold = FLB_TRUE;
+ /* hit reading threshold on read, then break. */
+ break;
+ }
+ }
+
+ *read = read_size;
+
+ return 0;
+}
+
+/*
+ * Open multiple channels at once. The return value is a linked
+ * list of winevtlog_channel objects.
+ *
+ * "channels" are comma-separated names like "Setup,Security".
+ */
+struct mk_list *winevtlog_open_all(const char *channels, struct winevtlog_config *ctx)
+{
+ char *tmp;
+ char *channel;
+ char *state;
+ struct winevtlog_channel *ch;
+ struct mk_list *list;
+
+ tmp = flb_strdup(channels);
+ if (!tmp) {
+ flb_errno();
+ return NULL;
+ }
+
+ list = flb_malloc(sizeof(struct mk_list));
+ if (!list) {
+ flb_errno();
+ flb_free(tmp);
+ return NULL;
+ }
+ mk_list_init(list);
+
+ channel = strtok_s(tmp , ",", &state);
+ while (channel) {
+ ch = winevtlog_subscribe(channel, ctx->read_existing_events, NULL, ctx->event_query);
+ if (ch) {
+ mk_list_add(&ch->_head, list);
+ }
+ else {
+ if (ctx->ignore_missing_channels) {
+ flb_debug("[in_winevtlog] channel '%s' does not exist", channel);
+ }
+ else {
+ flb_free(tmp);
+ winevtlog_close_all(list);
+ return NULL;
+ }
+ }
+ channel = strtok_s(NULL, ",", &state);
+ }
+
+ if (mk_list_size(list) == 0) {
+ flb_free(tmp);
+ winevtlog_close_all(list);
+ return NULL;
+ }
+
+ flb_free(tmp);
+ return list;
+}
+
+void winevtlog_close_all(struct mk_list *list)
+{
+ struct winevtlog_channel *ch;
+ struct mk_list *head;
+ struct mk_list *tmp;
+
+ mk_list_foreach_safe(head, tmp, list) {
+ ch = mk_list_entry(head, struct winevtlog_channel, _head);
+ mk_list_del(&ch->_head);
+ winevtlog_close(ch);
+ }
+ flb_free(list);
+}
+
+/*
+ * Callback function for flb_sqldb_query().
+ */
+static int winevtlog_sqlite_callback(void *data, int argc, char **argv, char **cols)
+{
+ struct winevtlog_sqlite_record *p = data;
+
+ p->name = argv[0];
+ p->bookmark_xml = strdup(argv[1]);
+ p->time_updated = (unsigned int) strtoul(argv[2], NULL, 10);
+ p->created = (unsigned int) strtoul(argv[3], NULL, 10);
+ return 0;
+}
+
+static wchar_t* convert_str(char *str)
+{
+ int size = 0;
+ wchar_t *buf = NULL;
+
+ size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
+ if (size == 0) {
+ return NULL;
+ }
+
+ buf = flb_malloc(sizeof(PWSTR) * size);
+ if (buf == NULL) {
+ flb_errno();
+ return NULL;
+ }
+ size = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, size);
+ if (size == 0) {
+ flb_free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+static char* convert_wstr(wchar_t *wstr, UINT codePage)
+{
+ int size = 0;
+ char *buf = NULL;
+
+ size = WideCharToMultiByte(codePage, 0, wstr, -1, NULL, 0, NULL, NULL);
+ if (size == 0) {
+ return NULL;
+ }
+
+ buf = flb_malloc(size);
+ if (buf == NULL) {
+ flb_errno();
+ return NULL;
+ }
+ size = WideCharToMultiByte(codePage, 0, wstr, -1, buf, size, NULL, NULL);
+ if (size == 0) {
+ flb_free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+/*
+ * Load the bookmark from SQLite DB.
+ */
+int winevtlog_sqlite_load(struct winevtlog_channel *ch, struct flb_sqldb *db)
+{
+ int ret;
+ char query[1024];
+ struct winevtlog_sqlite_record record = {0};
+ EVT_HANDLE bookmark = NULL;
+ PWSTR bookmark_xml = NULL;
+ struct winevtlog_channel *re_ch = NULL;
+
+ snprintf(query, sizeof(query) - 1, SQL_GET_CHANNEL, ch->name);
+
+ ret = flb_sqldb_query(db, query, winevtlog_sqlite_callback, &record);
+ if (ret == FLB_ERROR) {
+ return -1;
+ }
+
+ if (record.created) {
+ ch->time_created = record.created;
+ }
+ if (record.time_updated) {
+ ch->time_updated = record.time_updated;
+ }
+
+ if (record.name) {
+ bookmark_xml = convert_str(record.bookmark_xml);
+ if (bookmark_xml) {
+ bookmark = EvtCreateBookmark(bookmark_xml);
+ if (bookmark) {
+ /* re-create subscription handles */
+ re_ch = winevtlog_subscribe(ch->name, FLB_FALSE, bookmark, ch->query);
+ if (re_ch != NULL) {
+ close_handles(ch);
+
+ ch->bookmark = re_ch->bookmark;
+ ch->subscription = re_ch->subscription;
+ ch->signal_event = re_ch->signal_event;
+ }
+ else {
+ flb_error("Failed to subscribe with bookmark XML: %s\n", record.bookmark_xml);
+ ch->bookmark = EvtCreateBookmark(NULL);
+ }
+ }
+ else {
+ flb_error("Failed to load bookmark XML with %d\n", GetLastError());
+ ch->bookmark = EvtCreateBookmark(NULL);
+ }
+ }
+ if (bookmark_xml) {
+ flb_free(bookmark_xml);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Save the bookmark into SQLite DB.
+ */
+int winevtlog_sqlite_save(struct winevtlog_channel *ch, struct flb_sqldb *db)
+{
+ int ret;
+ char query[1024];
+ PWSTR wide_bookmark_xml = NULL;
+ char *bookmark_xml;
+ int used_size = 0;
+
+ wide_bookmark_xml = render_event(ch->bookmark, EvtRenderBookmark, &used_size);
+ if (wide_bookmark_xml == NULL) {
+ flb_error("failed to render bookmark with %d", GetLastError());
+ flb_free(wide_bookmark_xml);
+
+ return -1;
+ }
+ bookmark_xml = convert_wstr(wide_bookmark_xml, CP_UTF8);
+ if (bookmark_xml == NULL) {
+ flb_error("failed to convert Wider string with %d", GetLastError());
+ flb_free(wide_bookmark_xml);
+ flb_free(bookmark_xml);
+
+ return -1;
+ }
+
+ snprintf(query, sizeof(query) - 1, SQL_UPDATE_CHANNEL,
+ ch->name, bookmark_xml, ch->time_updated, time(NULL));
+
+ ret = flb_sqldb_query(db, query, NULL, NULL);
+ if (ret == FLB_ERROR) {
+ flb_error("failed to save db with %d", GetLastError());
+ flb_free(wide_bookmark_xml);
+ flb_free(bookmark_xml);
+
+ return -1;
+ }
+
+ flb_free(wide_bookmark_xml);
+ flb_free(bookmark_xml);
+
+ return 0;
+}
diff --git a/src/fluent-bit/plugins/in_winevtlog/winevtlog.h b/src/fluent-bit/plugins/in_winevtlog/winevtlog.h
new file mode 100644
index 000000000..10ef3e457
--- /dev/null
+++ b/src/fluent-bit/plugins/in_winevtlog/winevtlog.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed 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.
+ */
+
+#ifndef FLB_WINEVTLOG_H
+#define FLB_WINEVTLOG_H
+
+#include <winevt.h>
+#include <fluent-bit/flb_log_event_encoder.h>
+
+struct winevtlog_config {
+ unsigned int interval_sec;
+ unsigned int interval_nsec;
+ unsigned int total_size_threshold;
+ int string_inserts;
+ int read_existing_events;
+ int render_event_as_xml;
+ int use_ansi;
+ int ignore_missing_channels;
+ flb_sds_t event_query;
+
+ struct mk_list *active_channel;
+ struct flb_sqldb *db;
+ flb_pipefd_t coll_fd;
+ struct flb_input_instance *ins;
+ struct flb_log_event_encoder *log_encoder;
+};
+
+/* Some channels has very heavy contents for 10 events at same time.
+ * For now, we specify simultaneous subscribe size to 5.
+ */
+#define SUBSCRIBE_ARRAY_SIZE 5
+
+struct winevtlog_channel {
+ EVT_HANDLE subscription;
+ EVT_HANDLE bookmark;
+ HANDLE signal_event;
+ EVT_HANDLE events[SUBSCRIBE_ARRAY_SIZE];
+ int count;
+
+ char *name;
+ char *query;
+ unsigned int time_updated;
+ unsigned int time_created;
+ struct mk_list _head;
+};
+
+struct winevtlog_sqlite_record {
+ char *name;
+ char *bookmark_xml;
+ unsigned int time_updated;
+ unsigned int created;
+};
+
+/*
+ * Open a Windows Event Log channel.
+ */
+struct winevtlog_channel *winevtlog_open(const char *channel);
+void winevtlog_close(struct winevtlog_channel *ch);
+
+/*
+ * Read records from a channel.
+ */
+int winevtlog_read(struct winevtlog_channel *ch,
+ struct winevtlog_config *ctx, unsigned int *read);
+
+/*
+ * A bulk API to handle multiple channels at once using mk_list.
+ *
+ * "channels" are comma-separated names like "Setup,Security".
+ */
+struct mk_list *winevtlog_open_all(const char *channels, struct winevtlog_config *ctx);
+void winevtlog_close_all(struct mk_list *list);
+
+void winevtlog_pack_xml_event(WCHAR *system_xml, WCHAR *message,
+ PEVT_VARIANT string_inserts, UINT count_inserts, struct winevtlog_channel *ch,
+ struct winevtlog_config *ctx);
+void winevtlog_pack_event(PEVT_VARIANT system, WCHAR *message,
+ PEVT_VARIANT string_inserts, UINT count_inserts, struct winevtlog_channel *ch,
+ struct winevtlog_config *ctx);
+
+/*
+ * Save the read offset to disk.
+ */
+int winevtlog_sqlite_load(struct winevtlog_channel *ch, struct flb_sqldb *db);
+int winevtlog_sqlite_save(struct winevtlog_channel *ch, struct flb_sqldb *db);
+
+/*
+ * SQL templates
+ */
+#define SQL_CREATE_CHANNELS \
+ "CREATE TABLE IF NOT EXISTS in_winevtlog_channels (" \
+ " name TEXT PRIMARY KEY," \
+ " bookmark_xml TEXT," \
+ " time_updated INTEGER," \
+ " created INTEGER" \
+ ");"
+
+#define SQL_GET_CHANNEL \
+ "SELECT name, bookmark_xml, time_updated, created" \
+ " FROM in_winevtlog_channels WHERE name = '%s';"
+
+/*
+ * This uses UPCERT i.e. execute INSERT first and fall back to
+ * UPDATE if the entry already exists. It saves the trouble of
+ * doing an existence check manually.
+ *
+ * https://www.sqlite.org/lang_UPSERT.html
+ */
+#define SQL_UPDATE_CHANNEL \
+ "INSERT INTO in_winevtlog_channels" \
+ " (name, bookmark_xml, time_updated, created)" \
+ " VALUES ('%s', \"%s\", %u, %llu)" \
+ " ON CONFLICT(name) DO UPDATE" \
+ " SET bookmark_xml = excluded.bookmark_xml," \
+ " time_updated = excluded.time_updated" \
+
+#endif