new options --enumds and --unknwonaszero for xport
[rrdtool.git] / src / rrd_xport.c
1 /****************************************************************************
2  * RRDtool 1.2.15  Copyright by Tobi Oetiker, 1997-2006
3  ****************************************************************************
4  * rrd_xport.c  export RRD data 
5  ****************************************************************************/
6
7 #include <sys/stat.h>
8
9 #include "rrd_tool.h"
10 #include "rrd_graph.h"
11 #include "rrd_xport.h"
12 #include "unused.h"
13
14 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
15 #include <io.h>
16 #include <fcntl.h>
17 #endif
18
19
20 int rrd_xport(int, char **, int *,
21               time_t *, time_t *,
22               unsigned long *, unsigned long *,
23               char ***, rrd_value_t **);
24
25 int rrd_xport_fn(image_desc_t *,
26                  time_t *, time_t *,
27                  unsigned long *, unsigned long *,
28                  char ***, rrd_value_t **);
29
30
31
32
33 int 
34 rrd_xport(int argc, char **argv, int UNUSED(*xsize),
35           time_t         *start,
36           time_t         *end,        /* which time frame do you want ?
37                                        * will be changed to represent reality */
38           unsigned long  *step,       /* which stepsize do you want? 
39                                        * will be changed to represent reality */
40           unsigned long  *col_cnt,    /* number of data columns in the result */
41           char           ***legend_v, /* legend entries */
42           rrd_value_t    **data)      /* two dimensional array containing the data */
43
44 {
45
46     image_desc_t   im;
47     time_t         start_tmp=0,end_tmp=0;
48     struct rrd_time_value start_tv, end_tv;
49     char           *parsetime_error = NULL;
50     optind = 0; opterr = 0;  /* initialize getopt */
51
52     rrd_graph_init(&im);
53
54     parsetime("end-24h", &start_tv);
55     parsetime("now", &end_tv);
56
57     while (1){
58         static struct option long_options[] =
59         {
60             {"start",      required_argument, 0,  's'},
61             {"end",        required_argument, 0,  'e'},
62             {"maxrows",    required_argument, 0,  'm'},
63             {"step",       required_argument, 0,   261},
64             {"enumds",     no_argument,       0,   262}, /* these are handled in the frontend ... */
65             {"unknownaszero",  no_argument,       0,   263}, /* just ignore them here */
66             {0,0,0,0}
67         };
68         int option_index = 0;
69         int opt;
70         
71         opt = getopt_long(argc, argv, "s:e:m:",
72                           long_options, &option_index);
73
74         if (opt == EOF)
75             break;
76         
77         switch(opt) {
78         case 261:
79             im.step =  atoi(optarg);
80             break;
81         case 262:
82         case 263:
83             break;
84         case 's':
85             if ((parsetime_error = parsetime(optarg, &start_tv))) {
86                 rrd_set_error( "start time: %s", parsetime_error );
87                 return -1;
88             }
89             break;
90         case 'e':
91             if ((parsetime_error = parsetime(optarg, &end_tv))) {
92                 rrd_set_error( "end time: %s", parsetime_error );
93                 return -1;
94             }
95             break;
96         case 'm':
97             im.xsize = atol(optarg);
98             if (im.xsize < 10) {
99                 rrd_set_error("maxrows below 10 rows");
100                 return -1;
101             }
102             break;
103         case '?':
104             rrd_set_error("unknown option '%s'",argv[optind-1]);
105             return -1;
106         }
107     }
108
109     if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
110         return -1;
111     }  
112     
113     if (start_tmp < 3600*24*365*10){
114         rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
115         return -1;
116     }
117     
118     if (end_tmp < start_tmp) {
119         rrd_set_error("start (%ld) should be less than end (%ld)", 
120                start_tmp, end_tmp);
121         return -1;
122     }
123     
124     im.start = start_tmp;
125     im.end = end_tmp;
126     im.step = max((long)im.step, (im.end-im.start)/im.xsize);
127     
128     rrd_graph_script(argc,argv,&im,0);
129     if (rrd_test_error()) {
130         im_free(&im);
131         return -1;
132     }
133
134     if (im.gdes_c == 0){
135         rrd_set_error("can't make a graph without contents");
136         im_free(&im);
137         return(-1); 
138     }
139     
140     if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data) == -1){
141         im_free(&im);
142         return -1;
143     }
144
145     im_free(&im);
146     return 0;
147 }
148
149
150
151 int
152 rrd_xport_fn(image_desc_t *im,
153              time_t         *start,
154              time_t         *end,        /* which time frame do you want ?
155                                           * will be changed to represent reality */
156              unsigned long  *step,       /* which stepsize do you want? 
157                                           * will be changed to represent reality */
158              unsigned long  *col_cnt,    /* number of data columns in the result */
159              char           ***legend_v, /* legend entries */
160              rrd_value_t    **data)      /* two dimensional array containing the data */
161 {
162
163     int            i = 0, j = 0;
164     unsigned long  *ds_cnt;    /* number of data sources in file */
165     unsigned long  col, dst_row, row_cnt;
166     rrd_value_t    *srcptr, *dstptr;
167
168     unsigned long nof_xports = 0;
169     unsigned long xport_counter = 0;
170     unsigned long *ref_list;
171     rrd_value_t **srcptr_list;
172     char **legend_list;
173     int ii = 0;
174
175     time_t start_tmp = 0;
176     time_t end_tmp = 0;
177     unsigned long step_tmp = 1;
178
179     /* pull the data from the rrd files ... */
180     if(data_fetch(im)==-1)
181         return -1;
182
183     /* evaluate CDEF  operations ... */
184     if(data_calc(im)==-1)
185         return -1;
186
187     /* how many xports? */
188     for(i = 0; i < im->gdes_c; i++) {   
189         switch(im->gdes[i].gf) {
190         case GF_XPORT:
191           nof_xports++;
192           break;
193         default:
194           break;
195         }
196     }
197
198     if(nof_xports == 0) {
199       rrd_set_error("no XPORT found, nothing to do");
200       return -1;
201     }
202
203     /* a list of referenced gdes */
204     ref_list = malloc(sizeof(int) * nof_xports);
205     if(ref_list == NULL)
206       return -1;
207
208     /* a list to save pointers into each gdes data */
209     srcptr_list = malloc(sizeof(srcptr) * nof_xports);
210     if(srcptr_list == NULL) {
211       free(ref_list);
212       return -1;
213     }
214
215     /* a list to save pointers to the column's legend entry */
216     /* this is a return value! */
217     legend_list = malloc(sizeof(char *) * nof_xports);
218     if(legend_list == NULL) {
219       free(srcptr_list);
220       free(ref_list);
221       return -1;
222     }
223
224     /* find referenced gdes and save their index and */
225     /* a pointer into their data */
226     for(i = 0; i < im->gdes_c; i++) {   
227         switch(im->gdes[i].gf) {
228         case GF_XPORT:
229           ii = im->gdes[i].vidx;
230           if(xport_counter > nof_xports) {
231             rrd_set_error( "too many xports: should not happen. Hmmm");
232             free(srcptr_list);
233             free(ref_list);
234             free(legend_list);
235             return -1;
236           } 
237           srcptr_list[xport_counter] = im->gdes[ii].data;
238           ref_list[xport_counter++] = i;
239           break;
240         default:
241           break;
242         }
243     }
244
245     start_tmp = im->gdes[0].start;
246     end_tmp = im->gdes[0].end;
247     step_tmp = im->gdes[0].step;
248
249     /* fill some return values */
250     *col_cnt = nof_xports;
251     *start = start_tmp;
252     *end = end_tmp;
253     *step = step_tmp;
254
255     row_cnt = ((*end)-(*start))/(*step);
256
257     /* room for rearranged data */
258     /* this is a return value! */
259     if (((*data) = malloc((*col_cnt) * row_cnt * sizeof(rrd_value_t)))==NULL){
260         free(srcptr_list);
261         free(ref_list);
262         free(legend_list);
263         rrd_set_error("malloc xport data area");
264         return(-1);
265     }
266     dstptr = (*data);
267
268     j = 0;
269     for(i = 0; i < im->gdes_c; i++) {   
270         switch(im->gdes[i].gf) {
271         case GF_XPORT:
272           /* reserve room for one legend entry */
273           /* is FMT_LEG_LEN + 5 the correct size? */
274           if ((legend_list[j] = malloc(sizeof(char) * (FMT_LEG_LEN+5)))==NULL) {
275             free(srcptr_list);
276             free(ref_list);
277             free(*data);  *data = NULL;
278             while (--j > -1) free(legend_list[j]);
279             free(legend_list);
280             rrd_set_error("malloc xport legend entry");
281             return(-1);
282           }
283
284           if (im->gdes[i].legend)
285             /* omit bounds check, should have the same size */
286             strcpy (legend_list[j++], im->gdes[i].legend);
287           else
288             legend_list[j++][0] = '\0';
289
290           break;
291         default:
292           break;
293         }
294     }
295
296     /* fill data structure */
297     for(dst_row = 0; (int)dst_row < (int)row_cnt; dst_row++) {
298       for(i = 0; i < (int)nof_xports; i++) {
299         j = ref_list[i];
300         ii = im->gdes[j].vidx;
301         ds_cnt = &im->gdes[ii].ds_cnt;
302
303         srcptr = srcptr_list[i];
304         for(col = 0; col < (*ds_cnt); col++) {
305           rrd_value_t newval = DNAN;
306           newval = srcptr[col];
307
308           if (im->gdes[ii].ds_namv && im->gdes[ii].ds_nam) {
309             if(strcmp(im->gdes[ii].ds_namv[col],im->gdes[ii].ds_nam) == 0)
310               (*dstptr++) = newval;
311           } else {
312             (*dstptr++) = newval;
313           }
314
315         }
316         srcptr_list[i] += (*ds_cnt);
317       }
318     }
319
320     *legend_v = legend_list;
321     free(srcptr_list);
322     free(ref_list);
323     return 0;
324
325 }