Browse Source

bearer authorization API (#15321)

* 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 79eff56f77d12a9f840f1b30040e4123e3d9a7a2.

* 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
Costa Tsaousis 1 year ago
parent
commit
5943203a66

+ 0 - 2
CMakeLists.txt

@@ -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

+ 0 - 2
Makefile.am

@@ -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 = \

+ 5 - 0
libnetdata/avl/avl.h

@@ -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;

+ 0 - 4
libnetdata/dictionary/dictionary.c

@@ -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);
 }

+ 0 - 1
libnetdata/dictionary/dictionary.h

@@ -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;

+ 10 - 2
libnetdata/socket/socket.h

@@ -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
 

+ 144 - 82
libnetdata/string/string.c

@@ -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 {
-    Pvoid_t JudyHSArray;        // the Judy array - hashtable
-    netdata_rwlock_t rwlock;    // the R/W lock to protect the Judy array
+static struct string_partition {
+    RW_SPINLOCK spinlock;       // the R/W spinlock to protect the Judy array
 
-    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
+    Pvoid_t JudyHSArray;        // the Judy array - hashtable
 
-    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
 
+    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
+
 #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(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;
+    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;
+
+    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,

+ 94 - 41
registry/registry.c

@@ -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);
 }

+ 137 - 104
registry/registry_db.c

@@ -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;
+                }
+
+                u = string_strdupz(url);
 
-                time_t first_t = strtoul(&s[2], NULL, 16);
+                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;
         }
     }

+ 133 - 34
registry/registry_init.c

@@ -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();
-    }
 
-    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_db_stats();
+//        registry_generate_curl_urls();
+//        exit(0);
 
-    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);
+        netdata_thread_enable_cancelability();
+    }
 
-    return 1;
+    return 0;
 }
 
 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;
 }

Some files were not shown because too many files changed in this diff