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