Merge pull request #2512 from Stackdriver/pri
[collectd.git] / src / curl_xml.c
index e1f5ed3..19ae5f4 100644 (file)
@@ -157,13 +157,14 @@ static void cx_xpath_list_free(llist_t *list) /* {{{ */
   while (le != NULL) {
     llentry_t *le_next = le->next;
 
+    /* this also frees xpath->path used for le->key */
     cx_xpath_free(le->value);
 
     le = le_next;
   }
 
   llist_destroy(list);
-} /* }}} void cx_list_free */
+} /* }}} void cx_xpath_list_free */
 
 static void cx_free(void *arg) /* {{{ */
 {
@@ -239,8 +240,8 @@ 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 %zu values, but config "
-            "talks about %zu",
+    WARNING("curl_xml plugin: DataSet `%s' requires %" PRIsz
+            " values, but config talks about %" PRIsz,
             xpath->type, ds->ds_num, xpath->values_len);
     return -1;
   }
@@ -275,50 +276,65 @@ static int cx_if_not_text_node(xmlNodePtr node) /* {{{ */
   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, xpath->values[index].path);
+/*
+ * Returned value should be freed with xmlFree().
+ */
+static char *cx_get_text_node_value(xmlXPathContextPtr xpath_ctx, /* {{{ */
+                                    char *expr, const char *from_option) {
+  xmlXPathObjectPtr values_node_obj = cx_evaluate_xpath(xpath_ctx, expr);
   if (values_node_obj == NULL)
-    return -1; /* Error already logged. */
+    return NULL; /* Error already logged. */
 
-  values_node = values_node_obj->nodesetval;
-  tmp_size = (values_node) ? values_node->nodeNr : 0;
+  xmlNodeSetPtr values_node = values_node_obj->nodesetval;
+  size_t 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);
+            "relative xpath expression \"%s\" from '%s' doesn't match "
+            "any of the nodes.",
+            expr, from_option);
     xmlXPathFreeObject(values_node_obj);
-    return -1;
+    return NULL;
   }
 
   if (tmp_size > 1) {
     WARNING("curl_xml plugin: "
-            "relative xpath expression \"%s\" is expected to return "
-            "only one node. Skipping...",
-            xpath->values[index].path);
+            "relative xpath expression \"%s\" from '%s' is expected to return "
+            "only one text node. Skipping the node.",
+            expr, from_option);
     xmlXPathFreeObject(values_node_obj);
-    return -1;
+    return NULL;
   }
 
   /* 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);
+            "relative xpath expression \"%s\" from '%s' is expected to return "
+            "only text/attribute node which is not the case. "
+            "Skipping the node.",
+            expr, from_option);
     xmlXPathFreeObject(values_node_obj);
-    return -1;
+    return NULL;
   }
 
-  node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+  char *node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+
+  /* free up object */
+  xmlXPathFreeObject(values_node_obj);
+
+  return node_value;
+} /* }}} char * cx_get_text_node_value */
+
+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) {
+
+  char *node_value = cx_get_text_node_value(
+      xpath_ctx, xpath->values[index].path, "ValuesFrom");
+
+  if (node_value == NULL)
+    return -1;
+
   switch (ds->ds[index].type) {
   case DS_TYPE_COUNTER:
     vl->values[index].counter =
@@ -340,9 +356,7 @@ static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
                                               /* endptr = */ NULL);
   }
 
-  /* free up object */
-  xmlXPathFreeObject(values_node_obj);
-  sfree(node_value);
+  xmlFree(node_value);
 
   /* We have reached here which means that
    * we have got something to work */
@@ -373,128 +387,35 @@ static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */
 static int cx_handle_instance_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
                                     cx_xpath_t *xpath, value_list_t *vl) {
 
-  xmlXPathObjectPtr instance_node_obj = NULL;
-  xmlNodeSetPtr instance_node = NULL;
-
-  memset(vl->type_instance, 0, sizeof(vl->type_instance));
-
-  /* instance has to be an xpath expression */
+  /* Handle type instance */
   if (xpath->instance != NULL) {
-    int tmp_size;
-
-    instance_node_obj = cx_evaluate_xpath(xpath_ctx, 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);
+    char *node_value =
+        cx_get_text_node_value(xpath_ctx, xpath->instance, "InstanceFrom");
+    if (node_value == NULL)
       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) {
-      char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+    if (xpath->instance_prefix != NULL)
       snprintf(vl->type_instance, sizeof(vl->type_instance), "%s%s",
                xpath->instance_prefix, node_value);
-      sfree(node_value);
-    } 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) {
-      char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+    else
       sstrncpy(vl->type_instance, node_value, sizeof(vl->type_instance));
-      sfree(node_value);
-    }
-  }
 
-  /* Free `instance_node_obj' this late, because `instance_node' points to
-   * somewhere inside this structure. */
-  xmlXPathFreeObject(instance_node_obj);
+    xmlFree(node_value);
+  } else if (xpath->instance_prefix != NULL)
+    sstrncpy(vl->type_instance, xpath->instance_prefix,
+             sizeof(vl->type_instance));
 
