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