0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-28 14:42:31 +00:00

bearer authorization API ()

* bearer authorization API - untested

* add machine guid to bearer token response

* removed REGISTRY_URL and replaced it with STRING

* eliminate url pointer from registry_machine_url

* remove registry_url counters from registry

* Revert "eliminate url pointer from registry_machine_url"

This reverts commit 79eff56f77.

* registry machine urls are now a double linked list

* registry machine urls are now using aral

* all registry objects now use aral

* strings now have 64 partitions and use R/W spinlock

* string to 128 partitions

* fix macro without internal checks

* registry now uses the bearer token when the cookie is not there

* api/v1/registry sends back all nodes on each host

* registry option to use mmap; optimization of registry structures

* do not index the terminator byte in strings; use 256 string partitions

* registry loading optimization

* convert person urls to double linked list to save memory

* re-organize items loading and make sure person urls are always available as machine urls too

* disable registry mmap by default

* keep track of all machine guids and their URLs, even if the cookie cannot be set

* fix bearer parsing
This commit is contained in:
Costa Tsaousis 2023-07-10 18:02:02 +03:00 committed by GitHub
parent afb8095ec0
commit 5943203a66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 866 additions and 595 deletions

View file

@ -700,8 +700,6 @@ set(REGISTRY_PLUGIN_FILES
registry/registry_machine.h
registry/registry_person.c
registry/registry_person.h
registry/registry_url.c
registry/registry_url.h
)
set(STATSD_PLUGIN_FILES

View file

@ -661,8 +661,6 @@ REGISTRY_PLUGIN_FILES = \
registry/registry_machine.h \
registry/registry_person.c \
registry/registry_person.h \
registry/registry_url.c \
registry/registry_url.h \
$(NULL)
STATSD_PLUGIN_FILES = \

View file

@ -24,6 +24,11 @@ typedef struct avl_element {
signed char avl_balance; /* Balance factor. */
} avl_t;
typedef struct __attribute__((packed)) avl_element_packed {
struct avl_element *avl_link[2]; /* Subtrees. */
signed char avl_balance; /* Balance factor. */
} avl_t_packed;
/* An AVL tree */
typedef struct avl_tree_type {
avl_t *root;

View file

@ -369,10 +369,6 @@ size_t dictionary_referenced_items(DICTIONARY *dict) {
return referenced_items;
}
long int dictionary_stats_for_registry(DICTIONARY *dict) {
if(unlikely(!dict)) return 0;
return (dict->stats->memory.index + dict->stats->memory.dict);
}
void dictionary_version_increment(DICTIONARY *dict) {
__atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
}

View file

@ -310,7 +310,6 @@ void dictionary_foreach_unlock(DICTFE *dfe);
size_t dictionary_version(DICTIONARY *dict);
size_t dictionary_entries(DICTIONARY *dict);
size_t dictionary_referenced_items(DICTIONARY *dict);
long int dictionary_stats_for_registry(DICTIONARY *dict);
// for all cases that the caller does not provide a stats structure, this is where they are accumulated.
extern struct dictionary_stats dictionary_stats_category_other;

View file

@ -11,7 +11,7 @@
typedef enum web_client_acl {
WEB_CLIENT_ACL_NONE = (0),
WEB_CLIENT_ACL_NOCHECK = (0),
WEB_CLIENT_ACL_NOCHECK = (0), // Don't check anything - this should work on all channels
WEB_CLIENT_ACL_DASHBOARD = (1 << 0),
WEB_CLIENT_ACL_REGISTRY = (1 << 1),
WEB_CLIENT_ACL_BADGE = (1 << 2),
@ -23,9 +23,17 @@ typedef enum web_client_acl {
WEB_CLIENT_ACL_SSL_DEFAULT = (1 << 8),
WEB_CLIENT_ACL_ACLK = (1 << 9),
WEB_CLIENT_ACL_WEBRTC = (1 << 10),
WEB_CLIENT_ACL_BEARER_OPTIONAL = (1 << 11), // allow unprotected access if bearer is not enabled in netdata
WEB_CLIENT_ACL_BEARER_REQUIRED = (1 << 12), // allow access only if a valid bearer is used
} WEB_CLIENT_ACL;
#define WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC (WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK | WEB_CLIENT_ACL_WEBRTC)
#define WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC (WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK | WEB_CLIENT_ACL_WEBRTC | WEB_CLIENT_ACL_BEARER_OPTIONAL)
#ifdef NETDATA_DEV_MODE
#define ACL_DEV_OPEN_ACCESS WEB_CLIENT_ACL_DASHBOARD
#else
#define ACL_DEV_OPEN_ACCESS 0
#endif
#define WEB_CLIENT_ACL_ALL 0xFFFF

View file

@ -8,6 +8,11 @@ typedef int32_t REFCOUNT;
// ----------------------------------------------------------------------------
// STRING implementation - dedup all STRING
#define STRING_PARTITION_SHIFTS (0)
#define STRING_PARTITIONS (256 >> STRING_PARTITION_SHIFTS)
#define string_partition_str(str) ((uint8_t)((str)[0]) >> STRING_PARTITION_SHIFTS)
#define string_partition(string) (string_partition_str((string)->str))
struct netdata_string {
uint32_t length; // the string length including the terminating '\0'
@ -18,20 +23,22 @@ struct netdata_string {
const char str[]; // the string itself, is appended to this structure
};
static struct string_hashtable {
static struct string_partition {
RW_SPINLOCK spinlock; // the R/W spinlock to protect the Judy array
Pvoid_t JudyHSArray; // the Judy array - hashtable
netdata_rwlock_t rwlock; // the R/W lock to protect the Judy array
size_t searches; // the number of successful searches in the index
size_t duplications; // when a string is referenced
size_t releases; // when a string is unreferenced
size_t inserts; // the number of successful inserts to the index
size_t deletes; // the number of successful deleted from the index
long int entries; // the number of entries in the index
long int active_references; // the number of active references alive
long int memory; // the memory used, without the JudyHS index
size_t inserts; // the number of successful inserts to the index
size_t deletes; // the number of successful deleted from the index
size_t searches; // the number of successful searches in the index
size_t duplications; // when a string is referenced
size_t releases; // when a string is unreferenced
#ifdef NETDATA_INTERNAL_CHECKS
// internal statistics
size_t found_deleted_on_search;
@ -41,50 +48,45 @@ static struct string_hashtable {
size_t spins;
#endif
} string_base = {
.JudyHSArray = NULL,
.rwlock = NETDATA_RWLOCK_INITIALIZER,
};
} string_base[STRING_PARTITIONS] = { 0 };
#ifdef NETDATA_INTERNAL_CHECKS
#define string_internal_stats_add(var, val) __atomic_add_fetch(&string_base.var, val, __ATOMIC_RELAXED)
#define string_internal_stats_add(partition, var, val) __atomic_add_fetch(&string_base[partition].var, val, __ATOMIC_RELAXED)
#else
#define string_internal_stats_add(var, val) do {;} while(0)
#define string_internal_stats_add(partition, var, val) do {;} while(0)
#endif
#define string_stats_atomic_increment(var) __atomic_add_fetch(&string_base.var, 1, __ATOMIC_RELAXED)
#define string_stats_atomic_decrement(var) __atomic_sub_fetch(&string_base.var, 1, __ATOMIC_RELAXED)
#define string_stats_atomic_increment(partition, var) __atomic_add_fetch(&string_base[partition].var, 1, __ATOMIC_RELAXED)
#define string_stats_atomic_decrement(partition, var) __atomic_sub_fetch(&string_base[partition].var, 1, __ATOMIC_RELAXED)
void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *duplications, size_t *releases) {
if(inserts)
*inserts = string_base.inserts;
if (inserts) *inserts = 0;
if (deletes) *deletes = 0;
if (searches) *searches = 0;
if (entries) *entries = 0;
if (references) *references = 0;
if (memory) *memory = 0;
if (duplications) *duplications = 0;
if (releases) *releases = 0;
if(deletes)
*deletes = string_base.deletes;
if(searches)
*searches = string_base.searches;
if(entries)
*entries = (size_t)string_base.entries;
if(references)
*references = (size_t)string_base.active_references;
if(memory)
*memory = (size_t)string_base.memory;
if(duplications)
*duplications = string_base.duplications;
if(releases)
*releases = string_base.releases;
for(size_t i = 0; i < STRING_PARTITIONS ;i++) {
if (inserts) *inserts += string_base[i].inserts;
if (deletes) *deletes += string_base[i].deletes;
if (searches) *searches += string_base[i].searches;
if (entries) *entries += (size_t) string_base[i].entries;
if (references) *references += (size_t) string_base[i].active_references;
if (memory) *memory += (size_t) string_base[i].memory;
if (duplications) *duplications += string_base[i].duplications;
if (releases) *releases += string_base[i].releases;
}
}
#define string_entry_acquire(se) __atomic_add_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST);
#define string_entry_release(se) __atomic_sub_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST);
static inline bool string_entry_check_and_acquire(STRING *se) {
uint8_t partition = string_partition(se);
REFCOUNT expected, desired, count = 0;
expected = __atomic_load_n(&se->refcount, __ATOMIC_SEQ_CST);
@ -96,7 +98,7 @@ static inline bool string_entry_check_and_acquire(STRING *se) {
// We cannot use this.
// The reference counter reached value zero,
// so another thread is deleting this.
string_internal_stats_add(spins, count - 1);
string_internal_stats_add(partition, spins, count - 1);
return false;
}
@ -104,11 +106,11 @@ static inline bool string_entry_check_and_acquire(STRING *se) {
} while(!__atomic_compare_exchange_n(&se->refcount, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
string_internal_stats_add(spins, count - 1);
string_internal_stats_add(partition, spins, count - 1);
// statistics
// string_base.active_references is altered at the in string_strdupz() and string_freez()
string_stats_atomic_increment(duplications);
string_stats_atomic_increment(partition, duplications);
return true;
}
@ -123,9 +125,11 @@ STRING *string_dup(STRING *string) {
string_entry_acquire(string);
uint8_t partition = string_partition(string);
// statistics
string_stats_atomic_increment(active_references);
string_stats_atomic_increment(duplications);
string_stats_atomic_increment(partition, active_references);
string_stats_atomic_increment(partition, duplications);
return string;
}
@ -134,26 +138,28 @@ STRING *string_dup(STRING *string) {
static inline STRING *string_index_search(const char *str, size_t length) {
STRING *string;
uint8_t partition = string_partition_str(str);
// Find the string in the index
// With a read-lock so that multiple readers can use the index concurrently.
netdata_rwlock_rdlock(&string_base.rwlock);
rw_spinlock_read_lock(&string_base[partition].spinlock);
Pvoid_t *Rc;
Rc = JudyHSGet(string_base.JudyHSArray, (void *)str, length);
Rc = JudyHSGet(string_base[partition].JudyHSArray, (void *)str, length - 1);
if(likely(Rc)) {
// found in the hash table
string = *Rc;
if(string_entry_check_and_acquire(string)) {
// we can use this entry
string_internal_stats_add(found_available_on_search, 1);
string_internal_stats_add(partition, found_available_on_search, 1);
}
else {
// this entry is about to be deleted by another thread
// do not touch it, let it go...
string = NULL;
string_internal_stats_add(found_deleted_on_search, 1);
string_internal_stats_add(partition, found_deleted_on_search, 1);
}
}
else {
@ -161,8 +167,8 @@ static inline STRING *string_index_search(const char *str, size_t length) {
string = NULL;
}
string_stats_atomic_increment(searches);
netdata_rwlock_unlock(&string_base.rwlock);
string_stats_atomic_increment(partition, searches);
rw_spinlock_read_unlock(&string_base[partition].spinlock);
return string;
}
@ -175,12 +181,14 @@ static inline STRING *string_index_search(const char *str, size_t length) {
static inline STRING *string_index_insert(const char *str, size_t length) {
STRING *string;
netdata_rwlock_wrlock(&string_base.rwlock);
uint8_t partition = string_partition_str(str);
rw_spinlock_write_lock(&string_base[partition].spinlock);
STRING **ptr;
{
JError_t J_Error;
Pvoid_t *Rc = JudyHSIns(&string_base.JudyHSArray, (void *)str, length, &J_Error);
Pvoid_t *Rc = JudyHSIns(&string_base[partition].JudyHSArray, (void *)str, length - 1, &J_Error);
if (unlikely(Rc == PJERR)) {
fatal(
"STRING: Cannot insert entry with name '%s' to JudyHS, JU_ERRNO_* == %u, ID == %d",
@ -199,9 +207,9 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
string->length = length;
string->refcount = 1;
*ptr = string;
string_base.inserts++;
string_base.entries++;
string_base.memory += (long)(mem_size + JUDYHS_INDEX_SIZE_ESTIMATE(length));
string_base[partition].inserts++;
string_base[partition].entries++;
string_base[partition].memory += (long)(mem_size + JUDYHS_INDEX_SIZE_ESTIMATE(length));
}
else {
// the item is already in the index
@ -209,25 +217,27 @@ static inline STRING *string_index_insert(const char *str, size_t length) {
if(string_entry_check_and_acquire(string)) {
// we can use this entry
string_internal_stats_add(found_available_on_insert, 1);
string_internal_stats_add(partition, found_available_on_insert, 1);
}
else {
// this entry is about to be deleted by another thread
// do not touch it, let it go...
string = NULL;
string_internal_stats_add(found_deleted_on_insert, 1);
string_internal_stats_add(partition, found_deleted_on_insert, 1);
}
string_stats_atomic_increment(searches);
string_stats_atomic_increment(partition, searches);
}
netdata_rwlock_unlock(&string_base.rwlock);
rw_spinlock_write_unlock(&string_base[partition].spinlock);
return string;
}
// delete an entry from the index
static inline void string_index_delete(STRING *string) {
netdata_rwlock_wrlock(&string_base.rwlock);
uint8_t partition = string_partition(string);
rw_spinlock_write_lock(&string_base[partition].spinlock);
#ifdef NETDATA_INTERNAL_CHECKS
if(unlikely(__atomic_load_n(&string->refcount, __ATOMIC_SEQ_CST) != 0))
@ -236,9 +246,9 @@ static inline void string_index_delete(STRING *string) {
bool deleted = false;
if (likely(string_base.JudyHSArray)) {
if (likely(string_base[partition].JudyHSArray)) {
JError_t J_Error;
int ret = JudyHSDel(&string_base.JudyHSArray, (void *)string->str, string->length, &J_Error);
int ret = JudyHSDel(&string_base[partition].JudyHSArray, (void *)string->str, string->length - 1, &J_Error);
if (unlikely(ret == JERR)) {
netdata_log_error(
"STRING: Cannot delete entry with name '%s' from JudyHS, JU_ERRNO_* == %u, ID == %d",
@ -253,18 +263,20 @@ static inline void string_index_delete(STRING *string) {
netdata_log_error("STRING: tried to delete '%s' that is not in the index. Ignoring it.", string->str);
else {
size_t mem_size = sizeof(STRING) + string->length;
string_base.deletes++;
string_base.entries--;
string_base.memory -= (long)(mem_size + JUDYHS_INDEX_SIZE_ESTIMATE(string->length));
string_base[partition].deletes++;
string_base[partition].entries--;
string_base[partition].memory -= (long)(mem_size + JUDYHS_INDEX_SIZE_ESTIMATE(string->length));
freez(string);
}
netdata_rwlock_unlock(&string_base.rwlock);
rw_spinlock_write_unlock(&string_base[partition].spinlock);
}
STRING *string_strdupz(const char *str) {
if(unlikely(!str || !*str)) return NULL;
uint8_t partition = string_partition_str(str);
size_t length = strlen(str) + 1;
STRING *string = string_index_search(str, length);
@ -277,7 +289,7 @@ STRING *string_strdupz(const char *str) {
}
// statistics
string_stats_atomic_increment(active_references);
string_stats_atomic_increment(partition, active_references);
return string;
}
@ -285,6 +297,7 @@ STRING *string_strdupz(const char *str) {
void string_freez(STRING *string) {
if(unlikely(!string)) return;
uint8_t partition = string_partition(string);
REFCOUNT refcount = string_entry_release(string);
#ifdef NETDATA_INTERNAL_CHECKS
@ -296,8 +309,8 @@ void string_freez(STRING *string) {
string_index_delete(string);
// statistics
string_stats_atomic_decrement(active_references);
string_stats_atomic_increment(releases);
string_stats_atomic_decrement(partition, active_references);
string_stats_atomic_increment(partition, releases);
}
inline size_t string_strlen(STRING *string) {
@ -405,6 +418,54 @@ static void string_unittest_free_char_pp(char **pp, size_t entries) {
freez(pp);
}
static long unittest_string_entries(void) {
long entries = 0;
for(size_t p = 0; p < STRING_PARTITIONS ;p++)
entries += string_base[p].entries;
return entries;
}
#ifdef NETDATA_INTERNAL_CHECKS
static size_t unittest_string_found_deleted_on_search(void) {
size_t entries = 0;
for(size_t p = 0; p < STRING_PARTITIONS ;p++)
entries += string_base[p].found_deleted_on_search;
return entries;
}
static size_t unittest_string_found_available_on_search(void) {
size_t entries = 0;
for(size_t p = 0; p < STRING_PARTITIONS ;p++)
entries += string_base[p].found_available_on_search;
return entries;
}
static size_t unittest_string_found_deleted_on_insert(void) {
size_t entries = 0;
for(size_t p = 0; p < STRING_PARTITIONS ;p++)
entries += string_base[p].found_deleted_on_insert;
return entries;
}
static size_t unittest_string_found_available_on_insert(void) {
size_t entries = 0;
for(size_t p = 0; p < STRING_PARTITIONS ;p++)
entries += string_base[p].found_available_on_insert;
return entries;
}
static size_t unittest_string_spins(void) {
size_t entries = 0;
for(size_t p = 0; p < STRING_PARTITIONS ;p++)
entries += string_base[p].spins;
return entries;
}
#endif // NETDATA_INTERNAL_CHECKS
int string_unittest(size_t entries) {
size_t errors = 0;
@ -413,7 +474,7 @@ int string_unittest(size_t entries) {
// check string
{
long int string_entries_starting = string_base.entries;
long entries_starting = unittest_string_entries();
fprintf(stderr, "\nChecking strings...\n");
@ -496,9 +557,10 @@ int string_unittest(size_t entries) {
freez(strings);
if(string_base.entries != string_entries_starting + 2) {
if(unittest_string_entries() != entries_starting + 2) {
errors++;
fprintf(stderr, "ERROR: strings dictionary should have %ld items but it has %ld\n", string_entries_starting + 2, string_base.entries);
fprintf(stderr, "ERROR: strings dictionary should have %ld items but it has %ld\n",
entries_starting + 2, unittest_string_entries());
}
else
fprintf(stderr, "OK: strings dictionary has 2 items\n");
@ -551,11 +613,11 @@ int string_unittest(size_t entries) {
};
#ifdef NETDATA_INTERNAL_CHECKS
size_t ofound_deleted_on_search = string_base.found_deleted_on_search,
ofound_available_on_search = string_base.found_available_on_search,
ofound_deleted_on_insert = string_base.found_deleted_on_insert,
ofound_available_on_insert = string_base.found_available_on_insert,
ospins = string_base.spins;
size_t ofound_deleted_on_search = unittest_string_found_deleted_on_search(),
ofound_available_on_search = unittest_string_found_available_on_search(),
ofound_deleted_on_insert = unittest_string_found_deleted_on_insert(),
ofound_available_on_insert = unittest_string_found_available_on_insert(),
ospins = unittest_string_spins();
#endif
size_t oinserts, odeletes, osearches, oentries, oreferences, omemory, oduplications, oreleases;
@ -592,11 +654,11 @@ int string_unittest(size_t entries) {
inserts - oinserts, deletes - odeletes, searches - osearches, sentries - oentries, references - oreferences, memory - omemory, duplications - oduplications, releases - oreleases);
#ifdef NETDATA_INTERNAL_CHECKS
size_t found_deleted_on_search = string_base.found_deleted_on_search,
found_available_on_search = string_base.found_available_on_search,
found_deleted_on_insert = string_base.found_deleted_on_insert,
found_available_on_insert = string_base.found_available_on_insert,
spins = string_base.spins;
size_t found_deleted_on_search = unittest_string_found_deleted_on_search(),
found_available_on_search = unittest_string_found_available_on_search(),
found_deleted_on_insert = unittest_string_found_deleted_on_insert(),
found_available_on_insert = unittest_string_found_available_on_insert(),
spins = unittest_string_spins();
fprintf(stderr, "on insert: %zu ok + %zu deleted\non search: %zu ok + %zu deleted\nspins: %zu\n",
found_available_on_insert - ofound_available_on_insert,

View file

@ -8,6 +8,10 @@
#define REGISTRY_STATUS_FAILED "failed"
#define REGISTRY_STATUS_DISABLED "disabled"
bool registry_is_valid_url(const char *url) {
return url && (*url == 'h' || *url == '*');
}
// ----------------------------------------------------------------------------
// REGISTRY concurrency locking
@ -23,19 +27,19 @@ static inline void registry_unlock(void) {
// COOKIES
static void registry_set_cookie(struct web_client *w, const char *guid) {
char edate[100];
char e_date[100];
time_t et = now_realtime_sec() + registry.persons_expiration;
struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
struct tm e_tm_buf, *etm = gmtime_r(&et, &e_tm_buf);
strftime(e_date, sizeof(e_date), "%a, %d %b %Y %H:%M:%S %Z", etm);
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s\r\n", guid, edate);
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s\r\n", guid, e_date);
if(registry.enable_cookies_samesite_secure)
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; SameSite=None; Secure\r\n", guid, edate);
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; SameSite=None; Secure\r\n", guid, e_date);
if(registry.registry_domain && *registry.registry_domain) {
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s\r\n", guid, edate, registry.registry_domain);
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s\r\n", guid, e_date, registry.registry_domain);
if(registry.enable_cookies_samesite_secure)
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s; SameSite=None; Secure\r\n", guid, edate, registry.registry_domain);
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s; SameSite=None; Secure\r\n", guid, e_date, registry.registry_domain);
}
w->response.has_cookies = true;
@ -84,37 +88,41 @@ struct registry_json_walk_person_urls_callback {
int count;
};
static STRING *asterisks = NULL;
// callback for rendering PERSON_URLs
static int registry_json_person_url_callback(void *entry, void *data) {
REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
static int registry_json_person_url_callback(REGISTRY_PERSON_URL *pu, struct registry_json_walk_person_urls_callback *c) {
if(unlikely(!asterisks))
asterisks = string_strdupz("***");
struct web_client *w = c->w;
if (!strcmp(pu->url->url,"***")) return 0;
if (pu->url == asterisks) return 0;
buffer_json_add_array_item_array(w->response.data);
buffer_json_add_array_item_string(w->response.data, pu->machine->guid);
buffer_json_add_array_item_string(w->response.data, pu->url->url);
buffer_json_add_array_item_string(w->response.data, string2str(pu->url));
buffer_json_add_array_item_uint64(w->response.data, pu->last_t * (uint64_t) 1000);
buffer_json_add_array_item_uint64(w->response.data, pu->usages);
buffer_json_add_array_item_string(w->response.data, pu->machine_name);
buffer_json_add_array_item_string(w->response.data, string2str(pu->machine_name));
buffer_json_array_close(w->response.data);
return 1;
}
// callback for rendering MACHINE_URLs
static int registry_json_machine_url_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) {
REGISTRY_MACHINE_URL *mu = (REGISTRY_MACHINE_URL *)entry;
struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
static int registry_json_machine_url_callback(REGISTRY_MACHINE_URL *mu, struct registry_json_walk_person_urls_callback *c) {
if(unlikely(!asterisks))
asterisks = string_strdupz("***");
struct web_client *w = c->w;
REGISTRY_MACHINE *m = c->m;
if (!strcmp(mu->url->url,"***")) return 0;
if (mu->url == asterisks) return 0;
buffer_json_add_array_item_array(w->response.data);
buffer_json_add_array_item_string(w->response.data, m->guid);
buffer_json_add_array_item_string(w->response.data, mu->url->url);
buffer_json_add_array_item_string(w->response.data, string2str(mu->url));
buffer_json_add_array_item_uint64(w->response.data, mu->last_t * (uint64_t) 1000);
buffer_json_add_array_item_uint64(w->response.data, mu->usages);
buffer_json_array_close(w->response.data);
@ -130,9 +138,7 @@ struct registry_person_url_callback_verify_machine_exists_data {
int count;
};
static inline int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
static inline int registry_person_url_callback_verify_machine_exists(REGISTRY_PERSON_URL *pu, struct registry_person_url_callback_verify_machine_exists_data *d) {
REGISTRY_MACHINE *m = d->m;
if(pu->machine == m)
@ -161,34 +167,54 @@ int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
buffer_json_member_add_string(w->response.data, "cloud_base_url", registry.cloud_base_url);
buffer_json_member_add_boolean(w->response.data, "anonymous_statistics", netdata_anonymous_statistics_enabled);
buffer_json_member_add_array(w->response.data, "nodes");
RRDHOST *h;
dfe_start_read(rrdhost_root_index, h) {
buffer_json_add_array_item_object(w->response.data);
buffer_json_member_add_string(w->response.data, "machine_guid", h->machine_guid);
buffer_json_member_add_string(w->response.data, "hostname", rrdhost_registry_hostname(h));
buffer_json_object_close(w->response.data);
}
dfe_done(h);
buffer_json_array_close(w->response.data);
registry_json_footer(w);
return HTTP_RESP_OK;
}
// ----------------------------------------------------------------------------
//public ACCESS request
#define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please"
// public ACCESS request
// the main method for registering an access
int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
if(unlikely(!registry.enabled))
return registry_json_disabled(host, w, "access");
if(!registry_is_valid_url(url)) {
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Invalid URL given in the request");
return HTTP_RESP_BAD_REQUEST;
}
// ------------------------------------------------------------------------
// verify the browser supports cookies
// verify the browser supports cookies or the bearer
if(registry.verify_cookies_redirects > 0 && !person_guid[0]) {
registry_request_access(REGISTRY_VERIFY_COOKIES_GUID, machine_guid, url, name, when);
buffer_flush(w->response.data);
registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID);
w->response.data->content_type = CT_APPLICATION_JSON;
registry_json_header(host, w, "access", REGISTRY_STATUS_REDIRECT);
buffer_json_member_add_string(w->response.data, "person_guid", REGISTRY_VERIFY_COOKIES_GUID);
buffer_json_member_add_string(w->response.data, "registry", registry.registry_to_announce);
registry_json_footer(w);
return HTTP_RESP_OK;
}
if(unlikely(person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
if(unlikely(person_guid[0] && is_dummy_person(person_guid)))
// it passed the check - they gave us a different person_guid
// empty the dummy one, so that we will generate a new person_guid
person_guid[0] = '\0';
// ------------------------------------------------------------------------
@ -212,7 +238,8 @@ int registry_request_access_json(RRDHOST *host, struct web_client *w, char *pers
buffer_json_member_add_array(w->response.data, "urls");
struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
avl_traverse(&p->person_urls, registry_json_person_url_callback, &c);
for(REGISTRY_PERSON_URL *pu = p->person_urls; pu ;pu = pu->next)
registry_json_person_url_callback(pu, &c);
buffer_json_array_close(w->response.data); // urls
registry_json_footer(w);
@ -228,6 +255,12 @@ int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *pers
if(!registry.enabled)
return registry_json_disabled(host, w, "delete");
if(!registry_is_valid_url(url)) {
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Invalid URL given in the request");
return HTTP_RESP_BAD_REQUEST;
}
registry_lock();
REGISTRY_PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
@ -253,6 +286,12 @@ int registry_request_search_json(RRDHOST *host, struct web_client *w, char *pers
if(!registry.enabled)
return registry_json_disabled(host, w, "search");
if(!registry_is_valid_url(url)) {
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Invalid URL given in the request");
return HTTP_RESP_BAD_REQUEST;
}
registry_lock();
REGISTRY_MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
@ -267,7 +306,10 @@ int registry_request_search_json(RRDHOST *host, struct web_client *w, char *pers
buffer_json_member_add_array(w->response.data, "urls");
struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
dictionary_walkthrough_read(m->machine_urls, registry_json_machine_url_callback, &c);
for(REGISTRY_MACHINE_URL *mu = m->machine_urls; mu ; mu = mu->next)
registry_json_machine_url_callback(mu, &c);
buffer_json_array_close(w->response.data);
registry_json_footer(w);
@ -279,12 +321,15 @@ int registry_request_search_json(RRDHOST *host, struct web_client *w, char *pers
// SWITCH REQUEST
// the main method for switching user identity
int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) {
int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url __maybe_unused, char *new_person_guid, time_t when __maybe_unused) {
if(!registry.enabled)
return registry_json_disabled(host, w, "switch");
(void)url;
(void)when;
if(!registry_is_valid_url(url)) {
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Invalid URL given in the request");
return HTTP_RESP_BAD_REQUEST;
}
registry_lock();
@ -315,7 +360,9 @@ int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *pers
struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };
// verify the old person has access to this machine
avl_traverse(&op->person_urls, registry_person_url_callback_verify_machine_exists, &data);
for(REGISTRY_PERSON_URL *pu = op->person_urls; pu ;pu = pu->next)
registry_person_url_callback_verify_machine_exists(pu, &data);
if(!data.count) {
registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
registry_json_footer(w);
@ -325,7 +372,9 @@ int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *pers
// verify the new person has access to this machine
data.count = 0;
avl_traverse(&np->person_urls, registry_person_url_callback_verify_machine_exists, &data);
for(REGISTRY_PERSON_URL *pu = np->person_urls; pu ;pu = pu->next)
registry_person_url_callback_verify_machine_exists(pu, &data);
if(!data.count) {
registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
registry_json_footer(w);
@ -396,14 +445,12 @@ void registry_statistics(void) {
rrddim_add(stc, "persons", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stc, "machines", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stc, "urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stc, "persons_urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stc, "machines_urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
}
rrddim_set(stc, "persons", (collected_number)registry.persons_count);
rrddim_set(stc, "machines", (collected_number)registry.machines_count);
rrddim_set(stc, "urls", (collected_number)registry.urls_count);
rrddim_set(stc, "persons_urls", (collected_number)registry.persons_urls_count);
rrddim_set(stc, "machines_urls", (collected_number)registry.machines_urls_count);
rrdset_done(stc);
@ -428,15 +475,21 @@ void registry_statistics(void) {
rrddim_add(stm, "persons", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stm, "machines", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stm, "urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stm, "persons_urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
rrddim_add(stm, "machines_urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
}
rrddim_set(stm, "persons", (collected_number)registry.persons_memory + dictionary_stats_for_registry(registry.persons));
rrddim_set(stm, "machines", (collected_number)registry.machines_memory + dictionary_stats_for_registry(registry.machines));
rrddim_set(stm, "urls", (collected_number)registry.urls_memory);
rrddim_set(stm, "persons_urls", (collected_number)registry.persons_urls_memory);
rrddim_set(stm, "machines_urls", (collected_number)registry.machines_urls_memory);
struct aral_statistics *p_aral_stats = aral_statistics(registry.persons_aral);
rrddim_set(stm, "persons", (collected_number)p_aral_stats->structures.allocated_bytes + (collected_number)p_aral_stats->malloc.allocated_bytes);
struct aral_statistics *m_aral_stats = aral_statistics(registry.machines_aral);
rrddim_set(stm, "machines", (collected_number)m_aral_stats->structures.allocated_bytes + (collected_number)m_aral_stats->malloc.allocated_bytes);
struct aral_statistics *pu_aral_stats = aral_statistics(registry.person_urls_aral);
rrddim_set(stm, "persons_urls", (collected_number)pu_aral_stats->structures.allocated_bytes + (collected_number)pu_aral_stats->malloc.allocated_bytes);
struct aral_statistics *mu_aral_stats = aral_statistics(registry.machine_urls_aral);
rrddim_set(stm, "machines_urls", (collected_number)mu_aral_stats->structures.allocated_bytes + (collected_number)mu_aral_stats->malloc.allocated_bytes);
rrdset_done(stm);
}

View file

@ -11,18 +11,15 @@ int registry_db_should_be_saved(void) {
// ----------------------------------------------------------------------------
// INTERNAL FUNCTIONS FOR SAVING REGISTRY OBJECTS
static int registry_machine_save_url(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *file) {
REGISTRY_MACHINE_URL *mu = entry;
FILE *fp = file;
debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url);
static int registry_machine_save_url(REGISTRY_MACHINE_URL *mu, FILE *fp) {
debug(D_REGISTRY, "REGISTRY: registry_machine_save_url('%s')", string2str(mu->url));
int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
mu->first_t,
mu->last_t,
mu->usages,
mu->flags,
mu->url->url
string2str(mu->url)
);
// error handling is done at registry_db_save()
@ -35,7 +32,7 @@ static int registry_machine_save(const DICTIONARY_ITEM *item __maybe_unused, voi
REGISTRY_MACHINE *m = entry;
FILE *fp = file;
debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid);
debug(D_REGISTRY, "REGISTRY: registry_machine_save('%s')", m->guid);
int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
m->first_t,
@ -45,9 +42,13 @@ static int registry_machine_save(const DICTIONARY_ITEM *item __maybe_unused, voi
);
if(ret >= 0) {
int ret2 = dictionary_walkthrough_read(m->machine_urls, registry_machine_save_url, fp);
if(ret2 < 0) return ret2;
ret += ret2;
for(REGISTRY_MACHINE_URL *mu = m->machine_urls; mu ; mu = mu->next) {
int rc = registry_machine_save_url(mu, fp);
if(rc < 0)
return rc;
ret += rc;
}
}
// error handling is done at registry_db_save()
@ -55,11 +56,8 @@ static int registry_machine_save(const DICTIONARY_ITEM *item __maybe_unused, voi
return ret;
}
static inline int registry_person_save_url(void *entry, void *file) {
REGISTRY_PERSON_URL *pu = entry;
FILE *fp = file;
debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
static inline int registry_person_save_url(REGISTRY_PERSON_URL *pu, FILE *fp) {
debug(D_REGISTRY, "REGISTRY: registry_person_save_url('%s')", string2str(pu->url));
int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
pu->first_t,
@ -67,8 +65,8 @@ static inline int registry_person_save_url(void *entry, void *file) {
pu->usages,
pu->flags,
pu->machine->guid,
pu->machine_name,
pu->url->url
string2str(pu->machine_name),
string2str(pu->url)
);
// error handling is done at registry_db_save()
@ -80,7 +78,7 @@ static inline int registry_person_save(const DICTIONARY_ITEM *item __maybe_unuse
REGISTRY_PERSON *p = entry;
FILE *fp = file;
debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid);
debug(D_REGISTRY, "REGISTRY: registry_person_save('%s')", p->guid);
int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
p->first_t,
@ -90,10 +88,13 @@ static inline int registry_person_save(const DICTIONARY_ITEM *item __maybe_unuse
);
if(ret >= 0) {
//int ret2 = dictionary_walkthrough_read(p->person_urls, registry_person_save_url, fp);
int ret2 = avl_traverse(&p->person_urls, registry_person_save_url, fp);
if (ret2 < 0) return ret2;
ret += ret2;
for(REGISTRY_PERSON_URL *pu = p->person_urls; pu ;pu = pu->next) {
int rc = registry_person_save_url(pu, fp);
if(rc < 0)
return rc;
else
ret += rc;
}
}
// error handling is done at registry_db_save()
@ -119,42 +120,42 @@ int registry_db_save(void) {
snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
debug(D_REGISTRY, "REGISTRY: Creating file '%s'", tmp_filename);
FILE *fp = fopen(tmp_filename, "w");
if(!fp) {
netdata_log_error("Registry: Cannot create file: %s", tmp_filename);
netdata_log_error("REGISTRY: Cannot create file: %s", tmp_filename);
error_log_limit_reset();
return -1;
}
// dictionary_walkthrough_read() has its own locking, so this is safe to do
debug(D_REGISTRY, "Saving all machines");
debug(D_REGISTRY, "REGISTRY: saving all machines");
int bytes1 = dictionary_walkthrough_read(registry.machines, registry_machine_save, fp);
if(bytes1 < 0) {
netdata_log_error("Registry: Cannot save registry machines - return value %d", bytes1);
netdata_log_error("REGISTRY: Cannot save registry machines - return value %d", bytes1);
fclose(fp);
error_log_limit_reset();
return bytes1;
}
debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
debug(D_REGISTRY, "REGISTRY: saving machines took %d bytes", bytes1);
debug(D_REGISTRY, "Saving all persons");
int bytes2 = dictionary_walkthrough_read(registry.persons, registry_person_save, fp);
if(bytes2 < 0) {
netdata_log_error("Registry: Cannot save registry persons - return value %d", bytes2);
netdata_log_error("REGISTRY: Cannot save registry persons - return value %d", bytes2);
fclose(fp);
error_log_limit_reset();
return bytes2;
}
debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
debug(D_REGISTRY, "REGISTRY: saving persons took %d bytes", bytes2);
// save the totals
fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
registry.persons_count,
registry.machines_count,
registry.usages_count + 1, // this is required - it is lost on db rotation
registry.urls_count,
0LLU, //registry.urls_count,
registry.persons_urls_count,
registry.machines_urls_count
);
@ -164,36 +165,36 @@ int registry_db_save(void) {
errno = 0;
// remove the .old db
debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename);
debug(D_REGISTRY, "REGISTRY: Removing old db '%s'", old_filename);
if(unlink(old_filename) == -1 && errno != ENOENT)
netdata_log_error("Registry: cannot remove old registry file '%s'", old_filename);
netdata_log_error("REGISTRY: cannot remove old registry file '%s'", old_filename);
// rename the db to .old
debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
debug(D_REGISTRY, "REGISTRY: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
netdata_log_error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", registry.db_filename, old_filename);
netdata_log_error("REGISTRY: cannot move file '%s' to '%s'. Saving registry DB failed!", registry.db_filename, old_filename);
else {
// remove the database (it is saved in .old)
debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename);
debug(D_REGISTRY, "REGISTRY: removing db '%s'", registry.db_filename);
if (unlink(registry.db_filename) == -1 && errno != ENOENT)
netdata_log_error("Registry: cannot remove old registry file '%s'", registry.db_filename);
netdata_log_error("REGISTRY: cannot remove old registry file '%s'", registry.db_filename);
// move the .tmp to make it active
debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
debug(D_REGISTRY, "REGISTRY: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
if (link(tmp_filename, registry.db_filename) == -1) {
netdata_log_error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
netdata_log_error("REGISTRY: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
registry.db_filename);
// move the .old back
debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
debug(D_REGISTRY, "REGISTRY: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
if(link(old_filename, registry.db_filename) == -1)
netdata_log_error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
netdata_log_error("REGISTRY: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
}
else {
debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename);
debug(D_REGISTRY, "REGISTRY: removing tmp db '%s'", tmp_filename);
if(unlink(tmp_filename) == -1)
netdata_log_error("Registry: cannot remove tmp registry file '%s'", tmp_filename);
netdata_log_error("REGISTRY: cannot remove tmp registry file '%s'", tmp_filename);
// it has been moved successfully
// discard the current registry log
@ -215,75 +216,33 @@ size_t registry_db_load(void) {
char *s, buf[4096 + 1];
REGISTRY_PERSON *p = NULL;
REGISTRY_MACHINE *m = NULL;
REGISTRY_URL *u = NULL;
STRING *u = NULL;
size_t line = 0;
debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename);
debug(D_REGISTRY, "REGISTRY: loading active db from: '%s'", registry.db_filename);
FILE *fp = fopen(registry.db_filename, "r");
if(!fp) {
netdata_log_error("Registry: cannot open registry file: '%s'", registry.db_filename);
netdata_log_error("REGISTRY: cannot open registry file: '%s'", registry.db_filename);
return 0;
}
REGISTRY_MACHINE_URL *mu;
size_t len = 0;
buf[4096] = '\0';
while((s = fgets_trim_len(buf, 4096, fp, &len))) {
line++;
debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s);
debug(D_REGISTRY, "REGISTRY: read line %zu to length %zu: %s", line, len, s);
switch(*s) {
case 'T': // totals
if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) {
netdata_log_error("Registry totals line %zu is wrong (len = %zu).", line, len);
continue;
}
registry.persons_count = strtoull(&s[2], NULL, 16);
registry.machines_count = strtoull(&s[19], NULL, 16);
registry.usages_count = strtoull(&s[36], NULL, 16);
registry.urls_count = strtoull(&s[53], NULL, 16);
registry.persons_urls_count = strtoull(&s[70], NULL, 16);
registry.machines_urls_count = strtoull(&s[87], NULL, 16);
break;
case 'P': // person
m = NULL;
// verify it is valid
if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
netdata_log_error("Registry person line %zu is wrong (len = %zu).", line, len);
continue;
}
s[1] = s[10] = s[19] = s[28] = '\0';
p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16));
p->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
p->usages = (uint32_t)strtoul(&s[20], NULL, 16);
debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
break;
case 'M': // machine
p = NULL;
// verify it is valid
if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
netdata_log_error("Registry person line %zu is wrong (len = %zu).", line, len);
continue;
}
s[1] = s[10] = s[19] = s[28] = '\0';
m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16));
m->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
m->usages = (uint32_t)strtoul(&s[20], NULL, 16);
debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
break;
case 'U': // person URL
if(unlikely(!p)) {
netdata_log_error("Registry: ignoring line %zu, no person loaded: %s", line, s);
netdata_log_error("REGISTRY: ignoring line %zu, no person loaded: %s", line, s);
continue;
}
// verify it is valid
if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
netdata_log_error("Registry person URL line %zu is wrong (len = %zu).", line, len);
netdata_log_error("REGISTRY: person URL line %zu is wrong (len = %zu).", line, len);
continue;
}
@ -293,51 +252,125 @@ size_t registry_db_load(void) {
char *url = &s[69];
while(*url && *url != '\t') url++;
if(!*url) {
netdata_log_error("Registry person URL line %zu does not have a url.", line);
netdata_log_error("REGISTRY: person URL line %zu does not have a url.", line);
continue;
}
*url++ = '\0';
// u = registry_url_allocate_nolock(url, strlen(url));
u = registry_url_get(url, strlen(url));
if(*url != 'h' && *url != '*') {
netdata_log_error("REGISTRY: person URL line %zu does not have a valid url: %s", line, url);
continue;
}
time_t first_t = strtoul(&s[2], NULL, 16);
u = string_strdupz(url);
time_t first_t = (time_t)strtoul(&s[2], NULL, 16);
m = registry_machine_find(&s[32]);
if(!m) m = registry_machine_allocate(&s[32], first_t);
REGISTRY_PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
mu = registry_machine_url_find(m, u);
if(!mu) {
netdata_log_error("REGISTRY: person URL line %zu was not linked to the machine it refers to", line);
mu = registry_machine_url_allocate(m, u, first_t);
}
REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, u);
if(!pu)
pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
else
netdata_log_error("REGISTRY: person URL line %zu is duplicate, reusing the old one.", line);
pu->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
pu->usages = (uint32_t)strtoul(&s[20], NULL, 16);
pu->flags = (uint8_t)strtoul(&s[29], NULL, 16);
debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->machine_name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
debug(D_REGISTRY, "REGISTRY: loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x",
string2str(u), string2str(pu->machine_name), m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
string_freez(u);
break;
case 'P': // person
m = NULL;
// verify it is valid
if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
netdata_log_error("REGISTRY: person line %zu is wrong (len = %zu).", line, len);
continue;
}
s[1] = s[10] = s[19] = s[28] = '\0';
p = registry_person_allocate(&s[29], (time_t)strtoul(&s[2], NULL, 16));
p->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
p->usages = (uint32_t)strtoul(&s[20], NULL, 16);
debug(D_REGISTRY, "REGISTRY: loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
break;
case 'V': // machine URL
if(unlikely(!m)) {
netdata_log_error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
netdata_log_error("REGISTRY: ignoring line %zu, no machine loaded: %s", line, s);
continue;
}
// verify it is valid
if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
netdata_log_error("Registry person URL line %zu is wrong (len = %zu).", line, len);
netdata_log_error("REGISTRY: person URL line %zu is wrong (len = %zu).", line, len);
continue;
}
s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
// u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
u = registry_url_get(&s[32], strlen(&s[32]));
REGISTRY_MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
url = &s[32];
if(*url != 'h' && *url != '*') {
netdata_log_error("REGISTRY: machine URL line %zu does not have a valid url: %s", line, url);
continue;
}
u = string_strdupz(url);
mu = registry_machine_url_find(m, u);
if(!mu)
mu = registry_machine_url_allocate(m, u, (time_t)strtoul(&s[2], NULL, 16));
else
netdata_log_error("REGISTRY: machine URL line %zu is duplicate, reusing the old one.", line);
mu->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
mu->usages = (uint32_t)strtoul(&s[20], NULL, 16);
mu->flags = (uint8_t)strtoul(&s[29], NULL, 16);
debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x",
string2str(u), m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
string_freez(u);
break;
case 'M': // machine
p = NULL;
// verify it is valid
if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
netdata_log_error("REGISTRY: person line %zu is wrong (len = %zu).", line, len);
continue;
}
s[1] = s[10] = s[19] = s[28] = '\0';
m = registry_machine_allocate(&s[29], (time_t)strtoul(&s[2], NULL, 16));
m->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
m->usages = (uint32_t)strtoul(&s[20], NULL, 16);
debug(D_REGISTRY, "REGISTRY: loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
break;
case 'T': // totals
if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) {
netdata_log_error("REGISTRY: totals line %zu is wrong (len = %zu).", line, len);
continue;
}
registry.persons_count = strtoull(&s[2], NULL, 16);
registry.machines_count = strtoull(&s[19], NULL, 16);
registry.usages_count = strtoull(&s[36], NULL, 16);
registry.persons_urls_count = strtoull(&s[70], NULL, 16);
registry.machines_urls_count = strtoull(&s[87], NULL, 16);
break;
default:
netdata_log_error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
netdata_log_error("REGISTRY: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
break;
}
}

View file

@ -3,6 +3,63 @@
#include "daemon/common.h"
#include "registry_internals.h"
void registry_db_stats(void) {
size_t persons = 0;
size_t persons_urls = 0;
size_t max_urls_per_person = 0;
REGISTRY_PERSON *p;
dfe_start_read(registry.persons, p) {
persons++;
size_t urls = 0;
for(REGISTRY_PERSON_URL *pu = p->person_urls ; pu ;pu = pu->next)
urls++;
if(urls > max_urls_per_person)
max_urls_per_person = urls;
persons_urls += urls;
}
dfe_done(p);
size_t machines = 0;
size_t machines_urls = 0;
size_t max_urls_per_machine = 0;
REGISTRY_MACHINE *m;
dfe_start_read(registry.machines, m) {
machines++;
size_t urls = 0;
for(REGISTRY_MACHINE_URL *mu = m->machine_urls ; mu ;mu = mu->next)
urls++;
if(urls > max_urls_per_machine)
max_urls_per_machine = urls;
machines_urls += urls;
}
dfe_done(m);
netdata_log_info("REGISTRY: persons %zu, person_urls %zu, max_urls_per_person %zu, "
"machines %zu, machine_urls %zu, max_urls_per_machine %zu",
persons, persons_urls, max_urls_per_person,
machines, machines_urls, max_urls_per_machine);
}
void registry_generate_curl_urls(void) {
FILE *fp = fopen("/tmp/registry.curl", "w+");
REGISTRY_PERSON *p;
dfe_start_read(registry.persons, p) {
for(REGISTRY_PERSON_URL *pu = p->person_urls ; pu ;pu = pu->next) {
fprintf(fp, "do_curl '%s' '%s' '%s'\n", p->guid, pu->machine->guid, string2str(pu->url));
}
}
dfe_done(p);
fclose(fp);
}
int registry_init(void) {
char filename[FILENAME_MAX + 1];
@ -16,7 +73,7 @@ int registry_init(void) {
registry.enabled = 0;
}
// pathnames
// path names
snprintfz(filename, FILENAME_MAX, "%s/registry", netdata_configured_varlib_dir);
registry.pathname = config_get(CONFIG_SECTION_DIRECTORIES, "registry", filename);
if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST)
@ -57,73 +114,104 @@ int registry_init(void) {
config_set_number(CONFIG_SECTION_REGISTRY, "max URL name length", (long long)registry.max_name_length);
}
bool use_mmap = config_get_boolean(CONFIG_SECTION_REGISTRY, "use mmap", false);
// initialize entries counters
registry.persons_count = 0;
registry.machines_count = 0;
registry.usages_count = 0;
registry.urls_count = 0;
registry.persons_urls_count = 0;
registry.machines_urls_count = 0;
// initialize memory counters
registry.persons_memory = 0;
registry.machines_memory = 0;
registry.urls_memory = 0;
registry.persons_urls_memory = 0;
registry.machines_urls_memory = 0;
// initialize locks
netdata_mutex_init(&registry.lock);
// create dictionaries
registry.persons = dictionary_create(REGISTRY_DICTIONARY_OPTIONS);
registry.machines = dictionary_create(REGISTRY_DICTIONARY_OPTIONS);
avl_init(&registry.registry_urls_root_index, registry_url_compare);
// load the registry database
if(registry.enabled) {
// create dictionaries
registry.persons = dictionary_create(REGISTRY_DICTIONARY_OPTIONS);
registry.machines = dictionary_create(REGISTRY_DICTIONARY_OPTIONS);
// initialize the allocators
size_t min_page_size = 4 * 1024;
size_t max_page_size = 1024 * 1024;
if(use_mmap) {
min_page_size = 100 * 1024 * 1024;
max_page_size = 512 * 1024 * 1024;
}
registry.persons_aral = aral_create("registry_persons", sizeof(REGISTRY_PERSON),
min_page_size / sizeof(REGISTRY_PERSON), max_page_size,
&registry.aral_stats,
"registry_persons",
&netdata_configured_cache_dir,
use_mmap, true);
registry.machines_aral = aral_create("registry_machines", sizeof(REGISTRY_MACHINE),
min_page_size / sizeof(REGISTRY_MACHINE), max_page_size,
&registry.aral_stats,
"registry_machines",
&netdata_configured_cache_dir,
use_mmap, true);
registry.person_urls_aral = aral_create("registry_person_urls", sizeof(REGISTRY_PERSON_URL),
min_page_size / sizeof(REGISTRY_PERSON_URL), max_page_size,
&registry.aral_stats,
"registry_person_urls",
&netdata_configured_cache_dir,
use_mmap, true);
registry.machine_urls_aral = aral_create("registry_machine_urls", sizeof(REGISTRY_MACHINE_URL),
min_page_size / sizeof(REGISTRY_MACHINE_URL), max_page_size,
&registry.aral_stats,
"registry_machine_urls",
&netdata_configured_cache_dir,
use_mmap, true);
// disable cancelability to avoid enable/disable per item in the dictionary locks
netdata_thread_disable_cancelability();
registry_log_open();
registry_db_load();
registry_log_load();
if(unlikely(registry_db_should_be_saved()))
registry_db_save();
// registry_db_stats();
// registry_generate_curl_urls();
// exit(0);
netdata_thread_enable_cancelability();
}
return 0;
}
static int machine_urls_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) {
REGISTRY_MACHINE *m = (REGISTRY_MACHINE *)data;
(void)m;
REGISTRY_MACHINE_URL *mu = (REGISTRY_MACHINE_URL *)entry;
debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
registry_url_unlink(mu->url);
debug(D_REGISTRY, "Registry: freeing machine url");
freez(mu);
return 1;
}
static int machine_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data __maybe_unused) {
REGISTRY_MACHINE *m = (REGISTRY_MACHINE *)entry;
int ret = dictionary_walkthrough_read(m->machine_urls, machine_urls_delete_callback, m);
dictionary_destroy(m->machine_urls);
int count = 0;
while(m->machine_urls) {
registry_machine_url_unlink_from_machine_and_free(m, m->machine_urls);
count++;
}
freez(m);
return ret + 1;
return count + 1;
}
static int registry_person_del_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *d __maybe_unused) {
REGISTRY_PERSON *p = (REGISTRY_PERSON *)entry;
debug(D_REGISTRY, "Registry: registry_person_del('%s'): deleting person", p->guid);
while(p->person_urls.root)
registry_person_unlink_from_url(p, (REGISTRY_PERSON_URL *)p->person_urls.root);
while(p->person_urls)
registry_person_unlink_from_url(p, (REGISTRY_PERSON_URL *)p->person_urls);
//debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
//dictionary_del(registry.persons, p->guid);
@ -140,8 +228,19 @@ void registry_free(void) {
debug(D_REGISTRY, "Registry: destroying persons dictionary");
dictionary_walkthrough_read(registry.persons, registry_person_del_callback, NULL);
dictionary_destroy(registry.persons);
registry.persons = NULL;
debug(D_REGISTRY, "Registry: destroying machines dictionary");
dictionary_walkthrough_read(registry.machines, machine_delete_callback, NULL);
dictionary_destroy(registry.machines);
registry.machines = NULL;
aral_destroy(registry.persons_aral);
aral_destroy(registry.machines_aral);
aral_destroy(registry.person_urls_aral);
aral_destroy(registry.machine_urls_aral);
registry.persons_aral = NULL;
registry.machines_aral = NULL;
registry.person_urls_aral = NULL;
registry.machine_urls_aral = NULL;
}

View file

@ -80,7 +80,7 @@ static inline char *registry_fix_url(char *url, size_t *len) {
// HELPERS
// verify the person, the machine and the URL exist in our DB
REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, REGISTRY_PERSON **pp, REGISTRY_MACHINE **mm) {
REGISTRY_PERSON_URL *registry_verify_request(const char *person_guid, char *machine_guid, char *url, REGISTRY_PERSON **pp, REGISTRY_MACHINE **mm) {
char pbuf[GUID_LEN + 1], mbuf[GUID_LEN + 1];
if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
@ -121,7 +121,10 @@ REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_gu
}
if(pp) *pp = p;
REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, url);
STRING *u = string_strdupz(url);
REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, u);
string_freez(u);
if(!pu) {
netdata_log_info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
return NULL;
@ -138,23 +141,28 @@ REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_gu
// ----------------------------------------------------------------------------
// REGISTRY REQUESTS
REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
REGISTRY_PERSON *registry_request_access(const char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
REGISTRY_MACHINE *m = registry_machine_get(machine_guid, when);
bool is_dummy = is_dummy_person(person_guid);
REGISTRY_MACHINE *m = registry_machine_find_or_create(machine_guid, when, is_dummy);
if(!m) return NULL;
REGISTRY_PERSON *p = registry_person_find_or_create(person_guid, when, is_dummy);
// make sure the name is valid
size_t namelen;
name = registry_fix_machine_name(name, &namelen);
size_t name_len;
name = registry_fix_machine_name(name, &name_len);
size_t urllen;
url = registry_fix_url(url, &urllen);
size_t url_len;
url = registry_fix_url(url, &url_len);
REGISTRY_PERSON *p = registry_person_get(person_guid, when);
STRING *u = string_strdupz(url);
if(!is_dummy)
registry_person_link_to_url(p, m, u, name, name_len, when);
REGISTRY_URL *u = registry_url_get(url, urllen);
registry_person_link_to_url(p, m, u, name, namelen, when);
registry_machine_link_to_url(m, u, when);
registry_log('A', p, m, u, name);
@ -164,7 +172,7 @@ REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid,
return p;
}
REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
REGISTRY_PERSON *registry_request_delete(const char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
(void) when;
REGISTRY_PERSON *p = NULL;
@ -184,14 +192,17 @@ REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid,
}
*/
REGISTRY_PERSON_URL *dpu = registry_person_url_index_find(p, delete_url);
STRING *d_url = string_strdupz(delete_url);
REGISTRY_PERSON_URL *dpu = registry_person_url_index_find(p, d_url);
string_freez(d_url);
if(!dpu) {
netdata_log_info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid
, m->guid, pu->url->url, delete_url);
, m->guid, string2str(pu->url), delete_url);
return NULL;
}
registry_log('D', p, m, pu->url, dpu->url->url);
registry_log('D', p, m, pu->url, string2str(dpu->url));
registry_person_unlink_from_url(p, dpu);
return p;
@ -218,7 +229,7 @@ static int machine_request_callback(void *entry, void *data) {
return 0; // continue
}
REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
(void)when;
char mbuf[GUID_LEN + 1];
@ -230,7 +241,7 @@ REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid
// make sure the machine GUID is valid
if(regenerate_guid(request_machine, mbuf) == -1) {
netdata_log_info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
netdata_log_info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, string2str(pu->url), request_machine);
return NULL;
}
request_machine = mbuf;
@ -238,7 +249,7 @@ REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid
// make sure the machine exists
m = registry_machine_find(request_machine);
if(!m) {
netdata_log_info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, pu->url->url, request_machine);
netdata_log_info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, string2str(pu->url), request_machine);
return NULL;
}
@ -250,7 +261,8 @@ REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid
struct machine_request_callback_data rdata = { m, NULL };
// request a walk through on the dictionary
avl_traverse(&p->person_urls, machine_request_callback, &rdata);
for(pu = p->person_urls; pu ;pu = pu->next)
machine_request_callback(pu, &rdata);
if(rdata.result)
return m;

View file

@ -10,28 +10,24 @@
#define REGISTRY_DICTIONARY_OPTIONS (DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_SINGLE_THREADED)
#define REGISTRY_VERIFY_COOKIES_GUID "11111111-2222-3333-4444-555555555555"
#define is_dummy_person(person_guid) (strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID) == 0)
// ----------------------------------------------------------------------------
// COMMON structures
struct registry {
int enabled;
netdata_mutex_t lock;
// entries counters / statistics
unsigned long long persons_count;
unsigned long long machines_count;
unsigned long long usages_count;
unsigned long long urls_count;
unsigned long long persons_urls_count;
unsigned long long machines_urls_count;
unsigned long long log_count;
// memory counters / statistics
unsigned long long persons_memory;
unsigned long long machines_memory;
unsigned long long urls_memory;
unsigned long long persons_urls_memory;
unsigned long long machines_urls_memory;
// configuration
unsigned long long save_registry_every_entries;
char *registry_domain;
@ -50,7 +46,6 @@ struct registry {
char *db_filename;
char *log_filename;
char *machine_guid_filename;
char *session_key_filename;
// open files
FILE *log_fp;
@ -59,12 +54,15 @@ struct registry {
DICTIONARY *persons; // dictionary of REGISTRY_PERSON *, with key the REGISTRY_PERSON.guid
DICTIONARY *machines; // dictionary of REGISTRY_MACHINE *, with key the REGISTRY_MACHINE.guid
avl_tree_type registry_urls_root_index;
ARAL *persons_aral;
ARAL *machines_aral;
netdata_mutex_t lock;
ARAL *person_urls_aral;
ARAL *machine_urls_aral;
struct aral_statistics aral_stats;
};
#include "registry_url.h"
#include "registry_machine.h"
#include "registry_person.h"
#include "registry.h"
@ -72,12 +70,12 @@ struct registry {
extern struct registry registry;
// REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c)
REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when);
REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
REGISTRY_PERSON *registry_request_access(const char *person_guid, char *machine_guid, char *url, char *name, time_t when);
REGISTRY_PERSON *registry_request_delete(const char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
// REGISTRY LOG (in registry_log.c)
void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name);
void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *u, const char *name);
int registry_log_open(void);
void registry_log_close(void);
void registry_log_recreate(void);

View file

@ -3,7 +3,7 @@
#include "daemon/common.h"
#include "registry_internals.h"
void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name) {
void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *u, const char *name) {
if(likely(registry.log_fp)) {
if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n",
action,
@ -11,7 +11,7 @@ void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY
p->guid,
m->guid,
name,
u->url) < 0))
string2str(u)) < 0))
netdata_log_error("Registry: failed to save log. Registry data may be lost in case of abnormal restart.");
// we increase the counter even on failures
@ -94,7 +94,7 @@ ssize_t registry_log_load(void) {
s[1] = s[10] = s[47] = s[84] = '\0';
// get the variables
time_t when = strtoul(&s[2], NULL, 16);
time_t when = (time_t)strtoul(&s[2], NULL, 16);
char *person_guid = &s[11];
char *machine_guid = &s[48];
char *name = &s[85];

View file

@ -7,52 +7,58 @@
// MACHINE
REGISTRY_MACHINE *registry_machine_find(const char *machine_guid) {
debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid);
debug(D_REGISTRY, "REGISTRY: registry_machine_find('%s')", machine_guid);
return dictionary_get(registry.machines, machine_guid);
}
REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) {
debug(D_REGISTRY, "registry_machine_url_allocate('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(REGISTRY_MACHINE_URL));
REGISTRY_MACHINE_URL *registry_machine_url_find(REGISTRY_MACHINE *m, STRING *url) {
REGISTRY_MACHINE_URL *mu;
REGISTRY_MACHINE_URL *mu = mallocz(sizeof(REGISTRY_MACHINE_URL));
for(mu = m->machine_urls; mu ;mu = mu->next)
if(mu->url == url)
break;
return mu;
}
void registry_machine_url_unlink_from_machine_and_free(REGISTRY_MACHINE *m, REGISTRY_MACHINE_URL *mu) {
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(m->machine_urls, mu, prev, next);
string_freez(mu->url);
aral_freez(registry.machine_urls_aral, mu);
}
REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, STRING *u, time_t when) {
debug(D_REGISTRY, "REGISTRY: registry_machine_url_allocate('%s', '%s'): allocating %zu bytes", m->guid, string2str(u), sizeof(REGISTRY_MACHINE_URL));
REGISTRY_MACHINE_URL *mu = aral_mallocz(registry.machine_urls_aral);
mu->first_t = mu->last_t = (uint32_t)when;
mu->usages = 1;
mu->url = u;
mu->url = string_dup(u);
mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
registry.machines_urls_memory += sizeof(REGISTRY_MACHINE_URL);
debug(D_REGISTRY, "REGISTRY: registry_machine_url_allocate('%s', '%s'): indexing URL in machine", m->guid, string2str(u));
debug(D_REGISTRY, "registry_machine_url_allocate('%s', '%s'): indexing URL in machine", m->guid, u->url);
registry.machines_urls_memory -= dictionary_stats_for_registry(m->machine_urls);
dictionary_set(m->machine_urls, u->url, mu, sizeof(REGISTRY_MACHINE_URL));
registry.machines_urls_memory += dictionary_stats_for_registry(m->machine_urls);
registry_url_link(u);
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(m->machine_urls, mu, prev, next);
return mu;
}
REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) {
debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(REGISTRY_MACHINE));
debug(D_REGISTRY, "REGISTRY: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(REGISTRY_MACHINE));
REGISTRY_MACHINE *m = mallocz(sizeof(REGISTRY_MACHINE));
REGISTRY_MACHINE *m = aral_mallocz(registry.machines_aral);
strncpyz(m->guid, machine_guid, GUID_LEN);
debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
m->machine_urls = dictionary_create(REGISTRY_DICTIONARY_OPTIONS);
m->machine_urls = NULL;
m->first_t = m->last_t = (uint32_t)when;
m->usages = 0;
registry.machines_memory += sizeof(REGISTRY_MACHINE);
registry.machines_count++;
registry.machines_urls_memory -= dictionary_stats_for_registry(m->machine_urls);
dictionary_set(registry.machines, m->guid, m, sizeof(REGISTRY_MACHINE));
registry.machines_urls_memory += dictionary_stats_for_registry(m->machine_urls);
return m;
}
@ -60,14 +66,14 @@ REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t whe
// 1. validate machine GUID
// 2. if it is valid, find it or create it and return it
// 3. if it is not valid, return NULL
REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
REGISTRY_MACHINE *registry_machine_find_or_create(const char *machine_guid, time_t when, bool is_dummy __maybe_unused) {
REGISTRY_MACHINE *m = NULL;
if(likely(machine_guid && *machine_guid)) {
// validate it is a GUID
char buf[GUID_LEN + 1];
if(unlikely(regenerate_guid(machine_guid, buf) == -1))
netdata_log_info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
netdata_log_info("REGISTRY: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
else {
machine_guid = buf;
m = registry_machine_find(machine_guid);
@ -82,17 +88,17 @@ REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
// ----------------------------------------------------------------------------
// LINKING OF OBJECTS
REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) {
debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): searching for URL in machine", m->guid, u->url);
REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, STRING *url, time_t when) {
debug(D_REGISTRY, "REGISTRY: registry_machine_link_to_url('%s', '%s'): searching for URL in machine", m->guid, string2str(url));
REGISTRY_MACHINE_URL *mu = dictionary_get(m->machine_urls, u->url);
REGISTRY_MACHINE_URL *mu = registry_machine_url_find(m, url);
if(!mu) {
debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): not found", m->guid, u->url);
mu = registry_machine_url_allocate(m, u, when);
debug(D_REGISTRY, "REGISTRY: registry_machine_link_to_url('%s', '%s'): not found", m->guid, string2str(url));
mu = registry_machine_url_allocate(m, url, when);
registry.machines_urls_count++;
}
else {
debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): found", m->guid, u->url);
debug(D_REGISTRY, "REGISTRY: registry_machine_link_to_url('%s', '%s'): found", m->guid, string2str(url));
mu->usages++;
if(likely(mu->last_t < (uint32_t)when)) mu->last_t = (uint32_t)when;
}
@ -101,7 +107,7 @@ REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY
if(likely(m->last_t < (uint32_t)when)) m->last_t = (uint32_t)when;
if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, u->url);
debug(D_REGISTRY, "REGISTRY: registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, string2str(url));
mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
}

View file

@ -10,13 +10,15 @@
// For each MACHINE-URL pair we keep this
struct registry_machine_url {
REGISTRY_URL *url; // de-duplicated URL
STRING *url; // de-duplicated URL
uint8_t flags;
uint32_t first_t; // the first time we saw this
uint32_t last_t; // the last time we saw this
uint32_t usages; // how many times this has been accessed
struct registry_machine_url *prev, *next;
};
typedef struct registry_machine_url REGISTRY_MACHINE_URL;
@ -26,7 +28,7 @@ struct registry_machine {
uint32_t links; // the number of REGISTRY_PERSON_URL linked to this machine
DICTIONARY *machine_urls; // MACHINE_URL *
REGISTRY_MACHINE_URL *machine_urls; // MACHINE_URL *
uint32_t first_t; // the first time we saw this
uint32_t last_t; // the last time we saw this
@ -35,9 +37,12 @@ struct registry_machine {
typedef struct registry_machine REGISTRY_MACHINE;
REGISTRY_MACHINE *registry_machine_find(const char *machine_guid);
REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when);
REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, STRING *u, time_t when);
REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when);
REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when);
REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when);
REGISTRY_MACHINE *registry_machine_find_or_create(const char *machine_guid, time_t when, bool is_dummy);
REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, STRING *url, time_t when);
REGISTRY_MACHINE_URL *registry_machine_url_find(REGISTRY_MACHINE *m, STRING *url);
void registry_machine_url_unlink_from_machine_and_free(REGISTRY_MACHINE *m, REGISTRY_MACHINE_URL *mu);
#endif //NETDATA_REGISTRY_MACHINE_H

View file

@ -6,103 +6,78 @@
// ----------------------------------------------------------------------------
// PERSON_URL INDEX
int person_url_compare(void *a, void *b) {
register uint32_t hash1 = ((REGISTRY_PERSON_URL *)a)->url->hash;
register uint32_t hash2 = ((REGISTRY_PERSON_URL *)b)->url->hash;
inline REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, STRING *url) {
debug(D_REGISTRY, "Registry: registry_person_url_index_find('%s', '%s')", p->guid, string2str(url));
if(hash1 < hash2) return -1;
else if(hash1 > hash2) return 1;
else return strcmp(((REGISTRY_PERSON_URL *)a)->url->url, ((REGISTRY_PERSON_URL *)b)->url->url);
}
REGISTRY_PERSON_URL *pu;
for(pu = p->person_urls ; pu ;pu = pu->next)
if(pu->url == url)
break;
inline REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, const char *url) {
debug(D_REGISTRY, "Registry: registry_person_url_index_find('%s', '%s')", p->guid, url);
char buf[sizeof(REGISTRY_URL) + strlen(url)];
REGISTRY_URL *u = (REGISTRY_URL *)&buf;
strcpy(u->url, url);
u->hash = simple_hash(u->url);
REGISTRY_PERSON_URL tpu = { .url = u };
REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)avl_search(&p->person_urls, (void *)&tpu);
return pu;
}
inline REGISTRY_PERSON_URL *registry_person_url_index_add(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) {
debug(D_REGISTRY, "Registry: registry_person_url_index_add('%s', '%s')", p->guid, pu->url->url);
REGISTRY_PERSON_URL *tpu = (REGISTRY_PERSON_URL *)avl_insert(&(p->person_urls), (avl_t *)(pu));
if(tpu != pu)
netdata_log_error("Registry: registry_person_url_index_add('%s', '%s') already exists as '%s'", p->guid, pu->url->url, tpu->url->url);
return tpu;
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(p->person_urls, pu, prev, next);
return pu;
}
inline REGISTRY_PERSON_URL *registry_person_url_index_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) {
debug(D_REGISTRY, "Registry: registry_person_url_index_del('%s', '%s')", p->guid, pu->url->url);
REGISTRY_PERSON_URL *tpu = (REGISTRY_PERSON_URL *)avl_remove(&(p->person_urls), (avl_t *)(pu));
if(!tpu)
netdata_log_error("Registry: registry_person_url_index_del('%s', '%s') deleted nothing", p->guid, pu->url->url);
else if(tpu != pu)
netdata_log_error("Registry: registry_person_url_index_del('%s', '%s') deleted wrong URL '%s'", p->guid, pu->url->url, tpu->url->url);
return tpu;
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(p->person_urls, pu, prev, next);
return pu;
}
// ----------------------------------------------------------------------------
// PERSON_URL
REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) {
debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen);
REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *url, char *machine_name, size_t machine_name_len, time_t when) {
debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, string2str(url), sizeof(REGISTRY_PERSON_URL) + machine_name_len);
// protection from too big names
if(namelen > registry.max_name_length)
namelen = registry.max_name_length;
if(machine_name_len > registry.max_name_length)
machine_name_len = registry.max_name_length;
REGISTRY_PERSON_URL *pu = mallocz(sizeof(REGISTRY_PERSON_URL) + namelen);
REGISTRY_PERSON_URL *pu = aral_mallocz(registry.person_urls_aral);
// a simple strcpy() should do the job
// but I prefer to be safe, since the caller specified urllen
strncpyz(pu->machine_name, name, namelen);
// a simple strcpy() should do the job,
// but I prefer to be safe, since the caller specified name_len
pu->machine_name = string_strdupz(machine_name);
pu->machine = m;
pu->first_t = pu->last_t = (uint32_t)when;
pu->usages = 1;
pu->url = u;
pu->url = string_dup(url);
pu->flags = REGISTRY_URL_FLAGS_DEFAULT;
m->links++;
registry.persons_urls_memory += sizeof(REGISTRY_PERSON_URL) + namelen;
debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, string2str(url));
REGISTRY_PERSON_URL *tpu = registry_person_url_index_add(p, pu);
if(tpu != pu) {
netdata_log_error("Registry: Attempted to add duplicate person url '%s' with name '%s' to person '%s'", u->url, name, p->guid);
freez(pu);
netdata_log_error("Registry: Attempted to add duplicate person url '%s' with name '%s' to person '%s'", string2str(url), machine_name, p->guid);
string_freez(pu->machine_name);
string_freez(pu->url);
aral_freez(registry.person_urls_aral, pu);
pu = tpu;
}
else
registry_url_link(u);
return pu;
}
void registry_person_url_free(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) {
debug(D_REGISTRY, "registry_person_url_free('%s', '%s')", p->guid, pu->url->url);
void registry_person_url_deindex_and_free(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) {
debug(D_REGISTRY, "registry_person_url_deindex_and_free('%s', '%s')", p->guid, string2str(pu->url));
REGISTRY_PERSON_URL *tpu = registry_person_url_index_del(p, pu);
if(tpu) {
registry_url_unlink(tpu->url);
string_freez(tpu->machine_name);
string_freez(tpu->url);
tpu->machine->links--;
registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(tpu->machine_name);
freez(tpu);
aral_freez(registry.person_urls_aral, tpu);
}
}
// this function is needed to change the name of a PERSON_URL
REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when, REGISTRY_PERSON_URL *pu) {
debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen);
REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *url, char *machine_name, size_t machine_name_len, time_t when, REGISTRY_PERSON_URL *pu) {
debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, string2str(url), sizeof(REGISTRY_PERSON_URL) + machine_name_len);
// keep a backup
REGISTRY_PERSON_URL pu2 = {
@ -111,15 +86,15 @@ REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY
.usages = pu->usages,
.flags = pu->flags,
.machine = pu->machine,
.machine_name = ""
.machine_name = NULL
};
// remove the existing one from the index
registry_person_url_free(p, pu);
registry_person_url_deindex_and_free(p, pu);
pu = &pu2;
// allocate a new one
REGISTRY_PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
REGISTRY_PERSON_URL *tpu = registry_person_url_allocate(p, m, url, machine_name, machine_name_len, when);
tpu->first_t = pu->first_t;
tpu->last_t = pu->last_t;
tpu->usages = pu->usages;
@ -140,7 +115,7 @@ REGISTRY_PERSON *registry_person_find(const char *person_guid) {
REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) {
debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON));
REGISTRY_PERSON *p = mallocz(sizeof(REGISTRY_PERSON));
REGISTRY_PERSON *p = aral_mallocz(registry.persons_aral);
if(!person_guid) {
for(;;) {
uuid_t uuid;
@ -159,14 +134,11 @@ REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when)
else
strncpyz(p->guid, person_guid, GUID_LEN);
debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
avl_init(&p->person_urls, person_url_compare);
p->person_urls = NULL;
p->first_t = p->last_t = (uint32_t)when;
p->usages = 0;
registry.persons_memory += sizeof(REGISTRY_PERSON);
registry.persons_count++;
dictionary_set(registry.persons, p->guid, p, sizeof(REGISTRY_PERSON));
@ -178,23 +150,29 @@ REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when)
// 2. if it is valid, find it
// 3. if it is not valid, create a new one
// 4. return it
REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
debug(D_REGISTRY, "Registry: registry_person_get('%s'): creating dictionary of urls", person_guid);
REGISTRY_PERSON *registry_person_find_or_create(const char *person_guid, time_t when, bool is_dummy) {
debug(D_REGISTRY, "Registry: registry_person_find_or_create('%s'): creating dictionary of urls", person_guid);
char buf[GUID_LEN + 1];
REGISTRY_PERSON *p = NULL;
if(person_guid && *person_guid) {
char buf[GUID_LEN + 1];
// validate it is a GUID
if(unlikely(regenerate_guid(person_guid, buf) == -1))
if(unlikely(regenerate_guid(person_guid, buf) == -1)) {
netdata_log_info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
person_guid = NULL;
}
else {
person_guid = buf;
p = registry_person_find(person_guid);
if(!p && !is_dummy)
person_guid = NULL;
}
}
else
person_guid = NULL;
if(!p) p = registry_person_allocate(NULL, when);
if(!p) p = registry_person_allocate(person_guid, when);
return p;
}
@ -202,39 +180,39 @@ REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
// ----------------------------------------------------------------------------
// LINKING OF OBJECTS
REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) {
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *url, char *machine_name, size_t machine_name_len, time_t when) {
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, string2str(url));
REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, u->url);
REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, url);
if(!pu) {
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
pu = registry_person_url_allocate(p, m, u, name, namelen, when);
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, string2str(url));
pu = registry_person_url_allocate(p, m, url, machine_name, machine_name_len, when);
registry.persons_urls_count++;
}
else {
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, string2str(url));
pu->usages++;
if(likely(pu->last_t < (uint32_t)when)) pu->last_t = (uint32_t)when;
if(pu->machine != m) {
REGISTRY_MACHINE_URL *mu = dictionary_get(pu->machine->machine_urls, u->url);
REGISTRY_MACHINE_URL *mu = registry_machine_url_find(pu->machine, url);
if(mu) {
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
p->guid, m->guid, u->url, pu->machine->guid);
p->guid, m->guid, string2str(url), pu->machine->guid);
mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
}
else {
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.",
p->guid, m->guid, u->url, pu->machine->guid);
p->guid, m->guid, string2str(url), pu->machine->guid);
}
pu->machine->links--;
pu->machine = m;
}
if(strcmp(pu->machine_name, name) != 0) {
if(strcmp(string2str(pu->machine_name), machine_name) != 0) {
// the name of the PERSON_URL has changed !
pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu);
pu = registry_person_url_reallocate(p, m, url, machine_name, machine_name_len, when, pu);
}
}
@ -242,7 +220,7 @@ REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MA
if(likely(p->last_t < (uint32_t)when)) p->last_t = (uint32_t)when;
if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, string2str(url));
pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
}
@ -250,5 +228,5 @@ REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MA
}
void registry_person_unlink_from_url(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) {
registry_person_url_free(p, pu);
registry_person_url_deindex_and_free(p, pu);
}

