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