0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-14 09:38:34 +00:00

Windows Events Improvements 1 ()

* extract more information for the XML to form the message

* support slicing for EventID and Level

* the fastest it can get

* remove obsolete member

* stop service Netdata before starting Netdata

* do not reuse buffers

* convert newlines to unix style

* send progress based on the number of log events

* rework on opcodes, keywords and task to allow hard-coded values

* complete messages; facets annotated from XML

* working with field and provider caches

* cleanup

* query the event log in batch mode

* extract keywords from publishers

* detect closed sockets on non-Linux systems

* unified event log processing

* work on caching, improving accuracy

* optimization on keywords

* caching improvements
This commit is contained in:
Costa Tsaousis 2024-09-16 15:40:17 +03:00 committed by GitHub
parent d953ce31cd
commit f6a175aeb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 4221 additions and 2653 deletions

View file

@ -1411,6 +1411,10 @@ set(WINDOWS_EVENTS_PLUGIN_FILES
src/collectors/windows-events.plugin/windows-events-unicode.h
src/collectors/windows-events.plugin/windows-events-xml.c
src/collectors/windows-events.plugin/windows-events-xml.h
src/collectors/windows-events.plugin/windows-events-publishers.c
src/collectors/windows-events.plugin/windows-events-publishers.h
src/collectors/windows-events.plugin/windows-events-fields-cache.c
src/collectors/windows-events.plugin/windows-events-fields-cache.h
)
set(WINDOWS_PLUGIN_FILES

View file

@ -3,7 +3,7 @@
# On MSYS2, install these dependencies to build netdata:
install_dependencies() {
pacman -S \
git cmake ninja base-devel msys2-devel \
git cmake ninja clang base-devel msys2-devel \
libyaml-devel libzstd-devel libutil-linux libutil-linux-devel \
mingw-w64-x86_64-toolchain mingw-w64-ucrt-x86_64-toolchain \
mingw64/mingw-w64-x86_64-mold ucrt64/mingw-w64-ucrt-x86_64-mold \
@ -75,6 +75,9 @@ ninja -v -C "${build}" install || ninja -v -C "${build}" -j 1
#echo "Compile with:"
#echo "ninja -v -C \"${build}\" install || ninja -v -C \"${build}\" -j 1"
echo "Stopping service Netdata"
sc stop "Netdata" || echo "Failed"
echo "starting netdata..."
# enable JIT debug with gdb
export MSYS="error_start:$(cygpath -w /usr/bin/gdb)"

View file

@ -336,10 +336,10 @@ ND_SD_JOURNAL_STATUS netdata_systemd_journal_query_backward(
LOGS_QUERY_STATUS *fqs) {
usec_t anchor_delta = __atomic_load_n(&jf->max_journal_vs_realtime_delta_ut, __ATOMIC_RELAXED);
usec_t start_ut = ((fqs->rq.data_only && fqs->anchor.start_ut) ? fqs->anchor.start_ut : fqs->rq.before_ut) + anchor_delta;
usec_t stop_ut = (fqs->rq.data_only && fqs->anchor.stop_ut) ? fqs->anchor.stop_ut : fqs->rq.after_ut;
bool stop_when_full = (fqs->rq.data_only && !fqs->anchor.stop_ut);
lqs_query_timeframe(fqs, anchor_delta);
usec_t start_ut = fqs->query.start_ut;
usec_t stop_ut = fqs->query.stop_ut;
bool stop_when_full = fqs->query.stop_when_full;
fqs->c.query_file.start_ut = start_ut;
fqs->c.query_file.stop_ut = stop_ut;
@ -451,10 +451,10 @@ ND_SD_JOURNAL_STATUS netdata_systemd_journal_query_forward(
LOGS_QUERY_STATUS *fqs) {
usec_t anchor_delta = __atomic_load_n(&jf->max_journal_vs_realtime_delta_ut, __ATOMIC_RELAXED);
usec_t start_ut = (fqs->rq.data_only && fqs->anchor.start_ut) ? fqs->anchor.start_ut : fqs->rq.after_ut;
usec_t stop_ut = ((fqs->rq.data_only && fqs->anchor.stop_ut) ? fqs->anchor.stop_ut : fqs->rq.before_ut) + anchor_delta;
bool stop_when_full = (fqs->rq.data_only && !fqs->anchor.stop_ut);
lqs_query_timeframe(fqs, anchor_delta);
usec_t start_ut = fqs->query.start_ut;
usec_t stop_ut = fqs->query.stop_ut;
bool stop_when_full = fqs->query.stop_when_full;
fqs->c.query_file.start_ut = start_ut;
fqs->c.query_file.stop_ut = stop_ut;
@ -747,8 +747,13 @@ static int netdata_systemd_journal_query(BUFFER *wb, LOGS_QUERY_STATUS *lqs) {
lqs->c.files_matched = files_used;
if(lqs->rq.if_modified_since && !files_are_newer)
if(lqs->rq.if_modified_since && !files_are_newer) {
// release the files
for(size_t f = 0; f < files_used ;f++)
dictionary_acquired_item_release(journal_files_registry, file_items[f]);
return rrd_call_function_error(wb, "not modified", HTTP_RESP_NOT_MODIFIED);
}
// sort the files, so that they are optimal for facets
if(files_used >= 2) {

View file

@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-fields-cache.h"
typedef struct field_key {
uint64_t value;
ND_UUID provider;
} WEVT_FIELD_KEY;
typedef struct field_value {
WEVT_FIELD_KEY key;
uint32_t name_size;
char name[];
} WEVT_FIELD_VALUE;
#define SIMPLE_HASHTABLE_NAME _FIELDS_CACHE
#define SIMPLE_HASHTABLE_VALUE_TYPE WEVT_FIELD_VALUE
#define SIMPLE_HASHTABLE_KEY_TYPE WEVT_FIELD_KEY
#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION field_cache_value_to_key
#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION field_cache_cache_compar
#define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 1
#include "libnetdata/simple_hashtable.h"
static inline WEVT_FIELD_KEY *field_cache_value_to_key(WEVT_FIELD_VALUE *p) {
return &p->key;
}
static inline bool field_cache_cache_compar(WEVT_FIELD_KEY *a, WEVT_FIELD_KEY *b) {
return memcmp(a, b, sizeof(WEVT_FIELD_KEY)) == 0;
}
struct ht {
SPINLOCK spinlock;
size_t allocations;
size_t bytes;
struct simple_hashtable_FIELDS_CACHE ht;
};
static struct {
bool initialized;
struct ht ht[WEVT_FIELD_TYPE_MAX];
} fdc = {
.initialized = false,
};
void field_cache_init(void) {
for(size_t type = 0; type < WEVT_FIELD_TYPE_MAX ; type++) {
spinlock_init(&fdc.ht[type].spinlock);
simple_hashtable_init_FIELDS_CACHE(&fdc.ht[type].ht, 10000);
}
}
static inline bool should_zero_provider(WEVT_FIELD_TYPE type, uint64_t value) {
switch(type) {
case WEVT_FIELD_TYPE_LEVEL:
return !is_valid_publisher_level(value, true);
case WEVT_FIELD_TYPE_KEYWORDS:
return !is_valid_publisher_keywords(value, true);
case WEVT_FIELD_TYPE_OPCODE:
return !is_valid_publisher_opcode(value, true);
case WEVT_FIELD_TYPE_TASK:
return !is_valid_publisher_task(value, true);
default:
return false;
}
}
bool field_cache_get(WEVT_FIELD_TYPE type, const ND_UUID *uuid, uint64_t value, TXT_UTF8 *dst) {
fatal_assert(type < WEVT_FIELD_TYPE_MAX);
struct ht *ht = &fdc.ht[type];
WEVT_FIELD_KEY t = {
.value = value,
.provider = should_zero_provider(type, value) ? UUID_ZERO : *uuid,
};
XXH64_hash_t hash = XXH3_64bits(&t, sizeof(t));
spinlock_lock(&ht->spinlock);
SIMPLE_HASHTABLE_SLOT_FIELDS_CACHE *slot = simple_hashtable_get_slot_FIELDS_CACHE(&ht->ht, hash, &t, true);
WEVT_FIELD_VALUE *v = SIMPLE_HASHTABLE_SLOT_DATA(slot);
spinlock_unlock(&ht->spinlock);
if(v) {
txt_utf8_resize(dst, v->name_size, false);
memcpy(dst->data, v->name, v->name_size);
dst->used = v->name_size;
dst->src = TXT_SOURCE_FIELD_CACHE;
return true;
}
return false;
}
static WEVT_FIELD_VALUE *wevt_create_cache_entry(WEVT_FIELD_KEY *t, TXT_UTF8 *name, size_t *bytes) {
*bytes = sizeof(WEVT_FIELD_VALUE) + name->used;
WEVT_FIELD_VALUE *v = callocz(1, *bytes);
v->key = *t;
memcpy(v->name, name->data, name->used);
v->name_size = name->used;
return v;
}
//static bool is_numeric(const char *s) {
// while(*s) {
// if(!isdigit((uint8_t)*s++))
// return false;
// }
//
// return true;
//}
void field_cache_set(WEVT_FIELD_TYPE type, const ND_UUID *uuid, uint64_t value, TXT_UTF8 *name) {
fatal_assert(type < WEVT_FIELD_TYPE_MAX);
struct ht *ht = &fdc.ht[type];
WEVT_FIELD_KEY t = {
.value = value,
.provider = should_zero_provider(type, value) ? UUID_ZERO : *uuid,
};
XXH64_hash_t hash = XXH3_64bits(&t, sizeof(t));
spinlock_lock(&ht->spinlock);
SIMPLE_HASHTABLE_SLOT_FIELDS_CACHE *slot = simple_hashtable_get_slot_FIELDS_CACHE(&ht->ht, hash, &t, true);
WEVT_FIELD_VALUE *v = SIMPLE_HASHTABLE_SLOT_DATA(slot);
if(!v) {
size_t bytes;
v = wevt_create_cache_entry(&t, name, &bytes);
simple_hashtable_set_slot_FIELDS_CACHE(&ht->ht, slot, hash, v);
ht->allocations++;
ht->bytes += bytes;
}
// else {
// if((v->name_size == 1 && name->used > 0) || is_numeric(v->name)) {
// size_t bytes;
// WEVT_FIELD_VALUE *nv = wevt_create_cache_entry(&t, name, &bytes);
// simple_hashtable_set_slot_FIELDS_CACHE(&ht->ht, slot, hash, nv);
// ht->bytes += name->used;
// ht->bytes -= v->name_size;
// freez(v);
// }
// else if(name->used > 2 && !is_numeric(name->data) && (v->name_size != name->used || strcasecmp(v->name, name->data) != 0)) {
// const char *a = v->name;
// const char *b = name->data;
// int x = 0;
// x++;
// }
// }
spinlock_unlock(&ht->spinlock);
}

View file

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_FIELDS_CACHE_H
#define NETDATA_WINDOWS_EVENTS_FIELDS_CACHE_H
#include "windows-events.h"
typedef enum __attribute__((packed)) {
WEVT_FIELD_TYPE_LEVEL = 0,
WEVT_FIELD_TYPE_OPCODE,
WEVT_FIELD_TYPE_KEYWORDS,
WEVT_FIELD_TYPE_TASK,
// terminator
WEVT_FIELD_TYPE_MAX,
} WEVT_FIELD_TYPE;
void field_cache_init(void);
bool field_cache_get(WEVT_FIELD_TYPE type, const ND_UUID *uuid, uint64_t value, TXT_UTF8 *dst);
void field_cache_set(WEVT_FIELD_TYPE type, const ND_UUID *uuid, uint64_t value, TXT_UTF8 *name);
#endif //NETDATA_WINDOWS_EVENTS_FIELDS_CACHE_H

View file

@ -0,0 +1,569 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-publishers.h"
#define MAX_OPEN_HANDLES_PER_PUBLISHER 5
struct publisher;
// typedef as PROVIDER_META_HANDLE in include file
struct provider_meta_handle {
pid_t owner; // the owner of the handle, or zero
uint32_t locks; // the number of locks the owner has on this handle
EVT_HANDLE hMetadata; // the handle
struct publisher *publisher; // a pointer back to the publisher
// double linked list
PROVIDER_META_HANDLE *prev;
PROVIDER_META_HANDLE *next;
};
struct provider_data {
uint64_t value; // the mask of the keyword
XXH64_hash_t hash; // the hash of the name
uint32_t len; // the length of the name
char *name; // the name of the keyword in UTF-8
};
struct provider_list {
uint64_t min, max;
bool exceeds_data_type; // true when the manifest values exceed the capacity of the EvtXXX() API
uint32_t total; // the number of entries in the array
struct provider_data *array; // the array of entries, sorted (for binary search)
};
typedef struct publisher {
ND_UUID uuid; // the Provider GUID
const char *name; // the Provider name (UTF-8)
uint32_t total_handles; // the number of handles allocated
uint32_t available_handles; // the number of available handles
uint32_t deleted_handles; // the number of deleted handles
PROVIDER_META_HANDLE *handles; // a double linked list of all the handles
struct provider_list keywords;
struct provider_list tasks;
struct provider_list opcodes;
struct provider_list levels;
} PUBLISHER;
// A hashtable implementation for publishers
// using the provider GUID as key and PUBLISHER as value
#define SIMPLE_HASHTABLE_NAME _PROVIDER_GUID
#define SIMPLE_HASHTABLE_VALUE_TYPE PUBLISHER
#define SIMPLE_HASHTABLE_KEY_TYPE ND_UUID
#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION publisher_value_to_key
#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION publisher_cache_compar
#define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 1
#include "libnetdata/simple_hashtable.h"
static struct {
SPINLOCK spinlock;
uint32_t total_publishers;
uint32_t total_handles;
uint32_t deleted_handles;
struct simple_hashtable_PROVIDER_GUID hashtable;
ARAL *aral_publishers;
ARAL *aral_handles;
} pbc = {
.spinlock = NETDATA_SPINLOCK_INITIALIZER,
};
static void publisher_load_list(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, WEVT_VARIANT *property, TXT_UNICODE *unicode, struct provider_list *l, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id);
static inline ND_UUID *publisher_value_to_key(PUBLISHER *p) {
return &p->uuid;
}
static inline bool publisher_cache_compar(ND_UUID *a, ND_UUID *b) {
return UUIDeq(*a, *b);
}
void publisher_cache_init(void) {
simple_hashtable_init_PROVIDER_GUID(&pbc.hashtable, 100000);
pbc.aral_publishers = aral_create("wevt_publishers", sizeof(PUBLISHER), 0, 4096, NULL, NULL, NULL, false, true);
pbc.aral_handles = aral_create("wevt_handles", sizeof(PROVIDER_META_HANDLE), 0, 4096, NULL, NULL, NULL, false, true);
}
PROVIDER_META_HANDLE *publisher_get(ND_UUID uuid, LPCWSTR providerName) {
if(!providerName || !providerName[0] || UUIDiszero(uuid))
return NULL;
// XXH64_hash_t hash = XXH3_64bits(&uuid, sizeof(uuid));
uint64_t hash = uuid.parts.low64 + uuid.parts.hig64;
spinlock_lock(&pbc.spinlock);
SIMPLE_HASHTABLE_SLOT_PROVIDER_GUID *slot =
simple_hashtable_get_slot_PROVIDER_GUID(&pbc.hashtable, hash, &uuid, true);
bool load_it = false;
PUBLISHER *p = SIMPLE_HASHTABLE_SLOT_DATA(slot);
if(!p) {
p = aral_callocz(pbc.aral_publishers);
p->uuid = uuid;
simple_hashtable_set_slot_PROVIDER_GUID(&pbc.hashtable, slot, hash, p);
load_it = true;
pbc.total_publishers++;
}
pid_t me = gettid_cached();
PROVIDER_META_HANDLE *h;
for(h = p->handles; h ;h = h->next) {
// find the first that is mine,
// or the first not owned by anyone
if(!h->owner || h->owner == me)
break;
}
if(!h) {
h = aral_callocz(pbc.aral_handles);
h->publisher = p;
h->hMetadata = EvtOpenPublisherMetadata(
NULL, // Local machine
providerName, // Provider name
NULL, // Log file path (NULL for default)
0, // Locale (0 for default locale)
0 // Flags
);
// we put it at the beginning of the list
// to find it first if the same owner needs more locks on it
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(p->handles, h, prev, next);
pbc.total_handles++;
p->total_handles++;
p->available_handles++;
}
if(!h->owner) {
fatal_assert(p->available_handles > 0);
p->available_handles--;
h->owner = me;
}
h->locks++;
if(load_it) {
WEVT_VARIANT content = { 0 };
WEVT_VARIANT property = { 0 };
TXT_UNICODE unicode = { 0 };
publisher_load_list(h, &content, &property, &unicode, &p->keywords, EvtPublisherMetadataKeywords);
publisher_load_list(h, &content, &property, &unicode, &p->levels, EvtPublisherMetadataLevels);
publisher_load_list(h, &content, &property, &unicode, &p->opcodes, EvtPublisherMetadataOpcodes);
publisher_load_list(h, &content, &property, &unicode, &p->tasks, EvtPublisherMetadataTasks);
txt_unicode_cleanup(&unicode);
wevt_variant_cleanup(&content);
wevt_variant_cleanup(&property);
}
spinlock_unlock(&pbc.spinlock);
return h;
}
EVT_HANDLE publisher_handle(PROVIDER_META_HANDLE *h) {
return h ? h->hMetadata : NULL;
}
PROVIDER_META_HANDLE *publisher_dup(PROVIDER_META_HANDLE *h) {
if(h) h->locks++;
return h;
}
void publisher_release(PROVIDER_META_HANDLE *h) {
if(!h) return;
pid_t me = gettid_cached();
fatal_assert(h->owner == me);
fatal_assert(h->locks > 0);
if(--h->locks == 0) {
PUBLISHER *p = h->publisher;
spinlock_lock(&pbc.spinlock);
h->owner = 0;
if(++p->available_handles > MAX_OPEN_HANDLES_PER_PUBLISHER) {
// there are multiple handles on this publisher
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(p->handles, h, prev, next);
if(h->hMetadata)
EvtClose(h->hMetadata);
aral_freez(pbc.aral_handles, h);
pbc.total_handles--;
p->total_handles--;
pbc.deleted_handles++;
p->deleted_handles++;
p->available_handles--;
}
else if(h->next) {
// it is not the last, put it at the end
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(p->handles, h, prev, next);
DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(p->handles, h, prev, next);
}
spinlock_unlock(&pbc.spinlock);
}
}
// --------------------------------------------------------------------------------------------------------------------
// load publisher lists
static bool wevt_get_property_from_array(WEVT_VARIANT *property, EVT_HANDLE handle, DWORD dwIndex, EVT_PUBLISHER_METADATA_PROPERTY_ID PropertyId) {
DWORD used = 0;
if (!EvtGetObjectArrayProperty(handle, PropertyId, dwIndex, 0, property->size, property->data, &used)) {
DWORD status = GetLastError();
if (status != ERROR_INSUFFICIENT_BUFFER) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetObjectArrayProperty() failed");
return false;
}
wevt_variant_resize(property, used);
if (!EvtGetObjectArrayProperty(handle, PropertyId, dwIndex, 0, property->size, property->data, &used)) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetObjectArrayProperty() failed");
return false;
}
}
property->used = used;
return true;
}
// Comparison function for ascending order (for Levels, Opcodes, Tasks)
static int compare_ascending(const void *a, const void *b) {
struct provider_data *d1 = (struct provider_data *)a;
struct provider_data *d2 = (struct provider_data *)b;
if (d1->value < d2->value) return -1;
if (d1->value > d2->value) return 1;
return 0;
}
//// Comparison function for descending order (for Keywords)
//static int compare_descending(const void *a, const void *b) {
// struct provider_data *d1 = (struct provider_data *)a;
// struct provider_data *d2 = (struct provider_data *)b;
//
// if (d1->value > d2->value) return -1;
// if (d1->value < d2->value) return 1;
// return 0;
//}
static void publisher_load_list(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, WEVT_VARIANT *property, TXT_UNICODE *unicode, struct provider_list *l, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id) {
if(!h || !h->hMetadata) return;
EVT_PUBLISHER_METADATA_PROPERTY_ID name_id, message_id, value_id;
uint8_t value_bits = 32;
int (*compare_func)(const void *, const void *);
bool (*is_valid)(uint64_t, bool);
switch(property_id) {
case EvtPublisherMetadataLevels:
name_id = EvtPublisherMetadataLevelName;
message_id = EvtPublisherMetadataLevelMessageID;
value_id = EvtPublisherMetadataLevelValue;
value_bits = 32;
compare_func = compare_ascending;
is_valid = is_valid_publisher_level;
break;
case EvtPublisherMetadataOpcodes:
name_id = EvtPublisherMetadataOpcodeName;
message_id = EvtPublisherMetadataOpcodeMessageID;
value_id = EvtPublisherMetadataOpcodeValue;
value_bits = 32;
is_valid = is_valid_publisher_opcode;
compare_func = compare_ascending;
break;
case EvtPublisherMetadataTasks:
name_id = EvtPublisherMetadataTaskName;
message_id = EvtPublisherMetadataTaskMessageID;
value_id = EvtPublisherMetadataTaskValue;
value_bits = 32;
is_valid = is_valid_publisher_task;
compare_func = compare_ascending;
break;
case EvtPublisherMetadataKeywords:
name_id = EvtPublisherMetadataKeywordName;
message_id = EvtPublisherMetadataKeywordMessageID;
value_id = EvtPublisherMetadataKeywordValue;
value_bits = 64;
is_valid = is_valid_publisher_keywords;
compare_func = NULL;
break;
default:
nd_log(NDLS_COLLECTORS, NDLP_ERR, "Internal Error: Can't handle property id %u", property_id);
return;
}
EVT_HANDLE hMetadata = h->hMetadata;
EVT_HANDLE hArray = NULL;
DWORD bufferUsed = 0;
DWORD itemCount = 0;
// Get the metadata array for the list (e.g., opcodes, tasks, or levels)
if (!EvtGetPublisherMetadataProperty(hMetadata, property_id, 0, 0, NULL, &bufferUsed)) {
DWORD status = GetLastError();
if (status != ERROR_INSUFFICIENT_BUFFER) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetPublisherMetadataProperty() failed");
goto cleanup;
}
}
wevt_variant_resize(content, bufferUsed);
if (!EvtGetPublisherMetadataProperty(hMetadata, property_id, 0, content->size, content->data, &bufferUsed)) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetPublisherMetadataProperty() failed after resize");
goto cleanup;
}
// Get the number of items (e.g., levels, tasks, or opcodes)
hArray = content->data->EvtHandleVal;
if (!EvtGetObjectArraySize(hArray, &itemCount)) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetObjectArraySize() failed");
goto cleanup;
}
if (itemCount == 0) {
l->total = 0;
l->array = NULL;
goto cleanup;
}
// Allocate memory for the list items
l->array = callocz(itemCount, sizeof(struct provider_data));
l->total = itemCount;
uint64_t min = UINT64_MAX, max = 0;
// Iterate over the list and populate the entries
for (DWORD i = 0; i < itemCount; ++i) {
struct provider_data *d = &l->array[i];
// Get the value (e.g., opcode, task, or level)
if (wevt_get_property_from_array(property, hArray, i, value_id)) {
switch(value_bits) {
case 64:
d->value = wevt_field_get_uint64(property->data);
break;
case 32:
d->value = wevt_field_get_uint32(property->data);
break;
}
if(d->value < min)
min = d->value;
if(d->value > max)
max = d->value;
if(!is_valid(d->value, false))
l->exceeds_data_type = true;
}
// Get the message ID
if (wevt_get_property_from_array(property, hArray, i, message_id)) {
uint32_t messageID = wevt_field_get_uint32(property->data);
if (messageID != (uint32_t)-1) {
if (wevt_get_message_unicode(unicode, hMetadata, NULL, messageID, EvtFormatMessageId)) {
size_t len;
d->name = unicode2utf8_strdupz(unicode->data, &len);
d->len = len;
}
}
}
// Get the name if the message is missing
if (!d->name && wevt_get_property_from_array(property, hArray, i, name_id)) {
fatal_assert(property->data->Type == EvtVarTypeString);
size_t len;
d->name = unicode2utf8_strdupz(property->data->StringVal, &len);
d->len = len;
}
// Calculate the hash for the name
if (d->name)
d->hash = XXH3_64bits(d->name, d->len);
}
l->min = min;
l->max = max;
if(itemCount > 1 && compare_func != NULL) {
// Sort the array based on the value (ascending for all except keywords, descending for keywords)
qsort(l->array, itemCount, sizeof(struct provider_data), compare_func);
}
cleanup:
if (hArray)
EvtClose(hArray);
}
// --------------------------------------------------------------------------------------------------------------------
// lookup functions
// lookup bitmap metdata (returns a comma separated list of strings)
static bool publisher_bitmap_metadata(TXT_UTF8 *dst, struct provider_list *l, uint64_t value) {
if(value < l->min || value > l->max || !l->total || !l->array || l->exceeds_data_type)
return false;
dst->used = 0;
for(size_t k = 0; value && k < l->total; k++) {
struct provider_data *d = &l->array[k];
if(d->value && (value & d->value) == d->value && d->name && d->len) {
const char *s = d->name;
size_t slen = d->len;
// remove the mask from the value
value &= ~(d->value);
txt_utf8_resize(dst, dst->used + slen + 2 + 1, true);
if(dst->used) {
// add a comma and a space
dst->data[dst->used++] = ',';
dst->data[dst->used++] = ' ';
}
memcpy(&dst->data[dst->used], s, slen);
dst->used += slen;
dst->src = TXT_SOURCE_PUBLISHER;
}
}
if(dst->used) {
txt_utf8_resize(dst, dst->used + 1, true);
dst->data[dst->used++] = 0;
}
fatal_assert(dst->used <= dst->size);
return (dst->used > 0);
}
//// lookup a single value (returns its string)
//static bool publisher_value_metadata_linear(TXT_UTF8 *dst, struct provider_list *l, uint64_t value) {
// if(value < l->min || value > l->max || !l->total || !l->array || l->exceeds_data_type)
// return false;
//
// dst->used = 0;
//
// for(size_t k = 0; k < l->total; k++) {
// struct provider_data *d = &l->array[k];
//
// if(d->value == value && d->name && d->len) {
// const char *s = d->name;
// size_t slen = d->len;
//
// txt_utf8_resize(dst, slen + 1, false);
//
// memcpy(dst->data, s, slen);
// dst->used = slen;
// dst->src = TXT_SOURCE_PUBLISHER;
//
// break;
// }
// }
//
// if(dst->used) {
// txt_utf8_resize(dst, dst->used + 1, true);
// dst->data[dst->used++] = 0;
// }
//
// fatal_assert(dst->used <= dst->size);
//
// return (dst->used > 0);
//}
static bool publisher_value_metadata(TXT_UTF8 *dst, struct provider_list *l, uint64_t value) {
if(value < l->min || value > l->max || !l->total || !l->array || l->exceeds_data_type)
return false;
// if(l->total < 3) return publisher_value_metadata_linear(dst, l, value);
dst->used = 0;
size_t left = 0;
size_t right = l->total - 1;
// Binary search within bounds
while (left <= right) {
size_t mid = left + (right - left) / 2;
struct provider_data *d = &l->array[mid];
if (d->value == value) {
// Value found, now check if it has a valid name and length
if (d->name && d->len) {
const char *s = d->name;
size_t slen = d->len;
txt_utf8_resize(dst, slen + 1, false);
memcpy(dst->data, s, slen);
dst->used = slen;
dst->data[dst->used++] = 0;
dst->src = TXT_SOURCE_PUBLISHER;
}
break;
}
if (d->value < value)
left = mid + 1;
else {
if (mid == 0) break;
right = mid - 1;
}
}
fatal_assert(dst->used <= dst->size);
return (dst->used > 0);
}
// --------------------------------------------------------------------------------------------------------------------
// public API to lookup metadata
bool publisher_keywords_cacheable(PROVIDER_META_HANDLE *h) {
return h && !h->publisher->keywords.exceeds_data_type;
}
bool publisher_tasks_cacheable(PROVIDER_META_HANDLE *h) {
return h && !h->publisher->tasks.exceeds_data_type;
}
bool is_useful_publisher_for_levels(PROVIDER_META_HANDLE *h) {
return h && !h->publisher->levels.exceeds_data_type;
}
bool publisher_opcodes_cacheable(PROVIDER_META_HANDLE *h) {
return h && !h->publisher->opcodes.exceeds_data_type;
}
bool publisher_get_keywords(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
if(!h) return false;
return publisher_bitmap_metadata(dst, &h->publisher->keywords, value);
}
bool publisher_get_level(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
if(!h) return false;
return publisher_value_metadata(dst, &h->publisher->levels, value);
}
bool publisher_get_task(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
if(!h) return false;
return publisher_value_metadata(dst, &h->publisher->tasks, value);
}
bool publisher_get_opcode(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
if(!h) return false;
return publisher_value_metadata(dst, &h->publisher->opcodes, value);
}

View file

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_PUBLISHERS_H
#define NETDATA_WINDOWS_EVENTS_PUBLISHERS_H
#include "windows-events.h"
struct provider_meta_handle;
typedef struct provider_meta_handle PROVIDER_META_HANDLE;
PROVIDER_META_HANDLE *publisher_get(ND_UUID uuid, LPCWSTR providerName);
void publisher_release(PROVIDER_META_HANDLE *h);
EVT_HANDLE publisher_handle(PROVIDER_META_HANDLE *h);
PROVIDER_META_HANDLE *publisher_dup(PROVIDER_META_HANDLE *h);
void publisher_cache_init(void);
bool publisher_keywords_cacheable(PROVIDER_META_HANDLE *h);
bool publisher_tasks_cacheable(PROVIDER_META_HANDLE *h);
bool is_useful_publisher_for_levels(PROVIDER_META_HANDLE *h);
bool publisher_opcodes_cacheable(PROVIDER_META_HANDLE *h);
bool publisher_get_keywords(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value);
bool publisher_get_level(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value);
bool publisher_get_task(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value);
bool publisher_get_opcode(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value);
#endif //NETDATA_WINDOWS_EVENTS_PUBLISHERS_H

File diff suppressed because it is too large Load diff

View file

@ -1,81 +1,247 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_QUERY_H
#define NETDATA_WINDOWS_EVENTS_QUERY_H
#include "windows-events.h"
typedef struct wevt_event {
uint64_t id; // EventRecordId (unique and sequential per channel)
uint16_t event_id; // This is the template that defines the message to be shown
uint16_t opcode;
uint8_t level; // The severity of event
uint8_t version;
uint16_t task;
uint32_t process_id;
uint32_t thread_id;
uint64_t keyword; // Categorization of the event
ND_UUID provider;
ND_UUID correlation_activity_id;
nsec_t created_ns;
} WEVT_EVENT;
#define WEVT_EVENT_EMPTY (WEVT_EVENT){ .id = 0, .created_ns = 0, }
typedef struct {
WEVT_EVENT first_event;
WEVT_EVENT last_event;
uint64_t entries;
nsec_t duration_ns;
uint64_t size_bytes;
} EVT_RETENTION;
typedef struct wevt_log {
EVT_HANDLE event_query;
EVT_HANDLE render_context;
struct {
// temp buffer used for rendering event log messages
// never use directly
struct {
EVT_VARIANT *data;
size_t size;
size_t len;
} content;
// temp buffer used for fetching and converting UNICODE and UTF-8
// every string operation overwrites it, multiple times per event log entry
// it can be used within any function, for its own purposes,
// but never share between functions
TXT_UNICODE unicode;
// string attributes of the current event log entry
// valid until another event if fetched
TXT_UTF8 channel;
TXT_UTF8 provider;
TXT_UTF8 source;
TXT_UTF8 computer;
TXT_UTF8 event;
TXT_UTF8 user;
TXT_UTF8 opcode;
TXT_UTF8 level;
TXT_UTF8 keyword;
TXT_UTF8 xml;
BUFFER *message;
} ops;
} WEVT_LOG;
WEVT_LOG *wevt_openlog6(void);
void wevt_closelog6(WEVT_LOG *log);
bool wevt_channel_retention(WEVT_LOG *log, const wchar_t *channel, EVT_RETENTION *retention);
EVT_HANDLE wevt_query(LPCWSTR channel, usec_t seek_to, bool backward);
void wevt_query_done(WEVT_LOG *log);
bool wevt_get_next_event(WEVT_LOG *log, WEVT_EVENT *ev, bool full);
#endif //NETDATA_WINDOWS_EVENTS_QUERY_H
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_QUERY_H
#define NETDATA_WINDOWS_EVENTS_QUERY_H
#include "windows-events.h"
#define BATCH_NEXT_EVENT 500
typedef struct wevt_event {
uint64_t id; // EventRecordId (unique and sequential per channel)
uint8_t version;
uint8_t level; // The severity of event
uint8_t opcode; // we receive this as 8bit, but publishers use 32bit
uint16_t event_id; // This is the template that defines the message to be shown
uint16_t task;
uint32_t process_id;
uint32_t thread_id;
uint64_t keywords; // Categorization of the event
ND_UUID provider;
ND_UUID correlation_activity_id;
nsec_t created_ns;
} WEVT_EVENT;
#define WEVT_EVENT_EMPTY (WEVT_EVENT){ .id = 0, .created_ns = 0, }
typedef struct {
EVT_VARIANT *data;
size_t size;
size_t used;
} WEVT_VARIANT;
typedef struct {
WEVT_EVENT first_event;
WEVT_EVENT last_event;
uint64_t entries;
nsec_t duration_ns;
uint64_t size_bytes;
} EVT_RETENTION;
struct provider_meta_handle;
typedef struct wevt_log {
struct {
DWORD size;
DWORD used;
EVT_HANDLE bk[BATCH_NEXT_EVENT];
} batch;
EVT_HANDLE bookmark;
EVT_HANDLE event_query;
EVT_HANDLE render_context;
struct provider_meta_handle *publisher;
struct {
// temp buffer used for rendering event log messages
// never use directly
WEVT_VARIANT content;
// temp buffer used for fetching and converting UNICODE and UTF-8
// every string operation overwrites it, multiple times per event log entry
// it can be used within any function, for its own purposes,
// but never share between functions
TXT_UNICODE unicode;
// string attributes of the current event log entry
// valid until another event if fetched
// IMPORTANT:
// EVERY FIELD NEEDS ITS OWN BUFFER!
// the way facets work, all the field value pointers need to be valid
// until the entire row closes, so reusing a buffer for the same field
// actually copies the same value to all fields using the same buffer.
TXT_UTF8 channel;
TXT_UTF8 provider;
TXT_UTF8 source;
TXT_UTF8 computer;
TXT_UTF8 user;
TXT_UTF8 event;
TXT_UTF8 level;
TXT_UTF8 keywords;
TXT_UTF8 opcode;
TXT_UTF8 task;
TXT_UTF8 xml;
} ops;
struct {
size_t event_count;
size_t failed_count;
} query_stats;
struct {
size_t queries_count;
size_t queries_failed;
size_t event_count;
size_t failed_count;
} log_stats;
} WEVT_LOG;
WEVT_LOG *wevt_openlog6(void);
void wevt_closelog6(WEVT_LOG *log);
bool wevt_channel_retention(WEVT_LOG *log, const wchar_t *channel, EVT_RETENTION *retention);
bool wevt_query(WEVT_LOG *log, LPCWSTR channel, LPCWSTR query, EVT_QUERY_FLAGS direction);
void wevt_query_done(WEVT_LOG *log);
bool wevt_get_next_event(WEVT_LOG *log, WEVT_EVENT *ev, bool full);
bool wevt_get_message_unicode(TXT_UNICODE *unicode, EVT_HANDLE hMetadata, EVT_HANDLE bookmark, DWORD dwMessageId, EVT_FORMAT_MESSAGE_FLAGS flags);
struct provider_meta_handle;
bool wevt_get_event_utf8(WEVT_LOG *log, struct provider_meta_handle *p, EVT_HANDLE event_handle, TXT_UTF8 *dst);
bool wevt_get_xml_utf8(WEVT_LOG *log, struct provider_meta_handle *p, EVT_HANDLE event_handle, TXT_UTF8 *dst);
static inline void wevt_variant_cleanup(WEVT_VARIANT *v) {
freez(v->data);
}
static inline void wevt_variant_resize(WEVT_VARIANT *v, size_t required_size) {
if(required_size < v->size)
return;
wevt_variant_cleanup(v);
v->size = compute_new_size(v->size, required_size);
v->data = mallocz(v->size);
}
static inline uint8_t wevt_field_get_uint8(EVT_VARIANT *ev) {
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeByte);
return ev->ByteVal;
}
static inline uint16_t wevt_field_get_uint16(EVT_VARIANT *ev) {
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeUInt16);
return ev->UInt16Val;
}
static inline uint32_t wevt_field_get_uint32(EVT_VARIANT *ev) {
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeUInt32);
return ev->UInt32Val;
}
static inline uint64_t wevt_field_get_uint64(EVT_VARIANT *ev) {
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeUInt64);
return ev->UInt64Val;
}
static inline uint64_t wevt_field_get_uint64_hex(EVT_VARIANT *ev) {
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeHexInt64);
return ev->UInt64Val;
}
static inline bool wevt_field_get_string_utf8(EVT_VARIANT *ev, TXT_UTF8 *dst) {
if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull) {
wevt_utf8_empty(dst);
return false;
}
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeString);
return wevt_str_wchar_to_utf8(dst, ev->StringVal, -1);
}
bool wevt_convert_user_id_to_name(PSID sid, TXT_UTF8 *dst);
static inline bool wevt_field_get_sid(EVT_VARIANT *ev, TXT_UTF8 *dst) {
if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull) {
wevt_utf8_empty(dst);
return false;
}
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeSid);
return wevt_convert_user_id_to_name(ev->SidVal, dst);
}
static inline uint64_t wevt_field_get_filetime_to_ns(EVT_VARIANT *ev) {
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeFileTime);
return os_windows_ulonglong_to_unix_epoch_ns(ev->FileTimeVal);
}
static inline bool wevt_GUID_to_ND_UUID(ND_UUID *nd_uuid, const GUID *guid) {
if(guid && sizeof(GUID) == sizeof(ND_UUID)) {
memcpy(nd_uuid->uuid, guid, sizeof(ND_UUID));
return true;
}
else {
*nd_uuid = UUID_ZERO;
return false;
}
}
static inline bool wevt_get_uuid_by_type(EVT_VARIANT *ev, ND_UUID *dst) {
if((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeNull) {
wevt_GUID_to_ND_UUID(dst, NULL);
return false;
}
fatal_assert((ev->Type & EVT_VARIANT_TYPE_MASK) == EvtVarTypeGuid);
return wevt_GUID_to_ND_UUID(dst, ev->GuidVal);
}
// https://learn.microsoft.com/en-us/windows/win32/wes/defining-severity-levels
static inline bool is_valid_publisher_level(uint64_t level, bool strict) {
if(strict)
// when checking if the name is publisher independent
return level >= 16 && level <= 255;
else
// when checking acceptable values in publisher manifests
return level <= 255;
}
// https://learn.microsoft.com/en-us/windows/win32/wes/defining-tasks-and-opcodes
static inline bool is_valid_publisher_opcode(uint64_t opcode, bool strict) {
if(strict)
// when checking if the name is publisher independent
return opcode >= 10 && opcode <= 239;
else
// when checking acceptable values in publisher manifests
return opcode <= 255;
}
// https://learn.microsoft.com/en-us/windows/win32/wes/defining-tasks-and-opcodes
static inline bool is_valid_publisher_task(uint64_t task, bool strict) {
if(strict)
// when checking if the name is publisher independent
return task > 0 && task <= 0xFFFF;
else
// when checking acceptable values in publisher manifests
return task <= 0xFFFF;
}
// https://learn.microsoft.com/en-us/windows/win32/wes/defining-keywords-used-to-classify-types-of-events
static inline bool is_valid_publisher_keywords(uint64_t keyword, bool strict) {
if(strict)
// when checking if the name is publisher independent
return keyword > 0 && keyword <= 0x0000FFFFFFFFFFFF;
else
// when checking acceptable values in publisher manifests
return true;
}
#endif //NETDATA_WINDOWS_EVENTS_QUERY_H

View file

@ -1,120 +1,118 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-sid.h"
#include <sddl.h>
typedef struct {
size_t len;
uint8_t sid[];
} SID_KEY;
typedef struct {
const char *user;
size_t user_len;
// this needs to be last, because of its variable size
SID_KEY key;
} SID_VALUE;
#define SIMPLE_HASHTABLE_NAME _SID
#define SIMPLE_HASHTABLE_VALUE_TYPE SID_VALUE
#define SIMPLE_HASHTABLE_KEY_TYPE SID_KEY
#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION sid_value_to_key
#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION sid_cache_compar
#define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 1
#include "libnetdata/simple_hashtable.h"
static struct {
SPINLOCK spinlock;
bool initialized;
struct simple_hashtable_SID hashtable;
} sid_globals = {
.spinlock = NETDATA_SPINLOCK_INITIALIZER,
.hashtable = { 0 },
};
static inline SID_KEY *sid_value_to_key(SID_VALUE *s) {
return &s->key;
}
static inline bool sid_cache_compar(SID_KEY *a, SID_KEY *b) {
return a->len == b->len && memcmp(&a->sid, &b->sid, a->len) == 0;
}
static bool update_user(SID_VALUE *found, TXT_UTF8 *dst) {
if(found && found->user) {
txt_utf8_resize(dst, found->user_len + 1);
memcpy(dst->data, found->user, found->user_len + 1);
dst->used = found->user_len + 1;
return true;
}
txt_utf8_resize(dst, 1);
dst->data[0] = '\0';
dst->used = 1;
return false;
}
static void lookup_user(PSID *sid, TXT_UTF8 *dst) {
static __thread wchar_t account_unicode[256];
static __thread wchar_t domain_unicode[256];
DWORD account_name_size = sizeof(account_unicode) / sizeof(account_unicode[0]);
DWORD domain_name_size = sizeof(domain_unicode) / sizeof(domain_unicode[0]);
SID_NAME_USE sid_type;
txt_utf8_resize(dst, 1024);
if (LookupAccountSidW(NULL, sid, account_unicode, &account_name_size, domain_unicode, &domain_name_size, &sid_type)) {
const char *user = account2utf8(account_unicode);
const char *domain = domain2utf8(domain_unicode);
dst->used = snprintfz(dst->data, dst->size, "%s\\%s", domain, user) + 1;
}
else {
wchar_t *sid_string = NULL;
if (ConvertSidToStringSidW(sid, &sid_string)) {
const char *user = account2utf8(sid_string);
dst->used = snprintfz(dst->data, dst->size, "%s", user) + 1;
}
else
dst->used = snprintfz(dst->data, dst->size, "[invalid]") + 1;
}
}
bool wevt_convert_user_id_to_name(PSID sid, TXT_UTF8 *dst) {
if(!sid || !IsValidSid(sid))
return update_user(NULL, dst);
size_t size = GetLengthSid(sid);
size_t tmp_size = sizeof(SID_VALUE) + size;
size_t tmp_key_size = sizeof(SID_KEY) + size;
uint8_t buf[tmp_size];
SID_VALUE *tmp = (SID_VALUE *)&buf;
memcpy(&tmp->key.sid, sid, size);
tmp->key.len = size;
spinlock_lock(&sid_globals.spinlock);
if(!sid_globals.initialized) {
simple_hashtable_init_SID(&sid_globals.hashtable, 100);
sid_globals.initialized = true;
}
SID_VALUE *found = simple_hashtable_get_SID(&sid_globals.hashtable, &tmp->key, tmp_key_size);
spinlock_unlock(&sid_globals.spinlock);
if(found) return update_user(found, dst);
// allocate the SID_VALUE
found = mallocz(tmp_size);
memcpy(found, buf, tmp_size);
// lookup the user
lookup_user(sid, dst);
found->user = strdupz(dst->data);
found->user_len = dst->used - 1;
// add it to the cache
spinlock_lock(&sid_globals.spinlock);
simple_hashtable_set_SID(&sid_globals.hashtable, &found->key, tmp_key_size, found);
spinlock_unlock(&sid_globals.spinlock);
return update_user(found, dst);
}
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-sid.h"
#include <sddl.h>
typedef struct {
size_t len;
uint8_t sid[];
} SID_KEY;
typedef struct {
const char *user;
size_t user_len;
// this needs to be last, because of its variable size
SID_KEY key;
} SID_VALUE;
#define SIMPLE_HASHTABLE_NAME _SID
#define SIMPLE_HASHTABLE_VALUE_TYPE SID_VALUE
#define SIMPLE_HASHTABLE_KEY_TYPE SID_KEY
#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION sid_value_to_key
#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION sid_cache_compar
#define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 1
#include "libnetdata/simple_hashtable.h"
static struct {
SPINLOCK spinlock;
struct simple_hashtable_SID hashtable;
} sid_globals = {
.spinlock = NETDATA_SPINLOCK_INITIALIZER,
};
static inline SID_KEY *sid_value_to_key(SID_VALUE *s) {
return &s->key;
}
static inline bool sid_cache_compar(SID_KEY *a, SID_KEY *b) {
return a->len == b->len && memcmp(&a->sid, &b->sid, a->len) == 0;
}
void sid_cache_init(void) {
simple_hashtable_init_SID(&sid_globals.hashtable, 100);
}
static bool update_user(SID_VALUE *found, TXT_UTF8 *dst) {
if(found && found->user) {
txt_utf8_resize(dst, found->user_len + 1, false);
memcpy(dst->data, found->user, found->user_len + 1);
dst->used = found->user_len + 1;
return true;
}
txt_utf8_resize(dst, 1, false);
dst->data[0] = '\0';
dst->used = 1;
return false;
}
static void lookup_user(PSID *sid, TXT_UTF8 *dst) {
static __thread wchar_t account_unicode[256];
static __thread wchar_t domain_unicode[256];
DWORD account_name_size = sizeof(account_unicode) / sizeof(account_unicode[0]);
DWORD domain_name_size = sizeof(domain_unicode) / sizeof(domain_unicode[0]);
SID_NAME_USE sid_type;
txt_utf8_resize(dst, 1024, false);
if (LookupAccountSidW(NULL, sid, account_unicode, &account_name_size, domain_unicode, &domain_name_size, &sid_type)) {
const char *user = account2utf8(account_unicode);
const char *domain = domain2utf8(domain_unicode);
dst->used = snprintfz(dst->data, dst->size, "%s\\%s", domain, user) + 1;
}
else {
wchar_t *sid_string = NULL;
if (ConvertSidToStringSidW(sid, &sid_string)) {
const char *user = account2utf8(sid_string);
dst->used = snprintfz(dst->data, dst->size, "%s", user) + 1;
}
else
dst->used = snprintfz(dst->data, dst->size, "[invalid]") + 1;
}
}
bool wevt_convert_user_id_to_name(PSID sid, TXT_UTF8 *dst) {
if(!sid || !IsValidSid(sid))
return update_user(NULL, dst);
size_t size = GetLengthSid(sid);
size_t tmp_size = sizeof(SID_VALUE) + size;
size_t tmp_key_size = sizeof(SID_KEY) + size;
uint8_t buf[tmp_size];
SID_VALUE *tmp = (SID_VALUE *)&buf;
memcpy(&tmp->key.sid, sid, size);
tmp->key.len = size;
spinlock_lock(&sid_globals.spinlock);
SID_VALUE *found = simple_hashtable_get_SID(&sid_globals.hashtable, &tmp->key, tmp_key_size);
spinlock_unlock(&sid_globals.spinlock);
if(found) return update_user(found, dst);
// allocate the SID_VALUE
found = mallocz(tmp_size);
memcpy(found, buf, tmp_size);
// lookup the user
lookup_user(sid, dst);
found->user = strdupz(dst->data);
found->user_len = dst->used - 1;
// add it to the cache
spinlock_lock(&sid_globals.spinlock);
simple_hashtable_set_SID(&sid_globals.hashtable, &found->key, tmp_key_size, found);
spinlock_unlock(&sid_globals.spinlock);
return update_user(found, dst);
}

View file

@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_SID_H
#define NETDATA_WINDOWS_EVENTS_SID_H
#include "windows-events.h"
struct wevt_log;
bool wevt_convert_user_id_to_name(PSID sid, TXT_UTF8 *dst);
#endif //NETDATA_WINDOWS_EVENTS_SID_H
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_SID_H
#define NETDATA_WINDOWS_EVENTS_SID_H
#include "windows-events.h"
struct wevt_log;
bool wevt_convert_user_id_to_name(PSID sid, TXT_UTF8 *dst);
void sid_cache_init(void);
#endif //NETDATA_WINDOWS_EVENTS_SID_H

View file

@ -1,267 +1,317 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-sources.h"
DICTIONARY *wevt_sources = NULL;
DICTIONARY *used_hashes_registry = NULL;
static usec_t wevt_session = 0;
WEVT_SOURCE_TYPE wevt_internal_source_type(const char *value) {
if(strcmp(value, WEVT_SOURCE_ALL_NAME) == 0)
return WEVTS_ALL;
return WEVTS_NONE;
}
void wevt_sources_del_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
LOGS_QUERY_SOURCE *src = value;
freez((void *)src->fullname);
string_freez(src->source);
src->fullname = NULL;
src->source = NULL;
}
static bool wevt_sources_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
LOGS_QUERY_SOURCE *src_old = old_value;
LOGS_QUERY_SOURCE *src_new = new_value;
bool ret = false;
if(src_new->last_scan_monotonic_ut > src_old->last_scan_monotonic_ut) {
src_old->last_scan_monotonic_ut = src_new->last_scan_monotonic_ut;
if (src_old->source != src_new->source) {
string_freez(src_old->source);
src_old->source = src_new->source;
src_new->source = NULL;
}
src_old->source_type = src_new->source_type;
src_old->msg_first_ut = src_new->msg_first_ut;
src_old->msg_last_ut = src_new->msg_last_ut;
src_old->msg_first_id = src_new->msg_first_id;
src_old->msg_last_id = src_new->msg_last_id;
src_old->entries = src_new->entries;
src_old->size = src_new->size;
ret = true;
}
freez((void *)src_new->fullname);
string_freez(src_new->source);
src_new->fullname = NULL;
src_new->source = NULL;
return ret;
}
void wevt_sources_init(void) {
wevt_session = now_realtime_usec();
used_hashes_registry = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE);
wevt_sources = dictionary_create_advanced(DICT_OPTION_FIXED_SIZE | DICT_OPTION_DONT_OVERWRITE_VALUE,
NULL, sizeof(LOGS_QUERY_SOURCE));
dictionary_register_delete_callback(wevt_sources, wevt_sources_del_cb, NULL);
dictionary_register_conflict_callback(wevt_sources, wevt_sources_conflict_cb, NULL);
}
void buffer_json_wevt_versions(BUFFER *wb __maybe_unused) {
buffer_json_member_add_object(wb, "versions");
{
buffer_json_member_add_uint64(wb, "sources",
wevt_session + dictionary_version(wevt_sources));
}
buffer_json_object_close(wb);
}
// --------------------------------------------------------------------------------------------------------------------
int wevt_sources_dict_items_backward_compar(const void *a, const void *b) {
const DICTIONARY_ITEM **da = (const DICTIONARY_ITEM **)a, **db = (const DICTIONARY_ITEM **)b;
LOGS_QUERY_SOURCE *sa = dictionary_acquired_item_value(*da);
LOGS_QUERY_SOURCE *sb = dictionary_acquired_item_value(*db);
// compare the last message timestamps
if(sa->msg_last_ut < sb->msg_last_ut)
return 1;
if(sa->msg_last_ut > sb->msg_last_ut)
return -1;
// compare the first message timestamps
if(sa->msg_first_ut < sb->msg_first_ut)
return 1;
if(sa->msg_first_ut > sb->msg_first_ut)
return -1;
return 0;
}
int wevt_sources_dict_items_forward_compar(const void *a, const void *b) {
return -wevt_sources_dict_items_backward_compar(a, b);
}
// --------------------------------------------------------------------------------------------------------------------
struct wevt_source {
usec_t first_ut;
usec_t last_ut;
size_t count;
uint64_t size;
};
static int wevt_source_to_json_array_cb(const DICTIONARY_ITEM *item, void *entry, void *data) {
const struct wevt_source *s = entry;
BUFFER *wb = data;
const char *name = dictionary_acquired_item_name(item);
buffer_json_add_array_item_object(wb);
{
char size_for_humans[128];
size_snprintf(size_for_humans, sizeof(size_for_humans), s->size, "B", false);
char duration_for_humans[128];
duration_snprintf(duration_for_humans, sizeof(duration_for_humans),
(time_t)((s->last_ut - s->first_ut) / USEC_PER_SEC), "s", true);
char info[1024];
snprintfz(info, sizeof(info), "%zu channels, with a total size of %s, covering %s",
s->count, size_for_humans, duration_for_humans);
buffer_json_member_add_string(wb, "id", name);
buffer_json_member_add_string(wb, "name", name);
buffer_json_member_add_string(wb, "pill", size_for_humans);
buffer_json_member_add_string(wb, "info", info);
}
buffer_json_object_close(wb); // options object
return 1;
}
static bool wevt_source_merge_sizes(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value , void *data __maybe_unused) {
struct wevt_source *old_v = old_value;
const struct wevt_source *new_v = new_value;
old_v->count += new_v->count;
old_v->size += new_v->size;
if(new_v->first_ut && new_v->first_ut < old_v->first_ut)
old_v->first_ut = new_v->first_ut;
if(new_v->last_ut && new_v->last_ut > old_v->last_ut)
old_v->last_ut = new_v->last_ut;
return false;
}
void wevt_sources_to_json_array(BUFFER *wb) {
DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_NAME_LINK_DONT_CLONE|DICT_OPTION_DONT_OVERWRITE_VALUE);
dictionary_register_conflict_callback(dict, wevt_source_merge_sizes, NULL);
struct wevt_source t = { 0 };
LOGS_QUERY_SOURCE *src;
dfe_start_read(wevt_sources, src) {
t.first_ut = src->msg_first_ut;
t.last_ut = src->msg_last_ut;
t.count = 1;
t.size = src->size;
dictionary_set(dict, WEVT_SOURCE_ALL_NAME, &t, sizeof(t));
if(src->source)
dictionary_set(dict, string2str(src->source), &t, sizeof(t));
}
dfe_done(jf);
dictionary_sorted_walkthrough_read(dict, wevt_source_to_json_array_cb, wb);
}
void wevt_sources_scan(void) {
static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
LPWSTR channel = NULL;
EVT_HANDLE hChannelEnum = NULL;
if(spinlock_trylock(&spinlock)) {
const usec_t now_monotonic_ut = now_monotonic_usec();
DWORD dwChannelBufferSize = 0;
DWORD dwChannelBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
// Open a handle to enumerate the event channels
hChannelEnum = EvtOpenChannelEnum(NULL, 0);
if (!hChannelEnum) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "WINDOWS EVENTS: EvtOpenChannelEnum() failed with %" PRIu64 "\n",
(uint64_t)GetLastError());
goto cleanup;
}
WEVT_LOG *log = wevt_openlog6();
if(!log) goto cleanup;
while (true) {
if (!EvtNextChannelPath(hChannelEnum, dwChannelBufferSize, channel, &dwChannelBufferUsed)) {
status = GetLastError();
if (status == ERROR_NO_MORE_ITEMS)
break; // No more channels
else if (status == ERROR_INSUFFICIENT_BUFFER) {
dwChannelBufferSize = dwChannelBufferUsed;
freez(channel);
channel = mallocz(dwChannelBufferSize * sizeof(WCHAR));
continue;
} else {
nd_log(NDLS_COLLECTORS, NDLP_ERR,
"WINDOWS EVENTS: EvtNextChannelPath() failed\n");
break;
}
}
EVT_RETENTION retention;
if(!wevt_channel_retention(log, channel, &retention))
continue;
const char *name = channel2utf8(channel);
const char *fullname = strdupz(name);
char *slash = strchr(name, '/');
if(slash) *slash = '\0';
LOGS_QUERY_SOURCE src = {
.entries = retention.entries,
.fullname = fullname,
.fullname_len = strlen(fullname),
.last_scan_monotonic_ut = now_monotonic_usec(),
.msg_first_id = retention.first_event.id,
.msg_last_id = retention.last_event.id,
.msg_first_ut = retention.first_event.created_ns / NSEC_PER_USEC,
.msg_last_ut = retention.last_event.created_ns / NSEC_PER_USEC,
.size = retention.size_bytes,
.source_type = WEVTS_ALL,
.source = string_strdupz(name),
};
dictionary_set(wevt_sources, src.fullname, &src, sizeof(src));
}
wevt_closelog6(log);
LOGS_QUERY_SOURCE *src;
dfe_start_write(wevt_sources, src)
{
if(src->last_scan_monotonic_ut < now_monotonic_ut)
dictionary_del(wevt_sources, src->fullname);
}
dfe_done(src);
dictionary_garbage_collect(wevt_sources);
spinlock_unlock(&spinlock);
}
cleanup:
freez(channel);
EvtClose(hChannelEnum);
}
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-sources.h"
DICTIONARY *wevt_sources = NULL;
DICTIONARY *used_hashes_registry = NULL;
static usec_t wevt_session = 0;
WEVT_SOURCE_TYPE wevt_internal_source_type(const char *value) {
if(strcmp(value, WEVT_SOURCE_ALL_NAME) == 0)
return WEVTS_ALL;
if(strcmp(value, WEVT_SOURCE_ALL_ADMIN_NAME) == 0)
return WEVTS_ADMIN;
if(strcmp(value, WEVT_SOURCE_ALL_OPERATIONAL_NAME) == 0)
return WEVTS_OPERATIONAL;
if(strcmp(value, WEVT_SOURCE_ALL_ANALYTIC_NAME) == 0)
return WEVTS_ANALYTIC;
if(strcmp(value, WEVT_SOURCE_ALL_DEBUG_NAME) == 0)
return WEVTS_DEBUG;
if(strcmp(value, WEVT_SOURCE_ALL_WINDOWS_NAME) == 0)
return WEVTS_WINDOWS;
return WEVTS_NONE;
}
void wevt_sources_del_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
LOGS_QUERY_SOURCE *src = value;
freez((void *)src->fullname);
string_freez(src->source);
src->fullname = NULL;
src->source = NULL;
}
static bool wevt_sources_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
LOGS_QUERY_SOURCE *src_old = old_value;
LOGS_QUERY_SOURCE *src_new = new_value;
bool ret = false;
if(src_new->last_scan_monotonic_ut > src_old->last_scan_monotonic_ut) {
src_old->last_scan_monotonic_ut = src_new->last_scan_monotonic_ut;
if (src_old->source != src_new->source) {
string_freez(src_old->source);
src_old->source = src_new->source;
src_new->source = NULL;
}
src_old->source_type = src_new->source_type;
src_old->msg_first_ut = src_new->msg_first_ut;
src_old->msg_last_ut = src_new->msg_last_ut;
src_old->msg_first_id = src_new->msg_first_id;
src_old->msg_last_id = src_new->msg_last_id;
src_old->entries = src_new->entries;
src_old->size = src_new->size;
ret = true;
}
freez((void *)src_new->fullname);
string_freez(src_new->source);
src_new->fullname = NULL;
src_new->source = NULL;
return ret;
}
void wevt_sources_init(void) {
wevt_session = now_realtime_usec();
used_hashes_registry = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE);
wevt_sources = dictionary_create_advanced(DICT_OPTION_FIXED_SIZE | DICT_OPTION_DONT_OVERWRITE_VALUE,
NULL, sizeof(LOGS_QUERY_SOURCE));
dictionary_register_delete_callback(wevt_sources, wevt_sources_del_cb, NULL);
dictionary_register_conflict_callback(wevt_sources, wevt_sources_conflict_cb, NULL);
}
void buffer_json_wevt_versions(BUFFER *wb __maybe_unused) {
buffer_json_member_add_object(wb, "versions");
{
buffer_json_member_add_uint64(wb, "sources",
wevt_session + dictionary_version(wevt_sources));
}
buffer_json_object_close(wb);
}
// --------------------------------------------------------------------------------------------------------------------
int wevt_sources_dict_items_backward_compar(const void *a, const void *b) {
const DICTIONARY_ITEM **da = (const DICTIONARY_ITEM **)a, **db = (const DICTIONARY_ITEM **)b;
LOGS_QUERY_SOURCE *sa = dictionary_acquired_item_value(*da);
LOGS_QUERY_SOURCE *sb = dictionary_acquired_item_value(*db);
// compare the last message timestamps
if(sa->msg_last_ut < sb->msg_last_ut)
return 1;
if(sa->msg_last_ut > sb->msg_last_ut)
return -1;
// compare the first message timestamps
if(sa->msg_first_ut < sb->msg_first_ut)
return 1;
if(sa->msg_first_ut > sb->msg_first_ut)
return -1;
return 0;
}
int wevt_sources_dict_items_forward_compar(const void *a, const void *b) {
return -wevt_sources_dict_items_backward_compar(a, b);
}
// --------------------------------------------------------------------------------------------------------------------
struct wevt_source {
usec_t first_ut;
usec_t last_ut;
size_t count;
uint64_t size;
};
static int wevt_source_to_json_array_cb(const DICTIONARY_ITEM *item, void *entry, void *data) {
const struct wevt_source *s = entry;
BUFFER *wb = data;
const char *name = dictionary_acquired_item_name(item);
buffer_json_add_array_item_object(wb);
{
char size_for_humans[128];
size_snprintf(size_for_humans, sizeof(size_for_humans), s->size, "B", false);
char duration_for_humans[128];
duration_snprintf(duration_for_humans, sizeof(duration_for_humans),
(time_t)((s->last_ut - s->first_ut) / USEC_PER_SEC), "s", true);
char info[1024];
snprintfz(info, sizeof(info), "%zu channel%s, with a total size of %s, covering %s",
s->count, s->count > 1 ? "s":"", size_for_humans, duration_for_humans);
buffer_json_member_add_string(wb, "id", name);
buffer_json_member_add_string(wb, "name", name);
buffer_json_member_add_string(wb, "pill", size_for_humans);
buffer_json_member_add_string(wb, "info", info);
}
buffer_json_object_close(wb); // options object
return 1;
}
static bool wevt_source_merge_sizes(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value , void *data __maybe_unused) {
struct wevt_source *old_v = old_value;
const struct wevt_source *new_v = new_value;
old_v->count += new_v->count;
old_v->size += new_v->size;
if(new_v->first_ut && new_v->first_ut < old_v->first_ut)
old_v->first_ut = new_v->first_ut;
if(new_v->last_ut && new_v->last_ut > old_v->last_ut)
old_v->last_ut = new_v->last_ut;
return false;
}
void wevt_sources_to_json_array(BUFFER *wb) {
DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_NAME_LINK_DONT_CLONE|DICT_OPTION_DONT_OVERWRITE_VALUE);
dictionary_register_conflict_callback(dict, wevt_source_merge_sizes, NULL);
struct wevt_source t = { 0 };
LOGS_QUERY_SOURCE *src;
dfe_start_read(wevt_sources, src) {
t.first_ut = src->msg_first_ut;
t.last_ut = src->msg_last_ut;
t.count = 1;
t.size = src->size;
dictionary_set(dict, WEVT_SOURCE_ALL_NAME, &t, sizeof(t));
if(src->source_type & WEVTS_ADMIN)
dictionary_set(dict, WEVT_SOURCE_ALL_ADMIN_NAME, &t, sizeof(t));
if(src->source_type & WEVTS_OPERATIONAL)
dictionary_set(dict, WEVT_SOURCE_ALL_OPERATIONAL_NAME, &t, sizeof(t));
if(src->source_type & WEVTS_ANALYTIC)
dictionary_set(dict, WEVT_SOURCE_ALL_ANALYTIC_NAME, &t, sizeof(t));
if(src->source_type & WEVTS_DEBUG)
dictionary_set(dict, WEVT_SOURCE_ALL_DEBUG_NAME, &t, sizeof(t));
if(src->source_type & WEVTS_WINDOWS)
dictionary_set(dict, WEVT_SOURCE_ALL_WINDOWS_NAME, &t, sizeof(t));
if(src->source)
dictionary_set(dict, string2str(src->source), &t, sizeof(t));
}
dfe_done(jf);
dictionary_sorted_walkthrough_read(dict, wevt_source_to_json_array_cb, wb);
}
void wevt_sources_scan(void) {
static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
LPWSTR channel = NULL;
EVT_HANDLE hChannelEnum = NULL;
if(spinlock_trylock(&spinlock)) {
const usec_t now_monotonic_ut = now_monotonic_usec();
DWORD dwChannelBufferSize = 0;
DWORD dwChannelBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
// Open a handle to enumerate the event channels
hChannelEnum = EvtOpenChannelEnum(NULL, 0);
if (!hChannelEnum) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "WINDOWS EVENTS: EvtOpenChannelEnum() failed with %" PRIu64 "\n",
(uint64_t)GetLastError());
goto cleanup;
}
WEVT_LOG *log = wevt_openlog6();
if(!log) goto cleanup;
while (true) {
if (!EvtNextChannelPath(hChannelEnum, dwChannelBufferSize, channel, &dwChannelBufferUsed)) {
status = GetLastError();
if (status == ERROR_NO_MORE_ITEMS)
break; // No more channels
else if (status == ERROR_INSUFFICIENT_BUFFER) {
dwChannelBufferSize = dwChannelBufferUsed;
freez(channel);
channel = mallocz(dwChannelBufferSize * sizeof(WCHAR));
continue;
} else {
nd_log(NDLS_COLLECTORS, NDLP_ERR,
"WINDOWS EVENTS: EvtNextChannelPath() failed\n");
break;
}
}
EVT_RETENTION retention;
if(!wevt_channel_retention(log, channel, &retention))
continue;
const char *name = channel2utf8(channel);
const char *fullname = strdupz(name);
char *slash = strchr(name, '/');
WEVT_SOURCE_TYPE sources = WEVTS_ALL;
if(slash) {
*slash++ = '\0';
if(strcasecmp(slash, "Admin") == 0)
sources |= WEVTS_ADMIN;
if(strcasecmp(slash, "Operational") == 0)
sources |= WEVTS_OPERATIONAL;
if(strcasecmp(slash, "Analytic") == 0)
sources |= WEVTS_ANALYTIC;
if(strcasecmp(slash, "Debug") == 0)
sources |= WEVTS_DEBUG;
}
if(strcasecmp(name, "Application") == 0)
sources |= WEVTS_WINDOWS;
if(strcasecmp(name, "Security") == 0)
sources |= WEVTS_WINDOWS;
if(strcasecmp(name, "Setup") == 0)
sources |= WEVTS_WINDOWS;
if(strcasecmp(name, "System") == 0)
sources |= WEVTS_WINDOWS;
LOGS_QUERY_SOURCE src = {
.entries = retention.entries,
.fullname = fullname,
.fullname_len = strlen(fullname),
.last_scan_monotonic_ut = now_monotonic_usec(),
.msg_first_id = retention.first_event.id,
.msg_last_id = retention.last_event.id,
.msg_first_ut = retention.first_event.created_ns / NSEC_PER_USEC,
.msg_last_ut = retention.last_event.created_ns / NSEC_PER_USEC,
.size = retention.size_bytes,
.source_type = sources,
.source = string_strdupz(name),
};
dictionary_set(wevt_sources, src.fullname, &src, sizeof(src));
}
wevt_closelog6(log);
LOGS_QUERY_SOURCE *src;
dfe_start_write(wevt_sources, src)
{
if(src->last_scan_monotonic_ut < now_monotonic_ut)
dictionary_del(wevt_sources, src->fullname);
}
dfe_done(src);
dictionary_garbage_collect(wevt_sources);
spinlock_unlock(&spinlock);
}
cleanup:
freez(channel);
EvtClose(hChannelEnum);
}

