1 /*****************************************************************************
2 * RRDtool 1.3.9 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>
35 #include <locale.h>
37 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
38 #include <math.h>
39 # include <io.h>
40 # define open _open
41 # define close _close
42 #else
43 # include <unistd.h>
44 #endif
46 #include <libxml/parser.h>
47 #include "rrd_tool.h"
48 #include "rrd_rpncalc.h"
49 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof ((a)[0]))
50 static int opt_range_check = 0;
51 static int opt_force_overwrite = 0;
53 /*
54 * Auxiliary functions
55 */
56 static int get_string_from_node(
57 xmlDoc * doc,
58 xmlNode * node,
59 char *buffer,
60 size_t buffer_size)
61 {
62 xmlChar *temp0;
63 char *begin_ptr;
64 char *end_ptr;
66 temp0 = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
67 if (temp0 == NULL) {
68 rrd_set_error("get_string_from_node: xmlNodeListGetString failed.");
69 return (-1);
70 }
72 begin_ptr = (char *) temp0;
73 while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
74 begin_ptr++;
76 if (begin_ptr[0] == 0) {
77 xmlFree(temp0);
78 buffer[0] = 0;
79 return (0);
80 }
82 end_ptr = begin_ptr;
83 while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
84 end_ptr++;
85 end_ptr[0] = 0;
87 strncpy(buffer, begin_ptr, buffer_size);
88 buffer[buffer_size - 1] = 0;
90 xmlFree(temp0);
92 return (0);
93 } /* int get_string_from_node */
95 static int get_int_from_node(
96 xmlDoc * doc,
97 xmlNode * node,
98 int *value)
99 {
100 int temp;
101 char *str_ptr;
102 char *end_ptr;
104 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
105 if (str_ptr == NULL) {
106 rrd_set_error("get_int_from_node: xmlNodeListGetString failed.");
107 return (-1);
108 }
110 end_ptr = NULL;
111 temp = strtol(str_ptr, &end_ptr, 0);
113 if (str_ptr == end_ptr) {
114 rrd_set_error("get_int_from_node: Cannot parse buffer as long: %s",
115 str_ptr);
116 xmlFree(str_ptr);
117 return (-1);
118 }
119 xmlFree(str_ptr);
120 *value = temp;
122 return (0);
123 } /* int get_int_from_node */
125 static int get_ulong_from_node(
126 xmlDoc * doc,
127 xmlNode * node,
128 unsigned long *value)
129 {
130 unsigned long temp;
131 char *str_ptr;
132 char *end_ptr;
134 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
135 if (str_ptr == NULL) {
136 rrd_set_error("get_ulong_from_node: xmlNodeListGetString failed.");
137 return (-1);
138 }
140 end_ptr = NULL;
141 temp = strtoul(str_ptr, &end_ptr, 0);
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 xmlFree(str_ptr);
147 return (-1);
148 }
150 xmlFree(str_ptr);
151 *value = temp;
153 return (0);
154 } /* int get_ulong_from_node */
157 #ifdef WIN32
158 /* Gross Hack Alert */
159 #if _MSC_VER < 1300
160 #define strtoll(p, e, b) ((*(e) = (char*)(p) + (((b) == 10) ? strspn((p), "0123456789") : 0)), _atoi64(p))
161 #else
162 #define strtoll(p, e, b) _strtoi64(p, e, b)
163 #endif
164 #endif
166 static int get_llong_from_node(
167 xmlDoc * doc,
168 xmlNode * node,
169 long long *value)
170 {
171 long long temp;
172 char *str_ptr;
173 char *end_ptr;
175 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
176 if (str_ptr == NULL) {
177 rrd_set_error("get_llong_from_node: xmlNodeListGetString failed.");
178 return (-1);
179 }
181 end_ptr = NULL;
182 temp = strtoll(str_ptr, &end_ptr, 10);
184 if (str_ptr == end_ptr) {
185 rrd_set_error("get_llong_from_node: Cannot parse buffer as unsigned long long: %s",
186 str_ptr);
187 xmlFree(str_ptr);
188 return (-1);
189 }
190 xmlFree(str_ptr);
191 *value = temp;
193 return (0);
194 } /* int get_llong_from_node */
196 static int get_double_from_node(
197 xmlDoc * doc,
198 xmlNode * node,
199 double *value)
200 {
201 double temp;
202 char *str_ptr;
203 char *end_ptr;
205 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
206 if (str_ptr == NULL) {
207 rrd_set_error("get_double_from_node: xmlNodeListGetString failed.");
208 return (-1);
209 }
211 if (strstr(str_ptr, "NaN") != NULL)
212 {
213 *value = DNAN;
214 xmlFree(str_ptr);
215 return 0;
216 }
218 end_ptr = NULL;
219 temp = strtod(str_ptr, &end_ptr);
221 if (str_ptr == end_ptr) {
222 rrd_set_error
223 ("get_double_from_node: Cannot parse buffer as double: %s",
224 str_ptr);
225 xmlFree(str_ptr);
226 return (-1);
227 }
228 xmlFree(str_ptr);
229 *value = temp;
231 return (0);
232 } /* int get_double_from_node */
234 static int value_check_range(
235 rrd_value_t *rrd_value,
236 const ds_def_t *ds_def)
237 {
238 double min;
239 double max;
241 if (opt_range_check == 0)
242 return (0);
244 min = ds_def->par[DS_min_val].u_val;
245 max = ds_def->par[DS_max_val].u_val;
247 if (((!isnan(min)) && (*rrd_value < min))
248 || ((!isnan(max)) && (*rrd_value > max)))
249 *rrd_value = DNAN;
251 return (0);
252 } /* int value_check_range */
254 /*
255 * Parse the <database> block within an RRA definition
256 */
257 static int parse_tag_rra_database_row(
258 xmlDoc * doc,
259 xmlNode * node,
260 rrd_t *rrd,
261 rrd_value_t *rrd_value)
262 {
263 unsigned int values_count = 0;
264 xmlNode *child;
265 int status;
267 status = 0;
268 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
269 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
270 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
271 /* ignore */ ;
272 else if (xmlStrcmp(child->name, (const xmlChar *) "v") == 0) {
273 if (values_count < rrd->stat_head->ds_cnt) {
274 status =
275 get_double_from_node(doc, child,
276 rrd_value + values_count);
277 if (status == 0)
278 value_check_range(rrd_value + values_count,
279 rrd->ds_def + values_count);
280 }
282 values_count++;
283 } else {
284 rrd_set_error("parse_tag_rra_database_row: Unknown tag: %s",
285 child->name);
286 status = -1;
287 }
289 if (status != 0)
290 break;
291 } /* for (child = node->xmlChildrenNode) */
293 if (values_count != rrd->stat_head->ds_cnt) {
294 rrd_set_error("parse_tag_rra_database_row: Row has %u values "
295 "and RRD has %lu data sources.",
296 values_count, rrd->stat_head->ds_cnt);
297 status = -1;
298 }
300 return (status);
301 } /* int parse_tag_rra_database_row */
303 static int parse_tag_rra_database(
304 xmlDoc * doc,
305 xmlNode * node,
306 rrd_t *rrd)
307 {
308 rra_def_t *cur_rra_def;
309 unsigned int total_row_cnt;
310 xmlNode *child;
311 int status;
312 int i;
314 total_row_cnt = 0;
315 for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
316 total_row_cnt += rrd->rra_def[i].row_cnt;
318 cur_rra_def = rrd->rra_def + i;
320 status = 0;
321 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
322 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
323 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
324 /* ignore */ ;
325 else if (xmlStrcmp(child->name, (const xmlChar *) "row") == 0) {
326 rrd_value_t *temp;
327 rrd_value_t *cur_rrd_value;
328 unsigned int total_values_count = rrd->stat_head->ds_cnt
329 * (total_row_cnt + 1);
331 /* Allocate space for the new values.. */
332 temp = (rrd_value_t *) realloc(rrd->rrd_value,
333 sizeof(rrd_value_t) *
334 total_values_count);
335 if (temp == NULL) {
336 rrd_set_error("parse_tag_rra_database: realloc failed.");
337 status = -1;
338 break;
339 }
340 rrd->rrd_value = temp;
341 cur_rrd_value = rrd->rrd_value
342 + (rrd->stat_head->ds_cnt * total_row_cnt);
343 memset(cur_rrd_value, '\0',
344 sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
345 total_row_cnt++;
346 cur_rra_def->row_cnt++;
348 status =
349 parse_tag_rra_database_row(doc, child, rrd, cur_rrd_value);
350 } /* if (xmlStrcmp (child->name, (const xmlChar *) "row") == 0) */
351 else {
352 rrd_set_error("parse_tag_rra_database: Unknown tag: %s",
353 child->name);
354 status = -1;
355 }
357 if (status != 0)
358 break;
359 } /* for (child = node->xmlChildrenNode) */
361 return (status);
362 } /* int parse_tag_rra_database */
364 /*
365 * Parse the <cdp_prep> block within an RRA definition
366 */
367 static int parse_tag_rra_cdp_prep_ds_history(
368 xmlDoc * doc,
369 xmlNode * node,
370 cdp_prep_t *cdp_prep)
371 {
372 /* Make `history_buffer' the same size as the scratch area, plus the
373 * terminating NULL byte. */
374 char history_buffer[sizeof(((cdp_prep_t *)0)->scratch) + 1];
375 char *history_ptr;
376 int status;
377 int i;
379 status = get_string_from_node(doc, node,
380 history_buffer, sizeof(history_buffer));
381 if (status != 0)
382 return (-1);
384 history_ptr = (char *) (&cdp_prep->scratch[0]);
385 for (i = 0; history_buffer[i] != '\0'; i++)
386 history_ptr[i] = (history_buffer[i] == '1') ? 1 : 0;
388 return (0);
389 } /* int parse_tag_rra_cdp_prep_ds_history */
391 static int parse_tag_rra_cdp_prep_ds(
392 xmlDoc * doc,
393 xmlNode * node,
394 rrd_t *rrd,
395 cdp_prep_t *cdp_prep)
396 {
397 xmlNode *child;
398 int status;
400 memset(cdp_prep, '\0', sizeof(cdp_prep_t));
402 status = 0;
403 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
404 if (atoi(rrd->stat_head->version) == 1) {
405 cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
406 cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
407 }
408 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
409 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
410 /* ignore */ ;
411 else if (xmlStrcmp(child->name, (const xmlChar *) "primary_value") ==
412 0)
413 status =
414 get_double_from_node(doc, child,
415 &cdp_prep->scratch[CDP_primary_val].
416 u_val);
417 else if (xmlStrcmp(child->name, (const xmlChar *) "secondary_value")
418 == 0)
419 status =
420 get_double_from_node(doc, child,
421 &cdp_prep->scratch[CDP_secondary_val].
422 u_val);
423 else if (xmlStrcmp(child->name, (const xmlChar *) "intercept") == 0)
424 status = get_double_from_node(doc, child,
425 &cdp_prep->
426 scratch[CDP_hw_intercept].u_val);
427 else if (xmlStrcmp(child->name, (const xmlChar *) "last_intercept") ==
428 0)
429 status =
430 get_double_from_node(doc, child,
431 &cdp_prep->
432 scratch[CDP_hw_last_intercept].u_val);
433 else if (xmlStrcmp(child->name, (const xmlChar *) "slope") == 0)
434 status = get_double_from_node(doc, child,
435 &cdp_prep->scratch[CDP_hw_slope].
436 u_val);
437 else if (xmlStrcmp(child->name, (const xmlChar *) "last_slope") == 0)
438 status = get_double_from_node(doc, child,
439 &cdp_prep->
440 scratch[CDP_hw_last_slope].u_val);
441 else if (xmlStrcmp(child->name, (const xmlChar *) "nan_count") == 0)
442 status = get_ulong_from_node(doc, child,
443 &cdp_prep->
444 scratch[CDP_null_count].u_cnt);
445 else if (xmlStrcmp(child->name, (const xmlChar *) "last_nan_count") ==
446 0)
447 status =
448 get_ulong_from_node(doc, child,
449 &cdp_prep->
450 scratch[CDP_last_null_count].u_cnt);
451 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal") == 0)
452 status = get_double_from_node(doc, child,
453 &cdp_prep->scratch[CDP_hw_seasonal].
454 u_val);
455 else if (xmlStrcmp(child->name, (const xmlChar *) "last_seasonal") ==
456 0)
457 status =
458 get_double_from_node(doc, child,
459 &cdp_prep->scratch[CDP_hw_last_seasonal].
460 u_val);
461 else if (xmlStrcmp(child->name, (const xmlChar *) "init_flag") == 0)
462 status = get_ulong_from_node(doc, child,
463 &cdp_prep->
464 scratch[CDP_init_seasonal].u_cnt);
465 else if (xmlStrcmp(child->name, (const xmlChar *) "history") == 0)
466 status = parse_tag_rra_cdp_prep_ds_history(doc, child, cdp_prep);
467 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
468 status = get_double_from_node(doc, child,
469 &cdp_prep->scratch[CDP_val].u_val);
470 else if (xmlStrcmp(child->name,
471 (const xmlChar *) "unknown_datapoints") == 0)
472 status = get_ulong_from_node(doc, child,
473 &cdp_prep->
474 scratch[CDP_unkn_pdp_cnt].u_cnt);
475 else {
476 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
477 child->name);
478 status = -1;
479 }
481 if (status != 0)
482 break;
483 }
485 return (status);
486 } /* int parse_tag_rra_cdp_prep_ds */
488 static int parse_tag_rra_cdp_prep(
489 xmlDoc * doc,
490 xmlNode * node,
491 rrd_t *rrd,
492 cdp_prep_t *cdp_prep)
493 {
494 xmlNode *child;
495 int status;
497 unsigned int ds_count = 0;
499 status = 0;
500 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
501 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
502 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
503 /* ignore */ ;
504 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0) {
505 if (ds_count >= rrd->stat_head->ds_cnt)
506 status = -1;
507 else {
508 status = parse_tag_rra_cdp_prep_ds(doc, child, rrd,
509 cdp_prep + ds_count);
510 ds_count++;
511 }
512 } else {
513 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
514 child->name);
515 status = -1;
516 }
518 if (status != 0)
519 break;
520 }
522 if (ds_count != rrd->stat_head->ds_cnt) {
523 rrd_set_error("parse_tag_rra_cdp_prep: There are %i data sources in "
524 "the RRD file, but %i in this cdp_prep block!",
525 (int) rrd->stat_head->ds_cnt, ds_count);
526 status = -1;
527 }
529 return (status);
530 } /* int parse_tag_rra_cdp_prep */
532 /*
533 * Parse the <params> block within an RRA definition
534 */
535 static int parse_tag_rra_params(
536 xmlDoc * doc,
537 xmlNode * node,
538 rra_def_t *rra_def)
539 {
540 xmlNode *child;
541 int status;
543 status = 0;
544 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
545 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
546 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
547 /* ignore */ ;
548 /*
549 * Parameters for CF_HWPREDICT
550 */
551 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_alpha") == 0)
552 status = get_double_from_node(doc, child,
553 &rra_def->par[RRA_hw_alpha].u_val);
554 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_beta") == 0)
555 status = get_double_from_node(doc, child,
556 &rra_def->par[RRA_hw_beta].u_val);
557 else if (xmlStrcmp(child->name,
558 (const xmlChar *) "dependent_rra_idx") == 0)
559 status = get_ulong_from_node(doc, child,
560 &rra_def->
561 par[RRA_dependent_rra_idx].u_cnt);
562 /*
563 * Parameters for CF_SEASONAL and CF_DEVSEASONAL
564 */
565 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal_gamma") ==
566 0)
567 status =
568 get_double_from_node(doc, child,
569 &rra_def->par[RRA_seasonal_gamma].u_val);
570 else if (xmlStrcmp
571 (child->name, (const xmlChar *) "seasonal_smooth_idx") == 0)
572 status =
573 get_ulong_from_node(doc, child,
574 &rra_def->
575 par[RRA_seasonal_smooth_idx].u_cnt);
576 else if (xmlStrcmp(child->name, (const xmlChar *) "smoothing_window")
577 == 0)
578 status =
579 get_double_from_node(doc, child,
580 &rra_def->
581 par[RRA_seasonal_smoothing_window].
582 u_val);
583 /* else if (dependent_rra_idx) ...; */
584 /*
585 * Parameters for CF_FAILURES
586 */
587 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_pos") == 0)
588 status = get_double_from_node(doc, child,
589 &rra_def->par[RRA_delta_pos].u_val);
590 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_neg") == 0)
591 status = get_double_from_node(doc, child,
592 &rra_def->par[RRA_delta_neg].u_val);
593 else if (xmlStrcmp(child->name, (const xmlChar *) "window_len") == 0)
594 status = get_ulong_from_node(doc, child,
595 &rra_def->par[RRA_window_len].
596 u_cnt);
597 else if (xmlStrcmp(child->name, (const xmlChar *) "failure_threshold")
598 == 0)
599 status =
600 get_ulong_from_node(doc, child,
601 &rra_def->
602 par[RRA_failure_threshold].u_cnt);
603 /*
604 * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
605 */
606 else if (xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
607 status = get_double_from_node(doc, child,
608 &rra_def->par[RRA_cdp_xff_val].
609 u_val);
610 /*
611 * Compatibility code for 1.0.49
612 */
613 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) { /* {{{ */
614 unsigned int i = 0;
616 while (42) {
617 if (i >= ARRAY_LENGTH(rra_def->par)) {
618 status = -1;
619 break;
620 }
622 if ((i == RRA_dependent_rra_idx)
623 || (i == RRA_seasonal_smooth_idx)
624 || (i == RRA_failure_threshold))
625 status = get_ulong_from_node(doc, child,
626 &rra_def->par[i].
627 u_cnt);
628 else
629 status = get_double_from_node(doc, child,
630 &rra_def->par[i].u_val);
632 if (status != 0)
633 break;
635 /* When this loops exits (sucessfully) `child' points to the last
636 * `value' tag in the list. */
637 if ((child->next == NULL)
638 || (xmlStrcmp(child->name, (const xmlChar *) "value") !=
639 0))
640 break;
642 child = child->next;
643 i++;
644 }
645 } /* }}} */
646 else {
647 rrd_set_error("parse_tag_rra_params: Unknown tag: %s",
648 child->name);
649 status = -1;
650 }
652 if (status != 0)
653 break;
654 }
656 return (status);
657 } /* int parse_tag_rra_params */
659 /*
660 * Parse an RRA definition
661 */
662 static int parse_tag_rra_cf(
663 xmlDoc * doc,
664 xmlNode * node,
665 rra_def_t *rra_def)
666 {
667 int status;
669 status = get_string_from_node(doc, node,
670 rra_def->cf_nam, sizeof(rra_def->cf_nam));
671 if (status != 0)
672 return (-1);
674 status = cf_conv(rra_def->cf_nam);
675 if (status == -1) {
676 rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
677 rra_def->cf_nam);
678 return (-1);
679 }
681 return (0);
682 } /* int parse_tag_rra_cf */
684 static int parse_tag_rra(
685 xmlDoc * doc,
686 xmlNode * node,
687 rrd_t *rrd)
688 {
689 xmlNode *child;
690 int status;
692 rra_def_t *cur_rra_def;
693 cdp_prep_t *cur_cdp_prep;
694 rra_ptr_t *cur_rra_ptr;
696 /* Allocate more rra_def space for this RRA */
697 { /* {{{ */
698 rra_def_t *temp;
700 temp = (rra_def_t *) realloc(rrd->rra_def,
701 sizeof(rra_def_t) *
702 (rrd->stat_head->rra_cnt + 1));
703 if (temp == NULL) {
704 rrd_set_error("parse_tag_rra: realloc failed.");
705 return (-1);
706 }
707 rrd->rra_def = temp;
708 cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
709 memset(cur_rra_def, '\0', sizeof(rra_def_t));
710 } /* }}} */
712 /* allocate cdp_prep_t */
713 { /* {{{ */
714 cdp_prep_t *temp;
716 temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
717 * rrd->stat_head->ds_cnt
718 * (rrd->stat_head->rra_cnt + 1));
719 if (temp == NULL) {
720 rrd_set_error("parse_tag_rra: realloc failed.");
721 return (-1);
722 }
723 rrd->cdp_prep = temp;
724 cur_cdp_prep = rrd->cdp_prep
725 + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
726 memset(cur_cdp_prep, '\0',
727 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
728 } /* }}} */
730 /* allocate rra_ptr_t */
731 { /* {{{ */
732 rra_ptr_t *temp;
734 temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
735 sizeof(rra_ptr_t) *
736 (rrd->stat_head->rra_cnt + 1));
737 if (temp == NULL) {
738 rrd_set_error("parse_tag_rra: realloc failed.");
739 return (-1);
740 }
741 rrd->rra_ptr = temp;
742 cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
743 memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
744 } /* }}} */
746 /* All space successfully allocated, increment number of RRAs. */
747 rrd->stat_head->rra_cnt++;
749 status = 0;
750 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
751 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
752 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
753 /* ignore */ ;
754 else if (xmlStrcmp(child->name, (const xmlChar *) "cf") == 0)
755 status = parse_tag_rra_cf(doc, child, cur_rra_def);
756 else if (xmlStrcmp(child->name, (const xmlChar *) "pdp_per_row") == 0)
757 status = get_ulong_from_node(doc, child,
758 &cur_rra_def->pdp_cnt);
759 else if (atoi(rrd->stat_head->version) == 1
760 && xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
761 status = get_double_from_node(doc, child,
762 (double *) &cur_rra_def->
763 par[RRA_cdp_xff_val].u_val);
764 else if (atoi(rrd->stat_head->version) >= 2
765 && xmlStrcmp(child->name, (const xmlChar *) "params") == 0)
766 status = parse_tag_rra_params(doc, child, cur_rra_def);
767 else if (xmlStrcmp(child->name, (const xmlChar *) "cdp_prep") == 0)
768 status = parse_tag_rra_cdp_prep(doc, child, rrd, cur_cdp_prep);
769 else if (xmlStrcmp(child->name, (const xmlChar *) "database") == 0)
770 status = parse_tag_rra_database(doc, child, rrd);
771 else {
772 rrd_set_error("parse_tag_rra: Unknown tag: %s", child->name);
773 status = -1;
774 }
776 if (status != 0)
777 break;
778 }
780 /* Set the RRA pointer to a random location */
781 #ifdef WIN32
782 cur_rra_ptr->cur_row = rand() % cur_rra_def->row_cnt;
783 #else
784 cur_rra_ptr->cur_row = random() % cur_rra_def->row_cnt;
785 #endif
787 return (status);
788 } /* int parse_tag_rra */
790 /*
791 * Parse a DS definition
792 */
793 static int parse_tag_ds_cdef(
794 xmlDoc * doc,
795 xmlNode * node,
796 rrd_t *rrd)
797 {
798 char buffer[1024];
799 int status;
801 status = get_string_from_node(doc, node, buffer, sizeof(buffer));
802 if (status != 0)
803 return (-1);
805 /* We're always working on the last DS that has been added to the structure
806 * when we get here */
807 parseCDEF_DS(buffer, rrd, rrd->stat_head->ds_cnt - 1);
809 return (0);
810 } /* int parse_tag_ds_cdef */
812 static int parse_tag_ds_type(
813 xmlDoc * doc,
814 xmlNode * node,
815 ds_def_t *ds_def)
816 {
817 int status;
819 status = get_string_from_node(doc, node,
820 ds_def->dst, sizeof(ds_def->dst));
821 if (status != 0)
822 return (-1);
824 status = dst_conv(ds_def->dst);
825 if (status == -1) {
826 rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
827 ds_def->dst);
828 return (-1);
829 }
831 return (0);
832 } /* int parse_tag_ds_type */
834 static int parse_tag_ds(
835 xmlDoc * doc,
836 xmlNode * node,
837 rrd_t *rrd)
838 {
839 xmlNode *child;
840 int status;
842 ds_def_t *cur_ds_def;
843 pdp_prep_t *cur_pdp_prep;
845 /*
846 * If there are DS definitions after RRA definitions the number of values,
847 * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
848 * specific order in this case.
849 */
850 if (rrd->stat_head->rra_cnt > 0) {
851 rrd_set_error("parse_tag_ds: All data source definitions MUST "
852 "precede the RRA definitions!");
853 return (-1);
854 }
856 /* Allocate space for the new DS definition */
857 { /* {{{ */
858 ds_def_t *temp;
860 temp = (ds_def_t *) realloc(rrd->ds_def,
861 sizeof(ds_def_t) *
862 (rrd->stat_head->ds_cnt + 1));
863 if (temp == NULL) {
864 rrd_set_error("parse_tag_ds: malloc failed.");
865 return (-1);
866 }
867 rrd->ds_def = temp;
868 cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
869 memset(cur_ds_def, '\0', sizeof(ds_def_t));
870 } /* }}} */
872 /* Allocate pdp_prep space for the new DS definition */
873 { /* {{{ */
874 pdp_prep_t *temp;
876 temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
877 sizeof(pdp_prep_t) *
878 (rrd->stat_head->ds_cnt + 1));
879 if (temp == NULL) {
880 rrd_set_error("parse_tag_ds: malloc failed.");
881 return (-1);
882 }
883 rrd->pdp_prep = temp;
884 cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
885 memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
886 } /* }}} */
888 /* All allocations successful, let's increment the number of DSes. */
889 rrd->stat_head->ds_cnt++;
891 status = 0;
892 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
893 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
894 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
895 /* ignore */ ;
896 else if (xmlStrcmp(child->name, (const xmlChar *) "name") == 0)
897 status = get_string_from_node(doc, child,
898 cur_ds_def->ds_nam,
899 sizeof(cur_ds_def->ds_nam));
900 else if (xmlStrcmp(child->name, (const xmlChar *) "type") == 0)
901 status = parse_tag_ds_type(doc, child, cur_ds_def);
902 else if (xmlStrcmp(child->name,
903 (const xmlChar *) "minimal_heartbeat") == 0)
904 status = get_ulong_from_node(doc, child,
905 &cur_ds_def->par[DS_mrhb_cnt].
906 u_cnt);
907 else if (xmlStrcmp(child->name, (const xmlChar *) "min") == 0)
908 status = get_double_from_node(doc, child,
909 &cur_ds_def->par[DS_min_val].u_val);
910 else if (xmlStrcmp(child->name, (const xmlChar *) "max") == 0)
911 status = get_double_from_node(doc, child,
912 &cur_ds_def->par[DS_max_val].u_val);
913 else if (xmlStrcmp(child->name, (const xmlChar *) "cdef") == 0)
914 status = parse_tag_ds_cdef(doc, child, rrd);
915 else if (xmlStrcmp(child->name, (const xmlChar *) "last_ds") == 0)
916 status = get_string_from_node(doc, child,
917 cur_pdp_prep->last_ds,
918 sizeof(cur_pdp_prep->last_ds));
919 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
920 status = get_double_from_node(doc, child,
921 &cur_pdp_prep->scratch[PDP_val].
922 u_val);
923 else if (xmlStrcmp(child->name, (const xmlChar *) "unknown_sec") == 0)
924 status = get_ulong_from_node(doc, child,
925 &cur_pdp_prep->
926 scratch[PDP_unkn_sec_cnt].u_cnt);
927 else {
928 rrd_set_error("parse_tag_ds: Unknown tag: %s", child->name);
929 status = -1;
930 }
932 if (status != 0)
933 break;
934 }
936 return (status);
937 } /* int parse_tag_ds */
939 /*
940 * Parse root nodes
941 */
942 static int parse_tag_rrd(
943 xmlDoc * doc,
944 xmlNode * node,
945 rrd_t *rrd)
946 {
947 xmlNode *child;
948 int status;
950 status = 0;
951 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
952 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
953 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
954 /* ignore */ ;
955 else if (xmlStrcmp(child->name, (const xmlChar *) "version") == 0)
956 status = get_string_from_node(doc, child,
957 rrd->stat_head->version,
958 sizeof(rrd->stat_head->version));
959 else if (xmlStrcmp(child->name, (const xmlChar *) "step") == 0)
960 status = get_ulong_from_node(doc, child,
961 &rrd->stat_head->pdp_step);
962 else if (xmlStrcmp(child->name, (const xmlChar *) "lastupdate") == 0) {
963 if (sizeof(time_t) == sizeof(int)) {
964 status = get_int_from_node(doc, child, (int *)&rrd->live_head->last_up);
965 }
966 else { if (sizeof(time_t) == sizeof(long long)) {
967 status = get_llong_from_node(doc, child, (long long *)&rrd->live_head->last_up);
968 }
969 else {
970 rrd_set_error("can't convert to time_t ...", child->name);
971 status = -1;
972 }
973 }
974 }
975 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0)
976 status = parse_tag_ds(doc, child, rrd);
977 else if (xmlStrcmp(child->name, (const xmlChar *) "rra") == 0)
978 status = parse_tag_rra(doc, child, rrd);
979 else {
980 rrd_set_error("parse_tag_rrd: Unknown tag: %s", child->name);
981 status = -1;
982 }
984 if (status != 0)
985 break;
986 }
988 return (status);
989 } /* int parse_tag_rrd */
991 static rrd_t *parse_file(
992 const char *filename)
993 {
994 xmlDoc *doc;
995 xmlNode *cur;
996 int status;
998 rrd_t *rrd;
1000 doc = xmlParseFile(filename);
1001 if (doc == NULL) {
1002 rrd_set_error("Document not parsed successfully.");
1003 return (NULL);
1004 }
1006 cur = xmlDocGetRootElement(doc);
1007 if (cur == NULL) {
1008 rrd_set_error("Document is empty.");
1009 xmlFreeDoc(doc);
1010 return (NULL);
1011 }
1013 if (xmlStrcmp(cur->name, (const xmlChar *) "rrd") != 0) {
1014 rrd_set_error
1015 ("Document of the wrong type, root node is not \"rrd\".");
1016 xmlFreeDoc(doc);
1017 return (NULL);
1018 }
1020 rrd = (rrd_t *) malloc(sizeof(rrd_t));
1021 if (rrd == NULL) {
1022 rrd_set_error("parse_file: malloc failed.");
1023 xmlFreeDoc(doc);
1024 return (NULL);
1025 }
1026 memset(rrd, '\0', sizeof(rrd_t));
1028 rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
1029 if (rrd->stat_head == NULL) {
1030 rrd_set_error("parse_tag_rrd: malloc failed.");
1031 xmlFreeDoc(doc);
1032 free(rrd);
1033 return (NULL);
1034 }
1035 memset(rrd->stat_head, '\0', sizeof(stat_head_t));
1037 strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
1038 rrd->stat_head->float_cookie = FLOAT_COOKIE;
1040 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
1041 if (rrd->live_head == NULL) {
1042 rrd_set_error("parse_tag_rrd: malloc failed.");
1043 xmlFreeDoc(doc);
1044 free(rrd->stat_head);
1045 free(rrd);
1046 return (NULL);
1047 }
1048 memset(rrd->live_head, '\0', sizeof(live_head_t));
1050 status = parse_tag_rrd(doc, cur, rrd);
1052 xmlFreeDoc(doc);
1053 if (status != 0) {
1054 rrd_free(rrd);
1055 rrd = NULL;
1056 }
1058 return (rrd);
1059 } /* rrd_t *parse_file */
1061 static int write_file(
1062 const char *file_name,
1063 rrd_t *rrd)
1064 {
1065 FILE *fh;
1066 unsigned int i;
1067 unsigned int rra_offset;
1069 if (strcmp("-", file_name) == 0)
1070 fh = stdout;
1071 else {
1072 int fd_flags = O_WRONLY | O_CREAT;
1073 int fd;
1075 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1076 fd_flags |= O_BINARY;
1077 #endif
1079 if (opt_force_overwrite == 0)
1080 fd_flags |= O_EXCL;
1082 fd = open(file_name, fd_flags, 0666);
1083 if (fd == -1) {
1084 rrd_set_error("creating '%s': %s", file_name,
1085 rrd_strerror(errno));
1086 return (-1);
1087 }
1089 fh = fdopen(fd, "wb");
1090 if (fh == NULL) {
1091 rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1092 close(fd);
1093 return (-1);
1094 }
1095 }
1096 if (atoi(rrd->stat_head->version) < 3) {
1097 /* we output 3 or higher */
1098 strcpy(rrd->stat_head->version, "0003");
1099 }
1100 fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1101 fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1102 fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1103 fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1104 fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1105 fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1106 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1107 fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1109 /* calculate the number of rrd_values to dump */
1110 rra_offset = 0;
1111 for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
1112 unsigned long num_rows = rrd->rra_def[i].row_cnt;
1113 unsigned long cur_row = rrd->rra_ptr[i].cur_row;
1114 unsigned long ds_cnt = rrd->stat_head->ds_cnt;
1116 fwrite(rrd->rrd_value +
1117 (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1118 sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1120 fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1121 sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1123 rra_offset += num_rows;
1124 }
1126 /* lets see if we had an error */
1127 if (ferror(fh)) {
1128 rrd_set_error("a file error occurred while creating '%s'", file_name);
1129 fclose(fh);
1130 return (-1);
1131 }
1133 fclose(fh);
1134 return (0);
1135 } /* int write_file */
1137 int rrd_restore(
1138 int argc,
1139 char **argv)
1140 {
1141 rrd_t *rrd;
1142 char* old_locale;
1144 #ifdef WIN32
1145 srand((unsigned int) time(NULL));
1146 #else
1147 srandom((unsigned int) time(NULL) + (unsigned int) getpid());
1148 #endif
1149 /* init rrd clean */
1150 optind = 0;
1151 opterr = 0; /* initialize getopt */
1152 while (42) {
1153 int opt;
1154 int option_index = 0;
1155 static struct option long_options[] = {
1156 {"range-check", no_argument, 0, 'r'},
1157 {"force-overwrite", no_argument, 0, 'f'},
1158 {0, 0, 0, 0}
1159 };
1161 opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1163 if (opt == EOF)
1164 break;
1166 switch (opt) {
1167 case 'r':
1168 opt_range_check = 1;
1169 break;
1171 case 'f':
1172 opt_force_overwrite = 1;
1173 break;
1175 default:
1176 rrd_set_error("usage rrdtool %s [--range-check|-r] "
1177 "[--force-overwrite/-f] file.xml file.rrd",
1178 argv[0]);
1179 return (-1);
1180 break;
1181 }
1182 } /* while (42) */
1184 if ((argc - optind) != 2) {
1185 rrd_set_error("usage rrdtool %s [--range-check/-r] "
1186 "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1187 return (-1);
1188 }
1190 #ifdef HAVE_SETLOCALE
1191 old_locale = setlocale(LC_NUMERIC, "C");
1192 #endif
1194 rrd = parse_file(argv[optind]);
1196 #ifdef HAVE_SETLOCALE
1197 setlocale(LC_NUMERIC, old_locale);
1198 #endif
1200 if (rrd == NULL)
1201 return (-1);
1203 if (write_file(argv[optind + 1], rrd) != 0) {
1204 rrd_free(rrd);
1205 return (-1);
1206 }
1208 rrd_free(rrd);
1209 return (0);
1210 } /* int rrd_restore */
1212 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */