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_long_from_node(
99 xmlDoc * doc,
100 xmlNode * node,
101 long *value)
102 {
103 long 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_long_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_long_from_node: Cannot parse buffer as long: %s",
119 str_ptr);
120 return (-1);
121 }
123 *value = temp;
125 return (0);
126 } /* int get_long_from_node */
128 static int get_ulong_from_node(
129 xmlDoc * doc,
130 xmlNode * node,
131 unsigned long *value)
132 {
133 unsigned long 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_ulong_from_node: xmlNodeListGetString failed.");
140 return (-1);
141 }
143 end_ptr = NULL;
144 temp = strtoul(str_ptr, &end_ptr, 0);
145 xmlFree(str_ptr);
147 if (str_ptr == end_ptr) {
148 rrd_set_error("get_ulong_from_node: Cannot parse buffer as unsigned long: %s",
149 str_ptr);
150 return (-1);
151 }
153 *value = temp;
155 return (0);
156 } /* int get_ulong_from_node */
158 static int get_double_from_node(
159 xmlDoc * doc,
160 xmlNode * node,
161 double *value)
162 {
163 double temp;
164 char *str_ptr;
165 char *end_ptr;
167 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
168 if (str_ptr == NULL) {
169 rrd_set_error("get_double_from_node: xmlNodeListGetString failed.");
170 return (-1);
171 }
173 if (strstr(str_ptr, "NaN") != NULL)
174 {
175 *value = DNAN;
176 xmlFree(str_ptr);
177 return 0;
178 }
180 end_ptr = NULL;
181 temp = strtod(str_ptr, &end_ptr);
182 xmlFree(str_ptr);
184 if (str_ptr == end_ptr) {
185 rrd_set_error
186 ("get_double_from_node: Cannot parse buffer as double: %s",
187 str_ptr);
188 return (-1);
189 }
191 *value = temp;
193 return (0);
194 } /* int get_double_from_node */
196 static int value_check_range(
197 rrd_value_t *rrd_value,
198 const ds_def_t *ds_def)
199 {
200 double min;
201 double max;
203 if (opt_range_check == 0)
204 return (0);
206 min = ds_def->par[DS_min_val].u_val;
207 max = ds_def->par[DS_max_val].u_val;
209 if (((!isnan(min)) && (*rrd_value < min))
210 || ((!isnan(max)) && (*rrd_value > max)))
211 *rrd_value = DNAN;
213 return (0);
214 } /* int value_check_range */
216 /*
217 * Parse the <database> block within an RRA definition
218 */
219 static int parse_tag_rra_database_row(
220 xmlDoc * doc,
221 xmlNode * node,
222 rrd_t *rrd,
223 rrd_value_t *rrd_value)
224 {
225 unsigned int values_count = 0;
226 xmlNode *child;
227 int status;
229 status = 0;
230 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
231 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
232 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
233 /* ignore */ ;
234 else if (xmlStrcmp(child->name, (const xmlChar *) "v") == 0) {
235 if (values_count < rrd->stat_head->ds_cnt) {
236 status =
237 get_double_from_node(doc, child,
238 rrd_value + values_count);
239 if (status == 0)
240 value_check_range(rrd_value + values_count,
241 rrd->ds_def + values_count);
242 }
244 values_count++;
245 } else {
246 rrd_set_error("parse_tag_rra_database_row: Unknown tag: %s",
247 child->name);
248 status = -1;
249 }
251 if (status != 0)
252 break;
253 } /* for (child = node->xmlChildrenNode) */
255 if (values_count != rrd->stat_head->ds_cnt) {
256 rrd_set_error("parse_tag_rra_database_row: Row has %u values "
257 "and RRD has %lu data sources.",
258 values_count, rrd->stat_head->ds_cnt);
259 status = -1;
260 }
262 return (status);
263 } /* int parse_tag_rra_database_row */
265 static int parse_tag_rra_database(
266 xmlDoc * doc,
267 xmlNode * node,
268 rrd_t *rrd)
269 {
270 rra_def_t *cur_rra_def;
271 unsigned int total_row_cnt;
272 xmlNode *child;
273 int status;
274 int i;
276 total_row_cnt = 0;
277 for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
278 total_row_cnt += rrd->rra_def[i].row_cnt;
280 cur_rra_def = rrd->rra_def + i;
282 status = 0;
283 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
284 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
285 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
286 /* ignore */ ;
287 else if (xmlStrcmp(child->name, (const xmlChar *) "row") == 0) {
288 rrd_value_t *temp;
289 rrd_value_t *cur_rrd_value;
290 unsigned int total_values_count = rrd->stat_head->ds_cnt
291 * (total_row_cnt + 1);
293 /* Allocate space for the new values.. */
294 temp = (rrd_value_t *) realloc(rrd->rrd_value,
295 sizeof(rrd_value_t) *
296 total_values_count);
297 if (temp == NULL) {
298 rrd_set_error("parse_tag_rra_database: realloc failed.");
299 status = -1;
300 break;
301 }
302 rrd->rrd_value = temp;
303 cur_rrd_value = rrd->rrd_value
304 + (rrd->stat_head->ds_cnt * total_row_cnt);
305 memset(cur_rrd_value, '\0',
306 sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
307 total_row_cnt++;
308 cur_rra_def->row_cnt++;
310 status =
311 parse_tag_rra_database_row(doc, child, rrd, cur_rrd_value);
312 } /* if (xmlStrcmp (child->name, (const xmlChar *) "row") == 0) */
313 else {
314 rrd_set_error("parse_tag_rra_database: Unknown tag: %s",
315 child->name);
316 status = -1;
317 }
319 if (status != 0)
320 break;
321 } /* for (child = node->xmlChildrenNode) */
323 return (status);
324 } /* int parse_tag_rra_database */
326 /*
327 * Parse the <cdp_prep> block within an RRA definition
328 */
329 static int parse_tag_rra_cdp_prep_ds_history(
330 xmlDoc * doc,
331 xmlNode * node,
332 cdp_prep_t *cdp_prep)
333 {
334 /* Make `history_buffer' the same size as the scratch area, plus the
335 * terminating NULL byte. */
336 char history_buffer[sizeof(((cdp_prep_t *)0)->scratch) + 1];
337 char *history_ptr;
338 int status;
339 int i;
341 status = get_string_from_node(doc, node,
342 history_buffer, sizeof(history_buffer));
343 if (status != 0)
344 return (-1);
346 history_ptr = (char *) (&cdp_prep->scratch[0]);
347 for (i = 0; history_buffer[i] != '\0'; i++)
348 history_ptr[i] = (history_buffer[i] == '1') ? 1 : 0;
350 return (0);
351 } /* int parse_tag_rra_cdp_prep_ds_history */
353 static int parse_tag_rra_cdp_prep_ds(
354 xmlDoc * doc,
355 xmlNode * node,
356 rrd_t *rrd,
357 cdp_prep_t *cdp_prep)
358 {
359 xmlNode *child;
360 int status;
362 memset(cdp_prep, '\0', sizeof(cdp_prep_t));
364 status = 0;
365 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
366 if (atoi(rrd->stat_head->version) == 1) {
367 cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
368 cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
369 }
370 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
371 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
372 /* ignore */ ;
373 else if (xmlStrcmp(child->name, (const xmlChar *) "primary_value") ==
374 0)
375 status =
376 get_double_from_node(doc, child,
377 &cdp_prep->scratch[CDP_primary_val].
378 u_val);
379 else if (xmlStrcmp(child->name, (const xmlChar *) "secondary_value")
380 == 0)
381 status =
382 get_double_from_node(doc, child,
383 &cdp_prep->scratch[CDP_secondary_val].
384 u_val);
385 else if (xmlStrcmp(child->name, (const xmlChar *) "intercept") == 0)
386 status = get_double_from_node(doc, child,
387 &cdp_prep->
388 scratch[CDP_hw_intercept].u_val);
389 else if (xmlStrcmp(child->name, (const xmlChar *) "last_intercept") ==
390 0)
391 status =
392 get_double_from_node(doc, child,
393 &cdp_prep->
394 scratch[CDP_hw_last_intercept].u_val);
395 else if (xmlStrcmp(child->name, (const xmlChar *) "slope") == 0)
396 status = get_double_from_node(doc, child,
397 &cdp_prep->scratch[CDP_hw_slope].
398 u_val);
399 else if (xmlStrcmp(child->name, (const xmlChar *) "last_slope") == 0)
400 status = get_double_from_node(doc, child,
401 &cdp_prep->
402 scratch[CDP_hw_last_slope].u_val);
403 else if (xmlStrcmp(child->name, (const xmlChar *) "nan_count") == 0)
404 status = get_ulong_from_node(doc, child,
405 &cdp_prep->
406 scratch[CDP_null_count].u_cnt);
407 else if (xmlStrcmp(child->name, (const xmlChar *) "last_nan_count") ==
408 0)
409 status =
410 get_ulong_from_node(doc, child,
411 &cdp_prep->
412 scratch[CDP_last_null_count].u_cnt);
413 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal") == 0)
414 status = get_double_from_node(doc, child,
415 &cdp_prep->scratch[CDP_hw_seasonal].
416 u_val);
417 else if (xmlStrcmp(child->name, (const xmlChar *) "last_seasonal") ==
418 0)
419 status =
420 get_double_from_node(doc, child,
421 &cdp_prep->scratch[CDP_hw_last_seasonal].
422 u_val);
423 else if (xmlStrcmp(child->name, (const xmlChar *) "init_flag") == 0)
424 status = get_ulong_from_node(doc, child,
425 &cdp_prep->
426 scratch[CDP_init_seasonal].u_cnt);
427 else if (xmlStrcmp(child->name, (const xmlChar *) "history") == 0)
428 status = parse_tag_rra_cdp_prep_ds_history(doc, child, cdp_prep);
429 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
430 status = get_double_from_node(doc, child,
431 &cdp_prep->scratch[CDP_val].u_val);
432 else if (xmlStrcmp(child->name,
433 (const xmlChar *) "unknown_datapoints") == 0)
434 status = get_ulong_from_node(doc, child,
435 &cdp_prep->
436 scratch[CDP_unkn_pdp_cnt].u_cnt);
437 else {
438 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
439 child->name);
440 status = -1;
441 }
443 if (status != 0)
444 break;
445 }
447 return (status);
448 } /* int parse_tag_rra_cdp_prep_ds */
450 static int parse_tag_rra_cdp_prep(
451 xmlDoc * doc,
452 xmlNode * node,
453 rrd_t *rrd,
454 cdp_prep_t *cdp_prep)
455 {
456 xmlNode *child;
457 int status;
459 unsigned int ds_count = 0;
461 status = 0;
462 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
463 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
464 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
465 /* ignore */ ;
466 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0) {
467 if (ds_count >= rrd->stat_head->ds_cnt)
468 status = -1;
469 else {
470 status = parse_tag_rra_cdp_prep_ds(doc, child, rrd,
471 cdp_prep + ds_count);
472 ds_count++;
473 }
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 if (ds_count != rrd->stat_head->ds_cnt) {
485 rrd_set_error("parse_tag_rra_cdp_prep: There are %i data sources in "
486 "the RRD file, but %i in this cdp_prep block!",
487 (int) rrd->stat_head->ds_cnt, ds_count);
488 status = -1;
489 }
491 return (status);
492 } /* int parse_tag_rra_cdp_prep */
494 /*
495 * Parse the <params> block within an RRA definition
496 */
497 static int parse_tag_rra_params(
498 xmlDoc * doc,
499 xmlNode * node,
500 rra_def_t *rra_def)
501 {
502 xmlNode *child;
503 int status;
505 status = 0;
506 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
507 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
508 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
509 /* ignore */ ;
510 /*
511 * Parameters for CF_HWPREDICT
512 */
513 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_alpha") == 0)
514 status = get_double_from_node(doc, child,
515 &rra_def->par[RRA_hw_alpha].u_val);
516 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_beta") == 0)
517 status = get_double_from_node(doc, child,
518 &rra_def->par[RRA_hw_beta].u_val);
519 else if (xmlStrcmp(child->name,
520 (const xmlChar *) "dependent_rra_idx") == 0)
521 status = get_ulong_from_node(doc, child,
522 &rra_def->
523 par[RRA_dependent_rra_idx].u_cnt);
524 /*
525 * Parameters for CF_SEASONAL and CF_DEVSEASONAL
526 */
527 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal_gamma") ==
528 0)
529 status =
530 get_double_from_node(doc, child,
531 &rra_def->par[RRA_seasonal_gamma].u_val);
532 else if (xmlStrcmp
533 (child->name, (const xmlChar *) "seasonal_smooth_idx") == 0)
534 status =
535 get_ulong_from_node(doc, child,
536 &rra_def->
537 par[RRA_seasonal_smooth_idx].u_cnt);
538 else if (xmlStrcmp(child->name, (const xmlChar *) "smoothing_window")
539 == 0)
540 status =
541 get_double_from_node(doc, child,
542 &rra_def->
543 par[RRA_seasonal_smoothing_window].
544 u_val);
545 /* else if (dependent_rra_idx) ...; */
546 /*
547 * Parameters for CF_FAILURES
548 */
549 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_pos") == 0)
550 status = get_double_from_node(doc, child,
551 &rra_def->par[RRA_delta_pos].u_val);
552 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_neg") == 0)
553 status = get_double_from_node(doc, child,
554 &rra_def->par[RRA_delta_neg].u_val);
555 else if (xmlStrcmp(child->name, (const xmlChar *) "window_len") == 0)
556 status = get_ulong_from_node(doc, child,
557 &rra_def->par[RRA_window_len].
558 u_cnt);
559 else if (xmlStrcmp(child->name, (const xmlChar *) "failure_threshold")
560 == 0)
561 status =
562 get_ulong_from_node(doc, child,
563 &rra_def->
564 par[RRA_failure_threshold].u_cnt);
565 /*
566 * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
567 */
568 else if (xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
569 status = get_double_from_node(doc, child,
570 &rra_def->par[RRA_cdp_xff_val].
571 u_val);
572 /*
573 * Compatibility code for 1.0.49
574 */
575 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) { /* {{{ */
576 unsigned int i = 0;
578 while (42) {
579 if (i >= ARRAY_LENGTH(rra_def->par)) {
580 status = -1;
581 break;
582 }
584 if ((i == RRA_dependent_rra_idx)
585 || (i == RRA_seasonal_smooth_idx)
586 || (i == RRA_failure_threshold))
587 status = get_ulong_from_node(doc, child,
588 &rra_def->par[i].
589 u_cnt);
590 else
591 status = get_double_from_node(doc, child,
592 &rra_def->par[i].u_val);
594 if (status != 0)
595 break;
597 /* When this loops exits (sucessfully) `child' points to the last
598 * `value' tag in the list. */
599 if ((child->next == NULL)
600 || (xmlStrcmp(child->name, (const xmlChar *) "value") !=
601 0))
602 break;
604 child = child->next;
605 i++;
606 }
607 } /* }}} */
608 else {
609 rrd_set_error("parse_tag_rra_params: Unknown tag: %s",
610 child->name);
611 status = -1;
612 }
614 if (status != 0)
615 break;
616 }
618 return (status);
619 } /* int parse_tag_rra_params */
621 /*
622 * Parse an RRA definition
623 */
624 static int parse_tag_rra_cf(
625 xmlDoc * doc,
626 xmlNode * node,
627 rra_def_t *rra_def)
628 {
629 int status;
631 status = get_string_from_node(doc, node,
632 rra_def->cf_nam, sizeof(rra_def->cf_nam));
633 if (status != 0)
634 return (-1);
636 status = cf_conv(rra_def->cf_nam);
637 if (status == -1) {
638 rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
639 rra_def->cf_nam);
640 return (-1);
641 }
643 return (0);
644 } /* int parse_tag_rra_cf */
646 static int parse_tag_rra(
647 xmlDoc * doc,
648 xmlNode * node,
649 rrd_t *rrd)
650 {
651 xmlNode *child;
652 int status;
654 rra_def_t *cur_rra_def;
655 cdp_prep_t *cur_cdp_prep;
656 rra_ptr_t *cur_rra_ptr;
658 /* Allocate more rra_def space for this RRA */
659 { /* {{{ */
660 rra_def_t *temp;
662 temp = (rra_def_t *) realloc(rrd->rra_def,
663 sizeof(rra_def_t) *
664 (rrd->stat_head->rra_cnt + 1));
665 if (temp == NULL) {
666 rrd_set_error("parse_tag_rra: realloc failed.");
667 return (-1);
668 }
669 rrd->rra_def = temp;
670 cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
671 memset(cur_rra_def, '\0', sizeof(rra_def_t));
672 } /* }}} */
674 /* allocate cdp_prep_t */
675 { /* {{{ */
676 cdp_prep_t *temp;
678 temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
679 * rrd->stat_head->ds_cnt
680 * (rrd->stat_head->rra_cnt + 1));
681 if (temp == NULL) {
682 rrd_set_error("parse_tag_rra: realloc failed.");
683 return (-1);
684 }
685 rrd->cdp_prep = temp;
686 cur_cdp_prep = rrd->cdp_prep
687 + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
688 memset(cur_cdp_prep, '\0',
689 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
690 } /* }}} */
692 /* allocate rra_ptr_t */
693 { /* {{{ */
694 rra_ptr_t *temp;
696 temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
697 sizeof(rra_ptr_t) *
698 (rrd->stat_head->rra_cnt + 1));
699 if (temp == NULL) {
700 rrd_set_error("parse_tag_rra: realloc failed.");
701 return (-1);
702 }
703 rrd->rra_ptr = temp;
704 cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
705 memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
706 } /* }}} */
708 /* All space successfully allocated, increment number of RRAs. */
709 rrd->stat_head->rra_cnt++;
711 status = 0;
712 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
713 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
714 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
715 /* ignore */ ;
716 else if (xmlStrcmp(child->name, (const xmlChar *) "cf") == 0)
717 status = parse_tag_rra_cf(doc, child, cur_rra_def);
718 else if (xmlStrcmp(child->name, (const xmlChar *) "pdp_per_row") == 0)
719 status = get_ulong_from_node(doc, child,
720 &cur_rra_def->pdp_cnt);
721 else if (atoi(rrd->stat_head->version) == 1
722 && xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
723 status = get_double_from_node(doc, child,
724 (double *) &cur_rra_def->
725 par[RRA_cdp_xff_val].u_val);
726 else if (atoi(rrd->stat_head->version) >= 2
727 && xmlStrcmp(child->name, (const xmlChar *) "params") == 0)
728 status = parse_tag_rra_params(doc, child, cur_rra_def);
729 else if (xmlStrcmp(child->name, (const xmlChar *) "cdp_prep") == 0)
730 status = parse_tag_rra_cdp_prep(doc, child, rrd, cur_cdp_prep);
731 else if (xmlStrcmp(child->name, (const xmlChar *) "database") == 0)
732 status = parse_tag_rra_database(doc, child, rrd);
733 else {
734 rrd_set_error("parse_tag_rra: Unknown tag: %s", child->name);
735 status = -1;
736 }
738 if (status != 0)
739 break;
740 }
742 /* Set the RRA pointer to a random location */
743 cur_rra_ptr->cur_row = rrd_random() % cur_rra_def->row_cnt;
745 return (status);
746 } /* int parse_tag_rra */
748 /*
749 * Parse a DS definition
750 */
751 static int parse_tag_ds_cdef(
752 xmlDoc * doc,
753 xmlNode * node,
754 rrd_t *rrd)
755 {
756 char buffer[1024];
757 int status;
759 status = get_string_from_node(doc, node, buffer, sizeof(buffer));
760 if (status != 0)
761 return (-1);
763 /* We're always working on the last DS that has been added to the structure
764 * when we get here */
765 parseCDEF_DS(buffer, rrd, rrd->stat_head->ds_cnt - 1);
767 return (0);
768 } /* int parse_tag_ds_cdef */
770 static int parse_tag_ds_type(
771 xmlDoc * doc,
772 xmlNode * node,
773 ds_def_t *ds_def)
774 {
775 int status;
777 status = get_string_from_node(doc, node,
778 ds_def->dst, sizeof(ds_def->dst));
779 if (status != 0)
780 return (-1);
782 status = dst_conv(ds_def->dst);
783 if (status == -1) {
784 rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
785 ds_def->dst);
786 return (-1);
787 }
789 return (0);
790 } /* int parse_tag_ds_type */
792 static int parse_tag_ds(
793 xmlDoc * doc,
794 xmlNode * node,
795 rrd_t *rrd)
796 {
797 xmlNode *child;
798 int status;
800 ds_def_t *cur_ds_def;
801 pdp_prep_t *cur_pdp_prep;
803 /*
804 * If there are DS definitions after RRA definitions the number of values,
805 * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
806 * specific order in this case.
807 */
808 if (rrd->stat_head->rra_cnt > 0) {
809 rrd_set_error("parse_tag_ds: All data source definitions MUST "
810 "precede the RRA definitions!");
811 return (-1);
812 }
814 /* Allocate space for the new DS definition */
815 { /* {{{ */
816 ds_def_t *temp;
818 temp = (ds_def_t *) realloc(rrd->ds_def,
819 sizeof(ds_def_t) *
820 (rrd->stat_head->ds_cnt + 1));
821 if (temp == NULL) {
822 rrd_set_error("parse_tag_ds: malloc failed.");
823 return (-1);
824 }
825 rrd->ds_def = temp;
826 cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
827 memset(cur_ds_def, '\0', sizeof(ds_def_t));
828 } /* }}} */
830 /* Allocate pdp_prep space for the new DS definition */
831 { /* {{{ */
832 pdp_prep_t *temp;
834 temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
835 sizeof(pdp_prep_t) *
836 (rrd->stat_head->ds_cnt + 1));
837 if (temp == NULL) {
838 rrd_set_error("parse_tag_ds: malloc failed.");
839 return (-1);
840 }
841 rrd->pdp_prep = temp;
842 cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
843 memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
844 } /* }}} */
846 /* All allocations successful, let's increment the number of DSes. */
847 rrd->stat_head->ds_cnt++;
849 status = 0;
850 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
851 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
852 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
853 /* ignore */ ;
854 else if (xmlStrcmp(child->name, (const xmlChar *) "name") == 0)
855 status = get_string_from_node(doc, child,
856 cur_ds_def->ds_nam,
857 sizeof(cur_ds_def->ds_nam));
858 else if (xmlStrcmp(child->name, (const xmlChar *) "type") == 0)
859 status = parse_tag_ds_type(doc, child, cur_ds_def);
860 else if (xmlStrcmp(child->name,
861 (const xmlChar *) "minimal_heartbeat") == 0)
862 status = get_ulong_from_node(doc, child,
863 &cur_ds_def->par[DS_mrhb_cnt].
864 u_cnt);
865 else if (xmlStrcmp(child->name, (const xmlChar *) "min") == 0)
866 status = get_double_from_node(doc, child,
867 &cur_ds_def->par[DS_min_val].u_val);
868 else if (xmlStrcmp(child->name, (const xmlChar *) "max") == 0)
869 status = get_double_from_node(doc, child,
870 &cur_ds_def->par[DS_max_val].u_val);
871 else if (xmlStrcmp(child->name, (const xmlChar *) "cdef") == 0)
872 status = parse_tag_ds_cdef(doc, child, rrd);
873 else if (xmlStrcmp(child->name, (const xmlChar *) "last_ds") == 0)
874 status = get_string_from_node(doc, child,
875 cur_pdp_prep->last_ds,
876 sizeof(cur_pdp_prep->last_ds));
877 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
878 status = get_double_from_node(doc, child,
879 &cur_pdp_prep->scratch[PDP_val].
880 u_val);
881 else if (xmlStrcmp(child->name, (const xmlChar *) "unknown_sec") == 0)
882 status = get_ulong_from_node(doc, child,
883 &cur_pdp_prep->
884 scratch[PDP_unkn_sec_cnt].u_cnt);
885 else {
886 rrd_set_error("parse_tag_ds: Unknown tag: %s", child->name);
887 status = -1;
888 }
890 if (status != 0)
891 break;
892 }
894 return (status);
895 } /* int parse_tag_ds */
897 /*
898 * Parse root nodes
899 */
900 static int parse_tag_rrd(
901 xmlDoc * doc,
902 xmlNode * node,
903 rrd_t *rrd)
904 {
905 xmlNode *child;
906 int status;
908 status = 0;
909 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
910 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
911 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
912 /* ignore */ ;
913 else if (xmlStrcmp(child->name, (const xmlChar *) "version") == 0)
914 status = get_string_from_node(doc, child,
915 rrd->stat_head->version,
916 sizeof(rrd->stat_head->version));
917 else if (xmlStrcmp(child->name, (const xmlChar *) "step") == 0)
918 status = get_ulong_from_node(doc, child,
919 &rrd->stat_head->pdp_step);
920 else if (xmlStrcmp(child->name, (const xmlChar *) "lastupdate") == 0)
921 status = get_long_from_node(doc, child,
922 &rrd->live_head->last_up);
923 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0)
924 status = parse_tag_ds(doc, child, rrd);
925 else if (xmlStrcmp(child->name, (const xmlChar *) "rra") == 0)
926 status = parse_tag_rra(doc, child, rrd);
927 else {
928 rrd_set_error("parse_tag_rrd: Unknown tag: %s", child->name);
929 status = -1;
930 }
932 if (status != 0)
933 break;
934 }
936 return (status);
937 } /* int parse_tag_rrd */
939 static rrd_t *parse_file(
940 const char *filename)
941 {
942 xmlDoc *doc;
943 xmlNode *cur;
944 int status;
946 rrd_t *rrd;
948 doc = xmlParseFile(filename);
949 if (doc == NULL) {
950 rrd_set_error("Document not parsed successfully.");
951 return (NULL);
952 }
954 cur = xmlDocGetRootElement(doc);
955 if (cur == NULL) {
956 rrd_set_error("Document is empty.");
957 xmlFreeDoc(doc);
958 return (NULL);
959 }
961 if (xmlStrcmp(cur->name, (const xmlChar *) "rrd") != 0) {
962 rrd_set_error
963 ("Document of the wrong type, root node is not \"rrd\".");
964 xmlFreeDoc(doc);
965 return (NULL);
966 }
968 rrd = (rrd_t *) malloc(sizeof(rrd_t));
969 if (rrd == NULL) {
970 rrd_set_error("parse_file: malloc failed.");
971 xmlFreeDoc(doc);
972 return (NULL);
973 }
974 memset(rrd, '\0', sizeof(rrd_t));
976 rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
977 if (rrd->stat_head == NULL) {
978 rrd_set_error("parse_tag_rrd: malloc failed.");
979 xmlFreeDoc(doc);
980 free(rrd);
981 return (NULL);
982 }
983 memset(rrd->stat_head, '\0', sizeof(stat_head_t));
985 strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
986 rrd->stat_head->float_cookie = FLOAT_COOKIE;
988 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
989 if (rrd->live_head == NULL) {
990 rrd_set_error("parse_tag_rrd: malloc failed.");
991 xmlFreeDoc(doc);
992 free(rrd->stat_head);
993 free(rrd);
994 return (NULL);
995 }
996 memset(rrd->live_head, '\0', sizeof(live_head_t));
998 status = parse_tag_rrd(doc, cur, rrd);
1000 xmlFreeDoc(doc);
1001 if (status != 0) {
1002 rrd_free(rrd);
1003 rrd = NULL;
1004 }
1006 return (rrd);
1007 } /* rrd_t *parse_file */
1009 static int write_file(
1010 const char *file_name,
1011 rrd_t *rrd)
1012 {
1013 FILE *fh;
1014 unsigned int i;
1015 unsigned int rra_offset;
1017 if (strcmp("-", file_name) == 0)
1018 fh = stdout;
1019 else {
1020 int fd_flags = O_WRONLY | O_CREAT;
1021 int fd;
1023 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1024 fd_flags |= O_BINARY;
1025 #endif
1027 if (opt_force_overwrite == 0)
1028 fd_flags |= O_EXCL;
1030 fd = open(file_name, fd_flags, 0666);
1031 if (fd == -1) {
1032 rrd_set_error("creating '%s': %s", file_name,
1033 rrd_strerror(errno));
1034 return (-1);
1035 }
1037 fh = fdopen(fd, "wb");
1038 if (fh == NULL) {
1039 rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1040 close(fd);
1041 return (-1);
1042 }
1043 }
1044 if (atoi(rrd->stat_head->version) < 3) {
1045 /* we output 3 or higher */
1046 strcpy(rrd->stat_head->version, "0003");
1047 }
1048 fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1049 fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1050 fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1051 fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1052 fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1053 fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1054 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1055 fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1057 /* calculate the number of rrd_values to dump */
1058 rra_offset = 0;
1059 for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
1060 unsigned long num_rows = rrd->rra_def[i].row_cnt;
1061 unsigned long cur_row = rrd->rra_ptr[i].cur_row;
1062 unsigned long ds_cnt = rrd->stat_head->ds_cnt;
1064 fwrite(rrd->rrd_value +
1065 (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1066 sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1068 fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1069 sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1071 rra_offset += num_rows;
1072 }
1074 /* lets see if we had an error */
1075 if (ferror(fh)) {
1076 rrd_set_error("a file error occurred while creating '%s'", file_name);
1077 fclose(fh);
1078 return (-1);
1079 }
1081 fclose(fh);
1082 return (0);
1083 } /* int write_file */
1085 int rrd_restore(
1086 int argc,
1087 char **argv)
1088 {
1089 rrd_t *rrd;
1091 /* init rrd clean */
1092 optind = 0;
1093 opterr = 0; /* initialize getopt */
1094 while (42) {
1095 int opt;
1096 int option_index = 0;
1097 static struct option long_options[] = {
1098 {"range-check", no_argument, 0, 'r'},
1099 {"force-overwrite", no_argument, 0, 'f'},
1100 {0, 0, 0, 0}
1101 };
1103 opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1105 if (opt == EOF)
1106 break;
1108 switch (opt) {
1109 case 'r':
1110 opt_range_check = 1;
1111 break;
1113 case 'f':
1114 opt_force_overwrite = 1;
1115 break;
1117 default:
1118 rrd_set_error("usage rrdtool %s [--range-check|-r] "
1119 "[--force-overwrite/-f] file.xml file.rrd",
1120 argv[0]);
1121 return (-1);
1122 break;
1123 }
1124 } /* while (42) */
1126 if ((argc - optind) != 2) {
1127 rrd_set_error("usage rrdtool %s [--range-check/-r] "
1128 "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1129 return (-1);
1130 }
1132 rrd = parse_file(argv[optind]);
1133 if (rrd == NULL)
1134 return (-1);
1136 if (write_file(argv[optind + 1], rrd) != 0) {
1137 rrd_free(rrd);
1138 return (-1);
1139 }
1141 rrd_free(rrd);
1142 return (0);
1143 } /* int rrd_restore */
1145 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */