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