1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-#include "libnetdata/libnetdata.h"
4
-#include "libnetdata/required_dummies.h"
5
-
6
-#include "windows-events.h"
7
-
8
-#define WINDOWS_EVENTS_WORKER_THREADS 5
9
-#define WINDOWS_EVENTS_DEFAULT_TIMEOUT 600
10
-#define WINDOWS_EVENTS_SCAN_EVERY_USEC (5 * 60 * USEC_PER_SEC)
11
-#define WINDOWS_EVENTS_PROGRESS_EVERY_UT (250 * USEC_PER_MS)
12
-
13
-#define FUNCTION_PROGRESS_UPDATE_ROWS(rows_read, rows) __atomic_fetch_add(&(rows_read), rows, __ATOMIC_RELAXED)
14
-#define FUNCTION_PROGRESS_UPDATE_BYTES(bytes_read, bytes) __atomic_fetch_add(&(bytes_read), bytes, __ATOMIC_RELAXED)
15
-#define FUNCTION_PROGRESS_EVERY_ROWS (1ULL << 13)
16
-#define FUNCTION_DATA_ONLY_CHECK_EVERY_ROWS (1ULL << 7)
17
-#define ANCHOR_DELTA_UT (10 * USEC_PER_SEC)
18
-
19
-netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
20
-static bool plugin_should_exit = false;
21
-
22
-#define WEVT_FUNCTION_DESCRIPTION "View, search and analyze the Microsoft Windows Events log."
23
-#define WEVT_FUNCTION_NAME "windows-events"
24
-
25
-// functions needed by LQS
26
-
27
-// structures needed by LQS
28
-struct lqs_extension {
29
- struct {
30
- usec_t start_ut;
31
- usec_t stop_ut;
32
- usec_t first_msg_ut;
33
-
34
- uint64_t first_msg_seqnum;
35
- } query_file;
36
-
37
- // struct {
38
- // uint32_t enable_after_samples;
39
- // uint32_t slots;
40
- // uint32_t sampled;
41
- // uint32_t unsampled;
42
- // uint32_t estimated;
43
- // } samples;
44
-
45
- // struct {
46
- // uint32_t enable_after_samples;
47
- // uint32_t every;
48
- // uint32_t skipped;
49
- // uint32_t recalibrate;
50
- // uint32_t sampled;
51
- // uint32_t unsampled;
52
- // uint32_t estimated;
53
- // } samples_per_file;
54
-
55
- // struct {
56
- // usec_t start_ut;
57
- // usec_t end_ut;
58
- // usec_t step_ut;
59
- // uint32_t enable_after_samples;
60
- // uint32_t sampled[SYSTEMD_JOURNAL_SAMPLING_SLOTS];
61
- // uint32_t unsampled[SYSTEMD_JOURNAL_SAMPLING_SLOTS];
62
- // } samples_per_time_slot;
63
-
64
- // per file progress info
65
- // size_t cached_count;
66
-
67
- // progress statistics
68
- usec_t matches_setup_ut;
69
- size_t rows_useful;
70
- size_t rows_read;
71
- size_t bytes_read;
72
- size_t files_matched;
73
- size_t file_working;
74
-};
75
-
76
-// prepare LQS
77
-#define LQS_DEFAULT_SLICE_MODE 0
78
-#define LQS_FUNCTION_NAME WEVT_FUNCTION_NAME
79
-#define LQS_FUNCTION_DESCRIPTION WEVT_FUNCTION_DESCRIPTION
80
-#define LQS_DEFAULT_ITEMS_PER_QUERY 200
81
-#define LQS_DEFAULT_ITEMS_SAMPLING 1000000
82
-#define LQS_SOURCE_TYPE WEVT_SOURCE_TYPE
83
-#define LQS_SOURCE_TYPE_ALL WEVTS_ALL
84
-#define LQS_SOURCE_TYPE_NONE WEVTS_NONE
85
-#define LQS_FUNCTION_GET_INTERNAL_SOURCE_TYPE(value) wevt_internal_source_type(value)
86
-#define LQS_FUNCTION_SOURCE_TO_JSON_ARRAY(wb) wevt_sources_to_json_array(wb)
87
-#include "libnetdata/facets/logs_query_status.h"
88
-
89
-#define WEVT_FIELD_COMPUTER "Computer"
90
-#define WEVT_FIELD_CHANNEL "Channel"
91
-#define WEVT_FIELD_PROVIDER "Provider"
92
-#define WEVT_FIELD_SOURCE "Source"
93
-#define WEVT_FIELD_EVENTRECORDID "EventRecordID"
94
-#define WEVT_FIELD_EVENTID "EventID"
95
-#define WEVT_FIELD_LEVELID "LevelID"
96
-#define WEVT_FIELD_LEVEL "Level"
97
-#define WEVT_FIELD_KEYWORDID "KeywordID"
98
-#define WEVT_FIELD_KEYWORD "Keyword"
99
-#define WEVT_FIELD_OPCODEID "OpcodeID"
100
-#define WEVT_FIELD_OPCODE "Opcode"
101
-#define WEVT_FIELD_USER "User"
102
-#define WEVT_FIELD_EVENT "Event"
103
-#define WEVT_FIELD_TASK "Task"
104
-#define WEVT_FIELD_PROCESSID "ProcessID"
105
-#define WEVT_FIELD_THREADID "ThreadID"
106
-#define WEVT_FIELD_XML "XML"
107
-#define WEVT_FIELD_MESSAGE "Message"
108
-
109
-#define WEVT_ALWAYS_VISIBLE_KEYS NULL
110
-
111
-#define WEVT_KEYS_EXCLUDED_FROM_FACETS \
112
- "|" WEVT_FIELD_EVENT \
113
- "|" WEVT_FIELD_MESSAGE \
114
- "|" WEVT_FIELD_XML \
115
- ""
116
-
117
-#define WEVT_KEYS_INCLUDED_IN_FACETS \
118
- "|" WEVT_FIELD_PROVIDER \
119
- "|" WEVT_FIELD_SOURCE \
120
- "|" WEVT_FIELD_CHANNEL \
121
- "|" WEVT_FIELD_LEVEL \
122
- "|" WEVT_FIELD_KEYWORD \
123
- "|" WEVT_FIELD_OPCODE \
124
- "|" WEVT_FIELD_COMPUTER \
125
- "|" WEVT_FIELD_USER \
126
- ""
127
-
128
-static inline WEVT_QUERY_STATUS check_stop(const bool *cancelled, const usec_t *stop_monotonic_ut) {
129
- if(cancelled && __atomic_load_n(cancelled, __ATOMIC_RELAXED)) {
130
- internal_error(true, "Function has been cancelled");
131
- return WEVT_CANCELLED;
132
- }
133
-
134
- if(now_monotonic_usec() > __atomic_load_n(stop_monotonic_ut, __ATOMIC_RELAXED)) {
135
- internal_error(true, "Function timed out");
136
- return WEVT_TIMED_OUT;
137
- }
138
-
139
- return WEVT_OK;
140
-}
141
-
142
-FACET_ROW_SEVERITY wevt_levelid_to_facet_severity(FACETS *facets __maybe_unused, FACET_ROW *row, void *data __maybe_unused) {
143
- FACET_ROW_KEY_VALUE *levelid_rkv = dictionary_get(row->dict, WEVT_FIELD_LEVELID);
144
- if(!levelid_rkv || levelid_rkv->empty)
145
- return FACET_ROW_SEVERITY_NORMAL;
146
-
147
- int windows_event_level = str2i(buffer_tostring(levelid_rkv->wb));
148
-
149
- switch (windows_event_level) {
150
- case 5: // Verbose
151
- return FACET_ROW_SEVERITY_DEBUG;
152
- case 4: // Information
153
- return FACET_ROW_SEVERITY_NORMAL;
154
- case 3: // Warning
155
- return FACET_ROW_SEVERITY_WARNING;
156
- case 2: // Error
157
- return FACET_ROW_SEVERITY_CRITICAL;
158
- case 1: // Critical
159
- return FACET_ROW_SEVERITY_CRITICAL;
160
- default: // Any unhandled or special levels
161
- return FACET_ROW_SEVERITY_NORMAL;
162
- }
163
-}
164
-
165
-void wevt_render_message(
166
- FACETS *facets __maybe_unused,
167
- BUFFER *json_array,
168
- FACET_ROW_KEY_VALUE *rkv,
169
- FACET_ROW *row,
170
- void *data __maybe_unused) {
171
-
172
- buffer_flush(rkv->wb);
173
-
174
- const FACET_ROW_KEY_VALUE *event_rkv = dictionary_get(row->dict, WEVT_FIELD_EVENT);
175
- if(!event_rkv || !buffer_strlen(event_rkv->wb)) {
176
- const FACET_ROW_KEY_VALUE *event_id_rkv = dictionary_get(row->dict, WEVT_FIELD_EVENTID);
177
- if(event_id_rkv && buffer_strlen(event_id_rkv->wb)) {
178
- buffer_fast_strcat(rkv->wb, "EventID ", 8);
179
- buffer_fast_strcat(rkv->wb, buffer_tostring(event_id_rkv->wb), buffer_strlen(event_id_rkv->wb));
180
- }
181
- else
182
- buffer_strcat(rkv->wb, "Unknown EventID ");
183
-
184
- const FACET_ROW_KEY_VALUE *provider_rkv = dictionary_get(row->dict, WEVT_FIELD_PROVIDER);
185
- if(provider_rkv && buffer_strlen(provider_rkv->wb)) {
186
- buffer_fast_strcat(rkv->wb, " of ", 4);
187
- buffer_fast_strcat(rkv->wb, buffer_tostring(provider_rkv->wb), buffer_strlen(provider_rkv->wb));
188
- buffer_putc(rkv->wb, '.');
189
- }
190
- else
191
- buffer_strcat(rkv->wb, "of unknown Provider.");
192
- }
193
- else
194
- buffer_fast_strcat(rkv->wb, buffer_tostring(event_rkv->wb), buffer_strlen(event_rkv->wb));
195
-
196
- const FACET_ROW_KEY_VALUE *xml_rkv = dictionary_get(row->dict, WEVT_FIELD_XML);
197
- if(xml_rkv && buffer_strlen(xml_rkv->wb)) {
198
- const char *xml = buffer_tostring(xml_rkv->wb);
199
-
200
- bool added_event_data = false, added_user_data = false;
201
-
202
- const char *data_start = strstr(xml, "<EventData>");
203
- if(data_start) {
204
- data_start = &data_start[11];
205
- const char *data_end = strstr(data_start, "</EventData>");
206
-
207
- if(data_start && data_end) {
208
- buffer_fast_strcat(rkv->wb, "\n\nRelated event data:\n", 22);
209
- // copy the event data block
210
- buffer_pretty_print_xml(rkv->wb, data_start, data_end - data_start);
211
- added_event_data = true;
212
- }
213
- }
214
-
215
- data_start = strstr(xml, "<UserData>");
216
- if(data_start) {
217
- data_start = &data_start[10];
218
- const char *data_end = strstr(data_start, "</UserData>");
219
-
220
- if(data_start && data_end) {
221
- buffer_fast_strcat(rkv->wb, "\n\nRelated user data:\n", 21);
222
- // copy the event data block
223
- buffer_pretty_print_xml(rkv->wb, data_start, data_end - data_start);
224
- added_user_data = true;
225
- }
226
- }
227
-
228
- if(!added_event_data && !added_user_data)
229
- buffer_strcat(rkv->wb, " Without any related data.");
230
- }
231
-
232
- buffer_json_add_array_item_string(json_array, buffer_tostring(rkv->wb));
233
-}
234
-
235
-static void wevt_register_fields(LOGS_QUERY_STATUS *lqs) {
236
- FACETS *facets = lqs->facets;
237
- LOGS_QUERY_REQUEST *rq = &lqs->rq;
238
-
239
- facets_register_row_severity(facets, wevt_levelid_to_facet_severity, NULL);
240
-
241
- facets_register_key_name(
242
- facets, WEVT_FIELD_COMPUTER,
243
- rq->default_facet | FACET_KEY_OPTION_VISIBLE);
244
-
245
- facets_register_key_name(
246
- facets, WEVT_FIELD_CHANNEL,
247
- rq->default_facet |
248
- FACET_KEY_OPTION_FTS);
249
-
250
- facets_register_key_name(
251
- facets, WEVT_FIELD_PROVIDER,
252
- rq->default_facet |
253
- FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS);
254
-
255
- facets_register_key_name(
256
- facets, WEVT_FIELD_SOURCE,
257
- rq->default_facet |
258
- FACET_KEY_OPTION_FTS);
259
-
260
- facets_register_key_name(
261
- facets, WEVT_FIELD_EVENTID,
262
- rq->default_facet |
263
- FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS);
264
-
265
- facets_register_key_name(
266
- facets, WEVT_FIELD_LEVELID,
267
- rq->default_facet);
268
-
269
- facets_register_key_name(
270
- facets, WEVT_FIELD_LEVEL,
271
- rq->default_facet | FACET_KEY_OPTION_FTS);
272
-
273
- facets_register_key_name(
274
- facets, WEVT_FIELD_KEYWORDID,
275
- rq->default_facet | FACET_KEY_OPTION_FTS);
276
-
277
- facets_register_key_name(
278
- facets, WEVT_FIELD_KEYWORD,
279
- rq->default_facet | FACET_KEY_OPTION_FTS);
280
-
281
- facets_register_key_name(
282
- facets, WEVT_FIELD_OPCODEID,
283
- rq->default_facet | FACET_KEY_OPTION_FTS);
284
-
285
- facets_register_key_name(
286
- facets, WEVT_FIELD_OPCODE,
287
- rq->default_facet | FACET_KEY_OPTION_FTS);
288
-
289
- facets_register_key_name(
290
- facets, WEVT_FIELD_USER,
291
- rq->default_facet | FACET_KEY_OPTION_FTS);
292
-
293
- facets_register_key_name(
294
- facets, WEVT_FIELD_TASK,
295
- rq->default_facet | FACET_KEY_OPTION_FTS);
296
-
297
- facets_register_key_name(
298
- facets, WEVT_FIELD_PROCESSID,
299
- rq->default_facet | FACET_KEY_OPTION_FTS);
300
-
301
- facets_register_key_name(
302
- facets, WEVT_FIELD_THREADID,
303
- rq->default_facet | FACET_KEY_OPTION_FTS);
304
-
305
- facets_register_key_name(
306
- facets, WEVT_FIELD_EVENT,
307
- FACET_KEY_OPTION_NEVER_FACET |
308
- FACET_KEY_OPTION_FTS);
309
-
310
- facets_register_key_name(
311
- facets, WEVT_FIELD_TASK,
312
- rq->default_facet | FACET_KEY_OPTION_FTS);
313
-
314
- facets_register_key_name(
315
- facets, WEVT_FIELD_PROCESSID,
316
- rq->default_facet | FACET_KEY_OPTION_FTS);
317
-
318
- facets_register_key_name(
319
- facets, WEVT_FIELD_THREADID,
320
- rq->default_facet | FACET_KEY_OPTION_FTS);
321
-
322
- facets_register_key_name(
323
- facets, WEVT_FIELD_XML,
324
- FACET_KEY_OPTION_NEVER_FACET |
325
- FACET_KEY_OPTION_FTS);
326
-
327
- // facets_register_key_name(
328
- // facets, WEVT_FIELD_MESSAGE,
329
- // FACET_KEY_OPTION_NEVER_FACET | FACET_KEY_OPTION_MAIN_TEXT |
330
- // FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS);
331
-
332
- facets_register_dynamic_key_name(
333
- facets, WEVT_FIELD_MESSAGE,
334
- FACET_KEY_OPTION_NEVER_FACET | FACET_KEY_OPTION_MAIN_TEXT | FACET_KEY_OPTION_VISIBLE,
335
- wevt_render_message, NULL);
336
-}
337
-
338
-static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUERY_SOURCE *src, usec_t *msg_ut __maybe_unused, WEVT_EVENT *e) {
339
- size_t len, bytes = log->ops.content.len;
340
-
341
- bytes += log->ops.provider.used * 2; // unicode is double
342
- facets_add_key_value_length(facets,
343
- WEVT_FIELD_PROVIDER, sizeof(WEVT_FIELD_PROVIDER) - 1,
344
- log->ops.provider.data, log->ops.provider.used - 1);
345
-
346
- bytes += log->ops.source.used * 2;
347
- facets_add_key_value_length(facets,
348
- WEVT_FIELD_SOURCE, sizeof(WEVT_FIELD_SOURCE) - 1,
349
- log->ops.source.data, log->ops.source.used - 1);
350
-
351
- if(log->ops.channel.used > 1) {
352
- bytes += log->ops.channel.used * 2;
353
- facets_add_key_value_length(facets,
354
- WEVT_FIELD_CHANNEL, sizeof(WEVT_FIELD_CHANNEL) - 1,
355
- log->ops.channel.data, log->ops.channel.used - 1);
356
- }
357
- else {
358
- bytes += src->fullname_len * 2;
359
- facets_add_key_value_length(facets,
360
- WEVT_FIELD_CHANNEL, sizeof(WEVT_FIELD_CHANNEL) - 1,
361
- src->fullname, src->fullname_len);
362
- }
363
-
364
- static __thread char id_str[24];
365
- len = print_uint64(id_str, e->id);
366
- bytes += len;
367
- facets_add_key_value_length(facets,
368
- WEVT_FIELD_EVENTRECORDID, sizeof(WEVT_FIELD_EVENTRECORDID) - 1,
369
- id_str, len);
370
-
371
- static __thread char level_id_str[24];
372
- len = print_uint64(level_id_str, e->level);
373
- bytes += len;
374
- facets_add_key_value_length(facets,
375
- WEVT_FIELD_LEVELID, sizeof(WEVT_FIELD_LEVELID) - 1,
376
- level_id_str, len);
377
-
378
- bytes += log->ops.level.used * 2;
379
- facets_add_key_value_length(facets,
380
- WEVT_FIELD_LEVEL, sizeof(WEVT_FIELD_LEVEL) - 1,
381
- log->ops.level.data, log->ops.level.used - 1);
382
-
383
- if(e->keyword) {
384
- static __thread char keyword_id_str[24];
385
- len = print_uint64_hex(keyword_id_str, e->keyword);
386
- bytes += len;
387
- facets_add_key_value_length(facets,
388
- WEVT_FIELD_KEYWORDID, sizeof(WEVT_FIELD_KEYWORDID) - 1,
389
- keyword_id_str, len);
390
-
391
- bytes += log->ops.keyword.used * 2;
392
- facets_add_key_value_length(facets,
393
- WEVT_FIELD_KEYWORD, sizeof(WEVT_FIELD_KEYWORD) - 1,
394
- log->ops.keyword.data, log->ops.keyword.used - 1);
395
- }
396
-
397
- bytes += log->ops.computer.used * 2;
398
- facets_add_key_value_length(facets,
399
- WEVT_FIELD_COMPUTER, sizeof(WEVT_FIELD_COMPUTER) - 1,
400
- log->ops.computer.data, log->ops.computer.used - 1);
401
-
402
- if(e->opcode) {
403
- static __thread char opcode_id_str[24];
404
- len = print_uint64(opcode_id_str, e->opcode);
405
- bytes += len;
406
- facets_add_key_value_length(facets,
407
- WEVT_FIELD_OPCODEID, sizeof(WEVT_FIELD_OPCODEID) - 1,
408
- opcode_id_str, len);
409
-
410
- bytes += log->ops.opcode.used * 2;
411
- facets_add_key_value_length(facets,
412
- WEVT_FIELD_OPCODE, sizeof(WEVT_FIELD_OPCODE) - 1,
413
- log->ops.opcode.data, log->ops.opcode.used - 1);
414
- }
415
-
416
- bytes += log->ops.user.used * 2;
417
- facets_add_key_value_length(facets,
418
- WEVT_FIELD_USER, sizeof(WEVT_FIELD_USER) - 1,
419
- log->ops.user.data, log->ops.user.used - 1);
420
-
421
- if(e->event_id) {
422
- static __thread char event_id_str[24];
423
- len = print_uint64(event_id_str, e->event_id);
424
- bytes += len;
425
- facets_add_key_value_length(facets,
426
- WEVT_FIELD_EVENTID, sizeof(WEVT_FIELD_EVENTID) - 1,
427
- event_id_str, len);
428
-
429
- bytes += log->ops.event.used * 2;
430
- facets_add_key_value_length(facets,
431
- WEVT_FIELD_EVENT, sizeof(WEVT_FIELD_EVENT) - 1,
432
- log->ops.event.data, log->ops.event.used - 1);
433
- }
434
-
435
- if(e->task) {
436
- static __thread char task_str[24];
437
- len = print_uint64(task_str, e->task);
438
- bytes += len;
439
- facets_add_key_value_length(facets,
440
- WEVT_FIELD_TASK, sizeof(WEVT_FIELD_TASK) - 1,
441
- task_str, len);
442
- }
443
-
444
- if(e->process_id) {
445
- static __thread char process_id_str[24];
446
- len = print_uint64(process_id_str, e->process_id);
447
- bytes += len;
448
- facets_add_key_value_length(facets,
449
- WEVT_FIELD_PROCESSID, sizeof(WEVT_FIELD_PROCESSID) - 1,
450
- process_id_str, len);
451
- }
452
-
453
- if(e->thread_id) {
454
- static __thread char thread_id_str[24];
455
- len = print_uint64(thread_id_str, e->thread_id);
456
- bytes += len;
457
- facets_add_key_value_length(facets,
458
- WEVT_FIELD_THREADID, sizeof(WEVT_FIELD_THREADID) - 1,
459
- thread_id_str, len);
460
- }
461
-
462
- bytes += log->ops.xml.used * 2;
463
- facets_add_key_value_length(facets,
464
- WEVT_FIELD_XML, sizeof(WEVT_FIELD_XML) - 1,
465
- log->ops.xml.data, log->ops.xml.used - 1);
466
-
467
- // facets_add_key_value_length(facets,
468
- // WEVT_FIELD_MESSAGE, sizeof(WEVT_FIELD_MESSAGE) - 1,
469
- // buffer_tostring(log->ops.message), buffer_strlen(log->ops.message));
470
-
471
- return bytes;
472
-}
473
-
474
-static WEVT_QUERY_STATUS wevt_query_backward(
475
- WEVT_LOG *log, BUFFER *wb __maybe_unused, FACETS *facets,
476
- LOGS_QUERY_SOURCE *src,
477
- LOGS_QUERY_STATUS *lqs)
478
-{
479
-
480
- usec_t anchor_delta = ANCHOR_DELTA_UT;
481
-
482
- usec_t start_ut = ((lqs->rq.data_only && lqs->anchor.start_ut) ? lqs->anchor.start_ut : lqs->rq.before_ut) + anchor_delta;
483
- usec_t stop_ut = (lqs->rq.data_only && lqs->anchor.stop_ut) ? lqs->anchor.stop_ut : lqs->rq.after_ut;
484
- bool stop_when_full = (lqs->rq.data_only && !lqs->anchor.stop_ut);
485
-
486
- lqs->c.query_file.start_ut = start_ut;
487
- lqs->c.query_file.stop_ut = stop_ut;
488
-
489
- log->event_query = wevt_query(channel2unicode(src->fullname), start_ut, true);
490
- if(!log->event_query)
491
- return WEVT_FAILED_TO_SEEK;
492
-
493
- size_t errors_no_timestamp = 0;
494
- usec_t latest_msg_ut = 0; // the biggest timestamp we have seen so far
495
- usec_t first_msg_ut = 0; // the first message we got from the db
496
- size_t row_counter = 0, last_row_counter = 0, rows_useful = 0;
497
- size_t bytes = 0, last_bytes = 0;
498
-
499
- usec_t last_usec_from = 0;
500
- usec_t last_usec_to = 0;
501
-
502
- WEVT_QUERY_STATUS status = WEVT_OK;
503
-
504
- facets_rows_begin(facets);
505
- WEVT_EVENT e;
506
- while (status == WEVT_OK && wevt_get_next_event(log, &e, true)) {
507
- usec_t msg_ut = e.created_ns / NSEC_PER_USEC;
508
-
509
- if(unlikely(!msg_ut)) {
510
- errors_no_timestamp++;
511
- continue;
512
- }
513
-
514
- if (unlikely(msg_ut > start_ut))
515
- continue;
516
-
517
- if (unlikely(msg_ut < stop_ut))
518
- break;
519
-
520
- if(unlikely(msg_ut > latest_msg_ut))
521
- latest_msg_ut = msg_ut;
522
-
523
- if(unlikely(!first_msg_ut)) {
524
- first_msg_ut = msg_ut;
525
- lqs->c.query_file.first_msg_ut = msg_ut;
526
- }
527
-
528
-// sampling_t sample = is_row_in_sample(log, lqs, src, msg_ut,
529
-// FACETS_ANCHOR_DIRECTION_BACKWARD,
530
-// facets_row_candidate_to_keep(facets, msg_ut));
531
-//
532
-// if(sample == SAMPLING_FULL) {
533
- bytes += wevt_process_event(log, facets, src, &msg_ut, &e);
534
-
535
- // make sure each line gets a unique timestamp
536
- if(unlikely(msg_ut >= last_usec_from && msg_ut <= last_usec_to))
537
- msg_ut = --last_usec_from;
538
- else
539
- last_usec_from = last_usec_to = msg_ut;
540
-
541
- if(facets_row_finished(facets, msg_ut))
542
- rows_useful++;
543
-
544
- row_counter++;
545
- if(unlikely((row_counter % FUNCTION_DATA_ONLY_CHECK_EVERY_ROWS) == 0 &&
546
- stop_when_full &&
547
- facets_rows(facets) >= lqs->rq.entries)) {
548
- // stop the data only query
549
- usec_t oldest = facets_row_oldest_ut(facets);
550
- if(oldest && msg_ut < (oldest - anchor_delta))
551
- break;
552
- }
553
-
554
- if(unlikely(row_counter % FUNCTION_PROGRESS_EVERY_ROWS == 0)) {
555
- FUNCTION_PROGRESS_UPDATE_ROWS(lqs->c.rows_read, row_counter - last_row_counter);
556
- last_row_counter = row_counter;
557
-
558
- FUNCTION_PROGRESS_UPDATE_BYTES(lqs->c.bytes_read, bytes - last_bytes);
559
- last_bytes = bytes;
560
-
561
- status = check_stop(lqs->cancelled, lqs->stop_monotonic_ut);
562
- }
563
-// }
564
-// else if(sample == SAMPLING_SKIP_FIELDS)
565
-// facets_row_finished_unsampled(facets, msg_ut);
566
-// else {
567
-// sampling_update_running_query_file_estimates(facets, log, lqs, src, msg_ut, FACETS_ANCHOR_DIRECTION_BACKWARD);
568
-// break;
569
-// }
570
- }
571
-
572
- FUNCTION_PROGRESS_UPDATE_ROWS(lqs->c.rows_read, row_counter - last_row_counter);
573
- FUNCTION_PROGRESS_UPDATE_BYTES(lqs->c.bytes_read, bytes - last_bytes);
574
-
575
- lqs->c.rows_useful += rows_useful;
576
-
577
- if(errors_no_timestamp)
578
- netdata_log_error("WINDOWS-EVENTS: %zu events did not have timestamps", errors_no_timestamp);
579
-
580
- if(latest_msg_ut > lqs->last_modified)
581
- lqs->last_modified = latest_msg_ut;
582
-
583
- wevt_query_done(log);
584
-
585
- return status;
586
-}
587
-
588
-static WEVT_QUERY_STATUS wevt_query_forward(
589
- WEVT_LOG *log, BUFFER *wb __maybe_unused, FACETS *facets,
590
- LOGS_QUERY_SOURCE *src,
591
- LOGS_QUERY_STATUS *lqs)
592
-{
593
- usec_t anchor_delta = ANCHOR_DELTA_UT;
594
-
595
- usec_t start_ut = (lqs->rq.data_only && lqs->anchor.start_ut) ? lqs->anchor.start_ut : lqs->rq.after_ut;
596
- usec_t stop_ut = ((lqs->rq.data_only && lqs->anchor.stop_ut) ? lqs->anchor.stop_ut : lqs->rq.before_ut) + anchor_delta;
597
- bool stop_when_full = (lqs->rq.data_only && !lqs->anchor.stop_ut);
598
-
599
- lqs->c.query_file.start_ut = start_ut;
600
- lqs->c.query_file.stop_ut = stop_ut;
601
-
602
- log->event_query = wevt_query(channel2unicode(src->fullname), start_ut, false);
603
- if(!log->event_query)
604
- return WEVT_FAILED_TO_SEEK;
605
-
606
- size_t errors_no_timestamp = 0;
607
- usec_t latest_msg_ut = 0; // the biggest timestamp we have seen so far
608
- usec_t first_msg_ut = 0; // the first message we got from the db
609
- size_t row_counter = 0, last_row_counter = 0, rows_useful = 0;
610
- size_t bytes = 0, last_bytes = 0;
611
-
612
- usec_t last_usec_from = 0;
613
- usec_t last_usec_to = 0;
614
-
615
- WEVT_QUERY_STATUS status = WEVT_OK;
616
-
617
- facets_rows_begin(facets);
618
- WEVT_EVENT e;
619
- while (status == WEVT_OK && wevt_get_next_event(log, &e, true)) {
620
- usec_t msg_ut = e.created_ns / NSEC_PER_USEC;
621
-
622
- if(unlikely(!msg_ut)) {
623
- errors_no_timestamp++;
624
- continue;
625
- }
626
-
627
- if (unlikely(msg_ut < start_ut))
628
- continue;
629
-
630
- if (unlikely(msg_ut > stop_ut))
631
- break;
632
-
633
- if(likely(msg_ut > latest_msg_ut))
634
- latest_msg_ut = msg_ut;
635
-
636
- if(unlikely(!first_msg_ut)) {
637
- first_msg_ut = msg_ut;
638
- lqs->c.query_file.first_msg_ut = msg_ut;
639
- }
640
-
641
-// sampling_t sample = is_row_in_sample(log, lqs, src, msg_ut,
642
-// FACETS_ANCHOR_DIRECTION_FORWARD,
643
-// facets_row_candidate_to_keep(facets, msg_ut));
644
-//
645
-// if(sample == SAMPLING_FULL) {
646
- bytes += wevt_process_event(log, facets, src, &msg_ut, &e);
647
-
648
- // make sure each line gets a unique timestamp
649
- if(unlikely(msg_ut >= last_usec_from && msg_ut <= last_usec_to))
650
- msg_ut = ++last_usec_to;
651
- else
652
- last_usec_from = last_usec_to = msg_ut;
653
-
654
- if(facets_row_finished(facets, msg_ut))
655
- rows_useful++;
656
-
657
- row_counter++;
658
- if(unlikely((row_counter % FUNCTION_DATA_ONLY_CHECK_EVERY_ROWS) == 0 &&
659
- stop_when_full &&
660
- facets_rows(facets) >= lqs->rq.entries)) {
661
- // stop the data only query
662
- usec_t newest = facets_row_newest_ut(facets);
663
- if(newest && msg_ut > (newest + anchor_delta))
664
- break;
665
- }
666
-
667
- if(unlikely(row_counter % FUNCTION_PROGRESS_EVERY_ROWS == 0)) {
668
- FUNCTION_PROGRESS_UPDATE_ROWS(lqs->c.rows_read, row_counter - last_row_counter);
669
- last_row_counter = row_counter;
670
-
671
- FUNCTION_PROGRESS_UPDATE_BYTES(lqs->c.bytes_read, bytes - last_bytes);
672
- last_bytes = bytes;
673
-
674
- status = check_stop(lqs->cancelled, lqs->stop_monotonic_ut);
675
- }
676
-// }
677
-// else if(sample == SAMPLING_SKIP_FIELDS)
678
-// facets_row_finished_unsampled(facets, msg_ut);
679
-// else {
680
-// sampling_update_running_query_file_estimates(facets, log, lqs, src, msg_ut, FACETS_ANCHOR_DIRECTION_FORWARD);
681
-// break;
682
-// }
683
- }
684
-
685
- FUNCTION_PROGRESS_UPDATE_ROWS(lqs->c.rows_read, row_counter - last_row_counter);
686
- FUNCTION_PROGRESS_UPDATE_BYTES(lqs->c.bytes_read, bytes - last_bytes);
687
-
688
- lqs->c.rows_useful += rows_useful;
689
-
690
- if(errors_no_timestamp)
691
- netdata_log_error("WINDOWS-EVENTS: %zu events did not have timestamps", errors_no_timestamp);
692
-
693
- if(latest_msg_ut > lqs->last_modified)
694
- lqs->last_modified = latest_msg_ut;
695
-
696
- wevt_query_done(log);
697
-
698
- return status;
699
-}
700
-
701
-static WEVT_QUERY_STATUS wevt_query_one_channel(
702
- WEVT_LOG *log,
703
- BUFFER *wb, FACETS *facets,
704
- LOGS_QUERY_SOURCE *src,
705
- LOGS_QUERY_STATUS *lqs) {
706
-
707
- errno_clear();
708
-
709
- WEVT_QUERY_STATUS status;
710
- bool matches_filters = true;
711
-
712
- // if(lqs->rq.slice) {
713
- // usec_t started = now_monotonic_usec();
714
- //
715
- // matches_filters = netdata_systemd_filtering_by_journal(j, facets, lqs) || !lqs->rq.filters;
716
- // usec_t ended = now_monotonic_usec();
717
- //
718
- // lqs->c.matches_setup_ut += (ended - started);
719
- // }
720
-
721
- if(matches_filters) {
722
- if(lqs->rq.direction == FACETS_ANCHOR_DIRECTION_FORWARD)
723
- status = wevt_query_forward(log, wb, facets, src, lqs);
724
- else
725
- status = wevt_query_backward(log, wb, facets, src, lqs);
726
- }
727
- else
728
- status = WEVT_NO_CHANNEL_MATCHED;
729
-
730
- return status;
731
-}
732
-
733
-static bool source_is_mine(LOGS_QUERY_SOURCE *src, LOGS_QUERY_STATUS *lqs) {
734
- if((lqs->rq.source_type == WEVTS_NONE && !lqs->rq.sources) || (src->source_type & lqs->rq.source_type) ||
735
- (lqs->rq.sources && simple_pattern_matches(lqs->rq.sources, string2str(src->source)))) {
736
-
737
- if(!src->msg_last_ut)
738
- // the file is not scanned yet, or the timestamps have not been updated,
739
- // so we don't know if it can contribute or not - let's add it.
740
- return true;
741
-
742
- usec_t anchor_delta = 0;
743
- usec_t first_ut = src->msg_first_ut - anchor_delta;
744
- usec_t last_ut = src->msg_last_ut + anchor_delta;
745
-
746
- if(last_ut >= lqs->rq.after_ut && first_ut <= lqs->rq.before_ut)
747
- return true;
748
- }
749
-
750
- return false;
751
-}
752
-
753
-static int wevt_master_query(BUFFER *wb __maybe_unused, LOGS_QUERY_STATUS *lqs __maybe_unused) {
754
- // make sure the sources list is updated
755
- wevt_sources_scan();
756
-
757
- FACETS *facets = lqs->facets;
758
-
759
- WEVT_QUERY_STATUS status = WEVT_NO_CHANNEL_MATCHED;
760
-
761
- lqs->c.files_matched = 0;
762
- lqs->c.file_working = 0;
763
- lqs->c.rows_useful = 0;
764
- lqs->c.rows_read = 0;
765
- lqs->c.bytes_read = 0;
766
-
767
- size_t files_used = 0;
768
- size_t files_max = dictionary_entries(wevt_sources);
769
- const DICTIONARY_ITEM *file_items[files_max];
770
-
771
- // count the files
772
- bool files_are_newer = false;
773
- LOGS_QUERY_SOURCE *src;
774
- dfe_start_read(wevt_sources, src) {
775
- if(!source_is_mine(src, lqs))
776
- continue;
777
-
778
- file_items[files_used++] = dictionary_acquired_item_dup(wevt_sources, src_dfe.item);
779
-
780
- if(src->msg_last_ut > lqs->rq.if_modified_since)
781
- files_are_newer = true;
782
- }
783
- dfe_done(jf);
784
-
785
- lqs->c.files_matched = files_used;
786
-
787
- if(lqs->rq.if_modified_since && !files_are_newer)
788
- return rrd_call_function_error(wb, "not modified", HTTP_RESP_NOT_MODIFIED);
789
-
790
- // sort the files, so that they are optimal for facets
791
- if(files_used >= 2) {
792
- if (lqs->rq.direction == FACETS_ANCHOR_DIRECTION_BACKWARD)
793
- qsort(file_items, files_used, sizeof(const DICTIONARY_ITEM *),
794
- wevt_sources_dict_items_backward_compar);
795
- else
796
- qsort(file_items, files_used, sizeof(const DICTIONARY_ITEM *),
797
- wevt_sources_dict_items_forward_compar);
798
- }
799
-
800
- bool partial = false;
801
- usec_t query_started_ut = now_monotonic_usec();
802
- usec_t started_ut = query_started_ut;
803
- usec_t ended_ut = started_ut;
804
- usec_t duration_ut = 0, max_duration_ut = 0;
805
- usec_t progress_duration_ut = 0;
806
-
807
- WEVT_LOG *log = wevt_openlog6();
808
- if(!log) {
809
- netdata_log_error("WEVT: cannot windows event log");
810
- return rrd_call_function_error(wb, "cannot open windows events log", HTTP_RESP_INTERNAL_SERVER_ERROR);
811
- }
812
-
813
- // sampling_query_init(lqs, facets);
814
-
815
- buffer_json_member_add_array(wb, "_channels");
816
- for(size_t f = 0; f < files_used ;f++) {
817
- const char *filename = dictionary_acquired_item_name(file_items[f]);
818
- src = dictionary_acquired_item_value(file_items[f]);
819
-
820
- if(!source_is_mine(src, lqs))
821
- continue;
822
-
823
- started_ut = ended_ut;
824
-
825
- // do not even try to do the query if we expect it to pass the timeout
826
- if(ended_ut + max_duration_ut * 3 >= *lqs->stop_monotonic_ut) {
827
- partial = true;
828
- status = WEVT_TIMED_OUT;
829
- break;
830
- }
831
-
832
- lqs->c.file_working++;
833
-
834
- size_t rows_useful = lqs->c.rows_useful;
835
- size_t rows_read = lqs->c.rows_read;
836
- size_t bytes_read = lqs->c.bytes_read;
837
- size_t matches_setup_ut = lqs->c.matches_setup_ut;
838
-
839
- // sampling_file_init(lqs, src);
840
-
841
- WEVT_QUERY_STATUS tmp_status = wevt_query_one_channel(log, wb, facets, src, lqs);
842
-
843
- rows_useful = lqs->c.rows_useful - rows_useful;
844
- rows_read = lqs->c.rows_read - rows_read;
845
- bytes_read = lqs->c.bytes_read - bytes_read;
846
- matches_setup_ut = lqs->c.matches_setup_ut - matches_setup_ut;
847
-
848
- ended_ut = now_monotonic_usec();
849
- duration_ut = ended_ut - started_ut;
850
-
851
- if(duration_ut > max_duration_ut)
852
- max_duration_ut = duration_ut;
853
-
854
- progress_duration_ut += duration_ut;
855
- if(progress_duration_ut >= WINDOWS_EVENTS_PROGRESS_EVERY_UT) {
856
- progress_duration_ut = 0;
857
- netdata_mutex_lock(&stdout_mutex);
858
- pluginsd_function_progress_to_stdout(lqs->rq.transaction, f + 1, files_used);
859
- netdata_mutex_unlock(&stdout_mutex);
860
- }
861
-
862
- buffer_json_add_array_item_object(wb); // channel source
863
- {
864
- // information about the file
865
- buffer_json_member_add_string(wb, "_name", filename);
866
- buffer_json_member_add_uint64(wb, "_source_type", src->source_type);
867
- buffer_json_member_add_string(wb, "_source", string2str(src->source));
868
- buffer_json_member_add_uint64(wb, "_msg_first_ut", src->msg_first_ut);
869
- buffer_json_member_add_uint64(wb, "_msg_last_ut", src->msg_last_ut);
870
-
871
- // information about the current use of the file
872
- buffer_json_member_add_uint64(wb, "duration_ut", ended_ut - started_ut);
873
- buffer_json_member_add_uint64(wb, "rows_read", rows_read);
874
- buffer_json_member_add_uint64(wb, "rows_useful", rows_useful);
875
- buffer_json_member_add_double(wb, "rows_per_second", (double) rows_read / (double) duration_ut * (double) USEC_PER_SEC);
876
- buffer_json_member_add_uint64(wb, "bytes_read", bytes_read);
877
- buffer_json_member_add_double(wb, "bytes_per_second", (double) bytes_read / (double) duration_ut * (double) USEC_PER_SEC);
878
- buffer_json_member_add_uint64(wb, "duration_matches_ut", matches_setup_ut);
879
-
880
- // if(lqs->rq.sampling) {
881
- // buffer_json_member_add_object(wb, "_sampling");
882
- // {
883
- // buffer_json_member_add_uint64(wb, "sampled", lqs->c.samples_per_file.sampled);
884
- // buffer_json_member_add_uint64(wb, "unsampled", lqs->c.samples_per_file.unsampled);
885
- // buffer_json_member_add_uint64(wb, "estimated", lqs->c.samples_per_file.estimated);
886
- // }
887
- // buffer_json_object_close(wb); // _sampling
888
- // }
889
- }
890
- buffer_json_object_close(wb); // channel source
891
-
892
- bool stop = false;
893
- switch(tmp_status) {
894
- case WEVT_OK:
895
- case WEVT_NO_CHANNEL_MATCHED:
896
- status = (status == WEVT_OK) ? WEVT_OK : tmp_status;
897
- break;
898
-
899
- case WEVT_FAILED_TO_OPEN:
900
- case WEVT_FAILED_TO_SEEK:
901
- partial = true;
902
- if(status == WEVT_NO_CHANNEL_MATCHED)
903
- status = tmp_status;
904
- break;
905
-
906
- case WEVT_CANCELLED:
907
- case WEVT_TIMED_OUT:
908
- partial = true;
909
- stop = true;
910
- status = tmp_status;
911
- break;
912
-
913
- case WEVT_NOT_MODIFIED:
914
- internal_fatal(true, "this should never be returned here");
915
- break;
916
- }
917
-
918
- if(stop)
919
- break;
920
- }
921
- buffer_json_array_close(wb); // _channels
922
-
923
- // release the files
924
- for(size_t f = 0; f < files_used ;f++)
925
- dictionary_acquired_item_release(wevt_sources, file_items[f]);
926
-
927
- wevt_closelog6(log);
928
-
929
- switch (status) {
930
- case WEVT_OK:
931
- if(lqs->rq.if_modified_since && !lqs->c.rows_useful)
932
- return rrd_call_function_error(wb, "no useful logs, not modified", HTTP_RESP_NOT_MODIFIED);
933
- break;
934
-
935
- case WEVT_TIMED_OUT:
936
- case WEVT_NO_CHANNEL_MATCHED:
937
- break;
938
-
939
- case WEVT_CANCELLED:
940
- return rrd_call_function_error(wb, "client closed connection", HTTP_RESP_CLIENT_CLOSED_REQUEST);
941
-
942
- case WEVT_NOT_MODIFIED:
943
- return rrd_call_function_error(wb, "not modified", HTTP_RESP_NOT_MODIFIED);
944
-
945
- case WEVT_FAILED_TO_OPEN:
946
- return rrd_call_function_error(wb, "failed to open event log", HTTP_RESP_INTERNAL_SERVER_ERROR);
947
-
948
- case WEVT_FAILED_TO_SEEK:
949
- return rrd_call_function_error(wb, "failed to execute event log query", HTTP_RESP_INTERNAL_SERVER_ERROR);
950
-
951
- default:
952
- return rrd_call_function_error(wb, "unknown status", HTTP_RESP_INTERNAL_SERVER_ERROR);
953
- }
954
-
955
- buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
956
- buffer_json_member_add_boolean(wb, "partial", partial);
957
- buffer_json_member_add_string(wb, "type", "table");
958
-
959
- // build a message for the query
960
- if(!lqs->rq.data_only) {
961
- CLEAN_BUFFER *msg = buffer_create(0, NULL);
962
- CLEAN_BUFFER *msg_description = buffer_create(0, NULL);
963
- ND_LOG_FIELD_PRIORITY msg_priority = NDLP_INFO;
964
-
965
- // if(!journal_files_completed_once()) {
966
- // buffer_strcat(msg, "Journals are still being scanned. ");
967
- // buffer_strcat(msg_description
968
- // , "LIBRARY SCAN: The journal files are still being scanned, you are probably viewing incomplete data. ");
969
- // msg_priority = NDLP_WARNING;
970
- // }
971
-
972
- if(partial) {
973
- buffer_strcat(msg, "Query timed-out, incomplete data. ");
974
- buffer_strcat(msg_description
975
- , "QUERY TIMEOUT: The query timed out and may not include all the data of the selected window. ");
976
- msg_priority = NDLP_WARNING;
977
- }
978
-
979
- // if(lqs->c.samples.estimated || lqs->c.samples.unsampled) {
980
- // double percent = (double) (lqs->c.samples.sampled * 100.0 /
981
- // (lqs->c.samples.estimated + lqs->c.samples.unsampled + lqs->c.samples.sampled));
982
- // buffer_sprintf(msg, "%.2f%% real data", percent);
983
- // buffer_sprintf(msg_description, "ACTUAL DATA: The filters counters reflect %0.2f%% of the data. ", percent);
984
- // msg_priority = MIN(msg_priority, NDLP_NOTICE);
985
- // }
986
- //
987
- // if(lqs->c.samples.unsampled) {
988
- // double percent = (double) (lqs->c.samples.unsampled * 100.0 /
989
- // (lqs->c.samples.estimated + lqs->c.samples.unsampled + lqs->c.samples.sampled));
990
- // buffer_sprintf(msg, ", %.2f%% unsampled", percent);
991
- // buffer_sprintf(msg_description
992
- // , "UNSAMPLED DATA: %0.2f%% of the events exist and have been counted, but their values have not been evaluated, so they are not included in the filters counters. "
993
- // , percent);
994
- // msg_priority = MIN(msg_priority, NDLP_NOTICE);
995
- // }
996
- //
997
- // if(lqs->c.samples.estimated) {
998
- // double percent = (double) (lqs->c.samples.estimated * 100.0 /
999
- // (lqs->c.samples.estimated + lqs->c.samples.unsampled + lqs->c.samples.sampled));
1000
- // buffer_sprintf(msg, ", %.2f%% estimated", percent);
1001
- // buffer_sprintf(msg_description
1002
- // , "ESTIMATED DATA: The query selected a large amount of data, so to avoid delaying too much, the presented data are estimated by %0.2f%%. "
1003
- // , percent);
1004
- // msg_priority = MIN(msg_priority, NDLP_NOTICE);
1005
- // }
1006
-
1007
- buffer_json_member_add_object(wb, "message");
1008
- if(buffer_tostring(msg)) {
1009
- buffer_json_member_add_string(wb, "title", buffer_tostring(msg));
1010
- buffer_json_member_add_string(wb, "description", buffer_tostring(msg_description));
1011
- buffer_json_member_add_string(wb, "status", nd_log_id2priority(msg_priority));
1012
- }
1013
- // else send an empty object if there is nothing to tell
1014
- buffer_json_object_close(wb); // message
1015
- }
1016
-
1017
- if(!lqs->rq.data_only) {
1018
- buffer_json_member_add_time_t(wb, "update_every", 1);
1019
- buffer_json_member_add_string(wb, "help", WEVT_FUNCTION_DESCRIPTION);
1020
- }
1021
-
1022
- if(!lqs->rq.data_only || lqs->rq.tail)
1023
- buffer_json_member_add_uint64(wb, "last_modified", lqs->last_modified);
1024
-
1025
- facets_sort_and_reorder_keys(facets);
1026
- facets_report(facets, wb, used_hashes_registry);
1027
-
1028
- wb->expires = now_realtime_sec() + (lqs->rq.data_only ? 3600 : 0);
1029
- buffer_json_member_add_time_t(wb, "expires", wb->expires);
1030
-
1031
- // if(lqs->rq.sampling) {
1032
- // buffer_json_member_add_object(wb, "_sampling");
1033
- // {
1034
- // buffer_json_member_add_uint64(wb, "sampled", lqs->c.samples.sampled);
1035
- // buffer_json_member_add_uint64(wb, "unsampled", lqs->c.samples.unsampled);
1036
- // buffer_json_member_add_uint64(wb, "estimated", lqs->c.samples.estimated);
1037
- // }
1038
- // buffer_json_object_close(wb); // _sampling
1039
- // }
1040
-
1041
- wb->content_type = CT_APPLICATION_JSON;
1042
- wb->response_code = HTTP_RESP_OK;
1043
- return wb->response_code;
1044
-}
1045
-
1046
-void function_windows_events(const char *transaction, char *function, usec_t *stop_monotonic_ut, bool *cancelled,
1047
- BUFFER *payload, HTTP_ACCESS access __maybe_unused,
1048
- const char *source __maybe_unused, void *data __maybe_unused) {
1049
- bool have_slice = LQS_DEFAULT_SLICE_MODE;
1050
-
1051
- LOGS_QUERY_STATUS tmp_fqs = {
1052
- .facets = lqs_facets_create(
1053
- LQS_DEFAULT_ITEMS_PER_QUERY,
1054
- FACETS_OPTION_ALL_KEYS_FTS,
1055
- WEVT_ALWAYS_VISIBLE_KEYS,
1056
- WEVT_KEYS_INCLUDED_IN_FACETS,
1057
- WEVT_KEYS_EXCLUDED_FROM_FACETS,
1058
- have_slice),
1059
-
1060
- .rq = LOGS_QUERY_REQUEST_DEFAULTS(transaction, have_slice, FACETS_ANCHOR_DIRECTION_BACKWARD),
1061
-
1062
- .cancelled = cancelled,
1063
- .stop_monotonic_ut = stop_monotonic_ut,
1064
- };
1065
- LOGS_QUERY_STATUS *lqs = &tmp_fqs;
1066
-
1067
- CLEAN_BUFFER *wb = lqs_create_output_buffer();
1068
-
1069
- // ------------------------------------------------------------------------
1070
- // parse the parameters
1071
-
1072
- if(lqs_request_parse_and_validate(lqs, wb, function, payload, have_slice, WEVT_FIELD_LEVEL)) {
1073
- wevt_register_fields(lqs);
1074
-
1075
- // ------------------------------------------------------------------------
1076
- // add versions to the response
1077
-
1078
- buffer_json_wevt_versions(wb);
1079
-
1080
- // ------------------------------------------------------------------------
1081
- // run the request
1082
-
1083
- if (lqs->rq.info)
1084
- lqs_info_response(wb, lqs->facets);
1085
- else {
1086
- wevt_master_query(wb, lqs);
1087
- if (wb->response_code == HTTP_RESP_OK)
1088
- buffer_json_finalize(wb);
1089
- }
1090
- }
1091
-
1092
- netdata_mutex_lock(&stdout_mutex);
1093
- pluginsd_function_result_to_stdout(transaction, wb);
1094
- netdata_mutex_unlock(&stdout_mutex);
1095
-
1096
- lqs_cleanup(lqs);
1097
-}
1098
-
1099
-int main(int argc __maybe_unused, char **argv __maybe_unused) {
1100
- clocks_init();
1101
- nd_thread_tag_set("wevt.plugin");
1102
- nd_log_initialize_for_external_plugins("windows-events.plugin");
1103
-
1104
- // ------------------------------------------------------------------------
1105
- // initialization
1106
-
1107
- wevt_sources_init();
1108
-
1109
- // ------------------------------------------------------------------------
1110
- // debug
1111
-
1112
- if(argc >= 2 && strcmp(argv[argc - 1], "debug") == 0) {
1113
- wevt_sources_scan();
1114
-
1115
- struct {
1116
- const char *func;
1117
- } array[] = {
1118
- { "windows-events after:1725866669 before:1725953069 direction:backward last:200 facets:HdUoSYab5wV,Cq2r7mRUv4a,LAnVlsIQfeD,BnPLNbA5VWT,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6,ClaDGnYSQE7,H106l8MXSSr,HREiMN.4Ahu data_only:false source:All" },
1119
- //{ "windows-events after:1725650277 before:1725736677 last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1120
- //{ "windows-events after:1725650284 before:1725736684 last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1121
- //{ "windows-events after:1725650386 before:1725736786 anchor:1725652420809461 direction:forward last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 if_modified_since:1725736649011085 data_only:true delta:true tail:true source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1122
- //{ "windows-events info after:1725650420 before:1725736820" },
1123
- //{ "windows-events after:1725650420 before:1725736820 last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1124
- //{ "windows-events after:1725650430 before:1725736830 last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1125
- { NULL },
1126
- };
1127
-
1128
- for(int i = 0; array[i].func ;i++) {
1129
- bool cancelled = false;
1130
- usec_t stop_monotonic_ut = now_monotonic_usec() + 600 * USEC_PER_SEC;
1131
- //char buf[] = "windows-events after:-86400 before:0 direction:backward last:200 data_only:false slice:true source:all";
1132
- function_windows_events("123", (char *)array[i].func, &stop_monotonic_ut, &cancelled, NULL, HTTP_ACCESS_ALL, NULL, NULL);
1133
- }
1134
- printf("\n\nAll done!\n\n");
1135
- fflush(stdout);
1136
- exit(1);
1137
- }
1138
-
1139
- // ------------------------------------------------------------------------
1140
- // the event loop for functions
1141
-
1142
- struct functions_evloop_globals *wg =
1143
- functions_evloop_init(WINDOWS_EVENTS_WORKER_THREADS, "WEVT", &stdout_mutex, &plugin_should_exit);
1144
-
1145
- functions_evloop_add_function(wg,
1146
- WEVT_FUNCTION_NAME,
1147
- function_windows_events,
1148
- WINDOWS_EVENTS_DEFAULT_TIMEOUT,
1149
- NULL);
1150
-
1151
- // ------------------------------------------------------------------------
1152
- // register functions to netdata
1153
-
1154
- netdata_mutex_lock(&stdout_mutex);
1155
-
1156
- fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"logs\" "HTTP_ACCESS_FORMAT" %d\n",
1157
- WEVT_FUNCTION_NAME, WINDOWS_EVENTS_DEFAULT_TIMEOUT, WEVT_FUNCTION_DESCRIPTION,
1158
- (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA),
1159
- RRDFUNCTIONS_PRIORITY_DEFAULT);
1160
-
1161
- fflush(stdout);
1162
- netdata_mutex_unlock(&stdout_mutex);
1163
-
1164
- // ------------------------------------------------------------------------
1165
-
1166
- const usec_t step_ut = 100 * USEC_PER_MS;
1167
- usec_t send_newline_ut = 0;
1168
- usec_t since_last_scan_ut = WINDOWS_EVENTS_SCAN_EVERY_USEC * 2; // something big to trigger scanning at start
1169
- const bool tty = isatty(fileno(stdout)) == 1;
1170
-
1171
- heartbeat_t hb;
1172
- heartbeat_init(&hb);
1173
- while(!plugin_should_exit) {
1174
-
1175
- if(since_last_scan_ut > WINDOWS_EVENTS_SCAN_EVERY_USEC) {
1176
- wevt_sources_scan();
1177
- since_last_scan_ut = 0;
1178
- }
1179
-
1180
- usec_t dt_ut = heartbeat_next(&hb, step_ut);
1181
- since_last_scan_ut += dt_ut;
1182
- send_newline_ut += dt_ut;
1183
-
1184
- if(!tty && send_newline_ut > USEC_PER_SEC) {
1185
- send_newline_and_flush(&stdout_mutex);
1186
- send_newline_ut = 0;
1187
- }
1188
- }
1189
-
1190
- exit(0);
1191
-}
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "libnetdata/libnetdata.h"
4
+#include "libnetdata/required_dummies.h"
5
+
6
+#include "windows-events.h"
7
+
8
+#define WINDOWS_EVENTS_WORKER_THREADS 5
9
+#define WINDOWS_EVENTS_DEFAULT_TIMEOUT 600
10
+#define WINDOWS_EVENTS_SCAN_EVERY_USEC (5 * 60 * USEC_PER_SEC)
11
+#define WINDOWS_EVENTS_PROGRESS_EVERY_UT (250 * USEC_PER_MS)
12
+
13
+#define FUNCTION_PROGRESS_EVERY_ROWS (2000)
14
+#define FUNCTION_DATA_ONLY_CHECK_EVERY_ROWS (1000)
15
+#define ANCHOR_DELTA_UT (10 * USEC_PER_SEC)
16
+
17
+netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
18
+static bool plugin_should_exit = false;
19
+
20
+#define WEVT_FUNCTION_DESCRIPTION "View, search and analyze the Microsoft Windows Events log."
21
+#define WEVT_FUNCTION_NAME "windows-events"
22
+
23
+// functions needed by LQS
24
+
25
+// structures needed by LQS
26
+struct lqs_extension {
27
+ wchar_t *query;
28
+
29
+ struct {
30
+ struct {
31
+ size_t completed;
32
+ size_t total;
33
+ } queries;
34
+
35
+ struct {
36
+ size_t current_query_total;
37
+ size_t completed;
38
+ size_t total;
39
+ } entries;
40
+
41
+ usec_t last_ut;
42
+ } progress;
43
+
44
+ // struct {
45
+ // usec_t start_ut;
46
+ // usec_t stop_ut;
47
+ // usec_t first_msg_ut;
48
+ //
49
+ // uint64_t first_msg_seqnum;
50
+ // } query_file;
51
+
52
+ // struct {
53
+ // uint32_t enable_after_samples;
54
+ // uint32_t slots;
55
+ // uint32_t sampled;
56
+ // uint32_t unsampled;
57
+ // uint32_t estimated;
58
+ // } samples;
59
+
60
+ // struct {
61
+ // uint32_t enable_after_samples;
62
+ // uint32_t every;
63
+ // uint32_t skipped;
64
+ // uint32_t recalibrate;
65
+ // uint32_t sampled;
66
+ // uint32_t unsampled;
67
+ // uint32_t estimated;
68
+ // } samples_per_file;
69
+
70
+ // struct {
71
+ // usec_t start_ut;
72
+ // usec_t end_ut;
73
+ // usec_t step_ut;
74
+ // uint32_t enable_after_samples;
75
+ // uint32_t sampled[SYSTEMD_JOURNAL_SAMPLING_SLOTS];
76
+ // uint32_t unsampled[SYSTEMD_JOURNAL_SAMPLING_SLOTS];
77
+ // } samples_per_time_slot;
78
+
79
+ // per file progress info
80
+ // size_t cached_count;
81
+
82
+ // progress statistics
83
+ usec_t matches_setup_ut;
84
+ size_t rows_useful;
85
+ size_t rows_read;
86
+ size_t bytes_read;
87
+ size_t files_matched;
88
+ size_t file_working;
89
+};
90
+
91
+// prepare LQS
92
+#define LQS_DEFAULT_SLICE_MODE 0
93
+#define LQS_FUNCTION_NAME WEVT_FUNCTION_NAME
94
+#define LQS_FUNCTION_DESCRIPTION WEVT_FUNCTION_DESCRIPTION
95
+#define LQS_DEFAULT_ITEMS_PER_QUERY 200
96
+#define LQS_DEFAULT_ITEMS_SAMPLING 1000000
97
+#define LQS_SOURCE_TYPE WEVT_SOURCE_TYPE
98
+#define LQS_SOURCE_TYPE_ALL WEVTS_ALL
99
+#define LQS_SOURCE_TYPE_NONE WEVTS_NONE
100
+#define LQS_FUNCTION_GET_INTERNAL_SOURCE_TYPE(value) wevt_internal_source_type(value)
101
+#define LQS_FUNCTION_SOURCE_TO_JSON_ARRAY(wb) wevt_sources_to_json_array(wb)
102
+#include "libnetdata/facets/logs_query_status.h"
103
+
104
+#define WEVT_FIELD_COMPUTER "Computer"
105
+#define WEVT_FIELD_CHANNEL "Channel"
106
+#define WEVT_FIELD_PROVIDER "Provider"
107
+#define WEVT_FIELD_SOURCE "Source"
108
+#define WEVT_FIELD_EVENTRECORDID "EventRecordID"
109
+#define WEVT_FIELD_EVENTID "EventID"
110
+#define WEVT_FIELD_LEVEL "Level"
111
+#define WEVT_FIELD_KEYWORDS "Keywords"
112
+#define WEVT_FIELD_OPCODE "Opcode"
113
+#define WEVT_FIELD_USER "User"
114
+#define WEVT_FIELD_TASK "Task"
115
+#define WEVT_FIELD_PROCESSID "ProcessID"
116
+#define WEVT_FIELD_THREADID "ThreadID"
117
+#define WEVT_FIELD_XML "XML"
118
+#define WEVT_FIELD_MESSAGE "Message"
119
+
120
+#define WEVT_ALWAYS_VISIBLE_KEYS NULL
121
+
122
+#define WEVT_KEYS_EXCLUDED_FROM_FACETS \
123
+ "|" WEVT_FIELD_MESSAGE \
124
+ "|" WEVT_FIELD_XML \
125
+ ""
126
+
127
+#define WEVT_KEYS_INCLUDED_IN_FACETS \
128
+ "|" WEVT_FIELD_PROVIDER \
129
+ "|" WEVT_FIELD_SOURCE \
130
+ "|" WEVT_FIELD_CHANNEL \
131
+ "|" WEVT_FIELD_LEVEL \
132
+ "|" WEVT_FIELD_KEYWORDS \
133
+ "|" WEVT_FIELD_OPCODE \
134
+ "|" WEVT_FIELD_TASK \
135
+ "|" WEVT_FIELD_COMPUTER \
136
+ "|" WEVT_FIELD_USER \
137
+ ""
138
+
139
+static inline WEVT_QUERY_STATUS check_stop(const bool *cancelled, const usec_t *stop_monotonic_ut) {
140
+ if(cancelled && __atomic_load_n(cancelled, __ATOMIC_RELAXED)) {
141
+ nd_log(NDLS_COLLECTORS, NDLP_INFO, "Function has been cancelled");
142
+ return WEVT_CANCELLED;
143
+ }
144
+
145
+ if(now_monotonic_usec() > __atomic_load_n(stop_monotonic_ut, __ATOMIC_RELAXED)) {
146
+ internal_error(true, "Function timed out");
147
+ return WEVT_TIMED_OUT;
148
+ }
149
+
150
+ return WEVT_OK;
151
+}
152
+
153
+FACET_ROW_SEVERITY wevt_levelid_to_facet_severity(FACETS *facets __maybe_unused, FACET_ROW *row, void *data __maybe_unused) {
154
+ FACET_ROW_KEY_VALUE *levelid_rkv = dictionary_get(row->dict, WEVT_FIELD_LEVEL);
155
+ if(!levelid_rkv || levelid_rkv->empty)
156
+ return FACET_ROW_SEVERITY_NORMAL;
157
+
158
+ int windows_event_level = str2i(buffer_tostring(levelid_rkv->wb));
159
+
160
+ switch (windows_event_level) {
161
+ case 5: // Verbose
162
+ return FACET_ROW_SEVERITY_DEBUG;
163
+ default:
164
+ case 4: // Information
165
+ return FACET_ROW_SEVERITY_NORMAL;
166
+ case 3: // Warning
167
+ return FACET_ROW_SEVERITY_WARNING;
168
+ case 2: // Error
169
+ case 1: // Critical
170
+ return FACET_ROW_SEVERITY_CRITICAL;
171
+ }
172
+}
173
+
174
+struct wevt_bin_data {
175
+ WEVT_LOG *log;
176
+ EVT_HANDLE bookmark;
177
+ PROVIDER_META_HANDLE *publisher;
178
+};
179
+
180
+static void wevt_cleanup_bin_data(void *data) {
181
+ struct wevt_bin_data *d = data;
182
+
183
+ if(d->bookmark)
184
+ EvtClose(d->bookmark);
185
+
186
+ publisher_release(d->publisher);
187
+ freez(d);
188
+}
189
+
190
+static inline void wevt_facets_register_bin_data(WEVT_LOG *log, FACETS *facets) {
191
+ struct wevt_bin_data *d = mallocz(sizeof(struct wevt_bin_data));
192
+
193
+ d->log = log;
194
+
195
+ // take the bookmark
196
+ d->bookmark = log->bookmark; log->bookmark = NULL;
197
+
198
+ // dup the publisher
199
+ d->publisher = publisher_dup(log->publisher);
200
+
201
+ facets_row_bin_data_set(facets, wevt_cleanup_bin_data, d);
202
+}
203
+
204
+static void wevt_render_xml(
205
+ FACETS *facets,
206
+ BUFFER *json_array,
207
+ FACET_ROW_KEY_VALUE *rkv __maybe_unused,
208
+ FACET_ROW *row,
209
+ void *data __maybe_unused) {
210
+
211
+ struct wevt_bin_data *d = facets_row_bin_data_get(facets, row);
212
+ if(!d) {
213
+ buffer_json_add_array_item_string(json_array, "Failed to get row BIN DATA from facets");
214
+ return;
215
+ }
216
+
217
+ wevt_get_xml_utf8(d->log, d->publisher, d->bookmark, &d->log->ops.xml);
218
+ buffer_json_add_array_item_string(json_array, d->log->ops.xml.data);
219
+}
220
+
221
+static void wevt_render_message(
222
+ FACETS *facets,
223
+ BUFFER *json_array,
224
+ FACET_ROW_KEY_VALUE *rkv __maybe_unused,
225
+ FACET_ROW *row,
226
+ void *data __maybe_unused) {
227
+
228
+ struct wevt_bin_data *d = facets_row_bin_data_get(facets, row);
229
+ if(!d) {
230
+ buffer_json_add_array_item_string(json_array, "Failed to get row BIN DATA from facets");
231
+ return;
232
+ }
233
+
234
+ wevt_get_event_utf8(d->log, d->publisher, d->bookmark, &d->log->ops.event);
235
+ buffer_json_add_array_item_string(json_array, d->log->ops.event.data);
236
+}
237
+
238
+static void wevt_register_fields(LOGS_QUERY_STATUS *lqs) {
239
+ FACETS *facets = lqs->facets;
240
+ LOGS_QUERY_REQUEST *rq = &lqs->rq;
241
+
242
+ facets_register_row_severity(facets, wevt_levelid_to_facet_severity, NULL);
243
+
244
+ facets_register_key_name(
245
+ facets, WEVT_FIELD_COMPUTER,
246
+ rq->default_facet | FACET_KEY_OPTION_VISIBLE);
247
+
248
+ facets_register_key_name(
249
+ facets, WEVT_FIELD_CHANNEL,
250
+ rq->default_facet |
251
+ FACET_KEY_OPTION_FTS);
252
+
253
+ facets_register_key_name(
254
+ facets, WEVT_FIELD_PROVIDER,
255
+ rq->default_facet |
256
+ FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS);
257
+
258
+ facets_register_key_name(
259
+ facets, WEVT_FIELD_SOURCE,
260
+ rq->default_facet |
261
+ FACET_KEY_OPTION_FTS);
262
+
263
+ facets_register_key_name(
264
+ facets, WEVT_FIELD_USER,
265
+ rq->default_facet | FACET_KEY_OPTION_FTS);
266
+
267
+ facets_register_key_name(
268
+ facets, WEVT_FIELD_EVENTID,
269
+ rq->default_facet |
270
+ FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS);
271
+
272
+ facets_register_key_name(
273
+ facets, WEVT_FIELD_LEVEL,
274
+ rq->default_facet | FACET_KEY_OPTION_FTS | FACET_KEY_OPTION_EXPANDED_FILTER);
275
+
276
+ facets_register_key_name(
277
+ facets, WEVT_FIELD_LEVEL "ID",
278
+ FACET_KEY_OPTION_NONE);
279
+
280
+ facets_register_key_name(
281
+ facets, WEVT_FIELD_PROCESSID,
282
+ rq->default_facet | FACET_KEY_OPTION_FTS);
283
+
284
+ facets_register_key_name(
285
+ facets, WEVT_FIELD_THREADID,
286
+ rq->default_facet | FACET_KEY_OPTION_FTS);
287
+
288
+ facets_register_key_name(
289
+ facets, WEVT_FIELD_TASK,
290
+ rq->default_facet | FACET_KEY_OPTION_FTS);
291
+
292
+ facets_register_key_name(
293
+ facets, WEVT_FIELD_PROCESSID,
294
+ rq->default_facet | FACET_KEY_OPTION_FTS);
295
+
296
+ facets_register_key_name(
297
+ facets, WEVT_FIELD_THREADID,
298
+ rq->default_facet | FACET_KEY_OPTION_FTS);
299
+
300
+ facets_register_key_name(
301
+ facets, WEVT_FIELD_OPCODE,
302
+ rq->default_facet | FACET_KEY_OPTION_FTS | FACET_KEY_OPTION_VISIBLE);
303
+
304
+ facets_register_key_name(
305
+ facets, WEVT_FIELD_OPCODE "ID",
306
+ FACET_KEY_OPTION_NONE);
307
+
308
+ facets_register_key_name(
309
+ facets, WEVT_FIELD_TASK,
310
+ rq->default_facet | FACET_KEY_OPTION_FTS);
311
+
312
+ facets_register_key_name(
313
+ facets, WEVT_FIELD_TASK "ID",
314
+ FACET_KEY_OPTION_NONE);
315
+
316
+ facets_register_key_name(
317
+ facets, WEVT_FIELD_KEYWORDS,
318
+ rq->default_facet | FACET_KEY_OPTION_FTS);
319
+
320
+ facets_register_key_name(
321
+ facets, WEVT_FIELD_KEYWORDS "ID",
322
+ FACET_KEY_OPTION_NONE);
323
+
324
+ facets_register_dynamic_key_name(
325
+ facets, WEVT_FIELD_MESSAGE,
326
+ FACET_KEY_OPTION_NEVER_FACET | FACET_KEY_OPTION_MAIN_TEXT | FACET_KEY_OPTION_VISIBLE,
327
+ wevt_render_message, NULL);
328
+
329
+ facets_register_dynamic_key_name(
330
+ facets, WEVT_FIELD_XML,
331
+ FACET_KEY_OPTION_NEVER_FACET,
332
+ wevt_render_xml, NULL);
333
+
334
+#ifdef NETDATA_INTERNAL_CHECKS
335
+ facets_register_key_name(
336
+ facets, "z_level_source",
337
+ rq->default_facet);
338
+
339
+ facets_register_key_name(
340
+ facets, "z_keywords_source",
341
+ rq->default_facet);
342
+
343
+ facets_register_key_name(
344
+ facets, "z_opcode_source",
345
+ rq->default_facet);
346
+
347
+ facets_register_key_name(
348
+ facets, "z_task_source",
349
+ rq->default_facet);
350
+#endif
351
+}
352
+
353
+#ifdef NETDATA_INTERNAL_CHECKS
354
+static const char *source_to_str(TXT_UTF8 *txt) {
355
+ switch(txt->src) {
356
+ default:
357
+ case TXT_SOURCE_UNKNOWN:
358
+ return "unknown";
359
+
360
+ case TXT_SOURCE_EVENT_LOG:
361
+ return "event-log";
362
+
363
+ case TXT_SOURCE_PUBLISHER:
364
+ return "publisher";
365
+
366
+ case TXT_SOURCE_FIELD_CACHE:
367
+ return "fields-cache";
368
+
369
+ case TXT_SOURCE_HARDCODED:
370
+ return "hardcoded";
371
+ }
372
+}
373
+#endif
374
+
375
+static inline size_t wevt_process_event(WEVT_LOG *log, FACETS *facets, LOGS_QUERY_SOURCE *src, usec_t *msg_ut __maybe_unused, WEVT_EVENT *e) {
376
+ size_t len, bytes = log->ops.content.used;
377
+
378
+ if(log->ops.provider.used > 1) {
379
+ bytes += log->ops.provider.used * 2; // unicode is double
380
+ facets_add_key_value_length(
381
+ facets, WEVT_FIELD_PROVIDER, sizeof(WEVT_FIELD_PROVIDER) - 1,
382
+ log->ops.provider.data, log->ops.provider.used - 1);
383
+ }
384
+
385
+ if(log->ops.source.used > 1) {
386
+ bytes += log->ops.source.used * 2;
387
+ facets_add_key_value_length(
388
+ facets, WEVT_FIELD_SOURCE, sizeof(WEVT_FIELD_SOURCE) - 1,
389
+ log->ops.source.data, log->ops.source.used - 1);
390
+ }
391
+
392
+ if(log->ops.channel.used > 1) {
393
+ bytes += log->ops.channel.used * 2;
394
+ facets_add_key_value_length(
395
+ facets, WEVT_FIELD_CHANNEL, sizeof(WEVT_FIELD_CHANNEL) - 1,
396
+ log->ops.channel.data, log->ops.channel.used - 1);
397
+ }
398
+ else {
399
+ bytes += src->fullname_len * 2;
400
+ facets_add_key_value_length(
401
+ facets, WEVT_FIELD_CHANNEL, sizeof(WEVT_FIELD_CHANNEL) - 1,
402
+ src->fullname, src->fullname_len);
403
+ }
404
+
405
+ {
406
+ static __thread char event_record_id_str[UINT64_MAX_LENGTH];
407
+ len = print_uint64(event_record_id_str, e->id);
408
+ bytes += len;
409
+ facets_add_key_value_length(
410
+ facets, WEVT_FIELD_EVENTRECORDID, sizeof(WEVT_FIELD_EVENTRECORDID) - 1,
411
+ event_record_id_str, len);
412
+ }
413
+
414
+ if(log->ops.level.used > 1) {
415
+ bytes += log->ops.level.used * 2;
416
+ facets_add_key_value_length(
417
+ facets, WEVT_FIELD_LEVEL, sizeof(WEVT_FIELD_LEVEL) - 1,
418
+ log->ops.level.data, log->ops.level.used - 1);
419
+ }
420
+
421
+ if(log->ops.computer.used > 1) {
422
+ bytes += log->ops.computer.used * 2;
423
+ facets_add_key_value_length(
424
+ facets, WEVT_FIELD_COMPUTER, sizeof(WEVT_FIELD_COMPUTER) - 1,
425
+ log->ops.computer.data, log->ops.computer.used - 1);
426
+ }
427
+
428
+ if(log->ops.opcode.used > 1) {
429
+ bytes += log->ops.opcode.used * 2;
430
+ facets_add_key_value_length(
431
+ facets, WEVT_FIELD_OPCODE, sizeof(WEVT_FIELD_OPCODE) - 1,
432
+ log->ops.opcode.data, log->ops.opcode.used - 1);
433
+ }
434
+
435
+ if(log->ops.keywords.used > 1) {
436
+ bytes += log->ops.keywords.used * 2;
437
+ facets_add_key_value_length(
438
+ facets, WEVT_FIELD_KEYWORDS, sizeof(WEVT_FIELD_KEYWORDS) - 1,
439
+ log->ops.keywords.data, log->ops.keywords.used - 1);
440
+ }
441
+
442
+ if(log->ops.task.used > 1) {
443
+ bytes += log->ops.task.used * 2;
444
+ facets_add_key_value_length(
445
+ facets, WEVT_FIELD_TASK, sizeof(WEVT_FIELD_TASK) - 1,
446
+ log->ops.task.data, log->ops.task.used - 1);
447
+ }
448
+
449
+ if(log->ops.user.used > 1) {
450
+ bytes += log->ops.user.used * 2;
451
+ facets_add_key_value_length(
452
+ facets, WEVT_FIELD_USER, sizeof(WEVT_FIELD_USER) - 1,
453
+ log->ops.user.data, log->ops.user.used - 1);
454
+ }
455
+
456
+ {
457
+ static __thread char event_id_str[UINT64_MAX_LENGTH];
458
+ len = print_uint64(event_id_str, e->event_id);
459
+ bytes += len;
460
+ facets_add_key_value_length(
461
+ facets, WEVT_FIELD_EVENTID, sizeof(WEVT_FIELD_EVENTID) - 1,
462
+ event_id_str, len);
463
+ }
464
+
465
+ if(e->process_id) {
466
+ static __thread char process_id_str[UINT64_MAX_LENGTH];
467
+ len = print_uint64(process_id_str, e->process_id);
468
+ bytes += len;
469
+ facets_add_key_value_length(
470
+ facets, WEVT_FIELD_PROCESSID, sizeof(WEVT_FIELD_PROCESSID) - 1,
471
+ process_id_str, len);
472
+ }
473
+
474
+ if(e->thread_id) {
475
+ static __thread char thread_id_str[UINT64_MAX_LENGTH];
476
+ len = print_uint64(thread_id_str, e->thread_id);
477
+ bytes += len;
478
+ facets_add_key_value_length(
479
+ facets, WEVT_FIELD_THREADID, sizeof(WEVT_FIELD_THREADID) - 1,
480
+ thread_id_str, len);
481
+ }
482
+
483
+ {
484
+ static __thread char str[UINT64_MAX_LENGTH];
485
+ len = print_uint64(str, e->level);
486
+ bytes += len;
487
+ facets_add_key_value_length(
488
+ facets, WEVT_FIELD_LEVEL "ID", sizeof(WEVT_FIELD_LEVEL) + 2 - 1, str, len);
489
+ }
490
+
491
+ {
492
+ static __thread char str[UINT64_HEX_MAX_LENGTH];
493
+ len = print_uint64_hex_full(str, e->keywords);
494
+ bytes += len;
495
+ facets_add_key_value_length(
496
+ facets, WEVT_FIELD_KEYWORDS "ID", sizeof(WEVT_FIELD_KEYWORDS) + 2 - 1, str, len);
497
+ }
498
+
499
+ {
500
+ static __thread char str[UINT64_MAX_LENGTH];
501
+ len = print_uint64(str, e->opcode);
502
+ bytes += len;
503
+ facets_add_key_value_length(
504
+ facets, WEVT_FIELD_OPCODE "ID", sizeof(WEVT_FIELD_OPCODE) + 2 - 1, str, len);
505
+ }
506
+
507
+ {
508
+ static __thread char str[UINT64_MAX_LENGTH];
509
+ len = print_uint64(str, e->task);
510
+ bytes += len;
511
+ facets_add_key_value_length(
512
+ facets, WEVT_FIELD_TASK "ID", sizeof(WEVT_FIELD_TASK) + 2 - 1, str, len);
513
+ }
514
+
515
+// bytes += log->ops.xml.used * 2;
516
+// facets_add_key_value_length(
517
+// facets, WEVT_FIELD_XML, sizeof(WEVT_FIELD_XML) - 1,
518
+// log->ops.xml.data, log->ops.xml.used - 1);
519
+
520
+ wevt_facets_register_bin_data(log, facets);
521
+
522
+#ifdef NETDATA_INTERNAL_CHECKS
523
+ facets_add_key_value(facets, "z_level_source", source_to_str(&log->ops.level));
524
+ facets_add_key_value(facets, "z_keywords_source", source_to_str(&log->ops.keywords));
525
+ facets_add_key_value(facets, "z_opcode_source", source_to_str(&log->ops.opcode));
526
+ facets_add_key_value(facets, "z_task_source", source_to_str(&log->ops.task));
527
+#endif
528
+
529
+ return bytes;
530
+}
531
+
532
+static void send_progress_update(LOGS_QUERY_STATUS *lqs, size_t current_row_counter, bool flush_current_file) {
533
+ usec_t now_ut = now_monotonic_usec();
534
+
535
+ if(current_row_counter > lqs->c.progress.entries.current_query_total) {
536
+ lqs->c.progress.entries.total += current_row_counter - lqs->c.progress.entries.current_query_total;
537
+ lqs->c.progress.entries.current_query_total = current_row_counter;
538
+ }
539
+
540
+ if(flush_current_file) {
541
+ lqs->c.progress.entries.total += current_row_counter;
542
+ lqs->c.progress.entries.total -= lqs->c.progress.entries.current_query_total;
543
+ lqs->c.progress.entries.completed += current_row_counter;
544
+ lqs->c.progress.entries.current_query_total = 0;
545
+ }
546
+
547
+ size_t completed = lqs->c.progress.entries.completed + current_row_counter;
548
+ if(completed > lqs->c.progress.entries.total)
549
+ lqs->c.progress.entries.total = completed;
550
+
551
+ usec_t progress_duration_ut = now_ut - lqs->c.progress.last_ut;
552
+ if(progress_duration_ut >= WINDOWS_EVENTS_PROGRESS_EVERY_UT) {
553
+ lqs->c.progress.last_ut = now_ut;
554
+
555
+ netdata_mutex_lock(&stdout_mutex);
556
+ pluginsd_function_progress_to_stdout(lqs->rq.transaction, completed, lqs->c.progress.entries.total);
557
+ netdata_mutex_unlock(&stdout_mutex);
558
+ }
559
+}
560
+
561
+static WEVT_QUERY_STATUS wevt_query_backward(
562
+ WEVT_LOG *log, BUFFER *wb __maybe_unused, FACETS *facets,
563
+ LOGS_QUERY_SOURCE *src,
564
+ LOGS_QUERY_STATUS *lqs)
565
+{
566
+ usec_t start_ut = lqs->query.start_ut;
567
+ usec_t stop_ut = lqs->query.stop_ut;
568
+ bool stop_when_full = lqs->query.stop_when_full;
569
+
570
+// lqs->c.query_file.start_ut = start_ut;
571
+// lqs->c.query_file.stop_ut = stop_ut;
572
+
573
+ if(!wevt_query(log, channel2unicode(src->fullname), lqs->c.query, EvtQueryReverseDirection))
574
+ return WEVT_FAILED_TO_SEEK;
575
+
576
+ size_t errors_no_timestamp = 0;
577
+ usec_t latest_msg_ut = 0; // the biggest timestamp we have seen so far
578
+ usec_t first_msg_ut = 0; // the first message we got from the db
579
+ size_t row_counter = 0, last_row_counter = 0, rows_useful = 0;
580
+ size_t bytes = 0, last_bytes = 0;
581
+
582
+ usec_t last_usec_from = 0;
583
+ usec_t last_usec_to = 0;
584
+
585
+ WEVT_QUERY_STATUS status = WEVT_OK;
586
+
587
+ facets_rows_begin(facets);
588
+ WEVT_EVENT e;
589
+ while (status == WEVT_OK && wevt_get_next_event(log, &e, true)) {
590
+ usec_t msg_ut = e.created_ns / NSEC_PER_USEC;
591
+
592
+ if(unlikely(!msg_ut)) {
593
+ errors_no_timestamp++;
594
+ continue;
595
+ }
596
+
597
+ if (unlikely(msg_ut > start_ut))
598
+ continue;
599
+
600
+ if (unlikely(msg_ut < stop_ut))
601
+ break;
602
+
603
+ if(unlikely(msg_ut > latest_msg_ut))
604
+ latest_msg_ut = msg_ut;
605
+
606
+ if(unlikely(!first_msg_ut)) {
607
+ first_msg_ut = msg_ut;
608
+ // lqs->c.query_file.first_msg_ut = msg_ut;
609
+ }
610
+
611
+// sampling_t sample = is_row_in_sample(log, lqs, src, msg_ut,
612
+// FACETS_ANCHOR_DIRECTION_BACKWARD,
613
+// facets_row_candidate_to_keep(facets, msg_ut));
614
+//
615
+// if(sample == SAMPLING_FULL) {
616
+ bytes += wevt_process_event(log, facets, src, &msg_ut, &e);
617
+
618
+ // make sure each line gets a unique timestamp
619
+ if(unlikely(msg_ut >= last_usec_from && msg_ut <= last_usec_to))
620
+ msg_ut = --last_usec_from;
621
+ else
622
+ last_usec_from = last_usec_to = msg_ut;
623
+
624
+ if(facets_row_finished(facets, msg_ut))
625
+ rows_useful++;
626
+
627
+ row_counter++;
628
+ if(unlikely((row_counter % FUNCTION_DATA_ONLY_CHECK_EVERY_ROWS) == 0 &&
629
+ stop_when_full &&
630
+ facets_rows(facets) >= lqs->rq.entries)) {
631
+ // stop the data only query
632
+ usec_t oldest = facets_row_oldest_ut(facets);
633
+ if(oldest && msg_ut < (oldest - lqs->anchor.delta_ut))
634
+ break;
635
+ }
636
+
637
+ if(unlikely(row_counter % FUNCTION_PROGRESS_EVERY_ROWS == 0)) {
638
+ status = check_stop(lqs->cancelled, lqs->stop_monotonic_ut);
639
+
640
+ if(status == WEVT_OK) {
641
+ lqs->c.rows_read += row_counter - last_row_counter;
642
+ last_row_counter = row_counter;
643
+
644
+ lqs->c.bytes_read += bytes - last_bytes;
645
+ last_bytes = bytes;
646
+
647
+ send_progress_update(lqs, row_counter, false);
648
+ }
649
+ }
650
+// }
651
+// else if(sample == SAMPLING_SKIP_FIELDS)
652
+// facets_row_finished_unsampled(facets, msg_ut);
653
+// else {
654
+// sampling_update_running_query_file_estimates(facets, log, lqs, src, msg_ut, FACETS_ANCHOR_DIRECTION_BACKWARD);
655
+// break;
656
+// }
657
+ }
658
+
659
+ send_progress_update(lqs, row_counter, true);
660
+ lqs->c.rows_read += row_counter - last_row_counter;
661
+ lqs->c.bytes_read += bytes - last_bytes;
662
+ lqs->c.rows_useful += rows_useful;
663
+
664
+ if(errors_no_timestamp)
665
+ netdata_log_error("WINDOWS-EVENTS: %zu events did not have timestamps", errors_no_timestamp);
666
+
667
+ if(latest_msg_ut > lqs->last_modified)
668
+ lqs->last_modified = latest_msg_ut;
669
+
670
+ wevt_query_done(log);
671
+
672
+ return status;
673
+}
674
+
675
+static WEVT_QUERY_STATUS wevt_query_forward(
676
+ WEVT_LOG *log, BUFFER *wb __maybe_unused, FACETS *facets,
677
+ LOGS_QUERY_SOURCE *src,
678
+ LOGS_QUERY_STATUS *lqs)
679
+{
680
+ usec_t start_ut = lqs->query.start_ut;
681
+ usec_t stop_ut = lqs->query.stop_ut;
682
+ bool stop_when_full = lqs->query.stop_when_full;
683
+
684
+// lqs->c.query_file.start_ut = start_ut;
685
+// lqs->c.query_file.stop_ut = stop_ut;
686
+
687
+ if(!wevt_query(log, channel2unicode(src->fullname), lqs->c.query, EvtQueryForwardDirection))
688
+ return WEVT_FAILED_TO_SEEK;
689
+
690
+ size_t errors_no_timestamp = 0;
691
+ usec_t latest_msg_ut = 0; // the biggest timestamp we have seen so far
692
+ usec_t first_msg_ut = 0; // the first message we got from the db
693
+ size_t row_counter = 0, last_row_counter = 0, rows_useful = 0;
694
+ size_t bytes = 0, last_bytes = 0;
695
+
696
+ usec_t last_usec_from = 0;
697
+ usec_t last_usec_to = 0;
698
+
699
+ WEVT_QUERY_STATUS status = WEVT_OK;
700
+
701
+ facets_rows_begin(facets);
702
+ WEVT_EVENT e;
703
+ while (status == WEVT_OK && wevt_get_next_event(log, &e, true)) {
704
+ usec_t msg_ut = e.created_ns / NSEC_PER_USEC;
705
+
706
+ if(unlikely(!msg_ut)) {
707
+ errors_no_timestamp++;
708
+ continue;
709
+ }
710
+
711
+ if (unlikely(msg_ut < start_ut))
712
+ continue;
713
+
714
+ if (unlikely(msg_ut > stop_ut))
715
+ break;
716
+
717
+ if(likely(msg_ut > latest_msg_ut))
718
+ latest_msg_ut = msg_ut;
719
+
720
+ if(unlikely(!first_msg_ut)) {
721
+ first_msg_ut = msg_ut;
722
+ // lqs->c.query_file.first_msg_ut = msg_ut;
723
+ }
724
+
725
+// sampling_t sample = is_row_in_sample(log, lqs, src, msg_ut,
726
+// FACETS_ANCHOR_DIRECTION_FORWARD,
727
+// facets_row_candidate_to_keep(facets, msg_ut));
728
+//
729
+// if(sample == SAMPLING_FULL) {
730
+ bytes += wevt_process_event(log, facets, src, &msg_ut, &e);
731
+
732
+ // make sure each line gets a unique timestamp
733
+ if(unlikely(msg_ut >= last_usec_from && msg_ut <= last_usec_to))
734
+ msg_ut = ++last_usec_to;
735
+ else
736
+ last_usec_from = last_usec_to = msg_ut;
737
+
738
+ if(facets_row_finished(facets, msg_ut))
739
+ rows_useful++;
740
+
741
+ row_counter++;
742
+ if(unlikely((row_counter % FUNCTION_DATA_ONLY_CHECK_EVERY_ROWS) == 0 &&
743
+ stop_when_full &&
744
+ facets_rows(facets) >= lqs->rq.entries)) {
745
+ // stop the data only query
746
+ usec_t newest = facets_row_newest_ut(facets);
747
+ if(newest && msg_ut > (newest + lqs->anchor.delta_ut))
748
+ break;
749
+ }
750
+
751
+ if(unlikely(row_counter % FUNCTION_PROGRESS_EVERY_ROWS == 0)) {
752
+ status = check_stop(lqs->cancelled, lqs->stop_monotonic_ut);
753
+
754
+ if(status == WEVT_OK) {
755
+ lqs->c.rows_read += row_counter - last_row_counter;
756
+ last_row_counter = row_counter;
757
+
758
+ lqs->c.bytes_read += bytes - last_bytes;
759
+ last_bytes = bytes;
760
+
761
+ send_progress_update(lqs, row_counter, false);
762
+ }
763
+ }
764
+// }
765
+// else if(sample == SAMPLING_SKIP_FIELDS)
766
+// facets_row_finished_unsampled(facets, msg_ut);
767
+// else {
768
+// sampling_update_running_query_file_estimates(facets, log, lqs, src, msg_ut, FACETS_ANCHOR_DIRECTION_FORWARD);
769
+// break;
770
+// }
771
+ }
772
+
773
+ send_progress_update(lqs, row_counter, true);
774
+ lqs->c.rows_read += row_counter - last_row_counter;
775
+ lqs->c.bytes_read += bytes - last_bytes;
776
+ lqs->c.rows_useful += rows_useful;
777
+
778
+ if(errors_no_timestamp)
779
+ netdata_log_error("WINDOWS-EVENTS: %zu events did not have timestamps", errors_no_timestamp);
780
+
781
+ if(latest_msg_ut > lqs->last_modified)
782
+ lqs->last_modified = latest_msg_ut;
783
+
784
+ wevt_query_done(log);
785
+
786
+ return status;
787
+}
788
+
789
+static WEVT_QUERY_STATUS wevt_query_one_channel(
790
+ WEVT_LOG *log,
791
+ BUFFER *wb, FACETS *facets,
792
+ LOGS_QUERY_SOURCE *src,
793
+ LOGS_QUERY_STATUS *lqs) {
794
+
795
+ errno_clear();
796
+
797
+ WEVT_QUERY_STATUS status;
798
+ if(lqs->rq.direction == FACETS_ANCHOR_DIRECTION_FORWARD)
799
+ status = wevt_query_forward(log, wb, facets, src, lqs);
800
+ else
801
+ status = wevt_query_backward(log, wb, facets, src, lqs);
802
+
803
+ return status;
804
+}
805
+
806
+static bool source_is_mine(LOGS_QUERY_SOURCE *src, LOGS_QUERY_STATUS *lqs) {
807
+ if((lqs->rq.source_type == WEVTS_NONE && !lqs->rq.sources) || (src->source_type & lqs->rq.source_type) ||
808
+ (lqs->rq.sources && simple_pattern_matches(lqs->rq.sources, string2str(src->source)))) {
809
+
810
+ if(!src->msg_last_ut)
811
+ // the file is not scanned yet, or the timestamps have not been updated,
812
+ // so we don't know if it can contribute or not - let's add it.
813
+ return true;
814
+
815
+ usec_t anchor_delta = ANCHOR_DELTA_UT;
816
+ usec_t first_ut = src->msg_first_ut - anchor_delta;
817
+ usec_t last_ut = src->msg_last_ut + anchor_delta;
818
+
819
+ if(last_ut >= lqs->rq.after_ut && first_ut <= lqs->rq.before_ut)
820
+ return true;
821
+ }
822
+
823
+ return false;
824
+}
825
+
826
+typedef struct static_utf8_8k {
827
+ char buffer[8192];
828
+ size_t size;
829
+ size_t len;
830
+} STATIC_BUF_8K;
831
+
832
+typedef struct static_unicode_16k {
833
+ wchar_t buffer[16384];
834
+ size_t size;
835
+ size_t len;
836
+} STATIC_UNI_16K;
837
+
838
+static bool wevt_foreach_selected_value_cb(FACETS *facets __maybe_unused, size_t id, const char *key, const char *value, void *data) {
839
+ STATIC_BUF_8K *b = data;
840
+
841
+ b->len += snprintfz(&b->buffer[b->len], b->size - b->len,
842
+ "%s%s=%s",
843
+ id ? " or " : "", key, value);
844
+
845
+ return b->len < b->size;
846
+}
847
+
848
+static wchar_t *wevt_generate_query(LOGS_QUERY_STATUS *lqs, BUFFER *wb) {
849
+ static __thread STATIC_UNI_16K q = {
850
+ .size = sizeof(q.buffer) / sizeof(wchar_t),
851
+ .len = 0,
852
+ };
853
+ static __thread STATIC_BUF_8K b = {
854
+ .size = sizeof(b.buffer) / sizeof(char),
855
+ .len = 0,
856
+ };
857
+
858
+ lqs_query_timeframe(lqs, ANCHOR_DELTA_UT);
859
+
860
+ usec_t seek_to = lqs->query.start_ut;
861
+ if(lqs->rq.direction == FACETS_ANCHOR_DIRECTION_BACKWARD)
862
+ // windows events queries are limited to millisecond resolution
863
+ // so, in order not to lose data, we have to add
864
+ // a millisecond when the direction is backward
865
+ seek_to += USEC_PER_MS;
866
+
867
+ // Convert the microseconds since Unix epoch to FILETIME (used in Windows APIs)
868
+ FILETIME fileTime = os_unix_epoch_ut_to_filetime(seek_to);
869
+
870
+ // Convert FILETIME to SYSTEMTIME for use in XPath
871
+ SYSTEMTIME systemTime;
872
+ if (!FileTimeToSystemTime(&fileTime, &systemTime)) {
873
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "FileTimeToSystemTime() failed");
874
+ return NULL;
875
+ }
876
+
877
+ // Format SYSTEMTIME into ISO 8601 format (YYYY-MM-DDTHH:MM:SS.sssZ)
878
+ q.len = swprintf(q.buffer, q.size,
879
+ L"Event/System[TimeCreated[@SystemTime%ls\"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ\"]",
880
+ lqs->rq.direction == FACETS_ANCHOR_DIRECTION_BACKWARD ? L"<=" : L">=",
881
+ systemTime.wYear, systemTime.wMonth, systemTime.wDay,
882
+ systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
883
+
884
+
885
+ if(lqs->rq.slice) {
886
+ b.len = snprintf(b.buffer, b.size, " and (");
887
+ if (facets_foreach_selected_value_in_key(
888
+ lqs->facets,
889
+ WEVT_FIELD_LEVEL,
890
+ sizeof(WEVT_FIELD_LEVEL) - 1,
891
+ used_hashes_registry,
892
+ wevt_foreach_selected_value_cb,
893
+ &b)) {
894
+ b.len += snprintf(&b.buffer[b.len], b.size - b.len, ")");
895
+ if (b.len < b.size) {
896
+ utf82unicode(&q.buffer[q.len], q.size - q.len, b.buffer);
897
+ q.len = wcslen(q.buffer);
898
+ }
899
+ }
900
+
901
+ b.len = snprintf(b.buffer, b.size, " and (");
902
+ if (facets_foreach_selected_value_in_key(
903
+ lqs->facets,
904
+ WEVT_FIELD_EVENTID,
905
+ sizeof(WEVT_FIELD_EVENTID) - 1,
906
+ used_hashes_registry,
907
+ wevt_foreach_selected_value_cb,
908
+ &b)) {
909
+ b.len += snprintf(&b.buffer[b.len], b.size - b.len, ")");
910
+ if (b.len < b.size) {
911
+ utf82unicode(&q.buffer[q.len], q.size - q.len, b.buffer);
912
+ q.len = wcslen(q.buffer);
913
+ }
914
+ }
915
+ }
916
+
917
+ q.len += swprintf(&q.buffer[q.len], q.size - q.len, L"]");
918
+
919
+ buffer_json_member_add_string(wb, "_query", channel2utf8(q.buffer));
920
+
921
+ return q.buffer;
922
+}
923
+
924
+static int wevt_master_query(BUFFER *wb __maybe_unused, LOGS_QUERY_STATUS *lqs __maybe_unused) {
925
+ // make sure the sources list is updated
926
+ wevt_sources_scan();
927
+
928
+ lqs->c.query = wevt_generate_query(lqs, wb);
929
+ if(!lqs->c.query)
930
+ return rrd_call_function_error(wb, "failed to generate XPath query", HTTP_RESP_INTERNAL_SERVER_ERROR);
931
+
932
+ FACETS *facets = lqs->facets;
933
+
934
+ WEVT_QUERY_STATUS status = WEVT_NO_CHANNEL_MATCHED;
935
+
936
+ lqs->c.files_matched = 0;
937
+ lqs->c.file_working = 0;
938
+ lqs->c.rows_useful = 0;
939
+ lqs->c.rows_read = 0;
940
+ lqs->c.bytes_read = 0;
941
+
942
+ size_t files_used = 0;
943
+ size_t files_max = dictionary_entries(wevt_sources);
944
+ const DICTIONARY_ITEM *file_items[files_max];
945
+
946
+ // count the files
947
+ bool files_are_newer = false;
948
+ LOGS_QUERY_SOURCE *src;
949
+ dfe_start_read(wevt_sources, src) {
950
+ if(!source_is_mine(src, lqs))
951
+ continue;
952
+
953
+ file_items[files_used++] = dictionary_acquired_item_dup(wevt_sources, src_dfe.item);
954
+
955
+ if(src->msg_last_ut > lqs->rq.if_modified_since)
956
+ files_are_newer = true;
957
+
958
+ lqs->c.progress.entries.total += src->entries;
959
+ }
960
+ dfe_done(jf);
961
+
962
+ lqs->c.files_matched = files_used;
963
+
964
+ if(lqs->rq.if_modified_since && !files_are_newer) {
965
+ // release the files
966
+ for(size_t f = 0; f < files_used ;f++)
967
+ dictionary_acquired_item_release(wevt_sources, file_items[f]);
968
+
969
+ return rrd_call_function_error(wb, "not modified", HTTP_RESP_NOT_MODIFIED);
970
+ }
971
+
972
+ // sort the files, so that they are optimal for facets
973
+ if(files_used >= 2) {
974
+ if (lqs->rq.direction == FACETS_ANCHOR_DIRECTION_BACKWARD)
975
+ qsort(file_items, files_used, sizeof(const DICTIONARY_ITEM *),
976
+ wevt_sources_dict_items_backward_compar);
977
+ else
978
+ qsort(file_items, files_used, sizeof(const DICTIONARY_ITEM *),
979
+ wevt_sources_dict_items_forward_compar);
980
+ }
981
+
982
+ bool partial = false;
983
+ usec_t query_started_ut = now_monotonic_usec();
984
+ usec_t started_ut = query_started_ut;
985
+ usec_t ended_ut = started_ut;
986
+ usec_t duration_ut, max_duration_ut = 0;
987
+
988
+ WEVT_LOG *log = wevt_openlog6();
989
+ if(!log) {
990
+ // release the files
991
+ for(size_t f = 0; f < files_used ;f++)
992
+ dictionary_acquired_item_release(wevt_sources, file_items[f]);
993
+
994
+ netdata_log_error("WINDOWS EVENTS: cannot open windows event log");
995
+ return rrd_call_function_error(wb, "cannot open windows events log", HTTP_RESP_INTERNAL_SERVER_ERROR);
996
+ }
997
+
998
+ // sampling_query_init(lqs, facets);
999
+
1000
+ buffer_json_member_add_array(wb, "_channels");
1001
+ for(size_t f = 0; f < files_used ;f++) {
1002
+ const char *fullname = dictionary_acquired_item_name(file_items[f]);
1003
+ src = dictionary_acquired_item_value(file_items[f]);
1004
+
1005
+ if(!source_is_mine(src, lqs))
1006
+ continue;
1007
+
1008
+ started_ut = ended_ut;
1009
+
1010
+ // do not even try to do the query if we expect it to pass the timeout
1011
+ if(ended_ut + max_duration_ut * 3 >= *lqs->stop_monotonic_ut) {
1012
+ partial = true;
1013
+ status = WEVT_TIMED_OUT;
1014
+ break;
1015
+ }
1016
+
1017
+ lqs->c.file_working++;
1018
+
1019
+ size_t rows_useful = lqs->c.rows_useful;
1020
+ size_t rows_read = lqs->c.rows_read;
1021
+ size_t bytes_read = lqs->c.bytes_read;
1022
+ size_t matches_setup_ut = lqs->c.matches_setup_ut;
1023
+
1024
+ // sampling_file_init(lqs, src);
1025
+
1026
+ lqs->c.progress.entries.current_query_total = src->entries;
1027
+ WEVT_QUERY_STATUS tmp_status = wevt_query_one_channel(log, wb, facets, src, lqs);
1028
+
1029
+ rows_useful = lqs->c.rows_useful - rows_useful;
1030
+ rows_read = lqs->c.rows_read - rows_read;
1031
+ bytes_read = lqs->c.bytes_read - bytes_read;
1032
+ matches_setup_ut = lqs->c.matches_setup_ut - matches_setup_ut;
1033
+
1034
+ ended_ut = now_monotonic_usec();
1035
+ duration_ut = ended_ut - started_ut;
1036
+
1037
+ if(duration_ut > max_duration_ut)
1038
+ max_duration_ut = duration_ut;
1039
+
1040
+ buffer_json_add_array_item_object(wb); // channel source
1041
+ {
1042
+ // information about the file
1043
+ buffer_json_member_add_string(wb, "_name", fullname);
1044
+ buffer_json_member_add_uint64(wb, "_source_type", src->source_type);
1045
+ buffer_json_member_add_string(wb, "_source", string2str(src->source));
1046
+ buffer_json_member_add_uint64(wb, "_msg_first_ut", src->msg_first_ut);
1047
+ buffer_json_member_add_uint64(wb, "_msg_last_ut", src->msg_last_ut);
1048
+
1049
+ // information about the current use of the file
1050
+ buffer_json_member_add_uint64(wb, "duration_ut", ended_ut - started_ut);
1051
+ buffer_json_member_add_uint64(wb, "rows_read", rows_read);
1052
+ buffer_json_member_add_uint64(wb, "rows_useful", rows_useful);
1053
+ buffer_json_member_add_double(wb, "rows_per_second", (double) rows_read / (double) duration_ut * (double) USEC_PER_SEC);
1054
+ buffer_json_member_add_uint64(wb, "bytes_read", bytes_read);
1055
+ buffer_json_member_add_double(wb, "bytes_per_second", (double) bytes_read / (double) duration_ut * (double) USEC_PER_SEC);
1056
+ buffer_json_member_add_uint64(wb, "duration_matches_ut", matches_setup_ut);
1057
+
1058
+ // if(lqs->rq.sampling) {
1059
+ // buffer_json_member_add_object(wb, "_sampling");
1060
+ // {
1061
+ // buffer_json_member_add_uint64(wb, "sampled", lqs->c.samples_per_file.sampled);
1062
+ // buffer_json_member_add_uint64(wb, "unsampled", lqs->c.samples_per_file.unsampled);
1063
+ // buffer_json_member_add_uint64(wb, "estimated", lqs->c.samples_per_file.estimated);
1064
+ // }
1065
+ // buffer_json_object_close(wb); // _sampling
1066
+ // }
1067
+ }
1068
+ buffer_json_object_close(wb); // channel source
1069
+
1070
+ bool stop = false;
1071
+ switch(tmp_status) {
1072
+ case WEVT_OK:
1073
+ case WEVT_NO_CHANNEL_MATCHED:
1074
+ status = (status == WEVT_OK) ? WEVT_OK : tmp_status;
1075
+ break;
1076
+
1077
+ case WEVT_FAILED_TO_OPEN:
1078
+ case WEVT_FAILED_TO_SEEK:
1079
+ partial = true;
1080
+ if(status == WEVT_NO_CHANNEL_MATCHED)
1081
+ status = tmp_status;
1082
+ break;
1083
+
1084
+ case WEVT_CANCELLED:
1085
+ case WEVT_TIMED_OUT:
1086
+ partial = true;
1087
+ stop = true;
1088
+ status = tmp_status;
1089
+ break;
1090
+
1091
+ case WEVT_NOT_MODIFIED:
1092
+ internal_fatal(true, "this should never be returned here");
1093
+ break;
1094
+ }
1095
+
1096
+ if(stop)
1097
+ break;
1098
+ }
1099
+ buffer_json_array_close(wb); // _channels
1100
+
1101
+ // release the files
1102
+ for(size_t f = 0; f < files_used ;f++)
1103
+ dictionary_acquired_item_release(wevt_sources, file_items[f]);
1104
+
1105
+ switch (status) {
1106
+ case WEVT_OK:
1107
+ if(lqs->rq.if_modified_since && !lqs->c.rows_useful)
1108
+ return rrd_call_function_error(wb, "no useful logs, not modified", HTTP_RESP_NOT_MODIFIED);
1109
+ break;
1110
+
1111
+ case WEVT_TIMED_OUT:
1112
+ case WEVT_NO_CHANNEL_MATCHED:
1113
+ break;
1114
+
1115
+ case WEVT_CANCELLED:
1116
+ return rrd_call_function_error(wb, "client closed connection", HTTP_RESP_CLIENT_CLOSED_REQUEST);
1117
+
1118
+ case WEVT_NOT_MODIFIED:
1119
+ return rrd_call_function_error(wb, "not modified", HTTP_RESP_NOT_MODIFIED);
1120
+
1121
+ case WEVT_FAILED_TO_OPEN:
1122
+ return rrd_call_function_error(wb, "failed to open event log", HTTP_RESP_INTERNAL_SERVER_ERROR);
1123
+
1124
+ case WEVT_FAILED_TO_SEEK:
1125
+ return rrd_call_function_error(wb, "failed to execute event log query", HTTP_RESP_INTERNAL_SERVER_ERROR);
1126
+
1127
+ default:
1128
+ return rrd_call_function_error(wb, "unknown status", HTTP_RESP_INTERNAL_SERVER_ERROR);
1129
+ }
1130
+
1131
+ buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
1132
+ buffer_json_member_add_boolean(wb, "partial", partial);
1133
+ buffer_json_member_add_string(wb, "type", "table");
1134
+
1135
+ // build a message for the query
1136
+ if(!lqs->rq.data_only) {
1137
+ CLEAN_BUFFER *msg = buffer_create(0, NULL);
1138
+ CLEAN_BUFFER *msg_description = buffer_create(0, NULL);
1139
+ ND_LOG_FIELD_PRIORITY msg_priority = NDLP_INFO;
1140
+
1141
+ // if(!journal_files_completed_once()) {
1142
+ // buffer_strcat(msg, "Journals are still being scanned. ");
1143
+ // buffer_strcat(msg_description
1144
+ // , "LIBRARY SCAN: The journal files are still being scanned, you are probably viewing incomplete data. ");
1145
+ // msg_priority = NDLP_WARNING;
1146
+ // }
1147
+
1148
+ if(partial) {
1149
+ buffer_strcat(msg, "Query timed-out, incomplete data. ");
1150
+ buffer_strcat(msg_description
1151
+ , "QUERY TIMEOUT: The query timed out and may not include all the data of the selected window. ");
1152
+ msg_priority = NDLP_WARNING;
1153
+ }
1154
+
1155
+ // if(lqs->c.samples.estimated || lqs->c.samples.unsampled) {
1156
+ // double percent = (double) (lqs->c.samples.sampled * 100.0 /
1157
+ // (lqs->c.samples.estimated + lqs->c.samples.unsampled + lqs->c.samples.sampled));
1158
+ // buffer_sprintf(msg, "%.2f%% real data", percent);
1159
+ // buffer_sprintf(msg_description, "ACTUAL DATA: The filters counters reflect %0.2f%% of the data. ", percent);
1160
+ // msg_priority = MIN(msg_priority, NDLP_NOTICE);
1161
+ // }
1162
+ //
1163
+ // if(lqs->c.samples.unsampled) {
1164
+ // double percent = (double) (lqs->c.samples.unsampled * 100.0 /
1165
+ // (lqs->c.samples.estimated + lqs->c.samples.unsampled + lqs->c.samples.sampled));
1166
+ // buffer_sprintf(msg, ", %.2f%% unsampled", percent);
1167
+ // buffer_sprintf(msg_description
1168
+ // , "UNSAMPLED DATA: %0.2f%% of the events exist and have been counted, but their values have not been evaluated, so they are not included in the filters counters. "
1169
+ // , percent);
1170
+ // msg_priority = MIN(msg_priority, NDLP_NOTICE);
1171
+ // }
1172
+ //
1173
+ // if(lqs->c.samples.estimated) {
1174
+ // double percent = (double) (lqs->c.samples.estimated * 100.0 /
1175
+ // (lqs->c.samples.estimated + lqs->c.samples.unsampled + lqs->c.samples.sampled));
1176
+ // buffer_sprintf(msg, ", %.2f%% estimated", percent);
1177
+ // buffer_sprintf(msg_description
1178
+ // , "ESTIMATED DATA: The query selected a large amount of data, so to avoid delaying too much, the presented data are estimated by %0.2f%%. "
1179
+ // , percent);
1180
+ // msg_priority = MIN(msg_priority, NDLP_NOTICE);
1181
+ // }
1182
+
1183
+ buffer_json_member_add_object(wb, "message");
1184
+ if(buffer_tostring(msg)) {
1185
+ buffer_json_member_add_string(wb, "title", buffer_tostring(msg));
1186
+ buffer_json_member_add_string(wb, "description", buffer_tostring(msg_description));
1187
+ buffer_json_member_add_string(wb, "status", nd_log_id2priority(msg_priority));
1188
+ }
1189
+ // else send an empty object if there is nothing to tell
1190
+ buffer_json_object_close(wb); // message
1191
+ }
1192
+
1193
+ if(!lqs->rq.data_only) {
1194
+ buffer_json_member_add_time_t(wb, "update_every", 1);
1195
+ buffer_json_member_add_string(wb, "help", WEVT_FUNCTION_DESCRIPTION);
1196
+ }
1197
+
1198
+ if(!lqs->rq.data_only || lqs->rq.tail)
1199
+ buffer_json_member_add_uint64(wb, "last_modified", lqs->last_modified);
1200
+
1201
+ facets_sort_and_reorder_keys(facets);
1202
+ facets_report(facets, wb, used_hashes_registry);
1203
+
1204
+ wb->expires = now_realtime_sec() + (lqs->rq.data_only ? 3600 : 0);
1205
+ buffer_json_member_add_time_t(wb, "expires", wb->expires);
1206
+
1207
+ // if(lqs->rq.sampling) {
1208
+ // buffer_json_member_add_object(wb, "_sampling");
1209
+ // {
1210
+ // buffer_json_member_add_uint64(wb, "sampled", lqs->c.samples.sampled);
1211
+ // buffer_json_member_add_uint64(wb, "unsampled", lqs->c.samples.unsampled);
1212
+ // buffer_json_member_add_uint64(wb, "estimated", lqs->c.samples.estimated);
1213
+ // }
1214
+ // buffer_json_object_close(wb); // _sampling
1215
+ // }
1216
+
1217
+ wevt_closelog6(log);
1218
+
1219
+ wb->content_type = CT_APPLICATION_JSON;
1220
+ wb->response_code = HTTP_RESP_OK;
1221
+ return wb->response_code;
1222
+}
1223
+
1224
+void function_windows_events(const char *transaction, char *function, usec_t *stop_monotonic_ut, bool *cancelled,
1225
+ BUFFER *payload, HTTP_ACCESS access __maybe_unused,
1226
+ const char *source __maybe_unused, void *data __maybe_unused) {
1227
+ bool have_slice = LQS_DEFAULT_SLICE_MODE;
1228
+
1229
+ LOGS_QUERY_STATUS tmp_fqs = {
1230
+ .facets = lqs_facets_create(
1231
+ LQS_DEFAULT_ITEMS_PER_QUERY,
1232
+ FACETS_OPTION_ALL_KEYS_FTS,
1233
+ WEVT_ALWAYS_VISIBLE_KEYS,
1234
+ WEVT_KEYS_INCLUDED_IN_FACETS,
1235
+ WEVT_KEYS_EXCLUDED_FROM_FACETS,
1236
+ have_slice),
1237
+
1238
+ .rq = LOGS_QUERY_REQUEST_DEFAULTS(transaction, have_slice, FACETS_ANCHOR_DIRECTION_BACKWARD),
1239
+
1240
+ .cancelled = cancelled,
1241
+ .stop_monotonic_ut = stop_monotonic_ut,
1242
+ };
1243
+ LOGS_QUERY_STATUS *lqs = &tmp_fqs;
1244
+
1245
+ CLEAN_BUFFER *wb = lqs_create_output_buffer();
1246
+
1247
+ // ------------------------------------------------------------------------
1248
+ // parse the parameters
1249
+
1250
+ if(lqs_request_parse_and_validate(lqs, wb, function, payload, have_slice, WEVT_FIELD_LEVEL)) {
1251
+ wevt_register_fields(lqs);
1252
+
1253
+ // ------------------------------------------------------------------------
1254
+ // add versions to the response
1255
+
1256
+ buffer_json_wevt_versions(wb);
1257
+
1258
+ // ------------------------------------------------------------------------
1259
+ // run the request
1260
+
1261
+ if (lqs->rq.info)
1262
+ lqs_info_response(wb, lqs->facets);
1263
+ else {
1264
+ wevt_master_query(wb, lqs);
1265
+ if (wb->response_code == HTTP_RESP_OK)
1266
+ buffer_json_finalize(wb);
1267
+ }
1268
+ }
1269
+
1270
+ netdata_mutex_lock(&stdout_mutex);
1271
+ pluginsd_function_result_to_stdout(transaction, wb);
1272
+ netdata_mutex_unlock(&stdout_mutex);
1273
+
1274
+ lqs_cleanup(lqs);
1275
+}
1276
+
1277
+int main(int argc __maybe_unused, char **argv __maybe_unused) {
1278
+ clocks_init();
1279
+ nd_thread_tag_set("wevt.plugin");
1280
+ nd_log_initialize_for_external_plugins("windows-events.plugin");
1281
+
1282
+ // ------------------------------------------------------------------------
1283
+ // initialization
1284
+
1285
+ wevt_sources_init();
1286
+ publisher_cache_init();
1287
+ sid_cache_init();
1288
+ field_cache_init();
1289
+
1290
+ // ------------------------------------------------------------------------
1291
+ // debug
1292
+
1293
+ if(argc >= 2 && strcmp(argv[argc - 1], "debug") == 0) {
1294
+ wevt_sources_scan();
1295
+
1296
+ struct {
1297
+ const char *func;
1298
+ } array[] = {
1299
+ //{ "windows-events after:-86400 before:0 direction:backward last:200 facets:HdUoSYab5wV,Cq2r7mRUv4a,LAnVlsIQfeD,BnPLNbA5VWT,KeCITtVD5AD,HytMJ9kj82B,JM3OPW3kHn6,H106l8MXSSr,HREiMN.4Ahu,ClaDGnYSQE7,ApYltST_icg,PtkRm91M0En data_only:false slice:true source:All" },
1300
+ //{ "windows-events after:1726055370 before:1726056270 direction:backward last:200 facets:HdUoSYab5wV,Cq2r7mRUv4a,LAnVlsIQfeD,BnPLNbA5VWT,KeCITtVD5AD,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6,ClaDGnYSQE7,H106l8MXSSr,HREiMN.4Ahu data_only:false source:All HytMJ9kj82B:BlC24d5JBBV,PtVoyIuX.MU,HMj1B38kHTv KeCITtVD5AD:PY1JtCeWwSe,O9kz5J37nNl,JZoJURadhDb" },
1301
+ { "windows-events after:1725738894 before:0 last:200 facets:HdUoSYab5wV,Cq2r7mRUv4a,LAnVlsIQfeD,BnPLNbA5VWT,KeCITtVD5AD,HytMJ9kj82B,JM3OPW3kHn6,H106l8MXSSr,HREiMN.4Ahu,ClaDGnYSQE7,ApYltST_icg,PtkRm91M0En source:All" },
1302
+ // { "windows-events after:1725636012 before:1726240812 direction:backward last:200 facets:HdUoSYab5wV,Cq2r7mRUv4a,LAnVlsIQfeD,BnPLNbA5VWT,KeCITtVD5AD,HytMJ9kj82B,JM3OPW3kHn6,H106l8MXSSr,HREiMN.4Ahu,ClaDGnYSQE7,ApYltST_icg,PtkRm91M0En data_only:false source:All PtkRm91M0En:LDzHbP5libb" },
1303
+ //{ "windows-events after:1725650386 before:1725736786 anchor:1725652420809461 direction:forward last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 if_modified_since:1725736649011085 data_only:true delta:true tail:true source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1304
+ //{ "windows-events info after:1725650420 before:1725736820" },
1305
+ //{ "windows-events after:1725650420 before:1725736820 last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1306
+ //{ "windows-events after:1725650430 before:1725736830 last:200 facets:HWNGeY7tg6c,LAnVlsIQfeD,BnPLNbA5VWT,Cq2r7mRUv4a,KeCITtVD5AD,I_Amz_APBm3,HytMJ9kj82B,LT.Xp9I9tiP,No4kPTQbS.g,LQ2LQzfE8EG,PtkRm91M0En,JM3OPW3kHn6 source:all Cq2r7mRUv4a:PPc9fUy.q6o No4kPTQbS.g:Dwo9PhK27v3 HytMJ9kj82B:KbbznGjt_9r LAnVlsIQfeD:OfU1t5cpjgG JM3OPW3kHn6:CS_0g5AEpy2" },
1307
+ { NULL },
1308
+ };
1309
+
1310
+ for(int i = 0; array[i].func ;i++) {
1311
+ bool cancelled = false;
1312
+ usec_t stop_monotonic_ut = now_monotonic_usec() + 600 * USEC_PER_SEC;
1313
+ //char buf[] = "windows-events after:-86400 before:0 direction:backward last:200 data_only:false slice:true source:all";
1314
+ function_windows_events("123", (char *)array[i].func, &stop_monotonic_ut, &cancelled, NULL, HTTP_ACCESS_ALL, NULL, NULL);
1315
+ }
1316
+ printf("\n\nAll done!\n\n");
1317
+ fflush(stdout);
1318
+ exit(1);
1319
+ }
1320
+
1321
+ // ------------------------------------------------------------------------
1322
+ // the event loop for functions
1323
+
1324
+ struct functions_evloop_globals *wg =
1325
+ functions_evloop_init(WINDOWS_EVENTS_WORKER_THREADS, "WEVT", &stdout_mutex, &plugin_should_exit);
1326
+
1327
+ functions_evloop_add_function(wg,
1328
+ WEVT_FUNCTION_NAME,
1329
+ function_windows_events,
1330
+ WINDOWS_EVENTS_DEFAULT_TIMEOUT,
1331
+ NULL);
1332
+
1333
+ // ------------------------------------------------------------------------
1334
+ // register functions to netdata
1335
+
1336
+ netdata_mutex_lock(&stdout_mutex);
1337
+
1338
+ fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"logs\" "HTTP_ACCESS_FORMAT" %d\n",
1339
+ WEVT_FUNCTION_NAME, WINDOWS_EVENTS_DEFAULT_TIMEOUT, WEVT_FUNCTION_DESCRIPTION,
1340
+ (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA),
1341
+ RRDFUNCTIONS_PRIORITY_DEFAULT);
1342
+
1343
+ fflush(stdout);
1344
+ netdata_mutex_unlock(&stdout_mutex);
1345
+
1346
+ // ------------------------------------------------------------------------
1347
+
1348
+ const usec_t step_ut = 100 * USEC_PER_MS;
1349
+ usec_t send_newline_ut = 0;
1350
+ usec_t since_last_scan_ut = WINDOWS_EVENTS_SCAN_EVERY_USEC * 2; // something big to trigger scanning at start
1351
+ const bool tty = isatty(fileno(stdout)) == 1;
1352
+
1353
+ heartbeat_t hb;
1354
+ heartbeat_init(&hb);
1355
+ while(!plugin_should_exit) {
1356
+
1357
+ if(since_last_scan_ut > WINDOWS_EVENTS_SCAN_EVERY_USEC) {
1358
+ wevt_sources_scan();
1359
+ since_last_scan_ut = 0;
1360
+ }
1361
+
1362
+ usec_t dt_ut = heartbeat_next(&hb, step_ut);
1363
+ since_last_scan_ut += dt_ut;
1364
+ send_newline_ut += dt_ut;
1365
+
1366
+ if(!tty && send_newline_ut > USEC_PER_SEC) {
1367
+ send_newline_and_flush(&stdout_mutex);
1368
+ send_newline_ut = 0;
1369
+ }
1370
+ }
1371
+
1372
+ exit(0);
1373
+}