Code

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