Code

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