avl is now threat safe (found a condition where the trees could cause netdata to crash); added a still incomplete version of the new API
Costa Tsaousis (ktsaou) committed
Nov 28, 2015 at 00:03 UTC
537e212e1d9e9d64c250d43f39791e9ff30159af
10 files changed
+372
-22
src/appconfig.c
+3
-2
@@ -87,7 +87,8 @@ static int config_compare(void* a, void* b) {
87
88
avl_tree config_root_index = {
89
NULL,
90
- config_compare
90
+ config_compare,
91
+ PTHREAD_RWLOCK_INITIALIZER
92
};
93
94
#define config_index_add(cfg) avl_insert(&config_root_index, (avl *)(cfg))
@@ -141,7 +142,7 @@ struct config *config_create(const char *section)
142
if(!co->name) fatal("Cannot allocate config.name");
143
co->hash = simple_hash(co->name);
144
144
- co->values_index.compar = config_value_compare;
145
+ avl_init(&co->values_index, config_value_compare);
146
147
config_index_add(co);
148
src/apps_plugin.c
+2
-1
@@ -830,7 +830,8 @@ int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
830
831
avl_tree all_files_index = {
832
NULL,
833
- file_descriptor_compare
833
+ file_descriptor_compare,
834
+ PTHREAD_RWLOCK_INITIALIZER
835
};
836
837
static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
src/avl.c
+45
-12
@@ -67,7 +67,7 @@ void avl_nasty(avl* root) {
67
* returns 1 if the depth of the tree has grown
68
* Warning: do not insert elements already present
69
*/
70
-int avl_insert(avl_tree* t, avl* a) {
70
+int _avl_insert(avl_tree* t, avl* a) {
71
/* initialize */
72
a->left = 0;
73
a->right = 0;
@@ -84,7 +84,7 @@ int avl_insert(avl_tree* t, avl* a) {
84
avl_tree left_subtree;
85
left_subtree.root = t->root->left;
86
left_subtree.compar = t->compar;
87
- if (avl_insert(&left_subtree, a)) {
87
+ if (_avl_insert(&left_subtree, a)) {
88
switch (t->root->balance--) {
89
case 1:
90
return 0;
@@ -115,7 +115,7 @@ int avl_insert(avl_tree* t, avl* a) {
115
avl_tree right_subtree;
116
right_subtree.root = t->root->right;
117
right_subtree.compar = t->compar;
118
- if (avl_insert(&right_subtree, a)) {
118
+ if (_avl_insert(&right_subtree, a)) {
119
switch (t->root->balance++) {
120
case -1:
121
return 0;
@@ -142,16 +142,22 @@ int avl_insert(avl_tree* t, avl* a) {
142
}
143
}
144
}
145
+int avl_insert(avl_tree* t, avl* a) {
146
+ pthread_rwlock_wrlock(&t->rwlock);
147
+ int ret = _avl_insert(t, a);
148
+ pthread_rwlock_unlock(&t->rwlock);
149
+ return ret;
150
+}
151
152
/* Remove an element a from the AVL tree t
153
* returns -1 if the depth of the tree has shrunk
154
* Warning: if the element is not present in the tree,
155
* returns 0 as if it had been removed succesfully.
156
*/
151
-int avl_remove(avl_tree* t, avl* a) {
157
+int _avl_remove(avl_tree* t, avl* a) {
158
int b;
159
if (t->root == a)
154
- return avl_removeroot(t);
160
+ return _avl_removeroot(t);
161
b = t->compar(t->root, a);
162
if (b >= 0) {
163
/* remove from the left subtree */
@@ -159,7 +165,7 @@ int avl_remove(avl_tree* t, avl* a) {
165
avl_tree left_subtree;
166
if ((left_subtree.root = t->root->left)) {
167
left_subtree.compar = t->compar;
162
- ch = avl_remove(&left_subtree, a);
168
+ ch = _avl_remove(&left_subtree, a);
169
t->root->left = left_subtree.root;
170
if (ch) {
171
switch (t->root->balance++) {
@@ -193,7 +199,7 @@ int avl_remove(avl_tree* t, avl* a) {
199
avl_tree right_subtree;
200
if ((right_subtree.root = t->root->right)) {
201
right_subtree.compar = t->compar;
196
- ch = avl_remove(&right_subtree, a);
202
+ ch = _avl_remove(&right_subtree, a);
203
t->root->right = right_subtree.root;
204
if (ch) {
205
switch (t->root->balance--) {
@@ -224,10 +230,17 @@ int avl_remove(avl_tree* t, avl* a) {
230
return 0;
231
}
232
233
+int avl_remove(avl_tree* t, avl* a) {
234
+ pthread_rwlock_wrlock(&t->rwlock);
235
+ int ret = _avl_remove(t, a);
236
+ pthread_rwlock_unlock(&t->rwlock);
237
+ return ret;
238
+}
239
+
240
/* Remove the root of the AVL tree t
241
* Warning: dumps core if t is empty
242
*/
230
-int avl_removeroot(avl_tree* t) {
243
+int _avl_removeroot(avl_tree* t) {
244
int ch;
245
avl* a;
246
if (!t->root->left) {
@@ -253,7 +266,7 @@ int avl_removeroot(avl_tree* t) {
266
while (a->left)
267
a = a->left;
268
}
256
- ch = avl_remove(t, a);
269
+ ch = _avl_remove(t, a);
270
a->left = t->root->left;
271
a->right = t->root->right;
272
a->balance = t->root->balance;
@@ -263,12 +276,19 @@ int avl_removeroot(avl_tree* t) {
276
return 0;
277
}
278
279
+int avl_removeroot(avl_tree* t) {
280
+ pthread_rwlock_wrlock(&t->rwlock);
281
+ int ret = _avl_removeroot(t);
282
+ pthread_rwlock_unlock(&t->rwlock);
283
+ return ret;
284
+}
285
+
286
/* Iterate through elements in t from a range between a and b (inclusive)
287
* for each element calls iter(a) until it returns 0
288
* returns the last value returned by iterator or 0 if there were no calls
289
* Warning: a<=b must hold
290
*/
271
-int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
291
+int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
292
int x, c = 0;
293
if (!t->root)
294
return 0;
@@ -285,7 +305,7 @@ int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
305
avl_tree left_subtree;
306
if ((left_subtree.root = t->root->left)) {
307
left_subtree.compar = t->compar;
288
- if (!(c = avl_range(&left_subtree, a, b, iter, ret)))
308
+ if (!(c = _avl_range(&left_subtree, a, b, iter, ret)))
309
if (x > 0)
310
return 0;
311
}
@@ -302,7 +322,7 @@ int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
322
avl_tree right_subtree;
323
if ((right_subtree.root = t->root->right)) {
324
right_subtree.compar = t->compar;
305
- if (!(c = avl_range(&right_subtree, a, b, iter, ret)))
325
+ if (!(c = _avl_range(&right_subtree, a, b, iter, ret)))
326
if (x < 0)
327
return 0;
328
}
@@ -310,6 +330,13 @@ int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
330
return c;
331
}
332
333
+int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
334
+ pthread_rwlock_rdlock(&t->rwlock);
335
+ int ret2 = _avl_range(t, a, b, iter, ret);
336
+ pthread_rwlock_unlock(&t->rwlock);
337
+ return ret2;
338
+}
339
+
340
/* Iterate through elements in t equal to a
341
* for each element calls iter(a) until it returns 0
342
* returns the last value returned by iterator or 0 if there were no calls
@@ -317,3 +344,9 @@ int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
344
int avl_search(avl_tree* t, avl* a, int (*iter)(avl* a), avl** ret) {
345
return avl_range(t, a, a, iter, ret);
346
}
347
+
348
+void avl_init(avl_tree* t, int (*compar)(void* a, void* b)) {
349
+ t->root = NULL;
350
+ t->compar = compar;
351
+ pthread_rwlock_init(&t->rwlock, NULL);
352
+}
src/avl.h
+6
@@ -12,6 +12,7 @@
12
* Released under GNU General Public License (GPL) version 2
13
*
14
*/
15
+#include <pthread.h>
16
17
#ifndef _AVL_H
18
#define _AVL_H 1
@@ -29,6 +30,7 @@ typedef struct avl {
30
typedef struct avl_tree {
31
avl* root;
32
int (*compar)(void* a, void* b);
33
+ pthread_rwlock_t rwlock;
34
} avl_tree;
35
36
/* Public methods */
@@ -64,4 +66,8 @@ int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret);
66
*/
67
int avl_search(avl_tree* t, avl* a, int (*iter)(avl* a), avl** ret);
68
69
+/* Initialize the avl_tree
70
+ */
71
+void avl_init(avl_tree* t, int (*compar)(void* a, void* b));
72
+
73
#endif /* avl.h */
src/dictionary.c
+1
-1
@@ -88,7 +88,7 @@ DICTIONARY *dictionary_create(void) {
88
DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
89
if(!dict) fatal("Cannot allocate DICTIONARY");
90
91
- dict->values_index.compar = name_value_compare;
91
+ avl_init(&dict->values_index, name_value_compare);
92
pthread_rwlock_init(&dict->rwlock, NULL);
93
94
return dict;
src/plugin_tc.c
+3
-1
@@ -77,7 +77,8 @@ static int tc_device_compare(void* a, void* b) {
77
78
avl_tree tc_device_root_index = {
79
NULL,
80
- tc_device_compare
80
+ tc_device_compare,
81
+ PTHREAD_RWLOCK_INITIALIZER
82
};
83
84
#define tc_device_index_add(st) avl_insert(&tc_device_root_index, (avl *)(st))
@@ -323,6 +324,7 @@ static struct tc_device *tc_device_create(char *id)
324
325
d->classes_index.root = NULL;
326
d->classes_index.compar = tc_class_compare;
327
+ pthread_rwlock_init(&d->classes_index.rwlock, NULL);
328
329
tc_device_index_add(d);
330
}
src/rrd.c
+14
-4
@@ -26,6 +26,8 @@
26
// globals
27
28
// if not zero it gives the time (in seconds) to remove un-updated dimensions
29
+// DO NOT ENABLE
30
+// if dimensions are removed, the chart generation will have to run again
31
int rrd_delete_unupdated_dimensions = 0;
32
33
int rrd_update_every = UPDATE_EVERY;
@@ -50,7 +52,8 @@ static int rrdset_compare(void* a, void* b) {
52
53
avl_tree rrdset_root_index = {
54
NULL,
53
- rrdset_compare
55
+ rrdset_compare,
56
+ PTHREAD_RWLOCK_INITIALIZER
57
};
58
59
#define rrdset_index_add(st) avl_insert(&rrdset_root_index, (avl *)(st))
@@ -86,7 +89,8 @@ static int rrdset_compare_name(void* a, void* b) {
89
90
avl_tree rrdset_root_index_name = {
91
NULL,
89
- rrdset_compare_name
92
+ rrdset_compare_name,
93
+ PTHREAD_RWLOCK_INITIALIZER
94
};
95
96
int rrdset_index_add_name(RRDSET *st) {
@@ -318,6 +322,7 @@ void rrdset_reset(RRDSET *st)
322
for(rd = st->dimensions; rd ; rd = rd->next) {
323
rd->last_collected_time.tv_sec = 0;
324
rd->last_collected_time.tv_usec = 0;
325
+ rd->counter = 0;
326
bzero(rd->values, rd->entries * sizeof(storage_number));
327
}
328
}
@@ -426,8 +431,7 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
431
432
st->gap_when_lost_iterations_above = config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2;
433
429
- st->dimensions_index.root = NULL;
430
- st->dimensions_index.compar = rrddim_compare;
434
+ avl_init(&st->dimensions_index, rrddim_compare);
435
436
pthread_rwlock_init(&st->rwlock, NULL);
437
pthread_rwlock_wrlock(&rrdset_root_rwlock);
@@ -554,6 +558,9 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier
558
rd->entries = st->entries;
559
rd->update_every = st->update_every;
560
561
+ // prevent incremental calculation spikes
562
+ rd->counter = 0;
563
+
564
// append this dimension
565
if(!st->dimensions)
566
st->dimensions = rd;
@@ -813,6 +820,9 @@ unsigned long long rrdset_done(RRDSET *st)
820
// a read lock is OK here
821
pthread_rwlock_rdlock(&st->rwlock);
822
823
+ // enable the chart, if it was disabled
824
+ st->enabled = 1;
825
+
826
// check if the chart has a long time to be refreshed
827
if(unlikely(st->usec_since_last_update > st->entries * st->update_every * 1000000ULL)) {
828
info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
src/rrd2json.c
+99
@@ -12,6 +12,105 @@
12
#define HOSTNAME_MAX 1024
13
char *hostname = "unknown";
14
15
+void rrd_stats_api_v1_chart(RRDSET *st, struct web_buffer *wb)
16
+{
17
+ time_t now = time(NULL);
18
+
19
+ pthread_rwlock_rdlock(&st->rwlock);
20
+
21
+ web_buffer_snprintf(wb, 65536,
22
+ "\t\t{\n"
23
+ "\t\t\t\"id\": \"%s\",\n"
24
+ "\t\t\t\"name\": \"%s\",\n"
25
+ "\t\t\t\"type\": \"%s\",\n"
26
+ "\t\t\t\"family\": \"%s\",\n"
27
+ "\t\t\t\"title\": \"%s\",\n"
28
+ "\t\t\t\"priority\": %ld,\n"
29
+ "\t\t\t\"enabled\": %s,\n"
30
+ "\t\t\t\"units\": \"%s\",\n"
31
+ "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
32
+ "\t\t\t\"chart_type\": \"%s\",\n"
33
+ "\t\t\t\"duration\": %ld,\n"
34
+ "\t\t\t\"first_entry\": %lu,\n"
35
+ "\t\t\t\"last_entry\": %lu,\n"
36
+ "\t\t\t\"update_every\": %d,\n"
37
+ "\t\t\t\"dimensions\": {\n"
38
+ , st->id
39
+ , st->name
40
+ , st->type
41
+ , st->family
42
+ , st->title
43
+ , st->priority
44
+ , st->enabled?"true":"false"
45
+ , st->units
46
+ , st->name
47
+ , rrdset_type_name(st->chart_type)
48
+ , st->entries * st->update_every
49
+ , rrdset_first_entry_t(st)
50
+ , rrdset_last_entry_t(st)
51
+ , st->update_every
52
+ );
53
+
54
+ unsigned long memory = st->memsize;
55
+
56
+ int c = 0;
57
+ RRDDIM *rd;
58
+ for(rd = st->dimensions; rd ; rd = rd->next) {
59
+ if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
60
+
61
+ memory += rd->memsize;
62
+
63
+ web_buffer_snprintf(wb, 4096,
64
+ "%s"
65
+ "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
66
+ , c?",\n":""
67
+ , rd->id
68
+ , rd->name
69
+ );
70
+
71
+ c++;
72
+ }
73
+
74
+ web_buffer_snprintf(wb, 200,
75
+ "\n\t\t\t}\n"
76
+ "\t\t}"
77
+ );
78
+
79
+ pthread_rwlock_unlock(&st->rwlock);
80
+}
81
+
82
+void rrd_stats_api_v1_charts(struct web_buffer *wb)
83
+{
84
+ long c;
85
+ RRDSET *st;
86
+
87
+ web_buffer_snprintf(wb, 4096, "{\n"
88
+ "\t\"hostname\": \"%s\""
89
+ ",\n\t\"update_every\": %d"
90
+ ",\n\t\"history\": %d"
91
+ ",\n\t\"charts\": {"
92
+ , hostname
93
+ , rrd_update_every
94
+ , rrd_default_history_entries
95
+ );
96
+
97
+ pthread_rwlock_rdlock(&rrdset_root_rwlock);
98
+ for(st = rrdset_root, c = 0; st ; st = st->next) {
99
+ if(st->enabled) {
100
+ if(c) web_buffer_strcat(wb, ",");
101
+ web_buffer_strcat(wb, "\n\t\t\"");
102
+ web_buffer_strcat(wb, st->id);
103
+ web_buffer_strcat(wb, "\": ");
104
+ rrd_stats_api_v1_chart(st, wb);
105
+ c++;
106
+ }
107
+ }
108
+ pthread_rwlock_unlock(&rrdset_root_rwlock);
109
+
110
+ web_buffer_strcat(wb, "\n\t}\n}\n");
111
+}
112
+
113
+
114
unsigned long rrd_stats_one_json(RRDSET *st, char *options, struct web_buffer *wb)
115
{
116
time_t now = time(NULL);
src/rrd2json.h
+6
@@ -10,14 +10,20 @@
10
extern char *hostname;
11
12
// type of JSON generations
13
+#define DATASOURCE_INVALID -1
14
#define DATASOURCE_JSON 0
15
#define DATASOURCE_GOOGLE_JSON 1
16
#define DATASOURCE_GOOGLE_JSONP 2
17
+#define DATASOURCE_SSV 3
18
+#define DATASOURCE_CSV 4
19
20
#define GROUP_AVERAGE 0
21
#define GROUP_MAX 1
22
#define GROUP_SUM 2
23
24
+extern void rrd_stats_api_v1_chart(RRDSET *st, struct web_buffer *wb);
25
+extern void rrd_stats_api_v1_charts(struct web_buffer *wb);
26
+
27
extern unsigned long rrd_stats_one_json(RRDSET *st, char *options, struct web_buffer *wb);
28
29
extern void rrd_stats_graph_json(RRDSET *st, char *options, struct web_buffer *wb);
src/web_client.c
+193
-1
@@ -327,6 +327,193 @@ void web_client_enable_deflate(struct web_client *w) {
327
}
328
#endif // NETDATA_WITH_ZLIB
329
330
+int rrdset_validate_dimensions(RRDSET *st, struct web_buffer *dimensions)
331
+{
332
+ ;
333
+}
334
+
335
+int web_client_api_request_v1_data_format(char *name)
336
+{
337
+ if(!strcmp(name, "datatable"))
338
+ return DATASOURCE_GOOGLE_JSON;
339
+
340
+ else if(!strcmp(name, "datasource"))
341
+ return DATASOURCE_GOOGLE_JSONP;
342
+
343
+ else if(!strcmp(name, "json"))
344
+ return DATASOURCE_JSON;
345
+
346
+ else if(!strcmp(name, "ssv"))
347
+ return DATASOURCE_SSV;
348
+
349
+ else if(!strcmp(name, "csv"))
350
+ return DATASOURCE_CSV;
351
+
352
+ return DATASOURCE_INVALID;
353
+}
354
+
355
+int web_client_api_request_v1_charts(struct web_client *w, char *url)
356
+{
357
+ web_buffer_flush(w->data);
358
+ rrd_stats_api_v1_charts(w->data);
359
+ return 200;
360
+}
361
+
362
+int web_client_api_request_v1_chart(struct web_client *w, char *url)
363
+{
364
+ int ret = 400;
365
+ char *chart = NULL;
366
+
367
+ web_buffer_flush(w->data);
368
+
369
+ while(url) {
370
+ char *value = mystrsep(&url, "?&[]");
371
+ if(!value || !*value) continue;
372
+
373
+ char *name = mystrsep(&value, "=");
374
+ if(!name || !*name) continue;
375
+ if(!value || !*value) continue;
376
+
377
+ // name and value are now the parameters
378
+ // they are not null and not empty
379
+
380
+ if(!strcmp(name, "chart")) chart = value;
381
+ else {
382
+ web_buffer_snprintf(w->data, 1024, "Unknown parameter '%s' in request.", name);
383
+ goto cleanup;
384
+ }
385
+ }
386
+
387
+ if(!chart || !*chart) {
388
+ web_buffer_snprintf(w->data, 1024, "No chart id is given at the request.");
389
+ goto cleanup;
390
+ }
391
+
392
+ RRDSET *st = rrdset_find(chart);
393
+ if(!st) st = rrdset_find_byname(chart);
394
+ if(!st) {
395
+ web_buffer_snprintf(w->data, 1024, "Chart '%s' is not found.", chart);
396
+ ret = 404;
397
+ goto cleanup;
398
+ }
399
+
400
+ w->data->contenttype = CT_APPLICATION_JSON;
401
+ rrd_stats_api_v1_chart(st, w->data);
402
+ return 200;
403
+
404
+cleanup:
405
+ return ret;
406
+}
407
+
408
+int web_client_api_request_v1_data(struct web_client *w, char *url)
409
+{
410
+ int ret = 400;
411
+ struct web_buffer *dimensions = NULL;
412
+
413
+ web_buffer_flush(w->data);
414
+
415
+ char *chart = NULL
416
+ , *dim = NULL
417
+ , *before_str = NULL
418
+ , *after_str = NULL
419
+ , *points_str = NULL
420
+ , *group_str = NULL
421
+ , *options = NULL;
422
+
423
+ int format = DATASOURCE_JSON;
424
+
425
+ while(url) {
426
+ char *value = mystrsep(&url, "?&[]");
427
+ if(!value || !*value) continue;
428
+
429
+ char *name = mystrsep(&value, "=");
430
+ if(!name || !*name) continue;
431
+ if(!value || !*value) continue;
432
+
433
+ // name and value are now the parameters
434
+ // they are not null and not empty
435
+
436
+ if(!strcmp(name, "chart")) chart = value;
437
+ else if(!strcmp(name, "dimension") || !strcmp(name, "dim")) {
438
+ if(!dimensions) dimensions = web_buffer_create(strlen(value));
439
+ if(dimensions) {
440
+ web_buffer_strcat(dimensions, "|");
441
+ web_buffer_strcat(dimensions, value);
442
+ }
443
+ }
444
+ else if(!strcmp(name, "after")) after_str = value;
445
+ else if(!strcmp(name, "before")) before_str = value;
446
+ else if(!strcmp(name, "points")) points_str = value;
447
+ else if(!strcmp(name, "group")) group_str = value;
448
+ else if(!strcmp(name, "format")) {
449
+ format = web_client_api_request_v1_data_format(value);
450
+ }
451
+ else if(!strcmp(name, "options")) {
452
+ options = value;
453
+ }
454
+ else {
455
+ web_buffer_snprintf(w->data, 1024, "Unknown parameter '%s' in request.", name);
456
+ goto cleanup;
457
+ }
458
+ }
459
+
460
+ if(!chart || !*chart) {
461
+ web_buffer_snprintf(w->data, 1024, "No chart id is given at the request.");
462
+ goto cleanup;
463
+ }
464
+
465
+ RRDSET *st = rrdset_find(chart);
466
+ if(!st) st = rrdset_find_byname(chart);
467
+ if(!st) {
468
+ web_buffer_snprintf(w->data, 1024, "Chart '%s' is not found.", chart);
469
+ ret = 404;
470
+ goto cleanup;
471
+ }
472
+
473
+ uint32_t before = (before_str && *before_str)?atol(before_str):0;
474
+ uint32_t after = (after_str && *after_str) ?atol(after_str):0;
475
+ int points = (points_str && *points_str)?atoi(points_str):0;
476
+ int group = (group_str && *group_str) ?atoi(group_str):0;
477
+
478
+ web_buffer_snprintf(w->data, 1024, "API command 'data' for chart '%s', dimensions '%s', after '%s', before '%s', points '%s', group '%s', format '%s', options '%s'", chart, dim, after_str, before_str, points_str, group_str, format, options);
479
+
480
+cleanup:
481
+ if(dimensions) web_buffer_free(dimensions);
482
+ return ret;
483
+}
484
+
485
+int web_client_api_request_v1(struct web_client *w, char *url)
486
+{
487
+ // get the command
488
+ char *tok = mystrsep(&url, "/?&");
489
+ debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
490
+
491
+ if(strcmp(tok, "data") == 0)
492
+ return web_client_api_request_v1_data(w, url);
493
+ else if(strcmp(tok, "chart") == 0)
494
+ return web_client_api_request_v1_chart(w, url);
495
+ else if(strcmp(tok, "charts") == 0)
496
+ return web_client_api_request_v1_charts(w, url);
497
+
498
+ web_buffer_flush(w->data);
499
+ web_buffer_snprintf(w->data, 1024, "Unsupported v1 API command: %s", tok);
500
+ return 404;
501
+}
502
+
503
+int web_client_api_request(struct web_client *w, char *url)
504
+{
505
+ // get the api version
506
+ char *tok = mystrsep(&url, "/?&");
507
+ debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
508
+
509
+ if(strcmp(tok, "v1") == 0)
510
+ return web_client_api_request_v1(w, url);
511
+
512
+ web_buffer_flush(w->data);
513
+ web_buffer_snprintf(w->data, 1024, "Unsupported API version: %s", tok);
514
+ return 404;
515
+}
516
+
517
int web_client_data_request(struct web_client *w, char *url, int datasource_type)
518
{
519
char *args = strchr(url, '?');
@@ -587,7 +774,12 @@ void web_client_process(struct web_client *w) {
774
775
debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
776
590
- if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
777
+ if(strcmp(tok, "api") == 0) {
778
+ // the client is requesting api access
779
+ datasource_type = DATASOURCE_JSON;
780
+ code = web_client_api_request(w, url);
781
+ }
782
+ else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
783
// the client is requesting rrd data
784
datasource_type = DATASOURCE_JSON;
785
code = web_client_data_request(w, url, datasource_type);