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
Costa Tsaousis (ktsaou) committed
Dec 31, 2016 at 21:08 UTC
ba97a9f4d4dd21b74af45e511470bcbbc60ad992
17 files changed
+1692
-1680
CMakeLists.txt
+8
-1
@@ -94,7 +94,14 @@ set(NETDATA_SOURCE_FILES
94
src/web_client.h
95
src/web_server.c
96
src/web_server.h
97
- src/socket.c src/socket.h)
97
+ src/socket.c src/socket.h
98
+ src/registry_url.c
99
+ src/registry_url.h
100
+ src/registry_internals.h
101
+ src/registry_person.c
102
+ src/registry_person.h
103
+ src/registry_machine.c
104
+ src/registry_machine.h src/registry_internals.c src/registry_init.c src/registry_db.c src/registry_log.c)
105
106
set(APPS_PLUGIN_SOURCE_FILES
107
src/appconfig.c
src/Makefile.am
+7
@@ -50,6 +50,13 @@ netdata_SOURCES = \
50
procfile.c procfile.h \
51
proc_self_mountinfo.c proc_self_mountinfo.h \
52
registry.c registry.h \
53
+ registry_internals.c registry_internals.h \
54
+ registry_url.c registry_url.h \
55
+ registry_person.c registry_person.h \
56
+ registry_machine.c registry_machine.h \
57
+ registry_init.c \
58
+ registry_db.c \
59
+ registry_log.c \
60
rrd.c rrd.h \
61
rrd2json.c rrd2json.h \
62
storage_number.c storage_number.h \
src/common.h
+6
-4
@@ -170,6 +170,8 @@
170
#endif
171
#define abs(x) ((x < 0)? -x : x)
172
173
+#define GUID_LEN 36
174
+
175
extern void netdata_fix_chart_id(char *s);
176
extern void netdata_fix_chart_name(char *s);
177
@@ -198,10 +200,10 @@ extern void *mallocz_int(const char *file, const char *function, const unsigned
200
extern void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size);
201
extern void freez_int(const char *file, const char *function, const unsigned long line, void *ptr);
202
#else
201
-extern char *strdupz(const char *s);
202
-extern void *callocz(size_t nmemb, size_t size);
203
-extern void *mallocz(size_t size);
204
-extern void *reallocz(void *ptr, size_t size);
203
+extern char *strdupz(const char *s) __attribute__((returns_nonnull));
204
+extern void *callocz(size_t nmemb, size_t size) __attribute__((returns_nonnull));
205
+extern void *mallocz(size_t size) __attribute__((returns_nonnull));
206
+extern void *reallocz(void *ptr, size_t size) __attribute__((returns_nonnull));
207
extern void freez(void *ptr);
208
#endif
209
src/registry.c
+107
-1608
@@ -1,1016 +1,27 @@
1
#include "common.h"
2
3
-// ----------------------------------------------------------------------------
4
-// TODO
5
-//
6
-// 1. the default tracking cookie expires in 1 year, but the persons are not
7
-// removed from the db - this means the database only grows - ideally the
8
-// database should be cleaned in registry_save() for both on-disk and
9
-// on-memory entries.
10
-//
11
-// Cleanup:
12
-// i. Find all the PERSONs that have expired cookie
13
-// ii. For each of their PERSON_URLs:
14
-// - decrement the linked MACHINE links
15
-// - if the linked MACHINE has no other links, remove the linked MACHINE too
16
-// - remove the PERSON_URL
17
-//
18
-// 2. add protection to prevent abusing the registry by flooding it with
19
-// requests to fill the memory and crash it.
20
-//
21
-// Possible protections:
22
-// - limit the number of URLs per person
23
-// - limit the number of URLs per machine
24
-// - limit the number of persons
25
-// - limit the number of machines
26
-// - [DONE] limit the size of URLs
27
-// - [DONE] limit the size of PERSON_URL names
28
-// - limit the number of requests that add data to the registry,
29
-// per client IP per hour
30
-//
31
-// 3. lower memory requirements
32
-//
33
-// - embed avl structures directly into registry objects, instead of DICTIONARY
34
-// - store GUIDs in memory as UUID instead of char *
35
-// - do not track persons using the demo machines only
36
-// (i.e. start tracking them only when they access a non-demo machine)
37
-// - [DONE] do not track custom dashboards by default
38
-
39
-#define REGISTRY_URL_FLAGS_DEFAULT 0x00
40
-#define REGISTRY_URL_FLAGS_EXPIRED 0x01
41
-
42
-#define DICTIONARY_FLAGS DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE | DICTIONARY_FLAG_NAME_LINK_DONT_CLONE
43
-
44
-#define GUID_LEN 36
45
-
46
-// ----------------------------------------------------------------------------
47
-// COMMON structures
48
-
49
-struct registry {
50
- int enabled;
51
-
52
- char machine_guid[GUID_LEN + 1];
53
-
54
- // entries counters / statistics
55
- unsigned long long persons_count;
56
- unsigned long long machines_count;
57
- unsigned long long usages_count;
58
- unsigned long long urls_count;
59
- unsigned long long persons_urls_count;
60
- unsigned long long machines_urls_count;
61
- unsigned long long log_count;
62
-
63
- // memory counters / statistics
64
- unsigned long long persons_memory;
65
- unsigned long long machines_memory;
66
- unsigned long long urls_memory;
67
- unsigned long long persons_urls_memory;
68
- unsigned long long machines_urls_memory;
69
-
70
- // configuration
71
- unsigned long long save_registry_every_entries;
72
- char *registry_domain;
73
- char *hostname;
74
- char *registry_to_announce;
75
- time_t persons_expiration; // seconds to expire idle persons
76
- int verify_cookies_redirects;
77
-
78
- size_t max_url_length;
79
- size_t max_name_length;
80
-
81
- // file/path names
82
- char *pathname;
83
- char *db_filename;
84
- char *log_filename;
85
- char *machine_guid_filename;
86
-
87
- // open files
88
- FILE *log_fp;
89
-
90
- // the database
91
- DICTIONARY *persons; // dictionary of PERSON *, with key the PERSON.guid
92
- DICTIONARY *machines; // dictionary of MACHINE *, with key the MACHINE.guid
93
- DICTIONARY *urls; // dictionary of URL *, with key the URL.url
94
-
95
- // concurrency locking
96
- // we keep different locks for different things
97
- // so that many tasks can be completed in parallel
98
- pthread_mutex_t persons_lock;
99
- pthread_mutex_t machines_lock;
100
- pthread_mutex_t urls_lock;
101
- pthread_mutex_t person_urls_lock;
102
- pthread_mutex_t machine_urls_lock;
103
- pthread_mutex_t log_lock;
104
-} registry;
105
-
106
-
107
-// ----------------------------------------------------------------------------
108
-// URL structures
109
-// Save memory by de-duplicating URLs
110
-// so instead of storing URLs all over the place
111
-// we store them here and we keep pointers elsewhere
112
-
113
-struct registry_url {
114
- uint32_t links; // the number of links to this URL - when none is left, we free it
115
- uint16_t len; // the length of the URL in bytes
116
- char url[1]; // the URL - dynamically allocated to more size
117
-};
118
-typedef struct registry_url REGISTRY_URL;
119
-
120
-
121
-// ----------------------------------------------------------------------------
122
-// MACHINE structures
123
-
124
-// For each MACHINE-URL pair we keep this
125
-struct registry_machine_url {
126
- REGISTRY_URL *url; // de-duplicated URL
127
-
128
- uint8_t flags;
129
-
130
- uint32_t first_t; // the first time we saw this
131
- uint32_t last_t; // the last time we saw this
132
- uint32_t usages; // how many times this has been accessed
133
-};
134
-typedef struct registry_machine_url REGISTRY_MACHINE_URL;
135
-
136
-// A machine
137
-struct registry_machine {
138
- char guid[GUID_LEN + 1]; // the GUID
139
-
140
- uint32_t links; // the number of REGISTRY_PERSON_URL linked to this machine
141
-
142
- DICTIONARY *machine_urls; // MACHINE_URL *
143
-
144
- uint32_t first_t; // the first time we saw this
145
- uint32_t last_t; // the last time we saw this
146
- uint32_t usages; // how many times this has been accessed
147
-};
148
-typedef struct registry_machine REGISTRY_MACHINE;
149
-
150
-
151
-// ----------------------------------------------------------------------------
152
-// PERSON structures
153
-
154
-// for each PERSON-URL pair we keep this
155
-struct registry_person_url {
156
- REGISTRY_URL *url; // de-duplicated URL
157
- REGISTRY_MACHINE *machine; // link the MACHINE of this URL
158
-
159
- uint8_t flags;
160
-
161
- uint32_t first_t; // the first time we saw this
162
- uint32_t last_t; // the last time we saw this
163
- uint32_t usages; // how many times this has been accessed
164
-
165
- char machine_name[1]; // the name of the machine, as known by the user
166
- // dynamically allocated to fit properly
167
-};
168
-typedef struct registry_person_url REGISTRY_PERSON_URL;
169
-
170
-// A person
171
-struct registry_person {
172
- char guid[GUID_LEN + 1]; // the person GUID
173
-
174
- DICTIONARY *person_urls; // dictionary of PERSON_URL *
175
-
176
- uint32_t first_t; // the first time we saw this
177
- uint32_t last_t; // the last time we saw this
178
- uint32_t usages; // how many times this has been accessed
179
-};
180
-typedef struct registry_person REGISTRY_PERSON;
3
+#include "registry_internals.h"
4
5
+#define REGISTRY_STATUS_OK "ok"
6
+#define REGISTRY_STATUS_FAILED "failed"
7
+#define REGISTRY_STATUS_DISABLED "disabled"
8
9
// ----------------------------------------------------------------------------
10
// REGISTRY concurrency locking
11
186
-static inline void registry_persons_lock(void) {
187
- pthread_mutex_lock(®istry.persons_lock);
188
-}
189
-
190
-static inline void registry_persons_unlock(void) {
191
- pthread_mutex_unlock(®istry.persons_lock);
192
-}
193
-
194
-static inline void registry_machines_lock(void) {
195
- pthread_mutex_lock(®istry.machines_lock);
196
-}
197
-
198
-static inline void registry_machines_unlock(void) {
199
- pthread_mutex_unlock(®istry.machines_lock);
200
-}
201
-
202
-static inline void registry_urls_lock(void) {
203
- pthread_mutex_lock(®istry.urls_lock);
204
-}
205
-
206
-static inline void registry_urls_unlock(void) {
207
- pthread_mutex_unlock(®istry.urls_lock);
208
-}
209
-
210
-// ideally, we should not lock the whole registry for
211
-// updating a person's urls.
212
-// however, to save the memory required for keeping a
213
-// mutex (40 bytes) per person, we do...
214
-static inline void registry_person_urls_lock(REGISTRY_PERSON *p) {
215
- (void)p;
216
- pthread_mutex_lock(®istry.person_urls_lock);
217
-}
218
-
219
-static inline void registry_person_urls_unlock(REGISTRY_PERSON *p) {
220
- (void)p;
221
- pthread_mutex_unlock(®istry.person_urls_lock);
12
+static inline void registry_lock(void) {
13
+ pthread_mutex_lock(®istry.lock);
14
}
15
224
-// ideally, we should not lock the whole registry for
225
-// updating a machine's urls.
226
-// however, to save the memory required for keeping a
227
-// mutex (40 bytes) per machine, we do...
228
-static inline void registry_machine_urls_lock(REGISTRY_MACHINE *m) {
229
- (void)m;
230
- pthread_mutex_lock(®istry.machine_urls_lock);
231
-}
232
-
233
-static inline void registry_machine_urls_unlock(REGISTRY_MACHINE *m) {
234
- (void)m;
235
- pthread_mutex_unlock(®istry.machine_urls_lock);
236
-}
237
-
238
-static inline void registry_log_lock(void) {
239
- pthread_mutex_lock(®istry.log_lock);
240
-}
241
-
242
-static inline void registry_log_unlock(void) {
243
- pthread_mutex_unlock(®istry.log_lock);
16
+static inline void registry_unlock(void) {
17
+ pthread_mutex_unlock(®istry.lock);
18
}
19
20
21
// ----------------------------------------------------------------------------
248
-// common functions
249
-
250
-// parse a GUID and re-generated to be always lower case
251
-// this is used as a protection against the variations of GUIDs
252
-static inline int registry_regenerate_guid(const char *guid, char *result) {
253
- uuid_t uuid;
254
- if(unlikely(uuid_parse(guid, uuid) == -1)) {
255
- info("Registry: GUID '%s' is not a valid GUID.", guid);
256
- return -1;
257
- }
258
- else {
259
- uuid_unparse_lower(uuid, result);
260
-
261
-#ifdef NETDATA_INTERNAL_CHECKS
262
- if(strcmp(guid, result))
263
- info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result);
264
-#endif /* NETDATA_INTERNAL_CHECKS */
265
- }
266
-
267
- return 0;
268
-}
269
-
270
-// make sure the names of the machines / URLs do not contain any tabs
271
-// (which are used as our separator in the database files)
272
-// and are properly trimmed (before and after)
273
-static inline char *registry_fix_machine_name(char *name, size_t *len) {
274
- char *s = name?name:"";
275
-
276
- // skip leading spaces
277
- while(*s && isspace(*s)) s++;
278
-
279
- // make sure all spaces are a SPACE
280
- char *t = s;
281
- while(*t) {
282
- if(unlikely(isspace(*t)))
283
- *t = ' ';
284
-
285
- t++;
286
- }
287
-
288
- // remove trailing spaces
289
- while(--t >= s) {
290
- if(*t == ' ')
291
- *t = '\0';
292
- else
293
- break;
294
- }
295
- t++;
296
-
297
- if(likely(len))
298
- *len = (t - s);
299
-
300
- return s;
301
-}
302
-
303
-static inline char *registry_fix_url(char *url, size_t *len) {
304
- return registry_fix_machine_name(url, len);
305
-}
306
-
307
-
308
-// ----------------------------------------------------------------------------
309
-// forward definition of functions
310
-
311
-extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when);
312
-extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
313
-
314
-
315
-// ----------------------------------------------------------------------------
316
-// URL
317
-
318
-static inline REGISTRY_URL *registry_url_allocate_nolock(const char *url, size_t urllen) {
319
- // protection from too big URLs
320
- if(urllen > registry.max_url_length)
321
- urllen = registry.max_url_length;
322
-
323
- debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(REGISTRY_URL) + urllen);
324
- REGISTRY_URL *u = mallocz(sizeof(REGISTRY_URL) + urllen);
325
-
326
- // a simple strcpy() should do the job
327
- // but I prefer to be safe, since the caller specified urllen
328
- u->len = (uint16_t)urllen;
329
- strncpyz(u->url, url, u->len);
330
- u->links = 0;
331
-
332
- registry.urls_memory += sizeof(REGISTRY_URL) + urllen;
333
-
334
- debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url);
335
- dictionary_set(registry.urls, u->url, u, sizeof(REGISTRY_URL));
336
-
337
- return u;
338
-}
339
-
340
-static inline REGISTRY_URL *registry_url_get_nolock(const char *url, size_t urllen) {
341
- debug(D_REGISTRY, "Registry: registry_url_get_nolock('%s')", url);
342
-
343
- REGISTRY_URL *u = dictionary_get(registry.urls, url);
344
- if(!u) {
345
- u = registry_url_allocate_nolock(url, urllen);
346
- registry.urls_count++;
347
- }
348
-
349
- return u;
350
-}
351
-
352
-static inline REGISTRY_URL *registry_url_get(const char *url, size_t urllen) {
353
- debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
354
-
355
- registry_urls_lock();
356
-
357
- REGISTRY_URL *u = registry_url_get_nolock(url, urllen);
358
-
359
- registry_urls_unlock();
360
-
361
- return u;
362
-}
363
-
364
-static inline void registry_url_link_nolock(REGISTRY_URL *u) {
365
- u->links++;
366
- debug(D_REGISTRY, "Registry: registry_url_link_nolock('%s'): URL has now %u links", u->url, u->links);
367
-}
368
-
369
-static inline void registry_url_unlink_nolock(REGISTRY_URL *u) {
370
- u->links--;
371
- if(!u->links) {
372
- debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url);
373
- dictionary_del(registry.urls, u->url);
374
- freez(u);
375
- }
376
- else
377
- debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links);
378
-}
379
-
380
-
381
-// ----------------------------------------------------------------------------
382
-// MACHINE
383
-
384
-static inline REGISTRY_MACHINE *registry_machine_find(const char *machine_guid) {
385
- debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid);
386
- return dictionary_get(registry.machines, machine_guid);
387
-}
388
-
389
-static inline REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) {
390
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(REGISTRY_MACHINE_URL));
391
-
392
- REGISTRY_MACHINE_URL *mu = mallocz(sizeof(REGISTRY_MACHINE_URL));
393
-
394
- // mu->persons = dictionary_create(DICTIONARY_FLAGS);
395
- // dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
396
-
397
- mu->first_t = mu->last_t = (uint32_t)when;
398
- mu->usages = 1;
399
- mu->url = u;
400
- mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
401
-
402
- registry.machines_urls_memory += sizeof(REGISTRY_MACHINE_URL);
403
-
404
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url);
405
- dictionary_set(m->machine_urls, u->url, mu, sizeof(REGISTRY_MACHINE_URL));
406
- registry_url_link_nolock(u);
407
-
408
- return mu;
409
-}
410
-
411
-static inline REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) {
412
- debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(REGISTRY_MACHINE));
413
-
414
- REGISTRY_MACHINE *m = mallocz(sizeof(REGISTRY_MACHINE));
415
-
416
- strncpyz(m->guid, machine_guid, GUID_LEN);
417
-
418
- debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
419
- m->machine_urls = dictionary_create(DICTIONARY_FLAGS);
420
-
421
- m->first_t = m->last_t = (uint32_t)when;
422
- m->usages = 0;
423
-
424
- registry.machines_memory += sizeof(REGISTRY_MACHINE);
425
-
426
- registry.machines_count++;
427
- dictionary_set(registry.machines, m->guid, m, sizeof(REGISTRY_MACHINE));
428
-
429
- return m;
430
-}
431
-
432
-// 1. validate machine GUID
433
-// 2. if it is valid, find it or create it and return it
434
-// 3. if it is not valid, return NULL
435
-static inline REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
436
- REGISTRY_MACHINE *m = NULL;
437
-
438
- registry_machines_lock();
439
-
440
- if(likely(machine_guid && *machine_guid)) {
441
- // validate it is a GUID
442
- char buf[GUID_LEN + 1];
443
- if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1))
444
- info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
445
- else {
446
- machine_guid = buf;
447
- m = registry_machine_find(machine_guid);
448
- if(!m) m = registry_machine_allocate(machine_guid, when);
449
- }
450
- }
451
-
452
- registry_machines_unlock();
453
-
454
- return m;
455
-}
456
-
457
-
458
-// ----------------------------------------------------------------------------
459
-// PERSON
460
-
461
-static inline REGISTRY_PERSON *registry_person_find(const char *person_guid) {
462
- debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
463
- return dictionary_get(registry.persons, person_guid);
464
-}
465
-
466
-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) {
467
- // protection from too big names
468
- if(namelen > registry.max_name_length)
469
- namelen = registry.max_name_length;
470
-
471
- debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
472
- sizeof(REGISTRY_PERSON_URL) + namelen);
473
-
474
- REGISTRY_PERSON_URL *pu = mallocz(sizeof(REGISTRY_PERSON_URL) + namelen);
475
-
476
- // a simple strcpy() should do the job
477
- // but I prefer to be safe, since the caller specified urllen
478
- strncpyz(pu->machine_name, name, namelen);
479
-
480
- pu->machine = m;
481
- pu->first_t = pu->last_t = when;
482
- pu->usages = 1;
483
- pu->url = u;
484
- pu->flags = REGISTRY_URL_FLAGS_DEFAULT;
485
- m->links++;
486
-
487
- registry.persons_urls_memory += sizeof(REGISTRY_PERSON_URL) + namelen;
488
-
489
- debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
490
- dictionary_set(p->person_urls, u->url, pu, sizeof(REGISTRY_PERSON_URL));
491
- registry_url_link_nolock(u);
492
-
493
- return pu;
494
-}
495
-
496
-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) {
497
- // this function is needed to change the name of a PERSON_URL
498
-
499
- debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
500
- sizeof(REGISTRY_PERSON_URL) + namelen);
501
-
502
- REGISTRY_PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
503
- tpu->first_t = pu->first_t;
504
- tpu->last_t = pu->last_t;
505
- tpu->usages = pu->usages;
506
-
507
- // ok, these are a hack - since the registry_person_url_allocate() is
508
- // adding these, we have to subtract them
509
- tpu->machine->links--;
510
- registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(pu->machine_name);
511
- registry_url_unlink_nolock(u);
512
-
513
- freez(pu);
514
-
515
- return tpu;
516
-}
517
-
518
-static inline REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) {
519
- REGISTRY_PERSON *p = NULL;
520
-
521
- debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON));
522
-
523
- p = mallocz(sizeof(REGISTRY_PERSON));
524
-
525
- if(!person_guid) {
526
- for (; ;) {
527
- uuid_t uuid;
528
- uuid_generate(uuid);
529
- uuid_unparse_lower(uuid, p->guid);
530
-
531
- debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid);
532
- if (!dictionary_get(registry.persons, p->guid)) {
533
- debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid);
534
- break;
535
- }
536
- else
537
- info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid);
538
- }
539
- }
540
- else
541
- strncpyz(p->guid, person_guid, GUID_LEN);
542
-
543
- debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
544
- p->person_urls = dictionary_create(DICTIONARY_FLAGS);
545
-
546
- p->first_t = p->last_t = when;
547
- p->usages = 0;
548
-
549
- registry.persons_memory += sizeof(REGISTRY_PERSON);
550
-
551
- registry.persons_count++;
552
- dictionary_set(registry.persons, p->guid, p, sizeof(REGISTRY_PERSON));
553
-
554
- return p;
555
-}
556
-
557
-
558
-// 1. validate person GUID
559
-// 2. if it is valid, find it
560
-// 3. if it is not valid, create a new one
561
-// 4. return it
562
-static inline REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
563
- REGISTRY_PERSON *p = NULL;
564
-
565
- registry_persons_lock();
566
-
567
- if(person_guid && *person_guid) {
568
- char buf[GUID_LEN + 1];
569
- // validate it is a GUID
570
- if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
571
- info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
572
- else {
573
- person_guid = buf;
574
- p = registry_person_find(person_guid);
575
- }
576
- }
577
-
578
- if(!p) p = registry_person_allocate(NULL, when);
579
-
580
- registry_persons_unlock();
581
-
582
- return p;
583
-}
584
-
585
-// ----------------------------------------------------------------------------
586
-// LINKING OF OBJECTS
587
-
588
-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) {
589
- debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
590
-
591
- registry_person_urls_lock(p);
592
-
593
- REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, u->url);
594
- if(!pu) {
595
- debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
596
- pu = registry_person_url_allocate(p, m, u, name, namelen, when);
597
- registry.persons_urls_count++;
598
- }
599
- else {
600
- debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
601
- pu->usages++;
602
- if(likely(pu->last_t < (uint32_t)when)) pu->last_t = when;
603
-
604
- if(pu->machine != m) {
605
- REGISTRY_MACHINE_URL *mu = dictionary_get(pu->machine->machine_urls, u->url);
606
- if(mu) {
607
- info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
608
- p->guid, m->guid, u->url, pu->machine->guid);
609
- mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
610
- }
611
- else {
612
- 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.",
613
- p->guid, m->guid, u->url, pu->machine->guid);
614
- }
615
-
616
- pu->machine->links--;
617
- pu->machine = m;
618
- }
619
-
620
- if(strcmp(pu->machine_name, name)) {
621
- // the name of the PERSON_URL has changed !
622
- pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu);
623
- }
624
- }
625
-
626
- p->usages++;
627
- if(likely(p->last_t < (uint32_t)when)) p->last_t = when;
628
-
629
- if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
630
- info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
631
- pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
632
- }
633
-
634
- registry_person_urls_unlock(p);
635
-
636
- return pu;
637
-}
638
-
639
-static inline REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) {
640
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): searching for URL in machine", p->guid, m->guid, u->url);
641
-
642
- registry_machine_urls_lock(m);
643
-
644
- REGISTRY_MACHINE_URL *mu = dictionary_get(m->machine_urls, u->url);
645
- if(!mu) {
646
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
647
- mu = registry_machine_url_allocate(m, u, when);
648
- registry.machines_urls_count++;
649
- }
650
- else {
651
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
652
- mu->usages++;
653
- if(likely(mu->last_t < (uint32_t)when)) mu->last_t = when;
654
- }
655
-
656
- //debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): indexing person in machine", p->guid, m->guid, u->url);
657
- //dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
658
-
659
- m->usages++;
660
- if(likely(m->last_t < (uint32_t)when)) m->last_t = when;
661
-
662
- if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
663
- info("registry_machine_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url);
664
- mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
665
- }
666
-
667
- registry_machine_urls_unlock(m);
668
-
669
- return mu;
670
-}
671
-
672
-// ----------------------------------------------------------------------------
673
-// REGISTRY LOG LOAD/SAVE
674
-
675
-static inline int registry_should_save_db(void) {
676
- debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries);
677
- return registry.log_count > registry.save_registry_every_entries;
678
-}
679
-
680
-static inline void registry_log(const char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name) {
681
- if(likely(registry.log_fp)) {
682
- // we lock only if the file is open
683
- // to allow replaying the log at registry_log_load()
684
- registry_log_lock();
685
-
686
- if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n",
687
- action,
688
- p->last_t,
689
- p->guid,
690
- m->guid,
691
- name,
692
- u->url) < 0))
693
- error("Registry: failed to save log. Registry data may be lost in case of abnormal restart.");
694
-
695
- // we increase the counter even on failures
696
- // so that the registry will be saved periodically
697
- registry.log_count++;
698
-
699
- registry_log_unlock();
700
-
701
- // this must be outside the log_lock(), or a deadlock will happen.
702
- // registry_save() checks the same inside the log_lock, so only
703
- // one thread will save the db
704
- if(unlikely(registry_should_save_db()))
705
- registry_save();
706
- }
707
-}
708
-
709
-static inline int registry_log_open_nolock(void) {
710
- if(registry.log_fp)
711
- fclose(registry.log_fp);
712
-
713
- registry.log_fp = fopen(registry.log_filename, "a");
714
-
715
- if(registry.log_fp) {
716
- if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0)
717
- error("Cannot set line buffering on registry log file.");
718
- return 0;
719
- }
720
-
721
- error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename);
722
- return -1;
723
-}
724
-
725
-static inline void registry_log_close_nolock(void) {
726
- if(registry.log_fp) {
727
- fclose(registry.log_fp);
728
- registry.log_fp = NULL;
729
- }
730
-}
731
-
732
-static inline void registry_log_recreate_nolock(void) {
733
- if(registry.log_fp != NULL) {
734
- registry_log_close_nolock();
735
-
736
- // open it with truncate
737
- registry.log_fp = fopen(registry.log_filename, "w");
738
- if(registry.log_fp) fclose(registry.log_fp);
739
- else error("Cannot truncate registry log '%s'", registry.log_filename);
740
-
741
- registry.log_fp = NULL;
742
-
743
- registry_log_open_nolock();
744
- }
745
-}
746
-
747
-int registry_log_load(void) {
748
- ssize_t line = -1;
749
-
750
- // closing the log is required here
751
- // otherwise we will append to it the values we read
752
- registry_log_close_nolock();
753
-
754
- debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename);
755
- FILE *fp = fopen(registry.log_filename, "r");
756
- if(!fp)
757
- error("Registry: cannot open registry file: %s", registry.log_filename);
758
- else {
759
- char *s, buf[4096 + 1];
760
- line = 0;
761
- size_t len = 0;
762
-
763
- while ((s = fgets_trim_len(buf, 4096, fp, &len))) {
764
- line++;
765
-
766
- switch (s[0]) {
767
- case 'A': // accesses
768
- case 'D': // deletes
769
-
770
- // verify it is valid
771
- if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) {
772
- error("Registry: log line %zd is wrong (len = %zu).", line, len);
773
- continue;
774
- }
775
- s[1] = s[10] = s[47] = s[84] = '\0';
776
-
777
- // get the variables
778
- time_t when = strtoul(&s[2], NULL, 16);
779
- char *person_guid = &s[11];
780
- char *machine_guid = &s[48];
781
- char *name = &s[85];
782
-
783
- // skip the name to find the url
784
- char *url = name;
785
- while(*url && *url != '\t') url++;
786
- if(!*url) {
787
- error("Registry: log line %zd does not have a url.", line);
788
- continue;
789
- }
790
- *url++ = '\0';
791
-
792
- // make sure the person exists
793
- // without this, a new person guid will be created
794
- REGISTRY_PERSON *p = registry_person_find(person_guid);
795
- if(!p) p = registry_person_allocate(person_guid, when);
796
-
797
- if(s[0] == 'A')
798
- registry_request_access(p->guid, machine_guid, url, name, when);
799
- else
800
- registry_request_delete(p->guid, machine_guid, url, name, when);
801
-
802
- registry.log_count++;
803
- break;
804
-
805
- default:
806
- error("Registry: ignoring line %zd of filename '%s': %s.", line, registry.log_filename, s);
807
- break;
808
- }
809
- }
810
-
811
- fclose(fp);
812
- }
813
-
814
- // open the log again
815
- registry_log_open_nolock();
816
-
817
- return line;
818
-}
819
-
820
-
821
-// ----------------------------------------------------------------------------
822
-// REGISTRY REQUESTS
823
-
824
-REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
825
- debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
826
-
827
- REGISTRY_MACHINE *m = registry_machine_get(machine_guid, when);
828
- if(!m) return NULL;
829
-
830
- // make sure the name is valid
831
- size_t namelen;
832
- name = registry_fix_machine_name(name, &namelen);
833
-
834
- size_t urllen;
835
- url = registry_fix_url(url, &urllen);
836
-
837
- REGISTRY_URL *u = registry_url_get(url, urllen);
838
- REGISTRY_PERSON *p = registry_person_get(person_guid, when);
839
-
840
- registry_person_link_to_url(p, m, u, name, namelen, when);
841
- registry_machine_link_to_url(p, m, u, when);
842
-
843
- registry_log('A', p, m, u, name);
844
-
845
- registry.usages_count++;
846
- return p;
847
-}
848
-
849
-// verify the person, the machine and the URL exist in our DB
850
-REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, REGISTRY_PERSON **pp, REGISTRY_MACHINE **mm) {
851
- char pbuf[GUID_LEN + 1], mbuf[GUID_LEN + 1];
852
-
853
- if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
854
- 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");
855
- return NULL;
856
- }
857
-
858
- // normalize the url
859
- url = registry_fix_url(url, NULL);
860
-
861
- // make sure the person GUID is valid
862
- if(registry_regenerate_guid(person_guid, pbuf) == -1) {
863
- info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
864
- return NULL;
865
- }
866
- person_guid = pbuf;
867
-
868
- // make sure the machine GUID is valid
869
- if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
870
- info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
871
- return NULL;
872
- }
873
- machine_guid = mbuf;
874
-
875
- // make sure the machine exists
876
- REGISTRY_MACHINE *m = registry_machine_find(machine_guid);
877
- if(!m) {
878
- info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
879
- return NULL;
880
- }
881
- if(mm) *mm = m;
882
-
883
- // make sure the person exist
884
- REGISTRY_PERSON *p = registry_person_find(person_guid);
885
- if(!p) {
886
- info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
887
- return NULL;
888
- }
889
- if(pp) *pp = p;
890
-
891
- REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, url);
892
- if(!pu) {
893
- info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
894
- return NULL;
895
- }
896
- return pu;
897
-}
898
-
899
-REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
900
- (void)when;
22
+// COOKIES
23
902
- REGISTRY_PERSON *p = NULL;
903
- REGISTRY_MACHINE *m = NULL;
904
- REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
905
- if(!pu || !p || !m) return NULL;
906
-
907
- // normalize the url
908
- delete_url = registry_fix_url(delete_url, NULL);
909
-
910
- // make sure the user is not deleting the url it uses
911
- if(!strcmp(delete_url, pu->url->url)) {
912
- info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
913
- return NULL;
914
- }
915
-
916
- registry_person_urls_lock(p);
917
-
918
- REGISTRY_PERSON_URL *dpu = dictionary_get(p->person_urls, delete_url);
919
- if(!dpu) {
920
- info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
921
- registry_person_urls_unlock(p);
922
- return NULL;
923
- }
924
-
925
- registry_log('D', p, m, pu->url, dpu->url->url);
926
-
927
- dictionary_del(p->person_urls, dpu->url->url);
928
- registry_url_unlink_nolock(dpu->url);
929
- freez(dpu);
930
-
931
- registry_person_urls_unlock(p);
932
- return p;
933
-}
934
-
935
-
936
-// a structure to pass to the dictionary_get_all() callback handler
937
-struct machine_request_callback_data {
938
- REGISTRY_MACHINE *find_this_machine;
939
- REGISTRY_PERSON_URL *result;
940
-};
941
-
942
-// the callback function
943
-// this will be run for every PERSON_URL of this PERSON
944
-int machine_request_callback(void *entry, void *data) {
945
- REGISTRY_PERSON_URL *mypu = (REGISTRY_PERSON_URL *)entry;
946
- struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
947
-
948
- if(mypu->machine == myrdata->find_this_machine) {
949
- myrdata->result = mypu;
950
- return -1; // this will also stop the walk through
951
- }
952
-
953
- return 0; // continue
954
-}
955
-
956
-REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
957
- (void)when;
958
-
959
- char mbuf[GUID_LEN + 1];
960
-
961
- REGISTRY_PERSON *p = NULL;
962
- REGISTRY_MACHINE *m = NULL;
963
- REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
964
- if(!pu || !p || !m) return NULL;
965
-
966
- // make sure the machine GUID is valid
967
- if(registry_regenerate_guid(request_machine, mbuf) == -1) {
968
- 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);
969
- return NULL;
970
- }
971
- request_machine = mbuf;
972
-
973
- // make sure the machine exists
974
- m = registry_machine_find(request_machine);
975
- if(!m) {
976
- info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, pu->url->url, request_machine);
977
- return NULL;
978
- }
979
-
980
- // Verify the user has in the past accessed this machine
981
- // We will walk through the PERSON_URLs to find the machine
982
- // linking to our machine
983
-
984
- // a structure to pass to the dictionary_get_all() callback handler
985
- struct machine_request_callback_data rdata = { m, NULL };
986
-
987
- // request a walk through on the dictionary
988
- // no need for locking here, the underlying dictionary has its own
989
- dictionary_get_all(p->person_urls, machine_request_callback, &rdata);
990
-
991
- if(rdata.result)
992
- return m;
993
-
994
- return NULL;
995
-}
996
-
997
-
998
-// ----------------------------------------------------------------------------
999
-// REGISTRY JSON generation
1000
-
1001
-#define REGISTRY_STATUS_OK "ok"
1002
-#define REGISTRY_STATUS_FAILED "failed"
1003
-#define REGISTRY_STATUS_DISABLED "disabled"
1004
-
1005
-int registry_verify_cookies_redirects(void) {
1006
- return registry.verify_cookies_redirects;
1007
-}
1008
-
1009
-const char *registry_to_announce(void) {
1010
- return registry.registry_to_announce;
1011
-}
1012
-
1013
-void registry_set_cookie(struct web_client *w, const char *guid) {
24
+static void registry_set_cookie(struct web_client *w, const char *guid) {
25
char edate[100];
26
time_t et = now_realtime_sec() + registry.persons_expiration;
27
struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
@@ -1026,37 +37,35 @@ static inline void registry_set_person_cookie(struct web_client *w, REGISTRY_PER
37
registry_set_cookie(w, p->guid);
38
}
39
40
+
41
+// ----------------------------------------------------------------------------
42
+// JSON GENERATION
43
+
44
static inline void registry_json_header(struct web_client *w, const char *action, const char *status) {
45
buffer_flush(w->response.data);
46
w->response.data->contenttype = CT_APPLICATION_JSON;
47
buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
1033
- action, status, registry.hostname, registry.machine_guid);
48
+ action, status, registry.hostname, registry.machine_guid);
49
}
50
51
static inline void registry_json_footer(struct web_client *w) {
52
buffer_strcat(w->response.data, "\n}\n");
53
}
54
1040
-int registry_request_hello_json(struct web_client *w) {
1041
- registry_json_header(w, "hello", REGISTRY_STATUS_OK);
1042
-
1043
- buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
1044
- registry.registry_to_announce);
1045
-
1046
- registry_json_footer(w);
1047
- return 200;
1048
-}
1049
-
55
static inline int registry_json_disabled(struct web_client *w, const char *action) {
56
registry_json_header(w, action, REGISTRY_STATUS_DISABLED);
57
58
buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
1054
- registry.registry_to_announce);
59
+ registry.registry_to_announce);
60
61
registry_json_footer(w);
62
return 200;
63
}
64
65
+
66
+// ----------------------------------------------------------------------------
67
+// CALLBACKS FOR WALKING THROUGH REGISTRY OBJECTS
68
+
69
// structure used be the callbacks below
70
struct registry_json_walk_person_urls_callback {
71
REGISTRY_PERSON *p;
@@ -1075,7 +84,7 @@ static inline int registry_json_person_url_callback(void *entry, void *data) {
84
buffer_strcat(w->response.data, ",");
85
86
buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
1078
- pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->machine_name);
87
+ pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->machine_name);
88
89
return 1;
90
}
@@ -1091,21 +100,76 @@ static inline int registry_json_machine_url_callback(void *entry, void *data) {
100
buffer_strcat(w->response.data, ",");
101
102
buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]",
1094
- m->guid, mu->url->url, mu->last_t, mu->usages);
103
+ m->guid, mu->url->url, mu->last_t, mu->usages);
104
105
return 1;
106
}
107
108
+// ----------------------------------------------------------------------------
109
+
110
+// structure used be the callbacks below
111
+struct registry_person_url_callback_verify_machine_exists_data {
112
+ REGISTRY_MACHINE *m;
113
+ int count;
114
+};
115
+
116
+int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
117
+ struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
118
+ REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
119
+ REGISTRY_MACHINE *m = d->m;
120
+
121
+ if(pu->machine == m)
122
+ d->count++;
123
+
124
+ return 0;
125
+}
126
+
127
+// ----------------------------------------------------------------------------
128
+// public HELLO request
129
+
130
+int registry_request_hello_json(struct web_client *w) {
131
+ registry_json_header(w, "hello", REGISTRY_STATUS_OK);
132
+
133
+ buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
134
+ registry.registry_to_announce);
135
+
136
+ registry_json_footer(w);
137
+ return 200;
138
+}
139
+
140
+// ----------------------------------------------------------------------------
141
+//public ACCESS request
142
+
143
+#define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please"
144
145
// the main method for registering an access
146
int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
1102
- if(!registry.enabled)
147
+ if(unlikely(!registry.enabled))
148
return registry_json_disabled(w, "access");
149
150
+ // ------------------------------------------------------------------------
151
+ // verify the browser supports cookies
152
+
153
+ if(registry.verify_cookies_redirects > 0 && !person_guid[0]) {
154
+ buffer_flush(w->response.data);
155
+ registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID);
156
+ w->response.data->contenttype = CT_APPLICATION_JSON;
157
+ buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry.registry_to_announce);
158
+ return 200;
159
+ }
160
+
161
+ if(unlikely(person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
162
+ person_guid[0] = '\0';
163
+
164
+ // ------------------------------------------------------------------------
165
+
166
+ registry_lock();
167
+
168
REGISTRY_PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
169
if(!p) {
170
registry_json_header(w, "access", REGISTRY_STATUS_FAILED);
171
registry_json_footer(w);
172
+ registry_unlock();
173
return 412;
174
}
175
@@ -1121,36 +185,50 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
185
buffer_strcat(w->response.data, "\n\t]\n");
186
187
registry_json_footer(w);
188
+ registry_unlock();
189
return 200;
190
}
191
192
+// ----------------------------------------------------------------------------
193
+// public DELETE request
194
+
195
// the main method for deleting a URL from a person
196
int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
197
if(!registry.enabled)
198
return registry_json_disabled(w, "delete");
199
200
+ registry_lock();
201
+
202
REGISTRY_PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
203
if(!p) {
204
registry_json_header(w, "delete", REGISTRY_STATUS_FAILED);
205
registry_json_footer(w);
206
+ registry_unlock();
207
return 412;
208
}
209
210
// generate the response
211
registry_json_header(w, "delete", REGISTRY_STATUS_OK);
212
registry_json_footer(w);
213
+ registry_unlock();
214
return 200;
215
}
216
217
+// ----------------------------------------------------------------------------
218
+// public SEARCH request
219
+
220
// the main method for searching the URLs of a netdata
221
int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
222
if(!registry.enabled)
223
return registry_json_disabled(w, "search");
224
225
+ registry_lock();
226
+
227
REGISTRY_MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
228
if(!m) {
229
registry_json_header(w, "search", REGISTRY_STATUS_FAILED);
230
registry_json_footer(w);
231
+ registry_unlock();
232
return 404;
233
}
234
@@ -1162,38 +240,28 @@ int registry_request_search_json(struct web_client *w, char *person_guid, char *
240
buffer_strcat(w->response.data, "\n\t]\n");
241
242
registry_json_footer(w);
243
+ registry_unlock();
244
return 200;
245
}
246
1168
-// structure used be the callbacks below
1169
-struct registry_person_url_callback_verify_machine_exists_data {
1170
- REGISTRY_MACHINE *m;
1171
- int count;
1172
-};
1173
-
1174
-int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
1175
- struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
1176
- REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
1177
- REGISTRY_MACHINE *m = d->m;
1178
-
1179
- if(pu->machine == m)
1180
- d->count++;
1181
-
1182
- return 0;
1183
-}
247
+// ----------------------------------------------------------------------------
248
+// SWITCH REQUEST
249
250
// the main method for switching user identity
251
int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) {
252
+ if(!registry.enabled)
253
+ return registry_json_disabled(w, "switch");
254
+
255
(void)url;
256
(void)when;
257
1190
- if(!registry.enabled)
1191
- return registry_json_disabled(w, "switch");
258
+ registry_lock();
259
260
REGISTRY_PERSON *op = registry_person_find(person_guid);
261
if(!op) {
262
registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
263
registry_json_footer(w);
264
+ registry_unlock();
265
return 430;
266
}
267
@@ -1201,6 +269,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
269
if(!np) {
270
registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
271
registry_json_footer(w);
272
+ registry_unlock();
273
return 431;
274
}
275
@@ -1208,6 +277,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
277
if(!m) {
278
registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
279
registry_json_footer(w);
280
+ registry_unlock();
281
return 432;
282
}
283
@@ -1218,6 +288,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
288
if(!data.count) {
289
registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
290
registry_json_footer(w);
291
+ registry_unlock();
292
return 433;
293
}
294
@@ -1227,6 +298,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
298
if(!data.count) {
299
registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
300
registry_json_footer(w);
301
+ registry_unlock();
302
return 434;
303
}
304
@@ -1238,582 +310,9 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
310
registry_json_header(w, "switch", REGISTRY_STATUS_OK);
311
buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
312
registry_json_footer(w);
1241
- return 200;
1242
-}
1243
-
1244
-
1245
-// ----------------------------------------------------------------------------
1246
-// REGISTRY THIS MACHINE UNIQUE ID
1247
-
1248
-static inline int is_machine_guid_blacklisted(const char *guid) {
1249
- // these are machine GUIDs that have been included in distribution packages.
1250
- // we blacklist them here, so that the next version of netdata will generate
1251
- // new ones.
1252
-
1253
- if(!strcmp(guid, "8a795b0c-2311-11e6-8563-000c295076a6")
1254
- || !strcmp(guid, "4aed1458-1c3e-11e6-a53f-000c290fc8f5")
1255
- ) {
1256
- error("Blacklisted machine GUID '%s' found.", guid);
1257
- return 1;
1258
- }
1259
-
1260
- return 0;
1261
-}
1262
-
1263
-char *registry_get_this_machine_guid(void) {
1264
- if(likely(registry.machine_guid[0]))
1265
- return registry.machine_guid;
1266
-
1267
- // read it from disk
1268
- int fd = open(registry.machine_guid_filename, O_RDONLY);
1269
- if(fd != -1) {
1270
- char buf[GUID_LEN + 1];
1271
- if(read(fd, buf, GUID_LEN) != GUID_LEN)
1272
- error("Failed to read machine GUID from '%s'", registry.machine_guid_filename);
1273
- else {
1274
- buf[GUID_LEN] = '\0';
1275
- if(registry_regenerate_guid(buf, registry.machine_guid) == -1) {
1276
- error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.",
1277
- buf, registry.machine_guid_filename);
1278
-
1279
- registry.machine_guid[0] = '\0';
1280
- }
1281
- else if(is_machine_guid_blacklisted(registry.machine_guid))
1282
- registry.machine_guid[0] = '\0';
1283
- }
1284
- close(fd);
1285
- }
1286
-
1287
- // generate a new one?
1288
- if(!registry.machine_guid[0]) {
1289
- uuid_t uuid;
1290
-
1291
- uuid_generate_time(uuid);
1292
- uuid_unparse_lower(uuid, registry.machine_guid);
1293
- registry.machine_guid[GUID_LEN] = '\0';
1294
-
1295
- // save it
1296
- fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444);
1297
- if(fd == -1)
1298
- fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
1299
-
1300
- if(write(fd, registry.machine_guid, GUID_LEN) != GUID_LEN)
1301
- fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
1302
-
1303
- close(fd);
1304
- }
1305
-
1306
- setenv("NETDATA_REGISTRY_UNIQUE_ID", registry.machine_guid, 1);
1307
-
1308
- return registry.machine_guid;
1309
-}
1310
-
1311
-
1312
-// ----------------------------------------------------------------------------
1313
-// REGISTRY LOAD/SAVE
1314
-
1315
-int registry_machine_save_url(void *entry, void *file) {
1316
- REGISTRY_MACHINE_URL *mu = entry;
1317
- FILE *fp = file;
1318
-
1319
- debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url);
1320
-
1321
- int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
1322
- mu->first_t,
1323
- mu->last_t,
1324
- mu->usages,
1325
- mu->flags,
1326
- mu->url->url
1327
- );
1328
-
1329
- // error handling is done at registry_save()
1330
-
1331
- return ret;
1332
-}
1333
-
1334
-int registry_machine_save(void *entry, void *file) {
1335
- REGISTRY_MACHINE *m = entry;
1336
- FILE *fp = file;
1337
-
1338
- debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid);
1339
-
1340
- int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
1341
- m->first_t,
1342
- m->last_t,
1343
- m->usages,
1344
- m->guid
1345
- );
1346
-
1347
- if(ret >= 0) {
1348
- int ret2 = dictionary_get_all(m->machine_urls, registry_machine_save_url, fp);
1349
- if(ret2 < 0) return ret2;
1350
- ret += ret2;
1351
- }
1352
-
1353
- // error handling is done at registry_save()
1354
-
1355
- return ret;
1356
-}
1357
-
1358
-static inline int registry_person_save_url(void *entry, void *file) {
1359
- REGISTRY_PERSON_URL *pu = entry;
1360
- FILE *fp = file;
1361
-
1362
- debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
1363
-
1364
- int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
1365
- pu->first_t,
1366
- pu->last_t,
1367
- pu->usages,
1368
- pu->flags,
1369
- pu->machine->guid,
1370
- pu->machine_name,
1371
- pu->url->url
1372
- );
1373
-
1374
- // error handling is done at registry_save()
1375
-
1376
- return ret;
1377
-}
1378
-
1379
-static inline int registry_person_save(void *entry, void *file) {
1380
- REGISTRY_PERSON *p = entry;
1381
- FILE *fp = file;
1382
-
1383
- debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid);
1384
-
1385
- int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
1386
- p->first_t,
1387
- p->last_t,
1388
- p->usages,
1389
- p->guid
1390
- );
1391
-
1392
- if(ret >= 0) {
1393
- int ret2 = dictionary_get_all(p->person_urls, registry_person_save_url, fp);
1394
- if (ret2 < 0) return ret2;
1395
- ret += ret2;
1396
- }
1397
-
1398
- // error handling is done at registry_save()
1399
-
1400
- return ret;
1401
-}
1402
-
1403
-int registry_save(void) {
1404
- if(!registry.enabled) return -1;
1405
-
1406
- // make sure the log is not updated
1407
- registry_log_lock();
1408
-
1409
- if(unlikely(!registry_should_save_db())) {
1410
- registry_log_unlock();
1411
- return -2;
1412
- }
1413
-
1414
- error_log_limit_unlimited();
1415
-
1416
- char tmp_filename[FILENAME_MAX + 1];
1417
- char old_filename[FILENAME_MAX + 1];
1418
-
1419
- snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
1420
- snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
1421
-
1422
- debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
1423
- FILE *fp = fopen(tmp_filename, "w");
1424
- if(!fp) {
1425
- error("Registry: Cannot create file: %s", tmp_filename);
1426
- registry_log_unlock();
1427
- error_log_limit_reset();
1428
- return -1;
1429
- }
313
1431
- // dictionary_get_all() has its own locking, so this is safe to do
1432
-
1433
- debug(D_REGISTRY, "Saving all machines");
1434
- int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp);
1435
- if(bytes1 < 0) {
1436
- error("Registry: Cannot save registry machines - return value %d", bytes1);
1437
- fclose(fp);
1438
- registry_log_unlock();
1439
- error_log_limit_reset();
1440
- return bytes1;
1441
- }
1442
- debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
1443
-
1444
- debug(D_REGISTRY, "Saving all persons");
1445
- int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp);
1446
- if(bytes2 < 0) {
1447
- error("Registry: Cannot save registry persons - return value %d", bytes2);
1448
- fclose(fp);
1449
- registry_log_unlock();
1450
- error_log_limit_reset();
1451
- return bytes2;
1452
- }
1453
- debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
1454
-
1455
- // save the totals
1456
- fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
1457
- registry.persons_count,
1458
- registry.machines_count,
1459
- registry.usages_count + 1, // this is required - it is lost on db rotation
1460
- registry.urls_count,
1461
- registry.persons_urls_count,
1462
- registry.machines_urls_count
1463
- );
1464
-
1465
- fclose(fp);
1466
-
1467
- errno = 0;
1468
-
1469
- // remove the .old db
1470
- debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename);
1471
- if(unlink(old_filename) == -1 && errno != ENOENT)
1472
- error("Registry: cannot remove old registry file '%s'", old_filename);
1473
-
1474
- // rename the db to .old
1475
- debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
1476
- if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
1477
- error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", registry.db_filename, old_filename);
1478
-
1479
- else {
1480
- // remove the database (it is saved in .old)
1481
- debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename);
1482
- if (unlink(registry.db_filename) == -1 && errno != ENOENT)
1483
- error("Registry: cannot remove old registry file '%s'", registry.db_filename);
1484
-
1485
- // move the .tmp to make it active
1486
- debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
1487
- if (link(tmp_filename, registry.db_filename) == -1) {
1488
- error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
1489
- registry.db_filename);
1490
-
1491
- // move the .old back
1492
- debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
1493
- if(link(old_filename, registry.db_filename) == -1)
1494
- error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
1495
- }
1496
- else {
1497
- debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename);
1498
- if(unlink(tmp_filename) == -1)
1499
- error("Registry: cannot remove tmp registry file '%s'", tmp_filename);
1500
-
1501
- // it has been moved successfully
1502
- // discard the current registry log
1503
- registry_log_recreate_nolock();
1504
- registry.log_count = 0;
1505
- }
1506
- }
1507
-
1508
- // continue operations
1509
- registry_log_unlock();
1510
- error_log_limit_reset();
1511
-
1512
- return -1;
1513
-}
1514
-
1515
-static inline size_t registry_load(void) {
1516
- char *s, buf[4096 + 1];
1517
- REGISTRY_PERSON *p = NULL;
1518
- REGISTRY_MACHINE *m = NULL;
1519
- REGISTRY_URL *u = NULL;
1520
- size_t line = 0;
1521
-
1522
- debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename);
1523
- FILE *fp = fopen(registry.db_filename, "r");
1524
- if(!fp) {
1525
- error("Registry: cannot open registry file: '%s'", registry.db_filename);
1526
- return 0;
1527
- }
1528
-
1529
- size_t len = 0;
1530
- buf[4096] = '\0';
1531
- while((s = fgets_trim_len(buf, 4096, fp, &len))) {
1532
- line++;
1533
-
1534
- debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s);
1535
- switch(*s) {
1536
- case 'T': // totals
1537
- 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')) {
1538
- error("Registry totals line %zu is wrong (len = %zu).", line, len);
1539
- continue;
1540
- }
1541
- registry.persons_count = strtoull(&s[2], NULL, 16);
1542
- registry.machines_count = strtoull(&s[19], NULL, 16);
1543
- registry.usages_count = strtoull(&s[36], NULL, 16);
1544
- registry.urls_count = strtoull(&s[53], NULL, 16);
1545
- registry.persons_urls_count = strtoull(&s[70], NULL, 16);
1546
- registry.machines_urls_count = strtoull(&s[87], NULL, 16);
1547
- break;
1548
-
1549
- case 'P': // person
1550
- m = NULL;
1551
- // verify it is valid
1552
- if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
1553
- error("Registry person line %zu is wrong (len = %zu).", line, len);
1554
- continue;
1555
- }
1556
-
1557
- s[1] = s[10] = s[19] = s[28] = '\0';
1558
- p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16));
1559
- p->last_t = strtoul(&s[11], NULL, 16);
1560
- p->usages = strtoul(&s[20], NULL, 16);
1561
- debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
1562
- break;
1563
-
1564
- case 'M': // machine
1565
- p = NULL;
1566
- // verify it is valid
1567
- if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
1568
- error("Registry person line %zu is wrong (len = %zu).", line, len);
1569
- continue;
1570
- }
1571
-
1572
- s[1] = s[10] = s[19] = s[28] = '\0';
1573
- m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16));
1574
- m->last_t = strtoul(&s[11], NULL, 16);
1575
- m->usages = strtoul(&s[20], NULL, 16);
1576
- debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
1577
- break;
1578
-
1579
- case 'U': // person URL
1580
- if(unlikely(!p)) {
1581
- error("Registry: ignoring line %zu, no person loaded: %s", line, s);
1582
- continue;
1583
- }
1584
-
1585
- // verify it is valid
1586
- if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
1587
- error("Registry person URL line %zu is wrong (len = %zu).", line, len);
1588
- continue;
1589
- }
1590
-
1591
- s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
1592
-
1593
- // skip the name to find the url
1594
- char *url = &s[69];
1595
- while(*url && *url != '\t') url++;
1596
- if(!*url) {
1597
- error("Registry person URL line %zu does not have a url.", line);
1598
- continue;
1599
- }
1600
- *url++ = '\0';
1601
-
1602
- // u = registry_url_allocate_nolock(url, strlen(url));
1603
- u = registry_url_get_nolock(url, strlen(url));
1604
-
1605
- time_t first_t = strtoul(&s[2], NULL, 16);
1606
-
1607
- m = registry_machine_find(&s[32]);
1608
- if(!m) m = registry_machine_allocate(&s[32], first_t);
1609
-
1610
- REGISTRY_PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
1611
- pu->last_t = strtoul(&s[11], NULL, 16);
1612
- pu->usages = strtoul(&s[20], NULL, 16);
1613
- pu->flags = strtoul(&s[29], NULL, 16);
1614
- debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->machine_name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
1615
- break;
1616
-
1617
- case 'V': // machine URL
1618
- if(unlikely(!m)) {
1619
- error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
1620
- continue;
1621
- }
1622
-
1623
- // verify it is valid
1624
- if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
1625
- error("Registry person URL line %zu is wrong (len = %zu).", line, len);
1626
- continue;
1627
- }
1628
-
1629
- s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
1630
- // u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
1631
- u = registry_url_get_nolock(&s[32], strlen(&s[32]));
1632
-
1633
- REGISTRY_MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
1634
- mu->last_t = strtoul(&s[11], NULL, 16);
1635
- mu->usages = strtoul(&s[20], NULL, 16);
1636
- mu->flags = strtoul(&s[29], NULL, 16);
1637
- debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
1638
- break;
1639
-
1640
- default:
1641
- error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
1642
- break;
1643
- }
1644
- }
1645
- fclose(fp);
1646
-
1647
- return line;
1648
-}
1649
-
1650
-// ----------------------------------------------------------------------------
1651
-// REGISTRY
1652
-
1653
-int registry_init(void) {
1654
- char filename[FILENAME_MAX + 1];
1655
-
1656
- // registry enabled?
1657
- registry.enabled = config_get_boolean("registry", "enabled", 0);
1658
-
1659
- // pathnames
1660
- registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry");
1661
- if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST)
1662
- fatal("Cannot create directory '%s'.", registry.pathname);
1663
-
1664
- // filenames
1665
- snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
1666
- registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename);
1667
- registry_get_this_machine_guid();
1668
-
1669
- snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
1670
- registry.db_filename = config_get("registry", "registry db file", filename);
1671
-
1672
- snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
1673
- registry.log_filename = config_get("registry", "registry log file", filename);
1674
-
1675
- // configuration options
1676
- registry.save_registry_every_entries = config_get_number("registry", "registry save db every new entries", 1000000);
1677
- registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
1678
- registry.registry_domain = config_get("registry", "registry domain", "");
1679
- registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
1680
- registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", localhost.hostname));
1681
- registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1);
1682
-
1683
- setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1);
1684
- setenv("NETDATA_REGISTRY_URL", registry.registry_to_announce, 1);
1685
-
1686
- registry.max_url_length = config_get_number("registry", "max URL length", 1024);
1687
- if(registry.max_url_length < 10) {
1688
- registry.max_url_length = 10;
1689
- config_set_number("registry", "max URL length", registry.max_url_length);
1690
- }
1691
-
1692
- registry.max_name_length = config_get_number("registry", "max URL name length", 50);
1693
- if(registry.max_name_length < 10) {
1694
- registry.max_name_length = 10;
1695
- config_set_number("registry", "max URL name length", registry.max_name_length);
1696
- }
1697
-
1698
- // initialize entries counters
1699
- registry.persons_count = 0;
1700
- registry.machines_count = 0;
1701
- registry.usages_count = 0;
1702
- registry.urls_count = 0;
1703
- registry.persons_urls_count = 0;
1704
- registry.machines_urls_count = 0;
1705
-
1706
- // initialize memory counters
1707
- registry.persons_memory = 0;
1708
- registry.machines_memory = 0;
1709
- registry.urls_memory = 0;
1710
- registry.persons_urls_memory = 0;
1711
- registry.machines_urls_memory = 0;
1712
-
1713
- // initialize locks
1714
- pthread_mutex_init(®istry.persons_lock, NULL);
1715
- pthread_mutex_init(®istry.machines_lock, NULL);
1716
- pthread_mutex_init(®istry.urls_lock, NULL);
1717
- pthread_mutex_init(®istry.person_urls_lock, NULL);
1718
- pthread_mutex_init(®istry.machine_urls_lock, NULL);
1719
-
1720
- // create dictionaries
1721
- registry.persons = dictionary_create(DICTIONARY_FLAGS);
1722
- registry.machines = dictionary_create(DICTIONARY_FLAGS);
1723
- registry.urls = dictionary_create(DICTIONARY_FLAGS);
1724
-
1725
- // load the registry database
1726
- if(registry.enabled) {
1727
- registry_log_open_nolock();
1728
- registry_load();
1729
- registry_log_load();
1730
-
1731
- if(unlikely(registry_should_save_db()))
1732
- registry_save();
1733
- }
1734
-
1735
- return 0;
1736
-}
1737
-
1738
-void registry_free(void) {
1739
- if(!registry.enabled) return;
1740
-
1741
- // we need to destroy the dictionaries ourselves
1742
- // since the dictionaries use memory we allocated
1743
-
1744
- while(registry.persons->values_index.root) {
1745
- REGISTRY_PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value;
1746
-
1747
- // fprintf(stderr, "\nPERSON: '%s', first: %u, last: %u, usages: %u\n", p->guid, p->first_t, p->last_t, p->usages);
1748
-
1749
- while(p->person_urls->values_index.root) {
1750
- REGISTRY_PERSON_URL *pu = ((NAME_VALUE *)p->person_urls->values_index.root)->value;
1751
-
1752
- // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", pu->url->url, pu->first_t, pu->last_t, pu->usages, pu->flags);
1753
-
1754
- debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", pu->url->url, p->guid);
1755
- dictionary_del(p->person_urls, pu->url->url);
1756
-
1757
- debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url);
1758
- registry_url_unlink_nolock(pu->url);
1759
-
1760
- debug(D_REGISTRY, "Registry: freeing person url");
1761
- freez(pu);
1762
- }
1763
-
1764
- debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
1765
- dictionary_del(registry.persons, p->guid);
1766
-
1767
- debug(D_REGISTRY, "Registry: destroying URL dictionary of person '%s'", p->guid);
1768
- dictionary_destroy(p->person_urls);
1769
-
1770
- debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
1771
- freez(p);
1772
- }
1773
-
1774
- while(registry.machines->values_index.root) {
1775
- REGISTRY_MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value;
1776
-
1777
- // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages);
1778
-
1779
- while(m->machine_urls->values_index.root) {
1780
- REGISTRY_MACHINE_URL *mu = ((NAME_VALUE *)m->machine_urls->values_index.root)->value;
1781
-
1782
- // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags);
1783
-
1784
- //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url);
1785
- //dictionary_destroy(mu->persons);
1786
-
1787
- debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid);
1788
- dictionary_del(m->machine_urls, mu->url->url);
1789
-
1790
- debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
1791
- registry_url_unlink_nolock(mu->url);
1792
-
1793
- debug(D_REGISTRY, "Registry: freeing machine url");
1794
- freez(mu);
1795
- }
1796
-
1797
- debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid);
1798
- dictionary_del(registry.machines, m->guid);
1799
-
1800
- debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid);
1801
- dictionary_destroy(m->machine_urls);
1802
-
1803
- debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid);
1804
- freez(m);
1805
- }
1806
-
1807
- // and free the memory of remaining dictionary structures
1808
-
1809
- debug(D_REGISTRY, "Registry: destroying persons dictionary");
1810
- dictionary_destroy(registry.persons);
1811
-
1812
- debug(D_REGISTRY, "Registry: destroying machines dictionary");
1813
- dictionary_destroy(registry.machines);
1814
-
1815
- debug(D_REGISTRY, "Registry: destroying urls dictionary");
1816
- dictionary_destroy(registry.urls);
314
+ registry_unlock();
315
+ return 200;
316
}
317
318
// ----------------------------------------------------------------------------
@@ -1872,7 +371,7 @@ void registry_statistics(void) {
371
372
rrddim_set(stm, "persons", registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
373
rrddim_set(stm, "machines", registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
1875
- rrddim_set(stm, "urls", registry.urls_memory + registry.urls_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
374
+ rrddim_set(stm, "urls", registry.urls_memory);
375
rrddim_set(stm, "persons_urls", registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE));
376
rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
377
rrdset_done(stm);
src/registry.h
+56
-10
@@ -1,25 +1,71 @@
1
+/*
2
+ * netdata registry
3
+ *
4
+ * this header file describes the public interface
5
+ * to the netdata registry
6
+ *
7
+ * only these high level functions are exposed
8
+ *
9
+ */
10
+
11
+// ----------------------------------------------------------------------------
12
+// TODO
13
+//
14
+// 1. the default tracking cookie expires in 1 year, but the persons are not
15
+// removed from the db - this means the database only grows - ideally the
16
+// database should be cleaned in registry_db_save() for both on-disk and
17
+// on-memory entries.
18
+//
19
+// Cleanup:
20
+// i. Find all the PERSONs that have expired cookie
21
+// ii. For each of their PERSON_URLs:
22
+// - decrement the linked MACHINE links
23
+// - if the linked MACHINE has no other links, remove the linked MACHINE too
24
+// - remove the PERSON_URL
25
+//
26
+// 2. add protection to prevent abusing the registry by flooding it with
27
+// requests to fill the memory and crash it.
28
+//
29
+// Possible protections:
30
+// - limit the number of URLs per person
31
+// - limit the number of URLs per machine
32
+// - limit the number of persons
33
+// - limit the number of machines
34
+// - [DONE] limit the size of URLs
35
+// - [DONE] limit the size of PERSON_URL names
36
+// - limit the number of requests that add data to the registry,
37
+// per client IP per hour
38
+//
39
+// 3. lower memory requirements
40
+//
41
+// - embed avl structures directly into registry objects, instead of DICTIONARY
42
+// - store GUIDs in memory as UUID instead of char *
43
+// - do not track persons using the demo machines only
44
+// (i.e. start tracking them only when they access a non-demo machine)
45
+// - [DONE] do not track custom dashboards by default
46
+
47
+
48
#ifndef NETDATA_REGISTRY_H
49
#define NETDATA_REGISTRY_H 1
50
51
#define NETDATA_REGISTRY_COOKIE_NAME "netdata_registry_id"
52
6
-extern void registry_set_cookie(struct web_client *w, const char *guid);
7
-extern const char *registry_to_announce(void);
8
-extern int registry_verify_cookies_redirects(void);
53
+// initialize the registry
54
+// should only happen when netdata starts
55
+extern int registry_init(void);
56
+
57
+// free all data held by the registry
58
+// should only happen when netdata exits
59
+extern void registry_free(void);
60
61
+// HTTP requests handled by the registry
62
extern int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
63
extern int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
64
extern int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
65
extern int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when);
66
extern int registry_request_hello_json(struct web_client *w);
67
16
-extern int registry_init(void);
17
-extern void registry_free(void);
18
-extern int registry_save(void);
19
-
20
-extern char *registry_get_this_machine_guid(void);
21
-
68
+// update the registry monitoring charts
69
extern void registry_statistics(void);
70
24
-
71
#endif /* NETDATA_REGISTRY_H */
src/registry_db.c
new
+342
@@ -0,0 +1,342 @@
1
+#include "registry_internals.h"
2
+
3
+int registry_db_should_be_saved(void) {
4
+ debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries);
5
+ return registry.log_count > registry.save_registry_every_entries;
6
+}
7
+
8
+// ----------------------------------------------------------------------------
9
+// INTERNAL FUNCTIONS FOR SAVING REGISTRY OBJECTS
10
+
11
+static int registry_machine_save_url(void *entry, void *file) {
12
+ REGISTRY_MACHINE_URL *mu = entry;
13
+ FILE *fp = file;
14
+
15
+ debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url);
16
+
17
+ int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
18
+ mu->first_t,
19
+ mu->last_t,
20
+ mu->usages,
21
+ mu->flags,
22
+ mu->url->url
23
+ );
24
+
25
+ // error handling is done at registry_db_save()
26
+
27
+ return ret;
28
+}
29
+
30
+static int registry_machine_save(void *entry, void *file) {
31
+ REGISTRY_MACHINE *m = entry;
32
+ FILE *fp = file;
33
+
34
+ debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid);
35
+
36
+ int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
37
+ m->first_t,
38
+ m->last_t,
39
+ m->usages,
40
+ m->guid
41
+ );
42
+
43
+ if(ret >= 0) {
44
+ int ret2 = dictionary_get_all(m->machine_urls, registry_machine_save_url, fp);
45
+ if(ret2 < 0) return ret2;
46
+ ret += ret2;
47
+ }
48
+
49
+ // error handling is done at registry_db_save()
50
+
51
+ return ret;
52
+}
53
+
54
+static inline int registry_person_save_url(void *entry, void *file) {
55
+ REGISTRY_PERSON_URL *pu = entry;
56
+ FILE *fp = file;
57
+
58
+ debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
59
+
60
+ int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
61
+ pu->first_t,
62
+ pu->last_t,
63
+ pu->usages,
64
+ pu->flags,
65
+ pu->machine->guid,
66
+ pu->machine_name,
67
+ pu->url->url
68
+ );
69
+
70
+ // error handling is done at registry_db_save()
71
+
72
+ return ret;
73
+}
74
+
75
+static inline int registry_person_save(void *entry, void *file) {
76
+ REGISTRY_PERSON *p = entry;
77
+ FILE *fp = file;
78
+
79
+ debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid);
80
+
81
+ int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
82
+ p->first_t,
83
+ p->last_t,
84
+ p->usages,
85
+ p->guid
86
+ );
87
+
88
+ if(ret >= 0) {
89
+ int ret2 = dictionary_get_all(p->person_urls, registry_person_save_url, fp);
90
+ if (ret2 < 0) return ret2;
91
+ ret += ret2;
92
+ }
93
+
94
+ // error handling is done at registry_db_save()
95
+
96
+ return ret;
97
+}
98
+
99
+// ----------------------------------------------------------------------------
100
+// SAVE THE REGISTRY DATABASE
101
+
102
+int registry_db_save(void) {
103
+ if(unlikely(!registry.enabled))
104
+ return -1;
105
+
106
+ if(unlikely(!registry_db_should_be_saved()))
107
+ return -2;
108
+
109
+ error_log_limit_unlimited();
110
+
111
+ char tmp_filename[FILENAME_MAX + 1];
112
+ char old_filename[FILENAME_MAX + 1];
113
+
114
+ snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
115
+ snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
116
+
117
+ debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
118
+ FILE *fp = fopen(tmp_filename, "w");
119
+ if(!fp) {
120
+ error("Registry: Cannot create file: %s", tmp_filename);
121
+ error_log_limit_reset();
122
+ return -1;
123
+ }
124
+
125
+ // dictionary_get_all() has its own locking, so this is safe to do
126
+
127
+ debug(D_REGISTRY, "Saving all machines");
128
+ int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp);
129
+ if(bytes1 < 0) {
130
+ error("Registry: Cannot save registry machines - return value %d", bytes1);
131
+ fclose(fp);
132
+ error_log_limit_reset();
133
+ return bytes1;
134
+ }
135
+ debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
136
+
137
+ debug(D_REGISTRY, "Saving all persons");
138
+ int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp);
139
+ if(bytes2 < 0) {
140
+ error("Registry: Cannot save registry persons - return value %d", bytes2);
141
+ fclose(fp);
142
+ error_log_limit_reset();
143
+ return bytes2;
144
+ }
145
+ debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
146
+
147
+ // save the totals
148
+ fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
149
+ registry.persons_count,
150
+ registry.machines_count,
151
+ registry.usages_count + 1, // this is required - it is lost on db rotation
152
+ registry.urls_count,
153
+ registry.persons_urls_count,
154
+ registry.machines_urls_count
155
+ );
156
+
157
+ fclose(fp);
158
+
159
+ errno = 0;
160
+
161
+ // remove the .old db
162
+ debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename);
163
+ if(unlink(old_filename) == -1 && errno != ENOENT)
164
+ error("Registry: cannot remove old registry file '%s'", old_filename);
165
+
166
+ // rename the db to .old
167
+ debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
168
+ if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
169
+ error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", registry.db_filename, old_filename);
170
+
171
+ else {
172
+ // remove the database (it is saved in .old)
173
+ debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename);
174
+ if (unlink(registry.db_filename) == -1 && errno != ENOENT)
175
+ error("Registry: cannot remove old registry file '%s'", registry.db_filename);
176
+
177
+ // move the .tmp to make it active
178
+ debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
179
+ if (link(tmp_filename, registry.db_filename) == -1) {
180
+ error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
181
+ registry.db_filename);
182
+
183
+ // move the .old back
184
+ debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
185
+ if(link(old_filename, registry.db_filename) == -1)
186
+ error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
187
+ }
188
+ else {
189
+ debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename);
190
+ if(unlink(tmp_filename) == -1)
191
+ error("Registry: cannot remove tmp registry file '%s'", tmp_filename);
192
+
193
+ // it has been moved successfully
194
+ // discard the current registry log
195
+ registry_log_recreate();
196
+ registry.log_count = 0;
197
+ }
198
+ }
199
+
200
+ // continue operations
201
+ error_log_limit_reset();
202
+
203
+ return -1;
204
+}
205
+
206
+// ----------------------------------------------------------------------------
207
+// LOAD THE REGISTRY DATABASE
208
+
209
+size_t registry_db_load(void) {
210
+ char *s, buf[4096 + 1];
211
+ REGISTRY_PERSON *p = NULL;
212
+ REGISTRY_MACHINE *m = NULL;
213
+ REGISTRY_URL *u = NULL;
214
+ size_t line = 0;
215
+
216
+ debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename);
217
+ FILE *fp = fopen(registry.db_filename, "r");
218
+ if(!fp) {
219
+ error("Registry: cannot open registry file: '%s'", registry.db_filename);
220
+ return 0;
221
+ }
222
+
223
+ size_t len = 0;
224
+ buf[4096] = '\0';
225
+ while((s = fgets_trim_len(buf, 4096, fp, &len))) {
226
+ line++;
227
+
228
+ debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s);
229
+ switch(*s) {
230
+ case 'T': // totals
231
+ 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')) {
232
+ error("Registry totals line %zu is wrong (len = %zu).", line, len);
233
+ continue;
234
+ }
235
+ registry.persons_count = strtoull(&s[2], NULL, 16);
236
+ registry.machines_count = strtoull(&s[19], NULL, 16);
237
+ registry.usages_count = strtoull(&s[36], NULL, 16);
238
+ registry.urls_count = strtoull(&s[53], NULL, 16);
239
+ registry.persons_urls_count = strtoull(&s[70], NULL, 16);
240
+ registry.machines_urls_count = strtoull(&s[87], NULL, 16);
241
+ break;
242
+
243
+ case 'P': // person
244
+ m = NULL;
245
+ // verify it is valid
246
+ if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
247
+ error("Registry person line %zu is wrong (len = %zu).", line, len);
248
+ continue;
249
+ }
250
+
251
+ s[1] = s[10] = s[19] = s[28] = '\0';
252
+ p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16));
253
+ p->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
254
+ p->usages = (uint32_t)strtoul(&s[20], NULL, 16);
255
+ debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
256
+ break;
257
+
258
+ case 'M': // machine
259
+ p = NULL;
260
+ // verify it is valid
261
+ if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
262
+ error("Registry person line %zu is wrong (len = %zu).", line, len);
263
+ continue;
264
+ }
265
+
266
+ s[1] = s[10] = s[19] = s[28] = '\0';
267
+ m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16));
268
+ m->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
269
+ m->usages = (uint32_t)strtoul(&s[20], NULL, 16);
270
+ debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
271
+ break;
272
+
273
+ case 'U': // person URL
274
+ if(unlikely(!p)) {
275
+ error("Registry: ignoring line %zu, no person loaded: %s", line, s);
276
+ continue;
277
+ }
278
+
279
+ // verify it is valid
280
+ if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
281
+ error("Registry person URL line %zu is wrong (len = %zu).", line, len);
282
+ continue;
283
+ }
284
+
285
+ s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
286
+
287
+ // skip the name to find the url
288
+ char *url = &s[69];
289
+ while(*url && *url != '\t') url++;
290
+ if(!*url) {
291
+ error("Registry person URL line %zu does not have a url.", line);
292
+ continue;
293
+ }
294
+ *url++ = '\0';
295
+
296
+ // u = registry_url_allocate_nolock(url, strlen(url));
297
+ u = registry_url_get(url, strlen(url));
298
+
299
+ time_t first_t = strtoul(&s[2], NULL, 16);
300
+
301
+ m = registry_machine_find(&s[32]);
302
+ if(!m) m = registry_machine_allocate(&s[32], first_t);
303
+
304
+ REGISTRY_PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
305
+ pu->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
306
+ pu->usages = (uint32_t)strtoul(&s[20], NULL, 16);
307
+ pu->flags = (uint8_t)strtoul(&s[29], NULL, 16);
308
+ debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->machine_name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
309
+ break;
310
+
311
+ case 'V': // machine URL
312
+ if(unlikely(!m)) {
313
+ error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
314
+ continue;
315
+ }
316
+
317
+ // verify it is valid
318
+ if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
319
+ error("Registry person URL line %zu is wrong (len = %zu).", line, len);
320
+ continue;
321
+ }
322
+
323
+ s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
324
+ // u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
325
+ u = registry_url_get(&s[32], strlen(&s[32]));
326
+
327
+ REGISTRY_MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
328
+ mu->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
329
+ mu->usages = (uint32_t)strtoul(&s[20], NULL, 16);
330
+ mu->flags = (uint8_t)strtoul(&s[29], NULL, 16);
331
+ debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
332
+ break;
333
+
334
+ default:
335
+ error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
336
+ break;
337
+ }
338
+ }
339
+ fclose(fp);
340
+
341
+ return line;
342
+}
src/registry_init.c
new
+160
@@ -0,0 +1,160 @@
1
+#include "registry_internals.h"
2
+
3
+int registry_init(void) {
4
+ char filename[FILENAME_MAX + 1];
5
+
6
+ // registry enabled?
7
+ registry.enabled = config_get_boolean("registry", "enabled", 0);
8
+
9
+ // pathnames
10
+ registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry");
11
+ if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST)
12
+ fatal("Cannot create directory '%s'.", registry.pathname);
13
+
14
+ // filenames
15
+ snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
16
+ registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename);
17
+ registry_get_this_machine_guid();
18
+
19
+ snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
20
+ registry.db_filename = config_get("registry", "registry db file", filename);
21
+
22
+ snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
23
+ registry.log_filename = config_get("registry", "registry log file", filename);
24
+
25
+ // configuration options
26
+ registry.save_registry_every_entries = (unsigned long long)config_get_number("registry", "registry save db every new entries", 1000000);
27
+ registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
28
+ registry.registry_domain = config_get("registry", "registry domain", "");
29
+ registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
30
+ registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", localhost.hostname));
31
+ registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1);
32
+
33
+ setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1);
34
+ setenv("NETDATA_REGISTRY_URL", registry.registry_to_announce, 1);
35
+
36
+ registry.max_url_length = (size_t)config_get_number("registry", "max URL length", 1024);
37
+ if(registry.max_url_length < 10) {
38
+ registry.max_url_length = 10;
39
+ config_set_number("registry", "max URL length", (long long)registry.max_url_length);
40
+ }
41
+
42
+ registry.max_name_length = (size_t)config_get_number("registry", "max URL name length", 50);
43
+ if(registry.max_name_length < 10) {
44
+ registry.max_name_length = 10;
45
+ config_set_number("registry", "max URL name length", (long long)registry.max_name_length);
46
+ }
47
+
48
+ // initialize entries counters
49
+ registry.persons_count = 0;
50
+ registry.machines_count = 0;
51
+ registry.usages_count = 0;
52
+ registry.urls_count = 0;
53
+ registry.persons_urls_count = 0;
54
+ registry.machines_urls_count = 0;
55
+
56
+ // initialize memory counters
57
+ registry.persons_memory = 0;
58
+ registry.machines_memory = 0;
59
+ registry.urls_memory = 0;
60
+ registry.persons_urls_memory = 0;
61
+ registry.machines_urls_memory = 0;
62
+
63
+ // initialize locks
64
+ pthread_mutex_init(®istry.lock, NULL);
65
+
66
+ // create dictionaries
67
+ registry.persons = dictionary_create(DICTIONARY_FLAGS);
68
+ registry.machines = dictionary_create(DICTIONARY_FLAGS);
69
+ avl_init(®istry.registry_urls_root_index, registry_url_compare);
70
+
71
+ // load the registry database
72
+ if(registry.enabled) {
73
+ registry_log_open();
74
+ registry_db_load();
75
+ registry_log_load();
76
+
77
+ if(unlikely(registry_db_should_be_saved()))
78
+ registry_db_save();
79
+ }
80
+
81
+ return 0;
82
+}
83
+
84
+void registry_free(void) {
85
+ if(!registry.enabled) return;
86
+
87
+ // we need to destroy the dictionaries ourselves
88
+ // since the dictionaries use memory we allocated
89
+
90
+ while(registry.persons->values_index.root) {
91
+ REGISTRY_PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value;
92
+
93
+ // fprintf(stderr, "\nPERSON: '%s', first: %u, last: %u, usages: %u\n", p->guid, p->first_t, p->last_t, p->usages);
94
+
95
+ while(p->person_urls->values_index.root) {
96
+ REGISTRY_PERSON_URL *pu = ((NAME_VALUE *)p->person_urls->values_index.root)->value;
97
+
98
+ // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", pu->url->url, pu->first_t, pu->last_t, pu->usages, pu->flags);
99
+
100
+ debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", pu->url->url, p->guid);
101
+ dictionary_del(p->person_urls, pu->url->url);
102
+
103
+ debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url);
104
+ registry_url_unlink(pu->url);
105
+
106
+ debug(D_REGISTRY, "Registry: freeing person url");
107
+ freez(pu);
108
+ }
109
+
110
+ debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
111
+ dictionary_del(registry.persons, p->guid);
112
+
113
+ debug(D_REGISTRY, "Registry: destroying URL dictionary of person '%s'", p->guid);
114
+ dictionary_destroy(p->person_urls);
115
+
116
+ debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
117
+ freez(p);
118
+ }
119
+
120
+ while(registry.machines->values_index.root) {
121
+ REGISTRY_MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value;
122
+
123
+ // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages);
124
+
125
+ while(m->machine_urls->values_index.root) {
126
+ REGISTRY_MACHINE_URL *mu = ((NAME_VALUE *)m->machine_urls->values_index.root)->value;
127
+
128
+ // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags);
129
+
130
+ //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url);
131
+ //dictionary_destroy(mu->persons);
132
+
133
+ debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid);
134
+ dictionary_del(m->machine_urls, mu->url->url);
135
+
136
+ debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
137
+ registry_url_unlink(mu->url);
138
+
139
+ debug(D_REGISTRY, "Registry: freeing machine url");
140
+ freez(mu);
141
+ }
142
+
143
+ debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid);
144
+ dictionary_del(registry.machines, m->guid);
145
+
146
+ debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid);
147
+ dictionary_destroy(m->machine_urls);
148
+
149
+ debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid);
150
+ freez(m);
151
+ }
152
+
153
+ // and free the memory of remaining dictionary structures
154
+
155
+ debug(D_REGISTRY, "Registry: destroying persons dictionary");
156
+ dictionary_destroy(registry.persons);
157
+
158
+ debug(D_REGISTRY, "Registry: destroying machines dictionary");
159
+ dictionary_destroy(registry.machines);
160
+}
src/registry_internals.c
new
+317
@@ -0,0 +1,317 @@
1
+#include "registry_internals.h"
2
+
3
+struct registry registry;
4
+
5
+// ----------------------------------------------------------------------------
6
+// common functions
7
+
8
+// parse a GUID and re-generated to be always lower case
9
+// this is used as a protection against the variations of GUIDs
10
+int registry_regenerate_guid(const char *guid, char *result) {
11
+ uuid_t uuid;
12
+ if(unlikely(uuid_parse(guid, uuid) == -1)) {
13
+ info("Registry: GUID '%s' is not a valid GUID.", guid);
14
+ return -1;
15
+ }
16
+ else {
17
+ uuid_unparse_lower(uuid, result);
18
+
19
+#ifdef NETDATA_INTERNAL_CHECKS
20
+ if(strcmp(guid, result))
21
+ info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result);
22
+#endif /* NETDATA_INTERNAL_CHECKS */
23
+ }
24
+
25
+ return 0;
26
+}
27
+
28
+// make sure the names of the machines / URLs do not contain any tabs
29
+// (which are used as our separator in the database files)
30
+// and are properly trimmed (before and after)
31
+static inline char *registry_fix_machine_name(char *name, size_t *len) {
32
+ char *s = name?name:"";
33
+
34
+ // skip leading spaces
35
+ while(*s && isspace(*s)) s++;
36
+
37
+ // make sure all spaces are a SPACE
38
+ char *t = s;
39
+ while(*t) {
40
+ if(unlikely(isspace(*t)))
41
+ *t = ' ';
42
+
43
+ t++;
44
+ }
45
+
46
+ // remove trailing spaces
47
+ while(--t >= s) {
48
+ if(*t == ' ')
49
+ *t = '\0';
50
+ else
51
+ break;
52
+ }
53
+ t++;
54
+
55
+ if(likely(len))
56
+ *len = (t - s);
57
+
58
+ return s;
59
+}
60
+
61
+static inline char *registry_fix_url(char *url, size_t *len) {
62
+ return registry_fix_machine_name(url, len);
63
+}
64
+
65
+
66
+// ----------------------------------------------------------------------------
67
+// forward definition of functions
68
+
69
+extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when);
70
+extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
71
+
72
+
73
+// ----------------------------------------------------------------------------
74
+// HELPERS
75
+
76
+// verify the person, the machine and the URL exist in our DB
77
+REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, REGISTRY_PERSON **pp, REGISTRY_MACHINE **mm) {
78
+ char pbuf[GUID_LEN + 1], mbuf[GUID_LEN + 1];
79
+
80
+ if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
81
+ 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");
82
+ return NULL;
83
+ }
84
+
85
+ // normalize the url
86
+ url = registry_fix_url(url, NULL);
87
+
88
+ // make sure the person GUID is valid
89
+ if(registry_regenerate_guid(person_guid, pbuf) == -1) {
90
+ info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
91
+ return NULL;
92
+ }
93
+ person_guid = pbuf;
94
+
95
+ // make sure the machine GUID is valid
96
+ if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
97
+ info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
98
+ return NULL;
99
+ }
100
+ machine_guid = mbuf;
101
+
102
+ // make sure the machine exists
103
+ REGISTRY_MACHINE *m = registry_machine_find(machine_guid);
104
+ if(!m) {
105
+ info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
106
+ return NULL;
107
+ }
108
+ if(mm) *mm = m;
109
+
110
+ // make sure the person exist
111
+ REGISTRY_PERSON *p = registry_person_find(person_guid);
112
+ if(!p) {
113
+ info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
114
+ return NULL;
115
+ }
116
+ if(pp) *pp = p;
117
+
118
+ REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, url);
119
+ if(!pu) {
120
+ info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
121
+ return NULL;
122
+ }
123
+ return pu;
124
+}
125
+
126
+
127
+// ----------------------------------------------------------------------------
128
+// REGISTRY REQUESTS
129
+
130
+REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
131
+ debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
132
+
133
+ REGISTRY_MACHINE *m = registry_machine_get(machine_guid, when);
134
+ if(!m) return NULL;
135
+
136
+ // make sure the name is valid
137
+ size_t namelen;
138
+ name = registry_fix_machine_name(name, &namelen);
139
+
140
+ size_t urllen;
141
+ url = registry_fix_url(url, &urllen);
142
+
143
+ REGISTRY_PERSON *p = registry_person_get(person_guid, when);
144
+
145
+ REGISTRY_URL *u = registry_url_get(url, urllen);
146
+ registry_person_link_to_url(p, m, u, name, namelen, when);
147
+ registry_machine_link_to_url(m, u, when);
148
+
149
+ registry_log('A', p, m, u, name);
150
+
151
+ registry.usages_count++;
152
+
153
+ return p;
154
+}
155
+
156
+REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
157
+ (void)when;
158
+
159
+ REGISTRY_PERSON *p = NULL;
160
+ REGISTRY_MACHINE *m = NULL;
161
+ REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
162
+ if(!pu || !p || !m) return NULL;
163
+
164
+ // normalize the url
165
+ delete_url = registry_fix_url(delete_url, NULL);
166
+
167
+ // make sure the user is not deleting the url it uses
168
+ if(!strcmp(delete_url, pu->url->url)) {
169
+ info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
170
+ return NULL;
171
+ }
172
+
173
+ REGISTRY_PERSON_URL *dpu = dictionary_get(p->person_urls, delete_url);
174
+ if(!dpu) {
175
+ info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
176
+ return NULL;
177
+ }
178
+
179
+ registry_log('D', p, m, pu->url, dpu->url->url);
180
+
181
+ dictionary_del(p->person_urls, dpu->url->url);
182
+
183
+ registry_url_unlink(dpu->url);
184
+
185
+ freez(dpu);
186
+
187
+ return p;
188
+}
189
+
190
+
191
+// a structure to pass to the dictionary_get_all() callback handler
192
+struct machine_request_callback_data {
193
+ REGISTRY_MACHINE *find_this_machine;
194
+ REGISTRY_PERSON_URL *result;
195
+};
196
+
197
+// the callback function
198
+// this will be run for every PERSON_URL of this PERSON
199
+static int machine_request_callback(void *entry, void *data) {
200
+ REGISTRY_PERSON_URL *mypu = (REGISTRY_PERSON_URL *)entry;
201
+ struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
202
+
203
+ if(mypu->machine == myrdata->find_this_machine) {
204
+ myrdata->result = mypu;
205
+ return -1; // this will also stop the walk through
206
+ }
207
+
208
+ return 0; // continue
209
+}
210
+
211
+REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
212
+ (void)when;
213
+
214
+ char mbuf[GUID_LEN + 1];
215
+
216
+ REGISTRY_PERSON *p = NULL;
217
+ REGISTRY_MACHINE *m = NULL;
218
+ REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
219
+ if(!pu || !p || !m) return NULL;
220
+
221
+ // make sure the machine GUID is valid
222
+ if(registry_regenerate_guid(request_machine, mbuf) == -1) {
223
+ 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);
224
+ return NULL;
225
+ }
226
+ request_machine = mbuf;
227
+
228
+ // make sure the machine exists
229
+ m = registry_machine_find(request_machine);
230
+ if(!m) {
231
+ info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, pu->url->url, request_machine);
232
+ return NULL;
233
+ }
234
+
235
+ // Verify the user has in the past accessed this machine
236
+ // We will walk through the PERSON_URLs to find the machine
237
+ // linking to our machine
238
+
239
+ // a structure to pass to the dictionary_get_all() callback handler
240
+ struct machine_request_callback_data rdata = { m, NULL };
241
+
242
+ // request a walk through on the dictionary
243
+ // no need for locking here, the underlying dictionary has its own
244
+ dictionary_get_all(p->person_urls, machine_request_callback, &rdata);
245
+
246
+ if(rdata.result)
247
+ return m;
248
+
249
+ return NULL;
250
+}
251
+
252
+
253
+// ----------------------------------------------------------------------------
254
+// REGISTRY THIS MACHINE UNIQUE ID
255
+
256
+static inline int is_machine_guid_blacklisted(const char *guid) {
257
+ // these are machine GUIDs that have been included in distribution packages.
258
+ // we blacklist them here, so that the next version of netdata will generate
259
+ // new ones.
260
+
261
+ if(!strcmp(guid, "8a795b0c-2311-11e6-8563-000c295076a6")
262
+ || !strcmp(guid, "4aed1458-1c3e-11e6-a53f-000c290fc8f5")
263
+ ) {
264
+ error("Blacklisted machine GUID '%s' found.", guid);
265
+ return 1;
266
+ }
267
+
268
+ return 0;
269
+}
270
+
271
+char *registry_get_this_machine_guid(void) {
272
+ if(likely(registry.machine_guid[0]))
273
+ return registry.machine_guid;
274
+
275
+ // read it from disk
276
+ int fd = open(registry.machine_guid_filename, O_RDONLY);
277
+ if(fd != -1) {
278
+ char buf[GUID_LEN + 1];
279
+ if(read(fd, buf, GUID_LEN) != GUID_LEN)
280
+ error("Failed to read machine GUID from '%s'", registry.machine_guid_filename);
281
+ else {
282
+ buf[GUID_LEN] = '\0';
283
+ if(registry_regenerate_guid(buf, registry.machine_guid) == -1) {
284
+ error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.",
285
+ buf, registry.machine_guid_filename);
286
+
287
+ registry.machine_guid[0] = '\0';
288
+ }
289
+ else if(is_machine_guid_blacklisted(registry.machine_guid))
290
+ registry.machine_guid[0] = '\0';
291
+ }
292
+ close(fd);
293
+ }
294
+
295
+ // generate a new one?
296
+ if(!registry.machine_guid[0]) {
297
+ uuid_t uuid;
298
+
299
+ uuid_generate_time(uuid);
300
+ uuid_unparse_lower(uuid, registry.machine_guid);
301
+ registry.machine_guid[GUID_LEN] = '\0';
302
+
303
+ // save it
304
+ fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444);
305
+ if(fd == -1)
306
+ fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
307
+
308
+ if(write(fd, registry.machine_guid, GUID_LEN) != GUID_LEN)
309
+ fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
310
+
311
+ close(fd);
312
+ }
313
+
314
+ setenv("NETDATA_REGISTRY_UNIQUE_ID", registry.machine_guid, 1);
315
+
316
+ return registry.machine_guid;
317
+}
src/registry_internals.h
new
+92
@@ -0,0 +1,92 @@
1
+#include "common.h"
2
+
3
+#ifndef NETDATA_REGISTRY_INTERNALS_H_H
4
+#define NETDATA_REGISTRY_INTERNALS_H_H
5
+
6
+#define REGISTRY_URL_FLAGS_DEFAULT 0x00
7
+#define REGISTRY_URL_FLAGS_EXPIRED 0x01
8
+
9
+#define DICTIONARY_FLAGS DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE | DICTIONARY_FLAG_NAME_LINK_DONT_CLONE | DICTIONARY_FLAG_SINGLE_THREADED
10
+
11
+// ----------------------------------------------------------------------------
12
+// COMMON structures
13
+
14
+struct registry {
15
+ int enabled;
16
+
17
+ char machine_guid[GUID_LEN + 1];
18
+
19
+ // entries counters / statistics
20
+ unsigned long long persons_count;
21
+ unsigned long long machines_count;
22
+ unsigned long long usages_count;
23
+ unsigned long long urls_count;
24
+ unsigned long long persons_urls_count;
25
+ unsigned long long machines_urls_count;
26
+ unsigned long long log_count;
27
+
28
+ // memory counters / statistics
29
+ unsigned long long persons_memory;
30
+ unsigned long long machines_memory;
31
+ unsigned long long urls_memory;
32
+ unsigned long long persons_urls_memory;
33
+ unsigned long long machines_urls_memory;
34
+
35
+ // configuration
36
+ unsigned long long save_registry_every_entries;
37
+ char *registry_domain;
38
+ char *hostname;
39
+ char *registry_to_announce;
40
+ time_t persons_expiration; // seconds to expire idle persons
41
+ int verify_cookies_redirects;
42
+
43
+ size_t max_url_length;
44
+ size_t max_name_length;
45
+
46
+ // file/path names
47
+ char *pathname;
48
+ char *db_filename;
49
+ char *log_filename;
50
+ char *machine_guid_filename;
51
+
52
+ // open files
53
+ FILE *log_fp;
54
+
55
+ // the database
56
+ DICTIONARY *persons; // dictionary of REGISTRY_PERSON *, with key the REGISTRY_PERSON.guid
57
+ DICTIONARY *machines; // dictionary of REGISTRY_MACHINE *, with key the REGISTRY_MACHINE.guid
58
+
59
+ avl_tree registry_urls_root_index;
60
+
61
+ pthread_mutex_t lock;
62
+};
63
+
64
+extern int registry_regenerate_guid(const char *guid, char *result);
65
+
66
+#include "registry_url.h"
67
+#include "registry_machine.h"
68
+#include "registry_person.h"
69
+#include "registry.h"
70
+
71
+extern struct registry registry;
72
+
73
+extern char *registry_get_this_machine_guid(void);
74
+
75
+// REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c)
76
+extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when);
77
+extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
78
+extern REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
79
+
80
+// REGISTRY LOG (in registry_log.c)
81
+extern void registry_log(const char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name);
82
+extern int registry_log_open(void);
83
+extern void registry_log_close(void);
84
+extern void registry_log_recreate(void);
85
+extern ssize_t registry_log_load(void);
86
+
87
+// REGISTRY DB (in registry_db.c)
88
+extern int registry_db_save(void);
89
+extern size_t registry_db_load(void);
90
+extern int registry_db_should_be_saved(void);
91
+
92
+#endif //NETDATA_REGISTRY_INTERNALS_H_H
src/registry_log.c
new
+133
@@ -0,0 +1,133 @@
1
+#include "registry_internals.h"
2
+
3
+void registry_log(const char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name) {
4
+ if(likely(registry.log_fp)) {
5
+ if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n",
6
+ action,
7
+ p->last_t,
8
+ p->guid,
9
+ m->guid,
10
+ name,
11
+ u->url) < 0))
12
+ error("Registry: failed to save log. Registry data may be lost in case of abnormal restart.");
13
+
14
+ // we increase the counter even on failures
15
+ // so that the registry will be saved periodically
16
+ registry.log_count++;
17
+
18
+ // this must be outside the log_lock(), or a deadlock will happen.
19
+ // registry_db_save() checks the same inside the log_lock, so only
20
+ // one thread will save the db
21
+ if(unlikely(registry_db_should_be_saved()))
22
+ registry_db_save();
23
+ }
24
+}
25
+
26
+int registry_log_open(void) {
27
+ if(registry.log_fp)
28
+ fclose(registry.log_fp);
29
+
30
+ registry.log_fp = fopen(registry.log_filename, "a");
31
+ if(registry.log_fp) {
32
+ if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0)
33
+ error("Cannot set line buffering on registry log file.");
34
+ return 0;
35
+ }
36
+
37
+ error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename);
38
+ return -1;
39
+}
40
+
41
+void registry_log_close(void) {
42
+ if(registry.log_fp) {
43
+ fclose(registry.log_fp);
44
+ registry.log_fp = NULL;
45
+ }
46
+}
47
+
48
+void registry_log_recreate(void) {
49
+ if(registry.log_fp != NULL) {
50
+ registry_log_close();
51
+
52
+ // open it with truncate
53
+ registry.log_fp = fopen(registry.log_filename, "w");
54
+ if(registry.log_fp) fclose(registry.log_fp);
55
+ else error("Cannot truncate registry log '%s'", registry.log_filename);
56
+
57
+ registry.log_fp = NULL;
58
+ registry_log_open();
59
+ }
60
+}
61
+
62
+ssize_t registry_log_load(void) {
63
+ ssize_t line = -1;
64
+
65
+ // closing the log is required here
66
+ // otherwise we will append to it the values we read
67
+ registry_log_close();
68
+
69
+ debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename);
70
+ FILE *fp = fopen(registry.log_filename, "r");
71
+ if(!fp)
72
+ error("Registry: cannot open registry file: %s", registry.log_filename);
73
+ else {
74
+ char *s, buf[4096 + 1];
75
+ line = 0;
76
+ size_t len = 0;
77
+
78
+ while ((s = fgets_trim_len(buf, 4096, fp, &len))) {
79
+ line++;
80
+
81
+ switch (s[0]) {
82
+ case 'A': // accesses
83
+ case 'D': // deletes
84
+
85
+ // verify it is valid
86
+ if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) {
87
+ error("Registry: log line %zd is wrong (len = %zu).", line, len);
88
+ continue;
89
+ }
90
+ s[1] = s[10] = s[47] = s[84] = '\0';
91
+
92
+ // get the variables
93
+ time_t when = strtoul(&s[2], NULL, 16);
94
+ char *person_guid = &s[11];
95
+ char *machine_guid = &s[48];
96
+ char *name = &s[85];
97
+
98
+ // skip the name to find the url
99
+ char *url = name;
100
+ while(*url && *url != '\t') url++;
101
+ if(!*url) {
102
+ error("Registry: log line %zd does not have a url.", line);
103
+ continue;
104
+ }
105
+ *url++ = '\0';
106
+
107
+ // make sure the person exists
108
+ // without this, a new person guid will be created
109
+ REGISTRY_PERSON *p = registry_person_find(person_guid);
110
+ if(!p) p = registry_person_allocate(person_guid, when);
111
+
112
+ if(s[0] == 'A')
113
+ registry_request_access(p->guid, machine_guid, url, name, when);
114
+ else
115
+ registry_request_delete(p->guid, machine_guid, url, name, when);
116
+
117
+ registry.log_count++;
118
+ break;
119
+
120
+ default:
121
+ error("Registry: ignoring line %zd of filename '%s': %s.", line, registry.log_filename, s);
122
+ break;
123
+ }
124
+ }
125
+
126
+ fclose(fp);
127
+ }
128
+
129
+ // open the log again
130
+ registry_log_open();
131
+
132
+ return line;
133
+}
src/registry_machine.c
new
+101
@@ -0,0 +1,101 @@
1
+#include "registry_internals.h"
2
+
3
+// ----------------------------------------------------------------------------
4
+// MACHINE
5
+
6
+REGISTRY_MACHINE *registry_machine_find(const char *machine_guid) {
7
+ debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid);
8
+ return dictionary_get(registry.machines, machine_guid);
9
+}
10
+
11
+REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) {
12
+ debug(D_REGISTRY, "registry_machine_url_allocate('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(REGISTRY_MACHINE_URL));
13
+
14
+ REGISTRY_MACHINE_URL *mu = mallocz(sizeof(REGISTRY_MACHINE_URL));
15
+
16
+ mu->first_t = mu->last_t = (uint32_t)when;
17
+ mu->usages = 1;
18
+ mu->url = u;
19
+ mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
20
+
21
+ registry.machines_urls_memory += sizeof(REGISTRY_MACHINE_URL);
22
+
23
+ debug(D_REGISTRY, "registry_machine_url_allocate('%s', '%s'): indexing URL in machine", m->guid, u->url);
24
+ dictionary_set(m->machine_urls, u->url, mu, sizeof(REGISTRY_MACHINE_URL));
25
+
26
+ registry_url_link(u);
27
+
28
+ return mu;
29
+}
30
+
31
+REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) {
32
+ debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(REGISTRY_MACHINE));
33
+
34
+ REGISTRY_MACHINE *m = mallocz(sizeof(REGISTRY_MACHINE));
35
+
36
+ strncpyz(m->guid, machine_guid, GUID_LEN);
37
+
38
+ debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
39
+ m->machine_urls = dictionary_create(DICTIONARY_FLAGS);
40
+
41
+ m->first_t = m->last_t = (uint32_t)when;
42
+ m->usages = 0;
43
+
44
+ registry.machines_memory += sizeof(REGISTRY_MACHINE);
45
+
46
+ registry.machines_count++;
47
+ dictionary_set(registry.machines, m->guid, m, sizeof(REGISTRY_MACHINE));
48
+
49
+ return m;
50
+}
51
+
52
+// 1. validate machine GUID
53
+// 2. if it is valid, find it or create it and return it
54
+// 3. if it is not valid, return NULL
55
+REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
56
+ REGISTRY_MACHINE *m = NULL;
57
+
58
+ if(likely(machine_guid && *machine_guid)) {
59
+ // validate it is a GUID
60
+ char buf[GUID_LEN + 1];
61
+ if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1))
62
+ info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
63
+ else {
64
+ machine_guid = buf;
65
+ m = registry_machine_find(machine_guid);
66
+ if(!m) m = registry_machine_allocate(machine_guid, when);
67
+ }
68
+ }
69
+
70
+ return m;
71
+}
72
+
73
+
74
+// ----------------------------------------------------------------------------
75
+// LINKING OF OBJECTS
76
+
77
+REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when) {
78
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): searching for URL in machine", m->guid, u->url);
79
+
80
+ REGISTRY_MACHINE_URL *mu = dictionary_get(m->machine_urls, u->url);
81
+ if(!mu) {
82
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): not found", m->guid, u->url);
83
+ mu = registry_machine_url_allocate(m, u, when);
84
+ registry.machines_urls_count++;
85
+ }
86
+ else {
87
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): found", m->guid, u->url);
88
+ mu->usages++;
89
+ if(likely(mu->last_t < (uint32_t)when)) mu->last_t = (uint32_t)when;
90
+ }
91
+
92
+ m->usages++;
93
+ if(likely(m->last_t < (uint32_t)when)) m->last_t = (uint32_t)when;
94
+
95
+ if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
96
+ info("registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, u->url);
97
+ mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
98
+ }
99
+
100
+ return mu;
101
+}
src/registry_machine.h
new
+41
@@ -0,0 +1,41 @@
1
+#ifndef NETDATA_REGISTRY_MACHINE_H
2
+#define NETDATA_REGISTRY_MACHINE_H
3
+
4
+#include "registry_internals.h"
5
+
6
+// ----------------------------------------------------------------------------
7
+// MACHINE structures
8
+
9
+// For each MACHINE-URL pair we keep this
10
+struct registry_machine_url {
11
+ REGISTRY_URL *url; // de-duplicated URL
12
+
13
+ uint8_t flags;
14
+
15
+ uint32_t first_t; // the first time we saw this
16
+ uint32_t last_t; // the last time we saw this
17
+ uint32_t usages; // how many times this has been accessed
18
+};
19
+typedef struct registry_machine_url REGISTRY_MACHINE_URL;
20
+
21
+// A machine
22
+struct registry_machine {
23
+ char guid[GUID_LEN + 1]; // the GUID
24
+
25
+ uint32_t links; // the number of REGISTRY_PERSON_URL linked to this machine
26
+
27
+ DICTIONARY *machine_urls; // MACHINE_URL *
28
+
29
+ uint32_t first_t; // the first time we saw this
30
+ uint32_t last_t; // the last time we saw this
31
+ uint32_t usages; // how many times this has been accessed
32
+};
33
+typedef struct registry_machine REGISTRY_MACHINE;
34
+
35
+extern REGISTRY_MACHINE *registry_machine_find(const char *machine_guid);
36
+extern REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when);
37
+extern REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when);
38
+extern REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when);
39
+extern REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when);
40
+
41
+#endif //NETDATA_REGISTRY_MACHINE_H
src/registry_person.c
new
+175
@@ -0,0 +1,175 @@
1
+#include "registry_internals.h"
2
+
3
+// ----------------------------------------------------------------------------
4
+// PERSON
5
+
6
+REGISTRY_PERSON *registry_person_find(const char *person_guid) {
7
+ debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
8
+ return dictionary_get(registry.persons, person_guid);
9
+}
10
+
11
+REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) {
12
+ // protection from too big names
13
+ if(namelen > registry.max_name_length)
14
+ namelen = registry.max_name_length;
15
+
16
+ debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
17
+ sizeof(REGISTRY_PERSON_URL) + namelen);
18
+
19
+ REGISTRY_PERSON_URL *pu = mallocz(sizeof(REGISTRY_PERSON_URL) + namelen);
20
+
21
+ // a simple strcpy() should do the job
22
+ // but I prefer to be safe, since the caller specified urllen
23
+ strncpyz(pu->machine_name, name, namelen);
24
+
25
+ pu->machine = m;
26
+ pu->first_t = pu->last_t = (uint32_t)when;
27
+ pu->usages = 1;
28
+ pu->url = u;
29
+ pu->flags = REGISTRY_URL_FLAGS_DEFAULT;
30
+ m->links++;
31
+
32
+ registry.persons_urls_memory += sizeof(REGISTRY_PERSON_URL) + namelen;
33
+
34
+ debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
35
+ dictionary_set(p->person_urls, u->url, pu, sizeof(REGISTRY_PERSON_URL));
36
+ registry_url_link(u);
37
+
38
+ return pu;
39
+}
40
+
41
+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) {
42
+ // this function is needed to change the name of a PERSON_URL
43
+
44
+ debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
45
+ sizeof(REGISTRY_PERSON_URL) + namelen);
46
+
47
+ REGISTRY_PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
48
+ tpu->first_t = pu->first_t;
49
+ tpu->last_t = pu->last_t;
50
+ tpu->usages = pu->usages;
51
+
52
+ // ok, these are a hack - since the registry_person_url_allocate() is
53
+ // adding these, we have to subtract them
54
+ tpu->machine->links--;
55
+ registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(pu->machine_name);
56
+ registry_url_unlink(u);
57
+
58
+ freez(pu);
59
+
60
+ return tpu;
61
+}
62
+
63
+REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) {
64
+ REGISTRY_PERSON *p = NULL;
65
+
66
+ debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON));
67
+
68
+ p = mallocz(sizeof(REGISTRY_PERSON));
69
+
70
+ if(!person_guid) {
71
+ for (; ;) {
72
+ uuid_t uuid;
73
+ uuid_generate(uuid);
74
+ uuid_unparse_lower(uuid, p->guid);
75
+
76
+ debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid);
77
+ if (!dictionary_get(registry.persons, p->guid)) {
78
+ debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid);
79
+ break;
80
+ }
81
+ else
82
+ info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid);
83
+ }
84
+ }
85
+ else
86
+ strncpyz(p->guid, person_guid, GUID_LEN);
87
+
88
+ debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
89
+ p->person_urls = dictionary_create(DICTIONARY_FLAGS);
90
+
91
+ p->first_t = p->last_t = (uint32_t)when;
92
+ p->usages = 0;
93
+
94
+ registry.persons_memory += sizeof(REGISTRY_PERSON);
95
+
96
+ registry.persons_count++;
97
+ dictionary_set(registry.persons, p->guid, p, sizeof(REGISTRY_PERSON));
98
+
99
+ return p;
100
+}
101
+
102
+
103
+// 1. validate person GUID
104
+// 2. if it is valid, find it
105
+// 3. if it is not valid, create a new one
106
+// 4. return it
107
+REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
108
+ REGISTRY_PERSON *p = NULL;
109
+
110
+ if(person_guid && *person_guid) {
111
+ char buf[GUID_LEN + 1];
112
+ // validate it is a GUID
113
+ if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
114
+ info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
115
+ else {
116
+ person_guid = buf;
117
+ p = registry_person_find(person_guid);
118
+ }
119
+ }
120
+
121
+ if(!p) p = registry_person_allocate(NULL, when);
122
+
123
+ return p;
124
+}
125
+
126
+// ----------------------------------------------------------------------------
127
+// LINKING OF OBJECTS
128
+
129
+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) {
130
+ debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
131
+
132
+ REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, u->url);
133
+ if(!pu) {
134
+ debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
135
+ pu = registry_person_url_allocate(p, m, u, name, namelen, when);
136
+ registry.persons_urls_count++;
137
+ }
138
+ else {
139
+ debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
140
+ pu->usages++;
141
+ if(likely(pu->last_t < (uint32_t)when)) pu->last_t = (uint32_t)when;
142
+
143
+ if(pu->machine != m) {
144
+ REGISTRY_MACHINE_URL *mu = dictionary_get(pu->machine->machine_urls, u->url);
145
+ if(mu) {
146
+ info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
147
+ p->guid, m->guid, u->url, pu->machine->guid);
148
+ mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
149
+ }
150
+ else {
151
+ 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.",
152
+ p->guid, m->guid, u->url, pu->machine->guid);
153
+ }
154
+
155
+ pu->machine->links--;
156
+ pu->machine = m;
157
+ }
158
+
159
+ if(strcmp(pu->machine_name, name)) {
160
+ // the name of the PERSON_URL has changed !
161
+ pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu);
162
+ }
163
+ }
164
+
165
+ p->usages++;
166
+ if(likely(p->last_t < (uint32_t)when)) p->last_t = (uint32_t)when;
167
+
168
+ if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
169
+ info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
170
+ pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
171
+ }
172
+
173
+ return pu;
174
+}
175
+
src/registry_person.h
new
+44
@@ -0,0 +1,44 @@
1
+#ifndef NETDATA_REGISTRY_PERSON_H
2
+#define NETDATA_REGISTRY_PERSON_H
3
+
4
+#include "registry_internals.h"
5
+
6
+// ----------------------------------------------------------------------------
7
+// PERSON structures
8
+
9
+// for each PERSON-URL pair we keep this
10
+struct registry_person_url {
11
+ REGISTRY_URL *url; // de-duplicated URL
12
+ REGISTRY_MACHINE *machine; // link the MACHINE of this URL
13
+
14
+ uint8_t flags;
15
+
16
+ uint32_t first_t; // the first time we saw this
17
+ uint32_t last_t; // the last time we saw this
18
+ uint32_t usages; // how many times this has been accessed
19
+
20
+ char machine_name[1]; // the name of the machine, as known by the user
21
+ // dynamically allocated to fit properly
22
+};
23
+typedef struct registry_person_url REGISTRY_PERSON_URL;
24
+
25
+// A person
26
+struct registry_person {
27
+ char guid[GUID_LEN + 1]; // the person GUID
28
+
29
+ DICTIONARY *person_urls; // dictionary of PERSON_URL *
30
+
31
+ uint32_t first_t; // the first time we saw this
32
+ uint32_t last_t; // the last time we saw this
33
+ uint32_t usages; // how many times this has been accessed
34
+};
35
+typedef struct registry_person REGISTRY_PERSON;
36
+
37
+extern REGISTRY_PERSON *registry_person_find(const char *person_guid);
38
+extern REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when);
39
+extern 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);
40
+extern REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when);
41
+extern REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when);
42
+extern 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);
43
+
44
+#endif //NETDATA_REGISTRY_PERSON_H
src/registry_url.c
new
+74
@@ -0,0 +1,74 @@
1
+#include "registry_internals.h"
2
+
3
+// ----------------------------------------------------------------------------
4
+// REGISTRY_URL
5
+
6
+int registry_url_compare(void *a, void *b) {
7
+ if(((REGISTRY_URL *)a)->hash < ((REGISTRY_URL *)b)->hash) return -1;
8
+ else if(((REGISTRY_URL *)a)->hash > ((REGISTRY_URL *)b)->hash) return 1;
9
+ else return strcmp(((REGISTRY_URL *)a)->url, ((REGISTRY_URL *)b)->url);
10
+}
11
+
12
+#define registry_url_index_add(rc) (REGISTRY_URL *)avl_insert(&(registry.registry_urls_root_index), (avl *)(rc))
13
+#define registry_url_index_del(rc) (REGISTRY_URL *)avl_remove(&(registry.registry_urls_root_index), (avl *)(rc))
14
+
15
+REGISTRY_URL *registry_url_get(const char *url, size_t urllen) {
16
+ // protection from too big URLs
17
+ if(urllen > registry.max_url_length)
18
+ urllen = registry.max_url_length;
19
+
20
+ debug(D_REGISTRY, "Registry: registry_url_get('%s', %zu)", url, urllen);
21
+
22
+ char buf[sizeof(REGISTRY_URL) + urllen]; // no need for +1, 1 is already in REGISTRY_URL
23
+ REGISTRY_URL *n = (REGISTRY_URL *)&buf[0];
24
+ n->len = (uint16_t)urllen;
25
+ strncpyz(n->url, url, n->len);
26
+ n->hash = simple_hash(n->url);
27
+
28
+ REGISTRY_URL *u = (REGISTRY_URL *)avl_search(&(registry.registry_urls_root_index), (avl *)n);
29
+ if(!u) {
30
+ debug(D_REGISTRY, "Registry: registry_url_get('%s', %zu): allocating %zu bytes", url, urllen, sizeof(REGISTRY_URL) + urllen);
31
+ u = callocz(1, sizeof(REGISTRY_URL) + urllen); // no need for +1, 1 is already in REGISTRY_URL
32
+
33
+ // a simple strcpy() should do the job
34
+ // but I prefer to be safe, since the caller specified urllen
35
+ u->len = (uint16_t)urllen;
36
+ strncpyz(u->url, url, u->len);
37
+ u->links = 0;
38
+ u->hash = simple_hash(u->url);
39
+
40
+ registry.urls_memory += sizeof(REGISTRY_URL) + urllen; // no need for +1, 1 is already in REGISTRY_URL
41
+
42
+ debug(D_REGISTRY, "Registry: registry_url_get('%s'): indexing it", url);
43
+ n = registry_url_index_add(u);
44
+ if(n != u) {
45
+ error("INTERNAL ERROR: registry_url_get(): url '%s' already exists in the registry as '%s'", u->url, n->url);
46
+ free(u);
47
+ u = n;
48
+ }
49
+ else
50
+ registry.urls_count++;
51
+ }
52
+
53
+ return u;
54
+}
55
+
56
+void registry_url_link(REGISTRY_URL *u) {
57
+ u->links++;
58
+ debug(D_REGISTRY, "Registry: registry_url_link('%s'): URL has now %u links", u->url, u->links);
59
+}
60
+
61
+void registry_url_unlink(REGISTRY_URL *u) {
62
+ u->links--;
63
+ if(!u->links) {
64
+ debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): No more links for this URL", u->url);
65
+ REGISTRY_URL *n = registry_url_index_del(u);
66
+ if(n != u)
67
+ error("INTERNAL ERROR: registry_url_unlink(): request to delete url '%s', deleted url '%s'", u->url, n->url);
68
+
69
+ registry.urls_memory -= sizeof(REGISTRY_URL) + n->len; // no need for +1, 1 is already in REGISTRY_URL
70
+ freez(n);
71
+ }
72
+ else
73
+ debug(D_REGISTRY, "Registry: registry_url_unlink('%s'): URL has %u links left", u->url, u->links);
74
+}
src/registry_url.h
new
+28
@@ -0,0 +1,28 @@
1
+#ifndef NETDATA_REGISTRY_URL_H
2
+#define NETDATA_REGISTRY_URL_H
3
+
4
+#include "registry_internals.h"
5
+
6
+// ----------------------------------------------------------------------------
7
+// URL structures
8
+// Save memory by de-duplicating URLs
9
+// so instead of storing URLs all over the place
10
+// we store them here and we keep pointers elsewhere
11
+
12
+struct registry_url {
13
+ avl avl;
14
+ uint32_t hash; // the index hash
15
+
16
+ uint32_t links; // the number of links to this URL - when none is left, we free it
17
+
18
+ uint16_t len; // the length of the URL in bytes
19
+ char url[1]; // the URL - dynamically allocated to more size
20
+};
21
+typedef struct registry_url REGISTRY_URL;
22
+
23
+extern int registry_url_compare(void *a, void *b);
24
+extern REGISTRY_URL *registry_url_get(const char *url, size_t urllen) __attribute__((returns_nonnull));
25
+extern void registry_url_link(REGISTRY_URL *u);
26
+extern void registry_url_unlink(REGISTRY_URL *u);
27
+
28
+#endif //NETDATA_REGISTRY_URL_H
src/web_client.c
+1
-57
@@ -1213,8 +1213,6 @@ cleanup:
1213
}
1214
1215
1216
-#define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please"
1217
-
1216
int web_client_api_request_v1_registry(struct web_client *w, char *url)
1217
{
1218
static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
@@ -1239,7 +1237,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
1237
*/
1238
}
1239
1242
- char person_guid[36 + 1] = "";
1240
+ char person_guid[GUID_LEN + 1] = "";
1241
1242
debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
1243
@@ -1354,60 +1352,6 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
1352
switch(action) {
1353
case 'A':
1354
w->tracking_required = 1;
1357
- if(registry_verify_cookies_redirects() > 0 && (!cookie || !person_guid[0])) {
1358
- buffer_flush(w->response.data);
1359
- registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID);
1360
- w->response.data->contenttype = CT_APPLICATION_JSON;
1361
- buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry_to_announce());
1362
- return 200;
1363
-
1364
-/*
1365
- * it seems that web browsers are ignoring 307 (Moved Temporarily)
1366
- * under certain conditions, when using CORS
1367
- * so this is commented and we use application level redirects instead
1368
- *
1369
- redirects++;
1370
-
1371
- if(redirects > registry_verify_cookies_redirects()) {
1372
- buffer_flush(w->response.data);
1373
- buffer_sprintf(w->response.data, "Your browser does not support cookies");
1374
- return 400;
1375
- }
1376
-
1377
- char *encoded_url = url_encode(machine_url);
1378
- if(!encoded_url) {
1379
- error("%llu: Cannot URL encode string '%s'", w->id, machine_url);
1380
- return 500;
1381
- }
1382
-
1383
- char *encoded_name = url_encode(url_name);
1384
- if(!encoded_name) {
1385
- free(encoded_url);
1386
- error("%llu: Cannot URL encode string '%s'", w->id, url_name);
1387
- return 500;
1388
- }
1389
-
1390
- char *encoded_guid = url_encode(machine_guid);
1391
- if(!encoded_guid) {
1392
- free(encoded_url);
1393
- free(encoded_name);
1394
- error("%llu: Cannot URL encode string '%s'", w->id, machine_guid);
1395
- return 500;
1396
- }
1397
-
1398
- buffer_sprintf(w->response.header, "Location: %s/api/v1/registry?action=access&machine=%s&name=%s&url=%s&redirects=%d\r\n",
1399
- registry_to_announce(), encoded_guid, encoded_name, encoded_url, redirects);
1400
-
1401
- free(encoded_guid);
1402
- free(encoded_name);
1403
- free(encoded_url);
1404
- return 307
1405
-*/
1406
- }
1407
-
1408
- if(unlikely(cookie && person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
1409
- person_guid[0] = '\0';
1410
-
1355
return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());
1356
1357
case 'D':