+ unsigned int ds_count = 0;
+
+ status = 0;
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
+ if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
+ || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
+ /* ignore */ ;
+ else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0) {
+ if (ds_count >= rrd->stat_head->ds_cnt)
+ status = -1;
+ else {
+ status = parse_tag_rra_cdp_prep_ds(doc, child, rrd,
+ cdp_prep + ds_count);
+ ds_count++;
+ }
+ } else {
+ rrd_set_error("parse_tag_rra_cdp_prep: Unknown tag: %s",
+ child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (ds_count != rrd->stat_head->ds_cnt) {
+ rrd_set_error("parse_tag_rra_cdp_prep: There are %i data sources in "
+ "the RRD file, but %i in this cdp_prep block!",
+ (int) rrd->stat_head->ds_cnt, ds_count);
+ status = -1;
+ }
+
+ return (status);
+} /* int parse_tag_rra_cdp_prep */
+
+/*
+ * Parse the <params> block within an RRA definition
+ */
+static int parse_tag_rra_params(
+ xmlDoc * doc,
+ xmlNode * node,
+ rra_def_t *rra_def)
+{
+ xmlNode *child;
+ int status;
+
+ status = 0;
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
+ if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
+ || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
+ /* ignore */ ;
+ /*
+ * Parameters for CF_HWPREDICT
+ */
+ else if (xmlStrcmp(child->name, (const xmlChar *) "hw_alpha") == 0)
+ status = get_double_from_node(doc, child,
+ &rra_def->par[RRA_hw_alpha].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "hw_beta") == 0)
+ status = get_double_from_node(doc, child,
+ &rra_def->par[RRA_hw_beta].u_val);
+ else if (xmlStrcmp(child->name,
+ (const xmlChar *) "dependent_rra_idx") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &rra_def->
+ par[RRA_dependent_rra_idx].u_cnt);
+ /*
+ * Parameters for CF_SEASONAL and CF_DEVSEASONAL
+ */
+ else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal_gamma") ==
+ 0)
+ status =
+ get_double_from_node(doc, child,
+ &rra_def->par[RRA_seasonal_gamma].u_val);
+ else if (xmlStrcmp
+ (child->name, (const xmlChar *) "seasonal_smooth_idx") == 0)
+ status =
+ get_int_from_node(doc, child,
+ (int *) &rra_def->
+ par[RRA_seasonal_smooth_idx].u_cnt);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "smoothing_window")
+ == 0)
+ status =
+ get_double_from_node(doc, child,
+ &rra_def->
+ par[RRA_seasonal_smoothing_window].
+ u_val);
+ /* else if (dependent_rra_idx) ...; */
+ /*
+ * Parameters for CF_FAILURES
+ */
+ else if (xmlStrcmp(child->name, (const xmlChar *) "delta_pos") == 0)
+ status = get_double_from_node(doc, child,
+ &rra_def->par[RRA_delta_pos].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "delta_neg") == 0)
+ status = get_double_from_node(doc, child,
+ &rra_def->par[RRA_delta_neg].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "window_len") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &rra_def->par[RRA_window_len].
+ u_cnt);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "failure_threshold")
+ == 0)
+ status =
+ get_int_from_node(doc, child,
+ (int *) &rra_def->
+ par[RRA_failure_threshold].u_cnt);
+ /*
+ * Parameters for CF_AVERAGE, CF_MAXIMUM, CF_MINIMUM, and CF_LAST
+ */
+ else if (xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
+ status = get_double_from_node(doc, child,
+ &rra_def->par[RRA_cdp_xff_val].
+ u_val);
+ /*
+ * Compatibility code for 1.0.49
+ */
+ else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) { /* {{{ */
+ unsigned int i = 0;
+
+ while (42) {
+ if (i >= ARRAY_LENGTH(rra_def->par)) {
+ status = -1;
+ break;
+ }
+
+ if ((i == RRA_dependent_rra_idx)
+ || (i == RRA_seasonal_smooth_idx)
+ || (i == RRA_failure_threshold))
+ status = get_int_from_node(doc, child,
+ (int *) &rra_def->par[i].
+ u_cnt);
+ else
+ status = get_double_from_node(doc, child,
+ &rra_def->par[i].u_val);
+
+ if (status != 0)
+ break;
+
+ /* When this loops exits (sucessfully) `child' points to the last
+ * `value' tag in the list. */
+ if ((child->next == NULL)
+ || (xmlStrcmp(child->name, (const xmlChar *) "value") !=
+ 0))
+ break;
+
+ child = child->next;
+ i++;
+ }
+ } /* }}} */
+ else {
+ rrd_set_error("parse_tag_rra_params: Unknown tag: %s",
+ child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ return (status);
+} /* int parse_tag_rra_params */
+
+/*
+ * Parse an RRA definition
+ */
+static int parse_tag_rra_cf(
+ xmlDoc * doc,
+ xmlNode * node,
+ rra_def_t *rra_def)
+{
+ int status;
+
+ status = get_string_from_node(doc, node,
+ rra_def->cf_nam, sizeof(rra_def->cf_nam));
+ if (status != 0)
+ return (-1);
+
+ status = cf_conv(rra_def->cf_nam);
+ if (status == -1) {
+ rrd_set_error("parse_tag_rra_cf: Unknown consolidation function: %s",
+ rra_def->cf_nam);
+ return (-1);
+ }
+
+ return (0);
+} /* int parse_tag_rra_cf */
+
+static int parse_tag_rra(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd)
+{
+ xmlNode *child;
+ int status;
+
+ rra_def_t *cur_rra_def;
+ cdp_prep_t *cur_cdp_prep;
+ rra_ptr_t *cur_rra_ptr;
+
+ /* Allocate more rra_def space for this RRA */
+ { /* {{{ */
+ rra_def_t *temp;
+
+ temp = (rra_def_t *) realloc(rrd->rra_def,
+ sizeof(rra_def_t) *
+ (rrd->stat_head->rra_cnt + 1));
+ if (temp == NULL) {
+ rrd_set_error("parse_tag_rra: realloc failed.");
+ return (-1);
+ }
+ rrd->rra_def = temp;
+ cur_rra_def = rrd->rra_def + rrd->stat_head->rra_cnt;
+ memset(cur_rra_def, '\0', sizeof(rra_def_t));
+ } /* }}} */
+
+ /* allocate cdp_prep_t */
+ { /* {{{ */
+ cdp_prep_t *temp;
+
+ temp = (cdp_prep_t *) realloc(rrd->cdp_prep, sizeof(cdp_prep_t)
+ * rrd->stat_head->ds_cnt
+ * (rrd->stat_head->rra_cnt + 1));
+ if (temp == NULL) {
+ rrd_set_error("parse_tag_rra: realloc failed.");
+ return (-1);
+ }
+ rrd->cdp_prep = temp;
+ cur_cdp_prep = rrd->cdp_prep
+ + (rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt);
+ memset(cur_cdp_prep, '\0',
+ sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt);
+ } /* }}} */
+
+ /* allocate rra_ptr_t */
+ { /* {{{ */
+ rra_ptr_t *temp;
+
+ temp = (rra_ptr_t *) realloc(rrd->rra_ptr,
+ sizeof(rra_ptr_t) *
+ (rrd->stat_head->rra_cnt + 1));
+ if (temp == NULL) {
+ rrd_set_error("parse_tag_rra: realloc failed.");
+ return (-1);
+ }
+ rrd->rra_ptr = temp;
+ cur_rra_ptr = rrd->rra_ptr + rrd->stat_head->rra_cnt;
+ memset(cur_rra_ptr, '\0', sizeof(rra_ptr_t));
+ } /* }}} */
+
+ /* All space successfully allocated, increment number of RRAs. */
+ rrd->stat_head->rra_cnt++;
+
+ status = 0;
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
+ if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
+ || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
+ /* ignore */ ;
+ else if (xmlStrcmp(child->name, (const xmlChar *) "cf") == 0)
+ status = parse_tag_rra_cf(doc, child, cur_rra_def);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "pdp_per_row") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &cur_rra_def->pdp_cnt);
+ else if (atoi(rrd->stat_head->version) == 1
+ && xmlStrcmp(child->name, (const xmlChar *) "xff") == 0)
+ status = get_double_from_node(doc, child,
+ (double *) &cur_rra_def->
+ par[RRA_cdp_xff_val].u_val);
+ else if (atoi(rrd->stat_head->version) >= 2
+ && xmlStrcmp(child->name, (const xmlChar *) "params") == 0)
+ status = parse_tag_rra_params(doc, child, cur_rra_def);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "cdp_prep") == 0)
+ status = parse_tag_rra_cdp_prep(doc, child, rrd, cur_cdp_prep);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "database") == 0)
+ status = parse_tag_rra_database(doc, child, rrd);
+ else {
+ rrd_set_error("parse_tag_rra: Unknown tag: %s", child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ /* Set the RRA pointer to a random location */
+ cur_rra_ptr->cur_row = random() % cur_rra_def->row_cnt;
+
+ return (status);
+} /* int parse_tag_rra */
+
+/*
+ * Parse a DS definition
+ */
+static int parse_tag_ds_cdef(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd)
+{
+ char buffer[1024];
+ int status;
+
+ status = get_string_from_node(doc, node, buffer, sizeof(buffer));
+ if (status != 0)
+ return (-1);
+
+ /* We're always working on the last DS that has been added to the structure
+ * when we get here */
+ parseCDEF_DS(buffer, rrd, rrd->stat_head->ds_cnt - 1);
+
+ return (0);
+} /* int parse_tag_ds_cdef */
+
+static int parse_tag_ds_type(
+ xmlDoc * doc,
+ xmlNode * node,
+ ds_def_t *ds_def)
+{
+ int status;
+
+ status = get_string_from_node(doc, node,
+ ds_def->dst, sizeof(ds_def->dst));
+ if (status != 0)
+ return (-1);
+
+ status = dst_conv(ds_def->dst);
+ if (status == -1) {
+ rrd_set_error("parse_tag_ds_type: Unknown data source type: %s",
+ ds_def->dst);
+ return (-1);
+ }
+
+ return (0);
+} /* int parse_tag_ds_type */
+
+static int parse_tag_ds(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd)
+{
+ xmlNode *child;
+ int status;
+
+ ds_def_t *cur_ds_def;
+ pdp_prep_t *cur_pdp_prep;
+
+ /*
+ * If there are DS definitions after RRA definitions the number of values,
+ * cdp_prep areas and so on will be calculated wrong. Thus, enforce a
+ * specific order in this case.
+ */
+ if (rrd->stat_head->rra_cnt > 0) {
+ rrd_set_error("parse_tag_ds: All data source definitions MUST "
+ "precede the RRA definitions!");
+ return (-1);
+ }
+
+ /* Allocate space for the new DS definition */
+ { /* {{{ */
+ ds_def_t *temp;
+
+ temp = (ds_def_t *) realloc(rrd->ds_def,
+ sizeof(ds_def_t) *
+ (rrd->stat_head->ds_cnt + 1));
+ if (temp == NULL) {
+ rrd_set_error("parse_tag_ds: malloc failed.");
+ return (-1);
+ }
+ rrd->ds_def = temp;
+ cur_ds_def = rrd->ds_def + rrd->stat_head->ds_cnt;
+ memset(cur_ds_def, '\0', sizeof(ds_def_t));
+ } /* }}} */
+
+ /* Allocate pdp_prep space for the new DS definition */
+ { /* {{{ */
+ pdp_prep_t *temp;
+
+ temp = (pdp_prep_t *) realloc(rrd->pdp_prep,
+ sizeof(pdp_prep_t) *
+ (rrd->stat_head->ds_cnt + 1));
+ if (temp == NULL) {
+ rrd_set_error("parse_tag_ds: malloc failed.");
+ return (-1);
+ }
+ rrd->pdp_prep = temp;
+ cur_pdp_prep = rrd->pdp_prep + rrd->stat_head->ds_cnt;
+ memset(cur_pdp_prep, '\0', sizeof(pdp_prep_t));
+ } /* }}} */
+
+ /* All allocations successful, let's increment the number of DSes. */
+ rrd->stat_head->ds_cnt++;
+
+ status = 0;
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
+ if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
+ || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
+ /* ignore */ ;
+ else if (xmlStrcmp(child->name, (const xmlChar *) "name") == 0)
+ status = get_string_from_node(doc, child,
+ cur_ds_def->ds_nam,
+ sizeof(cur_ds_def->ds_nam));
+ else if (xmlStrcmp(child->name, (const xmlChar *) "type") == 0)
+ status = parse_tag_ds_type(doc, child, cur_ds_def);
+ else if (xmlStrcmp(child->name,
+ (const xmlChar *) "minimal_heartbeat") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &cur_ds_def->par[DS_mrhb_cnt].
+ u_cnt);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "min") == 0)
+ status = get_double_from_node(doc, child,
+ &cur_ds_def->par[DS_min_val].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "max") == 0)
+ status = get_double_from_node(doc, child,
+ &cur_ds_def->par[DS_max_val].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "cdef") == 0)
+ status = parse_tag_ds_cdef(doc, child, rrd);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "last_ds") == 0)
+ status = get_string_from_node(doc, child,
+ cur_pdp_prep->last_ds,
+ sizeof(cur_pdp_prep->last_ds));
+ else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
+ status = get_double_from_node(doc, child,
+ &cur_pdp_prep->scratch[PDP_val].
+ u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "unknown_sec") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &cur_pdp_prep->
+ scratch[PDP_unkn_sec_cnt].u_cnt);
+ else {
+ rrd_set_error("parse_tag_ds: Unknown tag: %s", child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ return (status);
+} /* int parse_tag_ds */
+
+/*
+ * Parse root nodes
+ */
+static int parse_tag_rrd(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd)
+{
+ xmlNode *child;
+ int status;
+
+ status = 0;
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
+ if ((xmlStrcmp(child->name, (const xmlChar *) "comment") == 0)
+ || (xmlStrcmp(child->name, (const xmlChar *) "text") == 0))
+ /* ignore */ ;
+ else if (xmlStrcmp(child->name, (const xmlChar *) "version") == 0)
+ status = get_string_from_node(doc, child,
+ rrd->stat_head->version,
+ sizeof(rrd->stat_head->version));
+ else if (xmlStrcmp(child->name, (const xmlChar *) "step") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &rrd->stat_head->pdp_step);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "lastupdate") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &rrd->live_head->last_up);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "ds") == 0)
+ status = parse_tag_ds(doc, child, rrd);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "rra") == 0)
+ status = parse_tag_rra(doc, child, rrd);
+ else {
+ rrd_set_error("parse_tag_rrd: Unknown tag: %s", child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ return (status);
+} /* int parse_tag_rrd */
+
+static rrd_t *parse_file(
+ const char *filename)
+{
+ xmlDoc *doc;
+ xmlNode *cur;
+ int status;
+
+ rrd_t *rrd;
+
+ doc = xmlParseFile(filename);
+ if (doc == NULL) {
+ rrd_set_error("Document not parsed successfully.");
+ return (NULL);
+ }
+
+ cur = xmlDocGetRootElement(doc);
+ if (cur == NULL) {
+ rrd_set_error("Document is empty.");
+ xmlFreeDoc(doc);
+ return (NULL);