+ 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);
+ }
+
+ if (xmlStrcmp(cur->name, (const xmlChar *) "rrd") != 0) {
+ rrd_set_error
+ ("Document of the wrong type, root node is not \"rrd\".");
+ xmlFreeDoc(doc);
+ return (NULL);
+ }
+
+ rrd = (rrd_t *) malloc(sizeof(rrd_t));
+ if (rrd == NULL) {
+ rrd_set_error("parse_file: malloc failed.");
+ xmlFreeDoc(doc);
+ return (NULL);
+ }
+ memset(rrd, '\0', sizeof(rrd_t));
+
+ rrd->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
+ if (rrd->stat_head == NULL) {
+ rrd_set_error("parse_tag_rrd: malloc failed.");
+ xmlFreeDoc(doc);
+ free(rrd);
+ return (NULL);
+ }
+ memset(rrd->stat_head, '\0', sizeof(stat_head_t));
+
+ strncpy(rrd->stat_head->cookie, "RRD", sizeof(rrd->stat_head->cookie));
+ rrd->stat_head->float_cookie = FLOAT_COOKIE;
+
+ rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
+ if (rrd->live_head == NULL) {
+ rrd_set_error("parse_tag_rrd: malloc failed.");
+ xmlFreeDoc(doc);
+ free(rrd->stat_head);
+ free(rrd);
+ return (NULL);
+ }
+ memset(rrd->live_head, '\0', sizeof(live_head_t));
+
+ status = parse_tag_rrd(doc, cur, rrd);
+
+ xmlFreeDoc(doc);
+ if (status != 0) {
+ rrd_free(rrd);
+ rrd = NULL;
+ }
+
+ return (rrd);
+} /* rrd_t *parse_file */
+
+static int write_file(
+ const char *file_name,
+ rrd_t *rrd)
+{
+ FILE *fh;
+ unsigned int i;
+ unsigned int rra_offset;
+
+ if (strcmp("-", file_name) == 0)
+ fh = stdout;
+ else {
+ int fd_flags = O_WRONLY | O_CREAT;
+ int fd;
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ fd_flags |= O_BINARY;
+#endif
+
+ if (opt_force_overwrite == 0)
+ fd_flags |= O_EXCL;
+
+ fd = open(file_name, fd_flags, 0666);
+ if (fd == -1) {
+ rrd_set_error("creating '%s': %s", file_name,
+ rrd_strerror(errno));
+ return (-1);
+ }
+
+ fh = fdopen(fd, "wb");
+ if (fh == NULL) {
+ rrd_set_error("fdopen failed: %s", rrd_strerror(errno));
+ close(fd);
+ return (-1);
+ }
+ }
+ if (atoi(rrd->stat_head->version) < 3) {
+ /* we output 3 or higher */
+ strcpy(rrd->stat_head->version, "0003");
+ }
+ fwrite(rrd->stat_head, sizeof(stat_head_t), 1, fh);
+ fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, fh);
+ fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, fh);
+ fwrite(rrd->live_head, sizeof(live_head_t), 1, fh);
+ fwrite(rrd->pdp_prep, sizeof(pdp_prep_t), rrd->stat_head->ds_cnt, fh);
+ fwrite(rrd->cdp_prep, sizeof(cdp_prep_t),
+ rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt, fh);
+ fwrite(rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt, fh);