fix freeing order when generating error messages. found by Dmitry V. Krivenok
[rrdtool.git] / src / rrd_restore.c
1 /*****************************************************************************
2  * RRDtool 1.3.2  Copyright by Tobi Oetiker, 1997-2009                    
3  *****************************************************************************
4  * rrd_restore.c  Contains logic to parse XML input and create an RRD file
5  *                initial libxml2 version of rrd_restore (c) by Florian octo Forster
6  *****************************************************************************
7  * $Id$
8  *************************************************************************** */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <libxml/parser.h>
14 #include <libxml/xmlreader.h>
15
16
17 #ifndef WIN32
18 #       include <unistd.h>     /* for off_t */
19 #else
20         typedef size_t ssize_t;
21         typedef long off_t;
22 #endif 
23
24 #include <fcntl.h>
25 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
26 # include <io.h>
27 # define open _open
28 # define close _close
29 #endif
30
31 #include "rrd_tool.h"
32 #include "rrd_rpncalc.h"
33
34 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof ((a)[0]))
35
36 static int opt_range_check = 0;
37 static int opt_force_overwrite = 0;
38
39 /*
40  * Helpers
41  */
42
43 /* skip all but tags. complain if we do not get the right tag */
44 /* dept -1 causes depth to be ignored */
45 static xmlChar* get_xml_element (
46     xmlTextReaderPtr reader
47     )
48 {
49     while(xmlTextReaderRead(reader)){
50         int type;
51         xmlChar *name;
52         type = xmlTextReaderNodeType(reader);
53         if (type == XML_READER_TYPE_TEXT){
54             xmlChar *value;
55             value = xmlTextReaderValue(reader);
56             rrd_set_error("line %d: expected element but found text '%s'",
57                           xmlTextReaderGetParserLineNumber(reader),value);
58             xmlFree(value);
59             return NULL;
60         }
61         /* skip all other non-elements */
62         if (type != XML_READER_TYPE_ELEMENT && type != XML_READER_TYPE_END_ELEMENT)
63             continue;
64
65         name = xmlTextReaderName(reader);
66         if (type == XML_READER_TYPE_END_ELEMENT){
67             xmlChar *temp;
68             xmlChar *temp2;            
69             temp = (xmlChar*)sprintf_alloc("/%s",name);
70             temp2 = xmlStrdup(temp);
71             free(temp);
72             xmlFree(name);            
73             return temp2;            
74         }
75         /* all seems well, return the happy news */
76         return name;
77     }
78     rrd_set_error("the xml ended while we were looking for an element");
79     return NULL;
80 } /* get_xml_element */
81
82 static void local_rrd_free (rrd_t *rrd)
83 {    
84     free(rrd->live_head);
85     free(rrd->stat_head);
86     free(rrd->ds_def);
87     free(rrd->rra_def); 
88     free(rrd->rra_ptr);
89     free(rrd->pdp_prep);
90     free(rrd->cdp_prep);
91     free(rrd->rrd_value);
92     free(rrd);
93 }
94
95
96 static int expect_element (
97     xmlTextReaderPtr reader,
98     char *exp_name)
99 {
100     xmlChar *name;
101     name = get_xml_element(reader);
102     if (!name)
103         return -1;    
104     if (xmlStrcasecmp(name,(xmlChar *)exp_name) != 0){
105         rrd_set_error("line %d: expected <%s> element but found <%s>",
106                       xmlTextReaderGetParserLineNumber(reader),name,exp_name);
107         xmlFree(name);            
108         return -1;            
109     }
110     xmlFree(name);    
111     return 0;    
112 } /* expect_element */
113
114 static int expect_element_end (
115     xmlTextReaderPtr reader,
116     char *exp_name)
117 {
118     xmlChar *name;
119     name = get_xml_element(reader);
120     if (name == NULL)
121         return -1;    
122     if (xmlStrcasecmp(name+1,(xmlChar *)exp_name) != 0 || name[0] != '/'){
123         rrd_set_error("line %d: expected </%s> end element but found <%s>",
124                       xmlTextReaderGetParserLineNumber(reader),exp_name,name);
125         xmlFree(name);            
126         return -1;            
127     }
128     xmlFree(name);    
129     return 0;    
130 } /* expect_element_end */
131
132
133 static xmlChar* get_xml_text (
134     xmlTextReaderPtr reader
135     )
136 {
137     while(xmlTextReaderRead(reader)){
138         xmlChar  *ret;    
139         xmlChar  *text;
140         xmlChar  *begin_ptr;
141         xmlChar  *end_ptr;
142         int type;        
143         type = xmlTextReaderNodeType(reader);
144         if (type == XML_READER_TYPE_ELEMENT){
145             xmlChar *name;
146             name = xmlTextReaderName(reader);
147             rrd_set_error("line %d: expected a value but found an <%s> element",
148                           xmlTextReaderGetParserLineNumber(reader),
149                           name);
150             xmlFree(name);            
151             return NULL;            
152         }
153         /* skip all other non-text */
154         if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT)
155             continue;
156         
157         text = xmlTextReaderValue(reader);
158
159         begin_ptr = text;
160         while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
161             begin_ptr++;
162         if (begin_ptr[0] == 0) {
163             xmlFree(text);
164             return xmlStrdup(BAD_CAST "");
165         }        
166         end_ptr = begin_ptr;
167         while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
168             end_ptr++;
169         end_ptr[0] = 0;
170         
171         ret = xmlStrdup(begin_ptr);
172         xmlFree(text);
173         return ret;
174     }
175     rrd_set_error("file ended while looking for text");
176     return NULL;
177 }  /* get_xml_text */ 
178
179
180 static int get_xml_string(
181     xmlTextReaderPtr reader,
182     char *value,
183     int max_len)
184 {
185     xmlChar *str;
186     str = get_xml_text(reader);
187     if (str != NULL){
188         strncpy(value,(char *)str,max_len);
189         xmlFree(str);
190         return 0;        
191     }
192     else
193         return -1;    
194 }
195
196  
197 static int get_xml_long(
198     xmlTextReaderPtr reader,
199     long *value)
200 {    
201     xmlChar *text;
202     long temp;    
203     if ((text = get_xml_text(reader)) != NULL){
204         errno = 0;        
205         temp = strtol((char *)text,NULL, 0);
206         if (errno>0){
207             rrd_set_error("ling %d: get_xml_long from '%s' %s",
208                           xmlTextReaderGetParserLineNumber(reader),
209                           text,rrd_strerror(errno));
210             xmlFree(text);            
211             return -1;
212         }
213         xmlFree(text);            
214         *value = temp;
215         return 0;
216     }
217     return -1;
218 } /* get_xml_long */
219
220 static int get_xml_ulong(
221     xmlTextReaderPtr reader,
222     unsigned long *value)
223 {
224     
225     xmlChar *text;
226     unsigned long temp;    
227     if ((text = get_xml_text(reader)) != NULL){
228         errno = 0;        
229         temp = strtoul((char *)text,NULL, 0);        
230         if (errno>0){
231             rrd_set_error("ling %d: get_xml_ulong from '%s' %s",
232                           xmlTextReaderGetParserLineNumber(reader),
233                           text,rrd_strerror(errno));
234             xmlFree(text);            
235             return -1;
236         }
237         xmlFree(text);
238         *value = temp;        
239         return 0;
240     }
241     return -1;
242 } /* get_xml_ulong */
243
244 #ifndef TIME_T_IS_LONG
245 static int get_xml_llong(
246     xmlTextReaderPtr reader,
247     long long *value)
248 {
249     
250     xmlChar *text;
251     long long temp;    
252     if ((text = get_xml_text(reader)) != NULL){
253         errno = 0;        
254         temp = strtoll((char *)text,NULL, 0);        
255         if (errno>0){
256             rrd_set_error("ling %d: get_xml_llong from '%s' %s",
257                           xmlTextReaderGetParserLineNumber(reader),
258                           text,rrd_strerror(errno));
259             xmlFree(text);            
260             return -1;
261         }
262         xmlFree(text);
263         *value = temp;        
264         return 0;
265     }
266     return -1;
267 } /* get_xml_llong */
268
269 #endif
270
271 static int get_xml_double(
272     xmlTextReaderPtr reader,
273     double *value)
274 {
275     
276     char *text;
277     double temp;    
278     if ((text = (char *)get_xml_text(reader))!= NULL){
279         if (strcasestr(text,"nan")){
280             *value = DNAN;
281             xmlFree(text);
282             return 0;            
283         }
284         else if (strcasestr(text,"-inf")){
285             *value = -DINF;
286             xmlFree(text);
287             return 0;            
288         }
289         else if (strcasestr(text,"+inf")
290                  || strcasestr(text,"inf")){
291             *value = DINF;
292             xmlFree(text);
293             return 0;            
294         }        
295         errno = 0;
296         temp = strtod((char *)text,NULL);
297         if (errno>0){
298             rrd_set_error("ling %d: get_xml_double from '%s' %s",
299                           xmlTextReaderGetParserLineNumber(reader),
300                           text,rrd_strerror(errno));
301             xmlFree(text);        
302             return -1;
303         }
304         xmlFree(text);        
305         *value = temp;
306         return 0;
307     }
308     return -1;
309 } /* get_xml_double */
310
311
312 static int value_check_range(
313     rrd_value_t *rrd_value,
314     const ds_def_t *ds_def)
315 {
316     double    min;
317     double    max;
318
319     if (opt_range_check == 0)
320         return (0);
321
322     min = ds_def->par[DS_min_val].u_val;
323     max = ds_def->par[DS_max_val].u_val;
324
325     if (((!isnan(min)) && (*rrd_value < min))
326         || ((!isnan(max)) && (*rrd_value > max)))
327         *rrd_value = DNAN;
328
329     return (0);
330 } /* int value_check_range */
331
332 /*
333  * Parse the <database> block within an RRA definition
334  */
335
336 static int parse_tag_rra_database_row(
337     xmlTextReaderPtr reader,
338     rrd_t *rrd,
339     rrd_value_t *rrd_value)
340 {
341     unsigned int values_count = 0;
342     int       status;
343     
344     status = 0;
345     for (values_count = 0;values_count <  rrd->stat_head->ds_cnt;values_count++){
346         if (expect_element(reader,"v") == 0){
347             status = get_xml_double(reader,rrd_value + values_count);
348             if (status == 0)
349                 value_check_range(rrd_value + values_count,
350                                   rrd->ds_def + values_count);
351             else
352                 break;            
353         } else
354             return -1;
355         if (expect_element(reader,"/v") == -1){
356             return -1;
357         }
358     }
359     return status;
360 }                       /* int parse_tag_rra_database_row */
361
362 static int parse_tag_rra_database(
363     xmlTextReaderPtr reader,
364     rrd_t *rrd )
365 {
366     rra_def_t *cur_rra_def;
367     unsigned int total_row_cnt;
368     int       status;
369     int       i;
370     xmlChar *element;
371
372     total_row_cnt = 0;
373     for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
374         total_row_cnt += rrd->rra_def[i].row_cnt;
375
376     cur_rra_def = rrd->rra_def + i;
377
378     status = 0;
379     while ((element = get_xml_element(reader)) != NULL){        
380         if (xmlStrcasecmp(element,(const xmlChar *)"row") == 0){
381            rrd_value_t *temp;
382            rrd_value_t *cur_rrd_value;
383            unsigned int total_values_count = rrd->stat_head->ds_cnt
384                * (total_row_cnt + 1);
385
386             /* Allocate space for the new values.. */
387             temp = (rrd_value_t *) realloc(rrd->rrd_value,
388                                            sizeof(rrd_value_t) *
389                                            total_values_count);
390             if (temp == NULL) {
391                 rrd_set_error("parse_tag_rra_database: realloc failed.");
392                 status = -1;
393                break;
394             }
395             rrd->rrd_value = temp;
396             cur_rrd_value = rrd->rrd_value
397                 + (rrd->stat_head->ds_cnt * total_row_cnt);
398             memset(cur_rrd_value, '\0',
399                    sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
400             total_row_cnt++;
401             cur_rra_def->row_cnt++;
402
403             status =
404                 parse_tag_rra_database_row(reader, rrd, cur_rrd_value);
405             if (status == 0)
406                 status =  expect_element(reader,"/row");
407         } /* if (xmlStrcasecmp(element,"row")) */
408         else {
409             if ( xmlStrcasecmp(element,(const xmlChar *)"/database") == 0){
410                 xmlFree(element);                
411                 break;
412             }
413             else {
414                 rrd_set_error("line %d: found unexpected tag: %s",
415                               xmlTextReaderGetParserLineNumber(reader),element);
416                 status = -1;
417             }
418         }
419         xmlFree(element);        
420         if (status != 0)
421             break;        
422     }
423     return (status);
424 }                       /* int parse_tag_rra_database */
425
426 /*
427  * Parse the <cdp_prep> block within an RRA definition
428  */
429 static int parse_tag_rra_cdp_prep_ds_history(
430     xmlTextReaderPtr reader,
431     cdp_prep_t *cdp_prep)
432 {
433     /* Make `history_buffer' the same size as the scratch area, plus the
434      * terminating NULL byte. */
435     xmlChar  *history;    
436     char     *history_ptr;
437     int       i;
438     if ((history = get_xml_text(reader)) != NULL){
439         history_ptr = (char *) (&cdp_prep->scratch[0]);
440         for (i = 0; history[i] != '\0'; i++)
441             history_ptr[i] = (history[i] == '1') ? 1 : 0;
442         xmlFree(history);        
443         return 0;        
444     }    
445     return -1;    
446 }  /* int parse_tag_rra_cdp_prep_ds_history */
447
448 static int parse_tag_rra_cdp_prep_ds(
449     xmlTextReaderPtr reader,
450     rrd_t *rrd,
451     cdp_prep_t *cdp_prep)
452 {
453     int       status;
454     xmlChar *element;
455     memset(cdp_prep, '\0', sizeof(cdp_prep_t));
456
457     status = -1;
458     
459     if (atoi(rrd->stat_head->version) == 1) {
460         cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
461         cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
462     }
463
464     while ((element = get_xml_element(reader)) != NULL){
465         if (xmlStrcasecmp(element, (const xmlChar *) "primary_value") == 0)
466             status =
467                 get_xml_double(reader,&cdp_prep->scratch[CDP_primary_val].u_val);
468         else if (xmlStrcasecmp(element, (const xmlChar *) "secondary_value") == 0)
469             status =
470                 get_xml_double(reader,&cdp_prep->scratch[CDP_secondary_val].u_val);
471         else if (xmlStrcasecmp(element, (const xmlChar *) "intercept") == 0)
472             status = get_xml_double(reader,
473                                           &cdp_prep->
474                                           scratch[CDP_hw_intercept].u_val);
475         else if (xmlStrcasecmp(element, (const xmlChar *) "last_intercept") ==
476                  0)
477             status =
478                 get_xml_double(reader,
479                                      &cdp_prep->
480                                      scratch[CDP_hw_last_intercept].u_val);
481         else if (xmlStrcasecmp(element, (const xmlChar *) "slope") == 0)
482             status = get_xml_double(reader,
483                                     &cdp_prep->scratch[CDP_hw_slope].
484                                     u_val);
485         else if (xmlStrcasecmp(element, (const xmlChar *) "last_slope") == 0)
486             status = get_xml_double(reader,
487                                     &cdp_prep->
488                                     scratch[CDP_hw_last_slope].u_val);
489         else if (xmlStrcasecmp(element, (const xmlChar *) "nan_count") == 0)
490             status = get_xml_ulong(reader,
491                                    &cdp_prep->
492                                    scratch[CDP_null_count].u_cnt);
493         else if (xmlStrcasecmp(element, (const xmlChar *) "last_nan_count") ==
494                  0)
495             status =
496                 get_xml_ulong(reader,
497                               &cdp_prep->
498                               scratch[CDP_last_null_count].u_cnt);
499         else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal") == 0)
500             status = get_xml_double(reader,
501                                     &cdp_prep->scratch[CDP_hw_seasonal].
502                                     u_val);
503         else if (xmlStrcasecmp(element, (const xmlChar *) "last_seasonal") ==
504                  0)
505             status =
506                 get_xml_double(reader,
507                                      &cdp_prep->scratch[CDP_hw_last_seasonal].
508                                      u_val);
509         else if (xmlStrcasecmp(element, (const xmlChar *) "init_flag") == 0)
510             status = get_xml_ulong(reader,
511                                         &cdp_prep->
512                                        scratch[CDP_init_seasonal].u_cnt);
513         else if (xmlStrcasecmp(element, (const xmlChar *) "history") == 0)
514             status = parse_tag_rra_cdp_prep_ds_history(reader, cdp_prep);
515         else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
516             status = get_xml_double(reader,
517                                     &cdp_prep->scratch[CDP_val].u_val);
518         else if (xmlStrcasecmp(element,
519                            (const xmlChar *) "unknown_datapoints") == 0)
520             status = get_xml_ulong(reader,
521                                         &cdp_prep->
522                                        scratch[CDP_unkn_pdp_cnt].u_cnt);
523         else if (xmlStrcasecmp(element,
524                                (const xmlChar *) "/ds") == 0){
525             xmlFree(element);            
526             break;
527         }        
528         else {
529             rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
530                           element);
531             status = -1;
532             xmlFree(element);            
533             break;            
534         }
535         if (status != 0){
536             xmlFree(element);
537             break;
538         }
539         status = expect_element_end(reader,(char *)element);
540         xmlFree(element);        
541         if (status != 0)
542             break;
543     }    
544     return (status);
545 }                       /* int parse_tag_rra_cdp_prep_ds */
546
547 static int parse_tag_rra_cdp_prep(
548     xmlTextReaderPtr reader,
549     rrd_t *rrd,
550     cdp_prep_t *cdp_prep)
551 {
552     int       status;
553
554     unsigned int ds_count;
555
556     status = 0;
557     for ( ds_count = 0; ds_count < rrd->stat_head->ds_cnt;ds_count++){
558         if (expect_element(reader,"ds") == 0) {
559             status = parse_tag_rra_cdp_prep_ds(reader, rrd,
560                                                cdp_prep + ds_count);
561             if (status != 0)
562                 break;
563         } else {
564             status = -1;            
565             break;
566         }        
567     }
568     if (status == 0)
569         status =  expect_element(reader,"/cdp_prep");
570     return (status);
571 }                       /* int parse_tag_rra_cdp_prep */
572
573 /*
574  * Parse the <params> block within an RRA definition
575  */
576 static int parse_tag_rra_params(
577     xmlTextReaderPtr reader,
578     rra_def_t *rra_def)
579 {
580     xmlChar *element;
581     int       status;
582
583     status = -1;
584     while ((element = get_xml_element(reader)) != NULL){
585         /*
586          * Parameters for CF_HWPREDICT
587          */
588         if (xmlStrcasecmp(element, (const xmlChar *) "hw_alpha") == 0)
589             status = get_xml_double(reader,
590                                           &rra_def->par[RRA_hw_alpha].u_val);
591         else if (xmlStrcasecmp(element, (const xmlChar *) "hw_beta") == 0)
592             status = get_xml_double(reader,
593                                           &rra_def->par[RRA_hw_beta].u_val);
594         else if (xmlStrcasecmp(element,
595                            (const xmlChar *) "dependent_rra_idx") == 0)
596             status = get_xml_ulong(reader,
597                                         &rra_def->
598                                        par[RRA_dependent_rra_idx].u_cnt);
599         /*
600          * Parameters for CF_SEASONAL and CF_DEVSEASONAL
601          */
602         else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal_gamma") ==
603                  0)
604             status =
605                 get_xml_double(reader,
606                                      &rra_def->par[RRA_seasonal_gamma].u_val);
607         else if (xmlStrcasecmp
608                  (element, (const xmlChar *) "seasonal_smooth_idx") == 0)
609             status =
610                 get_xml_ulong(reader,
611                                    &rra_def->
612                                   par[RRA_seasonal_smooth_idx].u_cnt);
613         else if (xmlStrcasecmp(element, (const xmlChar *) "smoothing_window")
614                  == 0)
615             status =
616                 get_xml_double(reader,
617                                      &rra_def->
618                                      par[RRA_seasonal_smoothing_window].
619                                      u_val);
620         /* else if (dependent_rra_idx) ...; */
621         /*
622          * Parameters for CF_FAILURES
623          */
624         else if (xmlStrcasecmp(element, (const xmlChar *) "delta_pos") == 0)
625             status = get_xml_double(reader,
626                                           &rra_def->par[RRA_delta_pos].u_val);
627         else if (xmlStrcasecmp(element, (const xmlChar *) "delta_neg") == 0)
628             status = get_xml_double(reader,
629                                           &rra_def->par[RRA_delta_neg].u_val);
630         else if (xmlStrcasecmp(element, (const xmlChar *) "window_len") == 0)
631             status = get_xml_ulong(reader,
632                                         &rra_def->par[RRA_window_len].
633                                        u_cnt);
634         else if (xmlStrcasecmp(element, (const xmlChar *) "failure_threshold")
635                  == 0)
636             status =
637                 get_xml_ulong(reader,
638                                    &rra_def->
639                                   par[RRA_failure_threshold].u_cnt);
640         /*
641          * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
642          */
643         else if (xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
644             status = get_xml_double(reader,
645                                           &rra_def->par[RRA_cdp_xff_val].
646                                           u_val);
647         /*
648          * Compatibility code for 1.0.49
649          */
650         else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0) {  /* {{{ */
651             unsigned int i = 0;
652
653             for (i=0;i<ARRAY_LENGTH(rra_def->par);i++){
654                 if ((i == RRA_dependent_rra_idx)
655                     || (i == RRA_seasonal_smooth_idx)
656                     || (i == RRA_failure_threshold))
657                     status = get_xml_ulong(reader,
658                                                 &rra_def->par[i].
659                                                u_cnt);
660                 else
661                     status = get_xml_double(reader,
662                                                   &rra_def->par[i].u_val);
663
664                 if (status != 0)
665                     break;
666                 if ( i-1 < ARRAY_LENGTH(rra_def->par)){
667                     status = expect_element(reader,"/value");
668                     if (status == 0){
669                         status  = expect_element(reader,"value");
670                     }
671                 }
672                 if (status != 0){
673                     break;                    
674                 }
675             }
676         }  /* }}} */        
677         else if (xmlStrcasecmp(element,(const xmlChar *) "/params") == 0){
678             xmlFree(element);            
679             return status;
680         }  /* }}} */        
681         else {
682             rrd_set_error("line %d: parse_tag_rra_params: Unknown tag: %s",
683                           xmlTextReaderGetParserLineNumber(reader),element);
684             status = -1;
685         }
686         status = expect_element_end(reader,(char *)element);
687         xmlFree(element);        
688         if (status != 0)
689             break;
690     }
691     return (status);
692 }                       /* int parse_tag_rra_params */
693
694 /*
695  * Parse an RRA definition
696  */
697 static int parse_tag_rra_cf(
698     xmlTextReaderPtr reader,
699     rra_def_t *rra_def)
700 {
701     int       status;
702
703     status = get_xml_string(reader,
704                                   rra_def->cf_nam, sizeof(rra_def->cf_nam));
705     if (status != 0)
706         return status;
707
708     status = cf_conv(rra_def->cf_nam);
709     if (status == -1) {
710         rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
711                       rra_def->cf_nam);
712         return -1;
713     }
714
715     return 0;
716 }                       /* int parse_tag_rra_cf */
717
718 static int parse_tag_rra(
719     xmlTextReaderPtr reader,
720     rrd_t *rrd)
721 {
722     int       status;
723     xmlChar *element;
724     
725     rra_def_t *cur_rra_def;
726     cdp_prep_t *cur_cdp_prep;
727     rra_ptr_t *cur_rra_ptr;
728
729     /* Allocate more rra_def space for this RRA */
730     {                   /* {{{ */
731         rra_def_t *temp;
732
733         temp = (rra_def_t *) realloc(rrd->rra_def,
734                                      sizeof(rra_def_t) *
735                                      (rrd->stat_head->rra_cnt + 1));
736         if (temp == NULL) {
737             rrd_set_error("parse_tag_rra: realloc failed.");
738             return (-1);
739         }
740         rrd->rra_def = temp;
741         cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
742         memset(cur_rra_def, '\0', sizeof(rra_def_t));
743     }                   /* }}} */
744
745     /* allocate cdp_prep_t */
746     {                   /* {{{ */
747         cdp_prep_t *temp;
748
749         temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
750                                       * rrd->stat_head->ds_cnt
751                                       * (rrd->stat_head->rra_cnt + 1));
752         if (temp == NULL) {
753             rrd_set_error("parse_tag_rra: realloc failed.");
754             return (-1);
755         }
756         rrd->cdp_prep = temp;
757         cur_cdp_prep = rrd->cdp_prep
758             + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
759         memset(cur_cdp_prep, '\0',
760                sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
761     }                   /* }}} */
762
763     /* allocate rra_ptr_t */
764     {                   /* {{{ */
765         rra_ptr_t *temp;
766
767         temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
768                                      sizeof(rra_ptr_t) *
769                                      (rrd->stat_head->rra_cnt + 1));
770         if (temp == NULL) {
771             rrd_set_error("parse_tag_rra: realloc failed.");
772             return (-1);
773         }
774         rrd->rra_ptr = temp;
775         cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
776         memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
777     }                   /* }}} */
778
779     /* All space successfully allocated, increment number of RRAs. */
780     rrd->stat_head->rra_cnt++;
781
782     status = 0;
783     while ((element = get_xml_element(reader)) != NULL){
784         if (xmlStrcasecmp(element, (const xmlChar *) "cf") == 0)
785             status = parse_tag_rra_cf(reader, cur_rra_def);
786         else if (xmlStrcasecmp(element, (const xmlChar *) "pdp_per_row") == 0)
787             status = get_xml_ulong(reader,
788                                         &cur_rra_def->pdp_cnt);
789         else if (atoi(rrd->stat_head->version) == 1
790                  && xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
791             status = get_xml_double(reader,
792                                           (double *) &cur_rra_def->
793                                           par[RRA_cdp_xff_val].u_val);
794         else if (atoi(rrd->stat_head->version) >= 2
795                  && xmlStrcasecmp(element, (const xmlChar *) "params") == 0){            
796             xmlFree(element);
797             status = parse_tag_rra_params(reader, cur_rra_def);
798             if (status == 0)
799                 continue;
800             else
801                 return status;
802         }
803         else if (xmlStrcasecmp(element, (const xmlChar *) "cdp_prep") == 0){
804             xmlFree(element);
805             status = parse_tag_rra_cdp_prep(reader, rrd, cur_cdp_prep);
806             if (status == 0)
807                 continue;
808             else
809                 return status;
810         }        
811         else if (xmlStrcasecmp(element, (const xmlChar *) "database") == 0){            
812             xmlFree(element);
813             status = parse_tag_rra_database(reader, rrd);
814             if (status == 0)
815                 continue;
816             else
817                 return status;
818         }
819         else if (xmlStrcasecmp(element,(const xmlChar *) "/rra") == 0){
820             xmlFree(element);            
821             return status;
822         }  /* }}} */        
823        else {
824             rrd_set_error("line %d: parse_tag_rra: Unknown tag: %s",
825                           xmlTextReaderGetParserLineNumber(reader), element);
826             status = -1;            
827         }
828         if (status != 0) {
829             xmlFree(element);
830             return status;
831         }        
832         status = expect_element_end(reader,(char *)element);
833         xmlFree(element);
834         if (status != 0) {
835             return status;
836         }        
837     }    
838     /* Set the RRA pointer to a random location */
839     cur_rra_ptr->cur_row = rrd_random() % cur_rra_def->row_cnt;
840
841     return (status);
842 }                       /* int parse_tag_rra */
843
844 /*
845  * Parse a DS definition
846  */
847 static int parse_tag_ds_cdef(
848     xmlTextReaderPtr reader,
849     rrd_t *rrd)
850 {
851     xmlChar *cdef;
852
853     cdef = get_xml_text(reader);
854     if (cdef != NULL){
855         /* We're always working on the last DS that has been added to the structure
856          * when we get here */
857         parseCDEF_DS((char *)cdef, rrd, rrd->stat_head->ds_cnt - 1);
858         xmlFree(cdef);
859         if (rrd_test_error())
860             return -1;
861         else            
862             return 0;        
863     }
864     return -1;
865 }                       /* int parse_tag_ds_cdef */
866
867 static int parse_tag_ds_type(
868     xmlTextReaderPtr reader,
869     ds_def_t *ds_def)
870 {
871     char *dst;
872     dst = (char *)get_xml_text(reader);
873     if (dst != NULL){
874         int status;
875         status = dst_conv(dst);
876         if (status == -1) {
877             rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
878                           dst);
879             return -1;
880         }
881         strncpy(ds_def->dst,dst,sizeof(ds_def->dst)-1);
882         ds_def->dst[sizeof(ds_def->dst)-1] = '\0';
883         xmlFree(dst);
884         return 0;        
885     }
886     return -1;
887 }                       /* int parse_tag_ds_type */
888
889 static int parse_tag_ds(
890     xmlTextReaderPtr reader,
891     rrd_t *rrd)
892 {
893     int       status;
894     xmlChar  *element;
895     
896     ds_def_t *cur_ds_def;
897     pdp_prep_t *cur_pdp_prep;
898
899     /*
900      * If there are DS definitions after RRA definitions the number of values,
901      * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
902      * specific order in this case.
903      */
904     if (rrd->stat_head->rra_cnt > 0) {
905         rrd_set_error("parse_tag_ds: All data source definitions MUST "
906                       "precede the RRA definitions!");
907         return (-1);
908     }
909
910     /* Allocate space for the new DS definition */
911     {                   /* {{{ */
912         ds_def_t *temp;
913
914         temp = (ds_def_t *) realloc(rrd->ds_def,
915                                     sizeof(ds_def_t) *
916                                     (rrd->stat_head->ds_cnt + 1));
917         if (temp == NULL) {
918             rrd_set_error("parse_tag_ds: malloc failed.");
919             return (-1);
920         }
921         rrd->ds_def = temp;
922         cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
923         memset(cur_ds_def, '\0', sizeof(ds_def_t));
924     }                   /* }}} */
925
926     /* Allocate pdp_prep space for the new DS definition */
927     {                   /* {{{ */
928         pdp_prep_t *temp;
929
930         temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
931                                       sizeof(pdp_prep_t) *
932                                       (rrd->stat_head->ds_cnt + 1));
933         if (temp == NULL) {
934             rrd_set_error("parse_tag_ds: malloc failed.");
935             return (-1);
936         }
937         rrd->pdp_prep = temp;
938         cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
939         memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
940     }                   /* }}} */
941
942     /* All allocations successful, let's increment the number of DSes. */
943     rrd->stat_head->ds_cnt++;
944
945     status = 0;
946     while ((element = get_xml_element(reader)) != NULL){
947         if (xmlStrcasecmp(element, (const xmlChar *) "name") == 0){
948             status = get_xml_string(reader,cur_ds_def->ds_nam,sizeof(cur_ds_def->ds_nam));
949         }
950         else if (xmlStrcasecmp(element, (const xmlChar *) "type") == 0)
951             status = parse_tag_ds_type(reader, cur_ds_def);
952         else if (xmlStrcasecmp(element,
953                            (const xmlChar *) "minimal_heartbeat") == 0)
954             status = get_xml_ulong(reader,
955                                         &cur_ds_def->par[DS_mrhb_cnt].
956                                        u_cnt);
957         else if (xmlStrcasecmp(element, (const xmlChar *) "min") == 0)
958             status = get_xml_double(reader,
959                                           &cur_ds_def->par[DS_min_val].u_val);
960         else if (xmlStrcasecmp(element, (const xmlChar *) "max") == 0)
961             status = get_xml_double(reader,
962                                           &cur_ds_def->par[DS_max_val].u_val);
963         else if (xmlStrcasecmp(element, (const xmlChar *) "cdef") == 0)
964             status = parse_tag_ds_cdef(reader, rrd);
965         else if (xmlStrcasecmp(element, (const xmlChar *) "last_ds") == 0)
966             status = get_xml_string(reader,
967                                           cur_pdp_prep->last_ds,
968                                           sizeof(cur_pdp_prep->last_ds));
969         else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
970             status = get_xml_double(reader,
971                                           &cur_pdp_prep->scratch[PDP_val].
972                                           u_val);
973         else if (xmlStrcasecmp(element, (const xmlChar *) "unknown_sec") == 0)
974             status = get_xml_ulong(reader,
975                                         &cur_pdp_prep->
976                                        scratch[PDP_unkn_sec_cnt].u_cnt);
977         else if (xmlStrcasecmp(element, (const xmlChar *) "/ds") == 0) {
978             xmlFree(element);            
979             break;
980         }        
981         else {
982             rrd_set_error("parse_tag_ds: Unknown tag: %s", element);
983             status = -1;
984         }        
985         if (status != 0) {            
986             xmlFree(element);        
987             break;
988         }
989         status = expect_element_end(reader,(char *)element);
990         xmlFree(element);        
991         if (status != 0)
992             break;        
993     }
994     
995     return (status);
996 }                       /* int parse_tag_ds */
997
998 /*
999  * Parse root nodes
1000  */
1001 static int parse_tag_rrd(
1002     xmlTextReaderPtr reader,
1003     rrd_t *rrd)
1004 {
1005     int       status;
1006     xmlChar *element;
1007     
1008     status = 0;
1009     while ((element = get_xml_element(reader)) != NULL ){
1010         if (xmlStrcasecmp(element, (const xmlChar *) "version") == 0)
1011             status = get_xml_string(reader,
1012                                           rrd->stat_head->version,
1013                                           sizeof(rrd->stat_head->version));
1014         else if (xmlStrcasecmp(element, (const xmlChar *) "step") == 0)
1015             status = get_xml_ulong(reader,
1016                                         &rrd->stat_head->pdp_step);
1017         else if (xmlStrcasecmp(element, (const xmlChar *) "lastupdate") == 0) {
1018 #ifdef TIME_T_IS_LONG
1019                 status = get_xml_long(reader, &rrd->live_head->last_up);
1020 #else
1021 #ifdef TIME_T_IS_LONG_LONG
1022                 status = get_xml_llong(reader, &rrd->live_head->last_up); 
1023 #else
1024             if (sizeof(time_t) == sizeof(long)) {
1025                 status = get_xml_long(reader,
1026                                         (long *)&rrd->live_head->last_up);
1027             }
1028             else if (sizeof(time_t) == sizeof(long long)) {
1029                 status = get_xml_llong(reader,
1030                                         (long long *)&rrd->live_head->last_up); 
1031             }
1032 #endif
1033 #endif
1034         }
1035         else if (xmlStrcasecmp(element, (const xmlChar *) "ds") == 0){            
1036             xmlFree(element);
1037             status = parse_tag_ds(reader, rrd);
1038             /* as we come back the </ds> tag is already gone */
1039             if (status == 0)
1040                 continue;
1041             else
1042                 return status;
1043         }        
1044         else if (xmlStrcasecmp(element, (const xmlChar *) "rra") == 0){            
1045             xmlFree(element);
1046             status = parse_tag_rra(reader, rrd);
1047             if (status == 0)
1048                 continue;
1049             else
1050                 return status;
1051         }
1052         else if (xmlStrcasecmp(element, (const xmlChar *) "/rrd") == 0) {
1053             xmlFree(element);
1054             return status;
1055         }
1056         else {
1057             rrd_set_error("parse_tag_rrd: Unknown tag: %s", element);
1058             status = -1;
1059         }
1060
1061         if (status != 0){
1062             xmlFree(element);
1063             break;
1064         }        
1065         status = expect_element_end(reader,(char *)element);
1066         xmlFree(element);        
1067         if (status != 0)
1068             break;        
1069     }
1070     return (status);
1071 }                       /* int parse_tag_rrd */
1072
1073 static rrd_t *parse_file(
1074     const char *filename)
1075 {
1076     xmlTextReaderPtr reader;
1077     int       status;
1078
1079     rrd_t    *rrd;
1080
1081     reader = xmlNewTextReaderFilename(filename);
1082     if (reader == NULL) {
1083         rrd_set_error("Could not create xml reader for: %s",filename);
1084         return (NULL);
1085     }
1086
1087     if (expect_element(reader,"rrd") != 0) {
1088         xmlFreeTextReader(reader);
1089         return (NULL);
1090     }
1091
1092     rrd = (rrd_t *) malloc(sizeof(rrd_t));
1093     if (rrd == NULL) {
1094         rrd_set_error("parse_file: malloc failed.");
1095         xmlFreeTextReader(reader);
1096         return (NULL);
1097     }
1098     memset(rrd, '\0', sizeof(rrd_t));
1099
1100     rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
1101     if (rrd->stat_head == NULL) {
1102         rrd_set_error("parse_tag_rrd: malloc failed.");
1103         xmlFreeTextReader(reader);
1104         free(rrd);
1105         return (NULL);
1106     }
1107     memset(rrd->stat_head, '\0', sizeof(stat_head_t));
1108
1109     strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
1110     rrd->stat_head->float_cookie = FLOAT_COOKIE;
1111
1112     rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
1113     if (rrd->live_head == NULL) {
1114         rrd_set_error("parse_tag_rrd: malloc failed.");
1115         xmlFreeTextReader(reader);
1116         free(rrd->stat_head);
1117         free(rrd);
1118         return (NULL);
1119     }
1120     memset(rrd->live_head, '\0', sizeof(live_head_t));
1121
1122     status = parse_tag_rrd(reader, rrd);
1123
1124     xmlFreeTextReader(reader);
1125
1126     if (status != 0) {
1127         local_rrd_free(rrd);
1128         rrd = NULL;
1129     }
1130
1131     return (rrd);
1132 }                       /* rrd_t *parse_file */
1133
1134 static int write_file(
1135     const char *file_name,
1136     rrd_t *rrd)
1137 {
1138     FILE     *fh;
1139     unsigned int i;
1140     unsigned int rra_offset;
1141
1142     if (strcmp("-", file_name) == 0)
1143         fh = stdout;
1144     else {
1145         int       fd_flags = O_WRONLY | O_CREAT;
1146         int       fd;
1147
1148 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1149         fd_flags |= O_BINARY;
1150 #endif
1151
1152         if (opt_force_overwrite == 0)
1153             fd_flags |= O_EXCL;
1154
1155         fd = open(file_name, fd_flags, 0666);
1156         if (fd == -1) {
1157             rrd_set_error("creating '%s': %s", file_name,
1158                           rrd_strerror(errno));
1159             return (-1);
1160         }
1161
1162         fh = fdopen(fd, "wb");
1163         if (fh == NULL) {
1164             rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1165             close(fd);
1166             return (-1);
1167         }
1168     }
1169     if (atoi(rrd->stat_head->version) < 3) {
1170         /* we output 3 or higher */
1171         strcpy(rrd->stat_head->version, "0003");
1172     }
1173     fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1174     fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1175     fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1176     fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1177     fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1178     fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1179            rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1180     fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1181
1182     /* calculate the number of rrd_values to dump */
1183     rra_offset = 0;
1184     for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
1185         unsigned long num_rows = rrd->rra_def[i].row_cnt;
1186         unsigned long cur_row = rrd->rra_ptr[i].cur_row;
1187         unsigned long ds_cnt = rrd->stat_head->ds_cnt;
1188
1189         fwrite(rrd->rrd_value +
1190                (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1191                sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1192
1193         fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1194                sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1195
1196         rra_offset += num_rows;
1197     }
1198
1199     /* lets see if we had an error */
1200     if (ferror(fh)) {
1201         rrd_set_error("a file error occurred while creating '%s'", file_name);
1202         fclose(fh);
1203         return (-1);
1204     }
1205
1206     fclose(fh);
1207     return (0);
1208 }                       /* int write_file */
1209
1210 int rrd_restore(
1211     int argc,
1212     char **argv)
1213 {
1214     rrd_t    *rrd;
1215
1216     /* init rrd clean */
1217     optind = 0;
1218     opterr = 0;         /* initialize getopt */
1219     while (42) {
1220         int       opt;
1221         int       option_index = 0;
1222         static struct option long_options[] = {
1223             {"range-check", no_argument, 0, 'r'},
1224             {"force-overwrite", no_argument, 0, 'f'},
1225             {0, 0, 0, 0}
1226         };
1227
1228         opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1229
1230         if (opt == EOF)
1231             break;
1232
1233         switch (opt) {
1234         case 'r':
1235             opt_range_check = 1;
1236             break;
1237
1238         case 'f':
1239             opt_force_overwrite = 1;
1240             break;
1241
1242         default:
1243             rrd_set_error("usage rrdtool %s [--range-check|-r] "
1244                           "[--force-overwrite/-f]  file.xml file.rrd",
1245                           argv[0]);
1246             return (-1);
1247             break;
1248         }
1249     }                   /* while (42) */
1250
1251     if ((argc - optind) != 2) {
1252         rrd_set_error("usage rrdtool %s [--range-check/-r] "
1253                       "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1254         return (-1);
1255     }
1256
1257     rrd = parse_file(argv[optind]);
1258     if (rrd == NULL)
1259         return (-1);
1260     
1261     if (write_file(argv[optind + 1], rrd) != 0) {
1262         local_rrd_free(rrd);
1263         return (-1);
1264     }
1265     local_rrd_free(rrd);
1266
1267
1268     return (0);
1269 }                       /* int rrd_restore */
1270
1271 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */