4cbc37adc45ebc08de59ab2fb80822055d2c7cfd
[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         xmlFree(text);        
298         if (errno>0){
299             rrd_set_error("ling %d: get_xml_double from '%s' %s",
300                           xmlTextReaderGetParserLineNumber(reader),
301                           text,rrd_strerror(errno));
302             return -1;
303         }
304         *value = temp;
305         return 0;
306     }
307     return -1;
308 } /* get_xml_double */
309
310
311 static int value_check_range(
312     rrd_value_t *rrd_value,
313     const ds_def_t *ds_def)
314 {
315     double    min;
316     double    max;
317
318     if (opt_range_check == 0)
319         return (0);
320
321     min = ds_def->par[DS_min_val].u_val;
322     max = ds_def->par[DS_max_val].u_val;
323
324     if (((!isnan(min)) && (*rrd_value < min))
325         || ((!isnan(max)) && (*rrd_value > max)))
326         *rrd_value = DNAN;
327
328     return (0);
329 } /* int value_check_range */
330
331 /*
332  * Parse the <database> block within an RRA definition
333  */
334
335 static int parse_tag_rra_database_row(
336     xmlTextReaderPtr reader,
337     rrd_t *rrd,
338     rrd_value_t *rrd_value)
339 {
340     unsigned int values_count = 0;
341     int       status;
342     
343     status = 0;
344     for (values_count = 0;values_count <  rrd->stat_head->ds_cnt;values_count++){
345         if (expect_element(reader,"v") == 0){
346             status = get_xml_double(reader,rrd_value + values_count);
347             if (status == 0)
348                 value_check_range(rrd_value + values_count,
349                                   rrd->ds_def + values_count);
350             else
351                 break;            
352         } else
353             return -1;
354         if (expect_element(reader,"/v") == -1){
355             return -1;
356         }
357     }
358     return status;
359 }                       /* int parse_tag_rra_database_row */
360
361 static int parse_tag_rra_database(
362     xmlTextReaderPtr reader,
363     rrd_t *rrd )
364 {
365     rra_def_t *cur_rra_def;
366     unsigned int total_row_cnt;
367     int       status;
368     int       i;
369     xmlChar *element;
370
371     total_row_cnt = 0;
372     for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
373         total_row_cnt += rrd->rra_def[i].row_cnt;
374
375     cur_rra_def = rrd->rra_def + i;
376
377     status = 0;
378     while ((element = get_xml_element(reader)) != NULL){        
379         if (xmlStrcasecmp(element,(const xmlChar *)"row") == 0){
380            rrd_value_t *temp;
381            rrd_value_t *cur_rrd_value;
382            unsigned int total_values_count = rrd->stat_head->ds_cnt
383                * (total_row_cnt + 1);
384
385             /* Allocate space for the new values.. */
386             temp = (rrd_value_t *) realloc(rrd->rrd_value,
387                                            sizeof(rrd_value_t) *
388                                            total_values_count);
389             if (temp == NULL) {
390                 rrd_set_error("parse_tag_rra_database: realloc failed.");
391                 status = -1;
392                break;
393             }
394             rrd->rrd_value = temp;
395             cur_rrd_value = rrd->rrd_value
396                 + (rrd->stat_head->ds_cnt * total_row_cnt);
397             memset(cur_rrd_value, '\0',
398                    sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
399             total_row_cnt++;
400             cur_rra_def->row_cnt++;
401
402             status =
403                 parse_tag_rra_database_row(reader, rrd, cur_rrd_value);
404             if (status == 0)
405                 status =  expect_element(reader,"/row");
406         } /* if (xmlStrcasecmp(element,"row")) */
407         else {
408             if ( xmlStrcasecmp(element,(const xmlChar *)"/database") == 0){
409                 xmlFree(element);                
410                 break;
411             }
412             else {
413                 rrd_set_error("line %d: found unexpected tag: %s",
414                               xmlTextReaderGetParserLineNumber(reader),element);
415                 status = -1;
416             }
417         }
418         xmlFree(element);        
419         if (status != 0)
420             break;        
421     }
422     return (status);
423 }                       /* int parse_tag_rra_database */
424
425 /*
426  * Parse the <cdp_prep> block within an RRA definition
427  */
428 static int parse_tag_rra_cdp_prep_ds_history(
429     xmlTextReaderPtr reader,
430     cdp_prep_t *cdp_prep)
431 {
432     /* Make `history_buffer' the same size as the scratch area, plus the
433      * terminating NULL byte. */
434     xmlChar  *history;    
435     char     *history_ptr;
436     int       i;
437     if ((history = get_xml_text(reader)) != NULL){
438         history_ptr = (char *) (&cdp_prep->scratch[0]);
439         for (i = 0; history[i] != '\0'; i++)
440             history_ptr[i] = (history[i] == '1') ? 1 : 0;
441         xmlFree(history);        
442         return 0;        
443     }    
444     return -1;    
445 }  /* int parse_tag_rra_cdp_prep_ds_history */
446
447 static int parse_tag_rra_cdp_prep_ds(
448     xmlTextReaderPtr reader,
449     rrd_t *rrd,
450     cdp_prep_t *cdp_prep)
451 {
452     int       status;
453     xmlChar *element;
454     memset(cdp_prep, '\0', sizeof(cdp_prep_t));
455
456     status = -1;
457     
458     if (atoi(rrd->stat_head->version) == 1) {
459         cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
460         cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
461     }
462
463     while ((element = get_xml_element(reader)) != NULL){
464         if (xmlStrcasecmp(element, (const xmlChar *) "primary_value") == 0)
465             status =
466                 get_xml_double(reader,&cdp_prep->scratch[CDP_primary_val].u_val);
467         else if (xmlStrcasecmp(element, (const xmlChar *) "secondary_value") == 0)
468             status =
469                 get_xml_double(reader,&cdp_prep->scratch[CDP_secondary_val].u_val);
470         else if (xmlStrcasecmp(element, (const xmlChar *) "intercept") == 0)
471             status = get_xml_double(reader,
472                                           &cdp_prep->
473                                           scratch[CDP_hw_intercept].u_val);
474         else if (xmlStrcasecmp(element, (const xmlChar *) "last_intercept") ==
475                  0)
476             status =
477                 get_xml_double(reader,
478                                      &cdp_prep->
479                                      scratch[CDP_hw_last_intercept].u_val);
480         else if (xmlStrcasecmp(element, (const xmlChar *) "slope") == 0)
481             status = get_xml_double(reader,
482                                     &cdp_prep->scratch[CDP_hw_slope].
483                                     u_val);
484         else if (xmlStrcasecmp(element, (const xmlChar *) "last_slope") == 0)
485             status = get_xml_double(reader,
486                                     &cdp_prep->
487                                     scratch[CDP_hw_last_slope].u_val);
488         else if (xmlStrcasecmp(element, (const xmlChar *) "nan_count") == 0)
489             status = get_xml_ulong(reader,
490                                    &cdp_prep->
491                                    scratch[CDP_null_count].u_cnt);
492         else if (xmlStrcasecmp(element, (const xmlChar *) "last_nan_count") ==
493                  0)
494             status =
495                 get_xml_ulong(reader,
496                               &cdp_prep->
497                               scratch[CDP_last_null_count].u_cnt);
498         else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal") == 0)
499             status = get_xml_double(reader,
500                                     &cdp_prep->scratch[CDP_hw_seasonal].
501                                     u_val);
502         else if (xmlStrcasecmp(element, (const xmlChar *) "last_seasonal") ==
503                  0)
504             status =
505                 get_xml_double(reader,
506                                      &cdp_prep->scratch[CDP_hw_last_seasonal].
507                                      u_val);
508         else if (xmlStrcasecmp(element, (const xmlChar *) "init_flag") == 0)
509             status = get_xml_ulong(reader,
510                                         &cdp_prep->
511                                        scratch[CDP_init_seasonal].u_cnt);
512         else if (xmlStrcasecmp(element, (const xmlChar *) "history") == 0)
513             status = parse_tag_rra_cdp_prep_ds_history(reader, cdp_prep);
514         else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
515             status = get_xml_double(reader,
516                                     &cdp_prep->scratch[CDP_val].u_val);
517         else if (xmlStrcasecmp(element,
518                            (const xmlChar *) "unknown_datapoints") == 0)
519             status = get_xml_ulong(reader,
520                                         &cdp_prep->
521                                        scratch[CDP_unkn_pdp_cnt].u_cnt);
522         else if (xmlStrcasecmp(element,
523                                (const xmlChar *) "/ds") == 0){
524             xmlFree(element);            
525             break;
526         }        
527         else {
528             rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
529                           element);
530             status = -1;
531             xmlFree(element);            
532             break;            
533         }
534         if (status != 0){
535             xmlFree(element);
536             break;
537         }
538         status = expect_element_end(reader,(char *)element);
539         xmlFree(element);        
540         if (status != 0)
541             break;
542     }    
543     return (status);
544 }                       /* int parse_tag_rra_cdp_prep_ds */
545
546 static int parse_tag_rra_cdp_prep(
547     xmlTextReaderPtr reader,
548     rrd_t *rrd,
549     cdp_prep_t *cdp_prep)
550 {
551     int       status;
552
553     unsigned int ds_count;
554
555     status = 0;
556     for ( ds_count = 0; ds_count < rrd->stat_head->ds_cnt;ds_count++){
557         if (expect_element(reader,"ds") == 0) {
558             status = parse_tag_rra_cdp_prep_ds(reader, rrd,
559                                                cdp_prep + ds_count);
560             if (status != 0)
561                 break;
562         } else {
563             status = -1;            
564             break;
565         }        
566     }
567     if (status == 0)
568         status =  expect_element(reader,"/cdp_prep");
569     return (status);
570 }                       /* int parse_tag_rra_cdp_prep */
571
572 /*
573  * Parse the <params> block within an RRA definition
574  */
575 static int parse_tag_rra_params(
576     xmlTextReaderPtr reader,
577     rra_def_t *rra_def)
578 {
579     xmlChar *element;
580     int       status;
581
582     status = -1;
583     while ((element = get_xml_element(reader)) != NULL){
584         /*
585          * Parameters for CF_HWPREDICT
586          */
587         if (xmlStrcasecmp(element, (const xmlChar *) "hw_alpha") == 0)
588             status = get_xml_double(reader,
589                                           &rra_def->par[RRA_hw_alpha].u_val);
590         else if (xmlStrcasecmp(element, (const xmlChar *) "hw_beta") == 0)
591             status = get_xml_double(reader,
592                                           &rra_def->par[RRA_hw_beta].u_val);
593         else if (xmlStrcasecmp(element,
594                            (const xmlChar *) "dependent_rra_idx") == 0)
595             status = get_xml_ulong(reader,
596                                         &rra_def->
597                                        par[RRA_dependent_rra_idx].u_cnt);
598         /*
599          * Parameters for CF_SEASONAL and CF_DEVSEASONAL
600          */
601         else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal_gamma") ==
602                  0)
603             status =
604                 get_xml_double(reader,
605                                      &rra_def->par[RRA_seasonal_gamma].u_val);
606         else if (xmlStrcasecmp
607                  (element, (const xmlChar *) "seasonal_smooth_idx") == 0)
608             status =
609                 get_xml_ulong(reader,
610                                    &rra_def->
611                                   par[RRA_seasonal_smooth_idx].u_cnt);
612         else if (xmlStrcasecmp(element, (const xmlChar *) "smoothing_window")
613                  == 0)
614             status =
615                 get_xml_double(reader,
616                                      &rra_def->
617                                      par[RRA_seasonal_smoothing_window].
618                                      u_val);
619         /* else if (dependent_rra_idx) ...; */
620         /*
621          * Parameters for CF_FAILURES
622          */
623         else if (xmlStrcasecmp(element, (const xmlChar *) "delta_pos") == 0)
624             status = get_xml_double(reader,
625                                           &rra_def->par[RRA_delta_pos].u_val);
626         else if (xmlStrcasecmp(element, (const xmlChar *) "delta_neg") == 0)
627             status = get_xml_double(reader,
628                                           &rra_def->par[RRA_delta_neg].u_val);
629         else if (xmlStrcasecmp(element, (const xmlChar *) "window_len") == 0)
630             status = get_xml_ulong(reader,
631                                         &rra_def->par[RRA_window_len].
632                                        u_cnt);
633         else if (xmlStrcasecmp(element, (const xmlChar *) "failure_threshold")
634                  == 0)
635             status =
636                 get_xml_ulong(reader,
637                                    &rra_def->
638                                   par[RRA_failure_threshold].u_cnt);
639         /*
640          * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
641          */
642         else if (xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
643             status = get_xml_double(reader,
644                                           &rra_def->par[RRA_cdp_xff_val].
645                                           u_val);
646         /*
647          * Compatibility code for 1.0.49
648          */
649         else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0) {  /* {{{ */
650             unsigned int i = 0;
651
652             for (i=0;i<ARRAY_LENGTH(rra_def->par);i++){
653                 if ((i == RRA_dependent_rra_idx)
654                     || (i == RRA_seasonal_smooth_idx)
655                     || (i == RRA_failure_threshold))
656                     status = get_xml_ulong(reader,
657                                                 &rra_def->par[i].
658                                                u_cnt);
659                 else
660                     status = get_xml_double(reader,
661                                                   &rra_def->par[i].u_val);
662
663                 if (status != 0)
664                     break;
665                 if ( i-1 < ARRAY_LENGTH(rra_def->par)){
666                     status = expect_element(reader,"/value");
667                     if (status == 0){
668                         status  = expect_element(reader,"value");
669                     }
670                 }
671                 if (status != 0){
672                     break;                    
673                 }
674             }
675         }  /* }}} */        
676         else if (xmlStrcasecmp(element,(const xmlChar *) "/params") == 0){
677             xmlFree(element);            
678             return status;
679         }  /* }}} */        
680         else {
681             rrd_set_error("line %d: parse_tag_rra_params: Unknown tag: %s",
682                           xmlTextReaderGetParserLineNumber(reader),element);
683             status = -1;
684         }
685         status = expect_element_end(reader,(char *)element);
686         xmlFree(element);        
687         if (status != 0)
688             break;
689     }
690     return (status);
691 }                       /* int parse_tag_rra_params */
692
693 /*
694  * Parse an RRA definition
695  */
696 static int parse_tag_rra_cf(
697     xmlTextReaderPtr reader,
698     rra_def_t *rra_def)
699 {
700     int       status;
701
702     status = get_xml_string(reader,
703                                   rra_def->cf_nam, sizeof(rra_def->cf_nam));
704     if (status != 0)
705         return status;
706
707     status = cf_conv(rra_def->cf_nam);
708     if (status == -1) {
709         rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
710                       rra_def->cf_nam);
711         return -1;
712     }
713
714     return 0;
715 }                       /* int parse_tag_rra_cf */
716
717 static int parse_tag_rra(
718     xmlTextReaderPtr reader,
719     rrd_t *rrd)
720 {
721     int       status;
722     xmlChar *element;
723     
724     rra_def_t *cur_rra_def;
725     cdp_prep_t *cur_cdp_prep;
726     rra_ptr_t *cur_rra_ptr;
727
728     /* Allocate more rra_def space for this RRA */
729     {                   /* {{{ */
730         rra_def_t *temp;
731
732         temp = (rra_def_t *) realloc(rrd->rra_def,
733                                      sizeof(rra_def_t) *
734                                      (rrd->stat_head->rra_cnt + 1));
735         if (temp == NULL) {
736             rrd_set_error("parse_tag_rra: realloc failed.");
737             return (-1);
738         }
739         rrd->rra_def = temp;
740         cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
741         memset(cur_rra_def, '\0', sizeof(rra_def_t));
742     }                   /* }}} */
743
744     /* allocate cdp_prep_t */
745     {                   /* {{{ */
746         cdp_prep_t *temp;
747
748         temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
749                                       * rrd->stat_head->ds_cnt
750                                       * (rrd->stat_head->rra_cnt + 1));
751         if (temp == NULL) {
752             rrd_set_error("parse_tag_rra: realloc failed.");
753             return (-1);
754         }
755         rrd->cdp_prep = temp;
756         cur_cdp_prep = rrd->cdp_prep
757             + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
758         memset(cur_cdp_prep, '\0',
759                sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
760     }                   /* }}} */
761
762     /* allocate rra_ptr_t */
763     {                   /* {{{ */
764         rra_ptr_t *temp;
765
766         temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
767                                      sizeof(rra_ptr_t) *
768                                      (rrd->stat_head->rra_cnt + 1));
769         if (temp == NULL) {
770             rrd_set_error("parse_tag_rra: realloc failed.");
771             return (-1);
772         }
773         rrd->rra_ptr = temp;
774         cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
775         memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
776     }                   /* }}} */
777
778     /* All space successfully allocated, increment number of RRAs. */
779     rrd->stat_head->rra_cnt++;
780
781     status = 0;
782     while ((element = get_xml_element(reader)) != NULL){
783         if (xmlStrcasecmp(element, (const xmlChar *) "cf") == 0)
784             status = parse_tag_rra_cf(reader, cur_rra_def);
785         else if (xmlStrcasecmp(element, (const xmlChar *) "pdp_per_row") == 0)
786             status = get_xml_ulong(reader,
787                                         &cur_rra_def->pdp_cnt);
788         else if (atoi(rrd->stat_head->version) == 1
789                  && xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
790             status = get_xml_double(reader,
791                                           (double *) &cur_rra_def->
792                                           par[RRA_cdp_xff_val].u_val);
793         else if (atoi(rrd->stat_head->version) >= 2
794                  && xmlStrcasecmp(element, (const xmlChar *) "params") == 0){            
795             xmlFree(element);
796             status = parse_tag_rra_params(reader, cur_rra_def);
797             if (status == 0)
798                 continue;
799             else
800                 return status;
801         }
802         else if (xmlStrcasecmp(element, (const xmlChar *) "cdp_prep") == 0){
803             xmlFree(element);
804             status = parse_tag_rra_cdp_prep(reader, rrd, cur_cdp_prep);
805             if (status == 0)
806                 continue;
807             else
808                 return status;
809         }        
810         else if (xmlStrcasecmp(element, (const xmlChar *) "database") == 0){            
811             xmlFree(element);
812             status = parse_tag_rra_database(reader, rrd);
813             if (status == 0)
814                 continue;
815             else
816                 return status;
817         }
818         else if (xmlStrcasecmp(element,(const xmlChar *) "/rra") == 0){
819             xmlFree(element);            
820             return status;
821         }  /* }}} */        
822        else {
823             rrd_set_error("line %d: parse_tag_rra: Unknown tag: %s",
824                           xmlTextReaderGetParserLineNumber(reader), element);
825             status = -1;            
826         }
827         if (status != 0) {
828             xmlFree(element);
829             return status;
830         }        
831         status = expect_element_end(reader,(char *)element);
832         xmlFree(element);
833         if (status != 0) {
834             return status;
835         }        
836     }    
837     /* Set the RRA pointer to a random location */
838     cur_rra_ptr->cur_row = rrd_random() % cur_rra_def->row_cnt;
839
840     return (status);
841 }                       /* int parse_tag_rra */
842
843 /*
844  * Parse a DS definition
845  */
846 static int parse_tag_ds_cdef(
847     xmlTextReaderPtr reader,
848     rrd_t *rrd)
849 {
850     xmlChar *cdef;
851
852     cdef = get_xml_text(reader);
853     if (cdef != NULL){
854         /* We're always working on the last DS that has been added to the structure
855          * when we get here */
856         parseCDEF_DS((char *)cdef, rrd, rrd->stat_head->ds_cnt - 1);
857         xmlFree(cdef);
858         if (rrd_test_error())
859             return -1;
860         else            
861             return 0;        
862     }
863     return -1;
864 }                       /* int parse_tag_ds_cdef */
865
866 static int parse_tag_ds_type(
867     xmlTextReaderPtr reader,
868     ds_def_t *ds_def)
869 {
870     char *dst;
871     dst = (char *)get_xml_text(reader);
872     if (dst != NULL){
873         int status;
874         status = dst_conv(dst);
875         if (status == -1) {
876             rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
877                           dst);
878             return -1;
879         }
880         strncpy(ds_def->dst,dst,sizeof(ds_def->dst)-1);
881         ds_def->dst[sizeof(ds_def->dst)-1] = '\0';
882         xmlFree(dst);
883         return 0;        
884     }
885     return -1;
886 }                       /* int parse_tag_ds_type */
887
888 static int parse_tag_ds(
889     xmlTextReaderPtr reader,
890     rrd_t *rrd)
891 {
892     int       status;
893     xmlChar  *element;
894     
895     ds_def_t *cur_ds_def;
896     pdp_prep_t *cur_pdp_prep;
897
898     /*
899      * If there are DS definitions after RRA definitions the number of values,
900      * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
901      * specific order in this case.
902      */
903     if (rrd->stat_head->rra_cnt > 0) {
904         rrd_set_error("parse_tag_ds: All data source definitions MUST "
905                       "precede the RRA definitions!");
906         return (-1);
907     }
908
909     /* Allocate space for the new DS definition */
910     {                   /* {{{ */
911         ds_def_t *temp;
912
913         temp = (ds_def_t *) realloc(rrd->ds_def,
914                                     sizeof(ds_def_t) *
915                                     (rrd->stat_head->ds_cnt + 1));
916         if (temp == NULL) {
917             rrd_set_error("parse_tag_ds: malloc failed.");
918             return (-1);
919         }
920         rrd->ds_def = temp;
921         cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
922         memset(cur_ds_def, '\0', sizeof(ds_def_t));
923     }                   /* }}} */
924
925     /* Allocate pdp_prep space for the new DS definition */
926     {                   /* {{{ */
927         pdp_prep_t *temp;
928
929         temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
930                                       sizeof(pdp_prep_t) *
931                                       (rrd->stat_head->ds_cnt + 1));
932         if (temp == NULL) {
933             rrd_set_error("parse_tag_ds: malloc failed.");
934             return (-1);
935         }
936         rrd->pdp_prep = temp;
937         cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
938         memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
939     }                   /* }}} */
940
941     /* All allocations successful, let's increment the number of DSes. */
942     rrd->stat_head->ds_cnt++;
943
944     status = 0;
945     while ((element = get_xml_element(reader)) != NULL){
946         if (xmlStrcasecmp(element, (const xmlChar *) "name") == 0){
947             status = get_xml_string(reader,cur_ds_def->ds_nam,sizeof(cur_ds_def->ds_nam));
948         }
949         else if (xmlStrcasecmp(element, (const xmlChar *) "type") == 0)
950             status = parse_tag_ds_type(reader, cur_ds_def);
951         else if (xmlStrcasecmp(element,
952                            (const xmlChar *) "minimal_heartbeat") == 0)
953             status = get_xml_ulong(reader,
954                                         &cur_ds_def->par[DS_mrhb_cnt].
955                                        u_cnt);
956         else if (xmlStrcasecmp(element, (const xmlChar *) "min") == 0)
957             status = get_xml_double(reader,
958                                           &cur_ds_def->par[DS_min_val].u_val);
959         else if (xmlStrcasecmp(element, (const xmlChar *) "max") == 0)
960             status = get_xml_double(reader,
961                                           &cur_ds_def->par[DS_max_val].u_val);
962         else if (xmlStrcasecmp(element, (const xmlChar *) "cdef") == 0)
963             status = parse_tag_ds_cdef(reader, rrd);
964         else if (xmlStrcasecmp(element, (const xmlChar *) "last_ds") == 0)
965             status = get_xml_string(reader,
966                                           cur_pdp_prep->last_ds,
967                                           sizeof(cur_pdp_prep->last_ds));
968         else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
969             status = get_xml_double(reader,
970                                           &cur_pdp_prep->scratch[PDP_val].
971                                           u_val);
972         else if (xmlStrcasecmp(element, (const xmlChar *) "unknown_sec") == 0)
973             status = get_xml_ulong(reader,
974                                         &cur_pdp_prep->
975                                        scratch[PDP_unkn_sec_cnt].u_cnt);
976         else if (xmlStrcasecmp(element, (const xmlChar *) "/ds") == 0) {
977             xmlFree(element);            
978             break;
979         }        
980         else {
981             rrd_set_error("parse_tag_ds: Unknown tag: %s", element);
982             status = -1;
983         }        
984         if (status != 0) {            
985             xmlFree(element);        
986             break;
987         }
988         status = expect_element_end(reader,(char *)element);
989         xmlFree(element);        
990         if (status != 0)
991             break;        
992     }
993     
994     return (status);
995 }                       /* int parse_tag_ds */
996
997 /*
998  * Parse root nodes
999  */
1000 static int parse_tag_rrd(
1001     xmlTextReaderPtr reader,
1002     rrd_t *rrd)
1003 {
1004     int       status;
1005     xmlChar *element;
1006     
1007     status = 0;
1008     while ((element = get_xml_element(reader)) != NULL ){
1009         if (xmlStrcasecmp(element, (const xmlChar *) "version") == 0)
1010             status = get_xml_string(reader,
1011                                           rrd->stat_head->version,
1012                                           sizeof(rrd->stat_head->version));
1013         else if (xmlStrcasecmp(element, (const xmlChar *) "step") == 0)
1014             status = get_xml_ulong(reader,
1015                                         &rrd->stat_head->pdp_step);
1016         else if (xmlStrcasecmp(element, (const xmlChar *) "lastupdate") == 0) {
1017 #ifdef TIME_T_IS_LONG
1018                 status = get_xml_long(reader, &rrd->live_head->last_up);
1019 #else
1020 #ifdef TIME_T_IS_LONG_LONG
1021                 status = get_xml_llong(reader, &rrd->live_head->last_up); 
1022 #else
1023             if (sizeof(time_t) == sizeof(long)) {
1024                 status = get_xml_long(reader,
1025                                         (long *)&rrd->live_head->last_up);
1026             }
1027             else if (sizeof(time_t) == sizeof(long long)) {
1028                 status = get_xml_llong(reader,
1029                                         (long long *)&rrd->live_head->last_up); 
1030             }
1031 #endif
1032 #endif
1033         }
1034         else if (xmlStrcasecmp(element, (const xmlChar *) "ds") == 0){            
1035             xmlFree(element);
1036             status = parse_tag_ds(reader, rrd);
1037             /* as we come back the </ds> tag is already gone */
1038             if (status == 0)
1039                 continue;
1040             else
1041                 return status;
1042         }        
1043         else if (xmlStrcasecmp(element, (const xmlChar *) "rra") == 0){            
1044             xmlFree(element);
1045             status = parse_tag_rra(reader, rrd);
1046             if (status == 0)
1047                 continue;
1048             else
1049                 return status;
1050         }
1051         else if (xmlStrcasecmp(element, (const xmlChar *) "/rrd") == 0) {
1052             xmlFree(element);
1053             return status;
1054         }
1055         else {
1056             rrd_set_error("parse_tag_rrd: Unknown tag: %s", element);
1057             status = -1;
1058         }
1059
1060         if (status != 0){
1061             xmlFree(element);
1062             break;
1063         }        
1064         status = expect_element_end(reader,(char *)element);
1065         xmlFree(element);        
1066         if (status != 0)
1067             break;        
1068     }
1069     return (status);
1070 }                       /* int parse_tag_rrd */
1071
1072 static rrd_t *parse_file(
1073     const char *filename)
1074 {
1075     xmlTextReaderPtr reader;
1076     int       status;
1077
1078     rrd_t    *rrd;
1079
1080     reader = xmlNewTextReaderFilename(filename);
1081     if (reader == NULL) {
1082         rrd_set_error("Could not create xml reader for: %s",filename);
1083         return (NULL);
1084     }
1085
1086     if (expect_element(reader,"rrd") != 0) {
1087         xmlFreeTextReader(reader);
1088         return (NULL);
1089     }
1090
1091     rrd = (rrd_t *) malloc(sizeof(rrd_t));
1092     if (rrd == NULL) {
1093         rrd_set_error("parse_file: malloc failed.");
1094         xmlFreeTextReader(reader);
1095         return (NULL);
1096     }
1097     memset(rrd, '\0', sizeof(rrd_t));
1098
1099     rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
1100     if (rrd->stat_head == NULL) {
1101         rrd_set_error("parse_tag_rrd: malloc failed.");
1102         xmlFreeTextReader(reader);
1103         free(rrd);
1104         return (NULL);
1105     }
1106     memset(rrd->stat_head, '\0', sizeof(stat_head_t));
1107
1108     strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
1109     rrd->stat_head->float_cookie = FLOAT_COOKIE;
1110
1111     rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
1112     if (rrd->live_head == NULL) {
1113         rrd_set_error("parse_tag_rrd: malloc failed.");
1114         xmlFreeTextReader(reader);
1115         free(rrd->stat_head);
1116         free(rrd);
1117         return (NULL);
1118     }
1119     memset(rrd->live_head, '\0', sizeof(live_head_t));
1120
1121     status = parse_tag_rrd(reader, rrd);
1122
1123     xmlFreeTextReader(reader);
1124
1125     if (status != 0) {
1126         local_rrd_free(rrd);
1127         rrd = NULL;
1128     }
1129
1130     return (rrd);
1131 }                       /* rrd_t *parse_file */
1132
1133 static int write_file(
1134     const char *file_name,
1135     rrd_t *rrd)
1136 {
1137     FILE     *fh;
1138     unsigned int i;
1139     unsigned int rra_offset;
1140
1141     if (strcmp("-", file_name) == 0)
1142         fh = stdout;
1143     else {
1144         int       fd_flags = O_WRONLY | O_CREAT;
1145         int       fd;
1146
1147 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1148         fd_flags |= O_BINARY;
1149 #endif
1150
1151         if (opt_force_overwrite == 0)
1152             fd_flags |= O_EXCL;
1153
1154         fd = open(file_name, fd_flags, 0666);
1155         if (fd == -1) {
1156             rrd_set_error("creating '%s': %s", file_name,
1157                           rrd_strerror(errno));
1158             return (-1);
1159         }
1160
1161         fh = fdopen(fd, "wb");
1162         if (fh == NULL) {
1163             rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1164             close(fd);
1165             return (-1);
1166         }
1167     }
1168     if (atoi(rrd->stat_head->version) < 3) {
1169         /* we output 3 or higher */
1170         strcpy(rrd->stat_head->version, "0003");
1171     }
1172     fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1173     fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1174     fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1175     fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1176     fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1177     fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1178            rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1179     fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1180
1181     /* calculate the number of rrd_values to dump */
1182     rra_offset = 0;
1183     for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
1184         unsigned long num_rows = rrd->rra_def[i].row_cnt;
1185         unsigned long cur_row = rrd->rra_ptr[i].cur_row;
1186         unsigned long ds_cnt = rrd->stat_head->ds_cnt;
1187
1188         fwrite(rrd->rrd_value +
1189                (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1190                sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1191
1192         fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1193                sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1194
1195         rra_offset += num_rows;
1196     }
1197
1198     /* lets see if we had an error */
1199     if (ferror(fh)) {
1200         rrd_set_error("a file error occurred while creating '%s'", file_name);
1201         fclose(fh);
1202         return (-1);
1203     }
1204
1205     fclose(fh);
1206     return (0);
1207 }                       /* int write_file */
1208
1209 int rrd_restore(
1210     int argc,
1211     char **argv)
1212 {
1213     rrd_t    *rrd;
1214
1215     /* init rrd clean */
1216     optind = 0;
1217     opterr = 0;         /* initialize getopt */
1218     while (42) {
1219         int       opt;
1220         int       option_index = 0;
1221         static struct option long_options[] = {
1222             {"range-check", no_argument, 0, 'r'},
1223             {"force-overwrite", no_argument, 0, 'f'},
1224             {0, 0, 0, 0}
1225         };
1226
1227         opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1228
1229         if (opt == EOF)
1230             break;
1231
1232         switch (opt) {
1233         case 'r':
1234             opt_range_check = 1;
1235             break;
1236
1237         case 'f':
1238             opt_force_overwrite = 1;
1239             break;
1240
1241         default:
1242             rrd_set_error("usage rrdtool %s [--range-check|-r] "
1243                           "[--force-overwrite/-f]  file.xml file.rrd",
1244                           argv[0]);
1245             return (-1);
1246             break;
1247         }
1248     }                   /* while (42) */
1249
1250     if ((argc - optind) != 2) {
1251         rrd_set_error("usage rrdtool %s [--range-check/-r] "
1252                       "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1253         return (-1);
1254     }
1255
1256     rrd = parse_file(argv[optind]);
1257     if (rrd == NULL)
1258         return (-1);
1259     
1260     if (write_file(argv[optind + 1], rrd) != 0) {
1261         local_rrd_free(rrd);
1262         return (-1);
1263     }
1264     local_rrd_free(rrd);
1265
1266
1267     return (0);
1268 }                       /* int rrd_restore */
1269
1270 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */