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