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