fix from alex
[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 #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->
680                                           par[RRA_cdp_xff_val].u_val);
681         else if (atoi(rrd->stat_head->version) >= 2
682                  && xmlStrcmp(child->name, (const xmlChar *) "params") == 0)
683             status = parse_tag_rra_params(doc, child, cur_rra_def);
684         else if (xmlStrcmp(child->name, (const xmlChar *) "cdp_prep") == 0)
685             status = parse_tag_rra_cdp_prep(doc, child, rrd, cur_cdp_prep);
686         else if (xmlStrcmp(child->name, (const xmlChar *) "database") == 0)
687             status = parse_tag_rra_database(doc, child, rrd);
688         else {
689             rrd_set_error("parse_tag_rra: Unknown tag: %s", child->name);
690             status = -1;
691         }
692
693         if (status != 0)
694             break;
695     }
696
697     /* Set the RRA pointer to a random location */
698     cur_rra_ptr->cur_row = random() % cur_rra_def->row_cnt;
699
700     return (status);
701 }                       /* int parse_tag_rra */
702
703 /*
704  * Parse a DS definition
705  */
706 static int parse_tag_ds_cdef(
707     xmlDoc * doc,
708     xmlNode * node,
709     rrd_t *rrd)
710 {
711     char      buffer[1024];
712     int       status;
713
714     status = get_string_from_node(doc, node, buffer, sizeof(buffer));
715     if (status != 0)
716         return (-1);
717
718     /* We're always working on the last DS that has been added to the structure
719      * when we get here */
720     parseCDEF_DS(buffer, rrd, rrd->stat_head->ds_cnt - 1);
721
722     return (0);
723 }                       /* int parse_tag_ds_cdef */
724
725 static int parse_tag_ds_type(
726     xmlDoc * doc,
727     xmlNode * node,
728     ds_def_t *ds_def)
729 {
730     int       status;
731
732     status = get_string_from_node(doc, node,
733                                   ds_def->dst, sizeof(ds_def->dst));
734     if (status != 0)
735         return (-1);
736
737     status = dst_conv(ds_def->dst);
738     if (status == -1) {
739         rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
740                       ds_def->dst);
741         return (-1);
742     }
743
744     return (0);
745 }                       /* int parse_tag_ds_type */
746
747 static int parse_tag_ds(
748     xmlDoc * doc,
749     xmlNode * node,
750     rrd_t *rrd)
751 {
752     xmlNode  *child;
753     int       status;
754
755     ds_def_t *cur_ds_def;
756     pdp_prep_t *cur_pdp_prep;
757
758     /*
759      * If there are DS definitions after RRA definitions the number of values,
760      * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
761      * specific order in this case.
762      */
763     if (rrd->stat_head->rra_cnt > 0) {
764         rrd_set_error("parse_tag_ds: All data source definitions MUST "
765                       "precede the RRA definitions!");
766         return (-1);
767     }
768
769     /* Allocate space for the new DS definition */
770     {                   /* {{{ */
771         ds_def_t *temp;
772
773         temp = (ds_def_t *) realloc(rrd->ds_def,
774                                     sizeof(ds_def_t) *
775                                     (rrd->stat_head->ds_cnt + 1));
776         if (temp == NULL) {
777             rrd_set_error("parse_tag_ds: malloc failed.");
778             return (-1);
779         }
780         rrd->ds_def = temp;
781         cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
782         memset(cur_ds_def, '\0', sizeof(ds_def_t));
783     }                   /* }}} */
784
785     /* Allocate pdp_prep space for the new DS definition */
786     {                   /* {{{ */
787         pdp_prep_t *temp;
788
789         temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
790                                       sizeof(pdp_prep_t) *
791                                       (rrd->stat_head->ds_cnt + 1));
792         if (temp == NULL) {
793             rrd_set_error("parse_tag_ds: malloc failed.");
794             return (-1);
795         }
796         rrd->pdp_prep = temp;
797         cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
798         memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
799     }                   /* }}} */
800
801     /* All allocations successful, let's increment the number of DSes. */
802     rrd->stat_head->ds_cnt++;
803
804     status = 0;
805     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
806         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
807             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
808             /* ignore */ ;
809         else if (xmlStrcmp(child->name, (const xmlChar *) "name") == 0)
810             status = get_string_from_node(doc, child,
811                                           cur_ds_def->ds_nam,
812                                           sizeof(cur_ds_def->ds_nam));
813         else if (xmlStrcmp(child->name, (const xmlChar *) "type") == 0)
814             status = parse_tag_ds_type(doc, child, cur_ds_def);
815         else if (xmlStrcmp(child->name,
816                            (const xmlChar *) "minimal_heartbeat") == 0)
817             status = get_int_from_node(doc, child,
818                                        (int *) &cur_ds_def->par[DS_mrhb_cnt].
819                                        u_cnt);
820         else if (xmlStrcmp(child->name, (const xmlChar *) "min") == 0)
821             status = get_double_from_node(doc, child,
822                                           &cur_ds_def->par[DS_min_val].u_val);
823         else if (xmlStrcmp(child->name, (const xmlChar *) "max") == 0)
824             status = get_double_from_node(doc, child,
825                                           &cur_ds_def->par[DS_max_val].u_val);
826         else if (xmlStrcmp(child->name, (const xmlChar *) "cdef") == 0)
827             status = parse_tag_ds_cdef(doc, child, rrd);
828         else if (xmlStrcmp(child->name, (const xmlChar *) "last_ds") == 0)
829             status = get_string_from_node(doc, child,
830                                           cur_pdp_prep->last_ds,
831                                           sizeof(cur_pdp_prep->last_ds));
832         else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
833             status = get_double_from_node(doc, child,
834                                           &cur_pdp_prep->scratch[PDP_val].
835                                           u_val);
836         else if (xmlStrcmp(child->name, (const xmlChar *) "unknown_sec") == 0)
837             status = get_int_from_node(doc, child,
838                                        (int *) &cur_pdp_prep->
839                                        scratch[PDP_unkn_sec_cnt].u_cnt);
840         else {
841             rrd_set_error("parse_tag_ds: Unknown tag: %s", child->name);
842             status = -1;
843         }
844
845         if (status != 0)
846             break;
847     }
848
849     return (status);
850 }                       /* int parse_tag_ds */
851
852 /*
853  * Parse root nodes
854  */
855 static int parse_tag_rrd(
856     xmlDoc * doc,
857     xmlNode * node,
858     rrd_t *rrd)
859 {
860     xmlNode  *child;
861     int       status;
862
863     status = 0;
864     for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
865         if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
866             || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
867             /* ignore */ ;
868         else if (xmlStrcmp(child->name, (const xmlChar *) "version") == 0)
869             status = get_string_from_node(doc, child,
870                                           rrd->stat_head->version,
871                                           sizeof(rrd->stat_head->version));
872         else if (xmlStrcmp(child->name, (const xmlChar *) "step") == 0)
873             status = get_int_from_node(doc, child,
874                                        (int *) &rrd->stat_head->pdp_step);
875         else if (xmlStrcmp(child->name, (const xmlChar *) "lastupdate") == 0)
876             status = get_int_from_node(doc, child,
877                                        (int *) &rrd->live_head->last_up);
878         else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0)
879             status = parse_tag_ds(doc, child, rrd);
880         else if (xmlStrcmp(child->name, (const xmlChar *) "rra") == 0)
881             status = parse_tag_rra(doc, child, rrd);
882         else {
883             rrd_set_error("parse_tag_rrd: Unknown tag: %s", child->name);
884             status = -1;
885         }
886
887         if (status != 0)
888             break;
889     }
890
891     return (status);
892 }                       /* int parse_tag_rrd */
893
894 static rrd_t *parse_file(
895     const char *filename)
896 {
897     xmlDoc   *doc;
898     xmlNode  *cur;
899     int       status;
900
901     rrd_t    *rrd;
902
903     doc = xmlParseFile(filename);
904     if (doc == NULL) {
905         rrd_set_error("Document not parsed successfully.");
906         return (NULL);
907     }
908
909     cur = xmlDocGetRootElement(doc);
910     if (cur == NULL) {
911         rrd_set_error("Document is empty.");
912         xmlFreeDoc(doc);
913         return (NULL);
914     }
915
916     if (xmlStrcmp(cur->name, (const xmlChar *) "rrd") != 0) {
917         rrd_set_error
918             ("Document of the wrong type, root node is not \"rrd\".");
919         xmlFreeDoc(doc);
920         return (NULL);
921     }
922
923     rrd = (rrd_t *) malloc(sizeof(rrd_t));
924     if (rrd == NULL) {
925         rrd_set_error("parse_file: malloc failed.");
926         xmlFreeDoc(doc);
927         return (NULL);
928     }
929     memset(rrd, '\0', sizeof(rrd_t));
930
931     rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
932     if (rrd->stat_head == NULL) {
933         rrd_set_error("parse_tag_rrd: malloc failed.");
934         xmlFreeDoc(doc);
935         free(rrd);
936         return (NULL);
937     }
938     memset(rrd->stat_head, '\0', sizeof(stat_head_t));
939
940     strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
941     rrd->stat_head->float_cookie = FLOAT_COOKIE;
942
943     rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
944     if (rrd->live_head == NULL) {
945         rrd_set_error("parse_tag_rrd: malloc failed.");
946         xmlFreeDoc(doc);
947         free(rrd->stat_head);
948         free(rrd);
949         return (NULL);
950     }
951     memset(rrd->live_head, '\0', sizeof(live_head_t));
952
953     status = parse_tag_rrd(doc, cur, rrd);
954
955     xmlFreeDoc(doc);
956     if (status != 0) {
957         rrd_free(rrd);
958         rrd = NULL;
959     }
960
961     return (rrd);
962 }                       /* rrd_t *parse_file */
963
964 static int write_file(
965     const char *file_name,
966     rrd_t *rrd)
967 {
968     FILE     *fh;
969     unsigned int i;
970     unsigned int rra_offset;
971
972     if (strcmp("-", file_name) == 0)
973         fh = stdout;
974     else {
975         int       fd_flags = O_WRONLY | O_CREAT;
976         int       fd;
977
978 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
979         fd_flags |= O_BINARY;
980 #endif
981
982         if (opt_force_overwrite == 0)
983             fd_flags |= O_EXCL;
984
985         fd = open(file_name, fd_flags, 0666);
986         if (fd == -1) {
987             rrd_set_error("creating '%s': %s", file_name,
988                           rrd_strerror(errno));
989             return (-1);
990         }
991
992         fh = fdopen(fd, "wb");
993         if (fh == NULL) {
994             rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
995             close(fd);
996             return (-1);
997         }
998     }
999     if (atoi(rrd->stat_head->version) < 3) {
1000         /* we output 3 or higher */
1001         strcpy(rrd->stat_head->version, "0003");
1002     }
1003     fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1004     fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1005     fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1006     fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1007     fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1008     fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1009            rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1010     fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1011
1012     /* calculate the number of rrd_values to dump */
1013     rra_offset = 0;
1014     for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
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 +
1020                (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1021                sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1022
1023         fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1024                sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1025
1026         rra_offset += num_rows;
1027     }
1028
1029     /* lets see if we had an error */
1030     if (ferror(fh)) {
1031         rrd_set_error("a file error occurred while creating '%s'", file_name);
1032         fclose(fh);
1033         return (-1);
1034     }
1035
1036     fclose(fh);
1037     return (0);
1038 }                       /* int write_file */
1039
1040 int rrd_restore(
1041     int argc,
1042     char **argv)
1043 {
1044     rrd_t    *rrd;
1045
1046     srandom((unsigned int) time(NULL) + (unsigned int) getpid());
1047     /* init rrd clean */
1048     optind = 0;
1049     opterr = 0;         /* initialize getopt */
1050     while (42) {
1051         int       opt;
1052         int       option_index = 0;
1053         static struct option long_options[] = {
1054             {"range-check", no_argument, 0, 'r'},
1055             {"force-overwrite", no_argument, 0, 'f'},
1056             {0, 0, 0, 0}
1057         };
1058
1059         opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1060
1061         if (opt == EOF)
1062             break;
1063
1064         switch (opt) {
1065         case 'r':
1066             opt_range_check = 1;
1067             break;
1068
1069         case 'f':
1070             opt_force_overwrite = 1;
1071             break;
1072
1073         default:
1074             rrd_set_error("usage rrdtool %s [--range-check|-r] "
1075                           "[--force-overwrite/-f]  file.xml file.rrd",
1076                           argv[0]);
1077             return (-1);
1078             break;
1079         }
1080     }                   /* while (42) */
1081
1082     if ((argc - optind) != 2) {
1083         rrd_set_error("usage rrdtool %s [--range-check/-r] "
1084                       "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1085         return (-1);
1086     }
1087
1088     rrd = parse_file(argv[optind]);
1089     if (rrd == NULL)
1090         return (-1);
1091
1092     if (write_file(argv[optind + 1], rrd) != 0) {
1093         rrd_free(rrd);
1094         return (-1);
1095     }
1096
1097     rrd_free(rrd);
1098     return (0);
1099 }                       /* int rrd_restore */
1100
1101 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */