fixed DEF_NAM_FMT definition
[rrdtool.git] / src / rrd_fetch.c
1 /*****************************************************************************
2  * RRDtool 1.1.x  Copyright Tobias Oetiker, 1997 - 2002
3  *****************************************************************************
4  * rrd_fetch.c  read date from an rrd to use for further processing
5  *****************************************************************************
6  * $Id$
7  * $Log$
8  * Revision 1.5  2002/06/23 22:29:40  alex
9  * Added "step=1800" and such to "DEF"
10  * Cleaned some of the signed vs. unsigned problems
11  *
12  * Revision 1.4  2002/02/01 20:34:49  oetiker
13  * fixed version number and date/time
14  *
15  * Revision 1.3  2001/12/24 06:51:49  alex
16  * A patch of size 44Kbytes... in short:
17  *
18  * Found and repaired the off-by-one error in rrd_fetch_fn().
19  * As a result I had to remove the hacks in rrd_fetch_fn(),
20  * rrd_tool.c, vdef_calc(), data_calc(), data_proc() and
21  * reduce_data().  There may be other places which I didn't
22  * find so be careful.
23  *
24  * Enhanced debugging in rrd_fetch_fn(), it shows the RRA selection
25  * process.
26  *
27  * Added the ability to print VDEF timestamps.  At the moment it
28  * is a hack, I needed it now to fix the off-by-one error.
29  * If the format string is "%c" (and nothing else!), the time
30  * will be printed by both ctime() and as a long int.
31  *
32  * Moved some code around (slightly altering it) from rrd_graph()
33  *   initializing     now in rrd_graph_init()
34  *   options parsing  now in rrd_graph_options()
35  *   script parsing   now in rrd_graph_script()
36  *
37  * Revision 1.2  2001/12/17 12:48:43  oetiker
38  * fix overflow error ...
39  *
40  * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
41  * checkin
42  *
43  *****************************************************************************/
44
45 #include "rrd_tool.h"
46 /*#define DEBUG*/
47
48 int
49 rrd_fetch(int argc, 
50           char **argv,
51           time_t         *start,
52           time_t         *end,       /* which time frame do you want ?
53                                       * will be changed to represent reality */
54           unsigned long  *step,      /* which stepsize do you want? 
55                                       * will be changed to represent reality */
56           unsigned long  *ds_cnt,    /* number of data sources in file */
57           char           ***ds_namv,   /* names of data sources */
58           rrd_value_t    **data)     /* two dimensional array containing the data */
59 {
60
61
62     long     step_tmp =1;
63     time_t   start_tmp=0, end_tmp=0;
64     enum     cf_en cf_idx;
65
66     struct time_value start_tv, end_tv;
67     char     *parsetime_error = NULL;
68
69     /* init start and end time */
70     parsetime("end-24h", &start_tv);
71     parsetime("now", &end_tv);
72
73     while (1){
74         static struct option long_options[] =
75         {
76             {"resolution",      required_argument, 0, 'r'},
77             {"start",      required_argument, 0, 's'},
78             {"end",      required_argument, 0, 'e'},
79             {0,0,0,0}
80         };
81         int option_index = 0;
82         int opt;
83         opt = getopt_long(argc, argv, "r:s:e:", 
84                           long_options, &option_index);
85
86         if (opt == EOF)
87             break;
88
89         switch(opt) {
90         case 's':
91             if ((parsetime_error = parsetime(optarg, &start_tv))) {
92                 rrd_set_error( "start time: %s", parsetime_error );
93                 return -1;
94             }
95             break;
96         case 'e':
97             if ((parsetime_error = parsetime(optarg, &end_tv))) {
98                 rrd_set_error( "end time: %s", parsetime_error );
99                 return -1;
100             }
101             break;
102         case 'r':
103             step_tmp = atol(optarg);
104             break;
105         case '?':
106             rrd_set_error("unknown option '-%c'",optopt);
107             return(-1);
108         }
109     }
110
111     
112     if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
113         return -1;
114     }  
115
116     
117     if (start_tmp < 3600*24*365*10){
118         rrd_set_error("the first entry to fetch should be after 1980");
119         return(-1);
120     }
121     
122     if (end_tmp < start_tmp) {
123         rrd_set_error("start (%ld) should be less than end (%ld)", start_tmp, end_tmp);
124         return(-1);
125     }
126     
127     *start = start_tmp;
128     *end = end_tmp;
129
130     if (step_tmp < 1) {
131         rrd_set_error("step must be >= 1 second");
132         return -1;
133     }
134     *step = step_tmp;
135     
136     if (optind + 1 >= argc){
137         rrd_set_error("not enough arguments");
138         return -1;
139     }
140     
141     if ((int)(cf_idx=cf_conv(argv[optind+1])) == -1 ){
142         return -1;
143     }
144
145     if (rrd_fetch_fn(argv[optind],cf_idx,start,end,step,ds_cnt,ds_namv,data) == -1)
146         return(-1);
147     return (0);
148 }
149
150 int
151 rrd_fetch_fn(
152     char           *filename,  /* name of the rrd */
153     enum cf_en     cf_idx,         /* which consolidation function ?*/
154     time_t         *start,
155     time_t         *end,       /* which time frame do you want ?
156                                 * will be changed to represent reality */
157     unsigned long  *step,      /* which stepsize do you want? 
158                                 * will be changed to represent reality */
159     unsigned long  *ds_cnt,    /* number of data sources in file */
160     char           ***ds_namv,   /* names of data_sources */
161     rrd_value_t    **data)     /* two dimensional array containing the data */
162 {
163     long           i,ii;
164     FILE           *in_file;
165     time_t         cal_start,cal_end, rra_start_time,rra_end_time;
166     long  best_full_rra=0, best_part_rra=0, chosen_rra=0, rra_pointer=0;
167     long  best_step_diff=0, tmp_step_diff=0, tmp_match=0, best_match=0;
168     long  full_match, rra_base;
169     long           start_offset, end_offset;
170     int            first_full = 1;
171     int            first_part = 1;
172     rrd_t     rrd;
173     rrd_value_t    *data_ptr;
174     unsigned long  rows = (*end - *start) / *step;
175
176 #ifdef DEBUG
177 fprintf(stderr,"Entered rrd_fetch_fn() searching for the best match\n");
178 fprintf(stderr,"Looking for: start %10lu end %10lu step %5lu rows  %lu\n",
179                                                 *start,*end,*step,rows);
180 #endif
181
182     if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1)
183         return(-1);
184     
185     /* when was the realy last update of this file ? */
186
187     if (((*ds_namv) = (char **) malloc(rrd.stat_head->ds_cnt * sizeof(char*)))==NULL){
188         rrd_set_error("malloc fetch ds_namv array");
189         rrd_free(&rrd);
190         fclose(in_file);
191         return(-1);
192     }
193     
194     for(i=0;(unsigned long)i<rrd.stat_head->ds_cnt;i++){
195         if ((((*ds_namv)[i]) = malloc(sizeof(char) * DS_NAM_SIZE))==NULL){
196             rrd_set_error("malloc fetch ds_namv entry");
197             rrd_free(&rrd);
198             free(*ds_namv);
199             fclose(in_file);
200             return(-1);
201         }
202         strncpy((*ds_namv)[i],rrd.ds_def[i].ds_nam,DS_NAM_SIZE-1);
203         (*ds_namv)[i][DS_NAM_SIZE-1]='\0';
204
205     }
206     
207     /* find the rra which best matches the requirements */
208     for(i=0;(unsigned)i<rrd.stat_head->rra_cnt;i++){
209         if(cf_conv(rrd.rra_def[i].cf_nam) == cf_idx){
210             
211             cal_end = (rrd.live_head->last_up - (rrd.live_head->last_up 
212                           % (rrd.rra_def[i].pdp_cnt 
213                              * rrd.stat_head->pdp_step)));
214             cal_start = (cal_end 
215                          - (rrd.rra_def[i].pdp_cnt 
216                             * rrd.rra_def[i].row_cnt
217                             * rrd.stat_head->pdp_step));
218
219             full_match = *end -*start;
220 #ifdef DEBUG
221 fprintf(stderr,"Considering: start %10lu end %10lu step %5lu ",
222                                                         cal_start,cal_end,
223                         rrd.stat_head->pdp_step * rrd.rra_def[i].pdp_cnt);
224 #endif
225             /* best full match */
226             if(cal_end >= *end 
227                && cal_start <= *start){
228                 tmp_step_diff = labs(*step - (rrd.stat_head->pdp_step
229                                          * rrd.rra_def[i].pdp_cnt));
230                 if (first_full || (tmp_step_diff < best_step_diff)){
231                     first_full=0;
232                     best_step_diff = tmp_step_diff;
233                     best_full_rra=i;
234 #ifdef DEBUG
235 fprintf(stderr,"best full match so far\n");
236 #endif
237                 } else {
238 #ifdef DEBUG
239 fprintf(stderr,"full match, not best\n");
240 #endif
241                 }
242                 
243             } else {
244                 /* best partial match */
245                 tmp_match = full_match;
246                 if (cal_start>*start)
247                     tmp_match -= (cal_start-*start);
248                 if (cal_end<*end)
249                     tmp_match -= (*end-cal_end);                
250                 if (first_part || best_match < tmp_match){
251 #ifdef DEBUG
252 fprintf(stderr,"best partial so far\n");
253 #endif
254                     first_part=0;
255                     best_match = tmp_match;
256                     best_part_rra =i;
257                 } else {
258 #ifdef DEBUG
259 fprintf(stderr,"partial match, not best\n");
260 #endif
261                 }
262             }
263         }
264     }
265
266     /* lets see how the matching went. */
267     if (first_full==0)
268         chosen_rra = best_full_rra;
269     else if (first_part==0)
270         chosen_rra = best_part_rra;
271     else {
272         rrd_set_error("the RRD does not contain an RRA matching the chosen CF");
273         rrd_free(&rrd);
274         fclose(in_file);
275         return(-1);
276     }
277         
278     /* set the wish parameters to their real values */
279     *step = rrd.stat_head->pdp_step * rrd.rra_def[chosen_rra].pdp_cnt;
280     *start -= (*start % *step);
281     if (*end % *step) *end += (*step - *end % *step);
282     rows = (*end - *start) / *step;
283
284 #ifdef DEBUG
285     fprintf(stderr,"We found:    start %10lu end %10lu step %5lu rows  %lu\n",
286                                                 *start,*end,*step,rows);
287 #endif
288
289 /* Start and end are now multiples of the step size.  The amount of
290 ** steps we want is (end-start)/step and *not* an extra one.
291 ** Reasoning:  if step is s and we want to graph from t to t+s,
292 ** we need exactly ((t+s)-t)/s rows.  The row to collect from the
293 ** database is the one with time stamp (t+s) which means t to t+s.
294 */
295     *ds_cnt =   rrd.stat_head->ds_cnt; 
296     if (((*data) = malloc(*ds_cnt * rows * sizeof(rrd_value_t)))==NULL){
297         rrd_set_error("malloc fetch data area");
298         for (i=0;(unsigned long)i<*ds_cnt;i++)
299               free((*ds_namv)[i]);
300         free(*ds_namv);
301         rrd_free(&rrd);
302         fclose(in_file);
303         return(-1);
304     }
305     
306     data_ptr=(*data);
307     
308     /* find base address of rra */
309     rra_base=ftell(in_file);
310     for(i=0;i<chosen_rra;i++)
311         rra_base += ( *ds_cnt
312                       * rrd.rra_def[i].row_cnt
313                       * sizeof(rrd_value_t));
314
315     /* find start and end offset */
316     rra_end_time = (rrd.live_head->last_up 
317                     - (rrd.live_head->last_up % *step));
318     rra_start_time = (rra_end_time
319                  - ( *step * (rrd.rra_def[chosen_rra].row_cnt-1)));
320     /* here's an error by one if we don't be careful */
321     start_offset =(long)(*start + *step - rra_start_time) / (long)*step;
322     end_offset = (long)(rra_end_time - *end ) / (long)*step; 
323 #ifdef DEBUG
324     fprintf(stderr,"rra_start %lu, rra_end %lu, start_off %li, end_off %li\n",
325             rra_start_time,rra_end_time,start_offset,end_offset);
326 #endif
327
328     /* fill the gap at the start if needs be */
329
330     if (start_offset <= 0)
331         rra_pointer = rrd.rra_ptr[chosen_rra].cur_row+1;
332     else 
333         rra_pointer = rrd.rra_ptr[chosen_rra].cur_row+1+start_offset;
334     
335     if(fseek(in_file,(rra_base 
336                    + (rra_pointer
337                       * *ds_cnt
338                       * sizeof(rrd_value_t))),SEEK_SET) != 0){
339         rrd_set_error("seek error in RRA");
340         for (i=0;(unsigned)i<*ds_cnt;i++)
341               free((*ds_namv)[i]);
342         free(*ds_namv);
343         rrd_free(&rrd);
344         free(*data);
345         *data = NULL;
346         fclose(in_file);
347         return(-1);
348
349     }
350 #ifdef DEBUG
351     fprintf(stderr,"First Seek: rra_base %lu rra_pointer %lu\n",
352             rra_base, rra_pointer);
353 #endif
354     /* step trough the array */
355
356     for (i=start_offset;
357          i< (signed)rrd.rra_def[chosen_rra].row_cnt - end_offset;
358          i++){
359         /* no valid data yet */
360         if (i<0) {
361 #ifdef DEBUG
362             fprintf(stderr,"pre fetch %li -- ",i);
363 #endif
364             for(ii=0;(unsigned)ii<*ds_cnt;ii++){
365                 *(data_ptr++) = DNAN;
366 #ifdef DEBUG
367                 fprintf(stderr,"%10.2f ",*(data_ptr-1));
368 #endif
369             }
370         } 
371         /* past the valid data area */
372         else if (i >= (signed)rrd.rra_def[chosen_rra].row_cnt) {
373 #ifdef DEBUG
374             fprintf(stderr,"post fetch %li -- ",i);
375 #endif
376             for(ii=0;(unsigned)ii<*ds_cnt;ii++){
377                 *(data_ptr++) = DNAN;
378 #ifdef DEBUG
379                 fprintf(stderr,"%10.2f ",*(data_ptr-1));
380 #endif
381             }
382         } else {
383             /* OK we are inside the valid area but the pointer has to 
384              * be wrapped*/
385             if (rra_pointer >= (signed)rrd.rra_def[chosen_rra].row_cnt) {
386                 rra_pointer -= rrd.rra_def[chosen_rra].row_cnt;
387                 if(fseek(in_file,(rra_base+rra_pointer
388                                * *ds_cnt
389                                * sizeof(rrd_value_t)),SEEK_SET) != 0){
390                     rrd_set_error("wrap seek in RRA did fail");
391                     for (ii=0;(unsigned)ii<*ds_cnt;ii++)
392                         free((*ds_namv)[ii]);
393                     free(*ds_namv);
394                     rrd_free(&rrd);
395                     free(*data);
396                     *data = NULL;
397                     fclose(in_file);
398                     return(-1);
399                 }
400 #ifdef DEBUG
401                 fprintf(stderr,"wrap seek ...\n");
402 #endif      
403             }
404             
405             if(fread(data_ptr,
406                      sizeof(rrd_value_t),
407                      *ds_cnt,in_file) != rrd.stat_head->ds_cnt){
408                 rrd_set_error("fetching cdp from rra");
409                 for (ii=0;(unsigned)ii<*ds_cnt;ii++)
410                     free((*ds_namv)[ii]);
411                 free(*ds_namv);
412                 rrd_free(&rrd);
413                 free(*data);
414                 *data = NULL;
415                 fclose(in_file);
416                 return(-1);
417             }
418 #ifdef DEBUG
419             fprintf(stderr,"post fetch %li -- ",i);
420             for(ii=0;ii<*ds_cnt;ii++)
421                 fprintf(stderr,"%10.2f ",*(data_ptr+ii));
422 #endif
423             data_ptr += *ds_cnt;
424             rra_pointer ++;
425         }
426 #ifdef DEBUG
427             fprintf(stderr,"\n");
428 #endif      
429         
430     }
431     rrd_free(&rrd);
432     fclose(in_file);
433     return(0);
434 }