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