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