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