1 /*****************************************************************************
2 * RRDtool 1.2.99907080300 Copyright by Tobi Oetiker, 1997-2007
3 * This file: Copyright 2008 Florian octo Forster
4 * Distributed under the GPL
5 *****************************************************************************
6 * rrd_thread_safe.c Contains routines used when thread safety is required
7 *****************************************************************************
8 * $Id$
9 *************************************************************************** */
10 * *This program is free software;
11 you can redistribute it and / or modify it
12 *
13 under the terms of the GNU General Public License as published by the Free
14 * Software Foundation;
15 either version 2 of the License, or(
16 at your option)
17 * any later version.
18 * *This program is distributed in the hope that it will be useful,
19 but WITHOUT * ANY WARRANTY;
20 without even the implied warranty of MERCHANTABILITY or
21 *
22 FITNESS FOR A PARTICULAR PURPOSE.
23 See the GNU General Public License for *more details. *
24 *You should have received a copy of the GNU General Public License
25 along * with this program; if not
26 , write to the Free Software Foundation, Inc., *51 Franklin St, Fifth Floor, Boston, MA 02110 - 1301 USA * *Authors:
27 *Florian octo Forster < octo at verplant.org > **/
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
35 # include <io.h>
36 # define open _open
37 # define close _close
38 #endif
39 #include <libxml/parser.h>
40 #include "rrd_tool.h"
41 #include "rrd_rpncalc.h"
42 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof ((a)[0]))
43 static int opt_range_check = 0;
44 static int opt_force_overwrite = 0;
46 /*
47 * Auxiliary functions
48 */
49 static int get_string_from_node(
50 xmlDoc * doc,
51 xmlNode * node,
52 char *buffer,
53 size_t buffer_size)
54 {
55 xmlChar *temp0;
56 char *begin_ptr;
57 char *end_ptr;
59 temp0 = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
60 if (temp0 == NULL) {
61 rrd_set_error("get_string_from_node: xmlNodeListGetString failed.");
62 return (-1);
63 }
65 begin_ptr = (char *) temp0;
66 while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
67 begin_ptr++;
69 if (begin_ptr[0] == 0) {
70 xmlFree(temp0);
71 buffer[0] = 0;
72 return (0);
73 }
75 end_ptr = begin_ptr;
76 while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
77 end_ptr++;
78 end_ptr[0] = 0;
80 strncpy(buffer, begin_ptr, buffer_size);
81 buffer[buffer_size - 1] = 0;
83 xmlFree(temp0);
85 return (0);
86 } /* int get_string_from_node */
88 static int get_int_from_node(
89 xmlDoc * doc,
90 xmlNode * node,
91 int *value)
92 {
93 int temp;
94 char *str_ptr;
95 char *end_ptr;
97 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
98 if (str_ptr == NULL) {
99 rrd_set_error("get_int_from_node: xmlNodeListGetString failed.");
100 return (-1);
101 }
103 end_ptr = NULL;
104 temp = strtol(str_ptr, &end_ptr, 0);
105 xmlFree(str_ptr);
107 if (str_ptr == end_ptr) {
108 rrd_set_error("get_int_from_node: Cannot parse buffer as int: %s",
109 str_ptr);
110 return (-1);
111 }
113 *value = temp;
115 return (0);
116 } /* int get_int_from_node */
118 static int get_double_from_node(
119 xmlDoc * doc,
120 xmlNode * node,
121 double *value)
122 {
123 double temp;
124 char *str_ptr;
125 char *end_ptr;
127 str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
128 if (str_ptr == NULL) {
129 rrd_set_error("get_double_from_node: xmlNodeListGetString failed.");
130 return (-1);
131 }
133 end_ptr = NULL;
134 temp = strtod(str_ptr, &end_ptr);
135 xmlFree(str_ptr);
137 if (str_ptr == end_ptr) {
138 rrd_set_error
139 ("get_double_from_node: Cannot parse buffer as double: %s",
140 str_ptr);
141 return (-1);
142 }
144 *value = temp;
146 return (0);
147 } /* int get_double_from_node */
149 static int value_check_range(
150 rrd_value_t *rrd_value,
151 const ds_def_t *ds_def)
152 {
153 double min;
154 double max;
156 if (opt_range_check == 0)
157 return (0);
159 min = ds_def->par[DS_min_val].u_val;
160 max = ds_def->par[DS_max_val].u_val;
162 if (((!isnan(min)) && (*rrd_value < min))
163 || ((!isnan(max)) && (*rrd_value > max)))
164 *rrd_value = NAN;
166 return (0);
167 } /* int value_check_range */
169 /*
170 * Parse the <database> block within an RRA definition
171 */
172 static int parse_tag_rra_database_row(
173 xmlDoc * doc,
174 xmlNode * node,
175 rrd_t *rrd,
176 rrd_value_t *rrd_value)
177 {
178 unsigned int values_count = 0;
179 xmlNode *child;
180 int status;
182 status = 0;
183 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
184 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
185 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
186 /* ignore */ ;
187 else if (xmlStrcmp(child->name, (const xmlChar *) "v") == 0) {
188 if (values_count < rrd->stat_head->ds_cnt) {
189 status =
190 get_double_from_node(doc, child,
191 rrd_value + values_count);
192 if (status == 0)
193 value_check_range(rrd_value + values_count,
194 rrd->ds_def + values_count);
195 }
197 values_count++;
198 } else {
199 rrd_set_error("parse_tag_rra_database_row: Unknown tag: %s",
200 child->name);
201 status = -1;
202 }
204 if (status != 0)
205 break;
206 } /* for (child = node->xmlChildrenNode) */
208 if (values_count != rrd->stat_head->ds_cnt) {
209 rrd_set_error("parse_tag_rra_database_row: Row has %u values "
210 "and RRD has %lu data sources.",
211 values_count, rrd->stat_head->ds_cnt);
212 status = -1;
213 }
215 return (status);
216 } /* int parse_tag_rra_database_row */
218 static int parse_tag_rra_database(
219 xmlDoc * doc,
220 xmlNode * node,
221 rrd_t *rrd)
222 {
223 rra_def_t *cur_rra_def;
224 unsigned int total_row_cnt;
225 xmlNode *child;
226 int status;
227 int i;
229 total_row_cnt = 0;
230 for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
231 total_row_cnt += rrd->rra_def[i].row_cnt;
233 cur_rra_def = rrd->rra_def + i;
235 status = 0;
236 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
237 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
238 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
239 /* ignore */ ;
240 else if (xmlStrcmp(child->name, (const xmlChar *) "row") == 0) {
241 rrd_value_t *temp;
242 rrd_value_t *cur_rrd_value;
243 unsigned int total_values_count = rrd->stat_head->ds_cnt
244 * (total_row_cnt + 1);
246 /* Allocate space for the new values.. */
247 temp = (rrd_value_t *) realloc(rrd->rrd_value,
248 sizeof(rrd_value_t) *
249 total_values_count);
250 if (temp == NULL) {
251 rrd_set_error("parse_tag_rra_database: realloc failed.");
252 status = -1;
253 break;
254 }
255 rrd->rrd_value = temp;
256 cur_rrd_value = rrd->rrd_value
257 + (rrd->stat_head->ds_cnt * total_row_cnt);
258 memset(cur_rrd_value, '\0',
259 sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
260 total_row_cnt++;
261 cur_rra_def->row_cnt++;
263 status =
264 parse_tag_rra_database_row(doc, child, rrd, cur_rrd_value);
265 } /* if (xmlStrcmp (child->name, (const xmlChar *) "row") == 0) */
266 else {
267 rrd_set_error("parse_tag_rra_database: Unknown tag: %s",
268 child->name);
269 status = -1;
270 }
272 if (status != 0)
273 break;
274 } /* for (child = node->xmlChildrenNode) */
276 return (status);
277 } /* int parse_tag_rra_database */
279 /*
280 * Parse the <cdp_prep> block within an RRA definition
281 */
282 static int parse_tag_rra_cdp_prep_ds_history(
283 xmlDoc * doc,
284 xmlNode * node,
285 cdp_prep_t *cdp_prep)
286 {
287 /* Make `history_buffer' the same size as the scratch area, plus the
288 * terminating NULL byte. */
289 char history_buffer[sizeof(((cdp_prep_t *)0)->scratch) + 1];
290 char *history_ptr;
291 int status;
292 int i;
294 status = get_string_from_node(doc, node,
295 history_buffer, sizeof(history_buffer));
296 if (status != 0)
297 return (-1);
299 history_ptr = (char *) (&cdp_prep->scratch[0]);
300 for (i = 0; history_buffer[i] != '\0'; i++)
301 history_ptr[i] = (history_buffer[i] == '1') ? 1 : 0;
303 return (0);
304 } /* int parse_tag_rra_cdp_prep_ds_history */
306 static int parse_tag_rra_cdp_prep_ds(
307 xmlDoc * doc,
308 xmlNode * node,
309 rrd_t *rrd,
310 cdp_prep_t *cdp_prep)
311 {
312 xmlNode *child;
313 int status;
315 memset(cdp_prep, '\0', sizeof(cdp_prep_t));
317 status = 0;
318 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
319 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
320 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
321 /* ignore */ ;
322 else if (xmlStrcmp(child->name, (const xmlChar *) "primary_value") ==
323 0)
324 status =
325 get_double_from_node(doc, child,
326 &cdp_prep->scratch[CDP_primary_val].
327 u_val);
328 else if (xmlStrcmp(child->name, (const xmlChar *) "secondary_value")
329 == 0)
330 status =
331 get_double_from_node(doc, child,
332 &cdp_prep->scratch[CDP_secondary_val].
333 u_val);
334 else if (xmlStrcmp(child->name, (const xmlChar *) "intercept") == 0)
335 status = get_double_from_node(doc, child,
336 &cdp_prep->
337 scratch[CDP_hw_intercept].u_val);
338 else if (xmlStrcmp(child->name, (const xmlChar *) "last_intercept") ==
339 0)
340 status =
341 get_double_from_node(doc, child,
342 &cdp_prep->
343 scratch[CDP_hw_last_intercept].u_val);
344 else if (xmlStrcmp(child->name, (const xmlChar *) "slope") == 0)
345 status = get_double_from_node(doc, child,
346 &cdp_prep->scratch[CDP_hw_slope].
347 u_val);
348 else if (xmlStrcmp(child->name, (const xmlChar *) "last_slope") == 0)
349 status = get_double_from_node(doc, child,
350 &cdp_prep->
351 scratch[CDP_hw_last_slope].u_val);
352 else if (xmlStrcmp(child->name, (const xmlChar *) "nan_count") == 0)
353 status = get_int_from_node(doc, child,
354 (int *) &cdp_prep->
355 scratch[CDP_null_count].u_cnt);
356 else if (xmlStrcmp(child->name, (const xmlChar *) "last_nan_count") ==
357 0)
358 status =
359 get_int_from_node(doc, child,
360 (int *) &cdp_prep->
361 scratch[CDP_last_null_count].u_cnt);
362 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal") == 0)
363 status = get_double_from_node(doc, child,
364 &cdp_prep->scratch[CDP_hw_seasonal].
365 u_val);
366 else if (xmlStrcmp(child->name, (const xmlChar *) "last_seasonal") ==
367 0)
368 status =
369 get_double_from_node(doc, child,
370 &cdp_prep->scratch[CDP_hw_last_seasonal].
371 u_val);
372 else if (xmlStrcmp(child->name, (const xmlChar *) "init_flag") == 0)
373 status = get_int_from_node(doc, child,
374 (int *) &cdp_prep->
375 scratch[CDP_init_seasonal].u_cnt);
376 else if (xmlStrcmp(child->name, (const xmlChar *) "history") == 0)
377 status = parse_tag_rra_cdp_prep_ds_history(doc, child, cdp_prep);
378 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
379 status = get_double_from_node(doc, child,
380 &cdp_prep->scratch[CDP_val].u_val);
381 else if (xmlStrcmp(child->name,
382 (const xmlChar *) "unknown_datapoints") == 0)
383 status = get_int_from_node(doc, child,
384 (int *) &cdp_prep->
385 scratch[CDP_unkn_pdp_cnt].u_cnt);
386 /*
387 * Compatibility code for 1.0.49
388 */
389 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) { /* {{{ */
390 unsigned int i = 0;
391 rra_def_t *rra_def = rrd->rra_def + (rrd->stat_head->rra_cnt - 1);
393 while (42) {
394 if (i >= ARRAY_LENGTH(cdp_prep->scratch)) {
395 status = -1;
396 break;
397 }
399 if ((cf_conv(rra_def->cf_nam) == CF_FAILURES)
400 || (i == CDP_unkn_pdp_cnt)
401 || (i == CDP_null_count)
402 || (i == CDP_last_null_count))
403 status = get_int_from_node(doc, child,
404 (int *) &cdp_prep->scratch[i].
405 u_cnt);
406 else
407 status = get_double_from_node(doc, child,
408 &cdp_prep->scratch[i].
409 u_val);
411 if (status != 0)
412 break;
414 /* When this loops exits (sucessfully) `child' points to the last
415 * `value' tag in the list. */
416 if ((child->next == NULL)
417 || (xmlStrcmp(child->name, (const xmlChar *) "value") !=
418 0))
419 break;
421 child = child->next;
422 i++;
423 }
424 } /* }}} */
425 else {
426 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
427 child->name);
428 status = -1;
429 }
431 if (status != 0)
432 break;
433 }
435 return (status);
436 } /* int parse_tag_rra_cdp_prep_ds */
438 static int parse_tag_rra_cdp_prep(
439 xmlDoc * doc,
440 xmlNode * node,
441 rrd_t *rrd,
442 cdp_prep_t *cdp_prep)
443 {
444 xmlNode *child;
445 int status;
447 unsigned int ds_count = 0;
449 status = 0;
450 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
451 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
452 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
453 /* ignore */ ;
454 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0) {
455 if (ds_count >= rrd->stat_head->ds_cnt)
456 status = -1;
457 else {
458 status = parse_tag_rra_cdp_prep_ds(doc, child, rrd,
459 cdp_prep + ds_count);
460 ds_count++;
461 }
462 } else {
463 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
464 child->name);
465 status = -1;
466 }
468 if (status != 0)
469 break;
470 }
472 if (ds_count != rrd->stat_head->ds_cnt) {
473 rrd_set_error("parse_tag_rra_cdp_prep: There are %i data sources in "
474 "the RRD file, but %i in this cdp_prep block!",
475 (int) rrd->stat_head->ds_cnt, ds_count);
476 status = -1;
477 }
479 return (status);
480 } /* int parse_tag_rra_cdp_prep */
482 /*
483 * Parse the <params> block within an RRA definition
484 */
485 static int parse_tag_rra_params(
486 xmlDoc * doc,
487 xmlNode * node,
488 rra_def_t *rra_def)
489 {
490 xmlNode *child;
491 int status;
493 status = 0;
494 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
495 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
496 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
497 /* ignore */ ;
498 /*
499 * Parameters for CF_HWPREDICT
500 */
501 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_alpha") == 0)
502 status = get_double_from_node(doc, child,
503 &rra_def->par[RRA_hw_alpha].u_val);
504 else if (xmlStrcmp(child->name, (const xmlChar *) "hw_beta") == 0)
505 status = get_double_from_node(doc, child,
506 &rra_def->par[RRA_hw_beta].u_val);
507 else if (xmlStrcmp(child->name,
508 (const xmlChar *) "dependent_rra_idx") == 0)
509 status = get_int_from_node(doc, child,
510 (int *) &rra_def->
511 par[RRA_dependent_rra_idx].u_cnt);
512 /*
513 * Parameters for CF_SEASONAL and CF_DEVSEASONAL
514 */
515 else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal_gamma") ==
516 0)
517 status =
518 get_double_from_node(doc, child,
519 &rra_def->par[RRA_seasonal_gamma].u_val);
520 else if (xmlStrcmp
521 (child->name, (const xmlChar *) "seasonal_smooth_idx") == 0)
522 status =
523 get_int_from_node(doc, child,
524 (int *) &rra_def->
525 par[RRA_seasonal_smooth_idx].u_cnt);
526 else if (xmlStrcmp(child->name, (const xmlChar *) "smoothing_window")
527 == 0)
528 status =
529 get_double_from_node(doc, child,
530 &rra_def->
531 par[RRA_seasonal_smoothing_window].
532 u_val);
533 /* else if (dependent_rra_idx) ...; */
534 /*
535 * Parameters for CF_FAILURES
536 */
537 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_pos") == 0)
538 status = get_double_from_node(doc, child,
539 &rra_def->par[RRA_delta_pos].u_val);
540 else if (xmlStrcmp(child->name, (const xmlChar *) "delta_neg") == 0)
541 status = get_double_from_node(doc, child,
542 &rra_def->par[RRA_delta_neg].u_val);
543 else if (xmlStrcmp(child->name, (const xmlChar *) "window_len") == 0)
544 status = get_int_from_node(doc, child,
545 (int *) &rra_def->par[RRA_window_len].
546 u_cnt);
547 else if (xmlStrcmp(child->name, (const xmlChar *) "failure_threshold")
548 == 0)
549 status =
550 get_int_from_node(doc, child,
551 (int *) &rra_def->
552 par[RRA_failure_threshold].u_cnt);
553 /*
554 * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
555 */
556 else if (xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
557 status = get_double_from_node(doc, child,
558 &rra_def->par[RRA_cdp_xff_val].
559 u_val);
560 /*
561 * Compatibility code for 1.0.49
562 */
563 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) { /* {{{ */
564 unsigned int i = 0;
566 while (42) {
567 if (i >= ARRAY_LENGTH(rra_def->par)) {
568 status = -1;
569 break;
570 }
572 if ((i == RRA_dependent_rra_idx)
573 || (i == RRA_seasonal_smooth_idx)
574 || (i == RRA_failure_threshold))
575 status = get_int_from_node(doc, child,
576 (int *) &rra_def->par[i].
577 u_cnt);
578 else
579 status = get_double_from_node(doc, child,
580 &rra_def->par[i].u_val);
582 if (status != 0)
583 break;
585 /* When this loops exits (sucessfully) `child' points to the last
586 * `value' tag in the list. */
587 if ((child->next == NULL)
588 || (xmlStrcmp(child->name, (const xmlChar *) "value") !=
589 0))
590 break;
592 child = child->next;
593 i++;
594 }
595 } /* }}} */
596 else {
597 rrd_set_error("parse_tag_rra_params: Unknown tag: %s",
598 child->name);
599 status = -1;
600 }
602 if (status != 0)
603 break;
604 }
606 return (status);
607 } /* int parse_tag_rra_params */
609 /*
610 * Parse an RRA definition
611 */
612 static int parse_tag_rra_cf(
613 xmlDoc * doc,
614 xmlNode * node,
615 rra_def_t *rra_def)
616 {
617 int status;
619 status = get_string_from_node(doc, node,
620 rra_def->cf_nam, sizeof(rra_def->cf_nam));
621 if (status != 0)
622 return (-1);
624 status = cf_conv(rra_def->cf_nam);
625 if (status == -1) {
626 rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
627 rra_def->cf_nam);
628 return (-1);
629 }
631 return (0);
632 } /* int parse_tag_rra_cf */
634 static int parse_tag_rra(
635 xmlDoc * doc,
636 xmlNode * node,
637 rrd_t *rrd)
638 {
639 xmlNode *child;
640 int status;
642 rra_def_t *cur_rra_def;
643 cdp_prep_t *cur_cdp_prep;
644 rra_ptr_t *cur_rra_ptr;
646 /* Allocate more rra_def space for this RRA */
647 { /* {{{ */
648 rra_def_t *temp;
650 temp = (rra_def_t *) realloc(rrd->rra_def,
651 sizeof(rra_def_t) *
652 (rrd->stat_head->rra_cnt + 1));
653 if (temp == NULL) {
654 rrd_set_error("parse_tag_rra: realloc failed.");
655 return (-1);
656 }
657 rrd->rra_def = temp;
658 cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
659 memset(cur_rra_def, '\0', sizeof(rra_def_t));
660 } /* }}} */
662 /* allocate cdp_prep_t */
663 { /* {{{ */
664 cdp_prep_t *temp;
666 temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
667 * rrd->stat_head->ds_cnt
668 * (rrd->stat_head->rra_cnt + 1));
669 if (temp == NULL) {
670 rrd_set_error("parse_tag_rra: realloc failed.");
671 return (-1);
672 }
673 rrd->cdp_prep = temp;
674 cur_cdp_prep = rrd->cdp_prep
675 + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
676 memset(cur_cdp_prep, '\0',
677 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
678 } /* }}} */
680 /* allocate rra_ptr_t */
681 { /* {{{ */
682 rra_ptr_t *temp;
684 temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
685 sizeof(rra_ptr_t) *
686 (rrd->stat_head->rra_cnt + 1));
687 if (temp == NULL) {
688 rrd_set_error("parse_tag_rra: realloc failed.");
689 return (-1);
690 }
691 rrd->rra_ptr = temp;
692 cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
693 memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
694 } /* }}} */
696 /* All space successfully allocated, increment number of RRAs. */
697 rrd->stat_head->rra_cnt++;
699 status = 0;
700 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
701 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
702 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
703 /* ignore */ ;
704 else if (xmlStrcmp(child->name, (const xmlChar *) "cf") == 0)
705 status = parse_tag_rra_cf(doc, child, cur_rra_def);
706 else if (xmlStrcmp(child->name, (const xmlChar *) "pdp_per_row") == 0)
707 status = get_int_from_node(doc, child,
708 (int *) &cur_rra_def->pdp_cnt);
709 else if (xmlStrcmp(child->name, (const xmlChar *) "params") == 0)
710 status = parse_tag_rra_params(doc, child, cur_rra_def);
711 else if (xmlStrcmp(child->name, (const xmlChar *) "cdp_prep") == 0)
712 status = parse_tag_rra_cdp_prep(doc, child, rrd, cur_cdp_prep);
713 else if (xmlStrcmp(child->name, (const xmlChar *) "database") == 0)
714 status = parse_tag_rra_database(doc, child, rrd);
715 else {
716 rrd_set_error("parse_tag_rra: Unknown tag: %s", child->name);
717 status = -1;
718 }
720 if (status != 0)
721 break;
722 }
724 /* Set the RRA pointer to the last value in the archive */
725 cur_rra_ptr->cur_row = cur_rra_def->row_cnt - 1;
727 return (status);
728 } /* int parse_tag_rra */
730 /*
731 * Parse a DS definition
732 */
733 static int parse_tag_ds_cdef(
734 xmlDoc * doc,
735 xmlNode * node,
736 rrd_t *rrd)
737 {
738 char buffer[1024];
739 int status;
741 status = get_string_from_node(doc, node, buffer, sizeof(buffer));
742 if (status != 0)
743 return (-1);
745 /* We're always working on the last DS that has been added to the structure
746 * when we get here */
747 parseCDEF_DS(buffer, rrd, rrd->stat_head->ds_cnt - 1);
749 return (0);
750 } /* int parse_tag_ds_cdef */
752 static int parse_tag_ds_type(
753 xmlDoc * doc,
754 xmlNode * node,
755 ds_def_t *ds_def)
756 {
757 int status;
759 status = get_string_from_node(doc, node,
760 ds_def->dst, sizeof(ds_def->dst));
761 if (status != 0)
762 return (-1);
764 status = dst_conv(ds_def->dst);
765 if (status == -1) {
766 rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
767 ds_def->dst);
768 return (-1);
769 }
771 return (0);
772 } /* int parse_tag_ds_type */
774 static int parse_tag_ds(
775 xmlDoc * doc,
776 xmlNode * node,
777 rrd_t *rrd)
778 {
779 xmlNode *child;
780 int status;
782 ds_def_t *cur_ds_def;
783 pdp_prep_t *cur_pdp_prep;
785 /*
786 * If there are DS definitions after RRA definitions the number of values,
787 * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
788 * specific order in this case.
789 */
790 if (rrd->stat_head->rra_cnt > 0) {
791 rrd_set_error("parse_tag_ds: All data source definitions MUST "
792 "precede the RRA definitions!");
793 return (-1);
794 }
796 /* Allocate space for the new DS definition */
797 { /* {{{ */
798 ds_def_t *temp;
800 temp = (ds_def_t *) realloc(rrd->ds_def,
801 sizeof(ds_def_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->ds_def = temp;
808 cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
809 memset(cur_ds_def, '\0', sizeof(ds_def_t));
810 } /* }}} */
812 /* Allocate pdp_prep space for the new DS definition */
813 { /* {{{ */
814 pdp_prep_t *temp;
816 temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
817 sizeof(pdp_prep_t) *
818 (rrd->stat_head->ds_cnt + 1));
819 if (temp == NULL) {
820 rrd_set_error("parse_tag_ds: malloc failed.");
821 return (-1);
822 }
823 rrd->pdp_prep = temp;
824 cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
825 memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
826 } /* }}} */
828 /* All allocations successful, let's increment the number of DSes. */
829 rrd->stat_head->ds_cnt++;
831 status = 0;
832 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
833 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
834 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
835 /* ignore */ ;
836 else if (xmlStrcmp(child->name, (const xmlChar *) "name") == 0)
837 status = get_string_from_node(doc, child,
838 cur_ds_def->ds_nam,
839 sizeof(cur_ds_def->ds_nam));
840 else if (xmlStrcmp(child->name, (const xmlChar *) "type") == 0)
841 status = parse_tag_ds_type(doc, child, cur_ds_def);
842 else if (xmlStrcmp(child->name,
843 (const xmlChar *) "minimal_heartbeat") == 0)
844 status = get_int_from_node(doc, child,
845 (int *) &cur_ds_def->par[DS_mrhb_cnt].
846 u_cnt);
847 else if (xmlStrcmp(child->name, (const xmlChar *) "min") == 0)
848 status = get_double_from_node(doc, child,
849 &cur_ds_def->par[DS_min_val].u_val);
850 else if (xmlStrcmp(child->name, (const xmlChar *) "max") == 0)
851 status = get_double_from_node(doc, child,
852 &cur_ds_def->par[DS_max_val].u_val);
853 else if (xmlStrcmp(child->name, (const xmlChar *) "cdef") == 0)
854 status = parse_tag_ds_cdef(doc, child, rrd);
855 else if (xmlStrcmp(child->name, (const xmlChar *) "last_ds") == 0)
856 status = get_string_from_node(doc, child,
857 cur_pdp_prep->last_ds,
858 sizeof(cur_pdp_prep->last_ds));
859 else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
860 status = get_double_from_node(doc, child,
861 &cur_pdp_prep->scratch[PDP_val].
862 u_val);
863 else if (xmlStrcmp(child->name, (const xmlChar *) "unknown_sec") == 0)
864 status = get_int_from_node(doc, child,
865 (int *) &cur_pdp_prep->
866 scratch[PDP_unkn_sec_cnt].u_cnt);
867 else {
868 rrd_set_error("parse_tag_ds: Unknown tag: %s", child->name);
869 status = -1;
870 }
872 if (status != 0)
873 break;
874 }
876 return (status);
877 } /* int parse_tag_ds */
879 /*
880 * Parse root nodes
881 */
882 static int parse_tag_rrd(
883 xmlDoc * doc,
884 xmlNode * node,
885 rrd_t *rrd)
886 {
887 xmlNode *child;
888 int status;
890 status = 0;
891 for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
892 if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
893 || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
894 /* ignore */ ;
895 else if (xmlStrcmp(child->name, (const xmlChar *) "version") == 0)
896 status = get_string_from_node(doc, child,
897 rrd->stat_head->version,
898 sizeof(rrd->stat_head->version));
899 else if (xmlStrcmp(child->name, (const xmlChar *) "step") == 0)
900 status = get_int_from_node(doc, child,
901 (int *) &rrd->stat_head->pdp_step);
902 else if (xmlStrcmp(child->name, (const xmlChar *) "lastupdate") == 0)
903 status = get_int_from_node(doc, child,
904 (int *) &rrd->live_head->last_up);
905 else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0)
906 status = parse_tag_ds(doc, child, rrd);
907 else if (xmlStrcmp(child->name, (const xmlChar *) "rra") == 0)
908 status = parse_tag_rra(doc, child, rrd);
909 else {
910 rrd_set_error("parse_tag_rrd: Unknown tag: %s", child->name);
911 status = -1;
912 }
914 if (status != 0)
915 break;
916 }
918 return (status);
919 } /* int parse_tag_rrd */
921 static rrd_t *parse_file(
922 const char *filename)
923 {
924 xmlDoc *doc;
925 xmlNode *cur;
926 int status;
928 rrd_t *rrd;
930 doc = xmlParseFile(filename);
931 if (doc == NULL) {
932 rrd_set_error("Document not parsed successfully.");
933 return (NULL);
934 }
936 cur = xmlDocGetRootElement(doc);
937 if (cur == NULL) {
938 rrd_set_error("Document is empty.");
939 xmlFreeDoc(doc);
940 return (NULL);
941 }
943 if (xmlStrcmp(cur->name, (const xmlChar *) "rrd") != 0) {
944 rrd_set_error
945 ("Document of the wrong type, root node is not \"rrd\".");
946 xmlFreeDoc(doc);
947 return (NULL);
948 }
950 rrd = (rrd_t *) malloc(sizeof(rrd_t));
951 if (rrd == NULL) {
952 rrd_set_error("parse_file: malloc failed.");
953 xmlFreeDoc(doc);
954 return (NULL);
955 }
956 memset(rrd, '\0', sizeof(rrd_t));
958 rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
959 if (rrd->stat_head == NULL) {
960 rrd_set_error("parse_tag_rrd: malloc failed.");
961 xmlFreeDoc(doc);
962 free(rrd);
963 return (NULL);
964 }
965 memset(rrd->stat_head, '\0', sizeof(stat_head_t));
967 strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
968 rrd->stat_head->float_cookie = FLOAT_COOKIE;
970 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
971 if (rrd->live_head == NULL) {
972 rrd_set_error("parse_tag_rrd: malloc failed.");
973 xmlFreeDoc(doc);
974 free(rrd->stat_head);
975 free(rrd);
976 return (NULL);
977 }
978 memset(rrd->live_head, '\0', sizeof(live_head_t));
980 status = parse_tag_rrd(doc, cur, rrd);
982 xmlFreeDoc(doc);
983 if (status != 0) {
984 rrd_free(rrd);
985 rrd = NULL;
986 }
988 return (rrd);
989 } /* rrd_t *parse_file */
991 static int write_file(
992 const char *file_name,
993 rrd_t *rrd)
994 {
995 FILE *fh;
996 unsigned int i;
997 unsigned int value_count;
999 if (strcmp("-", file_name) == 0)
1000 fh = stdout;
1001 else {
1002 int fd_flags = O_WRONLY | O_CREAT;
1003 int fd;
1005 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1006 fd_flags |= O_BINARY;
1007 #endif
1009 if (opt_force_overwrite == 0)
1010 fd_flags |= O_EXCL;
1012 fd = open(file_name, fd_flags, 0666);
1013 if (fd == -1) {
1014 rrd_set_error("creating '%s': %s", file_name,
1015 rrd_strerror(errno));
1016 return (-1);
1017 }
1019 fh = fdopen(fd, "wb");
1020 if (fh == NULL) {
1021 rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1022 close(fd);
1023 return (-1);
1024 }
1025 }
1027 fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1028 fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1029 fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1030 fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1031 fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1032 fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1033 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1034 fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1036 /* calculate the number of rrd_values to dump */
1037 value_count = 0;
1038 for (i = 0; i < rrd->stat_head->rra_cnt; i++)
1039 value_count += (rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt);
1041 fwrite(rrd->rrd_value, sizeof(rrd_value_t), value_count, fh);
1043 /* lets see if we had an error */
1044 if (ferror(fh)) {
1045 rrd_set_error("a file error occurred while creating '%s'", file_name);
1046 fclose(fh);
1047 return (-1);
1048 }
1050 fclose(fh);
1051 return (0);
1052 } /* int write_file */
1054 int rrd_restore(
1055 int argc,
1056 char **argv)
1057 {
1058 rrd_t *rrd;
1060 /* init rrd clean */
1061 optind = 0;
1062 opterr = 0; /* initialize getopt */
1063 while (42) {
1064 int opt;
1065 int option_index = 0;
1066 static struct option long_options[] = {
1067 {"range-check", no_argument, 0, 'r'},
1068 {"force-overwrite", no_argument, 0, 'f'},
1069 {0, 0, 0, 0}
1070 };
1072 opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1074 if (opt == EOF)
1075 break;
1077 switch (opt) {
1078 case 'r':
1079 opt_range_check = 1;
1080 break;
1082 case 'f':
1083 opt_force_overwrite = 1;
1084 break;
1086 default:
1087 rrd_set_error("usage rrdtool %s [--range-check|-r] "
1088 "[--force-overwrite/-f] file.xml file.rrd",
1089 argv[0]);
1090 return (-1);
1091 break;
1092 }
1093 } /* while (42) */
1095 if ((argc - optind) != 2) {
1096 rrd_set_error("usage rrdtool %s [--range-check/-r] "
1097 "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1098 return (-1);
1099 }
1101 rrd = parse_file(argv[optind]);
1102 if (rrd == NULL)
1103 return (-1);
1105 if (write_file(argv[optind + 1], rrd) != 0) {
1106 rrd_free(rrd);
1107 return (-1);
1108 }
1110 rrd_free(rrd);
1111 return (0);
1112 } /* int rrd_restore */
1114 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */