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