Code

Imported upstream version 1.3.8.
[pkg-rrdtool.git] / src / rrd_restore.c
1 /*****************************************************************************
2  * RRDtool 1.3.8  Copyright by Tobi Oetiker, 1997-2009
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: rrd_restore.c 1801 2009-05-19 13:45:05Z oetiker $
9  *************************************************************************** */
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  **/
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <fcntl.h>
36 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
37 #include <math.h>
38 # include <io.h>
39 # define open _open
40 # define close _close
41 #else
42 # include <unistd.h>
43 #endif
45 #include <libxml/parser.h>
46 #include "rrd_tool.h"
47 #include "rrd_rpncalc.h"
48 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof ((a)[0]))
49 static int opt_range_check = 0;
50 static int opt_force_overwrite = 0;
52 /*
53  * Auxiliary functions
54  */
55 static int get_string_from_node(
56     xmlDoc * doc,
57     xmlNode * node,
58     char *buffer,
59     size_t buffer_size)
60 {
61     xmlChar  *temp0;
62     char     *begin_ptr;
63     char     *end_ptr;
65     temp0 = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
66     if (temp0 == NULL) {
67         rrd_set_error("get_string_from_node: xmlNodeListGetString failed.");
68         return (-1);
69     }
71     begin_ptr = (char *) temp0;
72     while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
73         begin_ptr++;
75     if (begin_ptr[0] == 0) {
76         xmlFree(temp0);
77         buffer[0] = 0;
78         return (0);
79     }
81     end_ptr = begin_ptr;
82     while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
83         end_ptr++;
84     end_ptr[0] = 0;
86     strncpy(buffer, begin_ptr, buffer_size);
87     buffer[buffer_size - 1] = 0;
89     xmlFree(temp0);
91     return (0);
92 }                       /* int get_string_from_node */
94 static int get_long_from_node(
95     xmlDoc * doc,
96     xmlNode * node,
97     long *value)
98 {
99     long       temp;
100     char     *str_ptr;
101     char     *end_ptr;
103     str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
104     if (str_ptr == NULL) {
105         rrd_set_error("get_long_from_node: xmlNodeListGetString failed.");
106         return (-1);
107     }
109     end_ptr = NULL;
110     temp = strtol(str_ptr, &end_ptr, 0);
111     xmlFree(str_ptr);
113     if (str_ptr == end_ptr) {
114         rrd_set_error("get_long_from_node: Cannot parse buffer as long: %s",
115                       str_ptr);
116         return (-1);
117     }
119     *value = temp;
121     return (0);
122 }                       /* int get_long_from_node */
124 static int get_ulong_from_node(
125     xmlDoc * doc,
126     xmlNode * node,
127     unsigned long *value)
129     unsigned long       temp;
130     char     *str_ptr;
131     char     *end_ptr;
133     str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
134     if (str_ptr == NULL) {
135         rrd_set_error("get_ulong_from_node: xmlNodeListGetString failed.");
136         return (-1);
137     }
139     end_ptr = NULL;
140     temp = strtoul(str_ptr, &end_ptr, 0);
141     xmlFree(str_ptr);
143     if (str_ptr == end_ptr) {
144         rrd_set_error("get_ulong_from_node: Cannot parse buffer as unsigned long: %s",
145                       str_ptr);
146         return (-1);
147     }
149     *value = temp;
151     return (0);
152 }                       /* int get_ulong_from_node */
154 static int get_double_from_node(
155     xmlDoc * doc,
156     xmlNode * node,
157     double *value)
159     double    temp;
160     char     *str_ptr;
161     char     *end_ptr;
163     str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
164     if (str_ptr == NULL) {
165         rrd_set_error("get_double_from_node: xmlNodeListGetString failed.");
166         return (-1);
167     }
169     if (strstr(str_ptr, "NaN") != NULL)
170     {
171         *value = DNAN;
172         xmlFree(str_ptr);
173         return 0;
174     }
176     end_ptr = NULL;
177     temp = strtod(str_ptr, &end_ptr);
178     xmlFree(str_ptr);
180     if (str_ptr == end_ptr) {
181         rrd_set_error
182             ("get_double_from_node: Cannot parse buffer as double: %s",
183              str_ptr);
184         return (-1);
185     }
187     *value = temp;
189     return (0);
190 }                       /* int get_double_from_node */
192 static int value_check_range(
193     rrd_value_t *rrd_value,
194     const ds_def_t *ds_def)
196     double    min;
197     double    max;
199     if (opt_range_check == 0)
200         return (0);
202     min = ds_def->par[DS_min_val].u_val;
203     max = ds_def->par[DS_max_val].u_val;
205     if (((!isnan(min)) && (*rrd_value < min))
206         || ((!isnan(max)) && (*rrd_value > max)))
207         *rrd_value = DNAN;
209     return (0);
210 }                       /* int value_check_range */
212 /*
213  * Parse the <database> block within an RRA definition
214  */
215 static int parse_tag_rra_database_row(
216     xmlDoc * doc,
217     xmlNode * node,
218     rrd_t *rrd,
219     rrd_value_t *rrd_value)
221     unsigned int values_count = 0;
222     xmlNode  *child;
223     int       status;
225     status = 0;
226     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
227         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
228             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
229             /* ignore */ ;
230         else if (xmlStrcmp(child->name, (const xmlChar *) "v") == 0) {
231             if (values_count < rrd->stat_head->ds_cnt) {
232                 status =
233                     get_double_from_node(doc, child,
234                                          rrd_value + values_count);
235                 if (status == 0)
236                     value_check_range(rrd_value + values_count,
237                                       rrd->ds_def + values_count);
238             }
240             values_count++;
241         } else {
242             rrd_set_error("parse_tag_rra_database_row: Unknown tag: %s",
243                           child->name);
244             status = -1;
245         }
247         if (status != 0)
248             break;
249     }                   /* for (child = node->xmlChildrenNode) */
251     if (values_count != rrd->stat_head->ds_cnt) {
252         rrd_set_error("parse_tag_rra_database_row: Row has %u values "
253                       "and RRD has %lu data sources.",
254                       values_count, rrd->stat_head->ds_cnt);
255         status = -1;
256     }
258     return (status);
259 }                       /* int parse_tag_rra_database_row */
261 static int parse_tag_rra_database(
262     xmlDoc * doc,
263     xmlNode * node,
264     rrd_t *rrd)
266     rra_def_t *cur_rra_def;
267     unsigned int total_row_cnt;
268     xmlNode  *child;
269     int       status;
270     int       i;
272     total_row_cnt = 0;
273     for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
274         total_row_cnt += rrd->rra_def[i].row_cnt;
276     cur_rra_def = rrd->rra_def + i;
278     status = 0;
279     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
280         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
281             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
282             /* ignore */ ;
283         else if (xmlStrcmp(child->name, (const xmlChar *) "row") == 0) {
284             rrd_value_t *temp;
285             rrd_value_t *cur_rrd_value;
286             unsigned int total_values_count = rrd->stat_head->ds_cnt
287                 * (total_row_cnt + 1);
289             /* Allocate space for the new values.. */
290             temp = (rrd_value_t *) realloc(rrd->rrd_value,
291                                            sizeof(rrd_value_t) *
292                                            total_values_count);
293             if (temp == NULL) {
294                 rrd_set_error("parse_tag_rra_database: realloc failed.");
295                 status = -1;
296                 break;
297             }
298             rrd->rrd_value = temp;
299             cur_rrd_value = rrd->rrd_value
300                 + (rrd->stat_head->ds_cnt * total_row_cnt);
301             memset(cur_rrd_value, '\0',
302                    sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
303             total_row_cnt++;
304             cur_rra_def->row_cnt++;
306             status =
307                 parse_tag_rra_database_row(doc, child, rrd, cur_rrd_value);
308         } /* if (xmlStrcmp (child->name, (const xmlChar *) "row") == 0) */
309         else {
310             rrd_set_error("parse_tag_rra_database: Unknown tag: %s",
311                           child->name);
312             status = -1;
313         }
315         if (status != 0)
316             break;
317     }                   /* for (child = node->xmlChildrenNode) */
319     return (status);
320 }                       /* int parse_tag_rra_database */
322 /*
323  * Parse the <cdp_prep> block within an RRA definition
324  */
325 static int parse_tag_rra_cdp_prep_ds_history(
326     xmlDoc * doc,
327     xmlNode * node,
328     cdp_prep_t *cdp_prep)
330     /* Make `history_buffer' the same size as the scratch area, plus the
331      * terminating NULL byte. */
332     char      history_buffer[sizeof(((cdp_prep_t *)0)->scratch) + 1];
333     char     *history_ptr;
334     int       status;
335     int       i;
337     status = get_string_from_node(doc, node,
338                                   history_buffer, sizeof(history_buffer));
339     if (status != 0)
340         return (-1);
342     history_ptr = (char *) (&cdp_prep->scratch[0]);
343     for (i = 0; history_buffer[i] != '\0'; i++)
344         history_ptr[i] = (history_buffer[i] == '1') ? 1 : 0;
346     return (0);
347 }                       /* int parse_tag_rra_cdp_prep_ds_history */
349 static int parse_tag_rra_cdp_prep_ds(
350     xmlDoc * doc,
351     xmlNode * node,
352     rrd_t *rrd,
353     cdp_prep_t *cdp_prep)
355     xmlNode  *child;
356     int       status;
358     memset(cdp_prep, '\0', sizeof(cdp_prep_t));
360     status = 0;
361     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
362         if (atoi(rrd->stat_head->version) == 1) {
363             cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
364             cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
365         }
366         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
367             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
368             /* ignore */ ;
369         else if (xmlStrcmp(child->name, (const xmlChar *) "primary_value") ==
370                  0)
371             status =
372                 get_double_from_node(doc, child,
373                                      &cdp_prep->scratch[CDP_primary_val].
374                                      u_val);
375         else if (xmlStrcmp(child->name, (const xmlChar *) "secondary_value")
376                  == 0)
377             status =
378                 get_double_from_node(doc, child,
379                                      &cdp_prep->scratch[CDP_secondary_val].
380                                      u_val);
381         else if (xmlStrcmp(child->name, (const xmlChar *) "intercept") == 0)
382             status = get_double_from_node(doc, child,
383                                           &cdp_prep->
384                                           scratch[CDP_hw_intercept].u_val);
385         else if (xmlStrcmp(child->name, (const xmlChar *) "last_intercept") ==
386                  0)
387             status =
388                 get_double_from_node(doc, child,
389                                      &cdp_prep->
390                                      scratch[CDP_hw_last_intercept].u_val);
391         else if (xmlStrcmp(child->name, (const xmlChar *) "slope") == 0)
392             status = get_double_from_node(doc, child,
393                                           &cdp_prep->scratch[CDP_hw_slope].
394                                           u_val);
395         else if (xmlStrcmp(child->name, (const xmlChar *) "last_slope") == 0)
396             status = get_double_from_node(doc, child,
397                                           &cdp_prep->
398                                           scratch[CDP_hw_last_slope].u_val);
399         else if (xmlStrcmp(child->name, (const xmlChar *) "nan_count") == 0)
400             status = get_ulong_from_node(doc, child,
401                                         &cdp_prep->
402                                        scratch[CDP_null_count].u_cnt);
403         else if (xmlStrcmp(child->name, (const xmlChar *) "last_nan_count") ==
404                  0)
405             status =
406                 get_ulong_from_node(doc, child,
407                                    &cdp_prep->
408                                   scratch[CDP_last_null_count].u_cnt);
409         else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal") == 0)
410             status = get_double_from_node(doc, child,
411                                           &cdp_prep->scratch[CDP_hw_seasonal].
412                                           u_val);
413         else if (xmlStrcmp(child->name, (const xmlChar *) "last_seasonal") ==
414                  0)
415             status =
416                 get_double_from_node(doc, child,
417                                      &cdp_prep->scratch[CDP_hw_last_seasonal].
418                                      u_val);
419         else if (xmlStrcmp(child->name, (const xmlChar *) "init_flag") == 0)
420             status = get_ulong_from_node(doc, child,
421                                         &cdp_prep->
422                                        scratch[CDP_init_seasonal].u_cnt);
423         else if (xmlStrcmp(child->name, (const xmlChar *) "history") == 0)
424             status = parse_tag_rra_cdp_prep_ds_history(doc, child, cdp_prep);
425         else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
426             status = get_double_from_node(doc, child,
427                                           &cdp_prep->scratch[CDP_val].u_val);
428         else if (xmlStrcmp(child->name,
429                            (const xmlChar *) "unknown_datapoints") == 0)
430             status = get_ulong_from_node(doc, child,
431                                         &cdp_prep->
432                                        scratch[CDP_unkn_pdp_cnt].u_cnt);
433         else {
434             rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
435                           child->name);
436             status = -1;
437         }
439         if (status != 0)
440             break;
441     }
443     return (status);
444 }                       /* int parse_tag_rra_cdp_prep_ds */
446 static int parse_tag_rra_cdp_prep(
447     xmlDoc * doc,
448     xmlNode * node,
449     rrd_t *rrd,
450     cdp_prep_t *cdp_prep)
452     xmlNode  *child;
453     int       status;
455     unsigned int ds_count = 0;
457     status = 0;
458     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
459         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
460             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
461             /* ignore */ ;
462         else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0) {
463             if (ds_count >= rrd->stat_head->ds_cnt)
464                 status = -1;
465             else {
466                 status = parse_tag_rra_cdp_prep_ds(doc, child, rrd,
467                                                    cdp_prep + ds_count);
468                 ds_count++;
469             }
470         } else {
471             rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
472                           child->name);
473             status = -1;
474         }
476         if (status != 0)
477             break;
478     }
480     if (ds_count != rrd->stat_head->ds_cnt) {
481         rrd_set_error("parse_tag_rra_cdp_prep: There are %i data sources in "
482                       "the RRD file, but %i in this cdp_prep block!",
483                       (int) rrd->stat_head->ds_cnt, ds_count);
484         status = -1;
485     }
487     return (status);
488 }                       /* int parse_tag_rra_cdp_prep */
490 /*
491  * Parse the <params> block within an RRA definition
492  */
493 static int parse_tag_rra_params(
494     xmlDoc * doc,
495     xmlNode * node,
496     rra_def_t *rra_def)
498     xmlNode  *child;
499     int       status;
501     status = 0;
502     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
503         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
504             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
505             /* ignore */ ;
506         /*
507          * Parameters for CF_HWPREDICT
508          */
509         else if (xmlStrcmp(child->name, (const xmlChar *) "hw_alpha") == 0)
510             status = get_double_from_node(doc, child,
511                                           &rra_def->par[RRA_hw_alpha].u_val);
512         else if (xmlStrcmp(child->name, (const xmlChar *) "hw_beta") == 0)
513             status = get_double_from_node(doc, child,
514                                           &rra_def->par[RRA_hw_beta].u_val);
515         else if (xmlStrcmp(child->name,
516                            (const xmlChar *) "dependent_rra_idx") == 0)
517             status = get_ulong_from_node(doc, child,
518                                         &rra_def->
519                                        par[RRA_dependent_rra_idx].u_cnt);
520         /*
521          * Parameters for CF_SEASONAL and CF_DEVSEASONAL
522          */
523         else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal_gamma") ==
524                  0)
525             status =
526                 get_double_from_node(doc, child,
527                                      &rra_def->par[RRA_seasonal_gamma].u_val);
528         else if (xmlStrcmp
529                  (child->name, (const xmlChar *) "seasonal_smooth_idx") == 0)
530             status =
531                 get_ulong_from_node(doc, child,
532                                    &rra_def->
533                                   par[RRA_seasonal_smooth_idx].u_cnt);
534         else if (xmlStrcmp(child->name, (const xmlChar *) "smoothing_window")
535                  == 0)
536             status =
537                 get_double_from_node(doc, child,
538                                      &rra_def->
539                                      par[RRA_seasonal_smoothing_window].
540                                      u_val);
541         /* else if (dependent_rra_idx) ...; */
542         /*
543          * Parameters for CF_FAILURES
544          */
545         else if (xmlStrcmp(child->name, (const xmlChar *) "delta_pos") == 0)
546             status = get_double_from_node(doc, child,
547                                           &rra_def->par[RRA_delta_pos].u_val);
548         else if (xmlStrcmp(child->name, (const xmlChar *) "delta_neg") == 0)
549             status = get_double_from_node(doc, child,
550                                           &rra_def->par[RRA_delta_neg].u_val);
551         else if (xmlStrcmp(child->name, (const xmlChar *) "window_len") == 0)
552             status = get_ulong_from_node(doc, child,
553                                         &rra_def->par[RRA_window_len].
554                                        u_cnt);
555         else if (xmlStrcmp(child->name, (const xmlChar *) "failure_threshold")
556                  == 0)
557             status =
558                 get_ulong_from_node(doc, child,
559                                    &rra_def->
560                                   par[RRA_failure_threshold].u_cnt);
561         /*
562          * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
563          */
564         else if (xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
565             status = get_double_from_node(doc, child,
566                                           &rra_def->par[RRA_cdp_xff_val].
567                                           u_val);
568         /*
569          * Compatibility code for 1.0.49
570          */
571         else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) {  /* {{{ */
572             unsigned int i = 0;
574             while (42) {
575                 if (i >= ARRAY_LENGTH(rra_def->par)) {
576                     status = -1;
577                     break;
578                 }
580                 if ((i == RRA_dependent_rra_idx)
581                     || (i == RRA_seasonal_smooth_idx)
582                     || (i == RRA_failure_threshold))
583                     status = get_ulong_from_node(doc, child,
584                                                 &rra_def->par[i].
585                                                u_cnt);
586                 else
587                     status = get_double_from_node(doc, child,
588                                                   &rra_def->par[i].u_val);
590                 if (status != 0)
591                     break;
593                 /* When this loops exits (sucessfully) `child' points to the last
594                  * `value' tag in the list. */
595                 if ((child->next == NULL)
596                     || (xmlStrcmp(child->name, (const xmlChar *) "value") !=
597                         0))
598                     break;
600                 child = child->next;
601                 i++;
602             }
603         } /* }}} */
604         else {
605             rrd_set_error("parse_tag_rra_params: Unknown tag: %s",
606                           child->name);
607             status = -1;
608         }
610         if (status != 0)
611             break;
612     }
614     return (status);
615 }                       /* int parse_tag_rra_params */
617 /*
618  * Parse an RRA definition
619  */
620 static int parse_tag_rra_cf(
621     xmlDoc * doc,
622     xmlNode * node,
623     rra_def_t *rra_def)
625     int       status;
627     status = get_string_from_node(doc, node,
628                                   rra_def->cf_nam, sizeof(rra_def->cf_nam));
629     if (status != 0)
630         return (-1);
632     status = cf_conv(rra_def->cf_nam);
633     if (status == -1) {
634         rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
635                       rra_def->cf_nam);
636         return (-1);
637     }
639     return (0);
640 }                       /* int parse_tag_rra_cf */
642 static int parse_tag_rra(
643     xmlDoc * doc,
644     xmlNode * node,
645     rrd_t *rrd)
647     xmlNode  *child;
648     int       status;
650     rra_def_t *cur_rra_def;
651     cdp_prep_t *cur_cdp_prep;
652     rra_ptr_t *cur_rra_ptr;
654     /* Allocate more rra_def space for this RRA */
655     {                   /* {{{ */
656         rra_def_t *temp;
658         temp = (rra_def_t *) realloc(rrd->rra_def,
659                                      sizeof(rra_def_t) *
660                                      (rrd->stat_head->rra_cnt + 1));
661         if (temp == NULL) {
662             rrd_set_error("parse_tag_rra: realloc failed.");
663             return (-1);
664         }
665         rrd->rra_def = temp;
666         cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
667         memset(cur_rra_def, '\0', sizeof(rra_def_t));
668     }                   /* }}} */
670     /* allocate cdp_prep_t */
671     {                   /* {{{ */
672         cdp_prep_t *temp;
674         temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
675                                       * rrd->stat_head->ds_cnt
676                                       * (rrd->stat_head->rra_cnt + 1));
677         if (temp == NULL) {
678             rrd_set_error("parse_tag_rra: realloc failed.");
679             return (-1);
680         }
681         rrd->cdp_prep = temp;
682         cur_cdp_prep = rrd->cdp_prep
683             + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
684         memset(cur_cdp_prep, '\0',
685                sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
686     }                   /* }}} */
688     /* allocate rra_ptr_t */
689     {                   /* {{{ */
690         rra_ptr_t *temp;
692         temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
693                                      sizeof(rra_ptr_t) *
694                                      (rrd->stat_head->rra_cnt + 1));
695         if (temp == NULL) {
696             rrd_set_error("parse_tag_rra: realloc failed.");
697             return (-1);
698         }
699         rrd->rra_ptr = temp;
700         cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
701         memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
702     }                   /* }}} */
704     /* All space successfully allocated, increment number of RRAs. */
705     rrd->stat_head->rra_cnt++;
707     status = 0;
708     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
709         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
710             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
711             /* ignore */ ;
712         else if (xmlStrcmp(child->name, (const xmlChar *) "cf") == 0)
713             status = parse_tag_rra_cf(doc, child, cur_rra_def);
714         else if (xmlStrcmp(child->name, (const xmlChar *) "pdp_per_row") == 0)
715             status = get_ulong_from_node(doc, child,
716                                         &cur_rra_def->pdp_cnt);
717         else if (atoi(rrd->stat_head->version) == 1
718                  && xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
719             status = get_double_from_node(doc, child,
720                                           (double *) &cur_rra_def->
721                                           par[RRA_cdp_xff_val].u_val);
722         else if (atoi(rrd->stat_head->version) >= 2
723                  && xmlStrcmp(child->name, (const xmlChar *) "params") == 0)
724             status = parse_tag_rra_params(doc, child, cur_rra_def);
725         else if (xmlStrcmp(child->name, (const xmlChar *) "cdp_prep") == 0)
726             status = parse_tag_rra_cdp_prep(doc, child, rrd, cur_cdp_prep);
727         else if (xmlStrcmp(child->name, (const xmlChar *) "database") == 0)
728             status = parse_tag_rra_database(doc, child, rrd);
729         else {
730             rrd_set_error("parse_tag_rra: Unknown tag: %s", child->name);
731             status = -1;
732         }
734         if (status != 0)
735             break;
736     }
738     /* Set the RRA pointer to a random location */
739 #ifdef WIN32
740     cur_rra_ptr->cur_row = rand() % cur_rra_def->row_cnt;
741 #else
742     cur_rra_ptr->cur_row = random() % cur_rra_def->row_cnt;
743 #endif
745     return (status);
746 }                       /* int parse_tag_rra */
748 /*
749  * Parse a DS definition
750  */
751 static int parse_tag_ds_cdef(
752     xmlDoc * doc,
753     xmlNode * node,
754     rrd_t *rrd)
756     char      buffer[1024];
757     int       status;
759     status = get_string_from_node(doc, node, buffer, sizeof(buffer));
760     if (status != 0)
761         return (-1);
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);
767     return (0);
768 }                       /* int parse_tag_ds_cdef */
770 static int parse_tag_ds_type(
771     xmlDoc * doc,
772     xmlNode * node,
773     ds_def_t *ds_def)
775     int       status;
777     status = get_string_from_node(doc, node,
778                                   ds_def->dst, sizeof(ds_def->dst));
779     if (status != 0)
780         return (-1);
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     }
789     return (0);
790 }                       /* int parse_tag_ds_type */
792 static int parse_tag_ds(
793     xmlDoc * doc,
794     xmlNode * node,
795     rrd_t *rrd)
797     xmlNode  *child;
798     int       status;
800     ds_def_t *cur_ds_def;
801     pdp_prep_t *cur_pdp_prep;
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     }
814     /* Allocate space for the new DS definition */
815     {                   /* {{{ */
816         ds_def_t *temp;
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     }                   /* }}} */
830     /* Allocate pdp_prep space for the new DS definition */
831     {                   /* {{{ */
832         pdp_prep_t *temp;
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     }                   /* }}} */
846     /* All allocations successful, let's increment the number of DSes. */
847     rrd->stat_head->ds_cnt++;
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         }
890         if (status != 0)
891             break;
892     }
894     return (status);
895 }                       /* int parse_tag_ds */
897 /*
898  * Parse root nodes
899  */
900 static int parse_tag_rrd(
901     xmlDoc * doc,
902     xmlNode * node,
903     rrd_t *rrd)
905     xmlNode  *child;
906     int       status;
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         }
932         if (status != 0)
933             break;
934     }
936     return (status);
937 }                       /* int parse_tag_rrd */
939 static rrd_t *parse_file(
940     const char *filename)
942     xmlDoc   *doc;
943     xmlNode  *cur;
944     int       status;
946     rrd_t    *rrd;
948     doc = xmlParseFile(filename);
949     if (doc == NULL) {
950         rrd_set_error("Document not parsed successfully.");
951         return (NULL);
952     }
954     cur = xmlDocGetRootElement(doc);
955     if (cur == NULL) {
956         rrd_set_error("Document is empty.");
957         xmlFreeDoc(doc);
958         return (NULL);
959     }
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     }
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));
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));
985     strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
986     rrd->stat_head->float_cookie = FLOAT_COOKIE;
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));
998     status = parse_tag_rrd(doc, cur, rrd);
1000     xmlFreeDoc(doc);
1001     if (status != 0) {
1002         rrd_free(rrd);
1003         rrd = NULL;
1004     }
1006     return (rrd);
1007 }                       /* rrd_t *parse_file */
1009 static int write_file(
1010     const char *file_name,
1011     rrd_t *rrd)
1013     FILE     *fh;
1014     unsigned int i;
1015     unsigned int rra_offset;
1017     if (strcmp("-", file_name) == 0)
1018         fh = stdout;
1019     else {
1020         int       fd_flags = O_WRONLY | O_CREAT;
1021         int       fd;
1023 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1024         fd_flags |= O_BINARY;
1025 #endif
1027         if (opt_force_overwrite == 0)
1028             fd_flags |= O_EXCL;
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         }
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);
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;
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);
1068         fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1069                sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1071         rra_offset += num_rows;
1072     }
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     }
1081     fclose(fh);
1082     return (0);
1083 }                       /* int write_file */
1085 int rrd_restore(
1086     int argc,
1087     char **argv)
1089     rrd_t    *rrd;
1091 #ifdef WIN32
1092     srand((unsigned int) time(NULL));
1093 #else
1094     srandom((unsigned int) time(NULL) + (unsigned int) getpid());
1095 #endif
1096     /* init rrd clean */
1097     optind = 0;
1098     opterr = 0;         /* initialize getopt */
1099     while (42) {
1100         int       opt;
1101         int       option_index = 0;
1102         static struct option long_options[] = {
1103             {"range-check", no_argument, 0, 'r'},
1104             {"force-overwrite", no_argument, 0, 'f'},
1105             {0, 0, 0, 0}
1106         };
1108         opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1110         if (opt == EOF)
1111             break;
1113         switch (opt) {
1114         case 'r':
1115             opt_range_check = 1;
1116             break;
1118         case 'f':
1119             opt_force_overwrite = 1;
1120             break;
1122         default:
1123             rrd_set_error("usage rrdtool %s [--range-check|-r] "
1124                           "[--force-overwrite/-f]  file.xml file.rrd",
1125                           argv[0]);
1126             return (-1);
1127             break;
1128         }
1129     }                   /* while (42) */
1131     if ((argc - optind) != 2) {
1132         rrd_set_error("usage rrdtool %s [--range-check/-r] "
1133                       "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1134         return (-1);
1135     }
1137     rrd = parse_file(argv[optind]);
1138     if (rrd == NULL)
1139         return (-1);
1141     if (write_file(argv[optind + 1], rrd) != 0) {
1142         rrd_free(rrd);
1143         return (-1);
1144     }
1146     rrd_free(rrd);
1147     return (0);
1148 }                       /* int rrd_restore */
1150 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */