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