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