master
c 678 lines 22.3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "windows-events-providers.h"
4
5 #define MAX_OPEN_HANDLES_PER_PROVIDER 5
6
7 struct provider;
8
9 // typedef as PROVIDER_META_HANDLE in include file
10 struct provider_meta_handle {
11 pid_t owner; // the owner of the handle, or zero
12 uint32_t locks; // the number of locks the owner has on this handle
13 EVT_HANDLE hMetadata; // the handle
14 struct provider *provider; // a pointer back to the provider
15
16 usec_t created_monotonic_ut; // the monotonic timestamp this handle was created
17
18 // double linked list
19 PROVIDER_META_HANDLE *prev;
20 PROVIDER_META_HANDLE *next;
21 };
22
23 struct provider_data {
24 uint64_t value; // the mask of the keyword
25 XXH64_hash_t hash; // the hash of the name
26 uint32_t len; // the length of the name
27 char *name; // the name of the keyword in UTF-8
28 };
29
30 struct provider_list {
31 uint64_t min, max, mask;
32 bool exceeds_data_type; // true when the manifest values exceed the capacity of the EvtXXX() API
33 uint32_t total; // the number of entries in the array
34 struct provider_data *array; // the array of entries, sorted (for binary search)
35 };
36
37 typedef struct provider_key {
38 ND_UUID uuid; // the Provider GUID
39 DWORD len; // the length of the Provider Name
40 const wchar_t *wname; // the Provider wide-string Name (UTF-16)
41 } PROVIDER_KEY;
42
43 typedef struct provider {
44 PROVIDER_KEY key;
45 const char *name; // the Provider Name (UTF-8)
46 uint32_t total_handles; // the number of handles allocated
47 uint32_t available_handles; // the number of available handles
48 uint32_t deleted_handles; // the number of deleted handles
49 PROVIDER_META_HANDLE *handles; // a double linked list of all the handles
50
51 WEVT_PROVIDER_PLATFORM platform;
52
53 struct provider_list keyword;
54 struct provider_list tasks;
55 struct provider_list opcodes;
56 struct provider_list levels;
57 } PROVIDER;
58
59 // A hashtable implementation for Providers
60 // using the Provider GUID as key and PROVIDER as value
61 #define SIMPLE_HASHTABLE_NAME _PROVIDER
62 #define SIMPLE_HASHTABLE_VALUE_TYPE PROVIDER *
63 #define SIMPLE_HASHTABLE_KEY_TYPE PROVIDER_KEY
64 #define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION provider_value_to_key
65 #define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION provider_cache_compar
66 #define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 1
67 #include "libnetdata/simple_hashtable/simple_hashtable.h"
68
69 static struct {
70 SPINLOCK spinlock;
71 uint32_t total_providers;
72 uint32_t total_handles;
73 uint32_t deleted_handles;
74 struct simple_hashtable_PROVIDER hashtable;
75 ARAL *aral_providers;
76 ARAL *aral_handles;
77 } pbc = {
78 .spinlock = SPINLOCK_INITIALIZER,
79 };
80
81 static void provider_load_list(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, WEVT_VARIANT *property,
82 TXT_UTF16 *dst, struct provider_list *l, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id);
83
84 const char *provider_get_name(PROVIDER_META_HANDLE *p) {
85 return (p && p->provider && p->provider->name) ? p->provider->name : "__UNKNOWN PROVIDER__";
86 }
87
88 ND_UUID provider_get_uuid(PROVIDER_META_HANDLE *p) {
89 return (p && p->provider) ? p->provider->key.uuid : UUID_ZERO;
90 }
91
92 static inline PROVIDER_KEY *provider_value_to_key(PROVIDER *p) {
93 return &p->key;
94 }
95
96 static inline bool provider_cache_compar(PROVIDER_KEY *a, PROVIDER_KEY *b) {
97 return a->len == b->len && UUIDeq(a->uuid, b->uuid) && memcmp(a->wname, b->wname, a->len) == 0;
98 }
99
100 void provider_cache_init(void) {
101 simple_hashtable_init_PROVIDER(&pbc.hashtable, 100000);
102 pbc.aral_providers = aral_create("wevt_providers", sizeof(PROVIDER), 0, 4096, NULL, NULL, NULL, false, true, false);
103 pbc.aral_handles = aral_create("wevt_handles", sizeof(PROVIDER_META_HANDLE), 0, 4096, NULL, NULL, NULL, false, true, false);
104 }
105
106 static bool provider_property_get(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id) {
107 DWORD bufferUsed = 0;
108
109 if(!EvtGetPublisherMetadataProperty(h->hMetadata, property_id, 0, 0, NULL, &bufferUsed)) {
110 DWORD status = GetLastError();
111 if (status != ERROR_INSUFFICIENT_BUFFER) {
112 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetPublisherMetadataProperty() failed");
113 goto cleanup;
114 }
115 }
116
117 wevt_variant_resize(content, bufferUsed);
118 if (!EvtGetPublisherMetadataProperty(h->hMetadata, property_id, 0, content->size, content->data, &bufferUsed)) {
119 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetPublisherMetadataProperty() failed after resize");
120 goto cleanup;
121 }
122
123 return true;
124
125 cleanup:
126 return false;
127 }
128
129 static bool provider_string_property_exists(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id) {
130 if(!provider_property_get(h, content, property_id))
131 return false;
132
133 if(content->data->Type != EvtVarTypeString)
134 return false;
135
136 if(!content->data->StringVal[0])
137 return false;
138
139 return true;
140 }
141
142 static void provider_detect_platform(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content) {
143 if(UUIDiszero(h->provider->key.uuid))
144 h->provider->platform = WEVT_PLATFORM_WEL;
145 else if(h->hMetadata) {
146 if (provider_string_property_exists(h, content, EvtPublisherMetadataMessageFilePath) ||
147 provider_string_property_exists(h, content, EvtPublisherMetadataResourceFilePath) ||
148 provider_string_property_exists(h, content, EvtPublisherMetadataParameterFilePath))
149 h->provider->platform = WEVT_PLATFORM_ETW;
150 else
151 // The provider cannot be opened, does not have any resource files (message, resource, parameter)
152 h->provider->platform = WEVT_PLATFORM_TL;
153 }
154 else h->provider->platform = WEVT_PLATFORM_ETW;
155 }
156
157 WEVT_PROVIDER_PLATFORM provider_get_platform(PROVIDER_META_HANDLE *p) {
158 return p->provider->platform;
159 }
160
161 PROVIDER_META_HANDLE *provider_get(ND_UUID uuid, LPCWSTR providerName) {
162 if(!providerName || !providerName[0])
163 return NULL;
164
165 PROVIDER_KEY key = {
166 .uuid = uuid,
167 .len = wcslen(providerName),
168 .wname = providerName,
169 };
170 XXH64_hash_t hash = XXH3_64bits(providerName, wcslen(key.wname) * sizeof(*key.wname));
171
172 spinlock_lock(&pbc.spinlock);
173
174 SIMPLE_HASHTABLE_SLOT_PROVIDER *slot =
175 simple_hashtable_get_slot_PROVIDER(&pbc.hashtable, hash, &key, true);
176
177 bool load_it = false;
178 PROVIDER *p = SIMPLE_HASHTABLE_SLOT_DATA(slot);
179 if(!p) {
180 p = aral_callocz(pbc.aral_providers);
181 p->key.uuid = key.uuid;
182 p->key.len = key.len;
183 p->key.wname = wcsdup(key.wname);
184 p->name = strdupz(provider2utf8(key.wname));
185 simple_hashtable_set_slot_PROVIDER(&pbc.hashtable, slot, hash, p);
186 load_it = true;
187 pbc.total_providers++;
188 }
189
190 pid_t me = gettid_cached();
191 PROVIDER_META_HANDLE *h;
192 for(h = p->handles; h ;h = h->next) {
193 // find the first that is mine,
194 // or the first not owned by anyone
195 if(!h->owner || h->owner == me)
196 break;
197 }
198
199 if(!h) {
200 h = aral_callocz(pbc.aral_handles);
201 h->provider = p;
202 h->created_monotonic_ut = now_monotonic_usec();
203 h->hMetadata = EvtOpenPublisherMetadata(
204 NULL, // Local machine
205 providerName, // Provider name
206 NULL, // Log file path (NULL for default)
207 0, // Locale (0 for default locale)
208 0 // Flags
209 );
210
211 // we put it at the beginning of the list
212 // to find it first if the same owner needs more locks on it
213 DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(p->handles, h, prev, next);
214 pbc.total_handles++;
215 p->total_handles++;
216 p->available_handles++;
217 }
218
219 if(!h->owner) {
220 fatal_assert(p->available_handles > 0);
221 p->available_handles--;
222 h->owner = me;
223 }
224
225 h->locks++;
226
227 if(load_it) {
228 WEVT_VARIANT content = { 0 };
229 WEVT_VARIANT property = { 0 };
230 TXT_UTF16 unicode = { 0 };
231
232 provider_detect_platform(h, &content);
233 provider_load_list(h, &content, &property, &unicode, &p->keyword, EvtPublisherMetadataKeywords);
234 provider_load_list(h, &content, &property, &unicode, &p->levels, EvtPublisherMetadataLevels);
235 provider_load_list(h, &content, &property, &unicode, &p->opcodes, EvtPublisherMetadataOpcodes);
236 provider_load_list(h, &content, &property, &unicode, &p->tasks, EvtPublisherMetadataTasks);
237
238 txt_utf16_cleanup(&unicode);
239 wevt_variant_cleanup(&content);
240 wevt_variant_cleanup(&property);
241 }
242
243 spinlock_unlock(&pbc.spinlock);
244
245 return h;
246 }
247
248 EVT_HANDLE provider_handle(PROVIDER_META_HANDLE *h) {
249 return h ? h->hMetadata : NULL;
250 }
251
252 PROVIDER_META_HANDLE *provider_dup(PROVIDER_META_HANDLE *h) {
253 if(h) h->locks++;
254 return h;
255 }
256
257 static void provider_meta_handle_delete(PROVIDER_META_HANDLE *h) {
258 PROVIDER *p = h->provider;
259
260 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(p->handles, h, prev, next);
261
262 if(h->hMetadata)
263 EvtClose(h->hMetadata);
264
265 aral_freez(pbc.aral_handles, h);
266
267 fatal_assert(pbc.total_handles && p->total_handles && p->available_handles);
268
269 pbc.total_handles--;
270 p->total_handles--;
271
272 pbc.deleted_handles++;
273 p->deleted_handles++;
274
275 p->available_handles--;
276 }
277
278 void providers_release_unused_handles(void) {
279 usec_t now_ut = now_monotonic_usec();
280
281 spinlock_lock(&pbc.spinlock);
282 for(size_t i = 0; i < pbc.hashtable.size ; i++) {
283 SIMPLE_HASHTABLE_SLOT_PROVIDER *slot = &pbc.hashtable.hashtable[i];
284 PROVIDER *p = SIMPLE_HASHTABLE_SLOT_DATA(slot);
285 if(!p) continue;
286
287 PROVIDER_META_HANDLE *h = p->handles;
288 while(h) {
289 PROVIDER_META_HANDLE *next = h->next;
290
291 if(!h->locks && (now_ut - h->created_monotonic_ut) >= WINDOWS_EVENTS_RELEASE_IDLE_PROVIDER_HANDLES_TIME_UT)
292 provider_meta_handle_delete(h);
293
294 h = next;
295 }
296 }
297 spinlock_unlock(&pbc.spinlock);
298 }
299
300 void provider_release(PROVIDER_META_HANDLE *h) {
301 if(!h) return;
302 pid_t me = gettid_cached();
303 fatal_assert(h->owner == me);
304 fatal_assert(h->locks > 0);
305 if(--h->locks == 0) {
306 PROVIDER *p = h->provider;
307
308 spinlock_lock(&pbc.spinlock);
309 h->owner = 0;
310
311 if(++p->available_handles > MAX_OPEN_HANDLES_PER_PROVIDER) {
312 // there are too many idle handles on this provider
313 provider_meta_handle_delete(h);
314 }
315 else if(h->next) {
316 // it is not the last, put it at the end
317 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(p->handles, h, prev, next);
318 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(p->handles, h, prev, next);
319 }
320
321 spinlock_unlock(&pbc.spinlock);
322 }
323 }
324
325 // --------------------------------------------------------------------------------------------------------------------
326 // load provider lists
327
328 static bool wevt_get_property_from_array(WEVT_VARIANT *property, EVT_HANDLE handle, DWORD dwIndex, EVT_PUBLISHER_METADATA_PROPERTY_ID PropertyId) {
329 DWORD used = 0;
330
331 if (!EvtGetObjectArrayProperty(handle, PropertyId, dwIndex, 0, property->size, property->data, &used)) {
332 DWORD status = GetLastError();
333 if (status != ERROR_INSUFFICIENT_BUFFER) {
334 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetObjectArrayProperty() failed");
335 return false;
336 }
337
338 wevt_variant_resize(property, used);
339 if (!EvtGetObjectArrayProperty(handle, PropertyId, dwIndex, 0, property->size, property->data, &used)) {
340 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetObjectArrayProperty() failed");
341 return false;
342 }
343 }
344
345 property->used = used;
346 return true;
347 }
348
349 // Comparison function for ascending order (for Levels, Opcodes, Tasks)
350 static int compare_ascending(const void *a, const void *b) {
351 struct provider_data *d1 = (struct provider_data *)a;
352 struct provider_data *d2 = (struct provider_data *)b;
353
354 if (d1->value < d2->value) return -1;
355 if (d1->value > d2->value) return 1;
356 return 0;
357 }
358
359 //// Comparison function for descending order (for Keywords)
360 //static int compare_descending(const void *a, const void *b) {
361 // struct provider_data *d1 = (struct provider_data *)a;
362 // struct provider_data *d2 = (struct provider_data *)b;
363 //
364 // if (d1->value > d2->value) return -1;
365 // if (d1->value < d2->value) return 1;
366 // return 0;
367 //}
368
369 static void provider_load_list(PROVIDER_META_HANDLE *h, WEVT_VARIANT *content, WEVT_VARIANT *property,
370 TXT_UTF16 *dst, struct provider_list *l, EVT_PUBLISHER_METADATA_PROPERTY_ID property_id) {
371 if(!h || !h->hMetadata) return;
372
373 EVT_PUBLISHER_METADATA_PROPERTY_ID name_id, message_id, value_id;
374 uint8_t value_bits = 32;
375 int (*compare_func)(const void *, const void *);
376 bool (*is_valid)(uint64_t, bool);
377
378 switch(property_id) {
379 case EvtPublisherMetadataLevels:
380 name_id = EvtPublisherMetadataLevelName;
381 message_id = EvtPublisherMetadataLevelMessageID;
382 value_id = EvtPublisherMetadataLevelValue;
383 value_bits = 32;
384 compare_func = compare_ascending;
385 is_valid = is_valid_provider_level;
386 break;
387
388 case EvtPublisherMetadataOpcodes:
389 name_id = EvtPublisherMetadataOpcodeName;
390 message_id = EvtPublisherMetadataOpcodeMessageID;
391 value_id = EvtPublisherMetadataOpcodeValue;
392 value_bits = 32;
393 is_valid = is_valid_provider_opcode;
394 compare_func = compare_ascending;
395 break;
396
397 case EvtPublisherMetadataTasks:
398 name_id = EvtPublisherMetadataTaskName;
399 message_id = EvtPublisherMetadataTaskMessageID;
400 value_id = EvtPublisherMetadataTaskValue;
401 value_bits = 32;
402 is_valid = is_valid_provider_task;
403 compare_func = compare_ascending;
404 break;
405
406 case EvtPublisherMetadataKeywords:
407 name_id = EvtPublisherMetadataKeywordName;
408 message_id = EvtPublisherMetadataKeywordMessageID;
409 value_id = EvtPublisherMetadataKeywordValue;
410 value_bits = 64;
411 is_valid = is_valid_provider_keyword;
412 compare_func = NULL;
413 break;
414
415 default:
416 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Internal Error: Can't handle property id %u", property_id);
417 return;
418 }
419
420 EVT_HANDLE hMetadata = h->hMetadata;
421 EVT_HANDLE hArray = NULL;
422 DWORD itemCount = 0;
423
424 // Get the metadata array for the list (e.g., opcodes, tasks, or levels)
425 if(!provider_property_get(h, content, property_id))
426 goto cleanup;
427
428 // Get the number of items (e.g., levels, tasks, or opcodes)
429 hArray = content->data->EvtHandleVal;
430 if (!EvtGetObjectArraySize(hArray, &itemCount)) {
431 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetObjectArraySize() failed");
432 goto cleanup;
433 }
434
435 if (itemCount == 0) {
436 l->total = 0;
437 l->array = NULL;
438 goto cleanup;
439 }
440
441 // Allocate memory for the list items
442 l->array = callocz(itemCount, sizeof(struct provider_data));
443 l->total = itemCount;
444
445 uint64_t min = UINT64_MAX, max = 0, mask = 0;
446
447 // Iterate over the list and populate the entries
448 for (DWORD i = 0; i < itemCount; ++i) {
449 struct provider_data *d = &l->array[i];
450
451 // Get the value (e.g., opcode, task, or level)
452 if (wevt_get_property_from_array(property, hArray, i, value_id)) {
453 switch(value_bits) {
454 case 64:
455 d->value = wevt_field_get_uint64(property->data);
456 break;
457
458 case 32:
459 d->value = wevt_field_get_uint32(property->data);
460 break;
461 }
462
463 if(d->value < min)
464 min = d->value;
465
466 if(d->value > max)
467 max = d->value;
468
469 mask |= d->value;
470
471 if(!is_valid(d->value, false))
472 l->exceeds_data_type = true;
473 }
474
475 // Get the message ID
476 if (wevt_get_property_from_array(property, hArray, i, message_id)) {
477 uint32_t messageID = wevt_field_get_uint32(property->data);
478
479 if (messageID != (uint32_t)-1) {
480 if (EvtFormatMessage_utf16(dst, hMetadata, NULL, messageID, EvtFormatMessageId)) {
481 size_t len;
482 d->name = utf16_to_utf8_strdupz(dst->data, &len);
483 d->len = len;
484 }
485 }
486 }
487
488 // Get the name if the message is missing
489 if (!d->name && wevt_get_property_from_array(property, hArray, i, name_id)) {
490 fatal_assert(property->data->Type == EvtVarTypeString);
491 size_t len;
492 d->name = utf16_to_utf8_strdupz(property->data->StringVal, &len);
493 d->len = len;
494 }
495
496 // Calculate the hash for the name
497 if (d->name)
498 d->hash = XXH3_64bits(d->name, d->len);
499 }
500
501 l->min = min;
502 l->max = max;
503 l->mask = mask;
504
505 if(itemCount > 1 && compare_func != NULL) {
506 // Sort the array based on the value (ascending for all except keywords, descending for keywords)
507 qsort(l->array, itemCount, sizeof(struct provider_data), compare_func);
508 }
509
510 cleanup:
511 if (hArray)
512 EvtClose(hArray);
513 }
514
515 // --------------------------------------------------------------------------------------------------------------------
516 // lookup functions
517
518 // lookup bitmap metdata (returns a comma separated list of strings)
519 static bool provider_bitmap_metadata(TXT_UTF8 *dst, struct provider_list *l, uint64_t value) {
520 if(!(value & l->mask) || !l->total || !l->array || l->exceeds_data_type)
521 return false;
522
523 // do not empty the buffer, there may be reserved keywords in it
524 // dst->used = 0;
525
526 if(dst->used)
527 dst->used--;
528
529 size_t added = 0;
530 for(size_t k = 0; value && k < l->total; k++) {
531 struct provider_data *d = &l->array[k];
532
533 if(d->value && (value & d->value) == d->value && d->name && d->len) {
534 const char *s = d->name;
535 size_t slen = d->len;
536
537 // remove the mask from the value
538 value &= ~(d->value);
539
540 txt_utf8_resize(dst, dst->used + slen + 2 + 1, true);
541
542 if(dst->used) {
543 // add a comma and a space
544 dst->data[dst->used++] = ',';
545 dst->data[dst->used++] = ' ';
546 }
547
548 memcpy(&dst->data[dst->used], s, slen);
549 dst->used += slen;
550 dst->src = TXT_SOURCE_PROVIDER;
551 added++;
552 }
553 }
554
555 if(dst->used > 1) {
556 txt_utf8_resize(dst, dst->used + 1, true);
557 dst->data[dst->used++] = 0;
558 }
559
560 fatal_assert(dst->used <= dst->size);
561 return added;
562 }
563
564 //// lookup a single value (returns its string)
565 //static bool provider_value_metadata_linear(TXT_UTF8 *dst, struct provider_list *l, uint64_t value) {
566 // if(value < l->min || value > l->max || !l->total || !l->array || l->exceeds_data_type)
567 // return false;
568 //
569 // dst->used = 0;
570 //
571 // for(size_t k = 0; k < l->total; k++) {
572 // struct provider_data *d = &l->array[k];
573 //
574 // if(d->value == value && d->name && d->len) {
575 // const char *s = d->name;
576 // size_t slen = d->len;
577 //
578 // txt_utf8_resize(dst, slen + 1, false);
579 //
580 // memcpy(dst->data, s, slen);
581 // dst->used = slen;
582 // dst->src = TXT_SOURCE_PROVIDER;
583 //
584 // break;
585 // }
586 // }
587 //
588 // if(dst->used) {
589 // txt_utf8_resize(dst, dst->used + 1, true);
590 // dst->data[dst->used++] = 0;
591 // }
592 //
593 // fatal_assert(dst->used <= dst->size);
594 //
595 // return (dst->used > 0);
596 //}
597
598 static bool provider_value_metadata(TXT_UTF8 *dst, struct provider_list *l, uint64_t value) {
599 if(value < l->min || value > l->max || !l->total || !l->array || l->exceeds_data_type)
600 return false;
601
602 // if(l->total < 3) return provider_value_metadata_linear(dst, l, value);
603
604 dst->used = 0;
605
606 size_t left = 0;
607 size_t right = l->total - 1;
608
609 // Binary search within bounds
610 while (left <= right) {
611 size_t mid = left + (right - left) / 2;
612 struct provider_data *d = &l->array[mid];
613
614 if (d->value == value) {
615 // Value found, now check if it has a valid name and length
616 if (d->name && d->len) {
617 const char *s = d->name;
618 size_t slen = d->len;
619
620 txt_utf8_resize(dst, slen + 1, false);
621 memcpy(dst->data, s, slen);
622 dst->used = slen;
623 dst->data[dst->used++] = 0;
624 dst->src = TXT_SOURCE_PROVIDER;
625 }
626 break;
627 }
628
629 if (d->value < value)
630 left = mid + 1;
631 else {
632 if (mid == 0) break;
633 right = mid - 1;
634 }
635 }
636
637 fatal_assert(dst->used <= dst->size);
638 return (dst->used > 0);
639 }
640
641 // --------------------------------------------------------------------------------------------------------------------
642 // public API to lookup metadata
643
644 bool provider_keyword_cacheable(PROVIDER_META_HANDLE *h) {
645 return h && !h->provider->keyword.exceeds_data_type;
646 }
647
648 bool provider_tasks_cacheable(PROVIDER_META_HANDLE *h) {
649 return h && !h->provider->tasks.exceeds_data_type;
650 }
651
652 bool is_useful_provider_for_levels(PROVIDER_META_HANDLE *h) {
653 return h && !h->provider->levels.exceeds_data_type;
654 }
655
656 bool provider_opcodes_cacheable(PROVIDER_META_HANDLE *h) {
657 return h && !h->provider->opcodes.exceeds_data_type;
658 }
659
660 bool provider_get_keywords(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
661 if(!h) return false;
662 return provider_bitmap_metadata(dst, &h->provider->keyword, value);
663 }
664
665 bool provider_get_level(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
666 if(!h) return false;
667 return provider_value_metadata(dst, &h->provider->levels, value);
668 }
669
670 bool provider_get_task(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
671 if(!h) return false;
672 return provider_value_metadata(dst, &h->provider->tasks, value);
673 }
674
675 bool provider_get_opcode(TXT_UTF8 *dst, PROVIDER_META_HANDLE *h, uint64_t value) {
676 if(!h) return false;
677 return provider_value_metadata(dst, &h->provider->opcodes, value);
678 }