View file

@ -10,19 +10,18 @@
// for each PERSON-URL pair we keep this
struct registry_person_url {
avl_t avl; // binary tree node
REGISTRY_URL *url; // de-duplicated URL
REGISTRY_MACHINE *machine; // link the MACHINE of this URL
uint8_t flags;
uint32_t usages; // how many times this has been accessed
uint32_t first_t; // the first time we saw this
uint32_t last_t; // the last time we saw this
uint32_t usages; // how many times this has been accessed
char machine_name[1]; // the name of the machine, as known by the user
// dynamically allocated to fit properly
REGISTRY_MACHINE *machine; // link the MACHINE of this URL
STRING *machine_name; // the hostname of the machine
STRING *url; // de-duplicated URL
struct registry_person_url *prev, *next;
};
typedef struct registry_person_url REGISTRY_PERSON_URL;
@ -30,32 +29,29 @@ typedef struct registry_person_url REGISTRY_PERSON_URL;
struct registry_person {
char guid[GUID_LEN + 1]; // the person GUID
avl_tree_type person_urls; // dictionary of PERSON_URLs
REGISTRY_PERSON_URL *person_urls; // dictionary of PERSON_URLs
uint32_t first_t; // the first time we saw this
uint32_t last_t; // the last time we saw this
uint32_t usages; // how many times this has been accessed
//uint32_t flags;
//char *email;
};
typedef struct registry_person REGISTRY_PERSON;
// PERSON_URL
REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, const char *url);
REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, STRING *url);
REGISTRY_PERSON_URL *registry_person_url_index_add(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) NEVERNULL WARNUNUSED;
REGISTRY_PERSON_URL *registry_person_url_index_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) WARNUNUSED;
REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when);
REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when, REGISTRY_PERSON_URL *pu);
REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *url, char *machine_name, size_t machine_name_len, time_t when);
REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *url, char *machine_name, size_t machine_name_len, time_t when, REGISTRY_PERSON_URL *pu);
// PERSON
REGISTRY_PERSON *registry_person_find(const char *person_guid);
REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when);
REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when);
REGISTRY_PERSON *registry_person_find_or_create(const char *person_guid, time_t when, bool is_dummy);
// LINKING PERSON -> PERSON_URL
REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when);
REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *url, char *machine_name, size_t machine_name_len, time_t when);
void registry_person_unlink_from_url(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu);
#endif //NETDATA_REGISTRY_PERSON_H

