#include "dictionary.h"
#include "appconfig.h"
+#include "web_client.h"
+#include "registry.h"
+
#define REGISTRY_URL_FLAGS_DEFAULT 0x00
#define REGISTRY_URL_FLAGS_EXPIRED 0x01
// COMMON structures
struct registry {
+ int enabled;
+
+ // counters / statistics
unsigned long long persons_count;
unsigned long long machines_count;
unsigned long long usages_count;
unsigned long long persons_urls_count;
unsigned long long machines_urls_count;
+ // files
char *pathname;
char *db_filename;
char *log_filename;
FILE *registry_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 url {
- uint32_t links;
- uint16_t len;
- char url[1];
+ 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 url URL;
// A machine
struct machine {
- char guid[36 + 1];
+ char guid[36 + 1]; // the GUID
DICTIONARY *urls; // MACHINE_URL *
// for each PERSON-URL pair we keep this
struct person_url {
- URL *url; // de-duplicated URL
- MACHINE *machine;
+ URL *url; // de-duplicated URL
+ 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 name[1]; // the name of the URL, as known by the user
+ // dynamically allocated to fit properly
};
typedef struct person_url PERSON_URL;
// A person
struct person {
- char guid[36 + 1];
+ char guid[36 + 1]; // the person GUID
- DICTIONARY *urls; // PERSON_URL *
+ DICTIONARY *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
};
typedef struct person PERSON;
-extern PERSON *registry_request(const char *person_guid, const char *machine_guid, const char *url, time_t when);
+
+// ----------------------------------------------------------------------------
+// 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(PERSON *p) {
+ (void)p;
+ pthread_mutex_lock(®istry.person_urls_lock);
+}
+
+static inline void registry_person_urls_unlock(PERSON *p) {
+ (void)p;
+ pthread_mutex_unlock(®istry.person_urls_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(MACHINE *m) {
+ (void)m;
+ pthread_mutex_lock(®istry.machine_urls_lock);
+}
+
+static inline void registry_machine_urls_unlock(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);
+}
+
+
+// ----------------------------------------------------------------------------
+// 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 PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when);
+extern PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
+
// ----------------------------------------------------------------------------
// URL
-static inline URL *registry_url_allocate(const char *url) {
- size_t len = strlen(url);
+static inline URL *registry_url_allocate_nolock(const char *url, size_t urllen) {
+ debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(URL) + urllen);
+ URL *u = malloc(sizeof(URL) + urllen);
+ if(!u) fatal("Cannot allocate %zu bytes for URL '%s'", sizeof(URL) + urllen);
- debug(D_REGISTRY, "Registry: registry_url_allocate('%s'): allocating %zu bytes", url, sizeof(URL) + len);
- URL *u = malloc(sizeof(URL) + len);
- if(!u) fatal("Cannot allocate %zu bytes for URL '%s'", sizeof(URL) + len);
+ // a simple strcpy() should do the job
+ // but I prefer to be safe, since the caller specified urllen
+ strncpy(u->url, url, urllen);
+ u->url[urllen] = '\0';
- strcpy(u->url, url);
- u->len = len;
+ u->len = urllen;
u->links = 0;
- debug(D_REGISTRY, "Registry: registry_url_allocate('%s'): indexing it", url);
+ debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url);
dictionary_set(registry.urls, u->url, u, sizeof(URL));
return u;
}
-static inline URL *registry_url_get(const char *url) {
+static inline URL *registry_url_get(const char *url, size_t urllen) {
debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
+ registry_urls_lock();
+
URL *u = dictionary_get(registry.urls, url);
if(!u) {
- u = registry_url_allocate(url);
+ u = registry_url_allocate_nolock(url, urllen);
registry.urls_count++;
}
+ registry_urls_unlock();
+
return u;
}
-static inline void registry_url_link(URL *u) {
+static inline void registry_url_link_nolock(URL *u) {
u->links++;
- debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): This URL has now %u links", u->url, 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(URL *u) {
+static inline void registry_url_unlink_nolock(URL *u) {
u->links--;
if(!u->links) {
- debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): No more links for this URL", u->url);
+ debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url);
dictionary_del(registry.urls, u->url);
free(u);
}
else
- debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): This URL has %u links left", u->url, u->links);
+ debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links);
}
debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url);
dictionary_set(m->urls, u->url, mu, sizeof(MACHINE_URL));
- registry_url_link(u);
+ registry_url_link_nolock(u);
return mu;
}
static inline MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
MACHINE *m = NULL;
+ registry_machines_lock();
+
if(likely(machine_guid && *machine_guid)) {
// validate it is a GUID
- uuid_t uuid;
- if(uuid_parse(machine_guid, uuid) == -1) {
+ char buf[36 + 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 {
- char buf[36 + 1];
- uuid_unparse_lower(uuid, buf);
- if(strcmp(machine_guid, buf))
- info("Registry: machine guid '%s' and re-generated '%s' differ!", machine_guid, buf);
-
machine_guid = buf;
m = registry_machine_find(machine_guid);
if(!m) {
}
}
+ registry_machines_unlock();
+
return m;
}
return dictionary_get(registry.persons, person_guid);
}
-static inline PERSON_URL *registry_person_url_allocate(PERSON *p, MACHINE *m, URL *u, time_t when) {
- debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(PERSON_URL));
- PERSON_URL *pu = malloc(sizeof(PERSON_URL));
- if(!pu) fatal("registry_person_link_to_url('%s', '%s', '%s'): cannot allocate %zu bytes.", p->guid, m->guid, u->url, sizeof(PERSON_URL));
+static inline PERSON_URL *registry_person_url_allocate(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when) {
+ debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(PERSON_URL) + namelen);
+ PERSON_URL *pu = malloc(sizeof(PERSON_URL) + namelen);
+ if(!pu) fatal("registry_person_link_to_url('%s', '%s', '%s'): cannot allocate %zu bytes.", p->guid, m->guid, u->url, sizeof(PERSON_URL) + namelen);
+
+ // a simple strcpy() should do the job
+ // but I prefer to be safe, since the caller specified urllen
+ strncpy(pu->name, name, namelen);
+ pu->name[namelen] = '\0';
pu->machine = m;
pu->first_t = pu->last_t = when;
debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
dictionary_set(p->urls, u->url, pu, sizeof(PERSON_URL));
- registry_url_link(u);
+ registry_url_link_nolock(u);
return pu;
}
static inline PERSON *registry_person_get(const char *person_guid, time_t when) {
PERSON *p = NULL;
+ registry_persons_lock();
+
if(person_guid && *person_guid) {
+ char buf[36 + 1];
// validate it is a GUID
- uuid_t uuid;
- if(uuid_parse(person_guid, uuid) == -1) {
+ if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
- }
else {
- char buf[36 + 1];
- uuid_unparse_lower(uuid, buf);
- if(strcmp(person_guid, buf))
- info("Registry: person guid '%s' and re-generated '%s' differ!", person_guid, buf);
-
person_guid = buf;
p = registry_person_find(person_guid);
if(!p) person_guid = NULL;
registry.persons_count++;
}
+ registry_persons_unlock();
+
return p;
}
// ----------------------------------------------------------------------------
// LINKING OF OBJECTS
-static inline PERSON_URL *registry_person_link_to_url(PERSON *p, MACHINE *m, URL *u, time_t when) {
+static inline PERSON_URL *registry_person_link_to_url(PERSON *p, MACHINE *m, 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);
+
PERSON_URL *pu = dictionary_get(p->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, when);
+ pu = registry_person_url_allocate(p, m, u, name, namelen, when);
registry.persons_urls_count++;
}
else {
p->usages++;
p->last_t = when;
- if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED)
- info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url);
+ 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 MACHINE_URL *registry_machine_link_to_url(PERSON *p, MACHINE *m, 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);
+
MACHINE_URL *mu = dictionary_get(m->urls, u->url);
if(!mu) {
debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
m->usages++;
m->last_t = when;
- if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED)
- info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url);
+ 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 void registry_log(const char action, PERSON *p, MACHINE *m, URL *u) {
- if(likely(registry.registry_log_fp))
- fprintf(registry.registry_log_fp, "%c\t%08x\t%s\t%s\t%s\n",
+static inline void registry_log(const char action, PERSON *p, MACHINE *m, URL *u, char *name) {
+ if(likely(registry.registry_log_fp)) {
+ // we lock only if the file is open
+ // to allow replaying the log at registry_log_load()
+ registry_log_lock();
+
+ fprintf(registry.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
);
+
+ registry_log_unlock();
+ }
}
-void registry_log_open(void) {
+void registry_log_open_nolock(void) {
registry.registry_log_fp = fopen(registry.log_filename, "a");
if(registry.registry_log_fp) {
}
}
-void registry_log_close(void) {
+void registry_log_close_nolock(void) {
if(registry.registry_log_fp)
fclose(registry.registry_log_fp);
registry.registry_log_fp = NULL;
}
-void registry_log_recreate(void) {
+void registry_log_recreate_nolock(void) {
if(registry.registry_log_fp != NULL) {
- fclose(registry.registry_log_fp);
+ registry_log_close_nolock();
registry.registry_log_fp = fopen(registry.log_filename, "w");
if(registry.registry_log_fp) fclose(registry.registry_log_fp);
registry.registry_log_fp = NULL;
- registry_log_open();
+ registry_log_open_nolock();
}
+
}
int registry_log_load(void) {
char *s, buf[4096 + 1];
- size_t line = 0;
+ size_t line = -1;
- registry_log_close();
+ // 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) {
+ if(!fp)
error("Registry: cannot open registry file: %s", registry.log_filename);
- return 0;
- }
-
- size_t len = 0;
- while((s = fgets_trim_len(buf, 4096, fp, &len))) {
- line++;
-
- switch(s[0]) {
- case 'A':
- // verify it is valid
- if(unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t'))
- error("Registry log line %u is wrong (len = %zu).", line, len);
+ else {
+ 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 %u is wrong (len = %zu).", line, len);
+ continue;
+ }
+
+ s[1] = s[10] = s[47] = s[84] = '\0';
+
+ // skip the name to find the url
+ char *url = &s[85];
+ while(*url && *url != '\t') url++;
+ if(!*url) {
+ error("Registry: log line %u does not have a url.", line);
+ continue;
+ }
+ *url++ = '\0';
+
+ if(s[0] == 'A')
+ registry_request_access(&s[11], &s[48], url, &s[85], strtoul(&s[2], NULL, 16));
+ else
+ registry_request_delete(&s[11], &s[48], url, &s[85], strtoul(&s[2], NULL, 16));
- s[1] = s[10] = s[47] = s[84] = '\0';
- registry_request(&s[11], &s[48], &s[85], strtoul(&s[2], NULL, 16));
- break;
+ break;
- default:
- error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s);
- break;
+ default:
+ error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s);
+ break;
+ }
}
}
- registry_log_open();
+ // open the log again
+ registry_log_open_nolock();
- return 0;
+ return line;
}
// ----------------------------------------------------------------------------
// REGISTRY REQUESTS
-PERSON *registry_request(const char *person_guid, const char *machine_guid, const char *url, time_t when) {
- debug(D_REGISTRY, "registry_request('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
+PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
+ debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
MACHINE *m = registry_machine_get(machine_guid, when);
if(!m) return NULL;
- URL *u = registry_url_get(url);
+ // make sure the name is valid
+ size_t namelen;
+ name = registry_fix_machine_name(name, &namelen);
+
+ size_t urllen;
+ url = registry_fix_url(url, &urllen);
+
+ URL *u = registry_url_get(url, urllen);
PERSON *p = registry_person_get(person_guid, when);
- registry_person_link_to_url(p, m, u, when);
+ registry_person_link_to_url(p, m, u, name, namelen, when);
registry_machine_link_to_url(p, m, u, when);
- registry_log('A', p, m, u);
+ registry_log('A', p, m, u, name);
registry.usages_count++;
return p;
}
+// verify the person, the machine and the URL exist in our DB
+PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, PERSON **pp, MACHINE **mm) {
+ char pbuf[36 + 1], mbuf[36 + 1];
+
+ if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
+ info("Registry Request Verification: invalid request! person: '%s', machine '%s', url '%s'", person_guid?person_guid:"UNSET", machine_guid?machine_guid:"UNSET", url?url:"UNSET");
+ return NULL;
+ }
+
+ // normalize the url
+ url = registry_fix_url(url, NULL);
+
+ // make sure the person GUID is valid
+ if(registry_regenerate_guid(person_guid, pbuf) == -1) {
+ info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ person_guid = pbuf;
+
+ // make sure the machine GUID is valid
+ if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
+ info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ machine_guid = mbuf;
+
+ // make sure the machine exists
+ MACHINE *m = registry_machine_find(machine_guid);
+ if(!m) {
+ info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ if(mm) *mm = m;
+
+ // make sure the person exist
+ PERSON *p = registry_person_find(person_guid);
+ if(!p) {
+ info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ if(pp) *pp = p;
+
+ PERSON_URL *pu = dictionary_get(p->urls, url);
+ if(!pu) {
+ info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ return pu;
+}
+
+PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
+ (void)when;
+
+ PERSON *p = NULL;
+ MACHINE *m = NULL;
+ PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
+ if(!pu || !p || !m) return NULL;
+
+ // normalize the url
+ delete_url = registry_fix_url(delete_url, NULL);
+
+ // make sure the user is not deleting the url it uses
+ if(!strcmp(delete_url, pu->url->url)) {
+ info("Registry Delete Request: delete URL is the one currently accessing, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
+ return NULL;
+ }
+
+ registry_person_urls_lock(p);
+
+ PERSON_URL *dpu = dictionary_get(p->urls, delete_url);
+ if(!dpu) {
+ info("Registry Delete Request: URL not found for person, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
+ registry_person_urls_unlock(p);
+ return NULL;
+ }
+
+ registry_log('D', p, m, pu->url, dpu->url->url);
+
+ dictionary_del(p->urls, dpu->url->url);
+ registry_url_unlink_nolock(dpu->url);
+ free(dpu);
+
+ registry_person_urls_unlock(p);
+ return p;
+}
+
+MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
+ (void)when;
+
+ char mbuf[36 + 1];
+
+ PERSON *p = NULL;
+ MACHINE *m = NULL;
+ PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
+ if(!pu || !p || !m) return NULL;
+
+ // make sure the machine GUID is valid
+ if(registry_regenerate_guid(request_machine, mbuf) == -1) {
+ info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
+ return NULL;
+ }
+ request_machine = mbuf;
+
+ // make sure the machine exists
+ m = registry_machine_find(request_machine);
+ if(!m) {
+ info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
+ return NULL;
+ }
+
+ // Verify the user has in the past accessed this machine
+ // We will walk through the PERSON_URLs to find the machine
+ // linking to our machine
+
+ // C magic !
+
+ // a structure to pass to the dictionary_get_all() callback handler
+ struct machine_request_callback_data {
+ MACHINE *find_this_machine;
+ PERSON_URL *result;
+ } rdata = { m, NULL };
+
+ // the callback function
+ // this will be run for every PERSON_URL of this PERSON
+ int machine_request_callback(void *entry, void *data) {
+ PERSON_URL *mypu = (PERSON_URL *)entry;
+ struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
+
+ if(mypu->machine == myrdata->find_this_machine) {
+ myrdata->result = mypu;
+ return -1; // this will also stop the walk through
+ }
+
+ return 0; // continue
+ }
+
+ // request a walk through on the dictionary
+ // no need for locking here, the underlying dictionary has its own
+ dictionary_get_all(p->urls, machine_request_callback, &rdata);
+
+ if(rdata.result)
+ return m;
+
+ return NULL;
+}
+
+
+// ----------------------------------------------------------------------------
+// REGISTRY JSON generation
+
+#ifndef REGISTRY_STANDALONE_TESTS
+
+static inline void registry_json_header(struct web_client *w, int status) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "{\n\"success\": %s", status?"true":"false");
+}
+
+static inline void registry_json_footer(struct web_client *w) {
+ buffer_strcat(w->response.data, "\n}\n");
+}
+
+static inline int registry_json_redirect(struct web_client *w) {
+ registry_json_header(w, 0);
+ buffer_sprintf(w->response.data, "\"registries\": \"%s\"", config_get("global", "users registries", "reg1.mynetdata.io reg2.mynetdata.io reg3.mynetdata.io"));
+ registry_json_footer(w);
+ return 200;
+}
+
+int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
+ if(!registry.enabled)
+ return registry_json_redirect(w);
+
+ PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
+ if(!p) {
+ registry_json_header(w, 0);
+ registry_json_footer(w);
+ return 400;
+ }
+
+ // FIXME -- make this a permanent cookie
+ strncpy(w->cookie, p->guid, COOKIE_MAX);
+ w->cookie[COOKIE_MAX] = '\0';
+
+ registry_json_header(w, 1);
+
+ // FIXME - print an array of all URLs / Machines / Names
+
+ buffer_strcat(w->response.data, "\n}\n");
+ return 200;
+}
+
+int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
+ if(!registry.enabled)
+ return registry_json_redirect(w);
+
+ PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
+ if(!p) {
+ registry_json_header(w, 0);
+ registry_json_footer(w);
+ return 400;
+ }
+
+ registry_json_header(w, 1);
+ registry_json_footer(w);
+ return 200;
+}
+
+int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
+ if(!registry.enabled)
+ return registry_json_redirect(w);
+
+ MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
+ if(!m) {
+ registry_json_header(w, 0);
+ registry_json_footer(w);
+ return 400;
+ }
+
+ registry_json_header(w, 1);
+
+ // FIXME - print an array of all URLs the machine is accessible
+
+ registry_json_footer(w);
+ return 200;
+}
+
+#endif /* REGISTRY_STANDALONE_TESTS */
+
// ----------------------------------------------------------------------------
// REGISTRY LOAD/SAVE
mu->url->url
);
+ // error handling is done at registry_save()
+
return ret;
}
ret += ret2;
}
+ // error handling is done at registry_save()
+
return ret;
}
debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
- int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\n",
+ int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
pu->first_t,
pu->last_t,
pu->usages,
pu->flags,
pu->machine->guid,
+ pu->name,
pu->url->url
);
+ // error handling is done at registry_save()
+
return ret;
}
ret += ret2;
}
+ // error handling is done at registry_save()
+
return ret;
}
-static int registry_save(void) {
+int registry_save(void) {
+ if(!registry.enabled) return -1;
+
char tmp_filename[FILENAME_MAX + 1];
char old_filename[FILENAME_MAX + 1];
snprintf(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
snprintf(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
+ // make sure the log is not updated
+ registry_log_lock();
+
debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
FILE *fp = fopen(tmp_filename, "w");
if(!fp) {
error("Registry: Cannot create file: %s", tmp_filename);
+ registry_log_unlock();
return -1;
}
if(bytes1 < 0) {
error("Registry: Cannot save registry machines - return value %d", bytes1);
fclose(fp);
+ registry_log_unlock();
return bytes1;
}
debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
if(bytes2 < 0) {
error("Registry: Cannot save registry persons - return value %d", bytes2);
fclose(fp);
+ registry_log_unlock();
return 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,
+ registry.urls_count,
+ registry.persons_urls_count,
+ registry.machines_urls_count
+ );
+
fclose(fp);
errno = 0;
// it has been moved successfully
// discard the current registry log
- registry_log_recreate();
+ registry_log_recreate_nolock();
}
}
+ // continue operations
+ registry_log_unlock();
+
return -1;
}
line++;
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')) {
+ error("Registry totals line %u 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'))
+ if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
error("Registry person line %u 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));
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'))
+ if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
error("Registry person line %u 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));
case 'U': // person URL
if(unlikely(!p)) {
error("Registry: ignoring line %zu, no person loaded: %s", line, s);
- break;
+ 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')
+ if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
error("Registry person URL line %u is wrong (len = %zu).", line, len);
+ continue;
+ }
s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
- u = registry_url_allocate(&s[69]);
+
+ // skip the name to find the url
+ char *url = &s[69];
+ while(*url && *url != '\t') url++;
+ if(!*url) {
+ error("Registry person URL line %u does not have a url.", line);
+ continue;
+ }
+ *url++ = '\0';
+
+ u = registry_url_allocate_nolock(url, strlen(url));
time_t first_t = strtoul(&s[2], NULL, 16);
m = registry_machine_find(&s[32]);
if(!m) m = registry_machine_allocate(&s[32], first_t);
- PERSON_URL *pu = registry_person_url_allocate(p, m, u, first_t);
+ PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
pu->last_t = strtoul(&s[11], NULL, 16);
pu->usages = strtoul(&s[20], NULL, 16);
pu->flags = strtoul(&s[29], NULL, 16);
- debug(D_REGISTRY, "Registry loaded person URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, 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", u->url, pu->name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
break;
case 'V': // machine URL
if(unlikely(!m)) {
error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
- break;
+ continue;
}
// verify it is valid
- if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t')
+ if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
error("Registry person URL line %u is wrong (len = %zu).", line, len);
+ continue;
+ }
s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
- u = registry_url_allocate(&s[32]);
+ u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
mu->last_t = strtoul(&s[11], NULL, 16);
void registry_init(void) {
char filename[FILENAME_MAX + 1];
-
registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR);
if(mkdir(registry.pathname, 0644) == -1 && errno != EEXIST)
error("Cannot create directory '%s'", registry.pathname);
debug(D_REGISTRY, "Registry: creating global registry dictionary for urls.");
registry.urls = dictionary_create(DICTIONARY_FLAGS);
- registry_log_open();
+ pthread_mutex_init(®istry.persons_lock, NULL);
+ pthread_mutex_init(®istry.machines_lock, NULL);
+ pthread_mutex_init(®istry.urls_lock, NULL);
+ pthread_mutex_init(®istry.person_urls_lock, NULL);
+ pthread_mutex_init(®istry.machine_urls_lock, NULL);
+
+ registry_log_open_nolock();
registry_load();
+
+ registry.enabled = 1;
}
void registry_free(void) {
+ if(!registry.enabled) return;
+
+ // we need to destroy the dictionaries ourselves
+ // since the dictionaries use memory we allocated
+
while(registry.persons->values_index.root) {
PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value;
dictionary_del(p->urls, pu->url->url);
debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url);
- registry_url_unlink(pu->url);
+ registry_url_unlink_nolock(pu->url);
debug(D_REGISTRY, "Registry: freeing person url");
free(pu);
dictionary_del(m->urls, mu->url->url);
debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
- registry_url_unlink(mu->url);
+ registry_url_unlink_nolock(mu->url);
debug(D_REGISTRY, "Registry: freeing machine url");
free(mu);
free(m);
}
+ // and free the memory of remaining dictionary structures
+
debug(D_REGISTRY, "Registry: destroying persons dictionary");
dictionary_destroy(registry.persons);
}
+#ifdef REGISTRY_STANDALONE_TESTS
+
// ----------------------------------------------------------------------------
// TESTS
for(u = 0; u < users ; u++) {
if(++m == machines) m = 0;
- PERSON *p = registry_request(NULL, machines_guids[m], machines_urls[m], now);
+ PERSON *p = registry_request_access(NULL, machines_guids[m], machines_urls[m], "test", now);
users_guids[u] = p->guid;
}
print_stats(u, start, timems());
for(u = 0; u < users ; u++) {
if(++m == machines) m = 0;
- PERSON *p = registry_request(users_guids[u], machines_guids[m], machines_urls[m], now);
+ PERSON *p = registry_request_access(users_guids[u], machines_guids[m], machines_urls[m], "test", now);
if(p->guid != users_guids[u])
fprintf(stderr, "ERROR: expected to get user guid '%s' but git '%s'", users_guids[u], p->guid);
for(u = 0; u < users ; u++) {
if(++m == machines) m = 0;
- PERSON *p = registry_request(users_guids[u], machines_guids[m], machines_urls[m], now);
+ PERSON *p = registry_request_access(users_guids[u], machines_guids[m], machines_urls[m], "test", now);
if(p->guid != users_guids[u])
fprintf(stderr, "ERROR: expected to get user guid '%s' but git '%s'", users_guids[u], p->guid);
uint32_t tu = random() * users / RAND_MAX;
uint32_t tm = random() * machines / RAND_MAX;
- PERSON *p = registry_request(users_guids[tu], machines_guids[tm], machines_urls[tm], now);
+ PERSON *p = registry_request_access(users_guids[tu], machines_guids[tm], machines_urls[tm], "test", now);
if(p->guid != users_guids[tu])
fprintf(stderr, "ERROR: expected to get user guid '%s' but git '%s'", users_guids[tu], p->guid);
uint32_t tu = random() * users / RAND_MAX;
uint32_t tm = random() * machines2 / RAND_MAX;
- PERSON *p = registry_request(users_guids[tu], machines_guids[tm], machines_urls[tm], now);
+ PERSON *p = registry_request_access(users_guids[tu], machines_guids[tm], machines_urls[tm], "test", now);
if(p->guid != users_guids[tu])
fprintf(stderr, "ERROR: expected to get user guid '%s' but git '%s'", users_guids[tu], p->guid);
else if (random() % 1000 == 123)
url = machines_urls[random() * machines2 / RAND_MAX];
- PERSON *p = registry_request(users_guids[tu], machines_guids[tm], url, now);
+ PERSON *p = registry_request_access(users_guids[tu], machines_guids[tm], url, "test", now);
if (p->guid != users_guids[tu])
fprintf(stderr, "ERROR: expected to get user guid '%s' but git '%s'", users_guids[tu], p->guid);
// TESTING
int main(int argc, char **argv) {
- debug_flags = 0xFFFFFFFF;
- //test1(argc, argv);
- //exit(0);
+ // debug_flags = 0xFFFFFFFF;
+ // test1(argc, argv);
+ // exit(0);
(void)argc;
(void)argv;
int i = 2;
fprintf(stderr, "\n\nADDING ENTRY\n");
- // p1 = registry_request("2c95abd0-1542-11e6-8c66-00508db7e9c9", "7c173980-145c-11e6-b86f-00508db7e9c1", "http://localhost:19999/", time(NULL));
+ p1 = registry_request_access("2c95abd0-1542-11e6-8c66-00508db7e9c9", "7c173980-145c-11e6-b86f-00508db7e9c1", "http://localhost:19999/", "test", time(NULL));
if(0)
while(i--) {
#ifdef REGISTRY_STDOUT_DUMP
fprintf(stderr, "\n\nADDING ENTRY\n");
#endif /* REGISTRY_STDOUT_DUMP */
- p1 = registry_request(NULL, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://localhost:19999/", time(NULL));
+ p1 = registry_request_access(NULL, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://localhost:19999/", "test", time(NULL));
#ifdef REGISTRY_STDOUT_DUMP
fprintf(stderr, "\n\nADDING ANOTHER URL\n");
#endif /* REGISTRY_STDOUT_DUMP */
- p1 = registry_request(p1->guid, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://127.0.0.1:19999/", time(NULL));
+ p1 = registry_request_access(p1->guid, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://127.0.0.1:19999/", "test", time(NULL));
#ifdef REGISTRY_STDOUT_DUMP
fprintf(stderr, "\n\nADDING ANOTHER URL\n");
#endif /* REGISTRY_STDOUT_DUMP */
- p1 = registry_request(p1->guid, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://my.server:19999/", time(NULL));
+ p1 = registry_request_access(p1->guid, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://my.server:19999/", "test", time(NULL));
#ifdef REGISTRY_STDOUT_DUMP
fprintf(stderr, "\n\nADDING ANOTHER MACHINE\n");
#endif /* REGISTRY_STDOUT_DUMP */
- p1 = registry_request(p1->guid, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://my.server:19999/", time(NULL));
+ p1 = registry_request_access(p1->guid, "7c173980-145c-11e6-b86f-00508db7e9c1", "http://my.server:19999/", "test", time(NULL));
#ifdef REGISTRY_STDOUT_DUMP
fprintf(stderr, "\n\nADDING ANOTHER PERSON\n");
#endif /* REGISTRY_STDOUT_DUMP */
- p2 = registry_request(NULL, "7c173980-145c-11e6-b86f-00508db7e9c3", "http://localhost:19999/", time(NULL));
+ p2 = registry_request_access(NULL, "7c173980-145c-11e6-b86f-00508db7e9c3", "http://localhost:19999/", "test", time(NULL));
#ifdef REGISTRY_STDOUT_DUMP
fprintf(stderr, "\n\nADDING ANOTHER MACHINE\n");
#endif /* REGISTRY_STDOUT_DUMP */
- p2 = registry_request(p2->guid, "7c173980-145c-11e6-b86f-00508db7e9c3", "http://localhost:19999/", time(NULL));
+ p2 = registry_request_access(p2->guid, "7c173980-145c-11e6-b86f-00508db7e9c3", "http://localhost:19999/", "test", time(NULL));
}
fprintf(stderr, "\n\nSAVE\n");
registry_free();
return 0;
}
+
+#endif /* REGISTRY_STANDALONE_TESTS */
#include "global_statistics.h"
#include "rrd.h"
#include "rrd2json.h"
+#include "registry.h"
#include "web_client.h"
#include "../config.h"
}
w->last_url[0] = '\0';
+ w->cookie[0] = '\0';
w->mode = WEB_CLIENT_MODE_NORMAL;
if(!name || !*name) continue;
if(!value || !*value) continue;
- debug(D_WEB_CLIENT, "%llu: API v1 query param '%s' with value '%s'", w->id, name, value);
+ debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
// name and value are now the parameters
// they are not null and not empty
return ret;
}
+int web_client_api_request_v1_registry(struct web_client *w, char *url)
+{
+ char person_guid[36 + 1] = "";
+
+ debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
+
+ char *cookie = strstr(w->response.data->buffer, " " NETDATA_REGISTRY_COOKIE_NAME "=");
+ if(cookie) {
+ strncpy(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME) + 2], 36);
+ person_guid[36] = '\0';
+ }
+
+ char action = '\0';
+ char *machine_guid = NULL,
+ *machine_url = NULL,
+ *url_name = NULL,
+ *search_machine_guid = NULL,
+ *delete_url = NULL;
+
+ while(url) {
+ char *value = mystrsep(&url, "?&[]");
+ if (!value || !*value) continue;
+
+ char *name = mystrsep(&value, "=");
+ if (!name || !*name) continue;
+ if (!value || !*value) continue;
+
+ debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
+
+ if(!strcmp(name, "action")) {
+ if(!strcmp(value, "access")) action = 'A';
+ else if(!strcmp(value, "delete")) action = 'D';
+ else if(!strcmp(value, "search")) action = 'S';
+ }
+ else if(!strcmp(name, "machine"))
+ machine_guid = value;
+
+ else if(!strcmp(name, "url"))
+ machine_url = value;
+
+ else if(action == 'A') {
+ if(!strcmp(name, "name"))
+ url_name = value;
+ }
+ else if(action == 'D') {
+ if(!strcmp(name, "delete_url"))
+ delete_url = value;
+ }
+ else if(action == 'S') {
+ if(!strcmp(name, "for"))
+ search_machine_guid = value;
+ }
+ }
+
+ if((!action || !machine_guid || !machine_url) || (action == 'A' && !url_name) || (action == 'D' && !delete_url) || (action == 'S' && !search_machine_guid)) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Invalid registry request - required parameters missing.");
+ return 400;
+ }
+
+ switch(action) {
+ case 'A':
+ return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
+
+ case 'D':
+ return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, time(NULL));
+
+ case 'S':
+ return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, time(NULL));
+ }
+
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Invalid or no registry action.");
+ return 400;
+}
+
int web_client_api_request_v1(struct web_client *w, char *url)
{
+ static uint32_t data_hash = 0, chart_hash = 0, charts_hash = 0, registry_hash = 0;
+
+ if(unlikely(data_hash == 0)) {
+ data_hash = simple_hash("data");
+ chart_hash = simple_hash("chart");
+ charts_hash = simple_hash("charts");
+ registry_hash = simple_hash("registry");
+ }
+
// get the command
char *tok = mystrsep(&url, "/?&");
if(tok && *tok) {
debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
+ uint32_t hash = simple_hash(tok);
- if(strcmp(tok, "data") == 0)
+ if(hash == data_hash && !strcmp(tok, "data"))
return web_client_api_request_v1_data(w, url);
- else if(strcmp(tok, "chart") == 0)
+
+ else if(hash == chart_hash && !strcmp(tok, "chart"))
return web_client_api_request_v1_chart(w, url);
- else if(strcmp(tok, "charts") == 0)
+
+ else if(hash == charts_hash && !strcmp(tok, "charts"))
return web_client_api_request_v1_charts(w, url);
+
+ else if(hash == registry_hash && !strcmp(tok, "registry"))
+ return web_client_api_request_v1_registry(w, url);
+
else {
buffer_flush(w->response.data);
buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
}
*/
+// get the request buffer, just after the GET or OPTIONS
+// and find the url to be decoded, decode it and return
+// a newly allocated buffer with it
+static inline char *find_url_and_decode_it(char *request) {
+ char *e = request, *url = NULL;
+
+ // find the SPACE + "HTTP/"
+ while(*e) {
+ // find the space
+ while (*e && *e != ' ') e++;
+
+ // is it SPACE + "HTTP/" ?
+ if(*e && !strncmp(e, " HTTP/", 6))
+ break;
+ }
+
+ if(*e) {
+ // we have the end
+ *e = '\0';
+ url = url_decode(request);
+ *e = ' ';
+ }
+
+ return url;
+}
+
void web_client_process(struct web_client *w) {
int code = 500;
ssize_t bytes;
enable_gzip = 1;
#endif // NETDATA_WITH_ZLIB
- int datasource_type = DATASOURCE_DATATABLE_JSONP;
- //if(strstr(w->response.data->buffer, "X-DataSource-Auth"))
- // datasource_type = DATASOURCE_GOOGLE_JSON;
+ w->mode = WEB_CLIENT_MODE_NORMAL;
- char *buf = (char *)buffer_tostring(w->response.data);
- char *tok = strsep(&buf, " \r\n");
- char *url = NULL;
+ char *tok = (char *)buffer_tostring(w->response.data);
+ char *url = NULL, *encoded_url = NULL;
char *pointer_to_free = NULL; // keep url_decode() allocated buffer
- w->mode = WEB_CLIENT_MODE_NORMAL;
-
- if(buf && strcmp(tok, "GET") == 0) {
- tok = strsep(&buf, " \r\n");
- pointer_to_free = url = url_decode(tok);
- debug(D_WEB_CLIENT, "%llu: Processing HTTP GET on url '%s'.", w->id, url);
- }
- else if(buf && strcmp(tok, "OPTIONS") == 0) {
- tok = strsep(&buf, " \r\n");
- pointer_to_free = url = url_decode(tok);
- debug(D_WEB_CLIENT, "%llu: Processing HTTP OPTIONS on url '%s'.", w->id, url);
+ if(!strncmp(tok, "GET ", 4))
+ encoded_url = &tok[4];
+ else if(!strncmp(tok, "OPTIONS ", 8)) {
+ encoded_url = &tok[8];
w->mode = WEB_CLIENT_MODE_OPTIONS;
}
- else if (buf && strcmp(tok, "POST") == 0) {
- w->keepalive = 0;
- tok = strsep(&buf, " \r\n");
- pointer_to_free = url = url_decode(tok);
- debug(D_WEB_CLIENT, "%llu: I don't know how to handle POST with form data. Assuming it is a GET on url '%s'.", w->id, url);
+
+ if(encoded_url) {
+ pointer_to_free = url = find_url_and_decode_it(encoded_url);
+
+ if(url) debug(D_WEB_CLIENT, "%llu: Processing url '%s'.", w->id, url);
+ else debug(D_WEB_CLIENT, "%llu: Cannot find a valid URL in '%s'", w->id, encoded_url);
}
w->last_url[0] = '\0';
if(strcmp(tok, "api") == 0) {
// the client is requesting api access
- datasource_type = DATASOURCE_JSON;
code = web_client_api_request(w, url);
}
#ifdef NETDATA_INTERNAL_CHECKS
else if(strcmp(tok, "exit") == 0) {
- netdata_exit = 1;
code = 200;
w->response.data->contenttype = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
- buffer_strcat(w->response.data, "will do");
+
+ if(!netdata_exit)
+ buffer_strcat(w->response.data, "ok, will do...");
+ else
+ buffer_strcat(w->response.data, "I am doing it already");
+
+ netdata_exit = 1;
}
#endif
else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
// the client is requesting rrd data
- datasource_type = DATASOURCE_JSON;
- code = web_client_data_request(w, url, datasource_type);
+ code = web_client_data_request(w, url, DATASOURCE_JSON);
}
else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
// the client is requesting google datasource
- code = web_client_data_request(w, url, datasource_type);
+ code = web_client_data_request(w, url, DATASOURCE_DATATABLE_JSONP);
}
else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
// the client is requesting an rrd graph
else {
strcpy(w->last_url, "not a valid response");
- if(buf) debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, buf);
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
code = 500;
buffer_flush(w->response.data);
, date
);
+ if(w->cookie[0]) {
+ buffer_sprintf(w->response.header_output,
+ "Set-Cookie: %s\r\n",
+ w->cookie);
+ }
+
if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
buffer_strcat(w->response.header_output,
"Access-Control-Allow-Methods: GET, OPTIONS\r\n"
- "Access-Control-Allow-Headers: accept, x-requested-with\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "Access-Control-Allow-Headers: Accept, X-Requested-With, Content-Type, Cookie\r\n"
"Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
);
}