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