Code

kill a few warnings and properly release memory -- martin sperl
[rrdtool.git] / src / rrd_xport.c
1 /****************************************************************************
2  * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
3  ****************************************************************************
4  * rrd_xport.c  export RRD data 
5  ****************************************************************************/
7 #include <sys/stat.h>
8 #include <locale.h>
10 #include "rrd_tool.h"
11 #include "rrd_graph.h"
12 #include "rrd_xport.h"
13 #include "unused.h"
14 #include "rrd_client.h"
16 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
17 #include <io.h>
18 #include <fcntl.h>
19 #endif
22 int       rrd_xport(
23     int,
24     char **,
25     int *,
26     time_t *,
27     time_t *,
28     unsigned long *,
29     unsigned long *,
30     char ***,
31     rrd_value_t **);
33 int       rrd_xport_fn(
34     image_desc_t *,
35     time_t *,
36     time_t *,
37     unsigned long *,
38     unsigned long *,
39     char ***,
40     rrd_value_t **,
41     int);
45 int rrd_xport(
46     int argc,
47     char **argv,
48     int UNUSED(*xsize),
49     time_t *start,
50     time_t *end,        /* which time frame do you want ?
51                          * will be changed to represent reality */
52     unsigned long *step,    /* which stepsize do you want? 
53                              * will be changed to represent reality */
54     unsigned long *col_cnt, /* number of data columns in the result */
55     char ***legend_v,   /* legend entries */
56     rrd_value_t **data)
57 {                       /* two dimensional array containing the data */
58     image_desc_t im;
59     time_t    start_tmp = 0, end_tmp = 0;
60     rrd_time_value_t start_tv, end_tv;
61     char     *parsetime_error = NULL;
63     struct option long_options[] = {
64         {"start", required_argument, 0, 's'},
65         {"end", required_argument, 0, 'e'},
66         {"maxrows", required_argument, 0, 'm'},
67         {"step", required_argument, 0, 261},
68         {"enumds", no_argument, 0, 262},    /* these are handled in the frontend ... */
69         {"json", no_argument, 0, 263},    /* these are handled in the frontend ... */
70         {"daemon", required_argument, 0, 'd'},
71         {0, 0, 0, 0}
72     };
74     optind = 0;
75     opterr = 0;         /* initialize getopt */
77     rrd_graph_init(&im);
79     rrd_parsetime("end-24h", &start_tv);
80     rrd_parsetime("now", &end_tv);
82     while (1) {
83         int       option_index = 0;
84         int       opt;
86         opt = getopt_long(argc, argv, "s:e:m:d:", long_options, &option_index);
88         if (opt == EOF)
89             break;
91         switch (opt) {
92         case 261:
93             im.step = atoi(optarg);
94             break;
95         case 262:
96             break;
97         case 's':
98             if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
99                 rrd_set_error("start time: %s", parsetime_error);
100                 return -1;
101             }
102             break;
103         case 'e':
104             if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
105                 rrd_set_error("end time: %s", parsetime_error);
106                 return -1;
107             }
108             break;
109         case 'm':
110             im.xsize = atol(optarg);
111             if (im.xsize < 10) {
112                 rrd_set_error("maxrows below 10 rows");
113                 return -1;
114             }
115             break;
116         case 'd':
117         {
118             if (im.daemon_addr != NULL)
119             {
120                 rrd_set_error ("You cannot specify --daemon "
121                         "more than once.");
122                 return (-1);
123             }
125             im.daemon_addr = strdup(optarg);
126             if (im.daemon_addr == NULL)
127             {
128                 rrd_set_error("strdup error");
129                 return -1;
130             }
131             break;
132         }
134         case '?':
135             rrd_set_error("unknown option '%s'", argv[optind - 1]);
136             return -1;
137         }
138     }
140     if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
141         return -1;
142     }
144     if (start_tmp < 3600 * 24 * 365 * 10) {
145         rrd_set_error("the first entry to fetch should be after 1980 (%ld)",
146                       start_tmp);
147         return -1;
148     }
150     if (end_tmp < start_tmp) {
151         rrd_set_error("start (%ld) should be less than end (%ld)",
152                       start_tmp, end_tmp);
153         return -1;
154     }
156     im.start = start_tmp;
157     im.end = end_tmp;
158     im.step = max((long) im.step, (im.end - im.start) / im.xsize);
160     rrd_graph_script(argc, argv, &im, 0);
161     if (rrd_test_error()) {
162         im_free(&im);
163         return -1;
164     }
166     if (im.gdes_c == 0) {
167         rrd_set_error("can't make an xport without contents");
168         im_free(&im);
169         return (-1);
170     }
172     {   /* try to connect to rrdcached */
173         int status = rrdc_connect(im.daemon_addr);
174         if (status != 0) return status;
175     }
177     if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data,0) == -1) {
178         im_free(&im);
179         return -1;
180     }
182     im_free(&im);
183     return 0;
188 int rrd_xport_fn(
189     image_desc_t *im,
190     time_t *start,
191     time_t *end,        /* which time frame do you want ?
192                          * will be changed to represent reality */
193     unsigned long *step,    /* which stepsize do you want? 
194                              * will be changed to represent reality */
195     unsigned long *col_cnt, /* number of data columns in the result */
196     char ***legend_v,   /* legend entries */
197     rrd_value_t **data,
198     int dolines)
199 {                       /* two dimensional array containing the data */
201     int       i = 0, j = 0;
202     unsigned long dst_row, row_cnt;
203     rrd_value_t  *dstptr;
205     unsigned long xport_counter = 0;
206     int      *ref_list;
207     long     *step_list;
208     long     *step_list_ptr;    
209     char    **legend_list;
212     /* pull the data from the rrd files ... */
213     if (data_fetch(im) == -1)
214         return -1;
216     /* evaluate CDEF  operations ... */
217     if (data_calc(im) == -1)
218         return -1;
220     /* how many xports or lines/AREA/STACK ? */
221     *col_cnt = 0;
222     for (i = 0; i < im->gdes_c; i++) {
223         switch (im->gdes[i].gf) {
224         case GF_LINE:
225         case GF_AREA:
226         case GF_STACK:
227           (*col_cnt)+=dolines;
228           break;
229         case GF_XPORT:
230           (*col_cnt)++;
231             break;
232         default:
233             break;
234         }
235     }
236     if ((*col_cnt) == 0) {
237         rrd_set_error("no XPORT found, nothing to do");
238         return -1;
239     }
241     /* a list of referenced gdes */
242     ref_list = (int*)malloc(sizeof(int) * (*col_cnt));
243     if (ref_list == NULL)
244         return -1;
246     /* a list to save pointers to the column's legend entry */
247     /* this is a return value! */
248     legend_list = (char**)malloc(sizeof(char *) * (*col_cnt));
249     if (legend_list == NULL) {
250         free(ref_list);
251         return -1;
252     }
254     /* lets find the step size we have to use for xport */
255     step_list = (long*)malloc(sizeof(long)*((*col_cnt)+1));
256     step_list_ptr = step_list;
257     j = 0;
258     for (i = 0; i < im->gdes_c; i++) {
259       /* decide if we need to handle the output */
260         int handle=0;
261         switch (im->gdes[i].gf) {
262         case GF_LINE:
263         case GF_AREA:
264         case GF_STACK:
265           handle=dolines;
266           break;
267         case GF_XPORT:
268           handle=1;
269           break;
270         default:
271           handle=0;
272           break;
273         }
274         /* and now do the real work */
275         if (handle) {
276             ref_list[xport_counter++] = i;
277             *step_list_ptr = im->gdes[im->gdes[i].vidx].step;
278             /* printf("%s:%lu\n",im->gdes[i].legend,*step_list_ptr); */
279             step_list_ptr++;
280             /* reserve room for one legend entry */
281             /* is FMT_LEG_LEN + 5 the correct size? */
282             if ((legend_list[j] =
283                 (char*)malloc(sizeof(char) * (FMT_LEG_LEN + 5))) == NULL) {
284                 free(ref_list);
285                 *data = NULL;
286                 while (--j > -1)
287                     free(legend_list[j]);
288                 free(legend_list);
289                 free(step_list);
290                 rrd_set_error("malloc xport legend entry");
291                 return (-1);
292             }
294             if (im->gdes[i].legend)
295                 /* omit bounds check, should have the same size */
296                 strcpy(legend_list[j++], im->gdes[i].legend);
297             else
298                 legend_list[j++][0] = '\0';
299         }
300     }
301     *step_list_ptr=0;    
302     /* find a common step */
303     *step = lcd(step_list);
304     /* printf("step: %lu\n",*step); */
305     free(step_list);
306     
307     *start =  im->start - im->start % (*step);
308     *end = im->end - im->end % (*step) + (*step);
309     
311     /* room for rearranged data */
312     /* this is a return value! */
313     row_cnt = ((*end) - (*start)) / (*step);
314     if (((*data) =
315         (rrd_value_t*)malloc((*col_cnt) * row_cnt * sizeof(rrd_value_t))) == NULL) {
316         free(ref_list);
317         free(legend_list);
318         rrd_set_error("malloc xport data area");
319         return (-1);
320     }
321     dstptr = (*data);
323     /* fill data structure */
324     for (dst_row = 0; (int) dst_row < (int) row_cnt; dst_row++) {
325         for (i = 0; i < (int) (*col_cnt); i++) {
326             long vidx = im->gdes[ref_list[i]].vidx;
327             time_t now = *start + dst_row * *step;
328             (*dstptr++) = im->gdes[vidx].data[(unsigned long)
329                                               floor((double)
330                                                     (now - im->gdes[vidx].start)
331                                                     /im->gdes[vidx].step)
332                                               * im->gdes[vidx].ds_cnt +
333                                               im->gdes[vidx].ds];
335         }
336     }
338     *legend_v = legend_list;
339     free(ref_list);
340     return 0;
344 /* helper function for buffer handling */
345 typedef struct stringbuffer_t {
346   size_t allocated;
347   size_t len;
348   unsigned char* data;
349   FILE *file;
350 } stringbuffer_t;
351 int addToBuffer(stringbuffer_t *,char*,size_t);
353 int rrd_graph_xport(image_desc_t *);
354 int rrd_xport_format_xml(stringbuffer_t *,image_desc_t*,time_t, time_t, unsigned long, unsigned long, char**, rrd_value_t*);
355 int rrd_xport_format_json(stringbuffer_t *,image_desc_t*,time_t, time_t, unsigned long, unsigned long, char**, rrd_value_t*);
356 int rrd_xport_format_sv(char,stringbuffer_t *,image_desc_t*,time_t, time_t, unsigned long, unsigned long, char**, rrd_value_t*);
358 int rrd_graph_xport(image_desc_t *im) {
359   /* prepare the data for processing */
360   unsigned long col_cnt=0;
361   time_t start=im->start;
362   time_t end=im->end;
363   unsigned long step=im->step;
364   char **legend_v=NULL;
365   rrd_value_t *data=NULL;
366   /* initialize buffer */
367   stringbuffer_t buffer={0,0,NULL,NULL}; 
368   /* if we write a file, then open it */
369   if (strlen(im->graphfile)) {
370     buffer.file=fopen(im->graphfile,"w");
371   }
373   /* do the data processing */
374   if (rrd_xport_fn(im,&start,&end,&step,&col_cnt,&legend_v,&data,1)) { return -1;}
376   /* fill in some data */
377   rrd_infoval_t info;
378   info.u_cnt = im->start;
379   grinfo_push(im, sprintf_alloc("graph_start"), RD_I_CNT, info);
380   info.u_cnt = im->end;
381   grinfo_push(im, sprintf_alloc("graph_end"), RD_I_CNT, info);
382   info.u_cnt = im->step;
383   grinfo_push(im, sprintf_alloc("graph_step"), RD_I_CNT, info);
385   /* set locale */
386   char *old_locale = setlocale(LC_NUMERIC,NULL);
387   setlocale(LC_NUMERIC, "C");
389   /* format it for output */
390   int r=0;
391   switch(im->imgformat) {
392   case IF_XML:
393     r=rrd_xport_format_xml(&buffer,im, start, end, step, col_cnt, legend_v, data);
394     break;
395   case IF_JSON:
396     r=rrd_xport_format_json(&buffer,im, start, end, step, col_cnt, legend_v, data);
397     break;
398   case IF_CSV:
399     r=rrd_xport_format_sv(',',&buffer,im, start, end, step, col_cnt, legend_v, data);
400     break;
401   case IF_TSV:
402     r=rrd_xport_format_sv('\t',&buffer,im, start, end, step, col_cnt, legend_v, data);
403     break;
404   case IF_SSV:
405     r=rrd_xport_format_sv(';',&buffer,im, start, end, step, col_cnt, legend_v, data);
406     break;
407   default:
408     break;
409   }
410   /* restore locale */
411   setlocale(LC_NUMERIC, old_locale);
412   /* handle errors */
413   if (r) {
414     /* free legend */
415     for (unsigned long j = 0; j < col_cnt; j++) {
416       free(legend_v[j]);
417     }
418     free(legend_v);
419     /* free data */
420     free(data);
421     /* free the bufer */
422     if (buffer.data) {free(buffer.data);}
423     /* and return with error */
424     return r;
425   }
427   /* now do the cleanup */
428   if (buffer.file) {
429     fclose(buffer.file); buffer.file=NULL; 
430     im->rendered_image_size=0;
431     im->rendered_image=NULL;
432   } else {
433     im->rendered_image_size=buffer.len;
434     im->rendered_image=buffer.data;    
435   }
437   /* and print stuff */
438   return print_calc(im);
441 int addToBuffer(stringbuffer_t * sb,char* data,size_t len) {
442   /* if len <= 0  we assume a string and calculate the length ourself */
443   if (len<=0) { len=strlen(data); }
444   /* if we have got a file, then take the shortcut */
445   if (sb->file) { 
446     sb->len+=len;
447     fwrite(data,len,1,sb->file); 
448     return 0; 
449   }
450   /* if buffer is 0, then initialize */
451   if (! sb->data) { 
452     /* make buffer a multiple of 8192 */
453     sb->allocated+=8192;
454     sb->allocated-=(sb->allocated%8192);    
455     /* and allocate it */
456     sb->data=malloc(sb->allocated); 
457     if (! sb->data) { 
458       rrd_set_error("malloc issue");
459       return 1;
460     }
461     /* and initialize the buffer */
462     sb->len=0;
463     sb->data[0]=0;
464   }
465   /* and figure out if we need to extend the buffer */
466   if (sb->len+len+1>=sb->allocated) {
467     /* add so many pages until we have a buffer big enough */
468     while(sb->len+len+1>=sb->allocated) {
469       sb->allocated+=8192;
470     }
471     /* try to resize it */
472     unsigned char* resized=(unsigned char*)realloc(sb->data,sb->allocated);
473     if (resized) {
474       sb->data=resized;
475     } else {
476       free(sb->data);
477       sb->data=NULL;
478       sb->allocated=0;
479       rrd_set_error("realloc issue");
480       return -1;
481     }
482   }
483   /* and finally add to the buffer */
484   memcpy(sb->data+sb->len,data,len);
485   sb->len+=len;
486   /* and 0 terminate it */
487   sb->data[sb->len]=0;
488   /* and return */
489   return 0;
492 int rrd_xport_format_sv(char sep, stringbuffer_t *buffer,image_desc_t *im,time_t start, time_t end, unsigned long step, unsigned long col_cnt, char **legend_v, rrd_value_t* data) {
493   /* define the time format */
494   char* timefmt=NULL;
495   /* unfortunatley we have to do it this way, 
496      as when no --x-graph argument is given,
497      then the xlab_user is not in a clean state (e.g. zero-filled) */
498   if (im->xlab_user.minsec!=-1) { timefmt=im->xlab_user.stst; }
500   /* row count */
501   unsigned long row_cnt=(end-start)/step;
503   /* estimate buffer size (to avoid multiple allocations) */
504   buffer->allocated=
505     1024 /* bytes of overhead /header/footer */
506     +(12+19*col_cnt) /* 12 bytes overhead/line plus 19 bytes per column*/
507     *(1+row_cnt) /* number of columns + 1 (for header) */
508     ;
510   /* now start writing the header*/
511   if (addToBuffer(buffer,"\"time\"",6)) { return 1; }
512   char buf[256];
513   for(unsigned long i=0;i<col_cnt;i++) {
514     /* strip leading spaces */
515     char *t=legend_v[i]; while (isspace(*t)) { t++;}
516     /* and print it */
517     snprintf(buf,255,"%c\"%s\"",sep,t);
518     if (addToBuffer(buffer,buf,0)) { return 1;}
519   }
520   if (addToBuffer(buffer,"\r\n",2)) { return 1; }
521   /* and now write the data */
522   rrd_value_t *ptr=data;
523   for(time_t ti=start+step;ti<end;ti+=step) {
524     /* write time */
525     if (timefmt) {
526       struct tm loc;
527       localtime_r(&ti,&loc);
528       strftime(buf,254,timefmt,&loc);
529     } else {
530       snprintf(buf,254,"%lld",(long long int)ti);
531     }
532     if (addToBuffer(buffer,buf,0)) { return 1; }
533     /* write the columns */
534     for(unsigned long i=0;i<col_cnt;i++) {
535       /* get the value */
536       rrd_value_t v=*ptr;ptr++;
537       /* and print it */
538       if (isnan(v)) {
539         snprintf(buf,255,"%c\"NaN\"",sep);
540       } else {
541         snprintf(buf,255,"%c\"%0.10e\"",sep,v);
542       }
543       if (addToBuffer(buffer,buf,0)) { return 1;}
544     }
545     /* and add a newline */
546     if (addToBuffer(buffer,"\r\n",2)) { return 1; }
547   }
549   /* and return OK */
550   return 0;
553 int rrd_xport_format_xml(stringbuffer_t *buffer,image_desc_t *im,time_t start, time_t end, unsigned long step, unsigned long col_cnt, char **legend_v, rrd_value_t* data) {
554   addToBuffer(buffer,"xml not implemented",0);
555   return 0;
558 int rrd_xport_format_json(stringbuffer_t *buffer,image_desc_t *im,time_t start, time_t end, unsigned long step, unsigned long col_cnt, char **legend_v, rrd_value_t* data) {
559   addToBuffer(buffer,"JSON not implemented",0);
560   return 0;