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