View file

@ -1,45 +1,55 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_SOURCES_H
#define NETDATA_WINDOWS_EVENTS_SOURCES_H
#include "windows-events.h"
typedef enum {
WEVTS_NONE = 0,
WEVTS_ALL = (1 << 0),
} WEVT_SOURCE_TYPE;
typedef struct {
const char *fullname;
size_t fullname_len;
STRING *source;
WEVT_SOURCE_TYPE source_type;
usec_t msg_first_ut;
usec_t msg_last_ut;
size_t size;
usec_t last_scan_monotonic_ut;
uint64_t msg_first_id;
uint64_t msg_last_id;
uint64_t entries;
} LOGS_QUERY_SOURCE;
extern DICTIONARY *wevt_sources;
extern DICTIONARY *used_hashes_registry;
#define WEVT_SOURCE_ALL_NAME "All"
void wevt_sources_init(void);
void wevt_sources_scan(void);
void buffer_json_wevt_versions(BUFFER *wb);
void wevt_sources_to_json_array(BUFFER *wb);
WEVT_SOURCE_TYPE wevt_internal_source_type(const char *value);
int wevt_sources_dict_items_backward_compar(const void *a, const void *b);
int wevt_sources_dict_items_forward_compar(const void *a, const void *b);
#endif //NETDATA_WINDOWS_EVENTS_SOURCES_H
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_SOURCES_H
#define NETDATA_WINDOWS_EVENTS_SOURCES_H
#include "windows-events.h"
typedef enum {
WEVTS_NONE = 0,
WEVTS_ALL = (1 << 0),
WEVTS_ADMIN = (1 << 1),
WEVTS_OPERATIONAL = (1 << 2),
WEVTS_ANALYTIC = (1 << 3),
WEVTS_DEBUG = (1 << 4),
WEVTS_WINDOWS = (1 << 5),
} WEVT_SOURCE_TYPE;
typedef struct {
const char *fullname;
size_t fullname_len;
STRING *source;
WEVT_SOURCE_TYPE source_type;
usec_t msg_first_ut;
usec_t msg_last_ut;
size_t size;
usec_t last_scan_monotonic_ut;
uint64_t msg_first_id;
uint64_t msg_last_id;
uint64_t entries;
} LOGS_QUERY_SOURCE;
extern DICTIONARY *wevt_sources;
extern DICTIONARY *used_hashes_registry;
#define WEVT_SOURCE_ALL_NAME "All"
#define WEVT_SOURCE_ALL_ADMIN_NAME "All-Admin"
#define WEVT_SOURCE_ALL_OPERATIONAL_NAME "All-Operational"
#define WEVT_SOURCE_ALL_ANALYTIC_NAME "All-Analytic"
#define WEVT_SOURCE_ALL_DEBUG_NAME "All-Debug"
#define WEVT_SOURCE_ALL_WINDOWS_NAME "All-Windows"
void wevt_sources_init(void);
void wevt_sources_scan(void);
void buffer_json_wevt_versions(BUFFER *wb);
void wevt_sources_to_json_array(BUFFER *wb);
WEVT_SOURCE_TYPE wevt_internal_source_type(const char *value);
int wevt_sources_dict_items_backward_compar(const void *a, const void *b);
int wevt_sources_dict_items_forward_compar(const void *a, const void *b);
#endif //NETDATA_WINDOWS_EVENTS_SOURCES_H

