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