-  /* Part 2, handle PluginInstanceFrom */
-  instance_node_obj = NULL;
-  instance_node = NULL;
-
-  /* plugin_instance_from has to be an xpath expression */
+  /* Handle plugin instance */
   if (xpath->plugin_instance_from != NULL) {
-    instance_node_obj =
-        cx_evaluate_xpath(xpath_ctx, xpath->plugin_instance_from);
-    if (instance_node_obj == NULL)
-      return -1; /* error is already logged */
-
-    instance_node = instance_node_obj->nodesetval;
-    int tmp_size = (instance_node) ? instance_node->nodeNr : 0;
-
-    if (tmp_size <= 0) {
-      WARNING("curl_xml plugin: "
-              "relative xpath expression for 'PluginInstanceFrom' \"%s\" "
-              "doesn't match any of the nodes. Skipping the node.",
-              xpath->plugin_instance_from);
-      xmlXPathFreeObject(instance_node_obj);
-      return -1;
-    }
+    char *node_value = cx_get_text_node_value(
+        xpath_ctx, xpath->plugin_instance_from, "PluginInstanceFrom");
 
-    if (tmp_size > 1) {
-      WARNING("curl_xml plugin: "
-              "relative xpath expression for 'PluginInstanceFrom' \"%s\" "
-              "is expected to return only one text node. Skipping the node.",
-              xpath->plugin_instance_from);
-      xmlXPathFreeObject(instance_node_obj);
+    if (node_value == NULL)
       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->plugin_instance_from);
-      xmlXPathFreeObject(instance_node_obj);
-      return -1;
-    }
-
-    if (instance_node != NULL) {
-      char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
-      sstrncpy(vl->plugin_instance, node_value, sizeof(vl->plugin_instance));
-      sfree(node_value);
-    }
-
-    /* Free `instance_node_obj' this late, because `instance_node' points to
-     * somewhere inside this structure. */
-    xmlXPathFreeObject(instance_node_obj);
-  } /* if (xpath->plugin_instance_from != NULL) */
+    sstrncpy(vl->plugin_instance, node_value, sizeof(vl->plugin_instance));
+    xmlFree(node_value);
+  }
 
   return 0;
 } /* }}} int cx_handle_instance_xpath */
@@ -531,6 +452,7 @@ static int cx_handle_xpath(const cx_t *db, /* {{{ */
           "since the base xpath expression \"%s\" "
           "returned multiple results. Skipping the xpath block...",
           xpath->path);
+    xmlXPathFreeObject(base_node_obj);
     return -1;
   }
 
@@ -747,13 +669,10 @@ static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */
     return -1;
   }
 
-  if (db->xpath_list == NULL) {
-    db->xpath_list = llist_create();
-    if (db->xpath_list == NULL) {
-      ERROR("curl_xml plugin: list creation failed.");
-      cx_xpath_free(xpath);
-      return -1;
-    }
+  if (xpath->values_len == 0) {
+    WARNING("curl_xml plugin: `ValuesFrom' missing in `xpath' block.");
+    cx_xpath_free(xpath);
+    return -1;
   }
 
   llentry_t *le = llentry_create(xpath->path, xpath);
@@ -867,8 +786,6 @@ static int cx_init_curl(cx_t *db) /* {{{ */
 
 static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
 {
-  int status = 0;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("curl_xml plugin: The `URL' block "
             "needs exactly one string argument.");
@@ -881,22 +798,31 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
     return -1;
   }
 
-  db->timeout = -1;
+  db->instance = strdup("default");
+  if (db->instance == NULL) {
+    ERROR("curl_xml plugin: strdup failed.");
+    sfree(db);
+    return -1;
+  }
 
-  if (strcasecmp("URL", ci->key) == 0) {
-    status = cf_util_get_string(ci, &db->url);
-    if (status != 0) {
-      sfree(db);
-      return status;
-    }
-  } else {
-    ERROR("curl_xml plugin: cx_config: "
-          "Invalid key: %s",
-          ci->key);
-    cx_free(db);
+  db->xpath_list = llist_create();
+  if (db->xpath_list == NULL) {
+    ERROR("curl_xml plugin: list creation failed.");
+    sfree(db->instance);
+    sfree(db);
     return -1;
   }
 
+  db->timeout = -1;
+
+  int status = cf_util_get_string(ci, &db->url);
+  if (status != 0) {
+    llist_destroy(db->xpath_list);
+    sfree(db->instance);
+    sfree(db);
+    return status;
+  }
+
   /* Fill the `cx_t' structure.. */
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
@@ -942,37 +868,34 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
       break;
   }
 
-  if (status == 0) {
-    if (db->xpath_list == NULL) {
-      WARNING("curl_xml plugin: No (valid) `xpath' block "
-              "within `URL' block `%s'.",
-              db->url);
-      status = -1;
-    }
-    if (status == 0)
-      status = cx_init_curl(db);
+  if (status != 0) {
+    cx_free(db);
+    return status;
   }
 
-  /* If all went well, register this database for reading */
-  if (status == 0) {
-    if (db->instance == NULL)
-      db->instance = strdup("default");
-
-    DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
-
-    char *cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
+  if (llist_size(db->xpath_list) == 0) {
+    WARNING("curl_xml plugin: No `xpath' block within `URL' block `%s'.",
+            db->url);
+    cx_free(db);
+    return -1;
+  }
 
-    plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
-                                 /* interval = */ 0,
-                                 &(user_data_t){
-                                     .data = db, .free_func = cx_free,
-                                 });
-    sfree(cb_name);
-  } else {
+  if (cx_init_curl(db) != 0) {
     cx_free(db);
     return -1;
   }
 
+  /* If all went well, register this database for reading */
+  DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
+
+  char *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,
+                               &(user_data_t){
+                                   .data = db, .free_func = cx_free,
+                               });
+  sfree(cb_name);
   return 0;
 } /* }}} int cx_config_add_url */