View file

@ -1,121 +1,139 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-unicode.h"
inline void utf82unicode(wchar_t *dst, size_t dst_size, const char *src) {
if (src) {
// Convert from UTF-8 to wide char (UTF-16)
if (MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, (int)dst_size) == 0)
wcsncpy(dst, L"[failed conv.]", dst_size - 1);
}
else
wcsncpy(dst, L"[null]", dst_size - 1);
}
inline void unicode2utf8(char *dst, size_t dst_size, const wchar_t *src) {
if (src) {
if(WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, (int)dst_size, NULL, NULL) == 0)
strncpyz(dst, "[failed conv.]", dst_size - 1);
}
else
strncpyz(dst, "[null]", dst_size - 1);
}
wchar_t *channel2unicode(const char *utf8str) {
static __thread wchar_t buffer[1024];
utf82unicode(buffer, sizeof(buffer) / sizeof(buffer[0]), utf8str);
return buffer;
}
char *channel2utf8(const wchar_t *channel) {
static __thread char buffer[1024];
unicode2utf8(buffer, sizeof(buffer), channel);
return buffer;
}
char *account2utf8(const wchar_t *user) {
static __thread char buffer[1024];
unicode2utf8(buffer, sizeof(buffer), user);
return buffer;
}
char *domain2utf8(const wchar_t *domain) {
static __thread char buffer[1024];
unicode2utf8(buffer, sizeof(buffer), domain);
return buffer;
}
char *query2utf8(const wchar_t *query) {
static __thread char buffer[16384];
unicode2utf8(buffer, sizeof(buffer), query);
return buffer;
}
bool wevt_str_wchar_to_utf8(TXT_UTF8 *utf8, const wchar_t *src, int src_len_with_null) {
if(!src || !src_len_with_null)
goto cleanup;
// make sure the input is null terminated at the exact point we need it
// (otherwise, the output will not be null terminated either)
fatal_assert(src_len_with_null == -1 || (src_len_with_null >= 1 && src[src_len_with_null - 1] == 0));
// Try to convert using the existing buffer (if it exists, otherwise get the required buffer size)
int size = WideCharToMultiByte(CP_UTF8, 0, src, src_len_with_null, utf8->data, (int)utf8->size, NULL, NULL);
if(size <= 0 || !utf8->data) {
// we have to set a buffer, or increase it
if(utf8->data) {
// we need to increase it the buffer size
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "WideCharToMultiByte() failed.");
goto cleanup;
}
// we have to find the required buffer size
size = WideCharToMultiByte(CP_UTF8, 0, src, src_len_with_null, NULL, 0, NULL, NULL);
if(size <= 0)
goto cleanup;
}
// Retry conversion with the new buffer
txt_utf8_resize(utf8, size);
size = WideCharToMultiByte(CP_UTF8, 0, src, src_len_with_null, utf8->data, (int)utf8->size, NULL, NULL);
if (size <= 0) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "WideCharToMultiByte() failed after resizing.");
goto cleanup;
}
}
// Make sure it is not zero padded at the end
while(size >= 2 && utf8->data[size - 2] == 0)
size--;
utf8->used = (size_t)size;
internal_fatal(strlen(utf8->data) + 1 != utf8->used,
"Wrong UTF8 string length");
return true;
cleanup:
txt_utf8_resize(utf8, 128);
if(src)
utf8->used = snprintfz(utf8->data, utf8->size, "[failed conv.]") + 1;
else {
utf8->data[0] = '\0';
utf8->used = 1;
}
return false;
}
bool wevt_str_unicode_to_utf8(TXT_UTF8 *utf8, TXT_UNICODE *unicode) {
fatal_assert(utf8 && ((utf8->data && utf8->size) || (!utf8->data && !utf8->size)));
fatal_assert(unicode && ((unicode->data && unicode->size) || (!unicode->data && !unicode->size)));
// pass the entire unicode size, including the null terminator
// so that the resulting utf8 message will be null terminated too.
return wevt_str_wchar_to_utf8(utf8, unicode->data, (int)unicode->used);
}
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windows-events-unicode.h"
inline void utf82unicode(wchar_t *dst, size_t dst_size, const char *src) {
if (src) {
// Convert from UTF-8 to wide char (UTF-16)
if (MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, (int)dst_size) == 0)
wcsncpy(dst, L"[failed conv.]", dst_size - 1);
}
else
wcsncpy(dst, L"[null]", dst_size - 1);
}
inline void unicode2utf8(char *dst, size_t dst_size, const wchar_t *src) {
if (src) {
if(WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, (int)dst_size, NULL, NULL) == 0)
strncpyz(dst, "[failed conv.]", dst_size - 1);
}
else
strncpyz(dst, "[null]", dst_size - 1);
}
char *unicode2utf8_strdupz(const wchar_t *src, size_t *utf8_len) {
int size = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
if (size > 0) {
char *dst = mallocz(size);
WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, size, NULL, NULL);
if(utf8_len)
*utf8_len = size - 1;
return dst;
}
if(utf8_len)
*utf8_len = 0;
return NULL;
}
wchar_t *channel2unicode(const char *utf8str) {
static __thread wchar_t buffer[1024];
utf82unicode(buffer, sizeof(buffer) / sizeof(buffer[0]), utf8str);
return buffer;
}
char *channel2utf8(const wchar_t *channel) {
static __thread char buffer[1024];
unicode2utf8(buffer, sizeof(buffer), channel);
return buffer;
}
char *account2utf8(const wchar_t *user) {
static __thread char buffer[1024];
unicode2utf8(buffer, sizeof(buffer), user);
return buffer;
}
char *domain2utf8(const wchar_t *domain) {
static __thread char buffer[1024];
unicode2utf8(buffer, sizeof(buffer), domain);
return buffer;
}
char *query2utf8(const wchar_t *query) {
static __thread char buffer[16384];
unicode2utf8(buffer, sizeof(buffer), query);
return buffer;
}
bool wevt_str_wchar_to_utf8(TXT_UTF8 *utf8, const wchar_t *src, int src_len_with_null) {
if(!src || !src_len_with_null)
goto cleanup;
// make sure the input is null terminated at the exact point we need it
// (otherwise, the output will not be null terminated either)
fatal_assert(src_len_with_null == -1 || (src_len_with_null >= 1 && src[src_len_with_null - 1] == 0));
// Try to convert using the existing buffer (if it exists, otherwise get the required buffer size)
int size = WideCharToMultiByte(CP_UTF8, 0, src, src_len_with_null, utf8->data, (int)utf8->size, NULL, NULL);
if(size <= 0 || !utf8->data) {
// we have to set a buffer, or increase it
if(utf8->data) {
// we need to increase it the buffer size
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "WideCharToMultiByte() failed.");
goto cleanup;
}
// we have to find the required buffer size
size = WideCharToMultiByte(CP_UTF8, 0, src, src_len_with_null, NULL, 0, NULL, NULL);
if(size <= 0)
goto cleanup;
}
// Retry conversion with the new buffer
txt_utf8_resize(utf8, size, false);
size = WideCharToMultiByte(CP_UTF8, 0, src, src_len_with_null, utf8->data, (int)utf8->size, NULL, NULL);
if (size <= 0) {
nd_log(NDLS_COLLECTORS, NDLP_ERR, "WideCharToMultiByte() failed after resizing.");
goto cleanup;
}
}
// Make sure it is not zero padded at the end
while(size >= 2 && utf8->data[size - 2] == 0)
size--;
utf8->used = (size_t)size;
internal_fatal(strlen(utf8->data) + 1 != utf8->used,
"Wrong UTF8 string length");
return true;
cleanup:
txt_utf8_resize(utf8, 128, false);
if(src)
utf8->used = snprintfz(utf8->data, utf8->size, "[failed conv.]") + 1;
else {
utf8->data[0] = '\0';
utf8->used = 1;
}
return false;
}
bool wevt_str_unicode_to_utf8(TXT_UTF8 *utf8, TXT_UNICODE *unicode) {
fatal_assert(utf8 && ((utf8->data && utf8->size) || (!utf8->data && !utf8->size)));
fatal_assert(unicode && ((unicode->data && unicode->size) || (!unicode->data && !unicode->size)));
// pass the entire unicode size, including the null terminator
// so that the resulting utf8 message will be null terminated too.
return wevt_str_wchar_to_utf8(utf8, unicode->data, (int)unicode->used);
}

