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