ef524b2ed9fc663b296bc49e6346f4ff6da79045
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$
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_long_long_from_node(
155 xmlDoc * doc,
156 xmlNode * node,
157 long long *value)
158 {
159 long long 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_ulong_from_node: xmlNodeListGetString failed.");
166 return (-1);
167 }
169 end_ptr = NULL;
170 temp = strtoll(str_ptr, &end_ptr, 0);
171 xmlFree(str_ptr);
173 if (str_ptr == end_ptr) {
174 rrd_set_error("get_long_long_from_node: Cannot parse buffer as unsigned long long: %s",
175 str_ptr);
176 return (-1);
177 }
179 *value = temp;
181 return (0);
182 } /* int get_ulong_from_node */
184 static int get_double_from_node(
185 xmlDoc * doc,
186 xmlNode * node,
187 double *value)
188 {
189 double temp;
190 char *str_ptr;
191 char *end_ptr;
193 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
194 if (str_ptr == NULL) {
195 rrd_set_error("get_double_from_node: xmlNodeListGetString failed.");
196 return (-1);
197 }
199 if (strstr(str_ptr, "NaN") != NULL)
200 {
201 *value = DNAN;
202 xmlFree(str_ptr);
203 return 0;
204 }
206 end_ptr = NULL;
207 temp = strtod(str_ptr, &end_ptr);
208 xmlFree(str_ptr);
210 if (str_ptr == end_ptr) {
211 rrd_set_error
212 ("get_double_from_node: Cannot parse buffer as double: %s",
213 str_ptr);
214 return (-1);
215 }
217 *value = temp;
219 return (0);
220 } /* int get_double_from_node */
222 static int value_check_range(
223 rrd_value_t *rrd_value,
224 const ds_def_t *ds_def)
225 {
226 double min;
227 double max;
229 if (opt_range_check == 0)
230 return (0);
232 min = ds_def->par[DS_min_val].u_val;
233 max = ds_def->par[DS_max_val].u_val;
235 if (((!isnan(min)) && (*rrd_value < min))
236 || ((!isnan(max)) && (*rrd_value > max)))
237 *rrd_value = DNAN;
239 return (0);
240 } /* int value_check_range */
242 /*
243 * Parse the <database> block within an RRA definition
244 */
245 static int parse_tag_rra_database_row(
246 xmlDoc * doc,
247 xmlNode * node,
248 rrd_t *rrd,
249 rrd_value_t *rrd_value)
250 {
251 unsigned int values_count = 0;
252 xmlNode *child;
253 int status;
255 status = 0;
256 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
257 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
258 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
259 /* ignore */ ;
260 else if (xmlStrcmp(child->name, (const xmlChar *) "v") == 0) {
261 if (values_count < rrd->stat_head->ds_cnt) {
262 status =
263 get_double_from_node(doc, child,
264 rrd_value + values_count);
265 if (status == 0)
266 value_check_range(rrd_value + values_count,
267 rrd->ds_def + values_count);
268 }
270 values_count++;
271 } else {
272 rrd_set_error("parse_tag_rra_database_row: Unknown tag: %s",
273 child->name);
274 status = -1;
275 }
277 if (status != 0)
278 break;
279 } /* for (child = node->xmlChildrenNode) */
281 if (values_count != rrd->stat_head->ds_cnt) {
282 rrd_set_error("parse_tag_rra_database_row: Row has %u values "
283 "and RRD has %lu data sources.",
284 values_count, rrd->stat_head->ds_cnt);
285 status = -1;
286 }
288 return (status);
289 } /* int parse_tag_rra_database_row */
291 static int parse_tag_rra_database(
292 xmlDoc * doc,
293 xmlNode * node,
294 rrd_t *rrd)
295 {
296 rra_def_t *cur_rra_def;
297 unsigned int total_row_cnt;
298 xmlNode *child;
299 int status;
300 int i;
302 total_row_cnt = 0;
303 for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
304 total_row_cnt += rrd->rra_def[i].row_cnt;
306 cur_rra_def = rrd->rra_def + i;
308 status = 0;
309 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
310 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
311 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
312 /* ignore */ ;
313 else if (xmlStrcmp(child->name, (const xmlChar *) "row") == 0) {
314 rrd_value_t *temp;
315 rrd_value_t *cur_rrd_value;
316 unsigned int total_values_count = rrd->stat_head->ds_cnt
317 * (total_row_cnt + 1);
319 /* Allocate space for the new values.. */
320 temp = (rrd_value_t *) realloc(rrd->rrd_value,
321 sizeof(rrd_value_t) *
322 total_values_count);
323 if (temp == NULL) {
324 rrd_set_error("parse_tag_rra_database: realloc failed.");
325 status = -1;
326 break;
327 }
328 rrd->rrd_value = temp;
329 cur_rrd_value = rrd->rrd_value
330 + (rrd->stat_head->ds_cnt * total_row_cnt);
331 memset(cur_rrd_value, '\0',
332 sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
333 total_row_cnt++;
334 cur_rra_def->row_cnt++;
336 status =
337 parse_tag_rra_database_row(doc, child, rrd, cur_rrd_value);
338 } /* if (xmlStrcmp (child->name, (const xmlChar *) "row") == 0) */
339 else {
340 rrd_set_error("parse_tag_rra_database: Unknown tag: %s",
341 child->name);
342 status = -1;
343 }
345 if (status != 0)
346 break;
347 } /* for (child = node->xmlChildrenNode) */
349 return (status);
350 } /* int parse_tag_rra_database */
352 /*
353 * Parse the <cdp_prep> block within an RRA definition
354 */
355 static int parse_tag_rra_cdp_prep_ds_history(
356 xmlDoc * doc,
357 xmlNode * node,
358 cdp_prep_t *cdp_prep)
359 {
360 /* Make `history_buffer' the same size as the scratch area, plus the
361 * terminating NULL byte. */
362 char history_buffer[sizeof(((cdp_prep_t *)0)->scratch) + 1];
363 char *history_ptr;
364 int status;
365 int i;
367 status = get_string_from_node(doc, node,
368 history_buffer, sizeof(history_buffer));
369 if (status != 0)
370 return (-1);
372 history_ptr = (char *) (&cdp_prep->scratch[0]);
373 for (i = 0; history_buffer[i] != '\0'; i++)
374 history_ptr[i] = (history_buffer[i] == '1') ? 1 : 0;
376 return (0);
377 } /* int parse_tag_rra_cdp_prep_ds_history */
379 static int parse_tag_rra_cdp_prep_ds(
380 xmlDoc * doc,
381 xmlNode * node,
382 rrd_t *rrd,
383 cdp_prep_t *cdp_prep)
384 {
385 xmlNode *child;
386 int status;
388 memset(cdp_prep, '\0', sizeof(cdp_prep_t));
390 status = 0;
391 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
392 if (atoi(rrd->stat_head->version) == 1) {
393 cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
394 cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
395 }
396 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
397 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
398 /* ignore */ ;
399 else if (xmlStrcmp(child->name, (const xmlChar *) "primary_value") ==
400 0)
401 status =
402 get_double_from_node(doc, child,
403 &cdp_prep->scratch[CDP_primary_val].
404 u_val);
405 else if (xmlStrcmp(child->name, (const xmlChar *) "secondary_value")
406 == 0)
407 status =
408 get_double_from_node(doc, child,
409 &cdp_prep->scratch[CDP_secondary_val].
410 u_val);
411 else if (xmlStrcmp(child->name, (const xmlChar *) "intercept") == 0)
412 status = get_double_from_node(doc, child,
413 &cdp_prep->
414 scratch[CDP_hw_intercept].u_val);
415 else if (xmlStrcmp(child->name, (const xmlChar *) "last_intercept") ==
416 0)
417 status =
418 get_double_from_node(doc, child,
419 &cdp_prep->
420 scratch[CDP_hw_last_intercept].u_val);
421 else if (xmlStrcmp(child->name, (const xmlChar *) "slope") == 0)
422 status = get_double_from_node(doc, child,
423 &cdp_prep->scratch[CDP_hw_slope].
424 u_val);
425 else if (xmlStrcmp(child->name, (const xmlChar *) "last_slope") == 0)
426 status = get_double_from_node(doc, child,
427 &cdp_prep->
428 scratch[CDP_hw_last_slope].u_val);
429 else if (xmlStrcmp(child->name, (const xmlChar *) "nan_count") == 0)
430 status = get_ulong_from_node(doc, child,
431 &cdp_prep->
432 scratch[CDP_null_count].u_cnt);
433 else if (xmlStrcmp(child->name, (const xmlChar *) "last_nan_count") ==
434 0)
435 status =
436 get_ulong_from_node(doc, child,
437 &cdp_prep->
438 scratch[CDP_last_null_count].u_cnt);
439 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal") == 0)
440 status = get_double_from_node(doc, child,
441 &cdp_prep->scratch[CDP_hw_seasonal].
442 u_val);
443 else if (xmlStrcmp(child->name, (const xmlChar *) "last_seasonal") ==
444 0)
445 status =
446 get_double_from_node(doc, child,
447 &cdp_prep->scratch[CDP_hw_last_seasonal].
448 u_val);
449 else if (xmlStrcmp(child->name, (const xmlChar *) "init_flag") == 0)
450 status = get_ulong_from_node(doc, child,
451 &cdp_prep->
452 scratch[CDP_init_seasonal].u_cnt);
453 else if (xmlStrcmp(child->name, (const xmlChar *) "history") == 0)
454 status = parse_tag_rra_cdp_prep_ds_history(doc, child, cdp_prep);
455 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
456 status = get_double_from_node(doc, child,
457 &cdp_prep->scratch[CDP_val].u_val);
458 else if (xmlStrcmp(child->name,
459 (const xmlChar *) "unknown_datapoints") == 0)
460 status = get_ulong_from_node(doc, child,
461 &cdp_prep->
462 scratch[CDP_unkn_pdp_cnt].u_cnt);
463 else {
464 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
465 child->name);
466 status = -1;
467 }
469 if (status != 0)
470 break;
471 }
473 return (status);
474 } /* int parse_tag_rra_cdp_prep_ds */
476 static int parse_tag_rra_cdp_prep(
477 xmlDoc * doc,
478 xmlNode * node,
479 rrd_t *rrd,
480 cdp_prep_t *cdp_prep)
481 {
482 xmlNode *child;
483 int status;
485 unsigned int ds_count = 0;
487 status = 0;
488 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
489 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
490 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
491 /* ignore */ ;
492 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0) {
493 if (ds_count >= rrd->stat_head->ds_cnt)
494 status = -1;
495 else {
496 status = parse_tag_rra_cdp_prep_ds(doc, child, rrd,
497 cdp_prep + ds_count);
498 ds_count++;
499 }
500 } else {
501 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
502 child->name);
503 status = -1;
504 }
506 if (status != 0)
507 break;
508 }
510 if (ds_count != rrd->stat_head->ds_cnt) {
511 rrd_set_error("parse_tag_rra_cdp_prep: There are %i data sources in "
512 "the RRD file, but %i in this cdp_prep block!",
513 (int) rrd->stat_head->ds_cnt, ds_count);
514 status = -1;
515 }
517 return (status);
518 } /* int parse_tag_rra_cdp_prep */
520 /*
521 * Parse the <params> block within an RRA definition
522 */
523 static int parse_tag_rra_params(
524 xmlDoc * doc,
525 xmlNode * node,
526 rra_def_t *rra_def)
527 {
528 xmlNode *child;
529 int status;
531 status = 0;
532 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
533 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
534 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
535 /* ignore */ ;
536 /*
537 * Parameters for CF_HWPREDICT
538 */
539 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_alpha") == 0)
540 status = get_double_from_node(doc, child,
541 &rra_def->par[RRA_hw_alpha].u_val);
542 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_beta") == 0)
543 status = get_double_from_node(doc, child,
544 &rra_def->par[RRA_hw_beta].u_val);
545 else if (xmlStrcmp(child->name,
546 (const xmlChar *) "dependent_rra_idx") == 0)
547 status = get_ulong_from_node(doc, child,
548 &rra_def->
549 par[RRA_dependent_rra_idx].u_cnt);
550 /*
551 * Parameters for CF_SEASONAL and CF_DEVSEASONAL
552 */
553 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal_gamma") ==
554 0)
555 status =
556 get_double_from_node(doc, child,
557 &rra_def->par[RRA_seasonal_gamma].u_val);
558 else if (xmlStrcmp
559 (child->name, (const xmlChar *) "seasonal_smooth_idx") == 0)
560 status =
561 get_ulong_from_node(doc, child,
562 &rra_def->
563 par[RRA_seasonal_smooth_idx].u_cnt);
564 else if (xmlStrcmp(child->name, (const xmlChar *) "smoothing_window")
565 == 0)
566 status =
567 get_double_from_node(doc, child,
568 &rra_def->
569 par[RRA_seasonal_smoothing_window].
570 u_val);
571 /* else if (dependent_rra_idx) ...; */
572 /*
573 * Parameters for CF_FAILURES
574 */
575 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_pos") == 0)
576 status = get_double_from_node(doc, child,
577 &rra_def->par[RRA_delta_pos].u_val);
578 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_neg") == 0)
579 status = get_double_from_node(doc, child,
580 &rra_def->par[RRA_delta_neg].u_val);
581 else if (xmlStrcmp(child->name, (const xmlChar *) "window_len") == 0)
582 status = get_ulong_from_node(doc, child,
583 &rra_def->par[RRA_window_len].
584 u_cnt);
585 else if (xmlStrcmp(child->name, (const xmlChar *) "failure_threshold")
586 == 0)
587 status =
588 get_ulong_from_node(doc, child,
589 &rra_def->
590 par[RRA_failure_threshold].u_cnt);
591 /*
592 * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
593 */
594 else if (xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
595 status = get_double_from_node(doc, child,
596 &rra_def->par[RRA_cdp_xff_val].
597 u_val);
598 /*
599 * Compatibility code for 1.0.49
600 */
601 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) { /* {{{ */
602 unsigned int i = 0;
604 while (42) {
605 if (i >= ARRAY_LENGTH(rra_def->par)) {
606 status = -1;
607 break;
608 }
610 if ((i == RRA_dependent_rra_idx)
611 || (i == RRA_seasonal_smooth_idx)
612 || (i == RRA_failure_threshold))
613 status = get_ulong_from_node(doc, child,
614 &rra_def->par[i].
615 u_cnt);
616 else
617 status = get_double_from_node(doc, child,
618 &rra_def->par[i].u_val);
620 if (status != 0)
621 break;
623 /* When this loops exits (sucessfully) `child' points to the last
624 * `value' tag in the list. */
625 if ((child->next == NULL)
626 || (xmlStrcmp(child->name, (const xmlChar *) "value") !=
627 0))
628 break;
630 child = child->next;
631 i++;
632 }
633 } /* }}} */
634 else {
635 rrd_set_error("parse_tag_rra_params: Unknown tag: %s",
636 child->name);
637 status = -1;
638 }
640 if (status != 0)
641 break;
642 }
644 return (status);
645 } /* int parse_tag_rra_params */
647 /*
648 * Parse an RRA definition
649 */
650 static int parse_tag_rra_cf(
651 xmlDoc * doc,
652 xmlNode * node,
653 rra_def_t *rra_def)
654 {
655 int status;
657 status = get_string_from_node(doc, node,
658 rra_def->cf_nam, sizeof(rra_def->cf_nam));
659 if (status != 0)
660 return (-1);
662 status = cf_conv(rra_def->cf_nam);
663 if (status == -1) {
664 rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
665 rra_def->cf_nam);
666 return (-1);
667 }
669 return (0);
670 } /* int parse_tag_rra_cf */
672 static int parse_tag_rra(
673 xmlDoc * doc,
674 xmlNode * node,
675 rrd_t *rrd)
676 {
677 xmlNode *child;
678 int status;
680 rra_def_t *cur_rra_def;
681 cdp_prep_t *cur_cdp_prep;
682 rra_ptr_t *cur_rra_ptr;
684 /* Allocate more rra_def space for this RRA */
685 { /* {{{ */
686 rra_def_t *temp;
688 temp = (rra_def_t *) realloc(rrd->rra_def,
689 sizeof(rra_def_t) *
690 (rrd->stat_head->rra_cnt + 1));
691 if (temp == NULL) {
692 rrd_set_error("parse_tag_rra: realloc failed.");
693 return (-1);
694 }
695 rrd->rra_def = temp;
696 cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
697 memset(cur_rra_def, '\0', sizeof(rra_def_t));
698 } /* }}} */
700 /* allocate cdp_prep_t */
701 { /* {{{ */
702 cdp_prep_t *temp;
704 temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
705 * rrd->stat_head->ds_cnt
706 * (rrd->stat_head->rra_cnt + 1));
707 if (temp == NULL) {
708 rrd_set_error("parse_tag_rra: realloc failed.");
709 return (-1);
710 }
711 rrd->cdp_prep = temp;
712 cur_cdp_prep = rrd->cdp_prep
713 + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
714 memset(cur_cdp_prep, '\0',
715 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
716 } /* }}} */
718 /* allocate rra_ptr_t */
719 { /* {{{ */
720 rra_ptr_t *temp;
722 temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
723 sizeof(rra_ptr_t) *
724 (rrd->stat_head->rra_cnt + 1));
725 if (temp == NULL) {
726 rrd_set_error("parse_tag_rra: realloc failed.");
727 return (-1);
728 }
729 rrd->rra_ptr = temp;
730 cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
731 memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
732 } /* }}} */
734 /* All space successfully allocated, increment number of RRAs. */
735 rrd->stat_head->rra_cnt++;
737 status = 0;
738 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
739 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
740 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
741 /* ignore */ ;
742 else if (xmlStrcmp(child->name, (const xmlChar *) "cf") == 0)
743 status = parse_tag_rra_cf(doc, child, cur_rra_def);
744 else if (xmlStrcmp(child->name, (const xmlChar *) "pdp_per_row") == 0)
745 status = get_ulong_from_node(doc, child,
746 &cur_rra_def->pdp_cnt);
747 else if (atoi(rrd->stat_head->version) == 1
748 && xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
749 status = get_double_from_node(doc, child,
750 (double *) &cur_rra_def->
751 par[RRA_cdp_xff_val].u_val);
752 else if (atoi(rrd->stat_head->version) >= 2
753 && xmlStrcmp(child->name, (const xmlChar *) "params") == 0)
754 status = parse_tag_rra_params(doc, child, cur_rra_def);
755 else if (xmlStrcmp(child->name, (const xmlChar *) "cdp_prep") == 0)
756 status = parse_tag_rra_cdp_prep(doc, child, rrd, cur_cdp_prep);
757 else if (xmlStrcmp(child->name, (const xmlChar *) "database") == 0)
758 status = parse_tag_rra_database(doc, child, rrd);
759 else {
760 rrd_set_error("parse_tag_rra: Unknown tag: %s", child->name);
761 status = -1;
762 }
764 if (status != 0)
765 break;
766 }
768 /* Set the RRA pointer to a random location */
769 #ifdef WIN32
770 cur_rra_ptr->cur_row = rand() % cur_rra_def->row_cnt;
771 #else
772 cur_rra_ptr->cur_row = random() % cur_rra_def->row_cnt;
773 #endif
775 return (status);
776 } /* int parse_tag_rra */
778 /*
779 * Parse a DS definition
780 */
781 static int parse_tag_ds_cdef(
782 xmlDoc * doc,
783 xmlNode * node,
784 rrd_t *rrd)
785 {
786 char buffer[1024];
787 int status;
789 status = get_string_from_node(doc, node, buffer, sizeof(buffer));
790 if (status != 0)
791 return (-1);
793 /* We're always working on the last DS that has been added to the structure
794 * when we get here */
795 parseCDEF_DS(buffer, rrd, rrd->stat_head->ds_cnt - 1);
797 return (0);
798 } /* int parse_tag_ds_cdef */
800 static int parse_tag_ds_type(
801 xmlDoc * doc,
802 xmlNode * node,
803 ds_def_t *ds_def)
804 {
805 int status;
807 status = get_string_from_node(doc, node,
808 ds_def->dst, sizeof(ds_def->dst));
809 if (status != 0)
810 return (-1);
812 status = dst_conv(ds_def->dst);
813 if (status == -1) {
814 rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
815 ds_def->dst);
816 return (-1);
817 }
819 return (0);
820 } /* int parse_tag_ds_type */
822 static int parse_tag_ds(
823 xmlDoc * doc,
824 xmlNode * node,
825 rrd_t *rrd)
826 {
827 xmlNode *child;
828 int status;
830 ds_def_t *cur_ds_def;
831 pdp_prep_t *cur_pdp_prep;
833 /*
834 * If there are DS definitions after RRA definitions the number of values,
835 * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
836 * specific order in this case.
837 */
838 if (rrd->stat_head->rra_cnt > 0) {
839 rrd_set_error("parse_tag_ds: All data source definitions MUST "
840 "precede the RRA definitions!");
841 return (-1);
842 }
844 /* Allocate space for the new DS definition */
845 { /* {{{ */
846 ds_def_t *temp;
848 temp = (ds_def_t *) realloc(rrd->ds_def,
849 sizeof(ds_def_t) *
850 (rrd->stat_head->ds_cnt + 1));
851 if (temp == NULL) {
852 rrd_set_error("parse_tag_ds: malloc failed.");
853 return (-1);
854 }
855 rrd->ds_def = temp;
856 cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
857 memset(cur_ds_def, '\0', sizeof(ds_def_t));
858 } /* }}} */
860 /* Allocate pdp_prep space for the new DS definition */
861 { /* {{{ */
862 pdp_prep_t *temp;
864 temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
865 sizeof(pdp_prep_t) *
866 (rrd->stat_head->ds_cnt + 1));
867 if (temp == NULL) {
868 rrd_set_error("parse_tag_ds: malloc failed.");
869 return (-1);
870 }
871 rrd->pdp_prep = temp;
872 cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
873 memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
874 } /* }}} */
876 /* All allocations successful, let's increment the number of DSes. */
877 rrd->stat_head->ds_cnt++;
879 status = 0;
880 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
881 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
882 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
883 /* ignore */ ;
884 else if (xmlStrcmp(child->name, (const xmlChar *) "name") == 0)
885 status = get_string_from_node(doc, child,
886 cur_ds_def->ds_nam,
887 sizeof(cur_ds_def->ds_nam));
888 else if (xmlStrcmp(child->name, (const xmlChar *) "type") == 0)
889 status = parse_tag_ds_type(doc, child, cur_ds_def);
890 else if (xmlStrcmp(child->name,
891 (const xmlChar *) "minimal_heartbeat") == 0)
892 status = get_ulong_from_node(doc, child,
893 &cur_ds_def->par[DS_mrhb_cnt].
894 u_cnt);
895 else if (xmlStrcmp(child->name, (const xmlChar *) "min") == 0)
896 status = get_double_from_node(doc, child,
897 &cur_ds_def->par[DS_min_val].u_val);
898 else if (xmlStrcmp(child->name, (const xmlChar *) "max") == 0)
899 status = get_double_from_node(doc, child,
900 &cur_ds_def->par[DS_max_val].u_val);
901 else if (xmlStrcmp(child->name, (const xmlChar *) "cdef") == 0)
902 status = parse_tag_ds_cdef(doc, child, rrd);
903 else if (xmlStrcmp(child->name, (const xmlChar *) "last_ds") == 0)
904 status = get_string_from_node(doc, child,
905 cur_pdp_prep->last_ds,
906 sizeof(cur_pdp_prep->last_ds));
907 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
908 status = get_double_from_node(doc, child,
909 &cur_pdp_prep->scratch[PDP_val].
910 u_val);
911 else if (xmlStrcmp(child->name, (const xmlChar *) "unknown_sec") == 0)
912 status = get_ulong_from_node(doc, child,
913 &cur_pdp_prep->
914 scratch[PDP_unkn_sec_cnt].u_cnt);
915 else {
916 rrd_set_error("parse_tag_ds: Unknown tag: %s", child->name);
917 status = -1;
918 }
920 if (status != 0)
921 break;
922 }
924 return (status);
925 } /* int parse_tag_ds */
927 /*
928 * Parse root nodes
929 */
930 static int parse_tag_rrd(
931 xmlDoc * doc,
932 xmlNode * node,
933 rrd_t *rrd)
934 {
935 xmlNode *child;
936 int status;
938 status = 0;
939 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
940 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
941 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
942 /* ignore */ ;
943 else if (xmlStrcmp(child->name, (const xmlChar *) "version") == 0)
944 status = get_string_from_node(doc, child,
945 rrd->stat_head->version,
946 sizeof(rrd->stat_head->version));
947 else if (xmlStrcmp(child->name, (const xmlChar *) "step") == 0)
948 status = get_ulong_from_node(doc, child,
949 &rrd->stat_head->pdp_step);
950 else if (xmlStrcmp(child->name, (const xmlChar *) "lastupdate") == 0)
951 if (sizeof(time_t) == sizeof(long)) {
952 status = get_long_from_node(doc, child, (long *)&rrd->live_head->last_up);
953 }
954 else if (sizeof(time_t) == sizeof(long long)) {
955 status = get_long_long_from_node(doc, child, (long long *)&rrd->live_head->last_up);
956 }
957 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0)
958 status = parse_tag_ds(doc, child, rrd);
959 else if (xmlStrcmp(child->name, (const xmlChar *) "rra") == 0)
960 status = parse_tag_rra(doc, child, rrd);
961 else {
962 rrd_set_error("parse_tag_rrd: Unknown tag: %s", child->name);
963 status = -1;
964 }
966 if (status != 0)
967 break;
968 }
970 return (status);
971 } /* int parse_tag_rrd */
973 static rrd_t *parse_file(
974 const char *filename)
975 {
976 xmlDoc *doc;
977 xmlNode *cur;
978 int status;
980 rrd_t *rrd;
982 doc = xmlParseFile(filename);
983 if (doc == NULL) {
984 rrd_set_error("Document not parsed successfully.");
985 return (NULL);
986 }
988 cur = xmlDocGetRootElement(doc);
989 if (cur == NULL) {
990 rrd_set_error("Document is empty.");
991 xmlFreeDoc(doc);
992 return (NULL);
993 }
995 if (xmlStrcmp(cur->name, (const xmlChar *) "rrd") != 0) {
996 rrd_set_error
997 ("Document of the wrong type, root node is not \"rrd\".");
998 xmlFreeDoc(doc);
999 return (NULL);
1000 }
1002 rrd = (rrd_t *) malloc(sizeof(rrd_t));
1003 if (rrd == NULL) {
1004 rrd_set_error("parse_file: malloc failed.");
1005 xmlFreeDoc(doc);
1006 return (NULL);
1007 }
1008 memset(rrd, '\0', sizeof(rrd_t));
1010 rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
1011 if (rrd->stat_head == NULL) {
1012 rrd_set_error("parse_tag_rrd: malloc failed.");
1013 xmlFreeDoc(doc);
1014 free(rrd);
1015 return (NULL);
1016 }
1017 memset(rrd->stat_head, '\0', sizeof(stat_head_t));
1019 strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
1020 rrd->stat_head->float_cookie = FLOAT_COOKIE;
1022 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
1023 if (rrd->live_head == NULL) {
1024 rrd_set_error("parse_tag_rrd: malloc failed.");
1025 xmlFreeDoc(doc);
1026 free(rrd->stat_head);
1027 free(rrd);
1028 return (NULL);
1029 }
1030 memset(rrd->live_head, '\0', sizeof(live_head_t));
1032 status = parse_tag_rrd(doc, cur, rrd);
1034 xmlFreeDoc(doc);
1035 if (status != 0) {
1036 rrd_free(rrd);
1037 rrd = NULL;
1038 }
1040 return (rrd);
1041 } /* rrd_t *parse_file */
1043 static int write_file(
1044 const char *file_name,
1045 rrd_t *rrd)
1046 {
1047 FILE *fh;
1048 unsigned int i;
1049 unsigned int rra_offset;
1051 if (strcmp("-", file_name) == 0)
1052 fh = stdout;
1053 else {
1054 int fd_flags = O_WRONLY | O_CREAT;
1055 int fd;
1057 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1058 fd_flags |= O_BINARY;
1059 #endif
1061 if (opt_force_overwrite == 0)
1062 fd_flags |= O_EXCL;
1064 fd = open(file_name, fd_flags, 0666);
1065 if (fd == -1) {
1066 rrd_set_error("creating '%s': %s", file_name,
1067 rrd_strerror(errno));
1068 return (-1);
1069 }
1071 fh = fdopen(fd, "wb");
1072 if (fh == NULL) {
1073 rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1074 close(fd);
1075 return (-1);
1076 }
1077 }
1078 if (atoi(rrd->stat_head->version) < 3) {
1079 /* we output 3 or higher */
1080 strcpy(rrd->stat_head->version, "0003");
1081 }
1082 fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1083 fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1084 fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1085 fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1086 fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1087 fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1088 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1089 fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1091 /* calculate the number of rrd_values to dump */
1092 rra_offset = 0;
1093 for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
1094 unsigned long num_rows = rrd->rra_def[i].row_cnt;
1095 unsigned long cur_row = rrd->rra_ptr[i].cur_row;
1096 unsigned long ds_cnt = rrd->stat_head->ds_cnt;
1098 fwrite(rrd->rrd_value +
1099 (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1100 sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1102 fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1103 sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1105 rra_offset += num_rows;
1106 }
1108 /* lets see if we had an error */
1109 if (ferror(fh)) {
1110 rrd_set_error("a file error occurred while creating '%s'", file_name);
1111 fclose(fh);
1112 return (-1);
1113 }
1115 fclose(fh);
1116 return (0);
1117 } /* int write_file */
1119 int rrd_restore(
1120 int argc,
1121 char **argv)
1122 {
1123 rrd_t *rrd;
1125 #ifdef WIN32
1126 srand((unsigned int) time(NULL));
1127 #else
1128 srandom((unsigned int) time(NULL) + (unsigned int) getpid());
1129 #endif
1130 /* init rrd clean */
1131 optind = 0;
1132 opterr = 0; /* initialize getopt */
1133 while (42) {
1134 int opt;
1135 int option_index = 0;
1136 static struct option long_options[] = {
1137 {"range-check", no_argument, 0, 'r'},
1138 {"force-overwrite", no_argument, 0, 'f'},
1139 {0, 0, 0, 0}
1140 };
1142 opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1144 if (opt == EOF)
1145 break;
1147 switch (opt) {
1148 case 'r':
1149 opt_range_check = 1;
1150 break;
1152 case 'f':
1153 opt_force_overwrite = 1;
1154 break;
1156 default:
1157 rrd_set_error("usage rrdtool %s [--range-check|-r] "
1158 "[--force-overwrite/-f] file.xml file.rrd",
1159 argv[0]);
1160 return (-1);
1161 break;
1162 }
1163 } /* while (42) */
1165 if ((argc - optind) != 2) {
1166 rrd_set_error("usage rrdtool %s [--range-check/-r] "
1167 "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1168 return (-1);
1169 }
1171 rrd = parse_file(argv[optind]);
1172 if (rrd == NULL)
1173 return (-1);
1175 if (write_file(argv[optind + 1], rrd) != 0) {
1176 rrd_free(rrd);
1177 return (-1);
1178 }
1180 rrd_free(rrd);
1181 return (0);
1182 } /* int rrd_restore */
1184 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */