1 /*****************************************************************************
2 * RRDtool 1.4.2 Copyright by Tobi Oetiker, 1997-2009
3 *****************************************************************************
4 * rrd_restore.c Contains logic to parse XML input and create an RRD file
5 * This file:
6 * Copyright (C) 2008 Florian octo Forster (original libxml2 code)
7 * Copyright (C) 2008,2009 Tobias Oetiker
8 *****************************************************************************
9 * $Id$
10 *************************************************************************** */
12 #include "rrd_tool.h"
13 #include "rrd_rpncalc.h"
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <libxml/parser.h>
19 #include <libxml/xmlreader.h>
20 #include <locale.h>
22 #ifndef WIN32
23 # include <unistd.h> /* for off_t */
24 #else
25 typedef size_t ssize_t;
26 typedef long off_t;
27 #endif
29 #include <fcntl.h>
30 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
31 # include <io.h>
32 # define open _open
33 # define close _close
34 #endif
37 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof ((a)[0]))
39 static int opt_range_check = 0;
40 static int opt_force_overwrite = 0;
42 /*
43 * Helpers
44 */
46 /* skip all but tags. complain if we do not get the right tag */
47 /* dept -1 causes depth to be ignored */
48 static xmlChar* get_xml_element (
49 xmlTextReaderPtr reader
50 )
51 {
52 while(xmlTextReaderRead(reader)){
53 int type;
54 xmlChar *name;
55 type = xmlTextReaderNodeType(reader);
56 if (type == XML_READER_TYPE_TEXT){
57 xmlChar *value;
58 value = xmlTextReaderValue(reader);
59 rrd_set_error("line %d: expected element but found text '%s'",
60 xmlTextReaderGetParserLineNumber(reader),value);
61 xmlFree(value);
62 return NULL;
63 }
64 /* skip all other non-elements */
65 if (type != XML_READER_TYPE_ELEMENT && type != XML_READER_TYPE_END_ELEMENT)
66 continue;
68 name = xmlTextReaderName(reader);
69 if (type == XML_READER_TYPE_END_ELEMENT){
70 xmlChar *temp;
71 xmlChar *temp2;
72 temp = (xmlChar*)sprintf_alloc("/%s",name);
73 temp2 = xmlStrdup(temp);
74 free(temp);
75 xmlFree(name);
76 return temp2;
77 }
78 /* all seems well, return the happy news */
79 return name;
80 }
81 rrd_set_error("the xml ended while we were looking for an element");
82 return NULL;
83 } /* get_xml_element */
85 static void local_rrd_free (rrd_t *rrd)
86 {
87 free(rrd->live_head);
88 free(rrd->stat_head);
89 free(rrd->ds_def);
90 free(rrd->rra_def);
91 free(rrd->rra_ptr);
92 free(rrd->pdp_prep);
93 free(rrd->cdp_prep);
94 free(rrd->rrd_value);
95 free(rrd);
96 }
99 static int expect_element (
100 xmlTextReaderPtr reader,
101 char *exp_name)
102 {
103 xmlChar *name;
104 name = get_xml_element(reader);
105 if (!name)
106 return -1;
107 if (xmlStrcasecmp(name,(xmlChar *)exp_name) != 0){
108 rrd_set_error("line %d: expected <%s> element but found <%s>",
109 xmlTextReaderGetParserLineNumber(reader),name,exp_name);
110 xmlFree(name);
111 return -1;
112 }
113 xmlFree(name);
114 return 0;
115 } /* expect_element */
117 static int expect_element_end (
118 xmlTextReaderPtr reader,
119 char *exp_name)
120 {
121 xmlChar *name;
122 /* maybe we are already on the end element ... lets see */
123 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT){
124 xmlChar *temp;
125 xmlChar *temp2;
126 temp = xmlTextReaderName(reader);
127 temp2 = (xmlChar*)sprintf_alloc("/%s", temp);
128 name = xmlStrdup(temp2);
129 xmlFree(temp);
130 free(temp2);
131 } else {
132 name = get_xml_element(reader);
133 }
135 if (name == NULL)
136 return -1;
137 if (xmlStrcasecmp(name+1,(xmlChar *)exp_name) != 0 || name[0] != '/'){
138 rrd_set_error("line %d: expected </%s> end element but found <%s>",
139 xmlTextReaderGetParserLineNumber(reader),exp_name,name);
140 xmlFree(name);
141 return -1;
142 }
143 xmlFree(name);
144 return 0;
145 } /* expect_element_end */
148 static xmlChar* get_xml_text (
149 xmlTextReaderPtr reader
150 )
151 {
152 while(xmlTextReaderRead(reader)){
153 xmlChar *ret;
154 xmlChar *text;
155 xmlChar *begin_ptr;
156 xmlChar *end_ptr;
157 int type;
158 type = xmlTextReaderNodeType(reader);
159 if (type == XML_READER_TYPE_ELEMENT){
160 xmlChar *name;
161 name = xmlTextReaderName(reader);
162 rrd_set_error("line %d: expected a value but found a <%s> element",
163 xmlTextReaderGetParserLineNumber(reader),
164 name);
165 xmlFree(name);
166 return NULL;
167 }
169 /* trying to read text from <a></a> we end up here
170 lets return an empty string insead. This is a tad optimistic
171 since we do not check if it is actually </a> and not </b>
172 we got, but first we do not know if we expect </a> and second
173 we the whole implementation is on the optimistic side. */
174 if (type == XML_READER_TYPE_END_ELEMENT){
175 return xmlStrdup(BAD_CAST "");
176 }
178 /* skip all other non-text */
179 if (type != XML_READER_TYPE_TEXT)
180 continue;
182 text = xmlTextReaderValue(reader);
184 begin_ptr = text;
185 while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
186 begin_ptr++;
187 if (begin_ptr[0] == 0) {
188 xmlFree(text);
189 return xmlStrdup(BAD_CAST "");
190 }
191 end_ptr = begin_ptr;
192 while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
193 end_ptr++;
194 end_ptr[0] = 0;
196 ret = xmlStrdup(begin_ptr);
197 xmlFree(text);
198 return ret;
199 }
200 rrd_set_error("file ended while looking for text");
201 return NULL;
202 } /* get_xml_text */
205 static int get_xml_string(
206 xmlTextReaderPtr reader,
207 char *value,
208 int max_len)
209 {
210 xmlChar *str;
211 str = get_xml_text(reader);
212 if (str != NULL){
213 strncpy(value,(char *)str,max_len);
214 xmlFree(str);
215 return 0;
216 }
217 else
218 return -1;
219 }
222 static int get_xml_time_t(
223 xmlTextReaderPtr reader,
224 time_t *value)
225 {
226 xmlChar *text;
227 time_t temp;
228 if ((text = get_xml_text(reader)) != NULL){
229 errno = 0;
230 #ifdef TIME_T_IS_32BIT
231 temp = strtol((char *)text,NULL, 0);
232 #else
233 #ifdef TIME_T_IS_64BIT
234 temp = strtoll((char *)text,NULL, 0);
235 #else
236 if (sizeof(time_t) == 4){
237 temp = strtol((char *)text,NULL, 0);
238 } else {
239 temp = strtoll((char *)text,NULL, 0);
240 }
241 #endif
242 #endif
243 if (errno>0){
244 rrd_set_error("ling %d: get_xml_time_t from '%s' %s",
245 xmlTextReaderGetParserLineNumber(reader),
246 text,rrd_strerror(errno));
247 xmlFree(text);
248 return -1;
249 }
250 xmlFree(text);
251 *value = temp;
252 return 0;
253 }
254 return -1;
255 } /* get_xml_time_t */
257 static int get_xml_ulong(
258 xmlTextReaderPtr reader,
259 unsigned long *value)
260 {
262 xmlChar *text;
263 unsigned long temp;
264 if ((text = get_xml_text(reader)) != NULL){
265 errno = 0;
266 temp = strtoul((char *)text,NULL, 0);
267 if (errno>0){
268 rrd_set_error("ling %d: get_xml_ulong from '%s' %s",
269 xmlTextReaderGetParserLineNumber(reader),
270 text,rrd_strerror(errno));
271 xmlFree(text);
272 return -1;
273 }
274 xmlFree(text);
275 *value = temp;
276 return 0;
277 }
278 return -1;
279 } /* get_xml_ulong */
281 static int get_xml_double(
282 xmlTextReaderPtr reader,
283 double *value)
284 {
286 xmlChar *text;
287 double temp;
288 if ((text = get_xml_text(reader))!= NULL){
289 if (xmlStrcasestr(text,(xmlChar *)"nan")){
290 *value = DNAN;
291 xmlFree(text);
292 return 0;
293 }
294 else if (xmlStrcasestr(text,(xmlChar *)"-inf")){
295 *value = -DINF;
296 xmlFree(text);
297 return 0;
298 }
299 else if (xmlStrcasestr(text,(xmlChar *)"+inf")
300 || xmlStrcasestr(text,(xmlChar *)"inf")){
301 *value = DINF;
302 xmlFree(text);
303 return 0;
304 }
305 errno = 0;
306 temp = strtod((char *)text,NULL);
307 if (errno>0){
308 rrd_set_error("ling %d: get_xml_double from '%s' %s",
309 xmlTextReaderGetParserLineNumber(reader),
310 text,rrd_strerror(errno));
311 xmlFree(text);
312 return -1;
313 }
314 xmlFree(text);
315 *value = temp;
316 return 0;
317 }
318 return -1;
319 } /* get_xml_double */
322 static int value_check_range(
323 rrd_value_t *rrd_value,
324 const ds_def_t *ds_def)
325 {
326 double min;
327 double max;
329 if (opt_range_check == 0)
330 return (0);
332 min = ds_def->par[DS_min_val].u_val;
333 max = ds_def->par[DS_max_val].u_val;
335 if (((!isnan(min)) && (*rrd_value < min))
336 || ((!isnan(max)) && (*rrd_value > max)))
337 *rrd_value = DNAN;
339 return (0);
340 } /* int value_check_range */
342 /*
343 * Parse the <database> block within an RRA definition
344 */
346 static int parse_tag_rra_database_row(
347 xmlTextReaderPtr reader,
348 rrd_t *rrd,
349 rrd_value_t *rrd_value)
350 {
351 unsigned int values_count = 0;
352 int status;
354 status = 0;
355 for (values_count = 0;values_count < rrd->stat_head->ds_cnt;values_count++){
356 if (expect_element(reader,"v") == 0){
357 status = get_xml_double(reader,rrd_value + values_count);
358 if (status == 0)
359 value_check_range(rrd_value + values_count,
360 rrd->ds_def + values_count);
361 else
362 break;
363 } else
364 return -1;
365 if (expect_element(reader,"/v") == -1){
366 return -1;
367 }
368 }
369 return status;
370 } /* int parse_tag_rra_database_row */
372 static int parse_tag_rra_database(
373 xmlTextReaderPtr reader,
374 rrd_t *rrd )
375 {
376 rra_def_t *cur_rra_def;
377 unsigned int total_row_cnt;
378 int status;
379 int i;
380 xmlChar *element;
382 total_row_cnt = 0;
383 for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
384 total_row_cnt += rrd->rra_def[i].row_cnt;
386 cur_rra_def = rrd->rra_def + i;
388 status = 0;
389 while ((element = get_xml_element(reader)) != NULL){
390 if (xmlStrcasecmp(element,(const xmlChar *)"row") == 0){
391 rrd_value_t *temp;
392 rrd_value_t *cur_rrd_value;
393 unsigned int total_values_count = rrd->stat_head->ds_cnt
394 * (total_row_cnt + 1);
396 /* Allocate space for the new values.. */
397 temp = (rrd_value_t *) realloc(rrd->rrd_value,
398 sizeof(rrd_value_t) *
399 total_values_count);
400 if (temp == NULL) {
401 rrd_set_error("parse_tag_rra_database: realloc failed.");
402 status = -1;
403 break;
404 }
405 rrd->rrd_value = temp;
406 cur_rrd_value = rrd->rrd_value
407 + (rrd->stat_head->ds_cnt * total_row_cnt);
408 memset(cur_rrd_value, '\0',
409 sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
410 total_row_cnt++;
411 cur_rra_def->row_cnt++;
413 status =
414 parse_tag_rra_database_row(reader, rrd, cur_rrd_value);
415 if (status == 0)
416 status = expect_element(reader,"/row");
417 } /* if (xmlStrcasecmp(element,"row")) */
418 else {
419 if ( xmlStrcasecmp(element,(const xmlChar *)"/database") == 0){
420 xmlFree(element);
421 break;
422 }
423 else {
424 rrd_set_error("line %d: found unexpected tag: %s",
425 xmlTextReaderGetParserLineNumber(reader),element);
426 status = -1;
427 }
428 }
429 xmlFree(element);
430 if (status != 0)
431 break;
432 }
433 return (status);
434 } /* int parse_tag_rra_database */
436 /*
437 * Parse the <cdp_prep> block within an RRA definition
438 */
439 static int parse_tag_rra_cdp_prep_ds_history(
440 xmlTextReaderPtr reader,
441 cdp_prep_t *cdp_prep)
442 {
443 /* Make `history_buffer' the same size as the scratch area, plus the
444 * terminating NULL byte. */
445 xmlChar *history;
446 char *history_ptr;
447 int i;
448 if ((history = get_xml_text(reader)) != NULL){
449 history_ptr = (char *) (&cdp_prep->scratch[0]);
450 for (i = 0; history[i] != '\0'; i++)
451 history_ptr[i] = (history[i] == '1') ? 1 : 0;
452 xmlFree(history);
453 return 0;
454 }
455 return -1;
456 } /* int parse_tag_rra_cdp_prep_ds_history */
458 static int parse_tag_rra_cdp_prep_ds(
459 xmlTextReaderPtr reader,
460 rrd_t *rrd,
461 cdp_prep_t *cdp_prep)
462 {
463 int status;
464 xmlChar *element;
465 memset(cdp_prep, '\0', sizeof(cdp_prep_t));
467 status = -1;
469 if (atoi(rrd->stat_head->version) == 1) {
470 cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
471 cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
472 }
474 while ((element = get_xml_element(reader)) != NULL){
475 if (xmlStrcasecmp(element, (const xmlChar *) "primary_value") == 0)
476 status =
477 get_xml_double(reader,&cdp_prep->scratch[CDP_primary_val].u_val);
478 else if (xmlStrcasecmp(element, (const xmlChar *) "secondary_value") == 0)
479 status =
480 get_xml_double(reader,&cdp_prep->scratch[CDP_secondary_val].u_val);
481 else if (xmlStrcasecmp(element, (const xmlChar *) "intercept") == 0)
482 status = get_xml_double(reader,
483 &cdp_prep->
484 scratch[CDP_hw_intercept].u_val);
485 else if (xmlStrcasecmp(element, (const xmlChar *) "last_intercept") ==
486 0)
487 status =
488 get_xml_double(reader,
489 &cdp_prep->
490 scratch[CDP_hw_last_intercept].u_val);
491 else if (xmlStrcasecmp(element, (const xmlChar *) "slope") == 0)
492 status = get_xml_double(reader,
493 &cdp_prep->scratch[CDP_hw_slope].
494 u_val);
495 else if (xmlStrcasecmp(element, (const xmlChar *) "last_slope") == 0)
496 status = get_xml_double(reader,
497 &cdp_prep->
498 scratch[CDP_hw_last_slope].u_val);
499 else if (xmlStrcasecmp(element, (const xmlChar *) "nan_count") == 0)
500 status = get_xml_ulong(reader,
501 &cdp_prep->
502 scratch[CDP_null_count].u_cnt);
503 else if (xmlStrcasecmp(element, (const xmlChar *) "last_nan_count") ==
504 0)
505 status =
506 get_xml_ulong(reader,
507 &cdp_prep->
508 scratch[CDP_last_null_count].u_cnt);
509 else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal") == 0)
510 status = get_xml_double(reader,
511 &cdp_prep->scratch[CDP_hw_seasonal].
512 u_val);
513 else if (xmlStrcasecmp(element, (const xmlChar *) "last_seasonal") ==
514 0)
515 status =
516 get_xml_double(reader,
517 &cdp_prep->scratch[CDP_hw_last_seasonal].
518 u_val);
519 else if (xmlStrcasecmp(element, (const xmlChar *) "init_flag") == 0)
520 status = get_xml_ulong(reader,
521 &cdp_prep->
522 scratch[CDP_init_seasonal].u_cnt);
523 else if (xmlStrcasecmp(element, (const xmlChar *) "history") == 0)
524 status = parse_tag_rra_cdp_prep_ds_history(reader, cdp_prep);
525 else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
526 status = get_xml_double(reader,
527 &cdp_prep->scratch[CDP_val].u_val);
528 else if (xmlStrcasecmp(element,
529 (const xmlChar *) "unknown_datapoints") == 0)
530 status = get_xml_ulong(reader,
531 &cdp_prep->
532 scratch[CDP_unkn_pdp_cnt].u_cnt);
533 else if (xmlStrcasecmp(element,
534 (const xmlChar *) "/ds") == 0){
535 xmlFree(element);
536 break;
537 }
538 else {
539 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
540 element);
541 status = -1;
542 xmlFree(element);
543 break;
544 }
545 if (status != 0){
546 xmlFree(element);
547 break;
548 }
549 status = expect_element_end(reader,(char *)element);
550 xmlFree(element);
551 if (status != 0)
552 break;
553 }
554 return (status);
555 } /* int parse_tag_rra_cdp_prep_ds */
557 static int parse_tag_rra_cdp_prep(
558 xmlTextReaderPtr reader,
559 rrd_t *rrd,
560 cdp_prep_t *cdp_prep)
561 {
562 int status;
564 unsigned int ds_count;
566 status = 0;
567 for ( ds_count = 0; ds_count < rrd->stat_head->ds_cnt;ds_count++){
568 if (expect_element(reader,"ds") == 0) {
569 status = parse_tag_rra_cdp_prep_ds(reader, rrd,
570 cdp_prep + ds_count);
571 if (status != 0)
572 break;
573 } else {
574 status = -1;
575 break;
576 }
577 }
578 if (status == 0)
579 status = expect_element(reader,"/cdp_prep");
580 return (status);
581 } /* int parse_tag_rra_cdp_prep */
583 /*
584 * Parse the <params> block within an RRA definition
585 */
586 static int parse_tag_rra_params(
587 xmlTextReaderPtr reader,
588 rra_def_t *rra_def)
589 {
590 xmlChar *element;
591 int status;
593 status = -1;
594 while ((element = get_xml_element(reader)) != NULL){
595 /*
596 * Parameters for CF_HWPREDICT
597 */
598 if (xmlStrcasecmp(element, (const xmlChar *) "hw_alpha") == 0)
599 status = get_xml_double(reader,
600 &rra_def->par[RRA_hw_alpha].u_val);
601 else if (xmlStrcasecmp(element, (const xmlChar *) "hw_beta") == 0)
602 status = get_xml_double(reader,
603 &rra_def->par[RRA_hw_beta].u_val);
604 else if (xmlStrcasecmp(element,
605 (const xmlChar *) "dependent_rra_idx") == 0)
606 status = get_xml_ulong(reader,
607 &rra_def->
608 par[RRA_dependent_rra_idx].u_cnt);
609 /*
610 * Parameters for CF_SEASONAL and CF_DEVSEASONAL
611 */
612 else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal_gamma") ==
613 0)
614 status =
615 get_xml_double(reader,
616 &rra_def->par[RRA_seasonal_gamma].u_val);
617 else if (xmlStrcasecmp
618 (element, (const xmlChar *) "seasonal_smooth_idx") == 0)
619 status =
620 get_xml_ulong(reader,
621 &rra_def->
622 par[RRA_seasonal_smooth_idx].u_cnt);
623 else if (xmlStrcasecmp(element, (const xmlChar *) "smoothing_window")
624 == 0)
625 status =
626 get_xml_double(reader,
627 &rra_def->
628 par[RRA_seasonal_smoothing_window].
629 u_val);
630 /* else if (dependent_rra_idx) ...; */
631 /*
632 * Parameters for CF_FAILURES
633 */
634 else if (xmlStrcasecmp(element, (const xmlChar *) "delta_pos") == 0)
635 status = get_xml_double(reader,
636 &rra_def->par[RRA_delta_pos].u_val);
637 else if (xmlStrcasecmp(element, (const xmlChar *) "delta_neg") == 0)
638 status = get_xml_double(reader,
639 &rra_def->par[RRA_delta_neg].u_val);
640 else if (xmlStrcasecmp(element, (const xmlChar *) "window_len") == 0)
641 status = get_xml_ulong(reader,
642 &rra_def->par[RRA_window_len].
643 u_cnt);
644 else if (xmlStrcasecmp(element, (const xmlChar *) "failure_threshold")
645 == 0)
646 status =
647 get_xml_ulong(reader,
648 &rra_def->
649 par[RRA_failure_threshold].u_cnt);
650 /*
651 * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
652 */
653 else if (xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
654 status = get_xml_double(reader,
655 &rra_def->par[RRA_cdp_xff_val].
656 u_val);
657 /*
658 * Compatibility code for 1.0.49
659 */
660 else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0) { /* {{{ */
661 unsigned int i = 0;
663 for (i=0;i<ARRAY_LENGTH(rra_def->par);i++){
664 if ((i == RRA_dependent_rra_idx)
665 || (i == RRA_seasonal_smooth_idx)
666 || (i == RRA_failure_threshold))
667 status = get_xml_ulong(reader,
668 &rra_def->par[i].
669 u_cnt);
670 else
671 status = get_xml_double(reader,
672 &rra_def->par[i].u_val);
674 if (status != 0)
675 break;
676 if ( i-1 < ARRAY_LENGTH(rra_def->par)){
677 status = expect_element(reader,"/value");
678 if (status == 0){
679 status = expect_element(reader,"value");
680 }
681 }
682 if (status != 0){
683 break;
684 }
685 }
686 } /* }}} */
687 else if (xmlStrcasecmp(element,(const xmlChar *) "/params") == 0){
688 xmlFree(element);
689 return status;
690 } /* }}} */
691 else {
692 rrd_set_error("line %d: parse_tag_rra_params: Unknown tag: %s",
693 xmlTextReaderGetParserLineNumber(reader),element);
694 status = -1;
695 }
696 status = expect_element_end(reader,(char *)element);
697 xmlFree(element);
698 if (status != 0)
699 break;
700 }
701 return (status);
702 } /* int parse_tag_rra_params */
704 /*
705 * Parse an RRA definition
706 */
707 static int parse_tag_rra_cf(
708 xmlTextReaderPtr reader,
709 rra_def_t *rra_def)
710 {
711 int status;
713 status = get_xml_string(reader,
714 rra_def->cf_nam, sizeof(rra_def->cf_nam));
715 if (status != 0)
716 return status;
718 status = cf_conv(rra_def->cf_nam);
719 if (status == -1) {
720 rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
721 rra_def->cf_nam);
722 return -1;
723 }
725 return 0;
726 } /* int parse_tag_rra_cf */
728 static int parse_tag_rra(
729 xmlTextReaderPtr reader,
730 rrd_t *rrd)
731 {
732 int status;
733 xmlChar *element;
735 rra_def_t *cur_rra_def;
736 cdp_prep_t *cur_cdp_prep;
737 rra_ptr_t *cur_rra_ptr;
739 /* Allocate more rra_def space for this RRA */
740 { /* {{{ */
741 rra_def_t *temp;
743 temp = (rra_def_t *) realloc(rrd->rra_def,
744 sizeof(rra_def_t) *
745 (rrd->stat_head->rra_cnt + 1));
746 if (temp == NULL) {
747 rrd_set_error("parse_tag_rra: realloc failed.");
748 return (-1);
749 }
750 rrd->rra_def = temp;
751 cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
752 memset(cur_rra_def, '\0', sizeof(rra_def_t));
753 } /* }}} */
755 /* allocate cdp_prep_t */
756 { /* {{{ */
757 cdp_prep_t *temp;
759 temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
760 * rrd->stat_head->ds_cnt
761 * (rrd->stat_head->rra_cnt + 1));
762 if (temp == NULL) {
763 rrd_set_error("parse_tag_rra: realloc failed.");
764 return (-1);
765 }
766 rrd->cdp_prep = temp;
767 cur_cdp_prep = rrd->cdp_prep
768 + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
769 memset(cur_cdp_prep, '\0',
770 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
771 } /* }}} */
773 /* allocate rra_ptr_t */
774 { /* {{{ */
775 rra_ptr_t *temp;
777 temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
778 sizeof(rra_ptr_t) *
779 (rrd->stat_head->rra_cnt + 1));
780 if (temp == NULL) {
781 rrd_set_error("parse_tag_rra: realloc failed.");
782 return (-1);
783 }
784 rrd->rra_ptr = temp;
785 cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
786 memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
787 } /* }}} */
789 /* All space successfully allocated, increment number of RRAs. */
790 rrd->stat_head->rra_cnt++;
792 status = 0;
793 while ((element = get_xml_element(reader)) != NULL){
794 if (xmlStrcasecmp(element, (const xmlChar *) "cf") == 0)
795 status = parse_tag_rra_cf(reader, cur_rra_def);
796 else if (xmlStrcasecmp(element, (const xmlChar *) "pdp_per_row") == 0)
797 status = get_xml_ulong(reader,
798 &cur_rra_def->pdp_cnt);
799 else if (atoi(rrd->stat_head->version) == 1
800 && xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
801 status = get_xml_double(reader,
802 (double *) &cur_rra_def->
803 par[RRA_cdp_xff_val].u_val);
804 else if (atoi(rrd->stat_head->version) >= 2
805 && xmlStrcasecmp(element, (const xmlChar *) "params") == 0){
806 xmlFree(element);
807 status = parse_tag_rra_params(reader, cur_rra_def);
808 if (status == 0)
809 continue;
810 else
811 return status;
812 }
813 else if (xmlStrcasecmp(element, (const xmlChar *) "cdp_prep") == 0){
814 xmlFree(element);
815 status = parse_tag_rra_cdp_prep(reader, rrd, cur_cdp_prep);
816 if (status == 0)
817 continue;
818 else
819 return status;
820 }
821 else if (xmlStrcasecmp(element, (const xmlChar *) "database") == 0){
822 xmlFree(element);
823 status = parse_tag_rra_database(reader, rrd);
824 if (status == 0)
825 continue;
826 else
827 return status;
828 }
829 else if (xmlStrcasecmp(element,(const xmlChar *) "/rra") == 0){
830 xmlFree(element);
831 return status;
832 } /* }}} */
833 else {
834 rrd_set_error("line %d: parse_tag_rra: Unknown tag: %s",
835 xmlTextReaderGetParserLineNumber(reader), element);
836 status = -1;
837 }
838 if (status != 0) {
839 xmlFree(element);
840 return status;
841 }
842 status = expect_element_end(reader,(char *)element);
843 xmlFree(element);
844 if (status != 0) {
845 return status;
846 }
847 }
848 /* Set the RRA pointer to a random location */
849 cur_rra_ptr->cur_row = rrd_random() % cur_rra_def->row_cnt;
851 return (status);
852 } /* int parse_tag_rra */
854 /*
855 * Parse a DS definition
856 */
857 static int parse_tag_ds_cdef(
858 xmlTextReaderPtr reader,
859 rrd_t *rrd)
860 {
861 xmlChar *cdef;
863 cdef = get_xml_text(reader);
864 if (cdef != NULL){
865 /* We're always working on the last DS that has been added to the structure
866 * when we get here */
867 parseCDEF_DS((char *)cdef, rrd, rrd->stat_head->ds_cnt - 1);
868 xmlFree(cdef);
869 if (rrd_test_error())
870 return -1;
871 else
872 return 0;
873 }
874 return -1;
875 } /* int parse_tag_ds_cdef */
877 static int parse_tag_ds_type(
878 xmlTextReaderPtr reader,
879 ds_def_t *ds_def)
880 {
881 char *dst;
882 dst = (char *)get_xml_text(reader);
883 if (dst != NULL){
884 int status;
885 status = dst_conv(dst);
886 if (status == -1) {
887 rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
888 dst);
889 return -1;
890 }
891 strncpy(ds_def->dst,dst,sizeof(ds_def->dst)-1);
892 ds_def->dst[sizeof(ds_def->dst)-1] = '\0';
893 xmlFree(dst);
894 return 0;
895 }
896 return -1;
897 } /* int parse_tag_ds_type */
899 static int parse_tag_ds(
900 xmlTextReaderPtr reader,
901 rrd_t *rrd)
902 {
903 int status;
904 xmlChar *element;
906 ds_def_t *cur_ds_def;
907 pdp_prep_t *cur_pdp_prep;
909 /*
910 * If there are DS definitions after RRA definitions the number of values,
911 * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
912 * specific order in this case.
913 */
914 if (rrd->stat_head->rra_cnt > 0) {
915 rrd_set_error("parse_tag_ds: All data source definitions MUST "
916 "precede the RRA definitions!");
917 return (-1);
918 }
920 /* Allocate space for the new DS definition */
921 { /* {{{ */
922 ds_def_t *temp;
924 temp = (ds_def_t *) realloc(rrd->ds_def,
925 sizeof(ds_def_t) *
926 (rrd->stat_head->ds_cnt + 1));
927 if (temp == NULL) {
928 rrd_set_error("parse_tag_ds: malloc failed.");
929 return (-1);
930 }
931 rrd->ds_def = temp;
932 cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
933 memset(cur_ds_def, '\0', sizeof(ds_def_t));
934 } /* }}} */
936 /* Allocate pdp_prep space for the new DS definition */
937 { /* {{{ */
938 pdp_prep_t *temp;
940 temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
941 sizeof(pdp_prep_t) *
942 (rrd->stat_head->ds_cnt + 1));
943 if (temp == NULL) {
944 rrd_set_error("parse_tag_ds: malloc failed.");
945 return (-1);
946 }
947 rrd->pdp_prep = temp;
948 cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
949 memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
950 } /* }}} */
952 /* All allocations successful, let's increment the number of DSes. */
953 rrd->stat_head->ds_cnt++;
955 status = 0;
956 while ((element = get_xml_element(reader)) != NULL){
957 if (xmlStrcasecmp(element, (const xmlChar *) "name") == 0){
958 status = get_xml_string(reader,cur_ds_def->ds_nam,sizeof(cur_ds_def->ds_nam));
959 }
960 else if (xmlStrcasecmp(element, (const xmlChar *) "type") == 0)
961 status = parse_tag_ds_type(reader, cur_ds_def);
962 else if (xmlStrcasecmp(element,
963 (const xmlChar *) "minimal_heartbeat") == 0)
964 status = get_xml_ulong(reader,
965 &cur_ds_def->par[DS_mrhb_cnt].
966 u_cnt);
967 else if (xmlStrcasecmp(element, (const xmlChar *) "min") == 0)
968 status = get_xml_double(reader,
969 &cur_ds_def->par[DS_min_val].u_val);
970 else if (xmlStrcasecmp(element, (const xmlChar *) "max") == 0)
971 status = get_xml_double(reader,
972 &cur_ds_def->par[DS_max_val].u_val);
973 else if (xmlStrcasecmp(element, (const xmlChar *) "cdef") == 0)
974 status = parse_tag_ds_cdef(reader, rrd);
975 else if (xmlStrcasecmp(element, (const xmlChar *) "last_ds") == 0)
976 status = get_xml_string(reader,
977 cur_pdp_prep->last_ds,
978 sizeof(cur_pdp_prep->last_ds));
979 else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
980 status = get_xml_double(reader,
981 &cur_pdp_prep->scratch[PDP_val].
982 u_val);
983 else if (xmlStrcasecmp(element, (const xmlChar *) "unknown_sec") == 0)
984 status = get_xml_ulong(reader,
985 &cur_pdp_prep->
986 scratch[PDP_unkn_sec_cnt].u_cnt);
987 else if (xmlStrcasecmp(element, (const xmlChar *) "/ds") == 0) {
988 xmlFree(element);
989 break;
990 }
991 else {
992 rrd_set_error("parse_tag_ds: Unknown tag: %s", element);
993 status = -1;
994 }
995 if (status != 0) {
996 xmlFree(element);
997 break;
998 }
999 status = expect_element_end(reader,(char *)element);
1000 xmlFree(element);
1001 if (status != 0)
1002 break;
1003 }
1005 return (status);
1006 } /* int parse_tag_ds */
1008 /*
1009 * Parse root nodes
1010 */
1011 static int parse_tag_rrd(
1012 xmlTextReaderPtr reader,
1013 rrd_t *rrd)
1014 {
1015 int status;
1016 xmlChar *element;
1018 status = 0;
1019 while ((element = get_xml_element(reader)) != NULL ){
1020 if (xmlStrcasecmp(element, (const xmlChar *) "version") == 0)
1021 status = get_xml_string(reader,
1022 rrd->stat_head->version,
1023 sizeof(rrd->stat_head->version));
1024 else if (xmlStrcasecmp(element, (const xmlChar *) "step") == 0)
1025 status = get_xml_ulong(reader,
1026 &rrd->stat_head->pdp_step);
1027 else if (xmlStrcasecmp(element, (const xmlChar *) "lastupdate") == 0) {
1028 status = get_xml_time_t(reader, &rrd->live_head->last_up);
1029 }
1030 else if (xmlStrcasecmp(element, (const xmlChar *) "ds") == 0){
1031 xmlFree(element);
1032 status = parse_tag_ds(reader, rrd);
1033 /* as we come back the </ds> tag is already gone */
1034 if (status == 0)
1035 continue;
1036 else
1037 return status;
1038 }
1039 else if (xmlStrcasecmp(element, (const xmlChar *) "rra") == 0){
1040 xmlFree(element);
1041 status = parse_tag_rra(reader, rrd);
1042 if (status == 0)
1043 continue;
1044 else
1045 return status;
1046 }
1047 else if (xmlStrcasecmp(element, (const xmlChar *) "/rrd") == 0) {
1048 xmlFree(element);
1049 return status;
1050 }
1051 else {
1052 rrd_set_error("parse_tag_rrd: Unknown tag: %s", element);
1053 status = -1;
1054 }
1056 if (status != 0){
1057 xmlFree(element);
1058 break;
1059 }
1060 status = expect_element_end(reader,(char *)element);
1061 xmlFree(element);
1062 if (status != 0)
1063 break;
1064 }
1065 return (status);
1066 } /* int parse_tag_rrd */
1068 static rrd_t *parse_file(
1069 const char *filename)
1070 {
1071 xmlTextReaderPtr reader;
1072 int status;
1074 rrd_t *rrd;
1076 reader = xmlNewTextReaderFilename(filename);
1077 if (reader == NULL) {
1078 rrd_set_error("Could not create xml reader for: %s",filename);
1079 return (NULL);
1080 }
1082 if (expect_element(reader,"rrd") != 0) {
1083 xmlFreeTextReader(reader);
1084 return (NULL);
1085 }
1087 rrd = (rrd_t *) malloc(sizeof(rrd_t));
1088 if (rrd == NULL) {
1089 rrd_set_error("parse_file: malloc failed.");
1090 xmlFreeTextReader(reader);
1091 return (NULL);
1092 }
1093 memset(rrd, '\0', sizeof(rrd_t));
1095 rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
1096 if (rrd->stat_head == NULL) {
1097 rrd_set_error("parse_tag_rrd: malloc failed.");
1098 xmlFreeTextReader(reader);
1099 free(rrd);
1100 return (NULL);
1101 }
1102 memset(rrd->stat_head, '\0', sizeof(stat_head_t));
1104 strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
1105 rrd->stat_head->float_cookie = FLOAT_COOKIE;
1107 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
1108 if (rrd->live_head == NULL) {
1109 rrd_set_error("parse_tag_rrd: malloc failed.");
1110 xmlFreeTextReader(reader);
1111 free(rrd->stat_head);
1112 free(rrd);
1113 return (NULL);
1114 }
1115 memset(rrd->live_head, '\0', sizeof(live_head_t));
1117 status = parse_tag_rrd(reader, rrd);
1119 xmlFreeTextReader(reader);
1121 if (status != 0) {
1122 local_rrd_free(rrd);
1123 rrd = NULL;
1124 }
1126 return (rrd);
1127 } /* rrd_t *parse_file */
1129 static int write_file(
1130 const char *file_name,
1131 rrd_t *rrd)
1132 {
1133 FILE *fh;
1134 unsigned int i;
1135 unsigned int rra_offset;
1137 if (strcmp("-", file_name) == 0)
1138 fh = stdout;
1139 else {
1140 int fd_flags = O_WRONLY | O_CREAT;
1141 int fd;
1143 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1144 fd_flags |= O_BINARY;
1145 #endif
1147 if (opt_force_overwrite == 0)
1148 fd_flags |= O_EXCL;
1150 fd = open(file_name, fd_flags, 0666);
1151 if (fd == -1) {
1152 rrd_set_error("creating '%s': %s", file_name,
1153 rrd_strerror(errno));
1154 return (-1);
1155 }
1157 fh = fdopen(fd, "wb");
1158 if (fh == NULL) {
1159 rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1160 close(fd);
1161 return (-1);
1162 }
1163 }
1164 if (atoi(rrd->stat_head->version) < 3) {
1165 /* we output 3 or higher */
1166 strcpy(rrd->stat_head->version, "0003");
1167 }
1168 fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1169 fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1170 fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1171 fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1172 fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1173 fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1174 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1175 fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1177 /* calculate the number of rrd_values to dump */
1178 rra_offset = 0;
1179 for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
1180 unsigned long num_rows = rrd->rra_def[i].row_cnt;
1181 unsigned long cur_row = rrd->rra_ptr[i].cur_row;
1182 unsigned long ds_cnt = rrd->stat_head->ds_cnt;
1184 fwrite(rrd->rrd_value +
1185 (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1186 sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1188 fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1189 sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1191 rra_offset += num_rows;
1192 }
1194 /* lets see if we had an error */
1195 if (ferror(fh)) {
1196 rrd_set_error("a file error occurred while creating '%s'", file_name);
1197 fclose(fh);
1198 return (-1);
1199 }
1201 fclose(fh);
1202 return (0);
1203 } /* int write_file */
1205 int rrd_restore(
1206 int argc,
1207 char **argv)
1208 {
1209 rrd_t *rrd;
1210 char *old_locale;
1211 /* init rrd clean */
1212 optind = 0;
1213 opterr = 0; /* initialize getopt */
1214 while (42) {
1215 int opt;
1216 int option_index = 0;
1217 static struct option long_options[] = {
1218 {"range-check", no_argument, 0, 'r'},
1219 {"force-overwrite", no_argument, 0, 'f'},
1220 {0, 0, 0, 0}
1221 };
1223 opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1225 if (opt == EOF)
1226 break;
1228 switch (opt) {
1229 case 'r':
1230 opt_range_check = 1;
1231 break;
1233 case 'f':
1234 opt_force_overwrite = 1;
1235 break;
1237 default:
1238 rrd_set_error("usage rrdtool %s [--range-check|-r] "
1239 "[--force-overwrite/-f] file.xml file.rrd",
1240 argv[0]);
1241 return (-1);
1242 break;
1243 }
1244 } /* while (42) */
1246 if ((argc - optind) != 2) {
1247 rrd_set_error("usage rrdtool %s [--range-check/-r] "
1248 "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1249 return (-1);
1250 }
1252 old_locale = setlocale(LC_NUMERIC, "C");
1254 rrd = parse_file(argv[optind]);
1256 setlocale(LC_NUMERIC, old_locale);
1258 if (rrd == NULL)
1259 return (-1);
1261 if (write_file(argv[optind + 1], rrd) != 0) {
1262 local_rrd_free(rrd);
1263 return (-1);
1264 }
1265 local_rrd_free(rrd);
1268 return (0);
1269 } /* int rrd_restore */
1271 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */