]> arthur.barton.de Git - netdata.git/blob - src/rrd2json.c
remove all dots from prometheus names; #1497
[netdata.git] / src / rrd2json.c
1 #include "common.h"
2
3 void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used)
4 {
5     pthread_rwlock_rdlock(&st->rwlock);
6
7     buffer_sprintf(wb,
8         "\t\t{\n"
9         "\t\t\t\"id\": \"%s\",\n"
10         "\t\t\t\"name\": \"%s\",\n"
11         "\t\t\t\"type\": \"%s\",\n"
12         "\t\t\t\"family\": \"%s\",\n"
13         "\t\t\t\"context\": \"%s\",\n"
14         "\t\t\t\"title\": \"%s\",\n"
15         "\t\t\t\"priority\": %ld,\n"
16         "\t\t\t\"enabled\": %s,\n"
17         "\t\t\t\"units\": \"%s\",\n"
18         "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
19         "\t\t\t\"chart_type\": \"%s\",\n"
20         "\t\t\t\"duration\": %ld,\n"
21         "\t\t\t\"first_entry\": %ld,\n"
22         "\t\t\t\"last_entry\": %ld,\n"
23         "\t\t\t\"update_every\": %d,\n"
24         "\t\t\t\"dimensions\": {\n"
25         , st->id
26         , st->name
27         , st->type
28         , st->family
29         , st->context
30         , st->title
31         , st->priority
32         , st->enabled?"true":"false"
33         , st->units
34         , st->name
35         , rrdset_type_name(st->chart_type)
36         , st->entries * st->update_every
37         , rrdset_first_entry_t(st)
38         , rrdset_last_entry_t(st)
39         , st->update_every
40         );
41
42     unsigned long memory = st->memsize;
43
44     size_t dimensions = 0;
45     RRDDIM *rd;
46     for(rd = st->dimensions; rd ; rd = rd->next) {
47         if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
48
49         memory += rd->memsize;
50
51         buffer_sprintf(wb,
52             "%s"
53             "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
54             , dimensions?",\n":""
55             , rd->id
56             , rd->name
57             );
58
59         dimensions++;
60     }
61
62     if(dimensions_count) *dimensions_count += dimensions;
63     if(memory_used) *memory_used += memory;
64
65     buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": ");
66     buffer_rrd_value(wb, st->green);
67     buffer_strcat(wb, ",\n\t\t\t\"red\": ");
68     buffer_rrd_value(wb, st->red);
69
70     buffer_sprintf(wb,
71         "\n\t\t}"
72         );
73
74     pthread_rwlock_unlock(&st->rwlock);
75 }
76
77 void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
78     rrd_stats_api_v1_chart_with_data(st, wb, NULL, NULL);
79 }
80
81 void rrd_stats_api_v1_charts(BUFFER *wb)
82 {
83     size_t c, dimensions = 0, memory = 0, alarms = 0;
84     RRDSET *st;
85
86     buffer_sprintf(wb, "{\n"
87            "\t\"hostname\": \"%s\""
88         ",\n\t\"update_every\": %d"
89         ",\n\t\"history\": %d"
90         ",\n\t\"charts\": {"
91         , localhost.hostname
92         , rrd_update_every
93         , rrd_default_history_entries
94         );
95
96     pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
97     for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
98         if(st->enabled && st->dimensions) {
99             if(c) buffer_strcat(wb, ",");
100             buffer_strcat(wb, "\n\t\t\"");
101             buffer_strcat(wb, st->id);
102             buffer_strcat(wb, "\": ");
103             rrd_stats_api_v1_chart_with_data(st, wb, &dimensions, &memory);
104             c++;
105         }
106     }
107
108     RRDCALC *rc;
109     for(rc = localhost.alarms; rc ; rc = rc->next) {
110         alarms++;
111     }
112     pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
113
114     buffer_sprintf(wb, "\n\t}"
115                     ",\n\t\"charts_count\": %zu"
116                     ",\n\t\"dimensions_count\": %zu"
117                     ",\n\t\"alarms_count\": %zu"
118                     ",\n\t\"rrd_memory_bytes\": %zu"
119                     "\n}\n"
120                    , c
121                    , dimensions
122                    , alarms
123                    , memory
124     );
125 }
126
127 static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) {
128     size_t n;
129
130     for(n = 0; *s && n < usable ; d++, s++, n++) {
131         register char c = *s;
132
133         if(unlikely(!isalnum(c))) *d = '_';
134         else *d = c;
135     }
136     *d = '\0';
137
138     return n;
139 }
140
141 #define PROMETHEUS_ELEMENT_MAX 256
142
143 void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb)
144 {
145     pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
146
147     char host[PROMETHEUS_ELEMENT_MAX + 1];
148     prometheus_name_copy(host, config_get("global", "hostname", "localhost"), PROMETHEUS_ELEMENT_MAX);
149
150     // for each chart
151     RRDSET *st;
152     for(st = localhost.rrdset_root; st ; st = st->next) {
153         char chart[PROMETHEUS_ELEMENT_MAX + 1];
154         prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX);
155
156         buffer_strcat(wb, "\n");
157         if(st->enabled && st->dimensions) {
158             // for each dimension
159             RRDDIM *rd;
160             for(rd = st->dimensions; rd ; rd = rd->next) {
161                 if(rd->counter) {
162
163                     char dimension[PROMETHEUS_ELEMENT_MAX + 1];
164                     prometheus_name_copy(dimension, rd->id, PROMETHEUS_ELEMENT_MAX);
165
166                     // buffer_sprintf(wb, "# HELP %s.%s %s\n", st->id, rd->id, st->units);
167
168                     switch(rd->algorithm) {
169                         case RRDDIM_INCREMENTAL:
170                         case RRDDIM_PCENT_OVER_DIFF_TOTAL:
171                             buffer_sprintf(wb, "# TYPE %s_%s counter\n", chart, dimension);
172                             break;
173
174                         default:
175                             buffer_sprintf(wb, "# TYPE %s_%s gauge\n", chart, dimension);
176                             break;
177                     }
178
179                     // calculated_number n = (calculated_number)rd->last_collected_value * (calculated_number)(abs(rd->multiplier)) / (calculated_number)(abs(rd->divisor));
180                     // buffer_sprintf(wb, "%s.%s " CALCULATED_NUMBER_FORMAT " %llu\n", st->id, rd->id, n,
181                     //        (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
182
183                     buffer_sprintf(wb, "%s_%s{instance=\"%s\"} " COLLECTED_NUMBER_FORMAT " %llu\n",
184                             chart, dimension, host, rd->last_collected_value,
185                             (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
186                 }
187             }
188         }
189     }
190
191     pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
192 }
193
194 unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
195 {
196     time_t now = now_realtime_sec();
197
198     pthread_rwlock_rdlock(&st->rwlock);
199
200     buffer_sprintf(wb,
201         "\t\t{\n"
202         "\t\t\t\"id\": \"%s\",\n"
203         "\t\t\t\"name\": \"%s\",\n"
204         "\t\t\t\"type\": \"%s\",\n"
205         "\t\t\t\"family\": \"%s\",\n"
206         "\t\t\t\"context\": \"%s\",\n"
207         "\t\t\t\"title\": \"%s\",\n"
208         "\t\t\t\"priority\": %ld,\n"
209         "\t\t\t\"enabled\": %d,\n"
210         "\t\t\t\"units\": \"%s\",\n"
211         "\t\t\t\"url\": \"/data/%s/%s\",\n"
212         "\t\t\t\"chart_type\": \"%s\",\n"
213         "\t\t\t\"counter\": %lu,\n"
214         "\t\t\t\"entries\": %ld,\n"
215         "\t\t\t\"first_entry_t\": %ld,\n"
216         "\t\t\t\"last_entry\": %lu,\n"
217         "\t\t\t\"last_entry_t\": %ld,\n"
218         "\t\t\t\"last_entry_secs_ago\": %ld,\n"
219         "\t\t\t\"update_every\": %d,\n"
220         "\t\t\t\"isdetail\": %d,\n"
221         "\t\t\t\"usec_since_last_update\": %llu,\n"
222         "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
223         "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
224         "\t\t\t\"dimensions\": [\n"
225         , st->id
226         , st->name
227         , st->type
228         , st->family
229         , st->context
230         , st->title
231         , st->priority
232         , st->enabled
233         , st->units
234         , st->name, options?options:""
235         , rrdset_type_name(st->chart_type)
236         , st->counter
237         , st->entries
238         , rrdset_first_entry_t(st)
239         , rrdset_last_slot(st)
240         , rrdset_last_entry_t(st)
241         , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
242         , st->update_every
243         , st->isdetail
244         , st->usec_since_last_update
245         , st->collected_total
246         , st->last_collected_total
247         );
248
249     unsigned long memory = st->memsize;
250
251     RRDDIM *rd;
252     for(rd = st->dimensions; rd ; rd = rd->next) {
253
254         memory += rd->memsize;
255
256         buffer_sprintf(wb,
257             "\t\t\t\t{\n"
258             "\t\t\t\t\t\"id\": \"%s\",\n"
259             "\t\t\t\t\t\"name\": \"%s\",\n"
260             "\t\t\t\t\t\"entries\": %ld,\n"
261             "\t\t\t\t\t\"isHidden\": %d,\n"
262             "\t\t\t\t\t\"algorithm\": \"%s\",\n"
263             "\t\t\t\t\t\"multiplier\": %ld,\n"
264             "\t\t\t\t\t\"divisor\": %ld,\n"
265             "\t\t\t\t\t\"last_entry_t\": %ld,\n"
266             "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
267             "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
268             "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
269             "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
270             "\t\t\t\t\t\"memory\": %lu\n"
271             "\t\t\t\t}%s\n"
272             , rd->id
273             , rd->name
274             , rd->entries
275             , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
276             , rrddim_algorithm_name(rd->algorithm)
277             , rd->multiplier
278             , rd->divisor
279             , rd->last_collected_time.tv_sec
280             , rd->collected_value
281             , rd->calculated_value
282             , rd->last_collected_value
283             , rd->last_calculated_value
284             , rd->memsize
285             , rd->next?",":""
286             );
287     }
288
289     buffer_sprintf(wb,
290         "\t\t\t],\n"
291         "\t\t\t\"memory\" : %lu\n"
292         "\t\t}"
293         , memory
294         );
295
296     pthread_rwlock_unlock(&st->rwlock);
297     return memory;
298 }
299
300 #define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n"
301 #define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n"
302
303 void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb)
304 {
305     buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
306     rrd_stats_one_json(st, options, wb);
307     buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
308 }
309
310 void rrd_stats_all_json(BUFFER *wb)
311 {
312     unsigned long memory = 0;
313     long c;
314     RRDSET *st;
315
316     buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
317
318     pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
319     for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
320         if(st->enabled && st->dimensions) {
321             if(c) buffer_strcat(wb, ",\n");
322             memory += rrd_stats_one_json(st, NULL, wb);
323             c++;
324         }
325     }
326     pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
327
328     buffer_sprintf(wb, "\n\t],\n"
329         "\t\"hostname\": \"%s\",\n"
330         "\t\"update_every\": %d,\n"
331         "\t\"history\": %d,\n"
332         "\t\"memory\": %lu\n"
333         "}\n"
334         , localhost.hostname
335         , rrd_update_every
336         , rrd_default_history_entries
337         , memory
338         );
339 }
340
341
342
343 // ----------------------------------------------------------------------------
344
345 // RRDR dimension options
346 #define RRDR_EMPTY      0x01 // the dimension contains / the value is empty (null)
347 #define RRDR_RESET      0x02 // the dimension contains / the value is reset
348 #define RRDR_HIDDEN     0x04 // the dimension contains / the value is hidden
349 #define RRDR_NONZERO    0x08 // the dimension contains / the value is non-zero
350
351 // RRDR result options
352 #define RRDR_RESULT_OPTION_ABSOLUTE 0x00000001
353 #define RRDR_RESULT_OPTION_RELATIVE 0x00000002
354
355 typedef struct rrdresult {
356     RRDSET *st;         // the chart this result refers to
357
358     uint32_t result_options;    // RRDR_RESULT_OPTION_*
359
360     int d;                  // the number of dimensions
361     long n;                 // the number of values in the arrays
362     long rows;              // the number of rows used
363
364     uint8_t *od;            // the options for the dimensions
365
366     time_t *t;              // array of n timestamps
367     calculated_number *v;   // array n x d values
368     uint8_t *o;             // array n x d options
369
370     long c;                 // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
371
372     long group;             // how many collected values were grouped for each row
373     int update_every;       // what is the suggested update frequency in seconds
374
375     calculated_number min;
376     calculated_number max;
377
378     time_t before;
379     time_t after;
380
381     int has_st_lock;        // if st is read locked by us
382 } RRDR;
383
384 #define rrdr_rows(r) ((r)->rows)
385
386 /*
387 static void rrdr_dump(RRDR *r)
388 {
389     long c, i;
390     RRDDIM *d;
391
392     fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
393
394     for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
395         fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
396                 , d->id
397                 , d->name
398                 , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
399                 , (r->od[c] & RRDR_RESET)?"RESET ":""
400                 , (r->od[c] & RRDR_HIDDEN)?"HIDDEN ":""
401                 , (r->od[c] & RRDR_NONZERO)?"NONZERO ":""
402                 );
403     }
404
405     if(r->rows <= 0) {
406         fprintf(stderr, "RRDR does not have any values in it.\n");
407         return;
408     }
409
410     fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
411
412     // for each line in the array
413     for(i = 0; i < r->rows ;i++) {
414         calculated_number *cn = &r->v[ i * r->d ];
415         uint8_t *co = &r->o[ i * r->d ];
416
417         // print the id and the timestamp of the line
418         fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
419
420         // for each dimension
421         for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
422             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
423             if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
424
425             if(co[c] & RRDR_EMPTY)
426                 fprintf(stderr, "null ");
427             else
428                 fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
429                     , cn[c]
430                     , (co[c] & RRDR_EMPTY)?"E":" "
431                     , (co[c] & RRDR_RESET)?"R":" "
432                     , (co[c] & RRDR_HIDDEN)?"H":" "
433                     , (co[c] & RRDR_NONZERO)?"N":" "
434                     );
435         }
436
437         fprintf(stderr, "\n");
438     }
439 }
440 */
441
442 void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
443 {
444     char b[strlen(dims) + 1];
445     char *o = b, *tok;
446     strcpy(o, dims);
447
448     long c;
449     RRDDIM *d;
450
451     // disable all of them
452     for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
453         r->od[c] |= RRDR_HIDDEN;
454
455     while(o && *o && (tok = mystrsep(&o, ",|"))) {
456         if(!*tok) continue;
457         
458         uint32_t hash = simple_hash(tok);
459
460         // find it and enable it
461         for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
462             if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || !strcmp(d->name, tok))) {
463                 r->od[c] &= ~RRDR_HIDDEN;
464
465                 // since the user needs this dimension
466                 // make it appear as NONZERO, to return it
467                 // even if the dimension has only zeros
468                 r->od[c] |= RRDR_NONZERO;
469             }
470         }
471     }
472 }
473
474 void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
475 {
476     switch(format) {
477     case DATASOURCE_JSON:
478         buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
479         break;
480
481     case DATASOURCE_DATATABLE_JSON:
482         buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
483         break;
484
485     case DATASOURCE_DATATABLE_JSONP:
486         buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
487         break;
488
489     case DATASOURCE_JSONP:
490         buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
491         break;
492
493     case DATASOURCE_SSV:
494         buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
495         break;
496
497     case DATASOURCE_CSV:
498         buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
499         break;
500
501     case DATASOURCE_TSV:
502         buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
503         break;
504
505     case DATASOURCE_HTML:
506         buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
507         break;
508
509     case DATASOURCE_JS_ARRAY:
510         buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
511         break;
512
513     case DATASOURCE_SSV_COMMA:
514         buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
515         break;
516
517     default:
518         buffer_strcat(wb, "unknown");
519         break;
520     }
521 }
522
523 uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
524 {
525     if(options & RRDR_OPTION_NONZERO) {
526         long i;
527
528         if(dims && *dims) {
529             // the caller wants specific dimensions
530             // disable NONZERO option
531             // to make sure we don't accidentally prevent
532             // the specific dimensions from being returned
533             i = 0;
534         }
535         else {
536             // find how many dimensions are not zero
537             long c;
538             RRDDIM *rd;
539             for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ; c++, rd = rd->next) {
540                 if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
541                 if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
542                 i++;
543             }
544         }
545
546         // if with nonzero we get i = 0 (no dimensions will be returned)
547         // disable nonzero to show all dimensions
548         if(!i) options &= ~RRDR_OPTION_NONZERO;
549     }
550
551     return options;
552 }
553
554 void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
555 {
556     long rows = rrdr_rows(r);
557     long c, i;
558     RRDDIM *rd;
559
560     //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
561     char kq[2] = "",                    // key quote
562         sq[2] = "";                     // string quote
563
564     if( options & RRDR_OPTION_GOOGLE_JSON ) {
565         kq[0] = '\0';
566         sq[0] = '\'';
567     }
568     else {
569         kq[0] = '"';
570         sq[0] = '"';
571     }
572
573     buffer_sprintf(wb, "{\n"
574             "   %sapi%s: 1,\n"
575             "   %sid%s: %s%s%s,\n"
576             "   %sname%s: %s%s%s,\n"
577             "   %sview_update_every%s: %d,\n"
578             "   %supdate_every%s: %d,\n"
579             "   %sfirst_entry%s: %u,\n"
580             "   %slast_entry%s: %u,\n"
581             "   %sbefore%s: %u,\n"
582             "   %safter%s: %u,\n"
583             "   %sdimension_names%s: ["
584             , kq, kq
585             , kq, kq, sq, r->st->id, sq
586             , kq, kq, sq, r->st->name, sq
587             , kq, kq, r->update_every
588             , kq, kq, r->st->update_every
589             , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
590             , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
591             , kq, kq, (uint32_t)r->before
592             , kq, kq, (uint32_t)r->after
593             , kq, kq);
594
595     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
596         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
597         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
598
599         if(i) buffer_strcat(wb, ", ");
600         buffer_strcat(wb, sq);
601         buffer_strcat(wb, rd->name);
602         buffer_strcat(wb, sq);
603         i++;
604     }
605     if(!i) {
606 #ifdef NETDATA_INTERNAL_CHECKS
607         error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
608 #endif
609         rows = 0;
610         buffer_strcat(wb, sq);
611         buffer_strcat(wb, "no data");
612         buffer_strcat(wb, sq);
613     }
614
615     buffer_sprintf(wb, "],\n"
616             "   %sdimension_ids%s: ["
617             , kq, kq);
618
619     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
620         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
621         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
622
623         if(i) buffer_strcat(wb, ", ");
624         buffer_strcat(wb, sq);
625         buffer_strcat(wb, rd->id);
626         buffer_strcat(wb, sq);
627         i++;
628     }
629     if(!i) {
630         rows = 0;
631         buffer_strcat(wb, sq);
632         buffer_strcat(wb, "no data");
633         buffer_strcat(wb, sq);
634     }
635
636     buffer_sprintf(wb, "],\n"
637             "   %slatest_values%s: ["
638             , kq, kq);
639
640     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
641         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
642         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
643
644         if(i) buffer_strcat(wb, ", ");
645         i++;
646
647         storage_number n = rd->values[rrdset_last_slot(r->st)];
648
649         if(!does_storage_number_exist(n))
650             buffer_strcat(wb, "null");
651         else
652             buffer_rrd_value(wb, unpack_storage_number(n));
653     }
654     if(!i) {
655         rows = 0;
656         buffer_strcat(wb, "null");
657     }
658
659     buffer_sprintf(wb, "],\n"
660             "   %sview_latest_values%s: ["
661             , kq, kq);
662
663     i = 0;
664     if(rows) {
665         for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
666             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
667             if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
668
669             if(i) buffer_strcat(wb, ", ");
670             i++;
671
672             calculated_number *cn = &r->v[ (0) * r->d ];
673             uint8_t *co = &r->o[ (0) * r->d ];
674
675             if(co[c] & RRDR_EMPTY)
676                 buffer_strcat(wb, "null");
677             else
678                 buffer_rrd_value(wb, cn[c]);
679         }
680     }
681     if(!i) {
682         rows = 0;
683         buffer_strcat(wb, "null");
684     }
685
686     buffer_sprintf(wb, "],\n"
687             "   %sdimensions%s: %ld,\n"
688             "   %spoints%s: %ld,\n"
689             "   %sformat%s: %s"
690             , kq, kq, i
691             , kq, kq, rows
692             , kq, kq, sq
693             );
694
695     rrdr_buffer_print_format(wb, format);
696
697     buffer_sprintf(wb, "%s,\n"
698             "   %sresult%s: "
699             , sq
700             , kq, kq
701             );
702
703     if(string_value) buffer_strcat(wb, sq);
704     //info("JSONWRAPPER(): %s: END", r->st->id);
705 }
706
707 void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
708 {
709     (void)format;
710
711     char kq[2] = "",                    // key quote
712         sq[2] = "";                     // string quote
713
714     if( options & RRDR_OPTION_GOOGLE_JSON ) {
715         kq[0] = '\0';
716         sq[0] = '\'';
717     }
718     else {
719         kq[0] = '"';
720         sq[0] = '"';
721     }
722
723     if(string_value) buffer_strcat(wb, sq);
724
725     buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
726     buffer_rrd_value(wb, r->min);
727     buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
728     buffer_rrd_value(wb, r->max);
729     buffer_strcat(wb, "\n}\n");
730 }
731
732 #define JSON_DATES_JS 1
733 #define JSON_DATES_TIMESTAMP 2
734
735 static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
736 {
737     //info("RRD2JSON(): %s: BEGIN", r->st->id);
738     int row_annotations = 0, dates, dates_with_new = 0;
739     char kq[2] = "",                    // key quote
740         sq[2] = "",                     // string quote
741         pre_label[101] = "",            // before each label
742         post_label[101] = "",           // after each label
743         pre_date[101] = "",             // the beginning of line, to the date
744         post_date[101] = "",            // closing the date
745         pre_value[101] = "",            // before each value
746         post_value[101] = "",           // after each value
747         post_line[101] = "",            // at the end of each row
748         normal_annotation[201] = "",    // default row annotation
749         overflow_annotation[201] = "",  // overflow row annotation
750         data_begin[101] = "",           // between labels and values
751         finish[101] = "";               // at the end of everything
752
753     if(datatable) {
754         dates = JSON_DATES_JS;
755         if( options & RRDR_OPTION_GOOGLE_JSON ) {
756             kq[0] = '\0';
757             sq[0] = '\'';
758         }
759         else {
760             kq[0] = '"';
761             sq[0] = '"';
762         }
763         row_annotations = 1;
764         snprintfz(pre_date,   100, "        {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
765         snprintfz(post_date,  100, "%s}", sq);
766         snprintfz(pre_label,  100, ",\n     {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
767         snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
768         snprintfz(pre_value,  100, ",{%sv%s:", kq, kq);
769         strcpy(post_value,         "}");
770         strcpy(post_line,          "]}");
771         snprintfz(data_begin, 100, "\n  ],\n    %srows%s:\n [\n", kq, kq);
772         strcpy(finish,             "\n  ]\n}");
773
774         snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
775         snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
776
777         buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
778         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
779         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
780         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
781
782         // remove the valueobjects flag
783         // google wants its own keys
784         if(options & RRDR_OPTION_OBJECTSROWS)
785             options &= ~RRDR_OPTION_OBJECTSROWS;
786     }
787     else {
788         kq[0] = '"';
789         sq[0] = '"';
790         if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
791             dates = JSON_DATES_TIMESTAMP;
792             dates_with_new = 0;
793         }
794         else {
795             dates = JSON_DATES_JS;
796             dates_with_new = 1;
797         }
798         if( options & RRDR_OPTION_OBJECTSROWS )
799             strcpy(pre_date, "      { ");
800         else
801             strcpy(pre_date, "      [ ");
802         strcpy(pre_label,  ", \"");
803         strcpy(post_label, "\"");
804         strcpy(pre_value,  ", ");
805         if( options & RRDR_OPTION_OBJECTSROWS )
806             strcpy(post_line, "}");
807         else
808             strcpy(post_line, "]");
809         snprintfz(data_begin, 100, "],\n    %sdata%s:\n [\n", kq, kq);
810         strcpy(finish,             "\n  ]\n}");
811
812         buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
813         buffer_sprintf(wb, "%stime%s", sq, sq);
814     }
815
816     // -------------------------------------------------------------------------
817     // print the JSON header
818
819     long c, i;
820     RRDDIM *rd;
821
822     // print the header lines
823     for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
824         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
825         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
826
827         buffer_strcat(wb, pre_label);
828         buffer_strcat(wb, rd->name);
829         buffer_strcat(wb, post_label);
830         i++;
831     }
832     if(!i) {
833         buffer_strcat(wb, pre_label);
834         buffer_strcat(wb, "no data");
835         buffer_strcat(wb, post_label);
836     }
837
838     // print the begin of row data
839     buffer_strcat(wb, data_begin);
840
841     // if all dimensions are hidden, print a null
842     if(!i) {
843         buffer_strcat(wb, finish);
844         return;
845     }
846
847     long start = 0, end = rrdr_rows(r), step = 1;
848     if((options & RRDR_OPTION_REVERSED)) {
849         start = rrdr_rows(r) - 1;
850         end = -1;
851         step = -1;
852     }
853
854     // for each line in the array
855     calculated_number total = 1;
856     for(i = start; i != end ;i += step) {
857         calculated_number *cn = &r->v[ i * r->d ];
858         uint8_t *co = &r->o[ i * r->d ];
859
860         time_t now = r->t[i];
861
862         if(dates == JSON_DATES_JS) {
863             // generate the local date time
864             struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
865             if(!tm) { error("localtime_r() failed."); continue; }
866
867             if(likely(i != start)) buffer_strcat(wb, ",\n");
868             buffer_strcat(wb, pre_date);
869
870             if( options & RRDR_OPTION_OBJECTSROWS )
871                 buffer_sprintf(wb, "%stime%s: ", kq, kq);
872
873             if(dates_with_new)
874                 buffer_strcat(wb, "new ");
875
876             buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
877
878             buffer_strcat(wb, post_date);
879
880             if(row_annotations) {
881                 // google supports one annotation per row
882                 int annotation_found = 0;
883                 for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
884                     if(co[c] & RRDR_RESET) {
885                         buffer_strcat(wb, overflow_annotation);
886                         annotation_found = 1;
887                         break;
888                     }
889                 }
890                 if(!annotation_found)
891                     buffer_strcat(wb, normal_annotation);
892             }
893         }
894         else {
895             // print the timestamp of the line
896             if(likely(i != start)) buffer_strcat(wb, ",\n");
897             buffer_strcat(wb, pre_date);
898
899             if( options & RRDR_OPTION_OBJECTSROWS )
900                 buffer_sprintf(wb, "%stime%s: ", kq, kq);
901
902             buffer_rrd_value(wb, (calculated_number)r->t[i]);
903             // in ms
904             if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
905
906             buffer_strcat(wb, post_date);
907         }
908
909         if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
910             total = 0;
911             for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
912                 calculated_number n = cn[c];
913
914                 if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
915                     n = -n;
916
917                 total += n;
918             }
919             // prevent a division by zero
920             if(total == 0) total = 1;
921         }
922
923         // for each dimension
924         for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
925             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
926             if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
927
928             calculated_number n = cn[c];
929
930             buffer_strcat(wb, pre_value);
931
932             if( options & RRDR_OPTION_OBJECTSROWS )
933                 buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
934
935             if(co[c] & RRDR_EMPTY) {
936                 if(options & RRDR_OPTION_NULL2ZERO)
937                     buffer_strcat(wb, "0");
938                 else
939                     buffer_strcat(wb, "null");
940             }
941             else {
942                 if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
943                     n = -n;
944
945                 if(unlikely(options & RRDR_OPTION_PERCENTAGE))
946                     n = n * 100 / total;
947
948                 buffer_rrd_value(wb, n);
949             }
950
951             buffer_strcat(wb, post_value);
952         }
953
954         buffer_strcat(wb, post_line);
955     }
956
957     buffer_strcat(wb, finish);
958     //info("RRD2JSON(): %s: END", r->st->id);
959 }
960
961 static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines)
962 {
963     //info("RRD2CSV(): %s: BEGIN", r->st->id);
964     long c, i;
965     RRDDIM *d;
966
967     // print the csv header
968     for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
969         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
970         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
971
972         if(!i) {
973             buffer_strcat(wb, startline);
974             if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
975             buffer_strcat(wb, "time");
976             if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
977         }
978         buffer_strcat(wb, separator);
979         if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
980         buffer_strcat(wb, d->name);
981         if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
982         i++;
983     }
984     buffer_strcat(wb, endline);
985
986     if(!i) {
987         // no dimensions present
988         return;
989     }
990
991     long start = 0, end = rrdr_rows(r), step = 1;
992     if((options & RRDR_OPTION_REVERSED)) {
993         start = rrdr_rows(r) - 1;
994         end = -1;
995         step = -1;
996     }
997
998     // for each line in the array
999     calculated_number total = 1;
1000     for(i = start; i != end ;i += step) {
1001         calculated_number *cn = &r->v[ i * r->d ];
1002         uint8_t *co = &r->o[ i * r->d ];
1003
1004         buffer_strcat(wb, betweenlines);
1005         buffer_strcat(wb, startline);
1006
1007         time_t now = r->t[i];
1008
1009         if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
1010             // print the timestamp of the line
1011             buffer_rrd_value(wb, (calculated_number)now);
1012             // in ms
1013             if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
1014         }
1015         else {
1016             // generate the local date time
1017             struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
1018             if(!tm) { error("localtime() failed."); continue; }
1019             buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
1020         }
1021
1022         if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
1023             total = 0;
1024             for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1025                 calculated_number n = cn[c];
1026
1027                 if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1028                     n = -n;
1029
1030                 total += n;
1031             }
1032             // prevent a division by zero
1033             if(total == 0) total = 1;
1034         }
1035
1036         // for each dimension
1037         for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1038             if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
1039             if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
1040
1041             buffer_strcat(wb, separator);
1042
1043             calculated_number n = cn[c];
1044
1045             if(co[c] & RRDR_EMPTY) {
1046                 if(options & RRDR_OPTION_NULL2ZERO)
1047                     buffer_strcat(wb, "0");
1048                 else
1049                     buffer_strcat(wb, "null");
1050             }
1051             else {
1052                 if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1053                     n = -n;
1054
1055                 if(unlikely(options & RRDR_OPTION_PERCENTAGE))
1056                     n = n * 100 / total;
1057
1058                 buffer_rrd_value(wb, n);
1059             }
1060         }
1061
1062         buffer_strcat(wb, endline);
1063     }
1064     //info("RRD2CSV(): %s: END", r->st->id);
1065 }
1066
1067 inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) {
1068     long c;
1069     RRDDIM *d;
1070
1071     calculated_number *cn = &r->v[ i * r->d ];
1072     uint8_t *co = &r->o[ i * r->d ];
1073
1074     calculated_number sum = 0, min = 0, max = 0, v;
1075     int all_null = 1, init = 1;
1076
1077     calculated_number total = 1;
1078     if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
1079         total = 0;
1080         for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1081             calculated_number n = cn[c];
1082
1083             if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1084                 n = -n;
1085
1086             total += n;
1087         }
1088         // prevent a division by zero
1089         if(total == 0) total = 1;
1090     }
1091
1092     // for each dimension
1093     for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1094         if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
1095         if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
1096
1097         calculated_number n = cn[c];
1098
1099         if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1100             n = -n;
1101
1102         if(unlikely(options & RRDR_OPTION_PERCENTAGE))
1103             n = n * 100 / total;
1104
1105         if(unlikely(init)) {
1106             if(n > 0) {
1107                 min = 0;
1108                 max = n;
1109             }
1110             else {
1111                 min = n;
1112                 max = 0;
1113             }
1114             init = 0;
1115         }
1116
1117         if(likely(!(co[c] & RRDR_EMPTY))) {
1118             all_null = 0;
1119             sum += n;
1120         }
1121
1122         if(n < min) min = n;
1123         if(n > max) max = n;
1124     }
1125
1126     if(unlikely(all_null)) {
1127         if(likely(all_values_are_null))
1128             *all_values_are_null = 1;
1129         return 0;
1130     }
1131     else {
1132         if(likely(all_values_are_null))
1133             *all_values_are_null = 0;
1134     }
1135
1136     if(options & RRDR_OPTION_MIN2MAX)
1137         v = max - min;
1138     else
1139         v = sum;
1140
1141     return v;
1142 }
1143
1144 static void rrdr2ssv(RRDR *r, BUFFER *wb, uint32_t options, const char *prefix, const char *separator, const char *suffix)
1145 {
1146     //info("RRD2SSV(): %s: BEGIN", r->st->id);
1147     long i;
1148
1149     buffer_strcat(wb, prefix);
1150     long start = 0, end = rrdr_rows(r), step = 1;
1151     if((options & RRDR_OPTION_REVERSED)) {
1152         start = rrdr_rows(r) - 1;
1153         end = -1;
1154         step = -1;
1155     }
1156
1157     // for each line in the array
1158     for(i = start; i != end ;i += step) {
1159         int all_values_are_null = 0;
1160         calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
1161
1162         if(likely(i != start)) {
1163             if(r->min > v) r->min = v;
1164             if(r->max < v) r->max = v;
1165         }
1166         else {
1167             r->min = v;
1168             r->max = v;
1169         }
1170
1171         if(likely(i != start))
1172             buffer_strcat(wb, separator);
1173
1174         if(all_values_are_null) {
1175             if(options & RRDR_OPTION_NULL2ZERO)
1176                 buffer_strcat(wb, "0");
1177             else
1178                 buffer_strcat(wb, "null");
1179         }
1180         else
1181             buffer_rrd_value(wb, v);
1182     }
1183     buffer_strcat(wb, suffix);
1184     //info("RRD2SSV(): %s: END", r->st->id);
1185 }
1186
1187 inline static calculated_number *rrdr_line_values(RRDR *r)
1188 {
1189     return &r->v[ r->c * r->d ];
1190 }
1191
1192 inline static uint8_t *rrdr_line_options(RRDR *r)
1193 {
1194     return &r->o[ r->c * r->d ];
1195 }
1196
1197 inline static int rrdr_line_init(RRDR *r, time_t t)
1198 {
1199     r->c++;
1200
1201     if(unlikely(r->c >= r->n)) {
1202         error("requested to step above RRDR size for chart %s", r->st->name);
1203         r->c = r->n - 1;
1204     }
1205
1206     // save the time
1207     r->t[r->c] = t;
1208
1209     return 1;
1210 }
1211
1212 inline static void rrdr_lock_rrdset(RRDR *r) {
1213     if(unlikely(!r)) {
1214         error("NULL value given!");
1215         return;
1216     }
1217
1218     pthread_rwlock_rdlock(&r->st->rwlock);
1219     r->has_st_lock = 1;
1220 }
1221
1222 inline static void rrdr_unlock_rrdset(RRDR *r) {
1223     if(unlikely(!r)) {
1224         error("NULL value given!");
1225         return;
1226     }
1227
1228     if(likely(r->has_st_lock)) {
1229         pthread_rwlock_unlock(&r->st->rwlock);
1230         r->has_st_lock = 0;
1231     }
1232 }
1233
1234 inline static void rrdr_free(RRDR *r)
1235 {
1236     if(unlikely(!r)) {
1237         error("NULL value given!");
1238         return;
1239     }
1240
1241     rrdr_unlock_rrdset(r);
1242     freez(r->t);
1243     freez(r->v);
1244     freez(r->o);
1245     freez(r->od);
1246     freez(r);
1247 }
1248
1249 static inline void rrdr_done(RRDR *r)
1250 {
1251     r->rows = r->c + 1;
1252     r->c = 0;
1253 }
1254
1255 static RRDR *rrdr_create(RRDSET *st, long n)
1256 {
1257     if(unlikely(!st)) {
1258         error("NULL value given!");
1259         return NULL;
1260     }
1261
1262     RRDR *r = callocz(1, sizeof(RRDR));
1263     r->st = st;
1264
1265     rrdr_lock_rrdset(r);
1266
1267     RRDDIM *rd;
1268     for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
1269
1270     r->n = n;
1271
1272     r->t = mallocz(n * sizeof(time_t));
1273     r->v = mallocz(n * r->d * sizeof(calculated_number));
1274     r->o = mallocz(n * r->d * sizeof(uint8_t));
1275     r->od = mallocz(r->d * sizeof(uint8_t));
1276
1277     // set the hidden flag on hidden dimensions
1278     int c;
1279     for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
1280         if(unlikely(rd->flags & RRDDIM_FLAG_HIDDEN)) r->od[c] = RRDR_HIDDEN;
1281         else r->od[c] = 0;
1282     }
1283
1284     r->c = -1;
1285     r->group = 1;
1286     r->update_every = 1;
1287
1288     return r;
1289 }
1290
1291 RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned)
1292 {
1293     int debug = st->debug;
1294     int absolute_period_requested = -1;
1295
1296     time_t first_entry_t = rrdset_first_entry_t(st);
1297     time_t last_entry_t  = rrdset_last_entry_t(st);
1298
1299     if(before == 0 && after == 0) {
1300         // dump the all the data
1301         before = last_entry_t;
1302         after = first_entry_t;
1303         absolute_period_requested = 0;
1304     }
1305
1306     // allow relative for before (smaller than API_RELATIVE_TIME_MAX)
1307     if(((before < 0)?-before:before) <= API_RELATIVE_TIME_MAX) {
1308         if(abs(before) % st->update_every) {
1309             // make sure it is multiple of st->update_every
1310             if(before < 0) before = before - st->update_every - before % st->update_every;
1311             else           before = before + st->update_every - before % st->update_every;
1312         }
1313         if(before > 0) before = first_entry_t + before;
1314         else           before = last_entry_t  + before;
1315         absolute_period_requested = 0;
1316     }
1317
1318     // allow relative for after (smaller than API_RELATIVE_TIME_MAX)
1319     if(((after < 0)?-after:after) <= API_RELATIVE_TIME_MAX) {
1320         if(after == 0) after = -st->update_every;
1321         if(abs(after) % st->update_every) {
1322             // make sure it is multiple of st->update_every
1323             if(after < 0) after = after - st->update_every - after % st->update_every;
1324             else          after = after + st->update_every - after % st->update_every;
1325         }
1326         after = before + after;
1327         absolute_period_requested = 0;
1328     }
1329
1330     if(absolute_period_requested == -1)
1331         absolute_period_requested = 1;
1332
1333     // make sure they are within our timeframe
1334     if(before > last_entry_t)  before = last_entry_t;
1335     if(before < first_entry_t) before = first_entry_t;
1336
1337     if(after > last_entry_t)  after = last_entry_t;
1338     if(after < first_entry_t) after = first_entry_t;
1339
1340     // check if they are upside down
1341     if(after > before) {
1342         time_t tmp = before;
1343         before = after;
1344         after = tmp;
1345     }
1346
1347     // the duration of the chart
1348     time_t duration = before - after;
1349     long available_points = duration / st->update_every;
1350
1351     if(duration <= 0 || available_points <= 0)
1352         return rrdr_create(st, 1);
1353
1354     // check the wanted points
1355     if(points < 0) points = -points;
1356     if(points > available_points) points = available_points;
1357     if(points == 0) points = available_points;
1358
1359     // calculate proper grouping of source data
1360     long group = available_points / points;
1361     if(group <= 0) group = 1;
1362
1363     // round group to the closest integer
1364     if(available_points % points > points / 2) group++;
1365
1366     time_t after_new  = (aligned) ? (after  - (after  % (group * st->update_every))) : after;
1367     time_t before_new = (aligned) ? (before - (before % (group * st->update_every))) : before;
1368     long points_new   = (before_new - after_new) / st->update_every / group;
1369
1370     // find the starting and ending slots in our round robin db
1371     long    start_at_slot = rrdset_time2slot(st, before_new),
1372             stop_at_slot  = rrdset_time2slot(st, after_new);
1373
1374 #ifdef NETDATA_INTERNAL_CHECKS
1375     if(after_new < first_entry_t) {
1376         error("after_new %u is too small, minimum %u", (uint32_t)after_new, (uint32_t)first_entry_t);
1377     }
1378     if(after_new > last_entry_t) {
1379         error("after_new %u is too big, maximum %u", (uint32_t)after_new, (uint32_t)last_entry_t);
1380     }
1381     if(before_new < first_entry_t) {
1382         error("before_new %u is too small, minimum %u", (uint32_t)before_new, (uint32_t)first_entry_t);
1383     }
1384     if(before_new > last_entry_t) {
1385         error("before_new %u is too big, maximum %u", (uint32_t)before_new, (uint32_t)last_entry_t);
1386     }
1387     if(start_at_slot < 0 || start_at_slot >= st->entries) {
1388         error("start_at_slot is invalid %ld, expected 0 to %ld", start_at_slot, st->entries - 1);
1389     }
1390     if(stop_at_slot < 0 || stop_at_slot >= st->entries) {
1391         error("stop_at_slot is invalid %ld, expected 0 to %ld", stop_at_slot, st->entries - 1);
1392     }
1393     if(points_new > (before_new - after_new) / group / st->update_every + 1) {
1394         error("points_new %ld is more than points %ld", points_new, (before_new - after_new) / group / st->update_every + 1);
1395     }
1396 #endif
1397
1398     //info("RRD2RRDR(): %s: wanted %ld points, got %ld - group=%ld, wanted duration=%u, got %u - wanted %ld - %ld, got %ld - %ld", st->id, points, points_new, group, before - after, before_new - after_new, after, before, after_new, before_new);
1399
1400     after = after_new;
1401     before = before_new;
1402     duration = before - after;
1403     points = points_new;
1404
1405     // Now we have:
1406     // before = the end time of the calculation
1407     // after = the start time of the calculation
1408     // duration = the duration of the calculation
1409     // group = the number of source points to aggregate / group together
1410     // method = the method of grouping source points
1411     // points = the number of points to generate
1412
1413
1414     // -------------------------------------------------------------------------
1415     // initialize our result set
1416
1417     RRDR *r = rrdr_create(st, points);
1418     if(!r) {
1419 #ifdef NETDATA_INTERNAL_CHECKS
1420         error("Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
1421 #endif
1422         return NULL;
1423     }
1424     if(!r->d) {
1425 #ifdef NETDATA_INTERNAL_CHECKS
1426         error("Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
1427 #endif
1428         return r;
1429     }
1430
1431     if(absolute_period_requested == 1)
1432         r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
1433     else
1434         r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
1435
1436     // find how many dimensions we have
1437     long dimensions = r->d;
1438
1439
1440     // -------------------------------------------------------------------------
1441     // checks for debugging
1442
1443     if(debug) debug(D_RRD_STATS, "INFO %s first_t: %u, last_t: %u, all_duration: %u, after: %u, before: %u, duration: %u, points: %ld, group: %ld"
1444             , st->id
1445             , (uint32_t)first_entry_t
1446             , (uint32_t)last_entry_t
1447             , (uint32_t)(last_entry_t - first_entry_t)
1448             , (uint32_t)after
1449             , (uint32_t)before
1450             , (uint32_t)duration
1451             , points
1452             , group
1453             );
1454
1455
1456     // -------------------------------------------------------------------------
1457     // temp arrays for keeping values per dimension
1458
1459     calculated_number   last_values[dimensions]; // keep the last value of each dimension
1460     calculated_number   group_values[dimensions]; // keep sums when grouping
1461     long                group_counts[dimensions]; // keep the number of values added to group_values
1462     uint8_t             group_options[dimensions];
1463     uint8_t             found_non_zero[dimensions];
1464
1465
1466     // initialize them
1467     RRDDIM *rd;
1468     long c;
1469     for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1470         last_values[c] = 0;
1471         group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
1472         group_counts[c] = 0;
1473         group_options[c] = 0;
1474         found_non_zero[c] = 0;
1475     }
1476
1477
1478     // -------------------------------------------------------------------------
1479     // the main loop
1480
1481     time_t  now = rrdset_slot2time(st, start_at_slot),
1482             dt = st->update_every,
1483             group_start_t = 0;
1484
1485     if(unlikely(debug)) debug(D_RRD_STATS, "BEGIN %s after_t: %u (stop_at_t: %ld), before_t: %u (start_at_t: %ld), start_t(now): %u, current_entry: %ld, entries: %ld"
1486             , st->id
1487             , (uint32_t)after
1488             , stop_at_slot
1489             , (uint32_t)before
1490             , start_at_slot
1491             , (uint32_t)now
1492             , st->current_entry
1493             , st->entries
1494             );
1495
1496     r->group = group;
1497     r->update_every = group * st->update_every;
1498     r->before = now;
1499     r->after = now;
1500
1501     //info("RRD2RRDR(): %s: STARTING", st->id);
1502
1503     long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
1504     for(; !stop_now ; now -= dt, slot--, counter++) {
1505         if(unlikely(slot < 0)) slot = st->entries - 1;
1506         if(unlikely(slot == stop_at_slot)) stop_now = counter;
1507
1508         if(unlikely(debug)) debug(D_RRD_STATS, "ROW %s slot: %ld, entries_counter: %ld, group_count: %ld, added: %ld, now: %ld, %s %s"
1509                 , st->id
1510                 , slot
1511                 , counter
1512                 , group_count + 1
1513                 , added
1514                 , now
1515                 , (group_count + 1 == group)?"PRINT":"  -  "
1516                 , (now >= after && now <= before)?"RANGE":"  -  "
1517                 );
1518
1519         // make sure we return data in the proper time range
1520         if(unlikely(now > before)) continue;
1521         if(unlikely(now < after)) break;
1522
1523         if(unlikely(group_count == 0)) {
1524             group_start_t = now;
1525         }
1526         group_count++;
1527
1528         if(unlikely(group_count == group)) {
1529             if(unlikely(added >= points)) break;
1530             add_this = 1;
1531         }
1532
1533         // do the calculations
1534         for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1535             storage_number n = rd->values[slot];
1536             if(unlikely(!does_storage_number_exist(n))) continue;
1537
1538             group_counts[c]++;
1539
1540             calculated_number value = unpack_storage_number(n);
1541             if(likely(value != 0.0)) {
1542                 group_options[c] |= RRDR_NONZERO;
1543                 found_non_zero[c] = 1;
1544             }
1545
1546             if(unlikely(did_storage_number_reset(n)))
1547                 group_options[c] |= RRDR_RESET;
1548
1549             switch(group_method) {
1550                 case GROUP_MIN:
1551                     if(unlikely(isnan(group_values[c])) ||
1552                             fabsl(value) < fabsl(group_values[c]))
1553                         group_values[c] = value;
1554                     break;
1555
1556                 case GROUP_MAX:
1557                     if(unlikely(isnan(group_values[c])) ||
1558                             fabsl(value) > fabsl(group_values[c]))
1559                         group_values[c] = value;
1560                     break;
1561
1562                 default:
1563                 case GROUP_SUM:
1564                 case GROUP_AVERAGE:
1565                 case GROUP_UNDEFINED:
1566                     group_values[c] += value;
1567                     break;
1568
1569                 case GROUP_INCREMENTAL_SUM:
1570                     if(unlikely(slot == start_at_slot))
1571                         last_values[c] = value;
1572
1573                     group_values[c] += last_values[c] - value;
1574                     last_values[c] = value;
1575                     break;
1576             }
1577         }
1578
1579         // added it
1580         if(unlikely(add_this)) {
1581             if(unlikely(!rrdr_line_init(r, group_start_t))) break;
1582
1583             r->after = now;
1584
1585             calculated_number *cn = rrdr_line_values(r);
1586             uint8_t *co = rrdr_line_options(r);
1587
1588             for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1589
1590                 // update the dimension options
1591                 if(likely(found_non_zero[c])) r->od[c] |= RRDR_NONZERO;
1592
1593                 // store the specific point options
1594                 co[c] = group_options[c];
1595
1596                 // store the value
1597                 if(unlikely(group_counts[c] == 0)) {
1598                     cn[c] = 0.0;
1599                     co[c] |= RRDR_EMPTY;
1600                     group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
1601                 }
1602                 else {
1603                     switch(group_method) {
1604                         case GROUP_MIN:
1605                         case GROUP_MAX:
1606                             if(unlikely(isnan(group_values[c])))
1607                                 cn[c] = 0;
1608                             else {
1609                                 cn[c] = group_values[c];
1610                                 group_values[c] = NAN;
1611                             }
1612                             break;
1613
1614                         case GROUP_SUM:
1615                         case GROUP_INCREMENTAL_SUM:
1616                             cn[c] = group_values[c];
1617                             group_values[c] = 0;
1618                             break;
1619
1620                         default:
1621                         case GROUP_AVERAGE:
1622                         case GROUP_UNDEFINED:
1623                             cn[c] = group_values[c] / group_counts[c];
1624                             group_values[c] = 0;
1625                             break;
1626                     }
1627
1628                     if(cn[c] < r->min) r->min = cn[c];
1629                     if(cn[c] > r->max) r->max = cn[c];
1630                 }
1631
1632                 // reset for the next loop
1633                 group_counts[c] = 0;
1634                 group_options[c] = 0;
1635             }
1636
1637             added++;
1638             group_count = 0;
1639             add_this = 0;
1640         }
1641     }
1642
1643     rrdr_done(r);
1644     //info("RRD2RRDR(): %s: END %ld loops made, %ld points generated", st->id, counter, rrdr_rows(r));
1645     //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
1646     return r;
1647 }
1648
1649 int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *db_after, time_t *db_before, int *value_is_null)
1650 {
1651     RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
1652     if(!r) {
1653         if(value_is_null) *value_is_null = 1;
1654         return 500;
1655     }
1656
1657     if(rrdr_rows(r) == 0) {
1658         rrdr_free(r);
1659
1660         if(db_after)  *db_after  = 0;
1661         if(db_before) *db_before = 0;
1662         if(value_is_null) *value_is_null = 1;
1663
1664         return 400;
1665     }
1666
1667     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
1668         buffer_no_cacheable(wb);
1669     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
1670         buffer_cacheable(wb);
1671
1672     options = rrdr_check_options(r, options, dimensions);
1673
1674     if(dimensions)
1675         rrdr_disable_not_selected_dimensions(r, dimensions);
1676
1677     if(db_after)  *db_after  = r->after;
1678     if(db_before) *db_before = r->before;
1679
1680     long i = (options & RRDR_OPTION_REVERSED)?rrdr_rows(r) - 1:0;
1681     *n = rrdr2value(r, i, options, value_is_null);
1682
1683     rrdr_free(r);
1684     return 200;
1685 }
1686
1687 int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp)
1688 {
1689     RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
1690     if(!r) {
1691         buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
1692         return 500;
1693     }
1694
1695     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
1696         buffer_no_cacheable(wb);
1697     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
1698         buffer_cacheable(wb);
1699
1700     options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
1701
1702     if(dimensions)
1703         rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
1704
1705     if(latest_timestamp && rrdr_rows(r) > 0)
1706         *latest_timestamp = r->before;
1707
1708     switch(format) {
1709     case DATASOURCE_SSV:
1710         if(options & RRDR_OPTION_JSON_WRAP) {
1711             wb->contenttype = CT_APPLICATION_JSON;
1712             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1713             rrdr2ssv(r, wb, options, "", " ", "");
1714             rrdr_json_wrapper_end(r, wb, format, options, 1);
1715         }
1716         else {
1717             wb->contenttype = CT_TEXT_PLAIN;
1718             rrdr2ssv(r, wb, options, "", " ", "");
1719         }
1720         break;
1721
1722     case DATASOURCE_SSV_COMMA:
1723         if(options & RRDR_OPTION_JSON_WRAP) {
1724             wb->contenttype = CT_APPLICATION_JSON;
1725             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1726             rrdr2ssv(r, wb, options, "", ",", "");
1727             rrdr_json_wrapper_end(r, wb, format, options, 1);
1728         }
1729         else {
1730             wb->contenttype = CT_TEXT_PLAIN;
1731             rrdr2ssv(r, wb, options, "", ",", "");
1732         }
1733         break;
1734
1735     case DATASOURCE_JS_ARRAY:
1736         if(options & RRDR_OPTION_JSON_WRAP) {
1737             wb->contenttype = CT_APPLICATION_JSON;
1738             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1739             rrdr2ssv(r, wb, options, "[", ",", "]");
1740             rrdr_json_wrapper_end(r, wb, format, options, 0);
1741         }
1742         else {
1743             wb->contenttype = CT_APPLICATION_JSON;
1744             rrdr2ssv(r, wb, options, "[", ",", "]");
1745         }
1746         break;
1747
1748     case DATASOURCE_CSV:
1749         if(options & RRDR_OPTION_JSON_WRAP) {
1750             wb->contenttype = CT_APPLICATION_JSON;
1751             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1752             rrdr2csv(r, wb, options, "", ",", "\\n", "");
1753             rrdr_json_wrapper_end(r, wb, format, options, 1);
1754         }
1755         else {
1756             wb->contenttype = CT_TEXT_PLAIN;
1757             rrdr2csv(r, wb, options, "", ",", "\r\n", "");
1758         }
1759         break;
1760
1761     case DATASOURCE_CSV_JSON_ARRAY:
1762         wb->contenttype = CT_APPLICATION_JSON;
1763         if(options & RRDR_OPTION_JSON_WRAP) {
1764             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1765             buffer_strcat(wb, "[\n");
1766             rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
1767             buffer_strcat(wb, "\n]");
1768             rrdr_json_wrapper_end(r, wb, format, options, 0);
1769         }
1770         else {
1771             wb->contenttype = CT_TEXT_PLAIN;
1772             buffer_strcat(wb, "[\n");
1773             rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
1774             buffer_strcat(wb, "\n]");
1775         }
1776         break;
1777
1778     case DATASOURCE_TSV:
1779         if(options & RRDR_OPTION_JSON_WRAP) {
1780             wb->contenttype = CT_APPLICATION_JSON;
1781             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1782             rrdr2csv(r, wb, options, "", "\t", "\\n", "");
1783             rrdr_json_wrapper_end(r, wb, format, options, 1);
1784         }
1785         else {
1786             wb->contenttype = CT_TEXT_PLAIN;
1787             rrdr2csv(r, wb, options, "", "\t", "\r\n", "");
1788         }
1789         break;
1790
1791     case DATASOURCE_HTML:
1792         if(options & RRDR_OPTION_JSON_WRAP) {
1793             wb->contenttype = CT_APPLICATION_JSON;
1794             rrdr_json_wrapper_begin(r, wb, format, options, 1);
1795             buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
1796             rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
1797             buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
1798             rrdr_json_wrapper_end(r, wb, format, options, 1);
1799         }
1800         else {
1801             wb->contenttype = CT_TEXT_HTML;
1802             buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
1803             rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
1804             buffer_strcat(wb, "</table>\n</center>\n</html>\n");
1805         }
1806         break;
1807
1808     case DATASOURCE_DATATABLE_JSONP:
1809         wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
1810
1811         if(options & RRDR_OPTION_JSON_WRAP)
1812             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1813
1814         rrdr2json(r, wb, options, 1);
1815
1816         if(options & RRDR_OPTION_JSON_WRAP)
1817             rrdr_json_wrapper_end(r, wb, format, options, 0);
1818         break;
1819
1820     case DATASOURCE_DATATABLE_JSON:
1821         wb->contenttype = CT_APPLICATION_JSON;
1822
1823         if(options & RRDR_OPTION_JSON_WRAP)
1824             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1825
1826         rrdr2json(r, wb, options, 1);
1827
1828         if(options & RRDR_OPTION_JSON_WRAP)
1829             rrdr_json_wrapper_end(r, wb, format, options, 0);
1830         break;
1831
1832     case DATASOURCE_JSONP:
1833         wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
1834         if(options & RRDR_OPTION_JSON_WRAP)
1835             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1836
1837         rrdr2json(r, wb, options, 0);
1838
1839         if(options & RRDR_OPTION_JSON_WRAP)
1840             rrdr_json_wrapper_end(r, wb, format, options, 0);
1841         break;
1842
1843     case DATASOURCE_JSON:
1844     default:
1845         wb->contenttype = CT_APPLICATION_JSON;
1846
1847         if(options & RRDR_OPTION_JSON_WRAP)
1848             rrdr_json_wrapper_begin(r, wb, format, options, 0);
1849
1850         rrdr2json(r, wb, options, 0);
1851
1852         if(options & RRDR_OPTION_JSON_WRAP)
1853             rrdr_json_wrapper_end(r, wb, format, options, 0);
1854         break;
1855     }
1856
1857     rrdr_free(r);
1858     return 200;
1859 }
1860
1861 time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group, int group_method, time_t after, time_t before, int only_non_zero)
1862 {
1863     int c;
1864     pthread_rwlock_rdlock(&st->rwlock);
1865
1866
1867     // -------------------------------------------------------------------------
1868     // switch from JSON to google JSON
1869
1870     char kq[2] = "\"";
1871     char sq[2] = "\"";
1872     switch(type) {
1873         case DATASOURCE_DATATABLE_JSON:
1874         case DATASOURCE_DATATABLE_JSONP:
1875             kq[0] = '\0';
1876             sq[0] = '\'';
1877             break;
1878
1879         case DATASOURCE_JSON:
1880         default:
1881             break;
1882     }
1883
1884
1885     // -------------------------------------------------------------------------
1886     // validate the parameters
1887
1888     if(points < 1) points = 1;
1889     if(group < 1) group = 1;
1890
1891     if(before == 0 || before > rrdset_last_entry_t(st)) before = rrdset_last_entry_t(st);
1892     if(after  == 0 || after < rrdset_first_entry_t(st)) after = rrdset_first_entry_t(st);
1893
1894     // ---
1895
1896     // our return value (the last timestamp printed)
1897     // this is required to detect re-transmit in google JSONP
1898     time_t last_timestamp = 0;
1899
1900
1901     // -------------------------------------------------------------------------
1902     // find how many dimensions we have
1903
1904     int dimensions = 0;
1905     RRDDIM *rd;
1906     for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
1907     if(!dimensions) {
1908         pthread_rwlock_unlock(&st->rwlock);
1909         buffer_strcat(wb, "No dimensions yet.");
1910         return 0;
1911     }
1912
1913
1914     // -------------------------------------------------------------------------
1915     // prepare various strings, to speed up the loop
1916
1917     char overflow_annotation[201]; snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
1918     char normal_annotation[201];   snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
1919     char pre_date[51];             snprintfz(pre_date,             50, "        {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
1920     char post_date[21];            snprintfz(post_date,            20, "%s}", sq);
1921     char pre_value[21];            snprintfz(pre_value,            20, ",{%sv%s:", kq, kq);
1922     char post_value[21];           strcpy(post_value,                  "}");
1923
1924
1925     // -------------------------------------------------------------------------
1926     // checks for debugging
1927
1928     if(st->debug) {
1929         debug(D_RRD_STATS, "%s first_entry_t = %ld, last_entry_t = %ld, duration = %ld, after = %ld, before = %ld, duration = %ld, entries_to_show = %ld, group = %ld"
1930             , st->id
1931             , rrdset_first_entry_t(st)
1932             , rrdset_last_entry_t(st)
1933             , rrdset_last_entry_t(st) - rrdset_first_entry_t(st)
1934             , after
1935             , before
1936             , before - after
1937             , points
1938             , group
1939             );
1940
1941         if(before < after)
1942             debug(D_RRD_STATS, "WARNING: %s The newest value in the database (%ld) is earlier than the oldest (%ld)", st->name, before, after);
1943
1944         if((before - after) > st->entries * st->update_every)
1945             debug(D_RRD_STATS, "WARNING: %s The time difference between the oldest and the newest entries (%ld) is higher than the capacity of the database (%ld)", st->name, before - after, st->entries * st->update_every);
1946     }
1947
1948
1949     // -------------------------------------------------------------------------
1950     // temp arrays for keeping values per dimension
1951
1952     calculated_number group_values[dimensions]; // keep sums when grouping
1953     int               print_hidden[dimensions]; // keep hidden flags
1954     int               found_non_zero[dimensions];
1955     int               found_non_existing[dimensions];
1956
1957     // initialize them
1958     for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1959         group_values[c] = 0;
1960         print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
1961         found_non_zero[c] = 0;
1962         found_non_existing[c] = 0;
1963     }
1964
1965
1966     // error("OLD: points=%d after=%d before=%d group=%d, duration=%d", entries_to_show, before - (st->update_every * group * entries_to_show), before, group, before - after + 1);
1967     // rrd2array(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method, only_non_zero);
1968     // rrd2rrdr(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method);
1969
1970     // -------------------------------------------------------------------------
1971     // remove dimensions that contain only zeros
1972
1973     int max_loop = 1;
1974     if(only_non_zero) max_loop = 2;
1975
1976     for(; max_loop ; max_loop--) {
1977
1978         // -------------------------------------------------------------------------
1979         // print the JSON header
1980
1981         buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
1982         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
1983         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
1984         buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
1985
1986         // print the header for each dimension
1987         // and update the print_hidden array for the dimensions that should be hidden
1988         int pc = 0;
1989         for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1990             if(!print_hidden[c]) {
1991                 pc++;
1992                 buffer_sprintf(wb, ",\n     {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
1993             }
1994         }
1995         if(!pc) {
1996             buffer_sprintf(wb, ",\n     {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq);
1997         }
1998
1999         // print the begin of row data
2000         buffer_sprintf(wb, "\n  ],\n    %srows%s:\n [\n", kq, kq);
2001
2002
2003         // -------------------------------------------------------------------------
2004         // the main loop
2005
2006         int annotate_reset = 0;
2007         int annotation_count = 0;
2008
2009         long    t = rrdset_time2slot(st, before),
2010                 stop_at_t = rrdset_time2slot(st, after),
2011                 stop_now = 0;
2012
2013         t -= t % group;
2014
2015         time_t  now = rrdset_slot2time(st, t),
2016                 dt = st->update_every;
2017
2018         long count = 0, printed = 0, group_count = 0;
2019         last_timestamp = 0;
2020
2021         if(st->debug) debug(D_RRD_STATS, "%s: REQUEST after:%u before:%u, points:%ld, group:%ld, CHART cur:%ld first: %u last:%u, CALC start_t:%ld, stop_t:%ld"
2022                     , st->id
2023                     , (uint32_t)after
2024                     , (uint32_t)before
2025                     , points
2026                     , group
2027                     , st->current_entry
2028                     , (uint32_t)rrdset_first_entry_t(st)
2029                     , (uint32_t)rrdset_last_entry_t(st)
2030                     , t
2031                     , stop_at_t
2032                     );
2033
2034         long counter = 0;
2035         for(; !stop_now ; now -= dt, t--, counter++) {
2036             if(t < 0) t = st->entries - 1;
2037             if(t == stop_at_t) stop_now = counter;
2038
2039             int print_this = 0;
2040
2041             if(st->debug) debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %ld, %s %s"
2042                     , st->id
2043                     , t
2044                     , count + 1
2045                     , group_count + 1
2046                     , printed
2047                     , now
2048                     , (group_count + 1 == group)?"PRINT":"  -  "
2049                     , (now >= after && now <= before)?"RANGE":"  -  "
2050                     );
2051
2052
2053             // make sure we return data in the proper time range
2054             if(now > before) continue;
2055             if(now < after) break;
2056
2057             //if(rrdset_slot2time(st, t) != now)
2058             //  error("%s: slot=%ld, now=%ld, slot2time=%ld, diff=%ld, last_entry_t=%ld, rrdset_last_slot=%ld", st->id, t, now, rrdset_slot2time(st,t), now - rrdset_slot2time(st,t), rrdset_last_entry_t(st), rrdset_last_slot(st));
2059
2060             count++;
2061             group_count++;
2062
2063             // check if we have to print this now
2064             if(group_count == group) {
2065                 if(printed >= points) {
2066                     // debug(D_RRD_STATS, "Already printed all rows. Stopping.");
2067                     break;
2068                 }
2069
2070                 // generate the local date time
2071                 struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
2072                 if(!tm) { error("localtime() failed."); continue; }
2073                 if(now > last_timestamp) last_timestamp = now;
2074
2075                 if(printed) buffer_strcat(wb, "]},\n");
2076                 buffer_strcat(wb, pre_date);
2077                 buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
2078                 buffer_strcat(wb, post_date);
2079
2080                 print_this = 1;
2081             }
2082
2083             // do the calculations
2084             for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2085                 storage_number n = rd->values[t];
2086                 calculated_number value = unpack_storage_number(n);
2087
2088                 if(!does_storage_number_exist(n)) {
2089                     value = 0.0;
2090                     found_non_existing[c]++;
2091                 }
2092                 if(did_storage_number_reset(n)) annotate_reset = 1;
2093
2094                 switch(group_method) {
2095                     case GROUP_MAX:
2096                         if(abs(value) > abs(group_values[c])) group_values[c] = value;
2097                         break;
2098
2099                     case GROUP_SUM:
2100                         group_values[c] += value;
2101                         break;
2102
2103                     default:
2104                     case GROUP_AVERAGE:
2105                         group_values[c] += value;
2106                         if(print_this) group_values[c] /= ( group_count - found_non_existing[c] );
2107                         break;
2108                 }
2109             }
2110
2111             if(print_this) {
2112                 if(annotate_reset) {
2113                     annotation_count++;
2114                     buffer_strcat(wb, overflow_annotation);
2115                     annotate_reset = 0;
2116                 }
2117                 else
2118                     buffer_strcat(wb, normal_annotation);
2119
2120                 pc = 0;
2121                 for(c = 0 ; c < dimensions ; c++) {
2122                     if(found_non_existing[c] == group_count) {
2123                         // all entries are non-existing
2124                         pc++;
2125                         buffer_strcat(wb, pre_value);
2126                         buffer_strcat(wb, "null");
2127                         buffer_strcat(wb, post_value);
2128                     }
2129                     else if(!print_hidden[c]) {
2130                         pc++;
2131                         buffer_strcat(wb, pre_value);
2132                         buffer_rrd_value(wb, group_values[c]);
2133                         buffer_strcat(wb, post_value);
2134
2135                         if(group_values[c]) found_non_zero[c]++;
2136                     }
2137
2138                     // reset them for the next loop
2139                     group_values[c] = 0;
2140                     found_non_existing[c] = 0;
2141                 }
2142
2143                 // if all dimensions are hidden, print a null
2144                 if(!pc) {
2145                     buffer_strcat(wb, pre_value);
2146                     buffer_strcat(wb, "null");
2147                     buffer_strcat(wb, post_value);
2148                 }
2149
2150                 printed++;
2151                 group_count = 0;
2152             }
2153         }
2154
2155         if(printed) buffer_strcat(wb, "]}");
2156         buffer_strcat(wb, "\n   ]\n}\n");
2157
2158         if(only_non_zero && max_loop > 1) {
2159             int changed = 0;
2160             for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2161                 group_values[c] = 0;
2162                 found_non_existing[c] = 0;
2163
2164                 if(!print_hidden[c] && !found_non_zero[c]) {
2165                     changed = 1;
2166                     print_hidden[c] = 1;
2167                 }
2168             }
2169
2170             if(changed) buffer_flush(wb);
2171             else break;
2172         }
2173         else break;
2174
2175     } // max_loop
2176
2177     debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %lu bytes", st->name, wb->len);
2178
2179     pthread_rwlock_unlock(&st->rwlock);
2180     return last_timestamp;
2181 }