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_long(
223 xmlTextReaderPtr reader,
224 long *value)
225 {
226 xmlChar *text;
227 long temp;
228 if ((text = get_xml_text(reader)) != NULL){
229 errno = 0;
230 temp = strtol((char *)text,NULL, 0);
231 if (errno>0){
232 rrd_set_error("ling %d: get_xml_long from '%s' %s",
233 xmlTextReaderGetParserLineNumber(reader),
234 text,rrd_strerror(errno));
235 xmlFree(text);
236 return -1;
237 }
238 xmlFree(text);
239 *value = temp;
240 return 0;
241 }
242 return -1;
243 } /* get_xml_long */
245 static int get_xml_ulong(
246 xmlTextReaderPtr reader,
247 unsigned long *value)
248 {
250 xmlChar *text;
251 unsigned long temp;
252 if ((text = get_xml_text(reader)) != NULL){
253 errno = 0;
254 temp = strtoul((char *)text,NULL, 0);
255 if (errno>0){
256 rrd_set_error("ling %d: get_xml_ulong from '%s' %s",
257 xmlTextReaderGetParserLineNumber(reader),
258 text,rrd_strerror(errno));
259 xmlFree(text);
260 return -1;
261 }
262 xmlFree(text);
263 *value = temp;
264 return 0;
265 }
266 return -1;
267 } /* get_xml_ulong */
269 #ifndef TIME_T_IS_LONG
270 static int get_xml_llong(
271 xmlTextReaderPtr reader,
272 long long *value)
273 {
275 xmlChar *text;
276 long long temp;
277 if ((text = get_xml_text(reader)) != NULL){
278 errno = 0;
279 temp = strtoll((char *)text,NULL, 0);
280 if (errno>0){
281 rrd_set_error("ling %d: get_xml_llong from '%s' %s",
282 xmlTextReaderGetParserLineNumber(reader),
283 text,rrd_strerror(errno));
284 xmlFree(text);
285 return -1;
286 }
287 xmlFree(text);
288 *value = temp;
289 return 0;
290 }
291 return -1;
292 } /* get_xml_llong */
294 #endif
296 static int get_xml_double(
297 xmlTextReaderPtr reader,
298 double *value)
299 {
301 xmlChar *text;
302 double temp;
303 if ((text = get_xml_text(reader))!= NULL){
304 if (xmlStrcasestr(text,(xmlChar *)"nan")){
305 *value = DNAN;
306 xmlFree(text);
307 return 0;
308 }
309 else if (xmlStrcasestr(text,(xmlChar *)"-inf")){
310 *value = -DINF;
311 xmlFree(text);
312 return 0;
313 }
314 else if (xmlStrcasestr(text,(xmlChar *)"+inf")
315 || xmlStrcasestr(text,(xmlChar *)"inf")){
316 *value = DINF;
317 xmlFree(text);
318 return 0;
319 }
320 errno = 0;
321 temp = strtod((char *)text,NULL);
322 if (errno>0){
323 rrd_set_error("ling %d: get_xml_double from '%s' %s",
324 xmlTextReaderGetParserLineNumber(reader),
325 text,rrd_strerror(errno));
326 xmlFree(text);
327 return -1;
328 }
329 xmlFree(text);
330 *value = temp;
331 return 0;
332 }
333 return -1;
334 } /* get_xml_double */
337 static int value_check_range(
338 rrd_value_t *rrd_value,
339 const ds_def_t *ds_def)
340 {
341 double min;
342 double max;
344 if (opt_range_check == 0)
345 return (0);
347 min = ds_def->par[DS_min_val].u_val;
348 max = ds_def->par[DS_max_val].u_val;
350 if (((!isnan(min)) && (*rrd_value < min))
351 || ((!isnan(max)) && (*rrd_value > max)))
352 *rrd_value = DNAN;
354 return (0);
355 } /* int value_check_range */
357 /*
358 * Parse the <database> block within an RRA definition
359 */
361 static int parse_tag_rra_database_row(
362 xmlTextReaderPtr reader,
363 rrd_t *rrd,
364 rrd_value_t *rrd_value)
365 {
366 unsigned int values_count = 0;
367 int status;
369 status = 0;
370 for (values_count = 0;values_count < rrd->stat_head->ds_cnt;values_count++){
371 if (expect_element(reader,"v") == 0){
372 status = get_xml_double(reader,rrd_value + values_count);
373 if (status == 0)
374 value_check_range(rrd_value + values_count,
375 rrd->ds_def + values_count);
376 else
377 break;
378 } else
379 return -1;
380 if (expect_element(reader,"/v") == -1){
381 return -1;
382 }
383 }
384 return status;
385 } /* int parse_tag_rra_database_row */
387 static int parse_tag_rra_database(
388 xmlTextReaderPtr reader,
389 rrd_t *rrd )
390 {
391 rra_def_t *cur_rra_def;
392 unsigned int total_row_cnt;
393 int status;
394 int i;
395 xmlChar *element;
397 total_row_cnt = 0;
398 for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
399 total_row_cnt += rrd->rra_def[i].row_cnt;
401 cur_rra_def = rrd->rra_def + i;
403 status = 0;
404 while ((element = get_xml_element(reader)) != NULL){
405 if (xmlStrcasecmp(element,(const xmlChar *)"row") == 0){
406 rrd_value_t *temp;
407 rrd_value_t *cur_rrd_value;
408 unsigned int total_values_count = rrd->stat_head->ds_cnt
409 * (total_row_cnt + 1);
411 /* Allocate space for the new values.. */
412 temp = (rrd_value_t *) realloc(rrd->rrd_value,
413 sizeof(rrd_value_t) *
414 total_values_count);
415 if (temp == NULL) {
416 rrd_set_error("parse_tag_rra_database: realloc failed.");
417 status = -1;
418 break;
419 }
420 rrd->rrd_value = temp;
421 cur_rrd_value = rrd->rrd_value
422 + (rrd->stat_head->ds_cnt * total_row_cnt);
423 memset(cur_rrd_value, '\0',
424 sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
425 total_row_cnt++;
426 cur_rra_def->row_cnt++;
428 status =
429 parse_tag_rra_database_row(reader, rrd, cur_rrd_value);
430 if (status == 0)
431 status = expect_element(reader,"/row");
432 } /* if (xmlStrcasecmp(element,"row")) */
433 else {
434 if ( xmlStrcasecmp(element,(const xmlChar *)"/database") == 0){
435 xmlFree(element);
436 break;
437 }
438 else {
439 rrd_set_error("line %d: found unexpected tag: %s",
440 xmlTextReaderGetParserLineNumber(reader),element);
441 status = -1;
442 }
443 }
444 xmlFree(element);
445 if (status != 0)
446 break;
447 }
448 return (status);
449 } /* int parse_tag_rra_database */
451 /*
452 * Parse the <cdp_prep> block within an RRA definition
453 */
454 static int parse_tag_rra_cdp_prep_ds_history(
455 xmlTextReaderPtr reader,
456 cdp_prep_t *cdp_prep)
457 {
458 /* Make `history_buffer' the same size as the scratch area, plus the
459 * terminating NULL byte. */
460 xmlChar *history;
461 char *history_ptr;
462 int i;
463 if ((history = get_xml_text(reader)) != NULL){
464 history_ptr = (char *) (&cdp_prep->scratch[0]);
465 for (i = 0; history[i] != '\0'; i++)
466 history_ptr[i] = (history[i] == '1') ? 1 : 0;
467 xmlFree(history);
468 return 0;
469 }
470 return -1;
471 } /* int parse_tag_rra_cdp_prep_ds_history */
473 static int parse_tag_rra_cdp_prep_ds(
474 xmlTextReaderPtr reader,
475 rrd_t *rrd,
476 cdp_prep_t *cdp_prep)
477 {
478 int status;
479 xmlChar *element;
480 memset(cdp_prep, '\0', sizeof(cdp_prep_t));
482 status = -1;
484 if (atoi(rrd->stat_head->version) == 1) {
485 cdp_prep->scratch[CDP_primary_val].u_val = 0.0;
486 cdp_prep->scratch[CDP_secondary_val].u_val = 0.0;
487 }
489 while ((element = get_xml_element(reader)) != NULL){
490 if (xmlStrcasecmp(element, (const xmlChar *) "primary_value") == 0)
491 status =
492 get_xml_double(reader,&cdp_prep->scratch[CDP_primary_val].u_val);
493 else if (xmlStrcasecmp(element, (const xmlChar *) "secondary_value") == 0)
494 status =
495 get_xml_double(reader,&cdp_prep->scratch[CDP_secondary_val].u_val);
496 else if (xmlStrcasecmp(element, (const xmlChar *) "intercept") == 0)
497 status = get_xml_double(reader,
498 &cdp_prep->
499 scratch[CDP_hw_intercept].u_val);
500 else if (xmlStrcasecmp(element, (const xmlChar *) "last_intercept") ==
501 0)
502 status =
503 get_xml_double(reader,
504 &cdp_prep->
505 scratch[CDP_hw_last_intercept].u_val);
506 else if (xmlStrcasecmp(element, (const xmlChar *) "slope") == 0)
507 status = get_xml_double(reader,
508 &cdp_prep->scratch[CDP_hw_slope].
509 u_val);
510 else if (xmlStrcasecmp(element, (const xmlChar *) "last_slope") == 0)
511 status = get_xml_double(reader,
512 &cdp_prep->
513 scratch[CDP_hw_last_slope].u_val);
514 else if (xmlStrcasecmp(element, (const xmlChar *) "nan_count") == 0)
515 status = get_xml_ulong(reader,
516 &cdp_prep->
517 scratch[CDP_null_count].u_cnt);
518 else if (xmlStrcasecmp(element, (const xmlChar *) "last_nan_count") ==
519 0)
520 status =
521 get_xml_ulong(reader,
522 &cdp_prep->
523 scratch[CDP_last_null_count].u_cnt);
524 else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal") == 0)
525 status = get_xml_double(reader,
526 &cdp_prep->scratch[CDP_hw_seasonal].
527 u_val);
528 else if (xmlStrcasecmp(element, (const xmlChar *) "last_seasonal") ==
529 0)
530 status =
531 get_xml_double(reader,
532 &cdp_prep->scratch[CDP_hw_last_seasonal].
533 u_val);
534 else if (xmlStrcasecmp(element, (const xmlChar *) "init_flag") == 0)
535 status = get_xml_ulong(reader,
536 &cdp_prep->
537 scratch[CDP_init_seasonal].u_cnt);
538 else if (xmlStrcasecmp(element, (const xmlChar *) "history") == 0)
539 status = parse_tag_rra_cdp_prep_ds_history(reader, cdp_prep);
540 else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
541 status = get_xml_double(reader,
542 &cdp_prep->scratch[CDP_val].u_val);
543 else if (xmlStrcasecmp(element,
544 (const xmlChar *) "unknown_datapoints") == 0)
545 status = get_xml_ulong(reader,
546 &cdp_prep->
547 scratch[CDP_unkn_pdp_cnt].u_cnt);
548 else if (xmlStrcasecmp(element,
549 (const xmlChar *) "/ds") == 0){
550 xmlFree(element);
551 break;
552 }
553 else {
554 rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
555 element);
556 status = -1;
557 xmlFree(element);
558 break;
559 }
560 if (status != 0){
561 xmlFree(element);
562 break;
563 }
564 status = expect_element_end(reader,(char *)element);
565 xmlFree(element);
566 if (status != 0)
567 break;
568 }
569 return (status);
570 } /* int parse_tag_rra_cdp_prep_ds */
572 static int parse_tag_rra_cdp_prep(
573 xmlTextReaderPtr reader,
574 rrd_t *rrd,
575 cdp_prep_t *cdp_prep)
576 {
577 int status;
579 unsigned int ds_count;
581 status = 0;
582 for ( ds_count = 0; ds_count < rrd->stat_head->ds_cnt;ds_count++){
583 if (expect_element(reader,"ds") == 0) {
584 status = parse_tag_rra_cdp_prep_ds(reader, rrd,
585 cdp_prep + ds_count);
586 if (status != 0)
587 break;
588 } else {
589 status = -1;
590 break;
591 }
592 }
593 if (status == 0)
594 status = expect_element(reader,"/cdp_prep");
595 return (status);
596 } /* int parse_tag_rra_cdp_prep */
598 /*
599 * Parse the <params> block within an RRA definition
600 */
601 static int parse_tag_rra_params(
602 xmlTextReaderPtr reader,
603 rra_def_t *rra_def)
604 {
605 xmlChar *element;
606 int status;
608 status = -1;
609 while ((element = get_xml_element(reader)) != NULL){
610 /*
611 * Parameters for CF_HWPREDICT
612 */
613 if (xmlStrcasecmp(element, (const xmlChar *) "hw_alpha") == 0)
614 status = get_xml_double(reader,
615 &rra_def->par[RRA_hw_alpha].u_val);
616 else if (xmlStrcasecmp(element, (const xmlChar *) "hw_beta") == 0)
617 status = get_xml_double(reader,
618 &rra_def->par[RRA_hw_beta].u_val);
619 else if (xmlStrcasecmp(element,
620 (const xmlChar *) "dependent_rra_idx") == 0)
621 status = get_xml_ulong(reader,
622 &rra_def->
623 par[RRA_dependent_rra_idx].u_cnt);
624 /*
625 * Parameters for CF_SEASONAL and CF_DEVSEASONAL
626 */
627 else if (xmlStrcasecmp(element, (const xmlChar *) "seasonal_gamma") ==
628 0)
629 status =
630 get_xml_double(reader,
631 &rra_def->par[RRA_seasonal_gamma].u_val);
632 else if (xmlStrcasecmp
633 (element, (const xmlChar *) "seasonal_smooth_idx") == 0)
634 status =
635 get_xml_ulong(reader,
636 &rra_def->
637 par[RRA_seasonal_smooth_idx].u_cnt);
638 else if (xmlStrcasecmp(element, (const xmlChar *) "smoothing_window")
639 == 0)
640 status =
641 get_xml_double(reader,
642 &rra_def->
643 par[RRA_seasonal_smoothing_window].
644 u_val);
645 /* else if (dependent_rra_idx) ...; */
646 /*
647 * Parameters for CF_FAILURES
648 */
649 else if (xmlStrcasecmp(element, (const xmlChar *) "delta_pos") == 0)
650 status = get_xml_double(reader,
651 &rra_def->par[RRA_delta_pos].u_val);
652 else if (xmlStrcasecmp(element, (const xmlChar *) "delta_neg") == 0)
653 status = get_xml_double(reader,
654 &rra_def->par[RRA_delta_neg].u_val);
655 else if (xmlStrcasecmp(element, (const xmlChar *) "window_len") == 0)
656 status = get_xml_ulong(reader,
657 &rra_def->par[RRA_window_len].
658 u_cnt);
659 else if (xmlStrcasecmp(element, (const xmlChar *) "failure_threshold")
660 == 0)
661 status =
662 get_xml_ulong(reader,
663 &rra_def->
664 par[RRA_failure_threshold].u_cnt);
665 /*
666 * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
667 */
668 else if (xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
669 status = get_xml_double(reader,
670 &rra_def->par[RRA_cdp_xff_val].
671 u_val);
672 /*
673 * Compatibility code for 1.0.49
674 */
675 else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0) { /* {{{ */
676 unsigned int i = 0;
678 for (i=0;i<ARRAY_LENGTH(rra_def->par);i++){
679 if ((i == RRA_dependent_rra_idx)
680 || (i == RRA_seasonal_smooth_idx)
681 || (i == RRA_failure_threshold))
682 status = get_xml_ulong(reader,
683 &rra_def->par[i].
684 u_cnt);
685 else
686 status = get_xml_double(reader,
687 &rra_def->par[i].u_val);
689 if (status != 0)
690 break;
691 if ( i-1 < ARRAY_LENGTH(rra_def->par)){
692 status = expect_element(reader,"/value");
693 if (status == 0){
694 status = expect_element(reader,"value");
695 }
696 }
697 if (status != 0){
698 break;
699 }
700 }
701 } /* }}} */
702 else if (xmlStrcasecmp(element,(const xmlChar *) "/params") == 0){
703 xmlFree(element);
704 return status;
705 } /* }}} */
706 else {
707 rrd_set_error("line %d: parse_tag_rra_params: Unknown tag: %s",
708 xmlTextReaderGetParserLineNumber(reader),element);
709 status = -1;
710 }
711 status = expect_element_end(reader,(char *)element);
712 xmlFree(element);
713 if (status != 0)
714 break;
715 }
716 return (status);
717 } /* int parse_tag_rra_params */
719 /*
720 * Parse an RRA definition
721 */
722 static int parse_tag_rra_cf(
723 xmlTextReaderPtr reader,
724 rra_def_t *rra_def)
725 {
726 int status;
728 status = get_xml_string(reader,
729 rra_def->cf_nam, sizeof(rra_def->cf_nam));
730 if (status != 0)
731 return status;
733 status = cf_conv(rra_def->cf_nam);
734 if (status == -1) {
735 rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
736 rra_def->cf_nam);
737 return -1;
738 }
740 return 0;
741 } /* int parse_tag_rra_cf */
743 static int parse_tag_rra(
744 xmlTextReaderPtr reader,
745 rrd_t *rrd)
746 {
747 int status;
748 xmlChar *element;
750 rra_def_t *cur_rra_def;
751 cdp_prep_t *cur_cdp_prep;
752 rra_ptr_t *cur_rra_ptr;
754 /* Allocate more rra_def space for this RRA */
755 { /* {{{ */
756 rra_def_t *temp;
758 temp = (rra_def_t *) realloc(rrd->rra_def,
759 sizeof(rra_def_t) *
760 (rrd->stat_head->rra_cnt + 1));
761 if (temp == NULL) {
762 rrd_set_error("parse_tag_rra: realloc failed.");
763 return (-1);
764 }
765 rrd->rra_def = temp;
766 cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
767 memset(cur_rra_def, '\0', sizeof(rra_def_t));
768 } /* }}} */
770 /* allocate cdp_prep_t */
771 { /* {{{ */
772 cdp_prep_t *temp;
774 temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
775 * rrd->stat_head->ds_cnt
776 * (rrd->stat_head->rra_cnt + 1));
777 if (temp == NULL) {
778 rrd_set_error("parse_tag_rra: realloc failed.");
779 return (-1);
780 }
781 rrd->cdp_prep = temp;
782 cur_cdp_prep = rrd->cdp_prep
783 + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
784 memset(cur_cdp_prep, '\0',
785 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
786 } /* }}} */
788 /* allocate rra_ptr_t */
789 { /* {{{ */
790 rra_ptr_t *temp;
792 temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
793 sizeof(rra_ptr_t) *
794 (rrd->stat_head->rra_cnt + 1));
795 if (temp == NULL) {
796 rrd_set_error("parse_tag_rra: realloc failed.");
797 return (-1);
798 }
799 rrd->rra_ptr = temp;
800 cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
801 memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
802 } /* }}} */
804 /* All space successfully allocated, increment number of RRAs. */
805 rrd->stat_head->rra_cnt++;
807 status = 0;
808 while ((element = get_xml_element(reader)) != NULL){
809 if (xmlStrcasecmp(element, (const xmlChar *) "cf") == 0)
810 status = parse_tag_rra_cf(reader, cur_rra_def);
811 else if (xmlStrcasecmp(element, (const xmlChar *) "pdp_per_row") == 0)
812 status = get_xml_ulong(reader,
813 &cur_rra_def->pdp_cnt);
814 else if (atoi(rrd->stat_head->version) == 1
815 && xmlStrcasecmp(element, (const xmlChar *) "xff") == 0)
816 status = get_xml_double(reader,
817 (double *) &cur_rra_def->
818 par[RRA_cdp_xff_val].u_val);
819 else if (atoi(rrd->stat_head->version) >= 2
820 && xmlStrcasecmp(element, (const xmlChar *) "params") == 0){
821 xmlFree(element);
822 status = parse_tag_rra_params(reader, cur_rra_def);
823 if (status == 0)
824 continue;
825 else
826 return status;
827 }
828 else if (xmlStrcasecmp(element, (const xmlChar *) "cdp_prep") == 0){
829 xmlFree(element);
830 status = parse_tag_rra_cdp_prep(reader, rrd, cur_cdp_prep);
831 if (status == 0)
832 continue;
833 else
834 return status;
835 }
836 else if (xmlStrcasecmp(element, (const xmlChar *) "database") == 0){
837 xmlFree(element);
838 status = parse_tag_rra_database(reader, rrd);
839 if (status == 0)
840 continue;
841 else
842 return status;
843 }
844 else if (xmlStrcasecmp(element,(const xmlChar *) "/rra") == 0){
845 xmlFree(element);
846 return status;
847 } /* }}} */
848 else {
849 rrd_set_error("line %d: parse_tag_rra: Unknown tag: %s",
850 xmlTextReaderGetParserLineNumber(reader), element);
851 status = -1;
852 }
853 if (status != 0) {
854 xmlFree(element);
855 return status;
856 }
857 status = expect_element_end(reader,(char *)element);
858 xmlFree(element);
859 if (status != 0) {
860 return status;
861 }
862 }
863 /* Set the RRA pointer to a random location */
864 cur_rra_ptr->cur_row = rrd_random() % cur_rra_def->row_cnt;
866 return (status);
867 } /* int parse_tag_rra */
869 /*
870 * Parse a DS definition
871 */
872 static int parse_tag_ds_cdef(
873 xmlTextReaderPtr reader,
874 rrd_t *rrd)
875 {
876 xmlChar *cdef;
878 cdef = get_xml_text(reader);
879 if (cdef != NULL){
880 /* We're always working on the last DS that has been added to the structure
881 * when we get here */
882 parseCDEF_DS((char *)cdef, rrd, rrd->stat_head->ds_cnt - 1);
883 xmlFree(cdef);
884 if (rrd_test_error())
885 return -1;
886 else
887 return 0;
888 }
889 return -1;
890 } /* int parse_tag_ds_cdef */
892 static int parse_tag_ds_type(
893 xmlTextReaderPtr reader,
894 ds_def_t *ds_def)
895 {
896 char *dst;
897 dst = (char *)get_xml_text(reader);
898 if (dst != NULL){
899 int status;
900 status = dst_conv(dst);
901 if (status == -1) {
902 rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
903 dst);
904 return -1;
905 }
906 strncpy(ds_def->dst,dst,sizeof(ds_def->dst)-1);
907 ds_def->dst[sizeof(ds_def->dst)-1] = '\0';
908 xmlFree(dst);
909 return 0;
910 }
911 return -1;
912 } /* int parse_tag_ds_type */
914 static int parse_tag_ds(
915 xmlTextReaderPtr reader,
916 rrd_t *rrd)
917 {
918 int status;
919 xmlChar *element;
921 ds_def_t *cur_ds_def;
922 pdp_prep_t *cur_pdp_prep;
924 /*
925 * If there are DS definitions after RRA definitions the number of values,
926 * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
927 * specific order in this case.
928 */
929 if (rrd->stat_head->rra_cnt > 0) {
930 rrd_set_error("parse_tag_ds: All data source definitions MUST "
931 "precede the RRA definitions!");
932 return (-1);
933 }
935 /* Allocate space for the new DS definition */
936 { /* {{{ */
937 ds_def_t *temp;
939 temp = (ds_def_t *) realloc(rrd->ds_def,
940 sizeof(ds_def_t) *
941 (rrd->stat_head->ds_cnt + 1));
942 if (temp == NULL) {
943 rrd_set_error("parse_tag_ds: malloc failed.");
944 return (-1);
945 }
946 rrd->ds_def = temp;
947 cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
948 memset(cur_ds_def, '\0', sizeof(ds_def_t));
949 } /* }}} */
951 /* Allocate pdp_prep space for the new DS definition */
952 { /* {{{ */
953 pdp_prep_t *temp;
955 temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
956 sizeof(pdp_prep_t) *
957 (rrd->stat_head->ds_cnt + 1));
958 if (temp == NULL) {
959 rrd_set_error("parse_tag_ds: malloc failed.");
960 return (-1);
961 }
962 rrd->pdp_prep = temp;
963 cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
964 memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
965 } /* }}} */
967 /* All allocations successful, let's increment the number of DSes. */
968 rrd->stat_head->ds_cnt++;
970 status = 0;
971 while ((element = get_xml_element(reader)) != NULL){
972 if (xmlStrcasecmp(element, (const xmlChar *) "name") == 0){
973 status = get_xml_string(reader,cur_ds_def->ds_nam,sizeof(cur_ds_def->ds_nam));
974 }
975 else if (xmlStrcasecmp(element, (const xmlChar *) "type") == 0)
976 status = parse_tag_ds_type(reader, cur_ds_def);
977 else if (xmlStrcasecmp(element,
978 (const xmlChar *) "minimal_heartbeat") == 0)
979 status = get_xml_ulong(reader,
980 &cur_ds_def->par[DS_mrhb_cnt].
981 u_cnt);
982 else if (xmlStrcasecmp(element, (const xmlChar *) "min") == 0)
983 status = get_xml_double(reader,
984 &cur_ds_def->par[DS_min_val].u_val);
985 else if (xmlStrcasecmp(element, (const xmlChar *) "max") == 0)
986 status = get_xml_double(reader,
987 &cur_ds_def->par[DS_max_val].u_val);
988 else if (xmlStrcasecmp(element, (const xmlChar *) "cdef") == 0)
989 status = parse_tag_ds_cdef(reader, rrd);
990 else if (xmlStrcasecmp(element, (const xmlChar *) "last_ds") == 0)
991 status = get_xml_string(reader,
992 cur_pdp_prep->last_ds,
993 sizeof(cur_pdp_prep->last_ds));
994 else if (xmlStrcasecmp(element, (const xmlChar *) "value") == 0)
995 status = get_xml_double(reader,
996 &cur_pdp_prep->scratch[PDP_val].
997 u_val);
998 else if (xmlStrcasecmp(element, (const xmlChar *) "unknown_sec") == 0)
999 status = get_xml_ulong(reader,
1000 &cur_pdp_prep->
1001 scratch[PDP_unkn_sec_cnt].u_cnt);
1002 else if (xmlStrcasecmp(element, (const xmlChar *) "/ds") == 0) {
1003 xmlFree(element);
1004 break;
1005 }
1006 else {
1007 rrd_set_error("parse_tag_ds: Unknown tag: %s", element);
1008 status = -1;
1009 }
1010 if (status != 0) {
1011 xmlFree(element);
1012 break;
1013 }
1014 status = expect_element_end(reader,(char *)element);
1015 xmlFree(element);
1016 if (status != 0)
1017 break;
1018 }
1020 return (status);
1021 } /* int parse_tag_ds */
1023 /*
1024 * Parse root nodes
1025 */
1026 static int parse_tag_rrd(
1027 xmlTextReaderPtr reader,
1028 rrd_t *rrd)
1029 {
1030 int status;
1031 xmlChar *element;
1033 status = 0;
1034 while ((element = get_xml_element(reader)) != NULL ){
1035 if (xmlStrcasecmp(element, (const xmlChar *) "version") == 0)
1036 status = get_xml_string(reader,
1037 rrd->stat_head->version,
1038 sizeof(rrd->stat_head->version));
1039 else if (xmlStrcasecmp(element, (const xmlChar *) "step") == 0)
1040 status = get_xml_ulong(reader,
1041 &rrd->stat_head->pdp_step);
1042 else if (xmlStrcasecmp(element, (const xmlChar *) "lastupdate") == 0) {
1043 #ifdef TIME_T_IS_LONG
1044 status = get_xml_long(reader, &rrd->live_head->last_up);
1045 #else
1046 #ifdef TIME_T_IS_LONG_LONG
1047 status = get_xml_llong(reader, &rrd->live_head->last_up);
1048 #else
1049 if (sizeof(time_t) == sizeof(long)) {
1050 status = get_xml_long(reader,
1051 (long *)&rrd->live_head->last_up);
1052 }
1053 else if (sizeof(time_t) == sizeof(long long)) {
1054 status = get_xml_llong(reader,
1055 (long long *)&rrd->live_head->last_up);
1056 }
1057 #endif
1058 #endif
1059 }
1060 else if (xmlStrcasecmp(element, (const xmlChar *) "ds") == 0){
1061 xmlFree(element);
1062 status = parse_tag_ds(reader, rrd);
1063 /* as we come back the </ds> tag is already gone */
1064 if (status == 0)
1065 continue;
1066 else
1067 return status;
1068 }
1069 else if (xmlStrcasecmp(element, (const xmlChar *) "rra") == 0){
1070 xmlFree(element);
1071 status = parse_tag_rra(reader, rrd);
1072 if (status == 0)
1073 continue;
1074 else
1075 return status;
1076 }
1077 else if (xmlStrcasecmp(element, (const xmlChar *) "/rrd") == 0) {
1078 xmlFree(element);
1079 return status;
1080 }
1081 else {
1082 rrd_set_error("parse_tag_rrd: Unknown tag: %s", element);
1083 status = -1;
1084 }
1086 if (status != 0){
1087 xmlFree(element);
1088 break;
1089 }
1090 status = expect_element_end(reader,(char *)element);
1091 xmlFree(element);
1092 if (status != 0)
1093 break;
1094 }
1095 return (status);
1096 } /* int parse_tag_rrd */
1098 static rrd_t *parse_file(
1099 const char *filename)
1100 {
1101 xmlTextReaderPtr reader;
1102 int status;
1104 rrd_t *rrd;
1106 reader = xmlNewTextReaderFilename(filename);
1107 if (reader == NULL) {
1108 rrd_set_error("Could not create xml reader for: %s",filename);
1109 return (NULL);
1110 }
1112 if (expect_element(reader,"rrd") != 0) {
1113 xmlFreeTextReader(reader);
1114 return (NULL);
1115 }
1117 rrd = (rrd_t *) malloc(sizeof(rrd_t));
1118 if (rrd == NULL) {
1119 rrd_set_error("parse_file: malloc failed.");
1120 xmlFreeTextReader(reader);
1121 return (NULL);
1122 }
1123 memset(rrd, '\0', sizeof(rrd_t));
1125 rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
1126 if (rrd->stat_head == NULL) {
1127 rrd_set_error("parse_tag_rrd: malloc failed.");
1128 xmlFreeTextReader(reader);
1129 free(rrd);
1130 return (NULL);
1131 }
1132 memset(rrd->stat_head, '\0', sizeof(stat_head_t));
1134 strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
1135 rrd->stat_head->float_cookie = FLOAT_COOKIE;
1137 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
1138 if (rrd->live_head == NULL) {
1139 rrd_set_error("parse_tag_rrd: malloc failed.");
1140 xmlFreeTextReader(reader);
1141 free(rrd->stat_head);
1142 free(rrd);
1143 return (NULL);
1144 }
1145 memset(rrd->live_head, '\0', sizeof(live_head_t));
1147 status = parse_tag_rrd(reader, rrd);
1149 xmlFreeTextReader(reader);
1151 if (status != 0) {
1152 local_rrd_free(rrd);
1153 rrd = NULL;
1154 }
1156 return (rrd);
1157 } /* rrd_t *parse_file */
1159 static int write_file(
1160 const char *file_name,
1161 rrd_t *rrd)
1162 {
1163 FILE *fh;
1164 unsigned int i;
1165 unsigned int rra_offset;
1167 if (strcmp("-", file_name) == 0)
1168 fh = stdout;
1169 else {
1170 int fd_flags = O_WRONLY | O_CREAT;
1171 int fd;
1173 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
1174 fd_flags |= O_BINARY;
1175 #endif
1177 if (opt_force_overwrite == 0)
1178 fd_flags |= O_EXCL;
1180 fd = open(file_name, fd_flags, 0666);
1181 if (fd == -1) {
1182 rrd_set_error("creating '%s': %s", file_name,
1183 rrd_strerror(errno));
1184 return (-1);
1185 }
1187 fh = fdopen(fd, "wb");
1188 if (fh == NULL) {
1189 rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
1190 close(fd);
1191 return (-1);
1192 }
1193 }
1194 if (atoi(rrd->stat_head->version) < 3) {
1195 /* we output 3 or higher */
1196 strcpy(rrd->stat_head->version, "0003");
1197 }
1198 fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
1199 fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
1200 fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
1201 fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
1202 fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
1203 fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
1204 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
1205 fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);
1207 /* calculate the number of rrd_values to dump */
1208 rra_offset = 0;
1209 for (i = 0; i < rrd->stat_head->rra_cnt; i++) {
1210 unsigned long num_rows = rrd->rra_def[i].row_cnt;
1211 unsigned long cur_row = rrd->rra_ptr[i].cur_row;
1212 unsigned long ds_cnt = rrd->stat_head->ds_cnt;
1214 fwrite(rrd->rrd_value +
1215 (rra_offset + num_rows - 1 - cur_row) * ds_cnt,
1216 sizeof(rrd_value_t), (cur_row + 1) * ds_cnt, fh);
1218 fwrite(rrd->rrd_value + rra_offset * ds_cnt,
1219 sizeof(rrd_value_t), (num_rows - 1 - cur_row) * ds_cnt, fh);
1221 rra_offset += num_rows;
1222 }
1224 /* lets see if we had an error */
1225 if (ferror(fh)) {
1226 rrd_set_error("a file error occurred while creating '%s'", file_name);
1227 fclose(fh);
1228 return (-1);
1229 }
1231 fclose(fh);
1232 return (0);
1233 } /* int write_file */
1235 int rrd_restore(
1236 int argc,
1237 char **argv)
1238 {
1239 rrd_t *rrd;
1240 char *old_locale;
1241 /* init rrd clean */
1242 optind = 0;
1243 opterr = 0; /* initialize getopt */
1244 while (42) {
1245 int opt;
1246 int option_index = 0;
1247 static struct option long_options[] = {
1248 {"range-check", no_argument, 0, 'r'},
1249 {"force-overwrite", no_argument, 0, 'f'},
1250 {0, 0, 0, 0}
1251 };
1253 opt = getopt_long(argc, argv, "rf", long_options, &option_index);
1255 if (opt == EOF)
1256 break;
1258 switch (opt) {
1259 case 'r':
1260 opt_range_check = 1;
1261 break;
1263 case 'f':
1264 opt_force_overwrite = 1;
1265 break;
1267 default:
1268 rrd_set_error("usage rrdtool %s [--range-check|-r] "
1269 "[--force-overwrite/-f] file.xml file.rrd",
1270 argv[0]);
1271 return (-1);
1272 break;
1273 }
1274 } /* while (42) */
1276 if ((argc - optind) != 2) {
1277 rrd_set_error("usage rrdtool %s [--range-check/-r] "
1278 "[--force-overwrite/-f] file.xml file.rrd", argv[0]);
1279 return (-1);
1280 }
1282 old_locale = setlocale(LC_NUMERIC, "C");
1284 rrd = parse_file(argv[optind]);
1286 setlocale(LC_NUMERIC, old_locale);
1288 if (rrd == NULL)
1289 return (-1);
1291 if (write_file(argv[optind + 1], rrd) != 0) {
1292 local_rrd_free(rrd);
1293 return (-1);
1294 }
1295 local_rrd_free(rrd);
1298 return (0);
1299 } /* int rrd_restore */
1301 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */