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