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