-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-// gcc -O3 -Wall -Wextra -I ../src/ -I ../ -o registry ../src/registry.c ../src/dictionary.o ../src/log.o ../src/avl.o ../src/common.o -pthread -luuid -DHAVE_CONFIG_H
-
-#include <uuid/uuid.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "log.h"
#include "common.h"
-#include "dictionary.h"
-struct machine {
- char guid[36 + 1];
+#include "registry_internals.h"
- time_t first_t;
- time_t last_t;
- size_t usages;
-
- DICTIONARY *urls;
-};
-typedef struct machine MACHINE;
+#define REGISTRY_STATUS_OK "ok"
+#define REGISTRY_STATUS_FAILED "failed"
+#define REGISTRY_STATUS_DISABLED "disabled"
+// ----------------------------------------------------------------------------
+// REGISTRY concurrency locking
-struct person {
- char guid[36 + 1];
+static inline void registry_lock(void) {
+ netdata_mutex_lock(®istry.lock);
+}
- time_t first_t;
- time_t last_t;
- size_t usages;
+static inline void registry_unlock(void) {
+ netdata_mutex_unlock(®istry.lock);
+}
- DICTIONARY *urls;
-};
-typedef struct person PERSON;
+// ----------------------------------------------------------------------------
+// COOKIES
-struct url {
- MACHINE *machine;
- PERSON *person;
+static void registry_set_cookie(struct web_client *w, const char *guid) {
+ char edate[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);
- time_t first_t;
- time_t last_t;
- size_t usages;
-
- size_t url_length;
- char url[];
-};
-typedef struct url URL;
+ snprintfz(w->cookie1, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s", guid, edate);
+ if(registry.registry_domain && registry.registry_domain[0])
+ snprintfz(w->cookie2, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", guid, registry.registry_domain, edate);
+}
-struct registry {
- DICTIONARY *persons;
- DICTIONARY *machines;
-} registry;
+static inline void registry_set_person_cookie(struct web_client *w, REGISTRY_PERSON *p) {
+ registry_set_cookie(w, p->guid);
+}
// ----------------------------------------------------------------------------
-// MACHINE
+// JSON GENERATION
-MACHINE *registry_machine_load(const char *machine_guid) {
- (void)machine_guid;
+static inline void registry_json_header(RRDHOST *host, struct web_client *w, const char *action, const char *status) {
+ buffer_flush(w->response.data);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
+ action, status, (host == localhost)?registry.hostname:host->hostname, host->machine_guid);
+}
- return NULL;
+static inline void registry_json_footer(struct web_client *w) {
+ buffer_strcat(w->response.data, "\n}\n");
}
-int registry_machine_save(MACHINE *m) {
- (void)m;
+static inline int registry_json_disabled(RRDHOST *host, struct web_client *w, const char *action) {
+ registry_json_header(host, w, action, REGISTRY_STATUS_DISABLED);
- return -1;
-}
+ buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
+ registry.registry_to_announce);
-MACHINE *registry_machine_find(const char *machine_guid) {
- MACHINE *m = dictionary_get(registry.machines, machine_guid);
- if(!m) m = registry_machine_load(machine_guid);
- return m;
+ registry_json_footer(w);
+ return 200;
}
-MACHINE *registry_machine_get(const char *machine_guid) {
- MACHINE *m = registry_machine_find(machine_guid);
- if(!m) {
- debug(D_REGISTRY, "Registry: creating new machine '%s'", machine_guid);
+// ----------------------------------------------------------------------------
+// CALLBACKS FOR WALKING THROUGH REGISTRY OBJECTS
+
+// structure used be the callbacks below
+struct registry_json_walk_person_urls_callback {
+ REGISTRY_PERSON *p;
+ REGISTRY_MACHINE *m;
+ struct web_client *w;
+ int count;
+};
- m = calloc(1, sizeof(MACHINE));
- if(!m) fatal("Registry: cannot allocate memory for new machine '%s'", machine_guid);
+// 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;
+ struct web_client *w = c->w;
- strncpy(m->guid, machine_guid, 36);
+ if(unlikely(c->count++))
+ buffer_strcat(w->response.data, ",");
- dictionary_set(registry.machines, m->guid, m, sizeof(MACHINE));
-
- m->first_t = time(NULL);
- m->usages = 0;
- }
+ buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
+ pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->machine_name);
- return m;
+ return 0;
}
+// callback for rendering MACHINE_URLs
+static int registry_json_machine_url_callback(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;
+ struct web_client *w = c->w;
+ REGISTRY_MACHINE *m = c->m;
-// ----------------------------------------------------------------------------
-// PERSON
+ if(unlikely(c->count++))
+ buffer_strcat(w->response.data, ",");
-PERSON *registry_person_load(const char *person_guid) {
- (void)person_guid;
+ buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]",
+ m->guid, mu->url->url, mu->last_t, mu->usages);
- return NULL;
+ return 1;
}
-int registry_person_save(PERSON *p) {
- (void)p;
-
- return -1;
-}
+// ----------------------------------------------------------------------------
-PERSON *registry_person_find(const char *person_guid) {
- PERSON *p = dictionary_get(registry.persons, person_guid);
- if(!p) p = registry_person_load(person_guid);
- return p;
-}
+// structure used be the callbacks below
+struct registry_person_url_callback_verify_machine_exists_data {
+ REGISTRY_MACHINE *m;
+ int count;
+};
-PERSON *registry_person_get(const char *person_guid) {
- PERSON *p = NULL;
+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;
+ REGISTRY_MACHINE *m = d->m;
- if(person_guid && *person_guid)
- p = registry_person_find(person_guid);
+ if(pu->machine == m)
+ d->count++;
- if(!p) {
- if(person_guid && *person_guid)
- error("Registry: discarding unknown person guid '%s'. Will give a new PERSONID.", person_guid);
+ return 0;
+}
- debug(D_REGISTRY, "Registry: creating new person");
+// ----------------------------------------------------------------------------
+// public HELLO request
- p = calloc(1, sizeof(PERSON));
- if(!p) fatal("Registry: cannot allocate memory for new person.");
+int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
+ registry_json_header(host, w, "hello", REGISTRY_STATUS_OK);
- uuid_t uuid;
- if(uuid_generate_time_safe(uuid) == -1)
- info("Registry: uuid_generate_time_safe() reports UUID generation is not safe for uniqueness.");
+ buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
+ registry.registry_to_announce);
- uuid_unparse_lower(uuid, p->guid);
+ registry_json_footer(w);
+ return 200;
+}
- dictionary_set(registry.persons, p->guid, p, sizeof(PERSON));
+// ----------------------------------------------------------------------------
+//public ACCESS request
- p->first_t = time(NULL);
- p->usages = 0;
- }
+#define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please"
- return p;
-}
+// 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");
+ // ------------------------------------------------------------------------
+ // verify the browser supports cookies
-// ----------------------------------------------------------------------------
-// URL
+ if(registry.verify_cookies_redirects > 0 && !person_guid[0]) {
+ buffer_flush(w->response.data);
+ registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry.registry_to_announce);
+ return 200;
+ }
-URL *registry_url_update(PERSON *p, MACHINE *m, const char *url) {
- URL *pu = dictionary_get(p->urls, url);
- URL *mu = dictionary_get(m->urls, url);
- URL *u = NULL;
+ if(unlikely(person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
+ person_guid[0] = '\0';
- if(pu != mu || pu == NULL || mu == NULL)
- error("Registry: person/machine discrepancy on url '%s', for person '%s' and machine '%s'", url, p->guid, m->guid);
- else
- u = pu;
+ // ------------------------------------------------------------------------
- if(!u) {
- size_t len = strlen(url);
+ registry_lock();
- URL *u = calloc(1, sizeof(URL) + len);
- if(!u) fatal("Registry: cannot allocate memory for person '%s', machine '%s', url '%s'.", p->guid, m->guid, url);
+ REGISTRY_PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
+ if(!p) {
+ registry_json_header(host, w, "access", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 412;
+ }
- strcpy(u->url, url);
- u->url_length = len;
- u->person = p;
- u->machine = m;
- u->first_t = time(NULL);
- u->usages = 1;
+ // set the cookie
+ registry_set_person_cookie(w, p);
- dictionary_set(p->urls, url, u, sizeof(URL) + u->url_length);
- dictionary_set(m->urls, url, u, sizeof(URL) + u->url_length);
- if(pu) free(pu);
- if(mu) free(mu);
- }
- else
- u->usages++;
+ // generate the response
+ registry_json_header(host, w, "access", REGISTRY_STATUS_OK);
- p->usages++;
- m->usages++;
- p->last_t = m->last_t = u->last_t = time(NULL);
+ buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
+ struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
+ avl_traverse(&p->person_urls, registry_json_person_url_callback, &c);
+ buffer_strcat(w->response.data, "\n\t]\n");
- return u;
+ registry_json_footer(w);
+ registry_unlock();
+ return 200;
}
-
// ----------------------------------------------------------------------------
-// REGISTRY
-
-int registry_save(void) {
- return -1;
+// public DELETE request
+
+// the main method for deleting a URL from a person
+int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
+ if(!registry.enabled)
+ return registry_json_disabled(host, w, "delete");
+
+ registry_lock();
+
+ REGISTRY_PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
+ if(!p) {
+ registry_json_header(host, w, "delete", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 412;
+ }
+
+ // generate the response
+ registry_json_header(host, w, "delete", REGISTRY_STATUS_OK);
+ registry_json_footer(w);
+ registry_unlock();
+ return 200;
}
-char *registry_request(const char *person_guid, const char *machine_guid, const char *url) {
- PERSON *p = NULL;
- MACHINE *m = NULL;
-
- // --- PERSON ---
- p = registry_person_get(person_guid);
- person_guid = p->guid;
+// ----------------------------------------------------------------------------
+// public SEARCH request
- // --- MACHINE ---
- m = registry_machine_get(machine_guid);
- machine_guid = m->guid;
+// the main method for searching the URLs of a netdata
+int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
+ if(!registry.enabled)
+ return registry_json_disabled(host, w, "search");
- // --- URL ---
- registry_url_update(p, m, url);
+ registry_lock();
- registry_person_save(p);
- registry_machine_save(m);
- registry_save();
+ REGISTRY_MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
+ if(!m) {
+ registry_json_header(host, w, "search", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 404;
+ }
- return NULL;
-}
+ registry_json_header(host, w, "search", REGISTRY_STATUS_OK);
-void registry_init(void) {
- registry.persons = dictionary_create(DICTIONARY_FLAG_DEFAULT);
- if(!registry.persons)
- fatal("Registry: cannot create persons registry");
+ buffer_strcat(w->response.data, ",\n\t\"urls\": [");
+ struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
+ dictionary_get_all(m->machine_urls, registry_json_machine_url_callback, &c);
+ buffer_strcat(w->response.data, "\n\t]\n");
- registry.machines = dictionary_create(DICTIONARY_FLAG_DEFAULT);
- if(!registry.machines)
- fatal("Registry: cannot create machines registry");
+ registry_json_footer(w);
+ registry_unlock();
+ return 200;
}
-int main(int argc, char **argv) {
- (void)argc;
- (void)argv;
-
- registry_init();
+// ----------------------------------------------------------------------------
+// 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) {
+ if(!registry.enabled)
+ return registry_json_disabled(host, w, "switch");
+
+ (void)url;
+ (void)when;
+
+ registry_lock();
+
+ REGISTRY_PERSON *op = registry_person_find(person_guid);
+ if(!op) {
+ registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 430;
+ }
+
+ REGISTRY_PERSON *np = registry_person_find(new_person_guid);
+ if(!np) {
+ registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 431;
+ }
+
+ REGISTRY_MACHINE *m = registry_machine_find(machine_guid);
+ if(!m) {
+ registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 432;
+ }
+
+ 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);
+ if(!data.count) {
+ registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 433;
+ }
+
+ // 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);
+ if(!data.count) {
+ registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ registry_unlock();
+ return 434;
+ }
+
+ // set the cookie of the new person
+ // the user just switched identity
+ registry_set_person_cookie(w, np);
+
+ // generate the response
+ registry_json_header(host, w, "switch", REGISTRY_STATUS_OK);
+ buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
+ registry_json_footer(w);
+
+ registry_unlock();
+ return 200;
+}
- return 0;
+// ----------------------------------------------------------------------------
+// STATISTICS
+
+void registry_statistics(void) {
+ if(!registry.enabled) return;
+
+ static RRDSET *sts = NULL, *stc = NULL, *stm = NULL;
+
+ if(!sts) sts = rrdset_find_localhost("netdata.registry_sessions");
+ if(!sts) {
+ sts = rrdset_create_localhost("netdata", "registry_sessions", NULL, "registry", NULL
+ , "NetData Registry Sessions", "session", 131000, localhost->rrd_update_every
+ , RRDSET_TYPE_LINE);
+
+ rrddim_add(sts, "sessions", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ }
+ else rrdset_next(sts);
+
+ rrddim_set(sts, "sessions", registry.usages_count);
+ rrdset_done(sts);
+
+ // ------------------------------------------------------------------------
+
+ if(!stc) stc = rrdset_find_localhost("netdata.registry_entries");
+ if(!stc) {
+ stc = rrdset_create_localhost("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries"
+ , "entries", 131100, localhost->rrd_update_every, RRDSET_TYPE_LINE);
+
+ 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);
+ }
+ else rrdset_next(stc);
+
+ rrddim_set(stc, "persons", registry.persons_count);
+ rrddim_set(stc, "machines", registry.machines_count);
+ rrddim_set(stc, "urls", registry.urls_count);
+ rrddim_set(stc, "persons_urls", registry.persons_urls_count);
+ rrddim_set(stc, "machines_urls", registry.machines_urls_count);
+ rrdset_done(stc);
+
+ // ------------------------------------------------------------------------
+
+ if(!stm) stm = rrdset_find_localhost("netdata.registry_mem");
+ if(!stm) {
+ stm = rrdset_create_localhost("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB"
+ , 131300, localhost->rrd_update_every, RRDSET_TYPE_STACKED);
+
+ 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);
+ }
+ else rrdset_next(stm);
+
+ rrddim_set(stm, "persons", registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+ rrddim_set(stm, "machines", registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+ rrddim_set(stm, "urls", registry.urls_memory);
+ rrddim_set(stm, "persons_urls", registry.persons_urls_memory);
+ rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
+ rrdset_done(stm);
}