View file

@ -1,88 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "daemon/common.h"
#include "registry_internals.h"
// ----------------------------------------------------------------------------
// REGISTRY_URL
int registry_url_compare(void *a, void *b) {
if(((REGISTRY_URL *)a)->hash < ((REGISTRY_URL *)b)->hash) return -1;
else if(((REGISTRY_URL *)a)->hash > ((REGISTRY_URL *)b)->hash) return 1;
else return strcmp(((REGISTRY_URL *)a)->url, ((REGISTRY_URL *)b)->url);
}
inline REGISTRY_URL *registry_url_index_add(REGISTRY_URL *u) {
return (REGISTRY_URL *)avl_insert(&(registry.registry_urls_root_index), (avl_t *)(u));
}
inline REGISTRY_URL *registry_url_index_del(REGISTRY_URL *u) {
return (REGISTRY_URL *)avl_remove(&(registry.registry_urls_root_index), (avl_t *)(u));
}
REGISTRY_URL *registry_url_get(const char *url, size_t urllen) {
// protection from too big URLs
if(urllen > registry.max_url_length)
urllen = registry.max_url_length;
debug(D_REGISTRY, "Registry: registry_url_get('%s', %zu)", url, urllen);
char buf[sizeof(REGISTRY_URL) + urllen]; // no need for +1, 1 is already in REGISTRY_URL
REGISTRY_URL *n = (REGISTRY_URL *)&buf[0];
n->len = (uint16_t)urllen;
strncpyz(n->url, url, n->len);
n->hash = simple_hash(n->url);
REGISTRY_URL *u = (REGISTRY_URL *)avl_search(&(registry.registry_urls_root_index), (avl_t *)n);
if(!u) {
debug(D_REGISTRY, "Registry: registry_url_get('%s', %zu): allocating %zu bytes", url, urllen, sizeof(REGISTRY_URL) + urllen);
u = callocz(1, sizeof(REGISTRY_URL) + urllen); // no need for +1, 1 is already in REGISTRY_URL
// a simple strcpy() should do the job
// but I prefer to be safe, since the caller specified urllen
u->len = (uint16_t)urllen;
strncpyz(u->url, url, u->len);
u->links = 0;
u->hash = simple_hash(u->url);
registry.urls_memory += sizeof(REGISTRY_URL) + urllen; // no need for +1, 1 is already in REGISTRY_URL
debug(D_REGISTRY, "Registry: registry_url_get('%s'): indexing it", url);
n = registry_url_index_add(u);
if(n != u) {
netdata_log_error("INTERNAL ERROR: registry_url_get(): url '%s' already exists in the registry as '%s'", u->url, n->url);
freez(u);
u = n;
}
else
registry.urls_count++;
}
return u;
}
void registry_url_link(REGISTRY_URL *u) {
u->links++;
debug(D_REGISTRY, "Registry: registry_url_link('%s'): URL has now %u links", u->url, u->links);
}
void registry_url_unlink(REGISTRY_URL *u) {
u->links--;
if(!u->links) {
debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): No more links for this URL", u->url);
REGISTRY_URL *n = registry_url_index_del(u);
if(!n) {
netdata_log_error("INTERNAL ERROR: registry_url_unlink('%s'): cannot find url in index", u->url);
}
else {
if(n != u) {
netdata_log_error("INTERNAL ERROR: registry_url_unlink('%s'): deleted different url '%s'", u->url, n->url);
}
registry.urls_memory -= sizeof(REGISTRY_URL) + n->len; // no need for +1, 1 is already in REGISTRY_URL
freez(n);
}
}
else
debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): URL has %u links left", u->url, u->links);
}

