X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fcurl_xml.c;h=00a980cf3368d6cdb8c5fea6fde6a273cbffd58e;hb=592d8582e3370b7fb120ba9ed5957332936ab9f3;hp=e31e73d284f06d1a078d00211ef9f9a41b3698ef;hpb=5e9542b303a5f36b905473c4de6a4ab01f3ed632;p=collectd.git diff --git a/src/curl_xml.c b/src/curl_xml.c index e31e73d2..00a980cf 100644 --- a/src/curl_xml.c +++ b/src/curl_xml.c @@ -23,11 +23,13 @@ #include "common.h" #include "plugin.h" #include "configfile.h" +#include "utils_curl_stats.h" #include "utils_llist.h" #include #include #include +#include #include @@ -49,7 +51,7 @@ struct cx_xpath_s /* {{{ */ char *path; char *type; cx_values_t *values; - int values_len; + size_t values_len; char *instance_prefix; char *instance; int is_table; @@ -58,6 +60,14 @@ struct cx_xpath_s /* {{{ */ typedef struct cx_xpath_s cx_xpath_t; /* }}} */ +struct cx_namespace_s /* {{{ */ +{ + char *prefix; + char *url; +}; +typedef struct cx_namespace_s cx_namespace_t; +/* }}} */ + struct cx_s /* {{{ */ { char *instance; @@ -67,9 +77,17 @@ struct cx_s /* {{{ */ char *user; char *pass; char *credentials; + _Bool digest; _Bool verify_peer; _Bool verify_host; char *cacert; + char *post_body; + int timeout; + struct curl_slist *headers; + curl_stats_t *stats; + + cx_namespace_t *namespaces; + size_t namespaces_num; CURL *curl; char curl_errbuf[CURL_ERROR_SIZE]; @@ -98,14 +116,14 @@ static size_t cx_curl_callback (void *buf, /* {{{ */ return (0); } - if (len <= 0) + if (len == 0) return (len); if ((db->buffer_fill + len) >= db->buffer_size) { char *temp; - temp = (char *) realloc (db->buffer, + temp = realloc (db->buffer, db->buffer_fill + len + 1); if (temp == NULL) { @@ -154,12 +172,12 @@ static void cx_list_free (llist_t *list) /* {{{ */ } llist_destroy (list); - list = NULL; } /* }}} void cx_list_free */ static void cx_free (void *arg) /* {{{ */ { cx_t *db; + size_t i; DEBUG ("curl_xml plugin: cx_free (arg = %p);", arg); @@ -184,10 +202,46 @@ static void cx_free (void *arg) /* {{{ */ sfree (db->pass); sfree (db->credentials); sfree (db->cacert); + sfree (db->post_body); + curl_slist_free_all (db->headers); + curl_stats_destroy (db->stats); + + for (i = 0; i < db->namespaces_num; i++) + { + sfree (db->namespaces[i].prefix); + sfree (db->namespaces[i].url); + } + sfree (db->namespaces); sfree (db); } /* }}} void cx_free */ +static const char *cx_host (cx_t *db) /* {{{ */ +{ + if (db->host == NULL) + return hostname_g; + return db->host; +} /* }}} cx_host */ + +static int cx_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */ + oconfig_item_t *ci) +{ + struct curl_slist *temp = NULL; + 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); + } + + temp = curl_slist_append(*dest, ci->values[0].value.string); + if (temp == NULL) + return (-1); + + *dest = temp; + + return (0); +} /* }}} int cx_config_append_string */ + static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */ { if (!ds) @@ -198,7 +252,7 @@ static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */ if (ds->ds_num != xpath->values_len) { - WARNING ("curl_xml plugin: DataSet `%s' requires %i values, but config talks about %i", + WARNING ("curl_xml plugin: DataSet `%s' requires %zu values, but config talks about %zu", xpath->type, ds->ds_num, xpath->values_len); return (-1); } @@ -206,7 +260,7 @@ static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */ return (0); } /* }}} cx_check_type */ -static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ +static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ xmlChar *expr) { xmlXPathObjectPtr xpath_obj; @@ -225,7 +279,8 @@ static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */ { - if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE) + if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE || + node->type == XML_ELEMENT_NODE) return (0); WARNING ("curl_xml plugin: " @@ -272,7 +327,7 @@ static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ { WARNING ("curl_xml plugin: " "relative xpath expression \"%s\" is expected to return " - "only text/attribute node which is not the case. Skipping...", + "only text/attribute node which is not the case. Skipping...", xpath->values[index].path); xmlXPathFreeObject (values_node_obj); return (-1); @@ -293,13 +348,14 @@ static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ vl->values[index].absolute = (absolute_t) strtoull (node_value, /* endptr = */ NULL, /* base = */ 0); break; - case DS_TYPE_GAUGE: + case DS_TYPE_GAUGE: vl->values[index].gauge = (gauge_t) strtod (node_value, /* endptr = */ NULL); } /* free up object */ xmlXPathFreeObject (values_node_obj); + sfree (node_value); /* We have reached here which means that * we have got something to work */ @@ -312,7 +368,7 @@ static int cx_handle_all_value_xpaths (xmlXPathContextPtr xpath_ctx, /* {{{ */ { value_t values[xpath->values_len]; int status; - int i; + size_t i; assert (xpath->values_len > 0); assert (xpath->values_len == vl->values_len); @@ -342,9 +398,9 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ 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 `Instnce' option is not optional in this case. Check for the + * 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)) + if (is_table && (xpath->instance == NULL)) { WARNING ("curl_xml plugin: " "Base-XPath %s is a table (more than one result was returned), " @@ -397,8 +453,12 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ if (xpath->instance_prefix != NULL) { if (instance_node != NULL) + { + char *node_value = (char *) xmlNodeGetContent(instance_node->nodeTab[0]); ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s%s", - xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0])); + xpath->instance_prefix, node_value); + sfree (node_value); + } else sstrncpy (vl->type_instance, xpath->instance_prefix, sizeof (vl->type_instance)); @@ -408,8 +468,11 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ /* 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)); + { + char *node_value = (char *) xmlNodeGetContent(instance_node->nodeTab[0]); + sstrncpy (vl->type_instance, node_value, sizeof (vl->type_instance)); + sfree (node_value); + } } /* Free `instance_node_obj' this late, because `instance_node' points to @@ -421,7 +484,7 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ static int cx_handle_base_xpath (char const *plugin_instance, /* {{{ */ char const *host, - xmlXPathContextPtr xpath_ctx, const data_set_t *ds, + xmlXPathContextPtr xpath_ctx, const data_set_t *ds, char *base_xpath, cx_xpath_t *xpath) { int total_nodes; @@ -432,7 +495,7 @@ static int cx_handle_base_xpath (char const *plugin_instance, /* {{{ */ value_list_t vl = VALUE_LIST_INIT; - base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath); + base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath); if (base_node_obj == NULL) return -1; /* error is logged already */ @@ -449,7 +512,7 @@ static int cx_handle_base_xpath (char const *plugin_instance, /* {{{ */ } /* If base_xpath returned multiple results, then */ - /* Instance in the xpath block is required */ + /* Instance in the xpath block is required */ if (total_nodes > 1 && xpath->instance == NULL) { ERROR ("curl_xml plugin: " @@ -462,9 +525,9 @@ static int cx_handle_base_xpath (char const *plugin_instance, /* {{{ */ 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)); + sstrncpy (vl.host, host, sizeof (vl.host)); if (plugin_instance != NULL) - sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); + sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); for (i = 0; i < total_nodes; i++) { @@ -483,19 +546,19 @@ static int cx_handle_base_xpath (char const *plugin_instance, /* {{{ */ } /* for (i = 0; i < total_nodes; i++) */ /* free up the allocated memory */ - xmlXPathFreeObject (base_node_obj); + xmlXPathFreeObject (base_node_obj); - return (0); + return (0); } /* }}} cx_handle_base_xpath */ -static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */ +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) @@ -505,7 +568,7 @@ static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */ ds = plugin_get_ds (xpath->type); if ( (cx_check_type(ds, xpath) == 0) && - (cx_handle_base_xpath(db->instance, db->host, + (cx_handle_base_xpath(db->instance, cx_host (db), xpath_ctx, ds, le->key, xpath) == 0) ) status = 0; /* we got atleast one success */ @@ -520,6 +583,7 @@ 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); @@ -537,6 +601,22 @@ static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */ 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); @@ -552,7 +632,7 @@ static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */ char *url; url = db->url; - db->buffer_fill = 0; + db->buffer_fill = 0; status = curl_easy_perform (curl); if (status != CURLE_OK) { @@ -560,6 +640,8 @@ static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */ status, db->curl_errbuf, url); return (-1); } + if (db->stats != NULL) + curl_stats_dispatch (db->stats, db->curl, cx_host (db), "curl_xml", db->instance); curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc); @@ -618,10 +700,10 @@ static int cx_config_add_values (const char *name, cx_xpath_t *xpath, /* {{{ */ sfree (xpath->values); xpath->values_len = 0; - xpath->values = (cx_values_t *) malloc (sizeof (cx_values_t) * ci->values_num); + xpath->values = malloc (sizeof (cx_values_t) * ci->values_num); if (xpath->values == NULL) return (-1); - xpath->values_len = ci->values_num; + xpath->values_len = (size_t) ci->values_num; /* populate cx_values_t structure */ for (i = 0; i < ci->values_num; i++) @@ -630,36 +712,37 @@ static int cx_config_add_values (const char *name, cx_xpath_t *xpath, /* {{{ */ sstrncpy (xpath->values[i].path, ci->values[i].value.string, sizeof (xpath->values[i].path)); } - return (0); + return (0); } /* }}} cx_config_add_values */ -static int cx_config_add_xpath (cx_t *db, /* {{{ */ - oconfig_item_t *ci) +static int cx_config_add_xpath (cx_t *db, oconfig_item_t *ci) /* {{{ */ { cx_xpath_t *xpath; + char *name; + llentry_t *le; int status; int i; - xpath = (cx_xpath_t *) malloc (sizeof (*xpath)); + xpath = calloc (1, sizeof (*xpath)); if (xpath == NULL) { - ERROR ("curl_xml plugin: malloc failed."); + ERROR ("curl_xml plugin: calloc failed."); return (-1); } - memset (xpath, 0, sizeof (*xpath)); status = cf_util_get_string (ci, &xpath->path); if (status != 0) { - sfree (xpath); + cx_xpath_free (xpath); return (status); } /* error out if xpath->path is an empty string */ - if (*xpath->path == 0) + if (strlen (xpath->path) == 0) { ERROR ("curl_xml plugin: invalid xpath. " "xpath value can't be an empty string"); + cx_xpath_free (xpath); return (-1); } @@ -686,47 +769,91 @@ static int cx_config_add_xpath (cx_t *db, /* {{{ */ break; } /* for (i = 0; i < ci->children_num; i++) */ - if (status == 0 && xpath->type == NULL) + if (status != 0) { - WARNING ("curl_xml plugin: `Type' missing in `xpath' block."); - status = -1; + cx_xpath_free (xpath); + return status; } - if (status == 0) + if (xpath->type == NULL) { - char *name; - llentry_t *le; + WARNING ("curl_xml plugin: `Type' missing in `xpath' block."); + cx_xpath_free (xpath); + return -1; + } + if (db->list == NULL) + { + db->list = llist_create(); if (db->list == NULL) { - db->list = llist_create(); - if (db->list == NULL) - { - ERROR ("curl_xml plugin: list creation failed."); - return (-1); - } - } - - name = strdup(xpath->path); - if (name == NULL) - { - ERROR ("curl_xml plugin: strdup failed."); - return (-1); - } - - le = llentry_create (name, xpath); - if (le == NULL) - { - ERROR ("curl_xml plugin: llentry_create failed."); + ERROR ("curl_xml plugin: list creation failed."); + cx_xpath_free (xpath); return (-1); } + } + + name = strdup (xpath->path); + if (name == NULL) + { + ERROR ("curl_xml plugin: strdup failed."); + cx_xpath_free (xpath); + return (-1); + } - llist_append (db->list, le); + le = llentry_create (name, xpath); + if (le == NULL) + { + ERROR ("curl_xml plugin: llentry_create failed."); + cx_xpath_free (xpath); + sfree (name); + return (-1); } - return (status); + llist_append (db->list, le); + return (0); } /* }}} int cx_config_add_xpath */ +static int cx_config_add_namespace (cx_t *db, /* {{{ */ + oconfig_item_t *ci) +{ + cx_namespace_t *ns; + + if ((ci->values_num != 2) + || (ci->values[0].type != OCONFIG_TYPE_STRING) + || (ci->values[1].type != OCONFIG_TYPE_STRING)) + { + WARNING ("curl_xml plugin: The `Namespace' option " + "needs exactly two string arguments."); + return (EINVAL); + } + + ns = realloc (db->namespaces, sizeof (*db->namespaces) + * (db->namespaces_num + 1)); + if (ns == NULL) + { + ERROR ("curl_xml plugin: realloc failed."); + return (ENOMEM); + } + db->namespaces = ns; + ns = db->namespaces + db->namespaces_num; + memset (ns, 0, sizeof (*ns)); + + ns->prefix = strdup (ci->values[0].value.string); + ns->url = strdup (ci->values[1].value.string); + + if ((ns->prefix == NULL) || (ns->url == NULL)) + { + sfree (ns->prefix); + sfree (ns->url); + ERROR ("curl_xml plugin: strdup failed."); + return (ENOMEM); + } + + db->namespaces_num++; + return (0); +} /* }}} int cx_config_add_namespace */ + /* Initialize db->curl */ static int cx_init_curl (cx_t *db) /* {{{ */ { @@ -740,20 +867,26 @@ static int cx_init_curl (cx_t *db) /* {{{ */ curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback); curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db); - curl_easy_setopt (db->curl, CURLOPT_USERAGENT, - PACKAGE_NAME"/"PACKAGE_VERSION); + curl_easy_setopt (db->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT); curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf); curl_easy_setopt (db->curl, CURLOPT_URL, db->url); + curl_easy_setopt (db->curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt (db->curl, CURLOPT_MAXREDIRS, 50L); if (db->user != NULL) { +#ifdef HAVE_CURLOPT_USERNAME + curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user); + curl_easy_setopt (db->curl, CURLOPT_PASSWORD, + (db->pass == NULL) ? "" : db->pass); +#else size_t credentials_size; credentials_size = strlen (db->user) + 2; if (db->pass != NULL) credentials_size += strlen (db->pass); - db->credentials = (char *) malloc (credentials_size); + db->credentials = malloc (credentials_size); if (db->credentials == NULL) { ERROR ("curl_xml plugin: malloc failed."); @@ -763,6 +896,10 @@ static int cx_init_curl (cx_t *db) /* {{{ */ ssnprintf (db->credentials, credentials_size, "%s:%s", db->user, (db->pass == NULL) ? "" : db->pass); curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials); +#endif + + if (db->digest) + curl_easy_setopt (db->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); } curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L); @@ -770,6 +907,17 @@ static int cx_init_curl (cx_t *db) /* {{{ */ db->verify_host ? 2L : 0L); if (db->cacert != NULL) curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert); + if (db->headers != NULL) + curl_easy_setopt (db->curl, CURLOPT_HTTPHEADER, db->headers); + if (db->post_body != NULL) + curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body); + +#ifdef HAVE_CURLOPT_TIMEOUT_MS + if (db->timeout >= 0) + curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS, (long) db->timeout); + else + curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS, (long) CDTIME_T_TO_MS(plugin_get_interval())); +#endif return (0); } /* }}} int cx_init_curl */ @@ -788,13 +936,14 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ return (-1); } - db = (cx_t *) malloc (sizeof (*db)); + db = calloc (1, sizeof (*db)); if (db == NULL) { - ERROR ("curl_xml plugin: malloc failed."); + ERROR ("curl_xml plugin: calloc failed."); return (-1); } - memset (db, 0, sizeof (*db)); + + db->timeout = -1; if (strcasecmp ("URL", ci->key) == 0) { @@ -809,6 +958,7 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ { ERROR ("curl_xml plugin: cx_config: " "Invalid key: %s", ci->key); + cx_free (db); return (-1); } @@ -825,6 +975,8 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ status = cf_util_get_string (child, &db->user); else if (strcasecmp ("Password", child->key) == 0) status = cf_util_get_string (child, &db->pass); + else if (strcasecmp ("Digest", child->key) == 0) + status = cf_util_get_boolean (child, &db->digest); else if (strcasecmp ("VerifyPeer", child->key) == 0) status = cf_util_get_boolean (child, &db->verify_peer); else if (strcasecmp ("VerifyHost", child->key) == 0) @@ -833,6 +985,20 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ status = cf_util_get_string (child, &db->cacert); else if (strcasecmp ("xpath", child->key) == 0) status = cx_config_add_xpath (db, child); + else if (strcasecmp ("Header", child->key) == 0) + status = cx_config_append_string ("Header", &db->headers, child); + else if (strcasecmp ("Post", child->key) == 0) + status = cf_util_get_string (child, &db->post_body); + else if (strcasecmp ("Namespace", child->key) == 0) + status = cx_config_add_namespace (db, child); + else if (strcasecmp ("Timeout", child->key) == 0) + status = cf_util_get_int (child, &db->timeout); + else if (strcasecmp ("Statistics", child->key) == 0) + { + db->stats = curl_stats_from_config (child); + if (db->stats == NULL) + status = -1; + } else { WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key); @@ -859,7 +1025,7 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ if (status == 0) { user_data_t ud; - char cb_name[DATA_MAX_NAME_LEN]; + char *cb_name; if (db->instance == NULL) db->instance = strdup("default"); @@ -871,11 +1037,10 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ ud.data = (void *) db; ud.free_func = cx_free; - ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s", - db->instance, db->url); - - plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read, - /* interval = */ NULL, &ud); + cb_name = ssnprintf_alloc ("curl_xml-%s-%s", db->instance, db->url); + plugin_register_complex_read (/* group = */ "curl_xml", cb_name, cx_read, + /* interval = */ 0, &ud); + sfree (cb_name); } else {