View file

@ -1,72 +1,99 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_UNICODE_H
#define NETDATA_WINDOWS_EVENTS_UNICODE_H
#include "libnetdata/libnetdata.h"
#include <windows.h>
#include <wchar.h>
typedef struct {
char *data;
size_t size; // the allocated size of data buffer
size_t used; // the used size of the data buffer (including null terminators, if any)
} TXT_UTF8;
typedef struct {
wchar_t *data;
size_t size; // the allocated size of data buffer
size_t used; // the used size of the data buffer (including null terminators, if any)
} TXT_UNICODE;
static inline size_t compute_new_size(size_t old_size, size_t required_size) {
size_t size = (required_size % 2048 == 0) ? required_size : required_size + 2048;
size = (size / 2048) * 2048;
if(size < old_size * 2)
size = old_size * 2;
return size;
}
static inline void txt_utf8_cleanup(TXT_UTF8 *utf8) {
freez(utf8->data);
}
static inline void txt_utf8_resize(TXT_UTF8 *utf8, size_t required_size) {
if(required_size < utf8->size)
return;
txt_utf8_cleanup(utf8);
utf8->size = compute_new_size(utf8->size, required_size);
utf8->data = mallocz(utf8->size);
}
static inline void txt_unicode_cleanup(TXT_UNICODE *unicode) {
freez(unicode->data);
}
static inline void txt_unicode_resize(TXT_UNICODE *unicode, size_t required_size) {
if(required_size < unicode->size)
return;
txt_unicode_cleanup(unicode);
unicode->size = compute_new_size(unicode->size, required_size);
unicode->data = mallocz(unicode->size * sizeof(wchar_t));
}
bool wevt_str_unicode_to_utf8(TXT_UTF8 *utf8, TXT_UNICODE *unicode);
bool wevt_str_wchar_to_utf8(TXT_UTF8 *utf8, const wchar_t *src, int src_len_with_null);
void unicode2utf8(char *dst, size_t dst_size, const wchar_t *src);
void utf82unicode(wchar_t *dst, size_t dst_size, const char *src);
char *account2utf8(const wchar_t *user);
char *domain2utf8(const wchar_t *domain);
char *channel2utf8(const wchar_t *channel);
wchar_t *channel2unicode(const char *utf8str);
char *query2utf8(const wchar_t *query);
#endif //NETDATA_WINDOWS_EVENTS_UNICODE_H
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_UNICODE_H
#define NETDATA_WINDOWS_EVENTS_UNICODE_H
#include "libnetdata/libnetdata.h"
#include <windows.h>
#include <wchar.h>
typedef enum __attribute__((packed)) {
TXT_SOURCE_UNKNOWN = 0,
TXT_SOURCE_PUBLISHER,
TXT_SOURCE_FIELD_CACHE,
TXT_SOURCE_EVENT_LOG,
TXT_SOURCE_HARDCODED,
// terminator
TXT_SOURCE_MAX,
} TXT_SOURCE;
typedef struct {
char *data;
size_t size; // the allocated size of data buffer
size_t used; // the used size of the data buffer (including null terminators, if any)
TXT_SOURCE src;
} TXT_UTF8;
typedef struct {
wchar_t *data;
size_t size; // the allocated size of data buffer
size_t used; // the used size of the data buffer (including null terminators, if any)
} TXT_UNICODE;
static inline size_t compute_new_size(size_t old_size, size_t required_size) {
size_t size = (required_size % 2048 == 0) ? required_size : required_size + 2048;
size = (size / 2048) * 2048;
if(size < old_size * 2)
size = old_size * 2;
return size;
}
static inline void txt_utf8_cleanup(TXT_UTF8 *utf8) {
freez(utf8->data);
}
static inline void txt_utf8_resize(TXT_UTF8 *utf8, size_t required_size, bool keep) {
if(required_size < utf8->size)
return;
if(keep) {
size_t new_size = compute_new_size(utf8->size, required_size);
utf8->data = reallocz(utf8->data, new_size);
utf8->size = new_size;
}
else {
txt_utf8_cleanup(utf8);
utf8->size = compute_new_size(utf8->size, required_size);
utf8->data = mallocz(utf8->size);
}
}
static inline void txt_unicode_cleanup(TXT_UNICODE *unicode) {
freez(unicode->data);
}
static inline void txt_unicode_resize(TXT_UNICODE *unicode, size_t required_size) {
if(required_size < unicode->size)
return;
txt_unicode_cleanup(unicode);
unicode->size = compute_new_size(unicode->size, required_size);
unicode->data = mallocz(unicode->size * sizeof(wchar_t));
}
bool wevt_str_unicode_to_utf8(TXT_UTF8 *utf8, TXT_UNICODE *unicode);
bool wevt_str_wchar_to_utf8(TXT_UTF8 *utf8, const wchar_t *src, int src_len_with_null);
void unicode2utf8(char *dst, size_t dst_size, const wchar_t *src);
void utf82unicode(wchar_t *dst, size_t dst_size, const char *src);
char *account2utf8(const wchar_t *user);
char *domain2utf8(const wchar_t *domain);
char *channel2utf8(const wchar_t *channel);
wchar_t *channel2unicode(const char *utf8str);
char *query2utf8(const wchar_t *query);
char *unicode2utf8_strdupz(const wchar_t *src, size_t *utf8_len);
static inline void wevt_utf8_empty(TXT_UTF8 *dst) {
txt_utf8_resize(dst, 1, false);
dst->data[0] = '\0';
dst->used = 1;
}
#endif //NETDATA_WINDOWS_EVENTS_UNICODE_H