View file

@ -1,35 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef NETDATA_REGISTRY_URL_H
#define NETDATA_REGISTRY_URL_H 1
#include "registry_internals.h"
// ----------------------------------------------------------------------------
// URL structures
// Save memory by de-duplicating URLs
// so instead of storing URLs all over the place
// we store them here and we keep pointers elsewhere
struct registry_url {
avl_t avl;
uint32_t hash; // the index hash
uint32_t links; // the number of links to this URL - when none is left, we free it
uint16_t len; // the length of the URL in bytes
char url[1]; // the URL - dynamically allocated to more size
};
typedef struct registry_url REGISTRY_URL;
// REGISTRY_URL INDEX
int registry_url_compare(void *a, void *b);
REGISTRY_URL *registry_url_index_del(REGISTRY_URL *u) WARNUNUSED;
REGISTRY_URL *registry_url_index_add(REGISTRY_URL *u) NEVERNULL WARNUNUSED;
// REGISTRY_URL MANAGEMENT
REGISTRY_URL *registry_url_get(const char *url, size_t urllen) NEVERNULL;
void registry_url_link(REGISTRY_URL *u);
void registry_url_unlink(REGISTRY_URL *u);
#endif //NETDATA_REGISTRY_URL_H

View file

@ -2,6 +2,35 @@
#include "web_api.h"
bool netdata_is_protected_by_bearer = false; // this is controlled by cloud, at the point the agent logs in - this should also be saved to /var/lib/netdata
DICTIONARY *netdata_authorized_bearers = NULL;
static bool web_client_check_acl_and_bearer(struct web_client *w, WEB_CLIENT_ACL endpoint_acl) {
if(endpoint_acl == WEB_CLIENT_ACL_NOCHECK)
// the endpoint is totally public
return true;
bool acl_allows = w->acl & endpoint_acl;
if(!acl_allows)
// the channel we received the request from (w->acl) is not compatible with the endpoint
return false;
if(!netdata_is_protected_by_bearer && !(endpoint_acl & WEB_CLIENT_ACL_BEARER_REQUIRED))
// bearer protection is not enabled and is not required by the endpoint
return true;
if(!(endpoint_acl & (WEB_CLIENT_ACL_BEARER_REQUIRED|WEB_CLIENT_ACL_BEARER_OPTIONAL)))
// endpoint does not require a bearer
return true;
if((w->acl & (WEB_CLIENT_ACL_ACLK|WEB_CLIENT_ACL_WEBRTC)) || api_check_bearer_token(w))
// the request is coming from ACLK or WEBRTC (authorized already),
// or we have a valid bearer on the request
return true;
return false;
}
int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_path_endpoint, struct web_api_command *api_commands) {
if(unlikely(!url_path_endpoint || !*url_path_endpoint)) {
buffer_flush(w->response.data);
@ -13,7 +42,7 @@ int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_pat
for(int i = 0; api_commands[i].command ; i++) {
if(unlikely(hash == api_commands[i].hash && !strcmp(url_path_endpoint, api_commands[i].command))) {
if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NOCHECK) && !(w->acl & api_commands[i].acl))
if(unlikely(!web_client_check_acl_and_bearer(w, api_commands[i].acl)))
return web_client_permission_denied(w);
char *query_string = (char *)buffer_tostring(w->url_query_string_decoded);

View file

@ -9,6 +9,11 @@
#include "web/api/health/health_cmdapi.h"
#include "web/api/queries/weights.h"
extern bool netdata_is_protected_by_bearer;
extern DICTIONARY *netdata_authorized_bearers;
bool api_check_bearer_token(struct web_client *w);
bool extract_bearer_token_from_request(struct web_client *w, char *dst, size_t dst_len);
struct web_api_command {
const char *command;
uint32_t hash;

View file

@ -927,16 +927,17 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
*/
}
char person_guid[GUID_LEN + 1] = "";
debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
// TODO
// The browser may send multiple cookies with our id
char person_guid[UUID_STR_LEN] = "";
char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
if(cookie)
strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], UUID_STR_LEN - 1);
else if(!extract_bearer_token_from_request(w, person_guid, sizeof(person_guid)))
person_guid[0] = '\0';
char action = '\0';
char *machine_guid = NULL,
@ -1516,12 +1517,6 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc
}
#endif
#ifdef NETDATA_DEV_MODE
#define ACL_DEV_OPEN_ACCESS WEB_CLIENT_ACL_DASHBOARD
#else
#define ACL_DEV_OPEN_ACCESS 0
#endif
static struct web_api_command api_commands_v1[] = {
{ "info", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_info },
{ "data", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_data },
@ -1531,7 +1526,7 @@ static struct web_api_command api_commands_v1[] = {
{ "contexts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_contexts },
// registry checks the ACL by itself, so we allow everything
{ "registry", 0, WEB_CLIENT_ACL_NOCHECK, web_client_api_request_v1_registry },
{ "registry", 0, WEB_CLIENT_ACL_NOCHECK, web_client_api_request_v1_registry },
// badges can be fetched with both dashboard and badge permissions
{ "badge.svg", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC | WEB_CLIENT_ACL_BADGE, web_client_api_request_v1_badge },
@ -1545,16 +1540,16 @@ static struct web_api_command api_commands_v1[] = {
#if defined(ENABLE_ML)
{ "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_ml_info },
{ "ml_models", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_models },
// { "ml_models", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_models },
#endif
{ "manage/health", 0, WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_mgmt_health },
{"manage/health", 0, WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_ACLK | WEB_CLIENT_ACL_BEARER_REQUIRED, web_client_api_request_v1_mgmt_health },
{ "aclk", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_aclk_state },
{ "metric_correlations", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_metric_correlations },
{ "weights", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_weights },
{ "function", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_function },
{ "functions", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_functions },
{"function", 0, WEB_CLIENT_ACL_ACLK | WEB_CLIENT_ACL_BEARER_REQUIRED | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_function },
{"functions", 0, WEB_CLIENT_ACL_ACLK | WEB_CLIENT_ACL_BEARER_REQUIRED | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_functions },
{ "dbengine_stats", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_dbengine_stats },

View file

@ -3,6 +3,119 @@
#include "web_api_v2.h"
#include "../rtc/webrtc.h"
struct bearer_token {
time_t created_s;
time_t expires_s;
};
static void bearer_get_token(uuid_t *uuid) {
static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
static bool initialized = false;
if(!initialized) {
spinlock_lock(&spinlock);
if (!netdata_authorized_bearers) {
netdata_authorized_bearers = dictionary_create_advanced(
DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
NULL, sizeof(struct bearer_token));
}
spinlock_unlock(&spinlock);
initialized = true;
}
char uuid_str[UUID_STR_LEN];
uuid_generate_random(*uuid);
uuid_unparse_lower(*uuid, uuid_str);
struct bearer_token t = { 0 }, *z;
z = dictionary_set(netdata_authorized_bearers, uuid_str, &t, sizeof(t));
if(!z->created_s) {
z->created_s = now_monotonic_sec();
z->expires_s = z->created_s + 86400;
}
}
#define HTTP_REQUEST_AUTHORIZATION_BEARER "\r\nAuthorization: Bearer "
bool extract_bearer_token_from_request(struct web_client *w, char *dst, size_t dst_len) {
const char *req = buffer_tostring(w->response.data);
size_t req_len = buffer_strlen(w->response.data);
const char *bearer = strcasestr(req, HTTP_REQUEST_AUTHORIZATION_BEARER);
if(!bearer)
return false;
const char *token_start = bearer + sizeof(HTTP_REQUEST_AUTHORIZATION_BEARER) - 1;
while(isspace(*token_start))
token_start++;
const char *token_end = token_start + UUID_STR_LEN - 1 + 2;
if (token_end > req + req_len)
return false;
strncpyz(dst, token_start, dst_len - 1);
uuid_t uuid;
if (uuid_parse(dst, uuid) != 0)
return false;
return true;
}
bool api_check_bearer_token(struct web_client *w) {
if(!netdata_authorized_bearers)
return false;
char token[UUID_STR_LEN];
if(!extract_bearer_token_from_request(w, token, sizeof(token)))
return false;
struct bearer_token *z = dictionary_get(netdata_authorized_bearers, token);
return z && z->expires_s > now_monotonic_sec();
}
int api_v2_bearer_protection(RRDHOST *host __maybe_unused, struct web_client *w __maybe_unused, char *url) {
while (url) {
char *value = strsep_skip_consecutive_separators(&url, "&");
if (!value || !*value) continue;
char *name = strsep_skip_consecutive_separators(&value, "=");
if (!name || !*name) continue;
if (!value || !*value) continue;
if(!strcmp(name, "bearer_protection")) {
if(!strcmp(value, "on") || !strcmp(value, "true") || !strcmp(value, "yes"))
netdata_is_protected_by_bearer = true;
else
netdata_is_protected_by_bearer = false;
}
}
BUFFER *wb = w->response.data;
buffer_flush(wb);
buffer_json_initialize(wb, "\"", "\"", 0, true, false);
buffer_json_member_add_boolean(wb, "bearer_protection", netdata_is_protected_by_bearer);
buffer_json_finalize(wb);
return HTTP_RESP_OK;
}
int api_v2_bearer_token(RRDHOST *host __maybe_unused, struct web_client *w __maybe_unused, char *url __maybe_unused) {
uuid_t uuid;
bearer_get_token(&uuid);
BUFFER *wb = w->response.data;
buffer_flush(wb);
buffer_json_initialize(wb, "\"", "\"", 0, true, false);
buffer_json_member_add_string(wb, "mg", localhost->machine_guid);
buffer_json_member_add_boolean(wb, "bearer_protection", netdata_is_protected_by_bearer);
buffer_json_member_add_uuid(wb, "token", &uuid);
buffer_json_finalize(wb);
return HTTP_RESP_OK;
}
static int web_client_api_request_v2_contexts_internal(RRDHOST *host __maybe_unused, struct web_client *w, char *url, CONTEXTS_V2_MODE mode) {
struct api_v2_contexts_request req = { 0 };
@ -441,24 +554,30 @@ static int web_client_api_request_v2_webrtc(RRDHOST *host __maybe_unused, struct
}
static struct web_api_command api_commands_v2[] = {
{"data", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_data},
{"info", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_info},
{"nodes", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_nodes},
{"node_instances", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_node_instances},
{"contexts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_contexts},
{"weights", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_weights},
{"versions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_versions},
{"functions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_functions},
{"alerts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alerts},
{"alert_transitions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alert_transitions},
{"alert_config", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alert_config},
{"claim", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_claim},
{"q", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_q},
{"info", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_info},
{"rtc_offer", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v2_webrtc},
{"data", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_data},
{"weights", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_weights},
{"contexts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_contexts},
{"nodes", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_nodes},
{"node_instances", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_node_instances},
{"versions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_versions},
{"functions", 0, WEB_CLIENT_ACL_ACLK | WEB_CLIENT_ACL_BEARER_REQUIRED | ACL_DEV_OPEN_ACCESS, web_client_api_request_v2_functions},
{"q", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_q},
{"alerts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alerts},
{"alert_transitions", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alert_transitions},
{"alert_config", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v2_alert_config},
{"claim", 0, WEB_CLIENT_ACL_NOCHECK, web_client_api_request_v2_claim},
{"rtc_offer", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, web_client_api_request_v2_webrtc},
{"bearer_protection", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, api_v2_bearer_protection},
{"bearer_get_token", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, api_v2_bearer_token},
// terminator
{NULL, 0, WEB_CLIENT_ACL_NONE, NULL},
{NULL, 0, WEB_CLIENT_ACL_NONE, NULL},
};
inline int web_client_api_request_v2(RRDHOST *host, struct web_client *w, char *url_path_endpoint) {