+#include "rrd_rpncalc.h"
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof ((a)[0]))
+static int opt_range_check = 0;
+static int opt_force_overwrite = 0;
+
+/*
+ * Auxiliary functions
+ */
+static int get_string_from_node(
+ xmlDoc * doc,
+ xmlNode * node,
+ char *buffer,
+ size_t buffer_size)
+{
+ xmlChar *temp0;
+ char *begin_ptr;
+ char *end_ptr;
+
+ temp0 = xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+ if (temp0 == NULL) {
+ rrd_set_error("get_string_from_node: xmlNodeListGetString failed.");
+ return (-1);
+ }
+
+ begin_ptr = (char *) temp0;
+ while ((begin_ptr[0] != 0) && (isspace(begin_ptr[0])))
+ begin_ptr++;
+
+ if (begin_ptr[0] == 0) {
+ xmlFree(temp0);
+ buffer[0] = 0;
+ return (0);
+ }
+
+ end_ptr = begin_ptr;
+ while ((end_ptr[0] != 0) && (!isspace(end_ptr[0])))
+ end_ptr++;
+ end_ptr[0] = 0;
+
+ strncpy(buffer, begin_ptr, buffer_size);
+ buffer[buffer_size - 1] = 0;
+
+ xmlFree(temp0);
+
+ return (0);
+} /* int get_string_from_node */
+
+static int get_int_from_node(
+ xmlDoc * doc,
+ xmlNode * node,
+ int *value)
+{
+ int temp;
+ char *str_ptr;
+ char *end_ptr;
+
+ str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+ if (str_ptr == NULL) {
+ rrd_set_error("get_int_from_node: xmlNodeListGetString failed.");
+ return (-1);
+ }
+
+ end_ptr = NULL;
+ temp = strtol(str_ptr, &end_ptr, 0);
+ xmlFree(str_ptr);
+
+ if (str_ptr == end_ptr) {
+ rrd_set_error("get_int_from_node: Cannot parse buffer as int: %s",
+ str_ptr);
+ return (-1);
+ }
+
+ *value = temp;
+
+ return (0);
+} /* int get_int_from_node */
+
+static int get_double_from_node(
+ xmlDoc * doc,
+ xmlNode * node,
+ double *value)
+{
+ double temp;
+ char *str_ptr;
+ char *end_ptr;
+
+ str_ptr = (char *) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+ if (str_ptr == NULL) {
+ rrd_set_error("get_double_from_node: xmlNodeListGetString failed.");
+ return (-1);
+ }
+
+ end_ptr = NULL;
+ temp = strtod(str_ptr, &end_ptr);
+ xmlFree(str_ptr);
+
+ if (str_ptr == end_ptr) {
+ rrd_set_error
+ ("get_double_from_node: Cannot parse buffer as double: %s",
+ str_ptr);
+ return (-1);
+ }
+
+ *value = temp;
+
+ return (0);
+} /* int get_double_from_node */
+
+static int value_check_range(
+ rrd_value_t *rrd_value,
+ const ds_def_t *ds_def)
+{
+ double min;
+ double max;
+
+ if (opt_range_check == 0)
+ return (0);
+
+ min = ds_def->par[DS_min_val].u_val;
+ max = ds_def->par[DS_max_val].u_val;
+
+ if (((!isnan(min)) && (*rrd_value < min))
+ || ((!isnan(max)) && (*rrd_value > max)))
+ *rrd_value = NAN;
+
+ return (0);
+} /* int value_check_range */
+
+/*
+ * Parse the <database> block within an RRA definition
+ */
+static int parse_tag_rra_database_row(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd,
+ rrd_value_t *rrd_value)
+{
+ unsigned int values_count = 0;
+ 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 *) "v") == 0) {
+ if (values_count < rrd->stat_head->ds_cnt) {
+ status =
+ get_double_from_node(doc, child,
+ rrd_value + values_count);
+ if (status == 0)
+ value_check_range(rrd_value + values_count,
+ rrd->ds_def + values_count);
+ }
+
+ values_count++;
+ } else {
+ rrd_set_error("parse_tag_rra_database_row: Unknown tag: %s",
+ child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (child = node->xmlChildrenNode) */
+
+ if (values_count != rrd->stat_head->ds_cnt) {
+ rrd_set_error("parse_tag_rra_database_row: Row has %u values "
+ "and RRD has %lu data sources.",
+ values_count, rrd->stat_head->ds_cnt);
+ status = -1;
+ }
+
+ return (status);
+} /* int parse_tag_rra_database_row */
+
+static int parse_tag_rra_database(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd)
+{
+ rra_def_t *cur_rra_def;
+ unsigned int total_row_cnt;
+ xmlNode *child;
+ int status;
+ int i;
+
+ total_row_cnt = 0;
+ for (i = 0; i < (((int) rrd->stat_head->rra_cnt) - 1); i++)
+ total_row_cnt += rrd->rra_def[i].row_cnt;
+
+ cur_rra_def = rrd->rra_def + i;
+
+ 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 *) "row") == 0) {
+ rrd_value_t *temp;
+ rrd_value_t *cur_rrd_value;
+ unsigned int total_values_count = rrd->stat_head->ds_cnt
+ * (total_row_cnt + 1);
+
+ /* Allocate space for the new values.. */
+ temp = (rrd_value_t *) realloc(rrd->rrd_value,
+ sizeof(rrd_value_t) *
+ total_values_count);
+ if (temp == NULL) {
+ rrd_set_error("parse_tag_rra_database: realloc failed.");
+ status = -1;
+ break;
+ }
+ rrd->rrd_value = temp;
+ cur_rrd_value = rrd->rrd_value
+ + (rrd->stat_head->ds_cnt * total_row_cnt);
+ memset(cur_rrd_value, '\0',
+ sizeof(rrd_value_t) * rrd->stat_head->ds_cnt);
+ total_row_cnt++;
+ cur_rra_def->row_cnt++;
+
+ status =
+ parse_tag_rra_database_row(doc, child, rrd, cur_rrd_value);
+ } /* if (xmlStrcmp (child->name, (const xmlChar *) "row") == 0) */
+ else {
+ rrd_set_error("parse_tag_rra_database: Unknown tag: %s",
+ child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (child = node->xmlChildrenNode) */
+
+ return (status);
+} /* int parse_tag_rra_database */
+
+/*
+ * Parse the <cdp_prep> block within an RRA definition
+ */
+static int parse_tag_rra_cdp_prep_ds_history(
+ xmlDoc * doc,
+ xmlNode * node,
+ cdp_prep_t *cdp_prep)
+{
+ /* Make `history_buffer' the same size as the scratch area, plus the
+ * terminating NULL byte. */
+ char history_buffer[sizeof(((cdp_prep_t *)0)->scratch) + 1];
+ char *history_ptr;
+ int status;
+ int i;
+
+ status = get_string_from_node(doc, node,
+ history_buffer, sizeof(history_buffer));
+ if (status != 0)
+ return (-1);
+
+ history_ptr = (char *) (&cdp_prep->scratch[0]);
+ for (i = 0; history_buffer[i] != '\0'; i++)
+ history_ptr[i] = (history_buffer[i] == '1') ? 1 : 0;
+
+ return (0);
+} /* int parse_tag_rra_cdp_prep_ds_history */
+
+static int parse_tag_rra_cdp_prep_ds(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd,
+ cdp_prep_t *cdp_prep)
+{
+ xmlNode *child;
+ int status;
+
+ memset(cdp_prep, '\0', sizeof(cdp_prep_t));
+
+ 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 *) "primary_value") ==
+ 0)
+ status =
+ get_double_from_node(doc, child,
+ &cdp_prep->scratch[CDP_primary_val].
+ u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "secondary_value")
+ == 0)
+ status =
+ get_double_from_node(doc, child,
+ &cdp_prep->scratch[CDP_secondary_val].
+ u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "intercept") == 0)
+ status = get_double_from_node(doc, child,
+ &cdp_prep->
+ scratch[CDP_hw_intercept].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "last_intercept") ==
+ 0)
+ status =
+ get_double_from_node(doc, child,
+ &cdp_prep->
+ scratch[CDP_hw_last_intercept].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "slope") == 0)
+ status = get_double_from_node(doc, child,
+ &cdp_prep->scratch[CDP_hw_slope].
+ u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "last_slope") == 0)
+ status = get_double_from_node(doc, child,
+ &cdp_prep->
+ scratch[CDP_hw_last_slope].u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "nan_count") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &cdp_prep->
+ scratch[CDP_null_count].u_cnt);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "last_nan_count") ==
+ 0)
+ status =
+ get_int_from_node(doc, child,
+ (int *) &cdp_prep->
+ scratch[CDP_last_null_count].u_cnt);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "seasonal") == 0)
+ status = get_double_from_node(doc, child,
+ &cdp_prep->scratch[CDP_hw_seasonal].
+ u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "last_seasonal") ==
+ 0)
+ status =
+ get_double_from_node(doc, child,
+ &cdp_prep->scratch[CDP_hw_last_seasonal].
+ u_val);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "init_flag") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &cdp_prep->
+ scratch[CDP_init_seasonal].u_cnt);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "history") == 0)
+ status = parse_tag_rra_cdp_prep_ds_history(doc, child, cdp_prep);
+ else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0)
+ status = get_double_from_node(doc, child,
+ &cdp_prep->scratch[CDP_val].u_val);
+ else if (xmlStrcmp(child->name,
+ (const xmlChar *) "unknown_datapoints") == 0)
+ status = get_int_from_node(doc, child,
+ (int *) &cdp_prep->
+ scratch[CDP_unkn_pdp_cnt].u_cnt);
+ /*
+ * Compatibility code for 1.0.49
+ */
+ else if (xmlStrcmp(child->name, (const xmlChar *) "value") == 0) { /* {{{ */
+ unsigned int i = 0;
+ rra_def_t *rra_def = rrd->rra_def + (rrd->stat_head->rra_cnt - 1);
+
+ while (42) {
+ if (i >= ARRAY_LENGTH(cdp_prep->scratch)) {
+ status = -1;
+ break;
+ }
+
+ if ((cf_conv(rra_def->cf_nam) == CF_FAILURES)
+ || (i == CDP_unkn_pdp_cnt)
+ || (i == CDP_null_count)
+ || (i == CDP_last_null_count))
+ status = get_int_from_node(doc, child,
+ (int *) &cdp_prep->scratch[i].
+ u_cnt);
+ else
+ status = get_double_from_node(doc, child,
+ &cdp_prep->scratch[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_cdp_prep: Unknown tag: %s",
+ child->name);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ return (status);
+} /* int parse_tag_rra_cdp_prep_ds */
+
+static int parse_tag_rra_cdp_prep(
+ xmlDoc * doc,
+ xmlNode * node,
+ rrd_t *rrd,
+ cdp_prep_t *cdp_prep)
+{
+ xmlNode *child;
+ int status;
+
+ 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);