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