diff options
author | Costa Tsaousis (ktsaou) <costa@tsaousis.gr> | 2016-12-31 21:08:24 +0200 |
---|---|---|
committer | Costa Tsaousis (ktsaou) <costa@tsaousis.gr> | 2016-12-31 21:08:24 +0200 |
commit | ba97a9f4d4dd21b74af45e511470bcbbc60ad992 (patch) | |
tree | 69338fa8da88af62670873229aae35b5d7e56aea | |
parent | 97da3cf73b6e55e4f33282db75e0503c90f21399 (diff) |
removed over-optimization at the registry to simplify its logic and remove race conditions that could result to crashes; registry is now split into multiple files, to allow easier enhancements
-rwxr-xr-x | CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/common.h | 10 | ||||
-rw-r--r-- | src/registry.c | 1715 | ||||
-rw-r--r-- | src/registry.h | 66 | ||||
-rw-r--r-- | src/registry_db.c | 342 | ||||
-rw-r--r-- | src/registry_init.c | 160 | ||||
-rw-r--r-- | src/registry_internals.c | 317 | ||||
-rw-r--r-- | src/registry_internals.h | 92 | ||||
-rw-r--r-- | src/registry_log.c | 133 | ||||
-rw-r--r-- | src/registry_machine.c | 101 | ||||
-rw-r--r-- | src/registry_machine.h | 41 | ||||
-rw-r--r-- | src/registry_person.c | 175 | ||||
-rw-r--r-- | src/registry_person.h | 44 | ||||
-rw-r--r-- | src/registry_url.c | 74 | ||||
-rw-r--r-- | src/registry_url.h | 28 | ||||
-rw-r--r-- | src/web_client.c | 58 |
17 files changed, 1692 insertions, 1680 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index eb56d2a0c9..47faf76b86 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,14 @@ set(NETDATA_SOURCE_FILES src/web_client.h src/web_server.c src/web_server.h - src/socket.c src/socket.h) + src/socket.c src/socket.h + src/registry_url.c + src/registry_url.h + src/registry_internals.h + src/registry_person.c + src/registry_person.h + src/registry_machine.c + src/registry_machine.h src/registry_internals.c src/registry_init.c src/registry_db.c src/registry_log.c) set(APPS_PLUGIN_SOURCE_FILES src/appconfig.c diff --git a/src/Makefile.am b/src/Makefile.am index e1f87b5e4d..6ceeecde71 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,6 +50,13 @@ netdata_SOURCES = \ procfile.c procfile.h \ proc_self_mountinfo.c proc_self_mountinfo.h \ registry.c registry.h \ + registry_internals.c registry_internals.h \ + registry_url.c registry_url.h \ + registry_person.c registry_person.h \ + registry_machine.c registry_machine.h \ + registry_init.c \ + registry_db.c \ + registry_log.c \ rrd.c rrd.h \ rrd2json.c rrd2json.h \ storage_number.c storage_number.h \ diff --git a/src/common.h b/src/common.h index baa95dcb07..0fc4e820dc 100644 --- a/src/common.h +++ b/src/common.h @@ -170,6 +170,8 @@ #endif #define abs(x) ((x < 0)? -x : x) +#define GUID_LEN 36 + extern void netdata_fix_chart_id(char *s); extern void netdata_fix_chart_name(char *s); @@ -198,10 +200,10 @@ extern void *mallocz_int(const char *file, const char *function, const unsigned extern void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size); extern void freez_int(const char *file, const char *function, const unsigned long line, void *ptr); #else -extern char *strdupz(const char *s); -extern void *callocz(size_t nmemb, size_t size); -extern void *mallocz(size_t size); -extern void *reallocz(void *ptr, size_t size); +extern char *strdupz(const char *s) __attribute__((returns_nonnull)); +extern void *callocz(size_t nmemb, size_t size) __attribute__((returns_nonnull)); +extern void *mallocz(size_t size) __attribute__((returns_nonnull)); +extern void *reallocz(void *ptr, size_t size) __attribute__((returns_nonnull)); extern void freez(void *ptr); #endif diff --git a/src/registry.c b/src/registry.c index b32625756c..84dc47aded 100644 --- a/src/registry.c +++ b/src/registry.c @@ -1,1016 +1,27 @@ #include "common.h" -// ---------------------------------------------------------------------------- -// TODO -// -// 1. the default tracking cookie expires in 1 year, but the persons are not -// removed from the db - this means the database only grows - ideally the -// database should be cleaned in registry_save() for both on-disk and -// on-memory entries. -// -// Cleanup: -// i. Find all the PERSONs that have expired cookie -// ii. For each of their PERSON_URLs: -// - decrement the linked MACHINE links -// - if the linked MACHINE has no other links, remove the linked MACHINE too -// - remove the PERSON_URL -// -// 2. add protection to prevent abusing the registry by flooding it with -// requests to fill the memory and crash it. -// -// Possible protections: -// - limit the number of URLs per person -// - limit the number of URLs per machine -// - limit the number of persons -// - limit the number of machines -// - [DONE] limit the size of URLs -// - [DONE] limit the size of PERSON_URL names -// - limit the number of requests that add data to the registry, -// per client IP per hour -// -// 3. lower memory requirements -// -// - embed avl structures directly into registry objects, instead of DICTIONARY -// - store GUIDs in memory as UUID instead of char * -// - do not track persons using the demo machines only -// (i.e. start tracking them only when they access a non-demo machine) -// - [DONE] do not track custom dashboards by default - -#define REGISTRY_URL_FLAGS_DEFAULT 0x00 -#define REGISTRY_URL_FLAGS_EXPIRED 0x01 - -#define DICTIONARY_FLAGS DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE | DICTIONARY_FLAG_NAME_LINK_DONT_CLONE - -#define GUID_LEN 36 - -// ---------------------------------------------------------------------------- -// COMMON structures - -struct registry { - int enabled; - - char machine_guid[GUID_LEN + 1]; - - // 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; - char *hostname; - char *registry_to_announce; - time_t persons_expiration; // seconds to expire idle persons - int verify_cookies_redirects; - - size_t max_url_length; - size_t max_name_length; - - // file/path names - char *pathname; - char *db_filename; - char *log_filename; - char *machine_guid_filename; - - // open files - FILE *log_fp; - - // the database - DICTIONARY *persons; // dictionary of PERSON *, with key the PERSON.guid - DICTIONARY *machines; // dictionary of MACHINE *, with key the MACHINE.guid - DICTIONARY *urls; // dictionary of URL *, with key the URL.url - - // concurrency locking - // we keep different locks for different things - // so that many tasks can be completed in parallel - pthread_mutex_t persons_lock; - pthread_mutex_t machines_lock; - pthread_mutex_t urls_lock; - pthread_mutex_t person_urls_lock; - pthread_mutex_t machine_urls_lock; - pthread_mutex_t log_lock; -} registry; - - -// ---------------------------------------------------------------------------- -// 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 { - 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; - - -// ---------------------------------------------------------------------------- -// MACHINE structures - -// For each MACHINE-URL pair we keep this -struct registry_machine_url { - REGISTRY_URL *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 -}; -typedef struct registry_machine_url REGISTRY_MACHINE_URL; - -// A machine -struct registry_machine { - char guid[GUID_LEN + 1]; // the GUID - - uint32_t links; // the number of REGISTRY_PERSON_URL linked to this machine - - DICTIONARY *machine_urls; // MACHINE_URL * - - 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 -}; -typedef struct registry_machine REGISTRY_MACHINE; - - -// ---------------------------------------------------------------------------- -// PERSON structures - -// for each PERSON-URL pair we keep this -struct registry_person_url { - REGISTRY_URL *url; // de-duplicated URL - REGISTRY_MACHINE *machine; // link the MACHINE of this 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 - - char machine_name[1]; // the name of the machine, as known by the user - // dynamically allocated to fit properly -}; -typedef struct registry_person_url REGISTRY_PERSON_URL; - -// A person -struct registry_person { - char guid[GUID_LEN + 1]; // the person GUID - - DICTIONARY *person_urls; // dictionary of PERSON_URL * - - 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 -}; -typedef struct registry_person REGISTRY_PERSON; +#include "registry_internals.h" +#define REGISTRY_STATUS_OK "ok" +#define REGISTRY_STATUS_FAILED "failed" +#define REGISTRY_STATUS_DISABLED "disabled" // ---------------------------------------------------------------------------- // REGISTRY concurrency locking -static inline void registry_persons_lock(void) { - pthread_mutex_lock(®istry.persons_lock); -} - -static inline void registry_persons_unlock(void) { - pthread_mutex_unlock(®istry.persons_lock); -} - -static inline void registry_machines_lock(void) { - pthread_mutex_lock(®istry.machines_lock); -} - -static inline void registry_machines_unlock(void) { - pthread_mutex_unlock(®istry.machines_lock); -} - -static inline void registry_urls_lock(void) { - pthread_mutex_lock(®istry.urls_lock); -} - -static inline void registry_urls_unlock(void) { - pthread_mutex_unlock(®istry.urls_lock); -} - -// ideally, we should not lock the whole registry for -// updating a person's urls. -// however, to save the memory required for keeping a -// mutex (40 bytes) per person, we do... -static inline void registry_person_urls_lock(REGISTRY_PERSON *p) { - (void)p; - pthread_mutex_lock(®istry.person_urls_lock); -} - -static inline void registry_person_urls_unlock(REGISTRY_PERSON *p) { - (void)p; - pthread_mutex_unlock(®istry.person_urls_lock); +static inline void registry_lock(void) { + pthread_mutex_lock(®istry.lock); } -// ideally, we should not lock the whole registry for -// updating a machine's urls. -// however, to save the memory required for keeping a -// mutex (40 bytes) per machine, we do... -static inline void registry_machine_urls_lock(REGISTRY_MACHINE *m) { - (void)m; - pthread_mutex_lock(®istry.machine_urls_lock); -} - -static inline void registry_machine_urls_unlock(REGISTRY_MACHINE *m) { - (void)m; - pthread_mutex_unlock(®istry.machine_urls_lock); -} - -static inline void registry_log_lock(void) { - pthread_mutex_lock(®istry.log_lock); -} - -static inline void registry_log_unlock(void) { - pthread_mutex_unlock(®istry.log_lock); +static inline void registry_unlock(void) { + pthread_mutex_unlock(®istry.lock); } // ---------------------------------------------------------------------------- -// common functions - -// parse a GUID and re-generated to be always lower case -// this is used as a protection against the variations of GUIDs -static inline int registry_regenerate_guid(const char *guid, char *result) { - uuid_t uuid; - if(unlikely(uuid_parse(guid, uuid) == -1)) { - info("Registry: GUID '%s' is not a valid GUID.", guid); - return -1; - } - else { - uuid_unparse_lower(uuid, result); - -#ifdef NETDATA_INTERNAL_CHECKS - if(strcmp(guid, result)) - info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result); -#endif /* NETDATA_INTERNAL_CHECKS */ - } - - return 0; -} - -// make sure the names of the machines / URLs do not contain any tabs -// (which are used as our separator in the database files) -// and are properly trimmed (before and after) -static inline char *registry_fix_machine_name(char *name, size_t *len) { - char *s = name?name:""; - - // skip leading spaces - while(*s && isspace(*s)) s++; - - // make sure all spaces are a SPACE - char *t = s; - while(*t) { - if(unlikely(isspace(*t))) - *t = ' '; - - t++; - } - - // remove trailing spaces - while(--t >= s) { - if(*t == ' ') - *t = '\0'; - else - break; - } - t++; - - if(likely(len)) - *len = (t - s); - - return s; -} - -static inline char *registry_fix_url(char *url, size_t *len) { - return registry_fix_machine_name(url, len); -} - - -// ---------------------------------------------------------------------------- -// forward definition of functions - -extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when); -extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); - - -// ---------------------------------------------------------------------------- -// URL - -static inline REGISTRY_URL *registry_url_allocate_nolock(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_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(REGISTRY_URL) + urllen); - REGISTRY_URL *u = mallocz(sizeof(REGISTRY_URL) + urllen); - - // 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; - - registry.urls_memory += sizeof(REGISTRY_URL) + urllen; - - debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url); - dictionary_set(registry.urls, u->url, u, sizeof(REGISTRY_URL)); - - return u; -} - -static inline REGISTRY_URL *registry_url_get_nolock(const char *url, size_t urllen) { - debug(D_REGISTRY, "Registry: registry_url_get_nolock('%s')", url); - - REGISTRY_URL *u = dictionary_get(registry.urls, url); - if(!u) { - u = registry_url_allocate_nolock(url, urllen); - registry.urls_count++; - } - - return u; -} - -static inline REGISTRY_URL *registry_url_get(const char *url, size_t urllen) { - debug(D_REGISTRY, "Registry: registry_url_get('%s')", url); - - registry_urls_lock(); - - REGISTRY_URL *u = registry_url_get_nolock(url, urllen); - - registry_urls_unlock(); - - return u; -} - -static inline void registry_url_link_nolock(REGISTRY_URL *u) { - u->links++; - debug(D_REGISTRY, "Registry: registry_url_link_nolock('%s'): URL has now %u links", u->url, u->links); -} - -static inline void registry_url_unlink_nolock(REGISTRY_URL *u) { - u->links--; - if(!u->links) { - debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url); - dictionary_del(registry.urls, u->url); - freez(u); - } - else - debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links); -} - - -// ---------------------------------------------------------------------------- -// MACHINE - -static inline REGISTRY_MACHINE *registry_machine_find(const char *machine_guid) { - debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid); - return dictionary_get(registry.machines, machine_guid); -} - -static inline REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(REGISTRY_MACHINE_URL)); - - REGISTRY_MACHINE_URL *mu = mallocz(sizeof(REGISTRY_MACHINE_URL)); - - // mu->persons = dictionary_create(DICTIONARY_FLAGS); - // dictionary_set(mu->persons, p->guid, p, sizeof(PERSON)); - - mu->first_t = mu->last_t = (uint32_t)when; - mu->usages = 1; - mu->url = u; - mu->flags = REGISTRY_URL_FLAGS_DEFAULT; - - registry.machines_urls_memory += sizeof(REGISTRY_MACHINE_URL); - - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url); - dictionary_set(m->machine_urls, u->url, mu, sizeof(REGISTRY_MACHINE_URL)); - registry_url_link_nolock(u); - - return mu; -} - -static inline 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)); - - REGISTRY_MACHINE *m = mallocz(sizeof(REGISTRY_MACHINE)); - - 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(DICTIONARY_FLAGS); - - m->first_t = m->last_t = (uint32_t)when; - m->usages = 0; - - registry.machines_memory += sizeof(REGISTRY_MACHINE); - - registry.machines_count++; - dictionary_set(registry.machines, m->guid, m, sizeof(REGISTRY_MACHINE)); - - return m; -} - -// 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 -static inline REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) { - REGISTRY_MACHINE *m = NULL; - - registry_machines_lock(); - - if(likely(machine_guid && *machine_guid)) { - // validate it is a GUID - char buf[GUID_LEN + 1]; - if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1)) - info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid); - else { - machine_guid = buf; - m = registry_machine_find(machine_guid); - if(!m) m = registry_machine_allocate(machine_guid, when); - } - } - - registry_machines_unlock(); - - return m; -} - - -// ---------------------------------------------------------------------------- -// PERSON - -static inline REGISTRY_PERSON *registry_person_find(const char *person_guid) { - debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid); - return dictionary_get(registry.persons, person_guid); -} - -static inline REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) { - // protection from too big names - if(namelen > registry.max_name_length) - namelen = registry.max_name_length; - - 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 *pu = mallocz(sizeof(REGISTRY_PERSON_URL) + namelen); - - // a simple strcpy() should do the job - // but I prefer to be safe, since the caller specified urllen - strncpyz(pu->machine_name, name, namelen); - - pu->machine = m; - pu->first_t = pu->last_t = when; - pu->usages = 1; - pu->url = u; - 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); - dictionary_set(p->person_urls, u->url, pu, sizeof(REGISTRY_PERSON_URL)); - registry_url_link_nolock(u); - - return pu; -} - -static inline 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) { - // this function is needed to change the name of a PERSON_URL - - 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 *tpu = registry_person_url_allocate(p, m, u, name, namelen, when); - tpu->first_t = pu->first_t; - tpu->last_t = pu->last_t; - tpu->usages = pu->usages; - - // ok, these are a hack - since the registry_person_url_allocate() is - // adding these, we have to subtract them - tpu->machine->links--; - registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(pu->machine_name); - registry_url_unlink_nolock(u); - - freez(pu); - - return tpu; -} - -static inline REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) { - REGISTRY_PERSON *p = NULL; - - debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON)); - - p = mallocz(sizeof(REGISTRY_PERSON)); - - if(!person_guid) { - for (; ;) { - uuid_t uuid; - uuid_generate(uuid); - uuid_unparse_lower(uuid, p->guid); - - debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid); - if (!dictionary_get(registry.persons, p->guid)) { - debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid); - break; - } - else - info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid); - } - } - else - strncpyz(p->guid, person_guid, GUID_LEN); - - debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid); - p->person_urls = dictionary_create(DICTIONARY_FLAGS); - - p->first_t = p->last_t = when; - p->usages = 0; - - registry.persons_memory += sizeof(REGISTRY_PERSON); - - registry.persons_count++; - dictionary_set(registry.persons, p->guid, p, sizeof(REGISTRY_PERSON)); - - return p; -} - - -// 1. validate person GUID -// 2. if it is valid, find it -// 3. if it is not valid, create a new one -// 4. return it -static inline REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) { - REGISTRY_PERSON *p = NULL; - - registry_persons_lock(); - - if(person_guid && *person_guid) { - char buf[GUID_LEN + 1]; - // validate it is a GUID - if(unlikely(registry_regenerate_guid(person_guid, buf) == -1)) - info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid); - else { - person_guid = buf; - p = registry_person_find(person_guid); - } - } - - if(!p) p = registry_person_allocate(NULL, when); - - registry_persons_unlock(); - - return p; -} - -// ---------------------------------------------------------------------------- -// LINKING OF OBJECTS - -static inline 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_urls_lock(p); - - REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, u->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); - registry.persons_urls_count++; - } - else { - debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url); - pu->usages++; - if(likely(pu->last_t < (uint32_t)when)) pu->last_t = when; - - if(pu->machine != m) { - REGISTRY_MACHINE_URL *mu = dictionary_get(pu->machine->machine_urls, u->url); - if(mu) { - info("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); - mu->flags |= REGISTRY_URL_FLAGS_EXPIRED; - } - else { - info("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); - } - - pu->machine->links--; - pu->machine = m; - } - - if(strcmp(pu->machine_name, name)) { - // the name of the PERSON_URL has changed ! - pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu); - } - } - - p->usages++; - if(likely(p->last_t < (uint32_t)when)) p->last_t = when; - - if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) { - info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url); - pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED; - } - - registry_person_urls_unlock(p); - - return pu; -} - -static inline REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): searching for URL in machine", p->guid, m->guid, u->url); - - registry_machine_urls_lock(m); - - REGISTRY_MACHINE_URL *mu = dictionary_get(m->machine_urls, u->url); - if(!mu) { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url); - mu = registry_machine_url_allocate(m, u, when); - registry.machines_urls_count++; - } - else { - debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url); - mu->usages++; - if(likely(mu->last_t < (uint32_t)when)) mu->last_t = when; - } - - //debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): indexing person in machine", p->guid, m->guid, u->url); - //dictionary_set(mu->persons, p->guid, p, sizeof(PERSON)); - - m->usages++; - if(likely(m->last_t < (uint32_t)when)) m->last_t = when; - - if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) { - info("registry_machine_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url); - mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED; - } - - registry_machine_urls_unlock(m); - - return mu; -} - -// ---------------------------------------------------------------------------- -// REGISTRY LOG LOAD/SAVE - -static inline int registry_should_save_db(void) { - debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries); - return registry.log_count > registry.save_registry_every_entries; -} - -static inline void registry_log(const char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name) { - if(likely(registry.log_fp)) { - // we lock only if the file is open - // to allow replaying the log at registry_log_load() - registry_log_lock(); - - if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n", - action, - p->last_t, - p->guid, - m->guid, - name, - u->url) < 0)) - error("Registry: failed to save log. Registry data may be lost in case of abnormal restart."); - - // we increase the counter even on failures - // so that the registry will be saved periodically - registry.log_count++; - - registry_log_unlock(); - - // this must be outside the log_lock(), or a deadlock will happen. - // registry_save() checks the same inside the log_lock, so only - // one thread will save the db - if(unlikely(registry_should_save_db())) - registry_save(); - } -} - -static inline int registry_log_open_nolock(void) { - if(registry.log_fp) - fclose(registry.log_fp); - - registry.log_fp = fopen(registry.log_filename, "a"); - - if(registry.log_fp) { - if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0) - error("Cannot set line buffering on registry log file."); - return 0; - } - - error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename); - return -1; -} - -static inline void registry_log_close_nolock(void) { - if(registry.log_fp) { - fclose(registry.log_fp); - registry.log_fp = NULL; - } -} - -static inline void registry_log_recreate_nolock(void) { - if(registry.log_fp != NULL) { - registry_log_close_nolock(); - - // open it with truncate - registry.log_fp = fopen(registry.log_filename, "w"); - if(registry.log_fp) fclose(registry.log_fp); - else error("Cannot truncate registry log '%s'", registry.log_filename); - - registry.log_fp = NULL; - - registry_log_open_nolock(); - } -} - -int registry_log_load(void) { - ssize_t line = -1; - - // closing the log is required here - // otherwise we will append to it the values we read - registry_log_close_nolock(); - - debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename); - FILE *fp = fopen(registry.log_filename, "r"); - if(!fp) - error("Registry: cannot open registry file: %s", registry.log_filename); - else { - char *s, buf[4096 + 1]; - line = 0; - size_t len = 0; - - while ((s = fgets_trim_len(buf, 4096, fp, &len))) { - line++; - - switch (s[0]) { - case 'A': // accesses - case 'D': // deletes - - // verify it is valid - if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) { - error("Registry: log line %zd is wrong (len = %zu).", line, len); - continue; - } - s[1] = s[10] = s[47] = s[84] = '\0'; - - // get the variables - time_t when = strtoul(&s[2], NULL, 16); |