+static int cx_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
+ oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("curl_xml plugin: `%s' needs exactly one string argument.", name);
+ return (-1);
+ }
+
+ *dest = curl_slist_append(*dest, ci->values[0].value.string);
+ if (*dest == NULL)
+ return (-1);
+
+ return (0);
+} /* }}} int cx_config_append_string */
+
+static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
+{
+ if (!ds)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' not defined.", xpath->type);
+ return (-1);
+ }
+
+ if (ds->ds_num != xpath->values_len)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' requires %i values, but config talks about %i",
+ xpath->type, ds->ds_num, xpath->values_len);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} cx_check_type */
+
+static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ xmlChar *expr)
+{
+ xmlXPathObjectPtr xpath_obj;
+
+ /* XXX: When to free this? */
+ xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+ if (xpath_obj == NULL)
+ {
+ WARNING ("curl_xml plugin: "
+ "Error unable to evaluate xpath expression \"%s\". Skipping...", expr);
+ return NULL;
+ }
+
+ return xpath_obj;
+} /* }}} cx_evaluate_xpath */
+
+static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */
+{
+ if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE ||
+ node->type == XML_ELEMENT_NODE)
+ return (0);
+
+ WARNING ("curl_xml plugin: "
+ "Node \"%s\" doesn't seem to be a text node. Skipping...", node->name);
+ return -1;
+} /* }}} cx_if_not_text_node */
+
+static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl, int index)
+{
+ xmlXPathObjectPtr values_node_obj;
+ xmlNodeSetPtr values_node;
+ int tmp_size;
+ char *node_value;
+
+ values_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->values[index].path);
+ if (values_node_obj == NULL)
+ return (-1); /* Error already logged. */
+
+ values_node = values_node_obj->nodesetval;
+ tmp_size = (values_node) ? values_node->nodeNr : 0;
+
+ if (tmp_size == 0)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only one node. Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute*/
+ if (cx_if_not_text_node(values_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only text/attribute node which is not the case. Skipping...",
+ xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ node_value = (char *) xmlNodeGetContent(values_node->nodeTab[0]);
+ switch (ds->ds[index].type)
+ {
+ case DS_TYPE_COUNTER:
+ vl->values[index].counter = (counter_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_DERIVE:
+ vl->values[index].derive = (derive_t) strtoll (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_ABSOLUTE:
+ vl->values[index].absolute = (absolute_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_GAUGE:
+ vl->values[index].gauge = (gauge_t) strtod (node_value,
+ /* endptr = */ NULL);
+ }
+
+ /* free up object */
+ xmlXPathFreeObject (values_node_obj);
+
+ /* We have reached here which means that
+ * we have got something to work */
+ return (0);
+} /* }}} int cx_handle_single_value_xpath */
+
+static int cx_handle_all_value_xpaths (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl)
+{
+ value_t values[xpath->values_len];
+ int status;
+ int i;
+
+ assert (xpath->values_len > 0);
+ assert (xpath->values_len == vl->values_len);
+ assert (xpath->values_len == ds->ds_num);
+ vl->values = values;
+
+ for (i = 0; i < xpath->values_len; i++)
+ {
+ status = cx_handle_single_value_xpath (xpath_ctx, xpath, ds, vl, i);
+ if (status != 0)
+ return (-1); /* An error has been printed. */
+ } /* for (i = 0; i < xpath->values_len; i++) */
+
+ plugin_dispatch_values (vl);
+ vl->values = NULL;
+
+ return (0);
+} /* }}} int cx_handle_all_value_xpaths */
+
+static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath, value_list_t *vl,
+ _Bool is_table)
+{
+ xmlXPathObjectPtr instance_node_obj = NULL;
+ xmlNodeSetPtr instance_node = NULL;
+
+ memset (vl->type_instance, 0, sizeof (vl->type_instance));
+
+ /* If the base xpath returns more than one block, the result is assumed to be
+ * a table. The `Instance' option is not optional in this case. Check for the
+ * condition and inform the user. */
+ if (is_table && (vl->type_instance == NULL))
+ {
+ WARNING ("curl_xml plugin: "
+ "Base-XPath %s is a table (more than one result was returned), "
+ "but no instance-XPath has been defined.",
+ xpath->path);
+ return (-1);
+ }
+
+ /* instance has to be an xpath expression */
+ if (xpath->instance != NULL)
+ {
+ int tmp_size;
+
+ instance_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->instance);
+ if (instance_node_obj == NULL)
+ return (-1); /* error is logged already */
+
+ instance_node = instance_node_obj->nodesetval;
+ tmp_size = (instance_node) ? instance_node->nodeNr : 0;
+
+ if (tmp_size <= 0)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
+ "any of the nodes. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
+ "to return only one text node. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute */
+ if (cx_if_not_text_node(instance_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return only text node "
+ "which is not the case. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+ } /* if (xpath->instance != NULL) */
+
+ if (xpath->instance_prefix != NULL)
+ {
+ if (instance_node != NULL)
+ ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s%s",
+ xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0]));
+ else
+ sstrncpy (vl->type_instance, xpath->instance_prefix,
+ sizeof (vl->type_instance));
+ }
+ else
+ {
+ /* If instance_prefix and instance_node are NULL, then
+ * don't set the type_instance */
+ if (instance_node != NULL)
+ sstrncpy (vl->type_instance, (char *) xmlNodeGetContent(instance_node->nodeTab[0]),
+ sizeof (vl->type_instance));
+ }
+
+ /* Free `instance_node_obj' this late, because `instance_node' points to
+ * somewhere inside this structure. */
+ xmlXPathFreeObject (instance_node_obj);
+
+ return (0);
+} /* }}} int cx_handle_instance_xpath */
+
+static int cx_handle_base_xpath (char const *plugin_instance, /* {{{ */
+ char const *host,
+ xmlXPathContextPtr xpath_ctx, const data_set_t *ds,
+ char *base_xpath, cx_xpath_t *xpath)
+{
+ int total_nodes;
+ int i;
+
+ xmlXPathObjectPtr base_node_obj = NULL;
+ xmlNodeSetPtr base_nodes = NULL;
+
+ value_list_t vl = VALUE_LIST_INIT;
+
+ base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath);
+ if (base_node_obj == NULL)
+ return -1; /* error is logged already */
+
+ base_nodes = base_node_obj->nodesetval;
+ total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+
+ if (total_nodes == 0)
+ {
+ ERROR ("curl_xml plugin: "
+ "xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping the xpath block...", base_xpath);
+ xmlXPathFreeObject (base_node_obj);
+ return -1;
+ }
+
+ /* If base_xpath returned multiple results, then */
+ /* Instance in the xpath block is required */
+ if (total_nodes > 1 && xpath->instance == NULL)
+ {
+ ERROR ("curl_xml plugin: "
+ "InstanceFrom is must in xpath block since the base xpath expression \"%s\" "
+ "returned multiple results. Skipping the xpath block...", base_xpath);
+ return -1;
+ }
+
+ /* set the values for the value_list */
+ vl.values_len = ds->ds_num;
+ sstrncpy (vl.type, xpath->type, sizeof (vl.type));
+ sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
+ sstrncpy (vl.host, (host != NULL) ? host : hostname_g, sizeof (vl.host));
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+
+ for (i = 0; i < total_nodes; i++)
+ {
+ int status;
+
+ xpath_ctx->node = base_nodes->nodeTab[i];
+
+ status = cx_handle_instance_xpath (xpath_ctx, xpath, &vl,
+ /* is_table = */ (total_nodes > 1));
+ if (status != 0)
+ continue; /* An error has already been reported. */
+
+ status = cx_handle_all_value_xpaths (xpath_ctx, xpath, ds, &vl);
+ if (status != 0)
+ continue; /* An error has been logged. */
+ } /* for (i = 0; i < total_nodes; i++) */
+
+ /* free up the allocated memory */
+ xmlXPathFreeObject (base_node_obj);
+
+ return (0);
+} /* }}} cx_handle_base_xpath */
+
+static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, cx_t *db)
+{
+ llentry_t *le;
+ const data_set_t *ds;
+ cx_xpath_t *xpath;
+ int status=-1;
+
+
+ le = llist_head (db->list);
+ while (le != NULL)
+ {
+ /* get the ds */
+ xpath = (cx_xpath_t *) le->value;
+ ds = plugin_get_ds (xpath->type);
+
+ if ( (cx_check_type(ds, xpath) == 0) &&
+ (cx_handle_base_xpath(db->instance, db->host,
+ xpath_ctx, ds, le->key, xpath) == 0) )
+ status = 0; /* we got atleast one success */
+
+ le = le->next;
+ } /* while (le != NULL) */
+
+ return status;
+} /* }}} cx_handle_parsed_xml */
+
+static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
+{
+ int status;
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpath_ctx;
+ size_t i;
+
+ /* Load the XML */
+ doc = xmlParseDoc(xml);
+ if (doc == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to parse the xml document - %s", xml);
+ return (-1);
+ }
+
+ xpath_ctx = xmlXPathNewContext(doc);
+ if(xpath_ctx == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to create the xml context");
+ xmlFreeDoc(doc);
+ return (-1);
+ }
+
+ for (i = 0; i < db->namespaces_num; i++)
+ {
+ cx_namespace_t const *ns = db->namespaces + i;
+ status = xmlXPathRegisterNs (xpath_ctx,
+ BAD_CAST ns->prefix, BAD_CAST ns->url);
+ if (status != 0)
+ {
+ ERROR ("curl_xml plugin: "
+ "unable to register NS with prefix=\"%s\" and href=\"%s\"\n",
+ ns->prefix, ns->url);
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc (doc);
+ return (status);
+ }
+ }
+
+ status = cx_handle_parsed_xml (doc, xpath_ctx, db);
+ /* Cleanup */
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(doc);
+ return status;
+} /* }}} cx_parse_stats_xml */
+
+static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
+{
+ int status;
+ long rc;
+ char *ptr;
+ char *url;
+
+ db->buffer_fill = 0;
+ status = curl_easy_perform (curl);
+ if (status != CURLE_OK)
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
+ status, db->curl_errbuf, url);
+ return (-1);
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+
+ /* The response code is zero if a non-HTTP transport was used. */
+ if ((rc != 0) && (rc != 200))
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with response code %ld (%s)",
+ rc, url);
+ return (-1);
+ }
+
+ ptr = db->buffer;
+
+ status = cx_parse_stats_xml(BAD_CAST ptr, db);
+ db->buffer_fill = 0;
+
+ return status;
+} /* }}} int cx_curl_perform */
+
+static int cx_read (user_data_t *ud) /* {{{ */
+{
+ cx_t *db;
+
+ if ((ud == NULL) || (ud->data == NULL))
+ {
+ ERROR ("curl_xml plugin: cx_read: Invalid user data.");
+ return (-1);
+ }
+
+ db = (cx_t *) ud->data;
+
+ return cx_curl_perform (db, db->curl);
+} /* }}} int cx_read */
+