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