View file

@ -32,15 +32,20 @@ const char *parse_value_and_closing_tag(BUFFER *buffer, const char *xml, const c
const char *start = xml;
bool has_subnodes = false;
// const char *tag_start = NULL, *tag_end = NULL;
while (xml < end) {
if(*xml == '<') {
if(xml + 1 < end && *(xml + 1) == '/') {
// a closing tag
xml += 2;
// tag_start = xml;
while(xml < end && *xml != '>')
xml++;
// tag_end = xml;
if(xml < end && *xml == '>')
xml++;
@ -56,7 +61,7 @@ const char *parse_value_and_closing_tag(BUFFER *buffer, const char *xml, const c
// an opening tag
buffer_fast_strcat(buffer, start, xml - start);
xml = start = parse_node(buffer, xml, end, level + 1);
while(xml < end && isspace(*xml))
while(xml < end && isspace((uint8_t)*xml))
xml++;
has_subnodes = true;
}
@ -101,7 +106,7 @@ const char *parse_field_value(BUFFER *buffer, const char *xml, const char *end)
// Parse a field name and return the next position to parse
const char *parse_field(BUFFER *buffer, const char *xml, const char *end) {
while(isspace(*xml) && xml < end) xml++;
while(isspace((uint8_t)*xml) && xml < end) xml++;
const char *start = xml;
@ -136,16 +141,18 @@ static inline const char *parse_node(BUFFER *buffer, const char *xml, const char
buffer_add_xml_indent(buffer, level);
// skip spaces before the tag name
while(xml < end && isspace(*xml)) xml++;
while(xml < end && isspace((uint8_t)*xml)) xml++;
// Parse the tag name
// const char *tag_start = xml, *tag_end = NULL;
while (xml < end && *xml != '>' && *xml != '/') {
xml++;
if(xml < end && isspace(*xml)) {
if(xml < end && isspace((uint8_t)*xml)) {
xml++;
// tag_end = xml;
while(xml < end && isspace(*xml))
while(xml < end && isspace((uint8_t)*xml))
xml++;
if(xml < end && *xml == '/') {
@ -168,7 +175,7 @@ static inline const char *parse_node(BUFFER *buffer, const char *xml, const char
else {
buffer_fast_strcat(buffer, start, xml - start);
xml = start = parse_field(buffer, xml, end);
while(xml < end && isspace(*xml))
while(xml < end && isspace((uint8_t)*xml))
xml++;
}
}
@ -193,12 +200,9 @@ static inline const char *parse_node(BUFFER *buffer, const char *xml, const char
return append_the_rest(buffer, start, end);
}
// Main pretty-print XML function
void buffer_pretty_print_xml(BUFFER *buffer, const char *xml, size_t xml_len) {
const char *end = xml + xml_len;
static inline void buffer_pretty_print_xml_object(BUFFER *buffer, const char *xml, const char *end) {
while(xml < end) {
while(xml < end && isspace(*xml))
while(xml < end && isspace((uint8_t)*xml))
xml++;
if(xml < end && *xml == '<')
@ -209,3 +213,132 @@ void buffer_pretty_print_xml(BUFFER *buffer, const char *xml, size_t xml_len) {
}
}
}
void buffer_pretty_print_xml(BUFFER *buffer, const char *xml, size_t xml_len) {
const char *end = xml + xml_len;
buffer_pretty_print_xml_object(buffer, xml, end);
}
// --------------------------------------------------------------------------------------------------------------------
bool buffer_extract_and_print_xml_with_cb(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[],
void (*cb)(BUFFER *, const char *, const char *, const char *)) {
if(!keys || !*keys[0]) {
buffer_pretty_print_xml(buffer, xml, xml_len);
return true;
}
const char *start = xml, *end = NULL;
for(size_t k = 0; keys[k] ; k++) {
if(!*keys[k]) continue;
size_t klen = strlen(keys[k]);
char tag_open[klen + 2];
tag_open[0] = '<';
strcpy(&tag_open[1], keys[k]);
tag_open[klen + 1] = '\0';
const char *new_start = strstr(start, tag_open);
if(!new_start)
return false;
start = new_start + klen + 1;
if(*start != '>' && !isspace((uint8_t)*start))
return false;
if(*start != '>') {
start = strchr(start, '>');
if(!start) return false;
}
start++; // skip the >
char tag_close[klen + 4];
tag_close[0] = '<';
tag_close[1] = '/';
strcpy(&tag_close[2], keys[k]);
tag_close[klen + 2] = '>';
tag_close[klen + 3] = '\0';
const char *new_end = strstr(start, tag_close);
if(!new_end || (end && new_end > end))
return false;
end = new_end;
}
if(!start || !end || start == end)
return false;
cb(buffer, prefix, start, end);
return true;
}
static void print_xml_cb(BUFFER *buffer, const char *prefix, const char *start, const char *end) {
if(prefix)
buffer_strcat(buffer, prefix);
buffer_pretty_print_xml_object(buffer, start, end);
}
bool buffer_extract_and_print_xml(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[]) {
return buffer_extract_and_print_xml_with_cb(
buffer, xml, xml_len,
prefix, keys,
print_xml_cb);
}
static void print_value_cb(BUFFER *buffer, const char *prefix, const char *start, const char *end) {
if(prefix)
buffer_strcat(buffer, prefix);
buffer_need_bytes(buffer, end - start + 1);
char *started = &buffer->buffer[buffer->len];
char *d = started;
const char *s = start;
while(s < end && s) {
if(*s == '&' && s + 3 < end) {
if(*(s + 1) == '#') {
if(s + 4 < end && *(s + 2) == '1' && *(s + 4) == ';') {
if (*(s + 3) == '0') {
s += 5;
*d++ = '\n';
continue;
} else if (*(s + 3) == '3') {
s += 5;
// *d++ = '\r';
continue;
}
} else if (*(s + 2) == '9' && *(s + 3) == ';') {
s += 4;
*d++ = '\t';
continue;
}
}
else if(s + 3 < end && *(s + 2) == 't' && *(s + 3) == ';') {
if(*(s + 1) == 'l') {
s += 4;
*d++ = '<';
continue;
}
else if(*(s + 1) == 'g') {
s += 4;
*d++ = '>';
continue;
}
}
}
*d++ = *s++;
}
*d = '\0';
buffer->len += d - started;
}
bool buffer_xml_extract_and_print_value(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[]) {
return buffer_extract_and_print_xml_with_cb(
buffer, xml, xml_len,
prefix, keys,
print_value_cb);
}

View file

@ -6,5 +6,7 @@
#include "libnetdata/libnetdata.h"
void buffer_pretty_print_xml(BUFFER *buffer, const char *xml, size_t xml_len);
bool buffer_extract_and_print_xml(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[]);
bool buffer_xml_extract_and_print_value(BUFFER *buffer, const char *xml, size_t xml_len, const char *prefix, const char *keys[]);
#endif //WINDOWS_EVENTS_XML_H

File diff suppressed because it is too large Load diff

View file

@ -1,28 +1,30 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_H
#define NETDATA_WINDOWS_EVENTS_H
#include "libnetdata/libnetdata.h"
#include "collectors/all.h"
#include <windows.h>
#include <winevt.h>
#include <wchar.h>
typedef enum {
WEVT_NO_CHANNEL_MATCHED,
WEVT_FAILED_TO_OPEN,
WEVT_FAILED_TO_SEEK,
WEVT_TIMED_OUT,
WEVT_OK,
WEVT_NOT_MODIFIED,
WEVT_CANCELLED,
} WEVT_QUERY_STATUS;
#include "windows-events-unicode.h"
#include "windows-events-query.h"
#include "windows-events-sources.h"
#include "windows-events-sid.h"
#include "windows-events-xml.h"
#endif //NETDATA_WINDOWS_EVENTS_H
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_WINDOWS_EVENTS_H
#define NETDATA_WINDOWS_EVENTS_H
#include "libnetdata/libnetdata.h"
#include "collectors/all.h"
#include <windows.h>
#include <winevt.h>
#include <wchar.h>
typedef enum {
WEVT_NO_CHANNEL_MATCHED,
WEVT_FAILED_TO_OPEN,
WEVT_FAILED_TO_SEEK,
WEVT_TIMED_OUT,
WEVT_OK,
WEVT_NOT_MODIFIED,
WEVT_CANCELLED,
} WEVT_QUERY_STATUS;
#include "windows-events-unicode.h"
#include "windows-events-query.h"
#include "windows-events-sources.h"
#include "windows-events-sid.h"
#include "windows-events-xml.h"
#include "windows-events-publishers.h"
#include "windows-events-fields-cache.h"
#endif //NETDATA_WINDOWS_EVENTS_H

View file

@ -414,6 +414,16 @@ static inline char *print_uint64_hex_reversed(char *dst, uint64_t value) {
#endif
}
static inline char *print_uint64_hex_reversed_full(char *dst, uint64_t value) {
char *d = dst;
for(size_t c = 0; c < sizeof(uint64_t) * 2; c++) {
*d++ = hex_digits[value & 0xf];
value >>= 4;
}
return d;
}
static inline char *print_uint64_base64_reversed(char *dst, uint64_t value) {
char *d = dst;
do *d++ = base64_digits[value & 63]; while ((value >>= 6));
@ -509,6 +519,7 @@ static inline size_t print_int64(char *dst, int64_t value) {
return print_uint64(dst, value) + len;
}
#define UINT64_MAX_LENGTH (24) // 21 should be enough
static inline void buffer_print_uint64(BUFFER *wb, uint64_t value) {
buffer_need_bytes(wb, 50);
wb->len += print_uint64(&wb->buffer[wb->len], value);
@ -521,7 +532,7 @@ static inline void buffer_print_int64(BUFFER *wb, int64_t value) {
buffer_overflow_check(wb);
}
#define UINT64_HEX_LENGTH ((sizeof(HEX_PREFIX) - 1) + (sizeof(uint64_t) * 2) + 2 + 1)
#define UINT64_HEX_MAX_LENGTH ((sizeof(HEX_PREFIX) - 1) + (sizeof(uint64_t) * 2) + 1)
static inline size_t print_uint64_hex(char *dst, uint64_t value) {
char *d = dst;
@ -534,14 +545,33 @@ static inline size_t print_uint64_hex(char *dst, uint64_t value) {
return e - dst;
}
static inline size_t print_uint64_hex_full(char *dst, uint64_t value) {
char *d = dst;
const char *s = HEX_PREFIX;
while(*s) *d++ = *s++;
char *e = print_uint64_hex_reversed_full(d, value);
char_array_reverse(d, e - 1);
*e = '\0';
return e - dst;
}
static inline void buffer_print_uint64_hex(BUFFER *wb, uint64_t value) {
buffer_need_bytes(wb, UINT64_HEX_LENGTH);
buffer_need_bytes(wb, UINT64_HEX_MAX_LENGTH);
wb->len += print_uint64_hex(&wb->buffer[wb->len], value);
buffer_overflow_check(wb);
}
static inline void buffer_print_uint64_hex_full(BUFFER *wb, uint64_t value) {
buffer_need_bytes(wb, UINT64_HEX_MAX_LENGTH);
wb->len += print_uint64_hex_full(&wb->buffer[wb->len], value);
buffer_overflow_check(wb);
}
#define UINT64_B64_MAX_LENGTH ((sizeof(IEEE754_UINT64_B64_PREFIX) - 1) + (sizeof(uint64_t) * 2) + 1)
static inline void buffer_print_uint64_base64(BUFFER *wb, uint64_t value) {
buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1);
buffer_need_bytes(wb, UINT64_B64_MAX_LENGTH);
buffer_fast_strcat(wb, IEEE754_UINT64_B64_PREFIX, sizeof(IEEE754_UINT64_B64_PREFIX) - 1);
@ -580,8 +610,9 @@ static inline void buffer_print_int64_base64(BUFFER *wb, int64_t value) {
buffer_overflow_check(wb);
}
#define DOUBLE_MAX_LENGTH (512) // 318 should be enough, including null
static inline void buffer_print_netdata_double(BUFFER *wb, NETDATA_DOUBLE value) {
buffer_need_bytes(wb, 512 + 2);
buffer_need_bytes(wb, DOUBLE_MAX_LENGTH);
if(isnan(value) || isinf(value)) {
buffer_fast_strcat(wb, "null", 4);
@ -597,8 +628,9 @@ static inline void buffer_print_netdata_double(BUFFER *wb, NETDATA_DOUBLE value)
buffer_overflow_check(wb);
}
#define DOUBLE_HEX_MAX_LENGTH ((sizeof(IEEE754_DOUBLE_HEX_PREFIX) - 1) + (sizeof(uint64_t) * 2) + 1)
static inline void buffer_print_netdata_double_hex(BUFFER *wb, NETDATA_DOUBLE value) {
buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1);
buffer_need_bytes(wb, DOUBLE_HEX_MAX_LENGTH);
uint64_t *ptr = (uint64_t *) (&value);
buffer_fast_strcat(wb, IEEE754_DOUBLE_HEX_PREFIX, sizeof(IEEE754_DOUBLE_HEX_PREFIX) - 1);
@ -612,8 +644,9 @@ static inline void buffer_print_netdata_double_hex(BUFFER *wb, NETDATA_DOUBLE va
buffer_overflow_check(wb);
}
#define DOUBLE_B64_MAX_LENGTH ((sizeof(IEEE754_DOUBLE_B64_PREFIX) - 1) + (sizeof(uint64_t) * 2) + 1)
static inline void buffer_print_netdata_double_base64(BUFFER *wb, NETDATA_DOUBLE value) {
buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1);
buffer_need_bytes(wb, DOUBLE_B64_MAX_LENGTH);
uint64_t *ptr = (uint64_t *) (&value);
buffer_fast_strcat(wb, IEEE754_DOUBLE_B64_PREFIX, sizeof(IEEE754_DOUBLE_B64_PREFIX) - 1);

View file

@ -256,6 +256,7 @@ struct facets {
} keys_in_row;
FACET_ROW *base; // double linked list of the selected facets rows
FACET_ROW_BIN_DATA bin_data;
uint32_t items_to_return;
uint32_t max_items_to_return;
@ -329,6 +330,10 @@ struct facets {
struct {
size_t searches;
} fts;
struct {
size_t bin_data_inflight;
};
} operations;
struct {
@ -576,7 +581,7 @@ static inline void FACET_VALUE_ADD_OR_UPDATE_SELECTED(FACET_KEY *k, const char *
.hash = hash,
.selected = true,
.name = name,
.name_len = 0,
.name_len = name ? strlen(name) : 0,
};
FACET_VALUE_ADD_TO_INDEX(k, &tv);
}
@ -644,6 +649,35 @@ bool facets_key_name_value_length_is_selected(FACETS *facets, const char *key, s
return (v && v->selected) ? true : false;
}
bool facets_foreach_selected_value_in_key(FACETS *facets, const char *key, size_t key_length, DICTIONARY *used_hashes_registry, facets_foreach_selected_value_in_key_t cb, void *data) {
FACETS_HASH hash = FACETS_HASH_FUNCTION(key, key_length);
FACET_KEY *k = FACETS_KEY_GET_FROM_INDEX(facets, hash);
if(!k || k->default_selected_for_values)
return false;
size_t selected = 0;
for(FACET_VALUE *v = k->values.ll; v ;v = v->next) {
if(!v->selected) continue;
const char *value = v->name;
if(!value) {
if(used_hashes_registry) {
char hash_str[FACET_STRING_HASH_SIZE];
facets_hash_to_str(v->hash, hash_str);
value = dictionary_get(used_hashes_registry, hash_str);
}
if(!value)
return false;
}
if(!cb(facets, selected++, k->name, value, data))
return false;
}
return selected > 0;
}
void facets_add_possible_value_name_to_key(FACETS *facets, const char *key, size_t key_length, const char *value, size_t value_length) {
FACETS_HASH hash = FACETS_HASH_FUNCTION(key, key_length);
FACET_KEY *k = FACETS_KEY_GET_FROM_INDEX(facets, hash);
@ -1591,6 +1625,35 @@ static inline bool facets_key_is_facet(FACETS *facets, FACET_KEY *k) {
return false;
}
// ----------------------------------------------------------------------------
// bin_data management
static inline void facets_row_bin_data_cleanup(FACETS *facets, FACET_ROW_BIN_DATA *bin_data) {
if(!bin_data->data)
return;
bin_data->cleanup_cb(bin_data->data);
*bin_data = FACET_ROW_BIN_DATA_EMPTY;
fatal_assert(facets->operations.bin_data_inflight > 0);
facets->operations.bin_data_inflight--;
}
void facets_row_bin_data_set(FACETS *facets, void (*cleanup_cb)(void *data), void *data) {
// in case the caller tries to register bin_data multiple times
// for the same row.
facets_row_bin_data_cleanup(facets, &facets->bin_data);
// set the new values
facets->bin_data.cleanup_cb = cleanup_cb;
facets->bin_data.data = data;
facets->operations.bin_data_inflight++;
}
void *facets_row_bin_data_get(FACETS *facets __maybe_unused, FACET_ROW *row) {
return row->bin_data.data;
}
// ----------------------------------------------------------------------------
FACETS *facets_create(uint32_t items_to_return, FACETS_OPTIONS options, const char *visible_keys, const char *facet_keys, const char *non_facet_keys) {
@ -1633,6 +1696,13 @@ void facets_destroy(FACETS *facets) {
facets_row_free(facets, r);
}
// in case the caller did not call facets_row_finished()
// on the last row.
facets_row_bin_data_cleanup(facets, &facets->bin_data);
// make sure we didn't lose any data
fatal_assert(facets->operations.bin_data_inflight == 0);
freez(facets->histogram.chart);
freez(facets);
}
@ -1930,7 +2000,9 @@ static void facet_row_key_value_delete_callback(const DICTIONARY_ITEM *item __ma
// FACET_ROW management
static void facets_row_free(FACETS *facets __maybe_unused, FACET_ROW *row) {
facets_row_bin_data_cleanup(facets, &row->bin_data);
dictionary_destroy(row->dict);
row->dict = NULL;
freez(row);
}
@ -1940,6 +2012,7 @@ static FACET_ROW *facets_row_create(FACETS *facets, usec_t usec, FACET_ROW *into
if(into) {
row = into;
facets->operations.rows.reused++;
facets_row_bin_data_cleanup(facets, &row->bin_data);
}
else {
row = callocz(1, sizeof(FACET_ROW));
@ -1950,6 +2023,11 @@ static FACET_ROW *facets_row_create(FACETS *facets, usec_t usec, FACET_ROW *into
facets->operations.rows.created++;
}
// copy the bin_data to the row
// and forget about them in facets
row->bin_data = facets->bin_data;
facets->bin_data = FACET_ROW_BIN_DATA_EMPTY;
row->severity = facets->current_row.severity;
row->usec = usec;
@ -2134,6 +2212,8 @@ static void facets_reset_keys_with_value_and_row(FACETS *facets) {
facets->current_row.keys_matched_by_query_positive = 0;
facets->current_row.keys_matched_by_query_negative = 0;
facets->keys_in_row.used = 0;
facets_row_bin_data_cleanup(facets, &facets->bin_data);
}
void facets_rows_begin(FACETS *facets) {

View file

@ -52,10 +52,18 @@ typedef struct facet_row_key_value {
BUFFER *wb;
} FACET_ROW_KEY_VALUE;
typedef struct facet_row_bin_data {
void (*cleanup_cb)(void *data);
void *data;
} FACET_ROW_BIN_DATA;
#define FACET_ROW_BIN_DATA_EMPTY (FACET_ROW_BIN_DATA){.data = NULL, .cleanup_cb = NULL}
typedef struct facet_row {
usec_t usec;
DICTIONARY *dict;
FACET_ROW_SEVERITY severity;
FACET_ROW_BIN_DATA bin_data;
struct facet_row *prev, *next;
} FACET_ROW;
@ -132,4 +140,10 @@ void facets_table_config(BUFFER *wb);
const char *facets_severity_to_string(FACET_ROW_SEVERITY severity);
typedef bool (*facets_foreach_selected_value_in_key_t)(FACETS *facets, size_t id, const char *key, const char *value, void *data);
bool facets_foreach_selected_value_in_key(FACETS *facets, const char *key, size_t key_length, DICTIONARY *used_hashes_registry, facets_foreach_selected_value_in_key_t cb, void *data);
void facets_row_bin_data_set(FACETS *facets, void (*cleanup_cb)(void *data), void *data);
void *facets_row_bin_data_get(FACETS *facets __maybe_unused, FACET_ROW *row);
#endif

View file

@ -107,8 +107,15 @@ typedef struct {
struct {
usec_t start_ut;
usec_t stop_ut;
usec_t delta_ut;
} anchor;
struct {
usec_t start_ut;
usec_t stop_ut;
bool stop_when_full;
} query;
usec_t last_modified;
struct lqs_extension c;
@ -144,6 +151,21 @@ static inline void lqs_log_error(LOGS_QUERY_STATUS *lqs, const char *msg) {
, lqs->rq.direction == FACETS_ANCHOR_DIRECTION_FORWARD ? "forward" : "backward");
}
static inline void lqs_query_timeframe(LOGS_QUERY_STATUS *lqs, usec_t anchor_delta_ut) {
lqs->anchor.delta_ut = anchor_delta_ut;
if(lqs->rq.direction == FACETS_ANCHOR_DIRECTION_FORWARD) {
lqs->query.start_ut = (lqs->rq.data_only && lqs->anchor.start_ut) ? lqs->anchor.start_ut : lqs->rq.after_ut;
lqs->query.stop_ut = ((lqs->rq.data_only && lqs->anchor.stop_ut) ? lqs->anchor.stop_ut : lqs->rq.before_ut) + lqs->anchor.delta_ut;
}
else {
lqs->query.start_ut = ((lqs->rq.data_only && lqs->anchor.start_ut) ? lqs->anchor.start_ut : lqs->rq.before_ut) + lqs->anchor.delta_ut;
lqs->query.stop_ut = (lqs->rq.data_only && lqs->anchor.stop_ut) ? lqs->anchor.stop_ut : lqs->rq.after_ut;
}
lqs->query.stop_when_full = (lqs->rq.data_only && !lqs->anchor.stop_ut);
}
static inline void lqs_function_help(LOGS_QUERY_STATUS *lqs, BUFFER *wb) {
buffer_reset(wb);
wb->content_type = CT_TEXT_PLAIN;

View file

@ -119,22 +119,17 @@ bool fd_is_socket(int fd) {
return true;
}
bool sock_has_output_error(int fd) {
if(fd < 0) {
//internal_error(true, "invalid socket %d", fd);
return false;
}
#ifdef POLLRDHUP
bool is_socket_closed(int fd) {
if(fd < 0)
return true;
// if(!fd_is_socket(fd)) {
// //internal_error(true, "fd %d is not a socket", fd);
// return false;
// }
short int errors = POLLERR | POLLHUP | POLLNVAL;
#ifdef POLLRDHUP
errors |= POLLRDHUP;
#endif
short int errors = POLLERR | POLLHUP | POLLNVAL | POLLRDHUP;
struct pollfd pfd = {
.fd = fd,
@ -149,6 +144,31 @@ bool sock_has_output_error(int fd) {
return ((pfd.revents & errors) || !(pfd.revents & POLLOUT));
}
#else
bool is_socket_closed(int fd) {
if(fd < 0)
return true;
char buffer;
ssize_t result = recv(fd, &buffer, 1, MSG_PEEK | MSG_DONTWAIT);
if (result == 0) {
// Connection closed
return true;
}
else if (result < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No data available, but socket is still open
return false;
} else {
// An error occurred
return true;
}
}
// Data is available, socket is open
return false;
}
#endif
int sock_setnonblock(int fd) {
int flags;

View file

@ -44,7 +44,7 @@ ssize_t send_timeout(NETDATA_SSL *ssl,int sockfd, void *buf, size_t len, int fla
int wait_on_socket_or_cancel_with_timeout(NETDATA_SSL *ssl, int fd, int timeout_ms, short int poll_events, short int *revents);
bool fd_is_socket(int fd);
bool sock_has_output_error(int fd);
bool is_socket_closed(int fd);
int sock_setnonblock(int fd);
int sock_delnonblock(int fd);

View file

@ -219,7 +219,7 @@ static inline bool is_settings_file_valid(char *file) {
return false;
while(*s) {
if(!isalnum(*s) && *s != '-' && *s != '_')
if(!isalnum((uint8_t)*s) && *s != '-' && *s != '_')
return false;
s++;
}

View file

@ -121,10 +121,13 @@ RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o) {
bool web_client_interrupt_callback(void *data) {
struct web_client *w = data;
bool ret;
if(w->interrupt.callback)
return w->interrupt.callback(w, w->interrupt.callback_data);
ret = w->interrupt.callback(w, w->interrupt.callback_data);
else
ret = is_socket_closed(w->ofd);
return sock_has_output_error(w->ofd);
return ret;
}
void nd_web_api_init(void) {