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