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