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