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