1 /*****************************************************************************
2 * RRDtool 1.3.0 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 *************************************************************************** */
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 **/
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;
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;
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 }
67 begin_ptr = (char *) temp0;
68 while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
69 begin_ptr++;
71 if (begin_ptr[0] == 0) {
72 xmlFree(temp0);
73 buffer[0] = 0;
74 return (0);
75 }
77 end_ptr = begin_ptr;
78 while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
79 end_ptr++;
80 end_ptr[0] = 0;
82 strncpy(buffer, begin_ptr, buffer_size);
83 buffer[buffer_size - 1] = 0;
85 xmlFree(temp0);
87 return (0);
88 } /* int get_string_from_node */
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;
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 }
105 end_ptr = NULL;
106 temp = strtol(str_ptr, &end_ptr, 0);
107 xmlFree(str_ptr);
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 }
115 *value = temp;
117 return (0);
118 } /* int get_int_from_node */
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;
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 }
135 end_ptr = NULL;
136 temp = strtod(str_ptr, &end_ptr);
137 xmlFree(str_ptr);
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 }
146 *value = temp;
148 return (0);
149 } /* int get_double_from_node */
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;
158 if (opt_range_check == 0)
159 return (0);
161 min = ds_def->par[DS_min_val].u_val;
162 max = ds_def->par[DS_max_val].u_val;
164 if (((!isnan(min)) && (*rrd_value < min))
165 || ((!isnan(max)) && (*rrd_value > max)))
166 *rrd_value = DNAN;
168 return (0);
169 } /* int value_check_range */
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;
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 }
199 values_count++;
200 } else {
201 rrd_set_error("parse_tag_rra_database_row: Unknown tag: %s",
202 child->name);
203 status = -1;
204 }
206 if (status != 0)
207 break;
208 } /* for (child = node->xmlChildrenNode) */
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 }
217 return (status);
218 } /* int parse_tag_rra_database_row */
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;
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;
235 cur_rra_def = rrd->rra_def + i;
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);
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++;
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 }
274 if (status != 0)
275 break;
276 } /* for (child = node->xmlChildrenNode) */
278 return (status);
279 } /* int parse_tag_rra_database */
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;
296 status = get_string_from_node(doc, node,
297 history_buffer, sizeof(history_buffer));
298 if (status != 0)
299 return (-1);
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;
305 return (0);
306 } /* int parse_tag_rra_cdp_prep_ds_history */
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;
317 memset(cdp_prep, '\0', sizeof(cdp_prep_t));
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);
395 while (42) {
396 if (i >= ARRAY_LENGTH(cdp_prep->scratch)) {
397 status = -1;
398 break;
399 }
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);
413 if (status != 0)
414 break;
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;
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 }
433 if (status != 0)
434 break;
435 }
437 return (status);
438 } /* int parse_tag_rra_cdp_prep_ds */
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;
449 unsigned int ds_count = 0;
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 }
470 if (status != 0)
471 break;
472 }
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 }
481 return (status);
482 } /* int parse_tag_rra_cdp_prep */
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;
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;
568 while (42) {
569 if (i >= ARRAY_LENGTH(rra_def->par)) {
570 status = -1;
571 break;
572 }
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);
584 if (status != 0)
585 break;
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;
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 }
604 if (status != 0)
605 break;
606 }
608 return (status);
609 } /* int parse_tag_rra_params */
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;
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);
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 }
633 return (0);
634 } /* int parse_tag_rra_cf */
636 static int parse_tag_rra(
637 xmlDoc * doc,
638 xmlNode * node,
639 rrd_t *rrd)
640 {
641 xmlNode *child;
642 int status;
644 rra_def_t *cur_rra_def;
645 cdp_prep_t *cur_cdp_prep;
646 rra_ptr_t *cur_rra_ptr;
648 /* Allocate more rra_def space for this RRA */
649 { /* {{{ */
650 rra_def_t *temp;
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 } /* }}} */
664 /* allocate cdp_prep_t */
665 { /* {{{ */
666 cdp_prep_t *temp;
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 } /* }}} */
682 /* allocate rra_ptr_t */
683 { /* {{{ */
684 rra_ptr_t *temp;
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 } /* }}} */
698 /* All space successfully allocated, increment number of RRAs. */
699 rrd->stat_head->rra_cnt++;
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 }
722 if (status != 0)
723 break;
724 }
726 /* Set the RRA pointer to the last value in the archive */
727 cur_rra_ptr->cur_row = cur_rra_def->row_cnt - 1;
729 return (status);
730 } /* int parse_tag_rra */
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;
743 status = get_string_from_node(doc, node, buffer, sizeof(buffer));
744 if (status != 0)
745 return (-1);
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);
751 return (0);
752 } /* int parse_tag_ds_cdef */
754 static int parse_tag_ds_type(
755 xmlDoc * doc,
756 xmlNode * node,
757 ds_def_t *ds_def)
758 {
759 int status;
761 status = get_string_from_node(doc, node,
762 ds_def->dst, sizeof(ds_def->dst));
763 if (status != 0)
764 return (-1);
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 }
773 return (0);
774 } /* int parse_tag_ds_type */
776 static int parse_tag_ds(
777 xmlDoc * doc,
778 xmlNode * node,
779 rrd_t *rrd)
780 {
781 xmlNode *child;
782 int status;
784 ds_def_t *cur_ds_def;
785 pdp_prep_t *cur_pdp_prep;
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 }
798 /* Allocate space for the new DS definition */
799 { /* {{{ */
800 ds_def_t *temp;
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 } /* }}} */
814 /* Allocate pdp_prep space for the new DS definition */
815 { /* {{{ */
816 pdp_prep_t *temp;
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 } /* }}} */
830 /* All allocations successful, let's increment the number of DSes. */
831 rrd->stat_head->ds_cnt++;
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 }
874 if (status != 0)
875 break;
876 }
878 return (status);
879 } /* int parse_tag_ds */
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;
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 }
916 if (status != 0)
917 break;
918 }
920 return (status);
921 } /* int parse_tag_rrd */
923 static rrd_t *parse_file(
924 const char *filename)
925 {
926 xmlDoc *doc;
927 xmlNode *cur;
928 int status;
930 rrd_t *rrd;
932 doc = xmlParseFile(filename);
933 if (doc == NULL) {
934 rrd_set_error("Document not parsed successfully.");
935 return (NULL);
936 }
938 cur = xmlDocGetRootElement(doc);
939 if (cur == NULL) {
940 rrd_set_error("Document is empty.");
941 xmlFreeDoc(doc);
942 return (NULL);
943 }
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 }
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));
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));
969 strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
970 rrd->stat_head->float_cookie = FLOAT_COOKIE;
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));
982 status = parse_tag_rrd(doc, cur, rrd);
984 xmlFreeDoc(doc);
985 if (status != 0) {
986 rrd_free(rrd);
987 rrd = NULL;
988 }
990 return (rrd);
991 } /* rrd_t *parse_file */
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;
1001 if (strcmp("-", file_name) == 0)
1002 fh = stdout;
1003 else {
1004 int fd_flags = O_WRONLY | O_CREAT;
1005 int fd;
1007 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1008 fd_flags |= O_BINARY;
1009 #endif
1011 if (opt_force_overwrite == 0)
1012 fd_flags |= O_EXCL;
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 }
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 }
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);
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);
1043 fwrite(rrd->rrd_value, sizeof(rrd_value_t), value_count, fh);
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 }
1052 fclose(fh);
1053 return (0);
1054 } /* int write_file */
1056 int rrd_restore(
1057 int argc,
1058 char **argv)
1059 {
1060 rrd_t *rrd;
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 };
1074 opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1076 if (opt == EOF)
1077 break;
1079 switch (opt) {
1080 case 'r':
1081 opt_range_check = 1;
1082 break;
1084 case 'f':
1085 opt_force_overwrite = 1;
1086 break;
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) */
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 }
1103 rrd = parse_file(argv[optind]);
1104 if (rrd == NULL)
1105 return (-1);
1107 if (write_file(argv[optind + 1], rrd) != 0) {
1108 rrd_free(rrd);
1109 return (-1);
1110 }
1112 rrd_free(rrd);
1113 return (0);
1114 } /* int rrd_restore */
1116 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */