@cryptotaxi247 / netdata-1 / commits / 1958a3455

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