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