mirror of
https://github.com/netdata/netdata.git
synced 2025-04-14 09:38:34 +00:00
Windows Events Improvements 1 (#18528)
* 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:
parent
d953ce31cd
commit
f6a175aeb1
27 changed files with 4221 additions and 2653 deletions
CMakeLists.txt
packaging/utils
src
collectors
systemd-journal.plugin
windows-events.plugin
windows-events-fields-cache.cwindows-events-fields-cache.hwindows-events-publishers.cwindows-events-publishers.hwindows-events-query.cwindows-events-query.hwindows-events-sid.cwindows-events-sid.hwindows-events-sources.cwindows-events-sources.hwindows-events-unicode.cwindows-events-unicode.hwindows-events-xml.cwindows-events-xml.hwindows-events.cwindows-events.h
libnetdata
web/api
|
@ -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
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
569
src/collectors/windows-events.plugin/windows-events-publishers.c
Normal file
569
src/collectors/windows-events.plugin/windows-events-publishers.c
Normal 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);
|
||||
}
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue