summaryrefslogtreecommitdiffstats
path: root/src/collectors/windows-events.plugin/windows-events-providers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/collectors/windows-events.plugin/windows-events-providers.c')
-rw-r--r--src/collectors/windows-events.plugin/windows-events-providers.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/src/collectors/windows-events.plugin/windows-events-providers.c b/src/collectors/windows-events.plugin/windows-events-providers.c
new file mode 100644
index 000000000..d4c4d35ea
--- /dev/null
+++ b/src/collectors/windows-events.plugin/windows-events-providers.c
@@ -0,0 +1,678 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "windows-events-providers.h"
+
+#define MAX_OPEN_HANDLES_PER_PROVIDER 5
+
+struct provider;
+
+// 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 provider *provider; // a pointer back to the provider
+
+ usec_t created_monotonic_ut; // the monotonic timestamp this handle was created
+
+ // 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, mask;
+ 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 provider_key {
+ ND_UUID uuid; // the Provider GUID
+ DWORD len; // the length of the Provider Name
+ const wchar_t *wname; // the Provider wide-string Name (UTF-16)
+} PROVIDER_KEY;
+
+typedef struct provider {
+ PROVIDER_KEY key;
+ 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
+
+ WEVT_PROVIDER_PLATFORM platform;
+
+ struct provider_list keyword;
+ struct provider_list tasks;
+ struct provider_list opcodes;
+ struct provider_list levels;
+} PROVIDER;
+
+// A hashtable implementation for Providers
+// using the Provider GUID as key and PROVIDER as value
+#define SIMPLE_HASHTABLE_NAME _PROVIDER
+#define SIMPLE_HASHTABLE_VALUE_TYPE PROVIDER
+#define SIMPLE_HASHTABLE_KEY_TYPE PROVIDER_KEY
+#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION provider_value_to_key
+#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION provider_cache_compar
+#define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 1
+#include "libnetdata/simple_hashtable/simple_hashtable.h"
+
+static struct {
+ SPINLOCK spinlock;
+ uint32_t total_providers;
+ uint32_t total_handles;
+ uint32_t deleted_handles;
+ struct simple_hashtable_PROVIDER hashtable;
+ ARAL *aral_providers;
+ ARAL *aral_handles;
+} pbc = {
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
+};
+
+static void provider_load_list(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, WEVT_VARIANT *property,
+ TXT_UTF16 *dst, struct provider_list *l, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id);
+
+const char *provider_get_name(PROVIDER_META_HANDLE *p) {
+ return (p && p->provider && p->provider->name) ? p->provider->name : "__UNKNOWN PROVIDER__";
+}
+
+ND_UUID provider_get_uuid(PROVIDER_META_HANDLE *p) {
+ return (p && p->provider) ? p->provider->key.uuid : UUID_ZERO;
+}
+
+static inline PROVIDER_KEY *provider_value_to_key(PROVIDER *p) {
+ return &p->key;
+}
+
+static inline bool provider_cache_compar(PROVIDER_KEY *a, PROVIDER_KEY *b) {
+ return a->len == b->len && UUIDeq(a->uuid, b->uuid) && memcmp(a->wname, b->wname, a->len) == 0;
+}
+
+void provider_cache_init(void) {
+ simple_hashtable_init_PROVIDER(&pbc.hashtable, 100000);
+ pbc.aral_providers = aral_create("wevt_providers", sizeof(PROVIDER), 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);
+}
+
+static bool provider_property_get(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id) {
+ DWORD bufferUsed = 0;
+
+ if(!EvtGetPublisherMetadataProperty(h->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(h->hMetadata, property_id, 0, content->size, content->data, &bufferUsed)) {
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetPublisherMetadataProperty() failed after resize");
+ goto cleanup;
+ }
+
+ return true;
+
+cleanup:
+ return false;
+}
+
+static bool provider_string_property_exists(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id) {
+ if(!provider_property_get(h, content, property_id))
+ return false;
+
+ if(content->data->Type != EvtVarTypeString)
+ return false;
+
+ if(!content->data->StringVal[0])
+ return false;
+
+ return true;
+}
+
+static void provider_detect_platform(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content) {
+ if(UUIDiszero(h->provider->key.uuid))
+ h->provider->platform = WEVT_PLATFORM_WEL;
+ else if(h->hMetadata) {
+ if (provider_string_property_exists(h, content, EvtPublisherMetadataMessageFilePath) ||
+ provider_string_property_exists(h, content, EvtPublisherMetadataResourceFilePath) ||
+ provider_string_property_exists(h, content, EvtPublisherMetadataParameterFilePath))
+ h->provider->platform = WEVT_PLATFORM_ETW;
+ else
+ // The provider cannot be opened, does not have any resource files (message, resource, parameter)
+ h->provider->platform = WEVT_PLATFORM_TL;
+ }
+ else h->provider->platform = WEVT_PLATFORM_ETW;
+}
+
+WEVT_PROVIDER_PLATFORM provider_get_platform(PROVIDER_META_HANDLE *p) {
+ return p->provider->platform;
+}
+
+PROVIDER_META_HANDLE *provider_get(ND_UUID uuid, LPCWSTR providerName) {
+ if(!providerName || !providerName[0])
+ return NULL;
+
+ PROVIDER_KEY key = {
+ .uuid = uuid,
+ .len = wcslen(providerName),
+ .wname = providerName,
+ };
+ XXH64_hash_t hash = XXH3_64bits(providerName, wcslen(key.wname) * sizeof(*key.wname));
+
+ spinlock_lock(&pbc.spinlock);
+
+ SIMPLE_HASHTABLE_SLOT_PROVIDER *slot =
+ simple_hashtable_get_slot_PROVIDER(&pbc.hashtable, hash, &key, true);
+
+ bool load_it = false;
+ PROVIDER *p = SIMPLE_HASHTABLE_SLOT_DATA(slot);
+ if(!p) {
+ p = aral_callocz(pbc.aral_providers);
+ p->key.uuid = key.uuid;
+ p->key.len = key.len;
+ p->key.wname = wcsdup(key.wname);
+ p->name = strdupz(provider2utf8(key.wname));
+ simple_hashtable_set_slot_PROVIDER(&pbc.hashtable, slot, hash, p);
+ load_it = true;
+ pbc.total_providers++;
+ }
+
+ 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->provider = p;
+ h->created_monotonic_ut = now_monotonic_usec();
+ 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_UTF16 unicode = { 0 };
+
+ provider_detect_platform(h, &content);
+ provider_load_list(h, &content, &property, &unicode, &p->keyword, EvtPublisherMetadataKeywords);
+ provider_load_list(h, &content, &property, &unicode, &p->levels, EvtPublisherMetadataLevels);
+ provider_load_list(h, &content, &property, &unicode, &p->opcodes, EvtPublisherMetadataOpcodes);
+ provider_load_list(h, &content, &property, &unicode, &p->tasks, EvtPublisherMetadataTasks);
+
+ txt_utf16_cleanup(&unicode);
+ wevt_variant_cleanup(&content);
+ wevt_variant_cleanup(&property);
+ }
+
+ spinlock_unlock(&pbc.spinlock);
+
+ return h;
+}
+
+EVT_HANDLE provider_handle(PROVIDER_META_HANDLE *h) {
+ return h ? h->hMetadata : NULL;
+}
+
+PROVIDER_META_HANDLE *provider_dup(PROVIDER_META_HANDLE *h) {
+ if(h) h->locks++;
+ return h;
+}
+
+static void provider_meta_handle_delete(PROVIDER_META_HANDLE *h) {
+ PROVIDER *p = h->provider;
+
+ DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(p->handles, h, prev, next);
+
+ if(h->hMetadata)
+ EvtClose(h->hMetadata);
+
+ aral_freez(pbc.aral_handles, h);
+
+ fatal_assert(pbc.total_handles && p->total_handles && p->available_handles);
+
+ pbc.total_handles--;
+ p->total_handles--;
+
+ pbc.deleted_handles++;
+ p->deleted_handles++;
+
+ p->available_handles--;
+}
+
+void providers_release_unused_handles(void) {
+ usec_t now_ut = now_monotonic_usec();
+
+ spinlock_lock(&pbc.spinlock);
+ for(size_t i = 0; i < pbc.hashtable.size ; i++) {
+ SIMPLE_HASHTABLE_SLOT_PROVIDER *slot = &pbc.hashtable.hashtable[i];
+ PROVIDER *p = SIMPLE_HASHTABLE_SLOT_DATA(slot);
+ if(!p) continue;
+
+ PROVIDER_META_HANDLE *h = p->handles;
+ while(h) {
+ PROVIDER_META_HANDLE *next = h->next;
+
+ if(!h->locks && (now_ut - h->created_monotonic_ut) >= WINDOWS_EVENTS_RELEASE_IDLE_PROVIDER_HANDLES_TIME_UT)
+ provider_meta_handle_delete(h);
+
+ h = next;
+ }
+ }
+ spinlock_unlock(&pbc.spinlock);
+}
+
+void provider_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) {
+ PROVIDER *p = h->provider;
+
+ spinlock_lock(&pbc.spinlock);
+ h->owner = 0;
+
+ if(++p->available_handles > MAX_OPEN_HANDLES_PER_PROVIDER) {
+ // there are too many idle handles on this provider
+ provider_meta_handle_delete(h);
+ }
+ 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 provider 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 provider_load_list(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, WEVT_VARIANT *property,
+ TXT_UTF16 *dst, 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_provider_level;
+ break;
+
+ case EvtPublisherMetadataOpcodes:
+ name_id = EvtPublisherMetadataOpcodeName;
+ message_id = EvtPublisherMetadataOpcodeMessageID;
+ value_id = EvtPublisherMetadataOpcodeValue;
+ value_bits = 32;
+ is_valid = is_valid_provider_opcode;
+ compare_func = compare_ascending;
+ break;
+
+ case EvtPublisherMetadataTasks:
+ name_id = EvtPublisherMetadataTaskName;
+ message_id = EvtPublisherMetadataTaskMessageID;
+ value_id = EvtPublisherMetadataTaskValue;
+ value_bits = 32;
+ is_valid = is_valid_provider_task;
+ compare_func = compare_ascending;
+ break;
+
+ case EvtPublisherMetadataKeywords:
+ name_id = EvtPublisherMetadataKeywordName;
+ message_id = EvtPublisherMetadataKeywordMessageID;
+ value_id = EvtPublisherMetadataKeywordValue;
+ value_bits = 64;
+ is_valid = is_valid_provider_keyword;
+ 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 itemCount = 0;
+
+ // Get the metadata array for the list (e.g., opcodes, tasks, or levels)
+ if(!provider_property_get(h, content, property_id))
+ 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, mask = 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;
+
+ mask |= 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 (EvtFormatMessage_utf16(dst, hMetadata, NULL, messageID, EvtFormatMessageId)) {
+ size_t len;
+ d->name = utf16_to_utf8_strdupz(dst->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 = utf16_to_utf8_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;
+ l->mask = mask;
+
+ 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 provider_bitmap_metadata(TXT_UTF8 *dst, struct provider_list *l, uint64_t value) {
+ if(!(value & l->mask) || !l->total || !l->array || l->exceeds_data_type)
+ return false;
+
+ // do not empty the buffer, there may be reserved keywords in it
+ // dst->used = 0;
+
+ if(dst->used)
+ dst->used--;
+
+ size_t added = 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_PROVIDER;
+ added++;
+ }
+ }
+
+ if(dst->used > 1) {
+ txt_utf8_resize(dst, dst->used + 1, true);
+ dst->data[dst->used++] = 0;
+ }
+
+ fatal_assert(dst->used <= dst->size);
+ return added;
+}
+
+//// lookup a single value (returns its string)
+//static bool provider_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_PROVIDER;
+//
+// 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 provider_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 provider_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_PROVIDER;
+ }
+ 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 provider_keyword_cacheable(PROVIDER_META_HANDLE *h) {
+ return h && !h->provider->keyword.exceeds_data_type;
+}
+
+bool provider_tasks_cacheable(PROVIDER_META_HANDLE *h) {
+ return h && !h->provider->tasks.exceeds_data_type;
+}
+
+bool is_useful_provider_for_levels(PROVIDER_META_HANDLE *h) {
+ return h && !h->provider->levels.exceeds_data_type;
+}
+
+bool provider_opcodes_cacheable(PROVIDER_META_HANDLE *h) {
+ return h && !h->provider->opcodes.exceeds_data_type;
+}
+
+bool provider_get_keywords(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
+ if(!h) return false;
+ return provider_bitmap_metadata(dst, &h->provider->keyword, value);
+}
+
+bool provider_get_level(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
+ if(!h) return false;
+ return provider_value_metadata(dst, &h->provider->levels, value);
+}
+
+bool provider_get_task(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
+ if(!h) return false;
+ return provider_value_metadata(dst, &h->provider->tasks, value);
+}
+
+bool provider_get_opcode(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
+ if(!h) return false;
+ return provider_value_metadata(dst, &h->provider->opcodes, value);
+}