added rrd memory mode "none" to allow netdata act as a proxy to forward metrics to another netdata
Costa Tsaousis (ktsaou) committed
Feb 23, 2017 at 22:39 UTC
1958a34558a2874bb771a9d92dd121f1118e45dc
14 files changed
+1221
-1153
CMakeLists.txt
+1
-1
@@ -124,7 +124,7 @@ set(NETDATA_SOURCE_FILES
124
src/web_client.h
125
src/web_server.c
126
src/web_server.h
127
- src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c src/rrdcalc.c src/rrdcalctemplate.c src/rrdvar.c src/rrddimvar.c src/rrdsetvar.c src/rrdpush.c src/rrdpush.h)
127
+ src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c src/rrdcalc.c src/rrdcalctemplate.c src/rrdvar.c src/rrddimvar.c src/rrdsetvar.c src/rrdpush.c src/rrdpush.h src/web_api_old.c src/web_api_old.h src/web_api_v1.c src/web_api_v1.h)
128
129
set(APPS_PLUGIN_SOURCE_FILES
130
src/appconfig.c
src/Makefile.am
+2
@@ -79,6 +79,8 @@ netdata_SOURCES = \
79
storage_number.c storage_number.h \
80
unit_test.c unit_test.h \
81
url.c url.h \
82
+ web_api_old.c web_api_old.h \
83
+ web_api_v1.c web_api_v1.h \
84
web_buffer.c web_buffer.h \
85
web_buffer_svg.c web_buffer_svg.h \
86
web_client.c web_client.h \
src/common.h
+2
@@ -217,6 +217,8 @@
217
#include "inlined.h"
218
#include "adaptive_resortable_list.h"
219
#include "rrdpush.h"
220
+#include "web_api_v1.h"
221
+#include "web_api_old.h"
222
223
extern char *netdata_configured_config_dir;
224
extern char *netdata_configured_log_dir;
src/main.c
+1
-1
@@ -703,7 +703,7 @@ int main(int argc, char **argv) {
703
// get default memory mode for the database
704
705
if(rrdpush_exclusive) {
706
- default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
706
+ default_rrd_memory_mode = RRD_MEMORY_MODE_NONE;
707
config_set("global", "memory mode", rrd_memory_mode_name(default_rrd_memory_mode));
708
}
709
else
src/rrd.c
+5
@@ -27,6 +27,9 @@ inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) {
27
case RRD_MEMORY_MODE_MAP:
28
return RRD_MEMORY_MODE_MAP_NAME;
29
30
+ case RRD_MEMORY_MODE_NONE:
31
+ return RRD_MEMORY_MODE_NONE_NAME;
32
+
33
case RRD_MEMORY_MODE_SAVE:
34
default:
35
return RRD_MEMORY_MODE_SAVE_NAME;
@@ -38,6 +41,8 @@ RRD_MEMORY_MODE rrd_memory_mode_id(const char *name) {
41
return RRD_MEMORY_MODE_RAM;
42
else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
43
return RRD_MEMORY_MODE_MAP;
44
+ else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_NONE_NAME)))
45
+ return RRD_MEMORY_MODE_NONE;
46
47
return RRD_MEMORY_MODE_SAVE;
48
}
src/rrd.h
+5
-3
@@ -39,11 +39,13 @@ const char *rrdset_type_name(RRDSET_TYPE chart_type);
39
// memory mode
40
41
typedef enum rrd_memory_mode {
42
- RRD_MEMORY_MODE_RAM = 0,
43
- RRD_MEMORY_MODE_MAP = 1,
44
- RRD_MEMORY_MODE_SAVE = 2
42
+ RRD_MEMORY_MODE_NONE = 0,
43
+ RRD_MEMORY_MODE_RAM = 1,
44
+ RRD_MEMORY_MODE_MAP = 2,
45
+ RRD_MEMORY_MODE_SAVE = 3
46
} RRD_MEMORY_MODE;
47
48
+#define RRD_MEMORY_MODE_NONE_NAME "none"
49
#define RRD_MEMORY_MODE_RAM_NAME "ram"
50
#define RRD_MEMORY_MODE_MAP_NAME "map"
51
#define RRD_MEMORY_MODE_SAVE_NAME "save"
src/rrddim.c
+3
-2
@@ -71,7 +71,7 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
71
rrdset_strncpyz_name(filename, id, FILENAME_MAX);
72
snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
73
74
- if(st->rrd_memory_mode != RRD_MEMORY_MODE_RAM) {
74
+ if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
75
rd = (RRDDIM *)mymmap(fullfilename, size, ((st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1);
76
if(likely(rd)) {
77
// we have a file mapped for rd
@@ -132,7 +132,7 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
132
if(unlikely(!rd)) {
133
// if we didn't manage to get a mmap'd dimension, just create one
134
rd = callocz(1, size);
135
- rd->rrd_memory_mode = RRD_MEMORY_MODE_RAM;
135
+ rd->rrd_memory_mode = (st->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
136
}
137
138
rd->memsize = size;
@@ -243,6 +243,7 @@ void rrddim_free(RRDSET *st, RRDDIM *rd)
243
munmap(rd, rd->memsize);
244
break;
245
246
+ case RRD_MEMORY_MODE_NONE:
247
case RRD_MEMORY_MODE_RAM:
248
debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
249
freez((void *)rd->id);
src/rrdset.c
+5
-2
@@ -325,6 +325,9 @@ RRDSET *rrdset_create(RRDHOST *host, const char *type, const char *id, const cha
325
long entries = align_entries_to_pagesize(rentries);
326
if(entries != rentries) entries = config_set_number(config_section, "history", entries);
327
328
+ if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE && entries != rentries)
329
+ entries = config_set_number(config_section, "history", 10);
330
+
331
int enabled = config_get_boolean(config_section, "enabled", 1);
332
if(!enabled) entries = 5;
333
@@ -339,7 +342,7 @@ RRDSET *rrdset_create(RRDHOST *host, const char *type, const char *id, const cha
342
debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
343
344
snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
342
- if(host->rrd_memory_mode != RRD_MEMORY_MODE_RAM) {
345
+ if(host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
346
st = (RRDSET *) mymmap(fullfilename, size, ((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 0);
347
if(st) {
348
memset(&st->avl, 0, sizeof(avl));
@@ -405,7 +408,7 @@ RRDSET *rrdset_create(RRDHOST *host, const char *type, const char *id, const cha
408
409
if(unlikely(!st)) {
410
st = callocz(1, size);
408
- st->rrd_memory_mode = RRD_MEMORY_MODE_RAM;
411
+ st->rrd_memory_mode = (host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
412
}
413
414
st->config_section = strdup(config_section);
src/web_api_old.c
new
+231
@@ -0,0 +1,231 @@
1
+#include "common.h"
2
+
3
+int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type) {
4
+ if(!url || !*url) {
5
+ buffer_flush(w->response.data);
6
+ buffer_sprintf(w->response.data, "Incomplete request.");
7
+ return 400;
8
+ }
9
+
10
+ RRDSET *st = NULL;
11
+
12
+ char *args = strchr(url, '?');
13
+ if(args) {
14
+ *args='\0';
15
+ args = &args[1];
16
+ }
17
+
18
+ // get the name of the data to show
19
+ char *tok = mystrsep(&url, "/");
20
+ if(!tok) tok = "";
21
+
22
+ // do we have such a data set?
23
+ if(*tok) {
24
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
25
+ st = rrdset_find_byname(host, tok);
26
+ if(!st) st = rrdset_find(host, tok);
27
+ }
28
+
29
+ if(!st) {
30
+ // we don't have it
31
+ // try to send a file with that name
32
+ buffer_flush(w->response.data);
33
+ return(mysendfile(w, tok));
34
+ }
35
+
36
+ // we have it
37
+ debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
38
+
39
+ // how many entries does the client want?
40
+ int lines = (int)st->entries;
41
+ int group_count = 1;
42
+ time_t after = 0, before = 0;
43
+ int group_method = GROUP_AVERAGE;
44
+ int nonzero = 0;
45
+
46
+ if(url) {
47
+ // parse the lines required
48
+ tok = mystrsep(&url, "/");
49
+ if(tok) lines = str2i(tok);
50
+ if(lines < 1) lines = 1;
51
+ }
52
+ if(url) {
53
+ // parse the group count required
54
+ tok = mystrsep(&url, "/");
55
+ if(tok && *tok) group_count = str2i(tok);
56
+ if(group_count < 1) group_count = 1;
57
+ //if(group_count > save_history / 20) group_count = save_history / 20;
58
+ }
59
+ if(url) {
60
+ // parse the grouping method required
61
+ tok = mystrsep(&url, "/");
62
+ if(tok && *tok) {
63
+ if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
64
+ else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
65
+ else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
66
+ else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
67
+ }
68
+ }
69
+ if(url) {
70
+ // parse after time
71
+ tok = mystrsep(&url, "/");
72
+ if(tok && *tok) after = str2ul(tok);
73
+ if(after < 0) after = 0;
74
+ }
75
+ if(url) {
76
+ // parse before time
77
+ tok = mystrsep(&url, "/");
78
+ if(tok && *tok) before = str2ul(tok);
79
+ if(before < 0) before = 0;
80
+ }
81
+ if(url) {
82
+ // parse nonzero
83
+ tok = mystrsep(&url, "/");
84
+ if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
85
+ }
86
+
87
+ w->response.data->contenttype = CT_APPLICATION_JSON;
88
+ buffer_flush(w->response.data);
89
+
90
+ char *google_version = "0.6";
91
+ char *google_reqId = "0";
92
+ char *google_sig = "0";
93
+ char *google_out = "json";
94
+ char *google_responseHandler = "google.visualization.Query.setResponse";
95
+ char *google_outFileName = NULL;
96
+ time_t last_timestamp_in_data = 0;
97
+ if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
98
+
99
+ w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
100
+
101
+ while(args) {
102
+ tok = mystrsep(&args, "&");
103
+ if(tok && *tok) {
104
+ char *name = mystrsep(&tok, "=");
105
+ if(name && *name && strcmp(name, "tqx") == 0) {
106
+ char *key = mystrsep(&tok, ":");
107
+ char *value = mystrsep(&tok, ";");
108
+ if(key && value && *key && *value) {
109
+ if(strcmp(key, "version") == 0)
110
+ google_version = value;
111
+
112
+ else if(strcmp(key, "reqId") == 0)
113
+ google_reqId = value;
114
+
115
+ else if(strcmp(key, "sig") == 0)
116
+ google_sig = value;
117
+
118
+ else if(strcmp(key, "out") == 0)
119
+ google_out = value;
120
+
121
+ else if(strcmp(key, "responseHandler") == 0)
122
+ google_responseHandler = value;
123
+
124
+ else if(strcmp(key, "outFileName") == 0)
125
+ google_outFileName = value;
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
132
+ w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
133
+ );
134
+
135
+ if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
136
+ last_timestamp_in_data = strtoul(google_sig, NULL, 0);
137
+
138
+ // check the client wants json
139
+ if(strcmp(google_out, "json") != 0) {
140
+ buffer_sprintf(w->response.data,
141
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
142
+ google_responseHandler, google_version, google_reqId, google_out);
143
+ return 200;
144
+ }
145
+ }
146
+ }
147
+
148
+ if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
149
+ buffer_sprintf(w->response.data,
150
+ "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
151
+ google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
152
+ }
153
+
154
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
155
+ w->id, st->name, st->id, lines, group_count, group_method, after, before);
156
+
157
+ time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
158
+
159
+ if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
160
+ if(timestamp_in_data > last_timestamp_in_data)
161
+ buffer_strcat(w->response.data, "});");
162
+
163
+ else {
164
+ // the client already has the latest data
165
+ buffer_flush(w->response.data);
166
+ buffer_sprintf(w->response.data,
167
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
168
+ google_responseHandler, google_version, google_reqId);
169
+ }
170
+ }
171
+
172
+ return 200;
173
+}
174
+
175
+inline int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url) {
176
+ return web_client_api_old_data_request(host, w, url, DATASOURCE_JSON);
177
+}
178
+
179
+inline int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url) {
180
+ return web_client_api_old_data_request(host, w, url, DATASOURCE_DATATABLE_JSONP);
181
+}
182
+
183
+inline int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url) {
184
+ // get the name of the data to show
185
+ char *tok = mystrsep(&url, "/?&");
186
+ if(tok && *tok) {
187
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
188
+
189
+ // do we have such a data set?
190
+ RRDSET *st = rrdset_find_byname(host, tok);
191
+ if(!st) st = rrdset_find(host, tok);
192
+ if(!st) {
193
+ // we don't have it
194
+ // try to send a file with that name
195
+ buffer_flush(w->response.data);
196
+ return mysendfile(w, tok);
197
+ }
198
+
199
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
200
+ w->response.data->contenttype = CT_APPLICATION_JSON;
201
+ buffer_flush(w->response.data);
202
+ rrd_stats_graph_json(st, url, w->response.data);
203
+ return 200;
204
+ }
205
+
206
+ buffer_flush(w->response.data);
207
+ buffer_strcat(w->response.data, "Graph name?\r\n");
208
+ return 400;
209
+}
210
+
211
+inline int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url) {
212
+ (void)url;
213
+
214
+ buffer_flush(w->response.data);
215
+ RRDSET *st;
216
+
217
+ rrdhost_rdlock(host);
218
+ rrdset_foreach_read(st, host) buffer_sprintf(w->response.data, "%s\n", st->name);
219
+ rrdhost_unlock(host);
220
+
221
+ return 200;
222
+}
223
+
224
+inline int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url) {
225
+ (void)url;
226
+
227
+ w->response.data->contenttype = CT_APPLICATION_JSON;
228
+ buffer_flush(w->response.data);
229
+ rrd_stats_all_json(host, w->response.data);
230
+ return 200;
231
+}
src/web_api_old.h
new
+13
@@ -0,0 +1,13 @@
1
+#ifndef NETDATA_WEB_API_OLD_H
2
+#define NETDATA_WEB_API_OLD_H
3
+
4
+#include "common.h"
5
+
6
+extern int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type);
7
+extern int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url);
8
+extern int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url);
9
+extern int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url);
10
+extern int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url);
11
+extern int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url);
12
+
13
+#endif //NETDATA_WEB_API_OLD_H
src/web_api_v1.c
new
+902
@@ -0,0 +1,902 @@
1
+#include "common.h"
2
+
3
+inline int web_client_api_request_v1_data_group(char *name, int def) {
4
+ if(!strcmp(name, "average"))
5
+ return GROUP_AVERAGE;
6
+
7
+ else if(!strcmp(name, "min"))
8
+ return GROUP_MIN;
9
+
10
+ else if(!strcmp(name, "max"))
11
+ return GROUP_MAX;
12
+
13
+ else if(!strcmp(name, "sum"))
14
+ return GROUP_SUM;
15
+
16
+ else if(!strcmp(name, "incremental-sum"))
17
+ return GROUP_INCREMENTAL_SUM;
18
+
19
+ return def;
20
+}
21
+
22
+inline uint32_t web_client_api_request_v1_data_options(char *o) {
23
+ uint32_t ret = 0x00000000;
24
+ char *tok;
25
+
26
+ while(o && *o && (tok = mystrsep(&o, ", |"))) {
27
+ if(!*tok) continue;
28
+
29
+ if(!strcmp(tok, "nonzero"))
30
+ ret |= RRDR_OPTION_NONZERO;
31
+ else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
32
+ ret |= RRDR_OPTION_REVERSED;
33
+ else if(!strcmp(tok, "jsonwrap"))
34
+ ret |= RRDR_OPTION_JSON_WRAP;
35
+ else if(!strcmp(tok, "min2max"))
36
+ ret |= RRDR_OPTION_MIN2MAX;
37
+ else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
38
+ ret |= RRDR_OPTION_MILLISECONDS;
39
+ else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
40
+ ret |= RRDR_OPTION_ABSOLUTE;
41
+ else if(!strcmp(tok, "seconds"))
42
+ ret |= RRDR_OPTION_SECONDS;
43
+ else if(!strcmp(tok, "null2zero"))
44
+ ret |= RRDR_OPTION_NULL2ZERO;
45
+ else if(!strcmp(tok, "objectrows"))
46
+ ret |= RRDR_OPTION_OBJECTSROWS;
47
+ else if(!strcmp(tok, "google_json"))
48
+ ret |= RRDR_OPTION_GOOGLE_JSON;
49
+ else if(!strcmp(tok, "percentage"))
50
+ ret |= RRDR_OPTION_PERCENTAGE;
51
+ else if(!strcmp(tok, "unaligned"))
52
+ ret |= RRDR_OPTION_NOT_ALIGNED;
53
+ }
54
+
55
+ return ret;
56
+}
57
+
58
+inline uint32_t web_client_api_request_v1_data_format(char *name) {
59
+ if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
60
+ return DATASOURCE_DATATABLE_JSON;
61
+
62
+ else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
63
+ return DATASOURCE_DATATABLE_JSONP;
64
+
65
+ else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
66
+ return DATASOURCE_JSON;
67
+
68
+ else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
69
+ return DATASOURCE_JSONP;
70
+
71
+ else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
72
+ return DATASOURCE_SSV;
73
+
74
+ else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
75
+ return DATASOURCE_CSV;
76
+
77
+ else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
78
+ return DATASOURCE_TSV;
79
+
80
+ else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
81
+ return DATASOURCE_HTML;
82
+
83
+ else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
84
+ return DATASOURCE_JS_ARRAY;
85
+
86
+ else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
87
+ return DATASOURCE_SSV_COMMA;
88
+
89
+ else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
90
+ return DATASOURCE_CSV_JSON_ARRAY;
91
+
92
+ return DATASOURCE_JSON;
93
+}
94
+
95
+inline uint32_t web_client_api_request_v1_data_google_format(char *name) {
96
+ if(!strcmp(name, "json"))
97
+ return DATASOURCE_DATATABLE_JSONP;
98
+
99
+ else if(!strcmp(name, "html"))
100
+ return DATASOURCE_HTML;
101
+
102
+ else if(!strcmp(name, "csv"))
103
+ return DATASOURCE_CSV;
104
+
105
+ else if(!strcmp(name, "tsv-excel"))
106
+ return DATASOURCE_TSV;
107
+
108
+ return DATASOURCE_JSON;
109
+}
110
+
111
+
112
+inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url) {
113
+ int all = 0;
114
+
115
+ while(url) {
116
+ char *value = mystrsep(&url, "?&");
117
+ if (!value || !*value) continue;
118
+
119
+ if(!strcmp(value, "all")) all = 1;
120
+ else if(!strcmp(value, "active")) all = 0;
121
+ }
122
+
123
+ buffer_flush(w->response.data);
124
+ w->response.data->contenttype = CT_APPLICATION_JSON;
125
+ health_alarms2json(host, w->response.data, all);
126
+ return 200;
127
+}
128
+
129
+inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url) {
130
+ uint32_t after = 0;
131
+
132
+ while(url) {
133
+ char *value = mystrsep(&url, "?&");
134
+ if (!value || !*value) continue;
135
+
136
+ char *name = mystrsep(&value, "=");
137
+ if(!name || !*name) continue;
138
+ if(!value || !*value) continue;
139
+
140
+ if(!strcmp(name, "after")) after = (uint32_t)strtoul(value, NULL, 0);
141
+ }
142
+
143
+ buffer_flush(w->response.data);
144
+ w->response.data->contenttype = CT_APPLICATION_JSON;
145
+ health_alarm_log2json(host, w->response.data, after);
146
+ return 200;
147
+}
148
+
149
+inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) {
150
+ int ret = 400;
151
+ char *chart = NULL;
152
+
153
+ buffer_flush(w->response.data);
154
+
155
+ while(url) {
156
+ char *value = mystrsep(&url, "?&");
157
+ if(!value || !*value) continue;
158
+
159
+ char *name = mystrsep(&value, "=");
160
+ if(!name || !*name) continue;
161
+ if(!value || !*value) continue;
162
+
163
+ // name and value are now the parameters
164
+ // they are not null and not empty
165
+
166
+ if(!strcmp(name, "chart")) chart = value;
167
+ //else {
168
+ /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
169
+ // goto cleanup;
170
+ //}
171
+ }
172
+
173
+ if(!chart || !*chart) {
174
+ buffer_sprintf(w->response.data, "No chart id is given at the request.");
175
+ goto cleanup;
176
+ }
177
+
178
+ RRDSET *st = rrdset_find(host, chart);
179
+ if(!st) st = rrdset_find_byname(host, chart);
180
+ if(!st) {
181
+ buffer_strcat(w->response.data, "Chart is not found: ");
182
+ buffer_strcat_htmlescape(w->response.data, chart);
183
+ ret = 404;
184
+ goto cleanup;
185
+ }
186
+
187
+ w->response.data->contenttype = CT_APPLICATION_JSON;
188
+ callback(st, w->response.data);
189
+ return 200;
190
+
191
+ cleanup:
192
+ return ret;
193
+}
194
+
195
+inline int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url) {
196
+ return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json);
197
+}
198
+
199
+inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url) {
200
+ (void)url;
201
+
202
+ buffer_flush(w->response.data);
203
+ w->response.data->contenttype = CT_APPLICATION_JSON;
204
+ rrd_stats_api_v1_charts(host, w->response.data);
205
+ return 200;
206
+}
207
+
208
+inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) {
209
+ int format = ALLMETRICS_SHELL;
210
+
211
+ while(url) {
212
+ char *value = mystrsep(&url, "?&");
213
+ if (!value || !*value) continue;
214
+
215
+ char *name = mystrsep(&value, "=");
216
+ if(!name || !*name) continue;
217
+ if(!value || !*value) continue;
218
+
219
+ if(!strcmp(name, "format")) {
220
+ if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
221
+ format = ALLMETRICS_SHELL;
222
+ else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
223
+ format = ALLMETRICS_PROMETHEUS;
224
+ else
225
+ format = 0;
226
+ }
227
+ }
228
+
229
+ buffer_flush(w->response.data);
230
+ buffer_no_cacheable(w->response.data);
231
+
232
+ switch(format) {
233
+ case ALLMETRICS_SHELL:
234
+ w->response.data->contenttype = CT_TEXT_PLAIN;
235
+ rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data);
236
+ return 200;
237
+
238
+ case ALLMETRICS_PROMETHEUS:
239
+ w->response.data->contenttype = CT_PROMETHEUS;
240
+ rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data);
241
+ return 200;
242
+
243
+ default:
244
+ w->response.data->contenttype = CT_TEXT_PLAIN;
245
+ buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported.");
246
+ return 400;
247
+ }
248
+}
249
+
250
+inline int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url) {
251
+ return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart);
252
+}
253
+
254
+int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
255
+ int ret = 400;
256
+ buffer_flush(w->response.data);
257
+
258
+ BUFFER *dimensions = NULL;
259
+
260
+ const char *chart = NULL
261
+ , *before_str = NULL
262
+ , *after_str = NULL
263
+ , *points_str = NULL
264
+ , *multiply_str = NULL
265
+ , *divide_str = NULL
266
+ , *label = NULL
267
+ , *units = NULL
268
+ , *label_color = NULL
269
+ , *value_color = NULL
270
+ , *refresh_str = NULL
271
+ , *precision_str = NULL
272
+ , *alarm = NULL;
273
+
274
+ int group = GROUP_AVERAGE;
275
+ uint32_t options = 0x00000000;
276
+
277
+ while(url) {
278
+ char *value = mystrsep(&url, "/?&");
279
+ if(!value || !*value) continue;
280
+
281
+ char *name = mystrsep(&value, "=");
282
+ if(!name || !*name) continue;
283
+ if(!value || !*value) continue;
284
+
285
+ debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
286
+
287
+ // name and value are now the parameters
288
+ // they are not null and not empty
289
+
290
+ if(!strcmp(name, "chart")) chart = value;
291
+ else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
292
+ if(!dimensions)
293
+ dimensions = buffer_create(100);
294
+
295
+ buffer_strcat(dimensions, "|");
296
+ buffer_strcat(dimensions, value);
297
+ }
298
+ else if(!strcmp(name, "after")) after_str = value;
299
+ else if(!strcmp(name, "before")) before_str = value;
300
+ else if(!strcmp(name, "points")) points_str = value;
301
+ else if(!strcmp(name, "group")) {
302
+ group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
303
+ }
304
+ else if(!strcmp(name, "options")) {
305
+ options |= web_client_api_request_v1_data_options(value);
306
+ }
307
+ else if(!strcmp(name, "label")) label = value;
308
+ else if(!strcmp(name, "units")) units = value;
309
+ else if(!strcmp(name, "label_color")) label_color = value;
310
+ else if(!strcmp(name, "value_color")) value_color = value;
311
+ else if(!strcmp(name, "multiply")) multiply_str = value;
312
+ else if(!strcmp(name, "divide")) divide_str = value;
313
+ else if(!strcmp(name, "refresh")) refresh_str = value;
314
+ else if(!strcmp(name, "precision")) precision_str = value;
315
+ else if(!strcmp(name, "alarm")) alarm = value;
316
+ }
317
+
318
+ if(!chart || !*chart) {
319
+ buffer_no_cacheable(w->response.data);
320
+ buffer_sprintf(w->response.data, "No chart id is given at the request.");
321
+ goto cleanup;
322
+ }
323
+
324
+ RRDSET *st = rrdset_find(host, chart);
325
+ if(!st) st = rrdset_find_byname(host, chart);
326
+ if(!st) {
327
+ buffer_no_cacheable(w->response.data);
328
+ buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
329
+ ret = 200;
330
+ goto cleanup;
331
+ }
332
+
333
+ RRDCALC *rc = NULL;
334
+ if(alarm) {
335
+ rc = rrdcalc_find(st, alarm);
336
+ if (!rc) {
337
+ buffer_no_cacheable(w->response.data);
338
+ buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
339
+ ret = 200;
340
+ goto cleanup;
341
+ }
342
+ }
343
+
344
+ long long multiply = (multiply_str && *multiply_str )?str2l(multiply_str):1;
345
+ long long divide = (divide_str && *divide_str )?str2l(divide_str):1;
346
+ long long before = (before_str && *before_str )?str2l(before_str):0;
347
+ long long after = (after_str && *after_str )?str2l(after_str):-st->update_every;
348
+ int points = (points_str && *points_str )?str2i(points_str):1;
349
+ int precision = (precision_str && *precision_str)?str2i(precision_str):-1;
350
+
351
+ if(!multiply) multiply = 1;
352
+ if(!divide) divide = 1;
353
+
354
+ int refresh = 0;
355
+ if(refresh_str && *refresh_str) {
356
+ if(!strcmp(refresh_str, "auto")) {
357
+ if(rc) refresh = rc->update_every;
358
+ else if(options & RRDR_OPTION_NOT_ALIGNED)
359
+ refresh = st->update_every;
360
+ else {
361
+ refresh = (int)(before - after);
362
+ if(refresh < 0) refresh = -refresh;
363
+ }
364
+ }
365
+ else {
366
+ refresh = str2i(refresh_str);
367
+ if(refresh < 0) refresh = -refresh;
368
+ }
369
+ }
370
+
371
+ if(!label) {
372
+ if(alarm) {
373
+ char *s = (char *)alarm;
374
+ while(*s) {
375
+ if(*s == '_') *s = ' ';
376
+ s++;
377
+ }
378
+ label = alarm;
379
+ }
380
+ else if(dimensions) {
381
+ const char *dim = buffer_tostring(dimensions);
382
+ if(*dim == '|') dim++;
383
+ label = dim;
384
+ }
385
+ else
386
+ label = st->name;
387
+ }
388
+ if(!units) {
389
+ if(alarm) {
390
+ if(rc->units)
391
+ units = rc->units;
392
+ else
393
+ units = "";
394
+ }
395
+ else if(options & RRDR_OPTION_PERCENTAGE)
396
+ units = "%";
397
+ else
398
+ units = st->units;
399
+ }
400
+
401
+ debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
402
+ , w->id
403
+ , chart
404
+ , alarm?alarm:""
405
+ , (dimensions)?buffer_tostring(dimensions):""
406
+ , after
407
+ , before
408
+ , points
409
+ , group
410
+ , options
411
+ );
412
+
413
+ if(rc) {
414
+ if (refresh > 0) {
415
+ buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
416
+ w->response.data->expires = now_realtime_sec() + refresh;
417
+ }
418
+ else buffer_no_cacheable(w->response.data);
419
+
420
+ if(!value_color) {
421
+ switch(rc->status) {
422
+ case RRDCALC_STATUS_CRITICAL:
423
+ value_color = "red";
424
+ break;
425
+
426
+ case RRDCALC_STATUS_WARNING:
427
+ value_color = "orange";
428
+ break;
429
+
430
+ case RRDCALC_STATUS_CLEAR:
431
+ value_color = "brightgreen";
432
+ break;
433
+
434
+ case RRDCALC_STATUS_UNDEFINED:
435
+ value_color = "lightgrey";
436
+ break;
437
+
438
+ case RRDCALC_STATUS_UNINITIALIZED:
439
+ value_color = "#000";
440
+ break;
441
+
442
+ default:
443
+ value_color = "grey";
444
+ break;
445
+ }
446
+ }
447
+
448
+ buffer_svg(w->response.data,
449
+ label,
450
+ (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
451
+ units,
452
+ label_color,
453
+ value_color,
454
+ precision);
455
+ ret = 200;
456
+ }
457
+ else {
458
+ time_t latest_timestamp = 0;
459
+ int value_is_null = 1;
460
+ calculated_number n = NAN;
461
+ ret = 500;
462
+
463
+ // if the collected value is too old, don't calculate its value
464
+ if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
465
+ ret = rrd2value(st,
466
+ w->response.data,
467
+ &n,
468
+ (dimensions) ? buffer_tostring(dimensions) : NULL,
469
+ points,
470
+ after,
471
+ before,
472
+ group,
473
+ options,
474
+ NULL,
475
+ &latest_timestamp,
476
+ &value_is_null);
477
+
478
+ // if the value cannot be calculated, show empty badge
479
+ if (ret != 200) {
480
+ buffer_no_cacheable(w->response.data);
481
+ value_is_null = 1;
482
+ n = 0;
483
+ ret = 200;
484
+ }
485
+ else if (refresh > 0) {
486
+ buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
487
+ w->response.data->expires = now_realtime_sec() + refresh;
488
+ }
489
+ else buffer_no_cacheable(w->response.data);
490
+
491
+ // render the badge
492
+ buffer_svg(w->response.data,
493
+ label,
494
+ (value_is_null)?NAN:(n * multiply / divide),
495
+ units,
496
+ label_color,
497
+ value_color,
498
+ precision);
499
+ }
500
+
501
+ cleanup:
502
+ buffer_free(dimensions);
503
+ return ret;
504
+}
505
+
506
+// returns the HTTP code
507
+inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) {
508
+ debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
509
+
510
+ int ret = 400;
511
+ BUFFER *dimensions = NULL;
512
+
513
+ buffer_flush(w->response.data);
514
+
515
+ char *google_version = "0.6",
516
+ *google_reqId = "0",
517
+ *google_sig = "0",
518
+ *google_out = "json",
519
+ *responseHandler = NULL,
520
+ *outFileName = NULL;
521
+
522
+ time_t last_timestamp_in_data = 0, google_timestamp = 0;
523
+
524
+ char *chart = NULL
525
+ , *before_str = NULL
526
+ , *after_str = NULL
527
+ , *points_str = NULL;
528
+
529
+ int group = GROUP_AVERAGE;
530
+ uint32_t format = DATASOURCE_JSON;
531
+ uint32_t options = 0x00000000;
532
+
533
+ while(url) {
534
+ char *value = mystrsep(&url, "?&");
535
+ if(!value || !*value) continue;
536
+
537
+ char *name = mystrsep(&value, "=");
538
+ if(!name || !*name) continue;
539
+ if(!value || !*value) continue;
540
+
541
+ debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
542
+
543
+ // name and value are now the parameters
544
+ // they are not null and not empty
545
+
546
+ if(!strcmp(name, "chart")) chart = value;
547
+ else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
548
+ if(!dimensions) dimensions = buffer_create(100);
549
+ buffer_strcat(dimensions, "|");
550
+ buffer_strcat(dimensions, value);
551
+ }
552
+ else if(!strcmp(name, "after")) after_str = value;
553
+ else if(!strcmp(name, "before")) before_str = value;
554
+ else if(!strcmp(name, "points")) points_str = value;
555
+ else if(!strcmp(name, "group")) {
556
+ group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
557
+ }
558
+ else if(!strcmp(name, "format")) {
559
+ format = web_client_api_request_v1_data_format(value);
560
+ }
561
+ else if(!strcmp(name, "options")) {
562
+ options |= web_client_api_request_v1_data_options(value);
563
+ }
564
+ else if(!strcmp(name, "callback")) {
565
+ responseHandler = value;
566
+ }
567
+ else if(!strcmp(name, "filename")) {
568
+ outFileName = value;
569
+ }
570
+ else if(!strcmp(name, "tqx")) {
571
+ // parse Google Visualization API options
572
+ // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
573
+ char *tqx_name, *tqx_value;
574
+
575
+ while(value) {
576
+ tqx_value = mystrsep(&value, ";");
577
+ if(!tqx_value || !*tqx_value) continue;
578
+
579
+ tqx_name = mystrsep(&tqx_value, ":");
580
+ if(!tqx_name || !*tqx_name) continue;
581
+ if(!tqx_value || !*tqx_value) continue;
582
+
583
+ if(!strcmp(tqx_name, "version"))
584
+ google_version = tqx_value;
585
+ else if(!strcmp(tqx_name, "reqId"))
586
+ google_reqId = tqx_value;
587
+ else if(!strcmp(tqx_name, "sig")) {
588
+ google_sig = tqx_value;
589
+ google_timestamp = strtoul(google_sig, NULL, 0);
590
+ }
591
+ else if(!strcmp(tqx_name, "out")) {
592
+ google_out = tqx_value;
593
+ format = web_client_api_request_v1_data_google_format(google_out);
594
+ }
595
+ else if(!strcmp(tqx_name, "responseHandler"))
596
+ responseHandler = tqx_value;
597
+ else if(!strcmp(tqx_name, "outFileName"))
598
+ outFileName = tqx_value;
599
+ }
600
+ }
601
+ }
602
+
603
+ if(!chart || !*chart) {
604
+ buffer_sprintf(w->response.data, "No chart id is given at the request.");
605
+ goto cleanup;
606
+ }
607
+
608
+ RRDSET *st = rrdset_find(host, chart);
609
+ if(!st) st = rrdset_find_byname(host, chart);
610
+ if(!st) {
611
+ buffer_strcat(w->response.data, "Chart is not found: ");
612
+ buffer_strcat_htmlescape(w->response.data, chart);
613
+ ret = 404;
614
+ goto cleanup;
615
+ }
616
+
617
+ long long before = (before_str && *before_str)?str2l(before_str):0;
618
+ long long after = (after_str && *after_str) ?str2l(after_str):0;
619
+ int points = (points_str && *points_str)?str2i(points_str):0;
620
+
621
+ debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
622
+ , w->id
623
+ , chart
624
+ , (dimensions)?buffer_tostring(dimensions):""
625
+ , after
626
+ , before
627
+ , points
628
+ , group
629
+ , format
630
+ , options
631
+ );
632
+
633
+ if(outFileName && *outFileName) {
634
+ buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
635
+ debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
636
+ }
637
+
638
+ if(format == DATASOURCE_DATATABLE_JSONP) {
639
+ if(responseHandler == NULL)
640
+ responseHandler = "google.visualization.Query.setResponse";
641
+
642
+ debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
643
+ w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
644
+ );
645
+
646
+ buffer_sprintf(w->response.data,
647
+ "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
648
+ responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
649
+ }
650
+ else if(format == DATASOURCE_JSONP) {
651
+ if(responseHandler == NULL)
652
+ responseHandler = "callback";
653
+
654
+ buffer_strcat(w->response.data, responseHandler);
655
+ buffer_strcat(w->response.data, "(");
656
+ }
657
+
658
+ ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
659
+
660
+ if(format == DATASOURCE_DATATABLE_JSONP) {
661
+ if(google_timestamp < last_timestamp_in_data)
662
+ buffer_strcat(w->response.data, "});");
663
+
664
+ else {
665
+ // the client already has the latest data
666
+ buffer_flush(w->response.data);
667
+ buffer_sprintf(w->response.data,
668
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
669
+ responseHandler, google_version, google_reqId);
670
+ }
671
+ }
672
+ else if(format == DATASOURCE_JSONP)
673
+ buffer_strcat(w->response.data, ");");
674
+
675
+ cleanup:
676
+ buffer_free(dimensions);
677
+ return ret;
678
+}
679
+
680
+inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url) {
681
+ static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
682
+ hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
683
+ hash_to = 0 /*, hash_redirects = 0 */;
684
+
685
+ if(unlikely(!hash_action)) {
686
+ hash_action = simple_hash("action");
687
+ hash_access = simple_hash("access");
688
+ hash_hello = simple_hash("hello");
689
+ hash_delete = simple_hash("delete");
690
+ hash_search = simple_hash("search");
691
+ hash_switch = simple_hash("switch");
692
+ hash_machine = simple_hash("machine");
693
+ hash_url = simple_hash("url");
694
+ hash_name = simple_hash("name");
695
+ hash_delete_url = simple_hash("delete_url");
696
+ hash_for = simple_hash("for");
697
+ hash_to = simple_hash("to");
698
+/*
699
+ hash_redirects = simple_hash("redirects");
700
+*/
701
+ }
702
+
703
+ char person_guid[GUID_LEN + 1] = "";
704
+
705
+ debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
706
+
707
+ // FIXME
708
+ // The browser may send multiple cookies with our id
709
+
710
+ char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
711
+ if(cookie)
712
+ strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
713
+
714
+ char action = '\0';
715
+ char *machine_guid = NULL,
716
+ *machine_url = NULL,
717
+ *url_name = NULL,
718
+ *search_machine_guid = NULL,
719
+ *delete_url = NULL,
720
+ *to_person_guid = NULL;
721
+/*
722
+ int redirects = 0;
723
+*/
724
+
725
+ while(url) {
726
+ char *value = mystrsep(&url, "?&");
727
+ if (!value || !*value) continue;
728
+
729
+ char *name = mystrsep(&value, "=");
730
+ if (!name || !*name) continue;
731
+ if (!value || !*value) continue;
732
+
733
+ debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
734
+
735
+ uint32_t hash = simple_hash(name);
736
+
737
+ if(hash == hash_action && !strcmp(name, "action")) {
738
+ uint32_t vhash = simple_hash(value);
739
+
740
+ if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
741
+ else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
742
+ else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
743
+ else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
744
+ else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
745
+#ifdef NETDATA_INTERNAL_CHECKS
746
+ else error("unknown registry action '%s'", value);
747
+#endif /* NETDATA_INTERNAL_CHECKS */
748
+ }
749
+/*
750
+ else if(hash == hash_redirects && !strcmp(name, "redirects"))
751
+ redirects = atoi(value);
752
+*/
753
+ else if(hash == hash_machine && !strcmp(name, "machine"))
754
+ machine_guid = value;
755
+
756
+ else if(hash == hash_url && !strcmp(name, "url"))
757
+ machine_url = value;
758
+
759
+ else if(action == 'A') {
760
+ if(hash == hash_name && !strcmp(name, "name"))
761
+ url_name = value;
762
+ }
763
+ else if(action == 'D') {
764
+ if(hash == hash_delete_url && !strcmp(name, "delete_url"))
765
+ delete_url = value;
766
+ }
767
+ else if(action == 'S') {
768
+ if(hash == hash_for && !strcmp(name, "for"))
769
+ search_machine_guid = value;
770
+ }
771
+ else if(action == 'W') {
772
+ if(hash == hash_to && !strcmp(name, "to"))
773
+ to_person_guid = value;
774
+ }
775
+#ifdef NETDATA_INTERNAL_CHECKS
776
+ else error("unused registry URL parameter '%s' with value '%s'", name, value);
777
+#endif /* NETDATA_INTERNAL_CHECKS */
778
+ }
779
+
780
+ if(respect_web_browser_do_not_track_policy && w->donottrack) {
781
+ buffer_flush(w->response.data);
782
+ buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
783
+ return 400;
784
+ }
785
+
786
+ if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
787
+ error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
788
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
789
+ buffer_flush(w->response.data);
790
+ buffer_strcat(w->response.data, "Invalid registry Access request.");
791
+ return 400;
792
+ }
793
+ else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
794
+ error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
795
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
796
+ buffer_flush(w->response.data);
797
+ buffer_strcat(w->response.data, "Invalid registry Delete request.");
798
+ return 400;
799
+ }
800
+ else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
801
+ error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
802
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
803
+ buffer_flush(w->response.data);
804
+ buffer_strcat(w->response.data, "Invalid registry Search request.");
805
+ return 400;
806
+ }
807
+ else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
808
+ error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
809
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
810
+ buffer_flush(w->response.data);
811
+ buffer_strcat(w->response.data, "Invalid registry Switch request.");
812
+ return 400;
813
+ }
814
+
815
+ switch(action) {
816
+ case 'A':
817
+ w->tracking_required = 1;
818
+ return registry_request_access_json(host, w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());
819
+
820
+ case 'D':
821
+ w->tracking_required = 1;
822
+ return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());
823
+
824
+ case 'S':
825
+ w->tracking_required = 1;
826
+ return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());
827
+
828
+ case 'W':
829
+ w->tracking_required = 1;
830
+ return registry_request_switch_json(host, w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec());
831
+
832
+ case 'H':
833
+ return registry_request_hello_json(host, w);
834
+
835
+ default:
836
+ buffer_flush(w->response.data);
837
+ buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
838
+ return 400;
839
+ }
840
+}
841
+
842
+inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {
843
+ static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0;
844
+
845
+ if(unlikely(hash_data == 0)) {
846
+ hash_data = simple_hash("data");
847
+ hash_chart = simple_hash("chart");
848
+ hash_charts = simple_hash("charts");
849
+ hash_registry = simple_hash("registry");
850
+ hash_badge = simple_hash("badge.svg");
851
+ hash_alarms = simple_hash("alarms");
852
+ hash_alarm_log = simple_hash("alarm_log");
853
+ hash_alarm_variables = simple_hash("alarm_variables");
854
+ hash_raw = simple_hash("allmetrics");
855
+ }
856
+
857
+ // get the command
858
+ char *tok = mystrsep(&url, "/?&");
859
+ if(tok && *tok) {
860
+ debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
861
+ uint32_t hash = simple_hash(tok);
862
+
863
+ if(hash == hash_data && !strcmp(tok, "data"))
864
+ return web_client_api_request_v1_data(host, w, url);
865
+
866
+ else if(hash == hash_chart && !strcmp(tok, "chart"))
867
+ return web_client_api_request_v1_chart(host, w, url);
868
+
869
+ else if(hash == hash_charts && !strcmp(tok, "charts"))
870
+ return web_client_api_request_v1_charts(host, w, url);
871
+
872
+ else if(hash == hash_registry && !strcmp(tok, "registry"))
873
+ return web_client_api_request_v1_registry(host, w, url);
874
+
875
+ else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
876
+ return web_client_api_request_v1_badge(host, w, url);
877
+
878
+ else if(hash == hash_alarms && !strcmp(tok, "alarms"))
879
+ return web_client_api_request_v1_alarms(host, w, url);
880
+
881
+ else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
882
+ return web_client_api_request_v1_alarm_log(host, w, url);
883
+
884
+ else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables"))
885
+ return web_client_api_request_v1_alarm_variables(host, w, url);
886
+
887
+ else if(hash == hash_raw && !strcmp(tok, "allmetrics"))
888
+ return web_client_api_request_v1_allmetrics(host, w, url);
889
+
890
+ else {
891
+ buffer_flush(w->response.data);
892
+ buffer_strcat(w->response.data, "Unsupported v1 API command: ");
893
+ buffer_strcat_htmlescape(w->response.data, tok);
894
+ return 404;
895
+ }
896
+ }
897
+ else {
898
+ buffer_flush(w->response.data);
899
+ buffer_sprintf(w->response.data, "Which API v1 command?");
900
+ return 400;
901
+ }
902
+}
src/web_api_v1.h
new
+21
@@ -0,0 +1,21 @@
1
+#ifndef NETDATA_WEB_API_V1_H
2
+#define NETDATA_WEB_API_V1_H
3
+
4
+extern int web_client_api_request_v1_data_group(char *name, int def);
5
+extern uint32_t web_client_api_request_v1_data_options(char *o);
6
+extern uint32_t web_client_api_request_v1_data_format(char *name);
7
+extern uint32_t web_client_api_request_v1_data_google_format(char *name);
8
+
9
+extern int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url);
10
+extern int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url);
11
+extern int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf));
12
+extern int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url);
13
+extern int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url);
14
+extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url);
15
+extern int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url);
16
+extern int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url);
17
+extern int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url);
18
+extern int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url);
19
+extern int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url);
20
+
21
+#endif //NETDATA_WEB_API_V1_H
src/web_client.c
+27
-1144
@@ -288,8 +288,7 @@ gid_t web_files_gid(void) {
288
return(owner_gid);
289
}
290
291
-int mysendfile(struct web_client *w, char *filename)
292
-{
291
+int mysendfile(struct web_client *w, char *filename) {
292
debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
293
294
// skip leading slashes
@@ -531,98 +530,6 @@ void buffer_data_options2string(BUFFER *wb, uint32_t options) {
530
}
531
}
532
534
-uint32_t web_client_api_request_v1_data_options(char *o)
535
-{
536
- uint32_t ret = 0x00000000;
537
- char *tok;
538
-
539
- while(o && *o && (tok = mystrsep(&o, ", |"))) {
540
- if(!*tok) continue;
541
-
542
- if(!strcmp(tok, "nonzero"))
543
- ret |= RRDR_OPTION_NONZERO;
544
- else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
545
- ret |= RRDR_OPTION_REVERSED;
546
- else if(!strcmp(tok, "jsonwrap"))
547
- ret |= RRDR_OPTION_JSON_WRAP;
548
- else if(!strcmp(tok, "min2max"))
549
- ret |= RRDR_OPTION_MIN2MAX;
550
- else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
551
- ret |= RRDR_OPTION_MILLISECONDS;
552
- else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
553
- ret |= RRDR_OPTION_ABSOLUTE;
554
- else if(!strcmp(tok, "seconds"))
555
- ret |= RRDR_OPTION_SECONDS;
556
- else if(!strcmp(tok, "null2zero"))
557
- ret |= RRDR_OPTION_NULL2ZERO;
558
- else if(!strcmp(tok, "objectrows"))
559
- ret |= RRDR_OPTION_OBJECTSROWS;
560
- else if(!strcmp(tok, "google_json"))
561
- ret |= RRDR_OPTION_GOOGLE_JSON;
562
- else if(!strcmp(tok, "percentage"))
563
- ret |= RRDR_OPTION_PERCENTAGE;
564
- else if(!strcmp(tok, "unaligned"))
565
- ret |= RRDR_OPTION_NOT_ALIGNED;
566
- }
567
-
568
- return ret;
569
-}
570
-
571
-uint32_t web_client_api_request_v1_data_format(char *name)
572
-{
573
- if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
574
- return DATASOURCE_DATATABLE_JSON;
575
-
576
- else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
577
- return DATASOURCE_DATATABLE_JSONP;
578
-
579
- else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
580
- return DATASOURCE_JSON;
581
-
582
- else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
583
- return DATASOURCE_JSONP;
584
-
585
- else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
586
- return DATASOURCE_SSV;
587
-
588
- else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
589
- return DATASOURCE_CSV;
590
-
591
- else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
592
- return DATASOURCE_TSV;
593
-
594
- else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
595
- return DATASOURCE_HTML;
596
-
597
- else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
598
- return DATASOURCE_JS_ARRAY;
599
-
600
- else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
601
- return DATASOURCE_SSV_COMMA;
602
-
603
- else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
604
- return DATASOURCE_CSV_JSON_ARRAY;
605
-
606
- return DATASOURCE_JSON;
607
-}
608
-
609
-uint32_t web_client_api_request_v1_data_google_format(char *name)
610
-{
611
- if(!strcmp(name, "json"))
612
- return DATASOURCE_DATATABLE_JSONP;
613
-
614
- else if(!strcmp(name, "html"))
615
- return DATASOURCE_HTML;
616
-
617
- else if(!strcmp(name, "csv"))
618
- return DATASOURCE_CSV;
619
-
620
- else if(!strcmp(name, "tsv-excel"))
621
- return DATASOURCE_TSV;
622
-
623
- return DATASOURCE_JSON;
624
-}
625
-
533
const char *group_method2string(int group) {
534
switch(group) {
535
case GROUP_UNDEFINED:
@@ -648,826 +555,14 @@ const char *group_method2string(int group) {
555
}
556
}
557
651
-int web_client_api_request_v1_data_group(char *name, int def)
652
-{
653
- if(!strcmp(name, "average"))
654
- return GROUP_AVERAGE;
655
-
656
- else if(!strcmp(name, "min"))
657
- return GROUP_MIN;
658
-
659
- else if(!strcmp(name, "max"))
660
- return GROUP_MAX;
661
-
662
- else if(!strcmp(name, "sum"))
663
- return GROUP_SUM;
664
-
665
- else if(!strcmp(name, "incremental-sum"))
666
- return GROUP_INCREMENTAL_SUM;
667
-
668
- return def;
669
-}
670
-
671
-int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url)
672
-{
673
- int all = 0;
674
-
675
- while(url) {
676
- char *value = mystrsep(&url, "?&");
677
- if (!value || !*value) continue;
678
-
679
- if(!strcmp(value, "all")) all = 1;
680
- else if(!strcmp(value, "active")) all = 0;
681
- }
682
-
683
- buffer_flush(w->response.data);
684
- w->response.data->contenttype = CT_APPLICATION_JSON;
685
- health_alarms2json(host, w->response.data, all);
686
- return 200;
687
-}
688
-
689
-int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url)
690
-{
691
- uint32_t after = 0;
692
-
693
- while(url) {
694
- char *value = mystrsep(&url, "?&");
695
- if (!value || !*value) continue;
696
-
697
- char *name = mystrsep(&value, "=");
698
- if(!name || !*name) continue;
699
- if(!value || !*value) continue;
700
-
701
- if(!strcmp(name, "after")) after = (uint32_t)strtoul(value, NULL, 0);
702
- }
703
-
704
- buffer_flush(w->response.data);
705
- w->response.data->contenttype = CT_APPLICATION_JSON;
706
- health_alarm_log2json(host, w->response.data, after);
707
- return 200;
708
-}
709
-
710
-int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf))
711
-{
712
- int ret = 400;
713
- char *chart = NULL;
714
-
715
- buffer_flush(w->response.data);
716
-
717
- while(url) {
718
- char *value = mystrsep(&url, "?&");
719
- if(!value || !*value) continue;
720
-
721
- char *name = mystrsep(&value, "=");
722
- if(!name || !*name) continue;
723
- if(!value || !*value) continue;
724
-
725
- // name and value are now the parameters
726
- // they are not null and not empty
727
-
728
- if(!strcmp(name, "chart")) chart = value;
729
- //else {
730
- /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
731
- // goto cleanup;
732
- //}
733
- }
734
-
735
- if(!chart || !*chart) {
736
- buffer_sprintf(w->response.data, "No chart id is given at the request.");
737
- goto cleanup;
738
- }
739
-
740
- RRDSET *st = rrdset_find(host, chart);
741
- if(!st) st = rrdset_find_byname(host, chart);
742
- if(!st) {
743
- buffer_strcat(w->response.data, "Chart is not found: ");
744
- buffer_strcat_htmlescape(w->response.data, chart);
745
- ret = 404;
746
- goto cleanup;
747
- }
748
-
749
- w->response.data->contenttype = CT_APPLICATION_JSON;
750
- callback(st, w->response.data);
751
- return 200;
752
-
753
- cleanup:
754
- return ret;
755
-}
756
-
757
-int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url)
758
-{
759
- return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json);
760
-}
761
-
762
-int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url)
763
-{
764
- (void)url;
765
-
766
- buffer_flush(w->response.data);
767
- w->response.data->contenttype = CT_APPLICATION_JSON;
768
- rrd_stats_api_v1_charts(host, w->response.data);
769
- return 200;
770
-}
771
-
772
-int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url)
773
-{
774
- int format = ALLMETRICS_SHELL;
775
-
776
- while(url) {
777
- char *value = mystrsep(&url, "?&");
778
- if (!value || !*value) continue;
779
-
780
- char *name = mystrsep(&value, "=");
781
- if(!name || !*name) continue;
782
- if(!value || !*value) continue;
783
-
784
- if(!strcmp(name, "format")) {
785
- if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
786
- format = ALLMETRICS_SHELL;
787
- else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
788
- format = ALLMETRICS_PROMETHEUS;
789
- else
790
- format = 0;
791
- }
792
- }
793
-
794
- buffer_flush(w->response.data);
795
- buffer_no_cacheable(w->response.data);
796
-
797
- switch(format) {
798
- case ALLMETRICS_SHELL:
799
- w->response.data->contenttype = CT_TEXT_PLAIN;
800
- rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data);
801
- return 200;
802
-
803
- case ALLMETRICS_PROMETHEUS:
804
- w->response.data->contenttype = CT_PROMETHEUS;
805
- rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data);
806
- return 200;
807
-
808
- default:
809
- w->response.data->contenttype = CT_TEXT_PLAIN;
810
- buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported.");
811
- return 400;
812
- }
813
-}
814
-
815
-int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url)
816
-{
817
- return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart);
818
-}
819
-
820
-int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
821
- int ret = 400;
822
- buffer_flush(w->response.data);
823
-
824
- BUFFER *dimensions = NULL;
825
-
826
- const char *chart = NULL
827
- , *before_str = NULL
828
- , *after_str = NULL
829
- , *points_str = NULL
830
- , *multiply_str = NULL
831
- , *divide_str = NULL
832
- , *label = NULL
833
- , *units = NULL
834
- , *label_color = NULL
835
- , *value_color = NULL
836
- , *refresh_str = NULL
837
- , *precision_str = NULL
838
- , *alarm = NULL;
839
-
840
- int group = GROUP_AVERAGE;
841
- uint32_t options = 0x00000000;
842
-
843
- while(url) {
844
- char *value = mystrsep(&url, "/?&");
845
- if(!value || !*value) continue;
846
-
847
- char *name = mystrsep(&value, "=");
848
- if(!name || !*name) continue;
849
- if(!value || !*value) continue;
850
-
851
- debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
852
-
853
- // name and value are now the parameters
854
- // they are not null and not empty
855
-
856
- if(!strcmp(name, "chart")) chart = value;
857
- else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
858
- if(!dimensions)
859
- dimensions = buffer_create(100);
860
-
861
- buffer_strcat(dimensions, "|");
862
- buffer_strcat(dimensions, value);
863
- }
864
- else if(!strcmp(name, "after")) after_str = value;
865
- else if(!strcmp(name, "before")) before_str = value;
866
- else if(!strcmp(name, "points")) points_str = value;
867
- else if(!strcmp(name, "group")) {
868
- group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
869
- }
870
- else if(!strcmp(name, "options")) {
871
- options |= web_client_api_request_v1_data_options(value);
872
- }
873
- else if(!strcmp(name, "label")) label = value;
874
- else if(!strcmp(name, "units")) units = value;
875
- else if(!strcmp(name, "label_color")) label_color = value;
876
- else if(!strcmp(name, "value_color")) value_color = value;
877
- else if(!strcmp(name, "multiply")) multiply_str = value;
878
- else if(!strcmp(name, "divide")) divide_str = value;
879
- else if(!strcmp(name, "refresh")) refresh_str = value;
880
- else if(!strcmp(name, "precision")) precision_str = value;
881
- else if(!strcmp(name, "alarm")) alarm = value;
882
- }
883
-
884
- if(!chart || !*chart) {
885
- buffer_no_cacheable(w->response.data);
886
- buffer_sprintf(w->response.data, "No chart id is given at the request.");
887
- goto cleanup;
888
- }
889
-
890
- RRDSET *st = rrdset_find(host, chart);
891
- if(!st) st = rrdset_find_byname(host, chart);
892
- if(!st) {
893
- buffer_no_cacheable(w->response.data);
894
- buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
895
- ret = 200;
896
- goto cleanup;
897
- }
898
-
899
- RRDCALC *rc = NULL;
900
- if(alarm) {
901
- rc = rrdcalc_find(st, alarm);
902
- if (!rc) {
903
- buffer_no_cacheable(w->response.data);
904
- buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
905
- ret = 200;
906
- goto cleanup;
907
- }
908
- }
909
-
910
- long long multiply = (multiply_str && *multiply_str )?str2l(multiply_str):1;
911
- long long divide = (divide_str && *divide_str )?str2l(divide_str):1;
912
- long long before = (before_str && *before_str )?str2l(before_str):0;
913
- long long after = (after_str && *after_str )?str2l(after_str):-st->update_every;
914
- int points = (points_str && *points_str )?str2i(points_str):1;
915
- int precision = (precision_str && *precision_str)?str2i(precision_str):-1;
916
-
917
- if(!multiply) multiply = 1;
918
- if(!divide) divide = 1;
919
-
920
- int refresh = 0;
921
- if(refresh_str && *refresh_str) {
922
- if(!strcmp(refresh_str, "auto")) {
923
- if(rc) refresh = rc->update_every;
924
- else if(options & RRDR_OPTION_NOT_ALIGNED)
925
- refresh = st->update_every;
926
- else {
927
- refresh = (int)(before - after);
928
- if(refresh < 0) refresh = -refresh;
929
- }
930
- }
931
- else {
932
- refresh = str2i(refresh_str);
933
- if(refresh < 0) refresh = -refresh;
934
- }
935
- }
936
-
937
- if(!label) {
938
- if(alarm) {
939
- char *s = (char *)alarm;
940
- while(*s) {
941
- if(*s == '_') *s = ' ';
942
- s++;
943
- }
944
- label = alarm;
945
- }
946
- else if(dimensions) {
947
- const char *dim = buffer_tostring(dimensions);
948
- if(*dim == '|') dim++;
949
- label = dim;
950
- }
951
- else
952
- label = st->name;
953
- }
954
- if(!units) {
955
- if(alarm) {
956
- if(rc->units)
957
- units = rc->units;
958
- else
959
- units = "";
960
- }
961
- else if(options & RRDR_OPTION_PERCENTAGE)
962
- units = "%";
963
- else
964
- units = st->units;
965
- }
966
-
967
- debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
968
- , w->id
969
- , chart
970
- , alarm?alarm:""
971
- , (dimensions)?buffer_tostring(dimensions):""
972
- , after
973
- , before
974
- , points
975
- , group
976
- , options
977
- );
978
-
979
- if(rc) {
980
- if (refresh > 0) {
981
- buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
982
- w->response.data->expires = now_realtime_sec() + refresh;
983
- }
984
- else buffer_no_cacheable(w->response.data);
985
-
986
- if(!value_color) {
987
- switch(rc->status) {
988
- case RRDCALC_STATUS_CRITICAL:
989
- value_color = "red";
990
- break;
991
-
992
- case RRDCALC_STATUS_WARNING:
993
- value_color = "orange";
994
- break;
995
-
996
- case RRDCALC_STATUS_CLEAR:
997
- value_color = "brightgreen";
998
- break;
999
-
1000
- case RRDCALC_STATUS_UNDEFINED:
1001
- value_color = "lightgrey";
1002
- break;
1003
-
1004
- case RRDCALC_STATUS_UNINITIALIZED:
1005
- value_color = "#000";
1006
- break;
1007
-
1008
- default:
1009
- value_color = "grey";
1010
- break;
1011
- }
1012
- }
1013
-
1014
- buffer_svg(w->response.data,
1015
- label,
1016
- (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
1017
- units,
1018
- label_color,
1019
- value_color,
1020
- precision);
1021
- ret = 200;
1022
- }
1023
- else {
1024
- time_t latest_timestamp = 0;
1025
- int value_is_null = 1;
1026
- calculated_number n = NAN;
1027
- ret = 500;
1028
-
1029
- // if the collected value is too old, don't calculate its value
1030
- if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
1031
- ret = rrd2value(st,
1032
- w->response.data,
1033
- &n,
1034
- (dimensions) ? buffer_tostring(dimensions) : NULL,
1035
- points,
1036
- after,
1037
- before,
1038
- group,
1039
- options,
1040
- NULL,
1041
- &latest_timestamp,
1042
- &value_is_null);
1043
-
1044
- // if the value cannot be calculated, show empty badge
1045
- if (ret != 200) {
1046
- buffer_no_cacheable(w->response.data);
1047
- value_is_null = 1;
1048
- n = 0;
1049
- ret = 200;
1050
- }
1051
- else if (refresh > 0) {
1052
- buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
1053
- w->response.data->expires = now_realtime_sec() + refresh;
1054
- }
1055
- else buffer_no_cacheable(w->response.data);
1056
-
1057
- // render the badge
1058
- buffer_svg(w->response.data,
1059
- label,
1060
- (value_is_null)?NAN:(n * multiply / divide),
1061
- units,
1062
- label_color,
1063
- value_color,
1064
- precision);
1065
- }
1066
-
1067
-cleanup:
1068
- buffer_free(dimensions);
1069
- return ret;
1070
-}
1071
-
1072
-// returns the HTTP code
1073
-int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url)
1074
-{
1075
- debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
1076
-
1077
- int ret = 400;
1078
- BUFFER *dimensions = NULL;
1079
-
1080
- buffer_flush(w->response.data);
1081
-
1082
- char *google_version = "0.6",
1083
- *google_reqId = "0",
1084
- *google_sig = "0",
1085
- *google_out = "json",
1086
- *responseHandler = NULL,
1087
- *outFileName = NULL;
1088
-
1089
- time_t last_timestamp_in_data = 0, google_timestamp = 0;
1090
-
1091
- char *chart = NULL
1092
- , *before_str = NULL
1093
- , *after_str = NULL
1094
- , *points_str = NULL;
1095
-
1096
- int group = GROUP_AVERAGE;
1097
- uint32_t format = DATASOURCE_JSON;
1098
- uint32_t options = 0x00000000;
1099
-
1100
- while(url) {
1101
- char *value = mystrsep(&url, "?&");
1102
- if(!value || !*value) continue;
1103
-
1104
- char *name = mystrsep(&value, "=");
1105
- if(!name || !*name) continue;
1106
- if(!value || !*value) continue;
1107
-
1108
- debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
1109
-
1110
- // name and value are now the parameters
1111
- // they are not null and not empty
1112
-
1113
- if(!strcmp(name, "chart")) chart = value;
1114
- else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
1115
- if(!dimensions) dimensions = buffer_create(100);
1116
- buffer_strcat(dimensions, "|");
1117
- buffer_strcat(dimensions, value);
1118
- }
1119
- else if(!strcmp(name, "after")) after_str = value;
1120
- else if(!strcmp(name, "before")) before_str = value;
1121
- else if(!strcmp(name, "points")) points_str = value;
1122
- else if(!strcmp(name, "group")) {
1123
- group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
1124
- }
1125
- else if(!strcmp(name, "format")) {
1126
- format = web_client_api_request_v1_data_format(value);
1127
- }
1128
- else if(!strcmp(name, "options")) {
1129
- options |= web_client_api_request_v1_data_options(value);
1130
- }
1131
- else if(!strcmp(name, "callback")) {
1132
- responseHandler = value;
1133
- }
1134
- else if(!strcmp(name, "filename")) {
1135
- outFileName = value;
1136
- }
1137
- else if(!strcmp(name, "tqx")) {
1138
- // parse Google Visualization API options
1139
- // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
1140
- char *tqx_name, *tqx_value;
1141
-
1142
- while(value) {
1143
- tqx_value = mystrsep(&value, ";");
1144
- if(!tqx_value || !*tqx_value) continue;
1145
-
1146
- tqx_name = mystrsep(&tqx_value, ":");
1147
- if(!tqx_name || !*tqx_name) continue;
1148
- if(!tqx_value || !*tqx_value) continue;
1149
-
1150
- if(!strcmp(tqx_name, "version"))
1151
- google_version = tqx_value;
1152
- else if(!strcmp(tqx_name, "reqId"))
1153
- google_reqId = tqx_value;
1154
- else if(!strcmp(tqx_name, "sig")) {
1155
- google_sig = tqx_value;
1156
- google_timestamp = strtoul(google_sig, NULL, 0);
1157
- }
1158
- else if(!strcmp(tqx_name, "out")) {
1159
- google_out = tqx_value;
1160
- format = web_client_api_request_v1_data_google_format(google_out);
1161
- }
1162
- else if(!strcmp(tqx_name, "responseHandler"))
1163
- responseHandler = tqx_value;
1164
- else if(!strcmp(tqx_name, "outFileName"))
1165
- outFileName = tqx_value;
1166
- }
1167
- }
1168
- }
1169
-
1170
- if(!chart || !*chart) {
1171
- buffer_sprintf(w->response.data, "No chart id is given at the request.");
1172
- goto cleanup;
1173
- }
1174
-
1175
- RRDSET *st = rrdset_find(host, chart);
1176
- if(!st) st = rrdset_find_byname(host, chart);
1177
- if(!st) {
1178
- buffer_strcat(w->response.data, "Chart is not found: ");
1179
- buffer_strcat_htmlescape(w->response.data, chart);
1180
- ret = 404;
1181
- goto cleanup;
1182
- }
1183
-
1184
- long long before = (before_str && *before_str)?str2l(before_str):0;
1185
- long long after = (after_str && *after_str) ?str2l(after_str):0;
1186
- int points = (points_str && *points_str)?str2i(points_str):0;
1187
-
1188
- debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
1189
- , w->id
1190
- , chart
1191
- , (dimensions)?buffer_tostring(dimensions):""
1192
- , after
1193
- , before
1194
- , points
1195
- , group
1196
- , format
1197
- , options
1198
- );
1199
-
1200
- if(outFileName && *outFileName) {
1201
- buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
1202
- debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
1203
- }
1204
-
1205
- if(format == DATASOURCE_DATATABLE_JSONP) {
1206
- if(responseHandler == NULL)
1207
- responseHandler = "google.visualization.Query.setResponse";
1208
-
1209
- debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
1210
- w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
1211
- );
1212
-
1213
- buffer_sprintf(w->response.data,
1214
- "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
1215
- responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
1216
- }
1217
- else if(format == DATASOURCE_JSONP) {
1218
- if(responseHandler == NULL)
1219
- responseHandler = "callback";
1220
-
1221
- buffer_strcat(w->response.data, responseHandler);
1222
- buffer_strcat(w->response.data, "(");
1223
- }
1224
-
1225
- ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
1226
-
1227
- if(format == DATASOURCE_DATATABLE_JSONP) {
1228
- if(google_timestamp < last_timestamp_in_data)
1229
- buffer_strcat(w->response.data, "});");
1230
-
1231
- else {
1232
- // the client already has the latest data
1233
- buffer_flush(w->response.data);
1234
- buffer_sprintf(w->response.data,
1235
- "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
1236
- responseHandler, google_version, google_reqId);
1237
- }
1238
- }
1239
- else if(format == DATASOURCE_JSONP)
1240
- buffer_strcat(w->response.data, ");");
1241
-
1242
-cleanup:
1243
- buffer_free(dimensions);
1244
- return ret;
1245
-}
1246
-
1247
-
1248
-int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url)
1249
-{
1250
- static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
1251
- hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
1252
- hash_to = 0 /*, hash_redirects = 0 */;
1253
-
1254
- if(unlikely(!hash_action)) {
1255
- hash_action = simple_hash("action");
1256
- hash_access = simple_hash("access");
1257
- hash_hello = simple_hash("hello");
1258
- hash_delete = simple_hash("delete");
1259
- hash_search = simple_hash("search");
1260
- hash_switch = simple_hash("switch");
1261
- hash_machine = simple_hash("machine");
1262
- hash_url = simple_hash("url");
1263
- hash_name = simple_hash("name");
1264
- hash_delete_url = simple_hash("delete_url");
1265
- hash_for = simple_hash("for");
1266
- hash_to = simple_hash("to");
1267
-/*
1268
- hash_redirects = simple_hash("redirects");
1269
-*/
1270
- }
1271
-
1272
- char person_guid[GUID_LEN + 1] = "";
1273
-
1274
- debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
1275
-
1276
- // FIXME
1277
- // The browser may send multiple cookies with our id
1278
-
1279
- char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
1280
- if(cookie)
1281
- strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
1282
-
1283
- char action = '\0';
1284
- char *machine_guid = NULL,
1285
- *machine_url = NULL,
1286
- *url_name = NULL,
1287
- *search_machine_guid = NULL,
1288
- *delete_url = NULL,
1289
- *to_person_guid = NULL;
1290
-/*
1291
- int redirects = 0;
1292
-*/
1293
-
1294
- while(url) {
1295
- char *value = mystrsep(&url, "?&");
1296
- if (!value || !*value) continue;
1297
-
1298
- char *name = mystrsep(&value, "=");
1299
- if (!name || !*name) continue;
1300
- if (!value || !*value) continue;
1301
-
1302
- debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
1303
-
1304
- uint32_t hash = simple_hash(name);
1305
-
1306
- if(hash == hash_action && !strcmp(name, "action")) {
1307
- uint32_t vhash = simple_hash(value);
1308
-
1309
- if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
1310
- else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
1311
- else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
1312
- else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
1313
- else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
1314
-#ifdef NETDATA_INTERNAL_CHECKS
1315
- else error("unknown registry action '%s'", value);
1316
-#endif /* NETDATA_INTERNAL_CHECKS */
1317
- }
1318
-/*
1319
- else if(hash == hash_redirects && !strcmp(name, "redirects"))
1320
- redirects = atoi(value);
1321
-*/
1322
- else if(hash == hash_machine && !strcmp(name, "machine"))
1323
- machine_guid = value;
1324
-
1325
- else if(hash == hash_url && !strcmp(name, "url"))
1326
- machine_url = value;
1327
-
1328
- else if(action == 'A') {
1329
- if(hash == hash_name && !strcmp(name, "name"))
1330
- url_name = value;
1331
- }
1332
- else if(action == 'D') {
1333
- if(hash == hash_delete_url && !strcmp(name, "delete_url"))
1334
- delete_url = value;
1335
- }
1336
- else if(action == 'S') {
1337
- if(hash == hash_for && !strcmp(name, "for"))
1338
- search_machine_guid = value;
1339
- }
1340
- else if(action == 'W') {
1341
- if(hash == hash_to && !strcmp(name, "to"))
1342
- to_person_guid = value;
1343
- }
1344
-#ifdef NETDATA_INTERNAL_CHECKS
1345
- else error("unused registry URL parameter '%s' with value '%s'", name, value);
1346
-#endif /* NETDATA_INTERNAL_CHECKS */
1347
- }
1348
-
1349
- if(respect_web_browser_do_not_track_policy && w->donottrack) {
558
+static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
559
+ if(unlikely(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) {
560
buffer_flush(w->response.data);
1351
- buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
561
+ buffer_strcat(w->response.data, "This host does not maintain a database");
562
return 400;
563
}
564
1355
- if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
1356
- error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
1357
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
1358
- buffer_flush(w->response.data);
1359
- buffer_strcat(w->response.data, "Invalid registry Access request.");
1360
- return 400;
1361
- }
1362
- else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
1363
- error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
1364
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
1365
- buffer_flush(w->response.data);
1366
- buffer_strcat(w->response.data, "Invalid registry Delete request.");
1367
- return 400;
1368
- }
1369
- else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
1370
- error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
1371
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
1372
- buffer_flush(w->response.data);
1373
- buffer_strcat(w->response.data, "Invalid registry Search request.");
1374
- return 400;
1375
- }
1376
- else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
1377
- error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
1378
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
1379
- buffer_flush(w->response.data);
1380
- buffer_strcat(w->response.data, "Invalid registry Switch request.");
1381
- return 400;
1382
- }
1383
-
1384
- switch(action) {
1385
- case 'A':
1386
- w->tracking_required = 1;
1387
- return registry_request_access_json(host, w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());
1388
-
1389
- case 'D':
1390
- w->tracking_required = 1;
1391
- return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());
1392
-
1393
- case 'S':
1394
- w->tracking_required = 1;
1395
- return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());
1396
-
1397
- case 'W':
1398
- w->tracking_required = 1;
1399
- return registry_request_switch_json(host, w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec());
1400
-
1401
- case 'H':
1402
- return registry_request_hello_json(host, w);
1403
-
1404
- default:
1405
- buffer_flush(w->response.data);
1406
- buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
1407
- return 400;
1408
- }
1409
-}
1410
-
1411
-int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {
1412
- static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0;
1413
-
1414
- if(unlikely(hash_data == 0)) {
1415
- hash_data = simple_hash("data");
1416
- hash_chart = simple_hash("chart");
1417
- hash_charts = simple_hash("charts");
1418
- hash_registry = simple_hash("registry");
1419
- hash_badge = simple_hash("badge.svg");
1420
- hash_alarms = simple_hash("alarms");
1421
- hash_alarm_log = simple_hash("alarm_log");
1422
- hash_alarm_variables = simple_hash("alarm_variables");
1423
- hash_raw = simple_hash("allmetrics");
1424
- }
1425
-
1426
- // get the command
1427
- char *tok = mystrsep(&url, "/?&");
1428
- if(tok && *tok) {
1429
- debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
1430
- uint32_t hash = simple_hash(tok);
1431
-
1432
- if(hash == hash_data && !strcmp(tok, "data"))
1433
- return web_client_api_request_v1_data(host, w, url);
1434
-
1435
- else if(hash == hash_chart && !strcmp(tok, "chart"))
1436
- return web_client_api_request_v1_chart(host, w, url);
1437
-
1438
- else if(hash == hash_charts && !strcmp(tok, "charts"))
1439
- return web_client_api_request_v1_charts(host, w, url);
1440
-
1441
- else if(hash == hash_registry && !strcmp(tok, "registry"))
1442
- return web_client_api_request_v1_registry(host, w, url);
1443
-
1444
- else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
1445
- return web_client_api_request_v1_badge(host, w, url);
1446
-
1447
- else if(hash == hash_alarms && !strcmp(tok, "alarms"))
1448
- return web_client_api_request_v1_alarms(host, w, url);
1449
-
1450
- else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
1451
- return web_client_api_request_v1_alarm_log(host, w, url);
1452
-
1453
- else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables"))
1454
- return web_client_api_request_v1_alarm_variables(host, w, url);
1455
-
1456
- else if(hash == hash_raw && !strcmp(tok, "allmetrics"))
1457
- return web_client_api_request_v1_allmetrics(host, w, url);
1458
-
1459
- else {
1460
- buffer_flush(w->response.data);
1461
- buffer_strcat(w->response.data, "Unsupported v1 API command: ");
1462
- buffer_strcat_htmlescape(w->response.data, tok);
1463
- return 404;
1464
- }
1465
- }
1466
- else {
1467
- buffer_flush(w->response.data);
1468
- buffer_sprintf(w->response.data, "Which API v1 command?");
1469
- return 400;
1470
- }
565
+ return func(host, w, url);
566
}
567
568
int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
@@ -1492,179 +587,6 @@ int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
587
}
588
}
589
1495
-int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type)
1496
-{
1497
- if(!url || !*url) {
1498
- buffer_flush(w->response.data);
1499
- buffer_sprintf(w->response.data, "Incomplete request.");
1500
- return 400;
1501
- }
1502
-
1503
- RRDSET *st = NULL;
1504
-
1505
- char *args = strchr(url, '?');
1506
- if(args) {
1507
- *args='\0';
1508
- args = &args[1];
1509
- }
1510
-
1511
- // get the name of the data to show
1512
- char *tok = mystrsep(&url, "/");
1513
- if(!tok) tok = "";
1514
-
1515
- // do we have such a data set?
1516
- if(*tok) {
1517
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
1518
- st = rrdset_find_byname(host, tok);
1519
- if(!st) st = rrdset_find(host, tok);
1520
- }
1521
-
1522
- if(!st) {
1523
- // we don't have it
1524
- // try to send a file with that name
1525
- buffer_flush(w->response.data);
1526
- return(mysendfile(w, tok));
1527
- }
1528
-
1529
- // we have it
1530
- debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
1531
-
1532
- // how many entries does the client want?
1533
- int lines = (int)st->entries;
1534
- int group_count = 1;
1535
- time_t after = 0, before = 0;
1536
- int group_method = GROUP_AVERAGE;
1537
- int nonzero = 0;
1538
-
1539
- if(url) {
1540
- // parse the lines required
1541
- tok = mystrsep(&url, "/");
1542
- if(tok) lines = str2i(tok);
1543
- if(lines < 1) lines = 1;
1544
- }
1545
- if(url) {
1546
- // parse the group count required
1547
- tok = mystrsep(&url, "/");
1548
- if(tok && *tok) group_count = str2i(tok);
1549
- if(group_count < 1) group_count = 1;
1550
- //if(group_count > save_history / 20) group_count = save_history / 20;
1551
- }
1552
- if(url) {
1553
- // parse the grouping method required
1554
- tok = mystrsep(&url, "/");
1555
- if(tok && *tok) {
1556
- if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
1557
- else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
1558
- else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
1559
- else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
1560
- }
1561
- }
1562
- if(url) {
1563
- // parse after time
1564
- tok = mystrsep(&url, "/");
1565
- if(tok && *tok) after = str2ul(tok);
1566
- if(after < 0) after = 0;
1567
- }
1568
- if(url) {
1569
- // parse before time
1570
- tok = mystrsep(&url, "/");
1571
- if(tok && *tok) before = str2ul(tok);
1572
- if(before < 0) before = 0;
1573
- }
1574
- if(url) {
1575
- // parse nonzero
1576
- tok = mystrsep(&url, "/");
1577
- if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
1578
- }
1579
-
1580
- w->response.data->contenttype = CT_APPLICATION_JSON;
1581
- buffer_flush(w->response.data);
1582
-
1583
- char *google_version = "0.6";
1584
- char *google_reqId = "0";
1585
- char *google_sig = "0";
1586
- char *google_out = "json";
1587
- char *google_responseHandler = "google.visualization.Query.setResponse";
1588
- char *google_outFileName = NULL;
1589
- time_t last_timestamp_in_data = 0;
1590
- if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
1591
-
1592
- w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
1593
-
1594
- while(args) {
1595
- tok = mystrsep(&args, "&");
1596
- if(tok && *tok) {
1597
- char *name = mystrsep(&tok, "=");
1598
- if(name && *name && strcmp(name, "tqx") == 0) {
1599
- char *key = mystrsep(&tok, ":");
1600
- char *value = mystrsep(&tok, ";");
1601
- if(key && value && *key && *value) {
1602
- if(strcmp(key, "version") == 0)
1603
- google_version = value;
1604
-
1605
- else if(strcmp(key, "reqId") == 0)
1606
- google_reqId = value;
1607
-
1608
- else if(strcmp(key, "sig") == 0)
1609
- google_sig = value;
1610
-
1611
- else if(strcmp(key, "out") == 0)
1612
- google_out = value;
1613
-
1614
- else if(strcmp(key, "responseHandler") == 0)
1615
- google_responseHandler = value;
1616
-
1617
- else if(strcmp(key, "outFileName") == 0)
1618
- google_outFileName = value;
1619
- }
1620
- }
1621
- }
1622
- }
1623
-
1624
- debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
1625
- w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
1626
- );
1627
-
1628
- if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
1629
- last_timestamp_in_data = strtoul(google_sig, NULL, 0);
1630
-
1631
- // check the client wants json
1632
- if(strcmp(google_out, "json") != 0) {
1633
- buffer_sprintf(w->response.data,
1634
- "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
1635
- google_responseHandler, google_version, google_reqId, google_out);
1636
- return 200;
1637
- }
1638
- }
1639
- }
1640
-
1641
- if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
1642
- buffer_sprintf(w->response.data,
1643
- "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
1644
- google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
1645
- }
1646
-
1647
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
1648
- w->id, st->name, st->id, lines, group_count, group_method, after, before);
1649
-
1650
- time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
1651
-
1652
- if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
1653
- if(timestamp_in_data > last_timestamp_in_data)
1654
- buffer_strcat(w->response.data, "});");
1655
-
1656
- else {
1657
- // the client already has the latest data
1658
- buffer_flush(w->response.data);
1659
- buffer_sprintf(w->response.data,
1660
- "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
1661
- google_responseHandler, google_version, google_reqId);
1662
- }
1663
- }
1664
-
1665
- return 200;
1666
-}
1667
-
590
const char *web_content_type_to_string(uint8_t contenttype) {
591
switch(contenttype) {
592
case CT_TEXT_HTML:
@@ -2160,78 +1082,39 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1082
uint32_t hash = simple_hash(tok);
1083
debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
1084
2163
- if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {
1085
+ if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API
1086
debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
2165
- return web_client_api_request(host, w, url);
1087
+ return check_host_and_call(host, w, url, web_client_api_request);
1088
}
2167
- else if(unlikely(hash == hash_host && strcmp(tok, "host") == 0)) {
1089
+ else if(unlikely(hash == hash_host && strcmp(tok, "host") == 0)) { // host switching
1090
debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
1091
return web_client_switch_host(host, w, url);
1092
}
2171
- else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) {
2172
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
2173
- w->response.data->contenttype = CT_TEXT_PLAIN;
2174
- buffer_flush(w->response.data);
2175
- config_generate(w->response.data, 0);
2176
- return 200;
1093
+ else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) { // old API "data"
1094
+ debug(D_WEB_CLIENT_ACCESS, "%llu: old API data request...", w->id);
1095
+ return check_host_and_call(host, w, url, web_client_api_old_data_request_json);
1096
}
2178
- else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) { // "data"
2179
- // the client is requesting rrd data -- OLD API
2180
- return web_client_api_old_data_request(host, w, url, DATASOURCE_JSON);
1097
+ else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // old API "datasource"
1098
+ debug(D_WEB_CLIENT_ACCESS, "%llu: old API datasource request...", w->id);
1099
+ return check_host_and_call(host, w, url, web_client_api_old_data_request_jsonp);
1100
}
2182
- else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // "datasource"
2183
- // the client is requesting google datasource -- OLD API
2184
- return web_client_api_old_data_request(host, w, url, DATASOURCE_DATATABLE_JSONP);
1101
+ else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) { // old API "graph"
1102
+ debug(D_WEB_CLIENT_ACCESS, "%llu: old API graph request...", w->id);
1103
+ return check_host_and_call(host, w, url, web_client_api_old_graph_request);
1104
}
2186
- else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) { // "graph"
2187
- // the client is requesting an rrd graph -- OLD API
2188
-
2189
- // get the name of the data to show
2190
- tok = mystrsep(&url, "/?&");
2191
- if(tok && *tok) {
2192
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
2193
-
2194
- // do we have such a data set?
2195
- RRDSET *st = rrdset_find_byname(host, tok);
2196
- if(!st) st = rrdset_find(host, tok);
2197
- if(!st) {
2198
- // we don't have it
2199
- // try to send a file with that name
2200
- buffer_flush(w->response.data);
2201
- return mysendfile(w, tok);
2202
- }
2203
-
2204
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
2205
- w->response.data->contenttype = CT_APPLICATION_JSON;
2206
- buffer_flush(w->response.data);
2207
- rrd_stats_graph_json(st, url, w->response.data);
2208
- return 200;
2209
- }
2210
-
2211
- buffer_flush(w->response.data);
2212
- buffer_strcat(w->response.data, "Graph name?\r\n");
2213
- return 400;
1105
+ else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) { // old API "list"
1106
+ debug(D_WEB_CLIENT_ACCESS, "%llu: old API list request...", w->id);
1107
+ return check_host_and_call(host, w, url, web_client_api_old_list_request);
1108
}
2215
- else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) {
2216
- // OLD API
2217
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
2218
-
2219
- buffer_flush(w->response.data);
2220
- RRDSET *st;
2221
-
2222
- rrdhost_rdlock(host);
2223
- rrdset_foreach_read(st, host) buffer_sprintf(w->response.data, "%s\n", st->name);
2224
- rrdhost_unlock(host);
2225
-
2226
- return 200;
1109
+ else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) { // old API "all.json"
1110
+ debug(D_WEB_CLIENT_ACCESS, "%llu: old API all.json request...", w->id);
1111
+ return check_host_and_call(host, w, url, web_client_api_old_all_json);
1112
}
2228
- else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) {
2229
- // OLD API
2230
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
2231
-
2232
- w->response.data->contenttype = CT_APPLICATION_JSON;
1113
+ else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf
1114
+ debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
1115
+ w->response.data->contenttype = CT_TEXT_PLAIN;
1116
buffer_flush(w->response.data);
2234
- rrd_stats_all_json(host, w->response.data);
1117
+ config_generate(w->response.data, 0);
1118
return 200;
1119
}
1120
#ifdef NETDATA_INTERNAL_CHECKS
src/web_client.h
+3
@@ -115,4 +115,7 @@ extern int web_client_api_request_v1_data_group(char *name, int def);
115
extern const char *group_method2string(int group);
116
117
extern void buffer_data_options2string(BUFFER *wb, uint32_t options);
118
+
119
+extern int mysendfile(struct web_client *w, char *filename);
120
+
121
#endif