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