Merge branch 'collectd-5.7'
authorRuben Kerkhof <ruben@rubenkerkhof.com>
Fri, 7 Jul 2017 16:43:39 +0000 (18:43 +0200)
committerRuben Kerkhof <ruben@rubenkerkhof.com>
Fri, 7 Jul 2017 16:43:57 +0000 (18:43 +0200)
Fixes #2328
Conflicts:
src/curl_json.c
src/curl_xml.c

1  2 
src/apache.c
src/ascent.c
src/bind.c
src/curl.c
src/curl_json.c
src/curl_xml.c
src/nginx.c
src/write_http.c

diff --combined src/apache.c
@@@ -89,11 -89,11 +89,11 @@@ static size_t apache_curl_callback(voi
    if (st == NULL) {
      ERROR("apache plugin: apache_curl_callback: "
            "user_data pointer is NULL.");
 -    return (0);
 +    return 0;
    }
  
    if (len == 0)
 -    return (len);
 +    return len;
  
    if ((st->apache_buffer_fill + len) >= st->apache_buffer_size) {
      char *temp;
      temp = realloc(st->apache_buffer, st->apache_buffer_fill + len + 1);
      if (temp == NULL) {
        ERROR("apache plugin: realloc failed.");
 -      return (0);
 +      return 0;
      }
      st->apache_buffer = temp;
      st->apache_buffer_size = st->apache_buffer_fill + len + 1;
    st->apache_buffer_fill += len;
    st->apache_buffer[st->apache_buffer_fill] = 0;
  
 -  return (len);
 +  return len;
  } /* int apache_curl_callback */
  
  static size_t apache_header_callback(void *buf, size_t size, size_t nmemb,
    if (st == NULL) {
      ERROR("apache plugin: apache_header_callback: "
            "user_data pointer is NULL.");
 -    return (0);
 +    return 0;
    }
  
    if (len == 0)
 -    return (len);
 +    return len;
  
    /* look for the Server header */
    if (strncasecmp(buf, "Server: ", strlen("Server: ")) != 0)
 -    return (len);
 +    return len;
  
    if (strstr(buf, "Apache") != NULL)
      st->server_type = APACHE;
      NOTICE("apache plugin: Unknown server software: %s", hdr);
    }
  
 -  return (len);
 +  return len;
  } /* apache_header_callback */
  
  /* Configuration handling functiions
@@@ -164,7 -164,7 +164,7 @@@ static int config_add(oconfig_item_t *c
    st = calloc(1, sizeof(*st));
    if (st == NULL) {
      ERROR("apache plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
  
    st->timeout = -1;
    status = cf_util_get_string(ci, &st->name);
    if (status != 0) {
      sfree(st);
 -    return (status);
 +    return status;
    }
    assert(st->name != NULL);
  
  
    if (status != 0) {
      apache_free(st);
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  } /* int config_add */
  
  static int config(oconfig_item_t *ci) {
                child->key);
    } /* for (ci->children) */
  
 -  return (status);
 +  return status;
  } /* int config */
  
  /* initialize curl for each host */
@@@ -272,7 -272,7 +272,7 @@@ static int init_host(apache_t *st) /* {
  
    if ((st->curl = curl_easy_init()) == NULL) {
      ERROR("apache plugin: init_host: `curl_easy_init' failed.");
 -    return (-1);
 +    return -1;
    }
  
    curl_easy_setopt(st->curl, CURLOPT_NOSIGNAL, 1L);
              "truncated.");
        curl_easy_cleanup(st->curl);
        st->curl = NULL;
 -      return (-1);
 +      return -1;
      }
  
      curl_easy_setopt(st->curl, CURLOPT_USERPWD, credentials);
  #endif
    }
  
-   curl_easy_setopt(st->curl, CURLOPT_URL, st->url);
    curl_easy_setopt(st->curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(st->curl, CURLOPT_MAXREDIRS, 50L);
  
                       (long)CDTIME_T_TO_MS(plugin_get_interval()));
  #endif
  
 -  return (0);
 +  return 0;
  } /* }}} int init_host */
  
  static void submit_value(const char *type, const char *type_instance,
@@@ -505,14 -504,17 +504,17 @@@ static int apache_read_host(user_data_
    if (st->curl == NULL) {
      status = init_host(st);
      if (status != 0)
 -      return (-1);
 +      return -1;
    }
    assert(st->curl != NULL);
  
    st->apache_buffer_fill = 0;
+   curl_easy_setopt(st->curl, CURLOPT_URL, st->url);
    if (curl_easy_perform(st->curl) != CURLE_OK) {
      ERROR("apache: curl_easy_perform failed: %s", st->apache_curl_error);
 -    return (-1);
 +    return -1;
    }
  
    /* fallback - server_type to apache if not set at this time */
  
    st->apache_buffer_fill = 0;
  
 -  return (0);
 +  return 0;
  } /* }}} int apache_read_host */
  
  static int apache_init(void) /* {{{ */
    /* Call this while collectd is still single-threaded to avoid
     * initialization issues in libgcrypt. */
    curl_global_init(CURL_GLOBAL_SSL);
 -  return (0);
 +  return 0;
  } /* }}} int apache_init */
  
  void module_register(void) {
    plugin_register_complex_config("apache", config);
    plugin_register_init("apache", apache_init);
  } /* void module_register */
 -
 -/* vim: set sw=8 noet fdm=marker : */
diff --combined src/ascent.c
@@@ -125,7 -125,7 +125,7 @@@ static int ascent_submit_gauge(const ch
      sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
  
    plugin_dispatch_values(&vl);
 -  return (0);
 +  return 0;
  } /* }}} int ascent_submit_gauge */
  
  static size_t ascent_curl_callback(void *buf, size_t size,
    size_t len = size * nmemb;
  
    if (len == 0)
 -    return (len);
 +    return len;
  
    if ((ascent_buffer_fill + len) >= ascent_buffer_size) {
      char *temp;
      temp = realloc(ascent_buffer, ascent_buffer_fill + len + 1);
      if (temp == NULL) {
        ERROR("ascent plugin: realloc failed.");
 -      return (0);
 +      return 0;
      }
      ascent_buffer = temp;
      ascent_buffer_size = ascent_buffer_fill + len + 1;
    ascent_buffer_fill += len;
    ascent_buffer[ascent_buffer_fill] = 0;
  
 -  return (len);
 +  return len;
  } /* }}} size_t ascent_curl_callback */
  
  static int ascent_submit_players(player_stats_t *ps) /* {{{ */
      value = ((double)ps->latency_sum) / (1000.0 * ((double)ps->latency_num));
    ascent_submit_gauge(NULL, "latency", "average", value);
  
 -  return (0);
 +  return 0;
  } /* }}} int ascent_submit_players */
  
  static int ascent_account_player(player_stats_t *ps, /* {{{ */
      ps->latency_num++;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int ascent_account_player */
  
  static int ascent_xml_submit_gauge(xmlDoc *doc, xmlNode *node, /* {{{ */
    if (str_ptr == NULL) {
      ERROR(
          "ascent plugin: ascent_xml_submit_gauge: xmlNodeListGetString failed.");
 -    return (-1);
 +    return -1;
    }
  
    if (strcasecmp("N/A", str_ptr) == 0)
      if (str_ptr == end_ptr) {
        xmlFree(str_ptr);
        ERROR("ascent plugin: ascent_xml_submit_gauge: strtod failed.");
 -      return (-1);
 +      return -1;
      }
    }
    xmlFree(str_ptr);
  
 -  return (ascent_submit_gauge(plugin_instance, type, type_instance, value));
 +  return ascent_submit_gauge(plugin_instance, type, type_instance, value);
  } /* }}} int ascent_xml_submit_gauge */
  
  static int ascent_xml_read_int(xmlDoc *doc, xmlNode *node, /* {{{ */
    str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
    if (str_ptr == NULL) {
      ERROR("ascent plugin: ascent_xml_read_int: xmlNodeListGetString failed.");
 -    return (-1);
 +    return -1;
    }
  
    if (strcasecmp("N/A", str_ptr) == 0)
      if (str_ptr == end_ptr) {
        xmlFree(str_ptr);
        ERROR("ascent plugin: ascent_xml_read_int: strtol failed.");
 -      return (-1);
 +      return -1;
      }
    }
    xmlFree(str_ptr);
  
    *ret_value = value;
 -  return (0);
 +  return 0;
  } /* }}} int ascent_xml_read_int */
  
  static int ascent_xml_sessions_plr(xmlDoc *doc, xmlNode *node, /* {{{ */
      }
    } /* for (child) */
  
 -  return (0);
 +  return 0;
  } /* }}} int ascent_xml_sessions_plr */
  
  static int ascent_xml_sessions(xmlDoc *doc, xmlNode *node) /* {{{ */
  
    ascent_submit_players(&ps);
  
 -  return (0);
 +  return 0;
  } /* }}} int ascent_xml_sessions */
  
  static int ascent_xml_status(xmlDoc *doc, xmlNode *node) /* {{{ */
      }
    } /* for (child) */
  
 -  return (0);
 +  return 0;
  } /* }}} int ascent_xml_status */
  
  static int ascent_xml(const char *data) /* {{{ */
  #endif
    if (doc == NULL) {
      ERROR("ascent plugin: xmlParseMemory failed.");
 -    return (-1);
 +    return -1;
    }
  
    cur = xmlDocGetRootElement(doc);
    if (cur == NULL) {
      ERROR("ascent plugin: XML document is empty.");
      xmlFreeDoc(doc);
 -    return (-1);
 +    return -1;
    }
  
    if (xmlStrcmp((const xmlChar *)"serverpage", cur->name) != 0) {
      ERROR("ascent plugin: XML root element is not \"serverpage\".");
      xmlFreeDoc(doc);
 -    return (-1);
 +    return -1;
    }
  
    for (xmlNode *child = cur->xmlChildrenNode; child != NULL;
    } /* for (child) */
  
    xmlFreeDoc(doc);
 -  return (0);
 +  return 0;
  } /* }}} int ascent_xml */
  
  static int config_set(char **var, const char *value) /* {{{ */
    }
  
    if ((*var = strdup(value)) == NULL)
 -    return (1);
 +    return 1;
    else
 -    return (0);
 +    return 0;
  } /* }}} int config_set */
  
  static int ascent_config(const char *key, const char *value) /* {{{ */
  {
    if (strcasecmp(key, "URL") == 0)
 -    return (config_set(&url, value));
 +    return config_set(&url, value);
    else if (strcasecmp(key, "User") == 0)
 -    return (config_set(&user, value));
 +    return config_set(&user, value);
    else if (strcasecmp(key, "Password") == 0)
 -    return (config_set(&pass, value));
 +    return config_set(&pass, value);
    else if (strcasecmp(key, "VerifyPeer") == 0)
 -    return (config_set(&verify_peer, value));
 +    return config_set(&verify_peer, value);
    else if (strcasecmp(key, "VerifyHost") == 0)
 -    return (config_set(&verify_host, value));
 +    return config_set(&verify_host, value);
    else if (strcasecmp(key, "CACert") == 0)
 -    return (config_set(&cacert, value));
 +    return config_set(&cacert, value);
    else if (strcasecmp(key, "Timeout") == 0)
 -    return (config_set(&timeout, value));
 +    return config_set(&timeout, value);
    else
 -    return (-1);
 +    return -1;
  } /* }}} int ascent_config */
  
  static int ascent_init(void) /* {{{ */
    if (url == NULL) {
      WARNING("ascent plugin: ascent_init: No URL configured, "
              "returning an error.");
 -    return (-1);
 +    return -1;
    }
  
    if (curl != NULL) {
  
    if ((curl = curl_easy_init()) == NULL) {
      ERROR("ascent plugin: ascent_init: curl_easy_init failed.");
 -    return (-1);
 +    return -1;
    }
  
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
      if ((status < 0) || ((size_t)status >= sizeof(credentials))) {
        ERROR("ascent plugin: ascent_init: Returning an error because the "
              "credentials have been truncated.");
 -      return (-1);
 +      return -1;
      }
  
      curl_easy_setopt(curl, CURLOPT_USERPWD, credentials);
  #endif
    }
  
-   curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
  
                       (long)CDTIME_T_TO_MS(plugin_get_interval()));
  #endif
  
 -  return (0);
 +  return 0;
  } /* }}} int ascent_init */
  
  static int ascent_read(void) /* {{{ */
  
    if (curl == NULL) {
      ERROR("ascent plugin: I don't have a CURL object.");
 -    return (-1);
 +    return -1;
    }
  
    if (url == NULL) {
      ERROR("ascent plugin: No URL has been configured.");
 -    return (-1);
 +    return -1;
    }
  
    ascent_buffer_fill = 0;
+   curl_easy_setopt(curl, CURLOPT_URL, url);
    if (curl_easy_perform(curl) != CURLE_OK) {
      ERROR("ascent plugin: curl_easy_perform failed: %s", ascent_curl_error);
 -    return (-1);
 +    return -1;
    }
  
    status = ascent_xml(ascent_buffer);
    if (status != 0)
 -    return (-1);
 +    return -1;
    else
 -    return (0);
 +    return 0;
  } /* }}} int ascent_read */
  
  void module_register(void) {
    plugin_register_init("ascent", ascent_init);
    plugin_register_read("ascent", ascent_read);
  } /* void module_register */
 -
 -/* vim: set sw=2 sts=2 ts=8 et fdm=marker : */
diff --combined src/bind.c
@@@ -271,7 -271,7 +271,7 @@@ static size_t bind_curl_callback(void *
    size_t len = size * nmemb;
  
    if (len == 0)
 -    return (len);
 +    return len;
  
    if ((bind_buffer_fill + len) >= bind_buffer_size) {
      char *temp;
      temp = realloc(bind_buffer, bind_buffer_fill + len + 1);
      if (temp == NULL) {
        ERROR("bind plugin: realloc failed.");
 -      return (0);
 +      return 0;
      }
      bind_buffer = temp;
      bind_buffer_size = bind_buffer_fill + len + 1;
    bind_buffer_fill += len;
    bind_buffer[bind_buffer_fill] = 0;
  
 -  return (len);
 +  return len;
  } /* }}} size_t bind_curl_callback */
  
  /*
@@@ -301,7 -301,7 +301,7 @@@ static int bind_xml_table_callback(cons
    translation_table_ptr_t *table = (translation_table_ptr_t *)user_data;
  
    if (table == NULL)
 -    return (-1);
 +    return -1;
  
    for (size_t i = 0; i < table->table_length; i++) {
      if (strcmp(table->table[i].xml_name, name) != 0)
      break;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_table_callback */
  
  /*
@@@ -325,12 -325,12 +325,12 @@@ static int bind_xml_list_callback(cons
    list_info_ptr_t *list_info = (list_info_ptr_t *)user_data;
  
    if (list_info == NULL)
 -    return (-1);
 +    return -1;
  
    submit(current_time, list_info->plugin_instance, list_info->type,
           /* type instance = */ name, value);
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_list_callback */
  
  static int bind_xml_read_derive(xmlDoc *doc, xmlNode *node, /* {{{ */
    str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
    if (str_ptr == NULL) {
      ERROR("bind plugin: bind_xml_read_derive: xmlNodeListGetString failed.");
 -    return (-1);
 +    return -1;
    }
  
    status = parse_value(str_ptr, &value, DS_TYPE_DERIVE);
      ERROR("bind plugin: Parsing string \"%s\" to derive value failed.",
            str_ptr);
      xmlFree(str_ptr);
 -    return (-1);
 +    return -1;
    }
  
    xmlFree(str_ptr);
    *ret_value = value.derive;
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_read_derive */
  
  static int bind_xml_read_gauge(xmlDoc *doc, xmlNode *node, /* {{{ */
    str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
    if (str_ptr == NULL) {
      ERROR("bind plugin: bind_xml_read_gauge: xmlNodeListGetString failed.");
 -    return (-1);
 +    return -1;
    }
  
    errno = 0;
        ERROR("bind plugin: bind_xml_read_gauge: strtod failed with overflow.");
      else
        ERROR("bind plugin: bind_xml_read_gauge: strtod failed.");
 -    return (-1);
 +    return -1;
    }
  
    *ret_value = (gauge_t)value;
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_read_gauge */
  
  static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */
    if (xpathObj == NULL) {
      ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
            xpath_expression);
 -    return (-1);
 +    return -1;
    }
  
    if ((xpathObj->nodesetval == NULL) || (xpathObj->nodesetval->nodeNr < 1)) {
      xmlXPathFreeObject(xpathObj);
 -    return (-1);
 +    return -1;
    }
  
    if (xpathObj->nodesetval->nodeNr != 1) {
      ERROR("bind plugin: bind_xml_read_timestamp: "
            "node->xmlChildrenNode == NULL");
      xmlXPathFreeObject(xpathObj);
 -    return (-1);
 +    return -1;
    }
  
    str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
    if (str_ptr == NULL) {
      ERROR("bind plugin: bind_xml_read_timestamp: xmlNodeListGetString failed.");
      xmlXPathFreeObject(xpathObj);
 -    return (-1);
 +    return -1;
    }
  
    tmp = strptime(str_ptr, "%Y-%m-%dT%T", &tm);
    if (tmp == NULL) {
      ERROR("bind plugin: bind_xml_read_timestamp: strptime failed.");
      xmlXPathFreeObject(xpathObj);
 -    return (-1);
 +    return -1;
    }
  
  #if HAVE_TIMEGM
      char errbuf[1024];
      ERROR("bind plugin: timegm() failed: %s",
            sstrerror(errno, errbuf, sizeof(errbuf)));
 -    return (-1);
 +    return -1;
    }
    *ret_value = t;
  #else
      char errbuf[1024];
      ERROR("bind plugin: mktime() failed: %s",
            sstrerror(errno, errbuf, sizeof(errbuf)));
 -    return (-1);
 +    return -1;
    }
    /* mktime assumes that tm is local time. Luckily, it also sets timezone to
     * the offset used for the conversion, and we undo the conversion to convert
  #endif
  
    xmlXPathFreeObject(xpathObj);
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_read_timestamp */
  
  /*
@@@ -485,7 -485,7 +485,7 @@@ static int bind_parse_generic_name_valu
    if (xpathObj == NULL) {
      ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
            xpath_expression);
 -    return (-1);
 +    return -1;
    }
  
    num_entries = 0;
  
    xmlXPathFreeObject(xpathObj);
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_parse_generic_name_value */
  
  /*
@@@ -566,7 -566,7 +566,7 @@@ static int bind_parse_generic_value_lis
    if (xpathObj == NULL) {
      ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
            xpath_expression);
 -    return (-1);
 +    return -1;
    }
  
    num_entries = 0;
  
    xmlXPathFreeObject(xpathObj);
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_parse_generic_value_list */
  
  /*
@@@ -628,7 -628,7 +628,7 @@@ static int bind_parse_generic_name_attr
    if (xpathObj == NULL) {
      ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
            xpath_expression);
 -    return (-1);
 +    return -1;
    }
  
    num_entries = 0;
  
    xmlXPathFreeObject(xpathObj);
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_parse_generic_name_attr_value_list */
  
  static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */
      path_obj = xmlXPathEvalExpression(BAD_CAST "name", path_ctx);
      if (path_obj == NULL) {
        ERROR("bind plugin: xmlXPathEvalExpression failed.");
 -      return (-1);
 +      return -1;
      }
  
      for (int i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr);
  
    if (zone_name == NULL) {
      ERROR("bind plugin: Could not determine zone name.");
 -    return (-1);
 +    return -1;
    }
  
    for (j = 0; j < view->zones_num; j++) {
    zone_name = NULL;
  
    if (j >= view->zones_num)
 -    return (0);
 +    return 0;
  
    zone_name = view->zones[j];
  
      }
    } /* }}} */
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_stats_handle_zone */
  
  static int bind_xml_stats_search_zones(int version, xmlDoc *doc, /* {{{ */
    zone_path_context = xmlXPathNewContext(doc);
    if (zone_path_context == NULL) {
      ERROR("bind plugin: xmlXPathNewContext failed.");
 -    return (-1);
 +    return -1;
    }
  
    zone_nodes = xmlXPathEvalExpression(BAD_CAST "zones/zone", path_ctx);
    if (zone_nodes == NULL) {
      ERROR("bind plugin: Cannot find any <view> tags.");
      xmlXPathFreeContext(zone_path_context);
 -    return (-1);
 +    return -1;
    }
  
    for (int i = 0; i < zone_nodes->nodesetval->nodeNr; i++) {
  
    xmlXPathFreeObject(zone_nodes);
    xmlXPathFreeContext(zone_path_context);
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_stats_search_zones */
  
  static int bind_xml_stats_handle_view(int version, xmlDoc *doc, /* {{{ */
  
      if (view_name == NULL) {
        ERROR("bind plugin: Could not determine view name.");
 -      return (-1);
 +      return -1;
      }
  
      for (j = 0; j < views_num; j++) {
      path_obj = xmlXPathEvalExpression(BAD_CAST "name", path_ctx);
      if (path_obj == NULL) {
        ERROR("bind plugin: xmlXPathEvalExpression failed.");
 -      return (-1);
 +      return -1;
      }
  
      for (int i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr);
      if (view_name == NULL) {
        ERROR("bind plugin: Could not determine view name.");
        xmlXPathFreeObject(path_obj);
 -      return (-1);
 +      return -1;
      }
  
      for (j = 0; j < views_num; j++) {
    }
  
    if (j >= views_num)
 -    return (0);
 +    return 0;
  
    view = views + j;
  
      bind_xml_stats_search_zones(version, doc, path_ctx, node, view,
                                  current_time);
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_stats_handle_view */
  
  static int bind_xml_stats_search_views(int version, xmlDoc *doc, /* {{{ */
    view_path_context = xmlXPathNewContext(doc);
    if (view_path_context == NULL) {
      ERROR("bind plugin: xmlXPathNewContext failed.");
 -    return (-1);
 +    return -1;
    }
  
    view_nodes = xmlXPathEvalExpression(BAD_CAST "views/view", xpathCtx);
    if (view_nodes == NULL) {
      ERROR("bind plugin: Cannot find any <view> tags.");
      xmlXPathFreeContext(view_path_context);
 -    return (-1);
 +    return -1;
    }
  
    for (int i = 0; i < view_nodes->nodesetval->nodeNr; i++) {
  
    xmlXPathFreeObject(view_nodes);
    xmlXPathFreeContext(view_path_context);
 -  return (0);
 +  return 0;
  } /* }}} int bind_xml_stats_search_views */
  
  static void bind_xml_stats_v3(xmlDoc *doc, /* {{{ */
@@@ -1267,7 -1267,7 +1267,7 @@@ static int bind_xml_stats(int version, 
                                     &current_time);
    if (status != 0) {
      ERROR("bind plugin: Reading `server/current-time' failed.");
 -    return (-1);
 +    return -1;
    }
    DEBUG("bind plugin: Current server time is %i.", (int)current_time);
  
@@@ -1316,14 -1316,14 +1316,14 @@@ static int bind_xml(const char *data) /
    doc = xmlParseMemory(data, strlen(data));
    if (doc == NULL) {
      ERROR("bind plugin: xmlParseMemory failed.");
 -    return (-1);
 +    return -1;
    }
  
    xpathCtx = xmlXPathNewContext(doc);
    if (xpathCtx == NULL) {
      ERROR("bind plugin: xmlXPathNewContext failed.");
      xmlFreeDoc(doc);
 -    return (-1);
 +    return -1;
    }
  
    //
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc(doc);
  
 -    return (ret);
 +    return ret;
    }
  
    //
      ERROR("bind plugin: Cannot find the <statistics> tag.");
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc(doc);
 -    return (-1);
 +    return -1;
    } else if (xpathObj->nodesetval == NULL) {
      ERROR("bind plugin: xmlXPathEvalExpression failed.");
      xmlXPathFreeObject(xpathObj);
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc(doc);
 -    return (-1);
 +    return -1;
    }
  
    for (int i = 0; i < xpathObj->nodesetval->nodeNr; i++) {
    xmlXPathFreeContext(xpathCtx);
    xmlFreeDoc(doc);
  
 -  return (ret);
 +  return ret;
  } /* }}} int bind_xml */
  
  static int bind_config_set_bool(const char *name, int *var, /* {{{ */
      WARNING("bind plugin: The `%s' option needs "
              "exactly one boolean argument.",
              name);
 -    return (-1);
 +    return -1;
    }
  
    if (ci->values[0].value.boolean)
@@@ -1467,24 -1467,24 +1467,24 @@@ static int bind_config_add_view_zone(cb
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("bind plugin: The `Zone' option needs "
              "exactly one string argument.");
 -    return (-1);
 +    return -1;
    }
  
    tmp = realloc(view->zones, sizeof(char *) * (view->zones_num + 1));
    if (tmp == NULL) {
      ERROR("bind plugin: realloc failed.");
 -    return (-1);
 +    return -1;
    }
    view->zones = tmp;
  
    view->zones[view->zones_num] = strdup(ci->values[0].value.string);
    if (view->zones[view->zones_num] == NULL) {
      ERROR("bind plugin: strdup failed.");
 -    return (-1);
 +    return -1;
    }
    view->zones_num++;
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_config_add_view_zone */
  
  static int bind_config_add_view(oconfig_item_t *ci) /* {{{ */
  
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("bind plugin: `View' blocks need exactly one string argument.");
 -    return (-1);
 +    return -1;
    }
  
    tmp = realloc(views, sizeof(*views) * (views_num + 1));
    if (tmp == NULL) {
      ERROR("bind plugin: realloc failed.");
 -    return (-1);
 +    return -1;
    }
    views = tmp;
    tmp = views + views_num;
    if (tmp->name == NULL) {
      ERROR("bind plugin: strdup failed.");
      sfree(views);
 -    return (-1);
 +    return -1;
    }
  
    for (int i = 0; i < ci->children_num; i++) {
    } /* for (i = 0; i < ci->children_num; i++) */
  
    views_num++;
 -  return (0);
 +  return 0;
  } /* }}} int bind_config_add_view */
  
  static int bind_config(oconfig_item_t *ci) /* {{{ */
            (child->values[0].type != OCONFIG_TYPE_STRING)) {
          WARNING("bind plugin: The `Url' option needs "
                  "exactly one string argument.");
 -        return (-1);
 +        return -1;
        }
  
        sfree(url);
      }
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_config */
  
  static int bind_init(void) /* {{{ */
  {
    if (curl != NULL)
 -    return (0);
 +    return 0;
  
    curl = curl_easy_init();
    if (curl == NULL) {
      ERROR("bind plugin: bind_init: curl_easy_init failed.");
 -    return (-1);
 +    return -1;
    }
  
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, bind_curl_callback);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, bind_curl_error);
-   curl_easy_setopt(curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
  #ifdef HAVE_CURLOPT_TIMEOUT_MS
                                                          plugin_get_interval()));
  #endif
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_init */
  
  static int bind_read(void) /* {{{ */
  
    if (curl == NULL) {
      ERROR("bind plugin: I don't have a CURL object.");
 -    return (-1);
 +    return -1;
    }
  
    bind_buffer_fill = 0;
+   curl_easy_setopt(curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL);
    if (curl_easy_perform(curl) != CURLE_OK) {
      ERROR("bind plugin: curl_easy_perform failed: %s", bind_curl_error);
 -    return (-1);
 +    return -1;
    }
  
    status = bind_xml(bind_buffer);
    if (status != 0)
 -    return (-1);
 +    return -1;
    else
 -    return (0);
 +    return 0;
  } /* }}} int bind_read */
  
  static int bind_shutdown(void) /* {{{ */
      curl = NULL;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int bind_shutdown */
  
  void module_register(void) {
    plugin_register_read("bind", bind_read);
    plugin_register_shutdown("bind", bind_shutdown);
  } /* void module_register */
 -
 -/* vim: set sw=2 sts=2 ts=8 et fdm=marker : */
diff --combined src/curl.c
@@@ -97,11 -97,11 +97,11 @@@ static size_t cc_curl_callback(void *bu
  
    len = size * nmemb;
    if (len == 0)
 -    return (len);
 +    return len;
  
    wp = user_data;
    if (wp == NULL)
 -    return (0);
 +    return 0;
  
    if ((wp->buffer_fill + len) >= wp->buffer_size) {
      char *temp;
      temp = realloc(wp->buffer, temp_size);
      if (temp == NULL) {
        ERROR("curl plugin: realloc failed.");
 -      return (0);
 +      return 0;
      }
      wp->buffer = temp;
      wp->buffer_size = temp_size;
    wp->buffer_fill += len;
    wp->buffer[wp->buffer_fill] = 0;
  
 -  return (len);
 +  return len;
  } /* }}} size_t cc_curl_callback */
  
  static void cc_web_match_free(web_match_t *wm) /* {{{ */
@@@ -170,16 -170,16 +170,16 @@@ static int cc_config_append_string(cons
    struct curl_slist *temp = NULL;
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl plugin: `%s' needs exactly one string argument.", name);
 -    return (-1);
 +    return -1;
    }
  
    temp = curl_slist_append(*dest, ci->values[0].value.string);
    if (temp == NULL)
 -    return (-1);
 +    return -1;
  
    *dest = temp;
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_config_append_string */
  
  static int cc_config_add_match_dstype(int *dstype_ret, /* {{{ */
  
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl plugin: `DSType' needs exactly one string argument.");
 -    return (-1);
 +    return -1;
    }
  
    if (strncasecmp("Gauge", ci->values[0].value.string, strlen("Gauge")) == 0) {
    if (dstype == 0) {
      WARNING("curl plugin: `%s' is not a valid argument to `DSType'.",
              ci->values[0].value.string);
 -    return (-1);
 +    return -1;
    }
  
    *dstype_ret = dstype;
 -  return (0);
 +  return 0;
  } /* }}} int cc_config_add_match_dstype */
  
  static int cc_config_add_match(web_page_t *page, /* {{{ */
    match = calloc(1, sizeof(*match));
    if (match == NULL) {
      ERROR("curl plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
  
    status = 0;
  
    if (status != 0) {
      cc_web_match_free(match);
 -    return (status);
 +    return status;
    }
  
    match->match =
    if (match->match == NULL) {
      ERROR("curl plugin: match_create_simple failed.");
      cc_web_match_free(match);
 -    return (-1);
 +    return -1;
    } else {
      web_match_t *prev;
  
        prev->next = match;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_config_add_match */
  
  static int cc_page_init_curl(web_page_t *wp) /* {{{ */
    wp->curl = curl_easy_init();
    if (wp->curl == NULL) {
      ERROR("curl plugin: curl_easy_init failed.");
 -    return (-1);
 +    return -1;
    }
  
    curl_easy_setopt(wp->curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(wp->curl, CURLOPT_WRITEDATA, wp);
    curl_easy_setopt(wp->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
    curl_easy_setopt(wp->curl, CURLOPT_ERRORBUFFER, wp->curl_errbuf);
-   curl_easy_setopt(wp->curl, CURLOPT_URL, wp->url);
    curl_easy_setopt(wp->curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(wp->curl, CURLOPT_MAXREDIRS, 50L);
  
      wp->credentials = malloc(credentials_size);
      if (wp->credentials == NULL) {
        ERROR("curl plugin: malloc failed.");
 -      return (-1);
 +      return -1;
      }
  
      ssnprintf(wp->credentials, credentials_size, "%s:%s", wp->user,
                       (long)CDTIME_T_TO_MS(plugin_get_interval()));
  #endif
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_page_init_curl */
  
  static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
  
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl plugin: `Page' blocks need exactly one string argument.");
 -    return (-1);
 +    return -1;
    }
  
    page = calloc(1, sizeof(*page));
    if (page == NULL) {
      ERROR("curl plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
    page->url = NULL;
    page->user = NULL;
    if (page->instance == NULL) {
      ERROR("curl plugin: strdup failed.");
      sfree(page);
 -    return (-1);
 +    return -1;
    }
  
    /* Process all children */
  
    if (status != 0) {
      cc_web_page_free(page);
 -    return (status);
 +    return status;
    }
  
    /* Add the new page to the linked list */
      prev->next = page;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_config_add_page */
  
  static int cc_config(oconfig_item_t *ci) /* {{{ */
  
    if ((success == 0) && (errors > 0)) {
      ERROR("curl plugin: All statements failed.");
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_config */
  
  static int cc_init(void) /* {{{ */
  {
    if (pages_g == NULL) {
      INFO("curl plugin: No pages have been defined.");
 -    return (-1);
 +    return -1;
    }
    curl_global_init(CURL_GLOBAL_SSL);
 -  return (0);
 +  return 0;
  } /* }}} int cc_init */
  
  static void cc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */
@@@ -611,11 -610,14 +610,14 @@@ static int cc_read_page(web_page_t *wp
      start = cdtime();
  
    wp->buffer_fill = 0;
+   curl_easy_setopt(wp->curl, CURLOPT_URL, wp->url);
    status = curl_easy_perform(wp->curl);
    if (status != CURLE_OK) {
      ERROR("curl plugin: curl_easy_perform failed with status %i: %s", status,
            wp->curl_errbuf);
 -    return (-1);
 +    return -1;
    }
  
    if (wp->response_time)
      match_value_reset(mv);
    } /* for (wm = wp->matches; wm != NULL; wm = wm->next) */
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_read_page */
  
  static int cc_read(void) /* {{{ */
    for (web_page_t *wp = pages_g; wp != NULL; wp = wp->next)
      cc_read_page(wp);
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_read */
  
  static int cc_shutdown(void) /* {{{ */
    cc_web_page_free(pages_g);
    pages_g = NULL;
  
 -  return (0);
 +  return 0;
  } /* }}} int cc_shutdown */
  
  void module_register(void) {
    plugin_register_read("curl", cc_read);
    plugin_register_shutdown("curl", cc_shutdown);
  } /* void module_register */
 -
 -/* vim: set sw=2 sts=2 et fdm=marker : */
diff --combined src/curl_json.c
@@@ -44,6 -44,8 +44,6 @@@
  #endif
  
  #define CJ_DEFAULT_HOST "localhost"
 -#define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */
 -#define CJ_IS_KEY(key) ((key)->magic == CJ_KEY_MAGIC)
  #define CJ_ANY "*"
  #define COUCH_MIN(x, y) ((x) < (y) ? (x) : (y))
  
@@@ -51,34 -53,13 +51,34 @@@ struct cj_key_s
  typedef struct cj_key_s cj_key_t;
  struct cj_key_s /* {{{ */
  {
 -  unsigned long magic;
    char *path;
    char *type;
    char *instance;
  };
  /* }}} */
  
 +/* cj_tree_entry_t is a union of either a metric configuration ("key") or a tree
 + * mapping array indexes / map keys to a descendant cj_tree_entry_t*. */
 +typedef struct {
 +  enum { KEY, TREE } type;
 +  union {
 +    c_avl_tree_t *tree;
 +    cj_key_t *key;
 +  };
 +} cj_tree_entry_t;
 +
 +/* cj_state_t is a stack providing the configuration relevant for the context
 + * that is currently being parsed. If entry->type == KEY, the parser should
 + * expect a metric (a numeric value). If entry->type == TREE, the parser should
 + * expect an array of map to descent into. If entry == NULL, no configuration
 + * exists for this part of the JSON structure. */
 +typedef struct {
 +  cj_tree_entry_t *entry;
 +  _Bool in_array;
 +  int index;
 +  char name[DATA_MAX_NAME_LEN];
 +} cj_state_t;
 +
  struct cj_s /* {{{ */
  {
    char *instance;
  
    yajl_handle yajl;
    c_avl_tree_t *tree;
 -  cj_key_t *key;
    int depth;
 -  struct {
 -    union {
 -      c_avl_tree_t *tree;
 -      cj_key_t *key;
 -    };
 -    _Bool in_array;
 -    int index;
 -    char name[DATA_MAX_NAME_LEN];
 -  } state[YAJL_MAX_DEPTH];
 +  cj_state_t state[YAJL_MAX_DEPTH];
  };
  typedef struct cj_s cj_t; /* }}} */
  
@@@ -117,11 -107,7 +117,11 @@@ typedef unsigned int yajl_len_t
  #endif
  
  static int cj_read(user_data_t *ud);
 -static void cj_submit(cj_t *db, cj_key_t *key, value_t *value);
 +static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value);
 +
 +/* cj_submit is a function pointer to cj_submit_impl, allowing the unit-test to
 + * overwrite which function is called. */
 +static void (*cj_submit)(cj_t *, cj_key_t *, value_t *) = cj_submit_impl;
  
  static size_t cj_curl_callback(void *buf, /* {{{ */
                                 size_t size, size_t nmemb, void *user_data) {
    len = size * nmemb;
  
    if (len == 0)
 -    return (len);
 +    return len;
  
    db = user_data;
    if (db == NULL)
 -    return (0);
 +    return 0;
  
    status = yajl_parse(db->yajl, (unsigned char *)buf, len);
    if (status == yajl_status_ok)
 -    return (len);
 +    return len;
  #if !HAVE_YAJL_V2
    else if (status == yajl_status_insufficient_data)
 -    return (len);
 +    return len;
  #endif
  
    unsigned char *msg =
                       /* jsonText = */ (unsigned char *)buf, (unsigned int)len);
    ERROR("curl_json plugin: yajl_parse failed: %s", msg);
    yajl_free_error(db->yajl, msg);
 -  return (0); /* abort write callback */
 +  return 0; /* abort write callback */
  } /* }}} size_t cj_curl_callback */
  
  static int cj_get_type(cj_key_t *key) {
 -  const data_set_t *ds;
 -
 -  if ((key == NULL) || !CJ_IS_KEY(key))
 +  if (key == NULL)
      return -EINVAL;
  
 -  ds = plugin_get_ds(key->type);
 +  const data_set_t *ds = plugin_get_ds(key->type);
    if (ds == NULL) {
      static char type[DATA_MAX_NAME_LEN] = "!!!invalid!!!";
  
@@@ -191,20 -179,31 +191,20 @@@ static int cj_load_key(cj_t *db, char c
  
    sstrncpy(db->state[db->depth].name, key, sizeof(db->state[db->depth].name));
  
 -  c_avl_tree_t *tree = db->state[db->depth - 1].tree;
 -  if (tree == NULL) {
 +  if (db->state[db->depth - 1].entry == NULL ||
 +      db->state[db->depth - 1].entry->type != TREE) {
      return 0;
    }
  
 -  /* the parent has a key, so the tree pointer is invalid. */
 -  if (CJ_IS_KEY(db->state[db->depth - 1].key)) {
 -    return 0;
 -  }
 +  c_avl_tree_t *tree = db->state[db->depth - 1].entry->tree;
 +  cj_tree_entry_t *e = NULL;
  
 -  void *value = NULL;
 -  if (c_avl_get(tree, key, (void *)&value) == 0) {
 -    if (CJ_IS_KEY((cj_key_t *)value)) {
 -      db->state[db->depth].key = value;
 -    } else {
 -      db->state[db->depth].tree = value;
 -    }
 -  } else if (c_avl_get(tree, CJ_ANY, (void *)&value) == 0) {
 -    if (CJ_IS_KEY((cj_key_t *)value)) {
 -      db->state[db->depth].key = value;
 -    } else {
 -      db->state[db->depth].tree = value;
 -    }
 +  if (c_avl_get(tree, key, (void *)&e) == 0) {
 +    db->state[db->depth].entry = e;
 +  } else if (c_avl_get(tree, CJ_ANY, (void *)&e) == 0) {
 +    db->state[db->depth].entry = e;
    } else {
 -    db->state[db->depth].key = NULL;
 +    db->state[db->depth].entry = NULL;
    }
  
    return 0;
@@@ -227,47 -226,51 +227,47 @@@ static void cj_advance_array(cj_t *db) 
  
  static int cj_cb_boolean(void *ctx, int boolVal) {
    cj_advance_array(ctx);
 -  return (CJ_CB_CONTINUE);
 +  return CJ_CB_CONTINUE;
  }
  
  static int cj_cb_null(void *ctx) {
    cj_advance_array(ctx);
 -  return (CJ_CB_CONTINUE);
 +  return CJ_CB_CONTINUE;
  }
  
  static int cj_cb_number(void *ctx, const char *number, yajl_len_t number_len) {
 -  char buffer[number_len + 1];
 -
    cj_t *db = (cj_t *)ctx;
 -  cj_key_t *key = db->state[db->depth].key;
  
    /* Create a null-terminated version of the string. */
 +  char buffer[number_len + 1];
    memcpy(buffer, number, number_len);
    buffer[sizeof(buffer) - 1] = 0;
  
 -
 -
 -  if (key == NULL) {
 -    /* no config for this element. */
 -    cj_advance_array(ctx);
 -    return CJ_CB_CONTINUE;
 -  } else if (!CJ_IS_KEY(key)) {
 -    /* the config expects a map or an array. */
 -    NOTICE(
 -        "curl_json plugin: Found \"%s\", but the configuration expects a map.",
 -        buffer);
 +  if (db->state[db->depth].entry == NULL ||
 +      db->state[db->depth].entry->type != KEY) {
 +    if (db->state[db->depth].entry != NULL) {
 +      NOTICE("curl_json plugin: Found \"%s\", but the configuration expects a "
 +             "map.",
 +             buffer);
 +    }
      cj_advance_array(ctx);
      return CJ_CB_CONTINUE;
    }
  
 +  cj_key_t *key = db->state[db->depth].entry->key;
 +
    int type = cj_get_type(key);
    value_t vt;
    int status = parse_value(buffer, &vt, type);
    if (status != 0) {
      NOTICE("curl_json plugin: Unable to parse number: \"%s\"", buffer);
      cj_advance_array(ctx);
 -    return (CJ_CB_CONTINUE);
 +    return CJ_CB_CONTINUE;
    }
  
    cj_submit(db, key, &vt);
    cj_advance_array(ctx);
 -  return (CJ_CB_CONTINUE);
 +  return CJ_CB_CONTINUE;
  } /* int cj_cb_number */
  
  /* Queries the key-tree of the parent context for "in_name" and, if found,
@@@ -288,15 -291,15 +288,15 @@@ static int cj_cb_map_key(void *ctx, uns
  
  static int cj_cb_string(void *ctx, const unsigned char *val, yajl_len_t len) {
    /* Handle the string as if it was a number. */
 -  return (cj_cb_number(ctx, (const char *)val, len));
 +  return cj_cb_number(ctx, (const char *)val, len);
  } /* int cj_cb_string */
  
  static int cj_cb_end(void *ctx) {
    cj_t *db = (cj_t *)ctx;
 -  db->state[db->depth].tree = NULL;
 +  memset(&db->state[db->depth], 0, sizeof(db->state[db->depth]));
    db->depth--;
    cj_advance_array(ctx);
 -  return (CJ_CB_CONTINUE);
 +  return CJ_CB_CONTINUE;
  }
  
  static int cj_cb_start_map(void *ctx) {
    if ((db->depth + 1) >= YAJL_MAX_DEPTH) {
      ERROR("curl_json plugin: %s depth exceeds max, aborting.",
            db->url ? db->url : db->sock);
 -    return (CJ_CB_ABORT);
 +    return CJ_CB_ABORT;
    }
    db->depth++;
 -  return (CJ_CB_CONTINUE);
 +  return CJ_CB_CONTINUE;
  }
  
  static int cj_cb_end_map(void *ctx) { return cj_cb_end(ctx); }
@@@ -361,16 -364,17 +361,16 @@@ static void cj_key_free(cj_key_t *key) 
  static void cj_tree_free(c_avl_tree_t *tree) /* {{{ */
  {
    char *name;
 -  void *value;
 +  cj_tree_entry_t *e;
  
 -  while (c_avl_pick(tree, (void *)&name, (void *)&value) == 0) {
 -    cj_key_t *key = (cj_key_t *)value;
 +  while (c_avl_pick(tree, (void *)&name, (void *)&e) == 0) {
 +    sfree(name);
  
 -    if (CJ_IS_KEY(key))
 -      cj_key_free(key);
 +    if (e->type == KEY)
 +      cj_key_free(e->key);
      else
 -      cj_tree_free((c_avl_tree_t *)value);
 -
 -    sfree(name);
 +      cj_tree_free(e->tree);
 +    sfree(e);
    }
  
    c_avl_destroy(tree);
@@@ -424,79 -428,18 +424,79 @@@ static int cj_config_append_string(cons
    struct curl_slist *temp = NULL;
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl_json plugin: `%s' needs exactly one string argument.", name);
 -    return (-1);
 +    return -1;
    }
  
    temp = curl_slist_append(*dest, ci->values[0].value.string);
    if (temp == NULL)
 -    return (-1);
 +    return -1;
  
    *dest = temp;
  
 -  return (0);
 +  return 0;
  } /* }}} int cj_config_append_string */
  
 +/* cj_append_key adds key to the configuration stored in db.
 + *
 + * For example:
 + * "httpd/requests/count",
 + * "httpd/requests/current" ->
 + * { "httpd": { "requests": { "count": $key, "current": $key } } }
 + */
 +static int cj_append_key(cj_t *db, cj_key_t *key) { /* {{{ */
 +  if (db->tree == NULL)
 +    db->tree = cj_avl_create();
 +
 +  c_avl_tree_t *tree = db->tree;
 +
 +  char const *start = key->path;
 +  if (*start == '/')
 +    ++start;
 +
 +  char const *end;
 +  while ((end = strchr(start, '/')) != NULL) {
 +    char name[PATH_MAX];
 +
 +    size_t len = end - start;
 +    if (len == 0)
 +      break;
 +
 +    len = COUCH_MIN(len, sizeof(name) - 1);
 +    sstrncpy(name, start, len + 1);
 +
 +    cj_tree_entry_t *e;
 +    if (c_avl_get(tree, name, (void *)&e) != 0) {
 +      e = calloc(1, sizeof(*e));
 +      if (e == NULL)
 +        return ENOMEM;
 +      e->type = TREE;
 +      e->tree = cj_avl_create();
 +
 +      c_avl_insert(tree, strdup(name), e);
 +    }
 +
 +    if (e->type != TREE)
 +      return EINVAL;
 +
 +    tree = e->tree;
 +    start = end + 1;
 +  }
 +
 +  if (strlen(start) == 0) {
 +    ERROR("curl_json plugin: invalid key: %s", key->path);
 +    return -1;
 +  }
 +
 +  cj_tree_entry_t *e = calloc(1, sizeof(*e));
 +  if (e == NULL)
 +    return ENOMEM;
 +  e->type = KEY;
 +  e->key = key;
 +
 +  c_avl_insert(tree, strdup(start), e);
 +  return 0;
 +} /* }}} int cj_append_key */
 +
  static int cj_config_add_key(cj_t *db, /* {{{ */
                               oconfig_item_t *ci) {
    cj_key_t *key;
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl_json plugin: The `Key' block "
              "needs exactly one string argument.");
 -    return (-1);
 +    return -1;
    }
  
    key = calloc(1, sizeof(*key));
    if (key == NULL) {
      ERROR("curl_json plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
 -  key->magic = CJ_KEY_MAGIC;
  
    if (strcasecmp("Key", ci->key) == 0) {
      status = cf_util_get_string(ci, &key->path);
      if (status != 0) {
        sfree(key);
 -      return (status);
 +      return status;
      }
    } else {
      ERROR("curl_json plugin: cj_config: "
            "Invalid key: %s",
            ci->key);
      cj_key_free(key);
 -    return (-1);
 +    return -1;
    }
  
    status = 0;
  
    if (status != 0) {
      cj_key_free(key);
 -    return (-1);
 +    return -1;
    }
  
    if (key->type == NULL) {
      WARNING("curl_json plugin: `Type' missing in `Key' block.");
      cj_key_free(key);
 -    return (-1);
 -  }
 -
 -  /* store path in a tree that will match the json map structure, example:
 -   * "httpd/requests/count",
 -   * "httpd/requests/current" ->
 -   * { "httpd": { "requests": { "count": $key, "current": $key } } }
 -   */
 -  char *ptr;
 -  char *name;
 -  c_avl_tree_t *tree;
 -
 -  if (db->tree == NULL)
 -    db->tree = cj_avl_create();
 -
 -  tree = db->tree;
 -  ptr = key->path;
 -  if (*ptr == '/')
 -    ++ptr;
 -
 -  name = ptr;
 -  while ((ptr = strchr(name, '/')) != NULL) {
 -    char ent[PATH_MAX];
 -    c_avl_tree_t *value;
 -    size_t len;
 -
 -    len = ptr - name;
 -    if (len == 0)
 -      break;
 -
 -    len = COUCH_MIN(len, sizeof(ent) - 1);
 -    sstrncpy(ent, name, len + 1);
 -
 -    if (c_avl_get(tree, ent, (void *)&value) != 0) {
 -      value = cj_avl_create();
 -      c_avl_insert(tree, strdup(ent), value);
 -    }
 -
 -    tree = value;
 -    name = ptr + 1;
 +    return -1;
    }
  
 -  if (strlen(name) == 0) {
 -    ERROR("curl_json plugin: invalid key: %s", key->path);
 +  status = cj_append_key(db, key);
 +  if (status != 0) {
      cj_key_free(key);
 -    return (-1);
 +    return -1;
    }
  
 -  c_avl_insert(tree, strdup(name), key);
 -  return (status);
 +  return 0;
  } /* }}} int cj_config_add_key */
  
  static int cj_init_curl(cj_t *db) /* {{{ */
    db->curl = curl_easy_init();
    if (db->curl == NULL) {
      ERROR("curl_json plugin: curl_easy_init failed.");
 -    return (-1);
 +    return -1;
    }
  
    curl_easy_setopt(db->curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(db->curl, CURLOPT_WRITEDATA, db);
    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);
  
      db->credentials = malloc(credentials_size);
      if (db->credentials == NULL) {
        ERROR("curl_json plugin: malloc failed.");
 -      return (-1);
 +      return -1;
      }
  
      ssnprintf(db->credentials, credentials_size, "%s:%s", db->user,
                       (long)CDTIME_T_TO_MS(plugin_get_interval()));
  #endif
  
 -  return (0);
 +  return 0;
  } /* }}} int cj_init_curl */
  
  static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl_json plugin: The `URL' block "
              "needs exactly one string argument.");
 -    return (-1);
 +    return -1;
    }
  
    db = calloc(1, sizeof(*db));
    if (db == NULL) {
      ERROR("curl_json plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
  
    db->timeout = -1;
            "Invalid key: %s",
            ci->key);
      cj_free(db);
 -    return (-1);
 +    return -1;
    }
    if (status != 0) {
      sfree(db);
 -    return (status);
 +    return status;
    }
  
    /* Fill the `cj_t' structure.. */
      sfree(cb_name);
    } else {
      cj_free(db);
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  }
  /* }}} int cj_config_add_database */
  
@@@ -774,10 -757,10 +773,10 @@@ static int cj_config(oconfig_item_t *ci
  
    if ((success == 0) && (errors > 0)) {
      ERROR("curl_json plugin: All statements failed.");
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int cj_config */
  
  /* }}} End of configuration handling functions */
@@@ -790,7 -773,7 +789,7 @@@ static const char *cj_host(cj_t *db) /
    return db->host;
  } /* }}} cj_host */
  
 -static void cj_submit(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
 +static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
  {
    value_list_t vl = VALUE_LIST_INIT;
  
      vl.interval = db->interval;
  
    plugin_dispatch_values(&vl);
 -} /* }}} int cj_submit */
 +} /* }}} int cj_submit_impl */
  
  static int cj_sock_perform(cj_t *db) /* {{{ */
  {
    char errbuf[1024];
 -  struct sockaddr_un sa_unix = {0};
 -  sa_unix.sun_family = AF_UNIX;
 +  struct sockaddr_un sa_unix = {
 +      .sun_family = AF_UNIX,
 +  };
    sstrncpy(sa_unix.sun_path, db->sock, sizeof(sa_unix.sun_path));
  
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0)
 -    return (-1);
 +    return -1;
    if (connect(fd, (struct sockaddr *)&sa_unix, sizeof(sa_unix)) < 0) {
      ERROR("curl_json plugin: connect(%s) failed: %s",
            (db->sock != NULL) ? db->sock : "<null>",
            sstrerror(errno, errbuf, sizeof(errbuf)));
      close(fd);
 -    return (-1);
 +    return -1;
    }
  
    ssize_t red;
              (db->sock != NULL) ? db->sock : "<null>",
              sstrerror(errno, errbuf, sizeof(errbuf)));
        close(fd);
 -      return (-1);
 +      return -1;
      }
      if (!cj_curl_callback(buffer, red, 1, db))
        break;
    } while (red > 0);
    close(fd);
 -  return (0);
 +  return 0;
  } /* }}} int cj_sock_perform */
  
  static int cj_curl_perform(cj_t *db) /* {{{ */
    int status;
    long rc;
    char *url;
-   url = db->url;
+   curl_easy_setopt(db->curl, CURLOPT_URL, db->url);
  
    status = curl_easy_perform(db->curl);
    if (status != CURLE_OK) {
      ERROR("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
-           status, db->curl_errbuf, url);
+           status, db->curl_errbuf, db->url);
 -    return (-1);
 +    return -1;
    }
    if (db->stats != NULL)
      curl_stats_dispatch(db->stats, db->curl, cj_host(db), "curl_json",
      ERROR("curl_json plugin: curl_easy_perform failed with "
            "response code %ld (%s)",
            rc, url);
 -    return (-1);
 +    return -1;
    }
 -  return (0);
 +  return 0;
  } /* }}} int cj_curl_perform */
  
  static int cj_perform(cj_t *db) /* {{{ */
    if (db->yajl == NULL) {
      ERROR("curl_json plugin: yajl_alloc failed.");
      db->yajl = yprev;
 -    return (-1);
 +    return -1;
    }
  
    if (db->url)
    if (status < 0) {
      yajl_free(db->yajl);
      db->yajl = yprev;
 -    return (-1);
 +    return -1;
    }
  
  #if HAVE_YAJL_V2
      yajl_free_error(db->yajl, errmsg);
      yajl_free(db->yajl);
      db->yajl = yprev;
 -    return (-1);
 +    return -1;
    }
  
    yajl_free(db->yajl);
    db->yajl = yprev;
 -  return (0);
 +  return 0;
  } /* }}} int cj_perform */
  
  static int cj_read(user_data_t *ud) /* {{{ */
  
    if ((ud == NULL) || (ud->data == NULL)) {
      ERROR("curl_json plugin: cj_read: Invalid user data.");
 -    return (-1);
 +    return -1;
    }
  
    db = (cj_t *)ud->data;
  
    db->depth = 0;
    memset(&db->state, 0, sizeof(db->state));
 -  db->state[db->depth].tree = db->tree;
 -  db->key = NULL;
  
 -  return cj_perform(db);
 +  /* This is not a compound literal because EPEL6's GCC is not cool enough to
 +   * handle anonymous unions within compound literals. */
 +  cj_tree_entry_t root = {0};
 +  root.type = TREE;
 +  root.tree = db->tree;
 +  db->state[0].entry = &root;
 +
 +  int status = cj_perform(db);
 +
 +  db->state[0].entry = NULL;
 +
 +  return status;
  } /* }}} int cj_read */
  
  static int cj_init(void) /* {{{ */
    /* Call this while collectd is still single-threaded to avoid
     * initialization issues in libgcrypt. */
    curl_global_init(CURL_GLOBAL_SSL);
 -  return (0);
 +  return 0;
  } /* }}} int cj_init */
  
  void module_register(void) {
    plugin_register_complex_config("curl_json", cj_config);
    plugin_register_init("curl_json", cj_init);
  } /* void module_register */
 -
 -/* vim: set sw=2 sts=2 et fdm=marker : */
diff --combined src/curl_xml.c
@@@ -111,11 -111,11 +111,11 @@@ static size_t cx_curl_callback(void *bu
    if (db == NULL) {
      ERROR("curl_xml plugin: cx_curl_callback: "
            "user_data pointer is NULL.");
 -    return (0);
 +    return 0;
    }
  
    if (len == 0)
 -    return (len);
 +    return len;
  
    if ((db->buffer_fill + len) >= db->buffer_size) {
      char *temp;
      temp = realloc(db->buffer, db->buffer_fill + len + 1);
      if (temp == NULL) {
        ERROR("curl_xml plugin: realloc failed.");
 -      return (0);
 +      return 0;
      }
      db->buffer = temp;
      db->buffer_size = db->buffer_fill + len + 1;
    db->buffer_fill += len;
    db->buffer[db->buffer_fill] = 0;
  
 -  return (len);
 +  return len;
  } /* }}} size_t cx_curl_callback */
  
  static void cx_xpath_free(cx_xpath_t *xpath) /* {{{ */
@@@ -221,33 -221,33 +221,33 @@@ static int cx_config_append_string(cons
    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);
 +    return -1;
    }
  
    temp = curl_slist_append(*dest, ci->values[0].value.string);
    if (temp == NULL)
 -    return (-1);
 +    return -1;
  
    *dest = temp;
  
 -  return (0);
 +  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);
 +    return -1;
    }
  
    if (ds->ds_num != xpath->values_len) {
      WARNING("curl_xml plugin: DataSet `%s' requires %zu values, but config "
              "talks about %zu",
              xpath->type, ds->ds_num, xpath->values_len);
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} cx_check_type */
  
  static xmlXPathObjectPtr
@@@ -271,7 -271,7 +271,7 @@@ static int cx_if_not_text_node(xmlNodeP
  {
    if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE ||
        node->type == XML_ELEMENT_NODE)
 -    return (0);
 +    return 0;
  
    WARNING("curl_xml plugin: "
            "Node \"%s\" doesn't seem to be a text node. Skipping...",
@@@ -290,7 -290,7 +290,7 @@@ static int cx_handle_single_value_xpath
    values_node_obj =
        cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->values[index].path);
    if (values_node_obj == NULL)
 -    return (-1); /* Error already logged. */
 +    return -1; /* Error already logged. */
  
    values_node = values_node_obj->nodesetval;
    tmp_size = (values_node) ? values_node->nodeNr : 0;
              "Skipping...",
              xpath->values[index].path);
      xmlXPathFreeObject(values_node_obj);
 -    return (-1);
 +    return -1;
    }
  
    if (tmp_size > 1) {
              "only one node. Skipping...",
              xpath->values[index].path);
      xmlXPathFreeObject(values_node_obj);
 -    return (-1);
 +    return -1;
    }
  
    /* ignoring the element if other than textnode/attribute*/
              "only text/attribute node which is not the case. Skipping...",
              xpath->values[index].path);
      xmlXPathFreeObject(values_node_obj);
 -    return (-1);
 +    return -1;
    }
  
    node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
  
    /* We have reached here which means that
     * we have got something to work */
 -  return (0);
 +  return 0;
  } /* }}} int cx_handle_single_value_xpath */
  
  static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */
    for (size_t 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. */
 +      return -1; /* An error has been printed. */
    }                /* for (i = 0; i < xpath->values_len; i++) */
  
    plugin_dispatch_values(vl);
    vl->values = NULL;
  
 -  return (0);
 +  return 0;
  } /* }}} int cx_handle_all_value_xpaths */
  
  static int cx_handle_instance_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
              "Base-XPath %s is a table (more than one result was returned), "
              "but no instance-XPath has been defined.",
              xpath->path);
 -    return (-1);
 +    return -1;
    }
  
    /* instance has to be an xpath expression */
  
      instance_node_obj = cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->instance);
      if (instance_node_obj == NULL)
 -      return (-1); /* error is logged already */
 +      return -1; /* error is logged already */
  
      instance_node = instance_node_obj->nodesetval;
      tmp_size = (instance_node) ? instance_node->nodeNr : 0;
            "any of the nodes. Skipping the node.",
            xpath->instance);
        xmlXPathFreeObject(instance_node_obj);
 -      return (-1);
 +      return -1;
      }
  
      if (tmp_size > 1) {
                "to return only one text node. Skipping the node.",
                xpath->instance);
        xmlXPathFreeObject(instance_node_obj);
 -      return (-1);
 +      return -1;
      }
  
      /* ignoring the element if other than textnode/attribute */
                "which is not the case. Skipping the node.",
                xpath->instance);
        xmlXPathFreeObject(instance_node_obj);
 -      return (-1);
 +      return -1;
      }
    } /* if (xpath->instance != NULL) */
  
     * somewhere inside this structure. */
    xmlXPathFreeObject(instance_node_obj);
  
 -  return (0);
 +  return 0;
  } /* }}} int cx_handle_instance_xpath */
  
  static int cx_handle_base_xpath(char const *plugin_instance, /* {{{ */
    /* free up the allocated memory */
    xmlXPathFreeObject(base_node_obj);
  
 -  return (0);
 +  return 0;
  } /* }}} cx_handle_base_xpath */
  
  static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
@@@ -565,14 -565,14 +565,14 @@@ static int cx_parse_stats_xml(xmlChar *
    doc = xmlParseDoc(xml);
    if (doc == NULL) {
      ERROR("curl_xml plugin: Failed to parse the xml document  - %s", xml);
 -    return (-1);
 +    return -1;
    }
  
    xpath_ctx = xmlXPathNewContext(doc);
    if (xpath_ctx == NULL) {
      ERROR("curl_xml plugin: Failed to create the xml context");
      xmlFreeDoc(doc);
 -    return (-1);
 +    return -1;
    }
  
    for (size_t i = 0; i < db->namespaces_num; i++) {
              ns->prefix, ns->url);
        xmlXPathFreeContext(xpath_ctx);
        xmlFreeDoc(doc);
 -      return (status);
 +      return status;
      }
    }
  
@@@ -602,14 -602,16 +602,16 @@@ static int cx_curl_perform(cx_t *db, CU
    long rc;
    char *ptr;
    char *url;
-   url = db->url;
  
    db->buffer_fill = 0;
+   curl_easy_setopt(db->curl, CURLOPT_URL, db->url);
    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);
+           status, db->curl_errbuf, db->url);
 -    return (-1);
 +    return -1;
    }
    if (db->stats != NULL)
      curl_stats_dispatch(db->stats, db->curl, cx_host(db), "curl_xml",
      ERROR(
          "curl_xml plugin: curl_easy_perform failed with response code %ld (%s)",
          rc, url);
 -    return (-1);
 +    return -1;
    }
  
    ptr = db->buffer;
@@@ -640,7 -642,7 +642,7 @@@ static int cx_read(user_data_t *ud) /* 
  
    if ((ud == NULL) || (ud->data == NULL)) {
      ERROR("curl_xml plugin: cx_read: Invalid user data.");
 -    return (-1);
 +    return -1;
    }
  
    db = (cx_t *)ud->data;
@@@ -654,13 -656,13 +656,13 @@@ static int cx_config_add_values(const c
                                  oconfig_item_t *ci) {
    if (ci->values_num < 1) {
      WARNING("curl_xml plugin: `ValuesFrom' needs at least one argument.");
 -    return (-1);
 +    return -1;
    }
  
    for (int i = 0; i < ci->values_num; i++)
      if (ci->values[i].type != OCONFIG_TYPE_STRING) {
        WARNING("curl_xml plugin: `ValuesFrom' needs only string argument.");
 -      return (-1);
 +      return -1;
      }
  
    sfree(xpath->values);
    xpath->values_len = 0;
    xpath->values = malloc(sizeof(cx_values_t) * ci->values_num);
    if (xpath->values == NULL)
 -    return (-1);
 +    return -1;
    xpath->values_len = (size_t)ci->values_num;
  
    /* populate cx_values_t structure */
               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) /* {{{ */
    xpath = calloc(1, sizeof(*xpath));
    if (xpath == NULL) {
      ERROR("curl_xml plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
  
    status = cf_util_get_string(ci, &xpath->path);
    if (status != 0) {
      cx_xpath_free(xpath);
 -    return (status);
 +    return status;
    }
  
    /* error out if xpath->path is an empty string */
      ERROR("curl_xml plugin: invalid xpath. "
            "xpath value can't be an empty string");
      cx_xpath_free(xpath);
 -    return (-1);
 +    return -1;
    }
  
    status = 0;
      if (db->list == NULL) {
        ERROR("curl_xml plugin: list creation failed.");
        cx_xpath_free(xpath);
 -      return (-1);
 +      return -1;
      }
    }
  
    if (name == NULL) {
      ERROR("curl_xml plugin: strdup failed.");
      cx_xpath_free(xpath);
 -    return (-1);
 +    return -1;
    }
  
    le = llentry_create(name, xpath);
      ERROR("curl_xml plugin: llentry_create failed.");
      cx_xpath_free(xpath);
      sfree(name);
 -    return (-1);
 +    return -1;
    }
  
    llist_append(db->list, le);
 -  return (0);
 +  return 0;
  } /* }}} int cx_config_add_xpath */
  
  static int cx_config_add_namespace(cx_t *db, /* {{{ */
        (ci->values[1].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl_xml plugin: The `Namespace' option "
              "needs exactly two string arguments.");
 -    return (EINVAL);
 +    return EINVAL;
    }
  
    ns = realloc(db->namespaces,
                 sizeof(*db->namespaces) * (db->namespaces_num + 1));
    if (ns == NULL) {
      ERROR("curl_xml plugin: realloc failed.");
 -    return (ENOMEM);
 +    return ENOMEM;
    }
    db->namespaces = ns;
    ns = db->namespaces + db->namespaces_num;
      sfree(ns->prefix);
      sfree(ns->url);
      ERROR("curl_xml plugin: strdup failed.");
 -    return (ENOMEM);
 +    return ENOMEM;
    }
  
    db->namespaces_num++;
 -  return (0);
 +  return 0;
  } /* }}} int cx_config_add_namespace */
  
  /* Initialize db->curl */
@@@ -809,7 -811,7 +811,7 @@@ static int cx_init_curl(cx_t *db) /* {{
    db->curl = curl_easy_init();
    if (db->curl == NULL) {
      ERROR("curl_xml plugin: curl_easy_init failed.");
 -    return (-1);
 +    return -1;
    }
  
    curl_easy_setopt(db->curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(db->curl, CURLOPT_WRITEDATA, db);
    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);
  
      db->credentials = malloc(credentials_size);
      if (db->credentials == NULL) {
        ERROR("curl_xml plugin: malloc failed.");
 -      return (-1);
 +      return -1;
      }
  
      ssnprintf(db->credentials, credentials_size, "%s:%s", db->user,
                       (long)CDTIME_T_TO_MS(plugin_get_interval()));
  #endif
  
 -  return (0);
 +  return 0;
  } /* }}} int cx_init_curl */
  
  static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("curl_xml plugin: The `URL' block "
              "needs exactly one string argument.");
 -    return (-1);
 +    return -1;
    }
  
    db = calloc(1, sizeof(*db));
    if (db == NULL) {
      ERROR("curl_xml plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
  
    db->timeout = -1;
      status = cf_util_get_string(ci, &db->url);
      if (status != 0) {
        sfree(db);
 -      return (status);
 +      return status;
      }
    } else {
      ERROR("curl_xml plugin: cx_config: "
            "Invalid key: %s",
            ci->key);
      cx_free(db);
 -    return (-1);
 +    return -1;
    }
  
    /* Fill the `cx_t' structure.. */
      sfree(cb_name);
    } else {
      cx_free(db);
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int cx_config_add_url */
  
  /* }}} End of configuration handling functions */
@@@ -1008,10 -1009,10 +1009,10 @@@ static int cx_config(oconfig_item_t *ci
  
    if ((success == 0) && (errors > 0)) {
      ERROR("curl_xml plugin: All statements failed.");
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int cx_config */
  
  static int cx_init(void) /* {{{ */
    /* Call this while collectd is still single-threaded to avoid
     * initialization issues in libgcrypt. */
    curl_global_init(CURL_GLOBAL_SSL);
 -  return (0);
 +  return 0;
  } /* }}} int cx_init */
  
  void module_register(void) {
    plugin_register_complex_config("curl_xml", cx_config);
    plugin_register_init("curl_xml", cx_init);
  } /* void module_register */
 -
 -/* vim: set sw=2 sts=2 et fdm=marker : */
diff --combined src/nginx.c
@@@ -62,13 -62,13 +62,13 @@@ static size_t nginx_curl_callback(void 
    }
  
    if (len == 0)
 -    return (len);
 +    return len;
  
    memcpy(&nginx_buffer[nginx_buffer_len], buf, len);
    nginx_buffer_len += len;
    nginx_buffer[nginx_buffer_len] = 0;
  
 -  return (len);
 +  return len;
  }
  
  static int config_set(char **var, const char *value) {
    }
  
    if ((*var = strdup(value)) == NULL)
 -    return (1);
 +    return 1;
    else
 -    return (0);
 +    return 0;
  }
  
  static int config(const char *key, const char *value) {
    if (strcasecmp(key, "url") == 0)
 -    return (config_set(&url, value));
 +    return config_set(&url, value);
    else if (strcasecmp(key, "user") == 0)
 -    return (config_set(&user, value));
 +    return config_set(&user, value);
    else if (strcasecmp(key, "password") == 0)
 -    return (config_set(&pass, value));
 +    return config_set(&pass, value);
    else if (strcasecmp(key, "verifypeer") == 0)
 -    return (config_set(&verify_peer, value));
 +    return config_set(&verify_peer, value);
    else if (strcasecmp(key, "verifyhost") == 0)
 -    return (config_set(&verify_host, value));
 +    return config_set(&verify_host, value);
    else if (strcasecmp(key, "cacert") == 0)
 -    return (config_set(&cacert, value));
 +    return config_set(&cacert, value);
    else if (strcasecmp(key, "timeout") == 0)
 -    return (config_set(&timeout, value));
 +    return config_set(&timeout, value);
    else
 -    return (-1);
 +    return -1;
  } /* int config */
  
  static int init(void) {
  
    if ((curl = curl_easy_init()) == NULL) {
      ERROR("nginx plugin: curl_easy_init failed.");
 -    return (-1);
 +    return -1;
    }
  
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
                             pass == NULL ? "" : pass);
      if ((status < 0) || ((size_t)status >= sizeof(credentials))) {
        ERROR("nginx plugin: Credentials would have been truncated.");
 -      return (-1);
 +      return -1;
      }
  
      curl_easy_setopt(curl, CURLOPT_USERPWD, credentials);
  #endif
    }
  
-   if (url != NULL) {
-     curl_easy_setopt(curl, CURLOPT_URL, url);
-   }
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
  
    }
  #endif
  
 -  return (0);
 +  return 0;
  } /* void init */
  
  static void submit(const char *type, const char *inst, long long value) {
@@@ -202,14 -198,17 +198,17 @@@ static int nginx_read(void) 
    int fields_num;
  
    if (curl == NULL)
 -    return (-1);
 +    return -1;
    if (url == NULL)
 -    return (-1);
 +    return -1;
  
    nginx_buffer_len = 0;
+   curl_easy_setopt(curl, CURLOPT_URL, url);
    if (curl_easy_perform(curl) != CURLE_OK) {
      WARNING("nginx plugin: curl_easy_perform failed: %s", nginx_curl_error);
 -    return (-1);
 +    return -1;
    }
  
    ptr = nginx_buffer;
  
    nginx_buffer_len = 0;
  
 -  return (0);
 +  return 0;
  } /* int nginx_read */
  
  void module_register(void) {
    plugin_register_init("nginx", init);
    plugin_register_read("nginx", nginx_read);
  } /* void module_register */
 -
 -/*
 - * vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
 - */
diff --combined src/write_http.c
@@@ -78,14 -78,9 +78,14 @@@ struct wh_callback_s 
    cdtime_t send_buffer_init_time;
  
    pthread_mutex_t send_lock;
 +
 +  int data_ttl;
  };
  typedef struct wh_callback_s wh_callback_t;
  
 +static char **http_attrs;
 +static size_t http_attrs_num;
 +
  static void wh_log_http_error(wh_callback_t *cb) {
    if (!cb->log_http_error)
      return;
@@@ -119,6 -114,7 +119,7 @@@ static int wh_post_nolock(wh_callback_
  {
    int status = 0;
  
+   curl_easy_setopt(cb->curl, CURLOPT_URL, cb->location);
    curl_easy_setopt(cb->curl, CURLOPT_POSTFIELDS, data);
    status = curl_easy_perform(cb->curl);
  
            "status %i: %s",
            status, cb->curl_errbuf);
    }
 -  return (status);
 +  return status;
  } /* }}} wh_post_nolock */
  
  static int wh_callback_init(wh_callback_t *cb) /* {{{ */
  {
    if (cb->curl != NULL)
 -    return (0);
 +    return 0;
  
    cb->curl = curl_easy_init();
    if (cb->curl == NULL) {
      ERROR("curl plugin: curl_easy_init failed.");
 -    return (-1);
 +    return -1;
    }
  
    if (cb->low_speed_limit > 0 && cb->low_speed_time > 0) {
    curl_easy_setopt(cb->curl, CURLOPT_HTTPHEADER, cb->headers);
  
    curl_easy_setopt(cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf);
-   curl_easy_setopt(cb->curl, CURLOPT_URL, cb->location);
    curl_easy_setopt(cb->curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(cb->curl, CURLOPT_MAXREDIRS, 50L);
  
      cb->credentials = malloc(credentials_size);
      if (cb->credentials == NULL) {
        ERROR("curl plugin: malloc failed.");
 -      return (-1);
 +      return -1;
      }
  
      ssnprintf(cb->credentials, credentials_size, "%s:%s", cb->user,
  
    wh_reset_buffer(cb);
  
 -  return (0);
 +  return 0;
  } /* }}} int wh_callback_init */
  
  static int wh_flush_nolock(cdtime_t timeout, wh_callback_t *cb) /* {{{ */
  
      now = cdtime();
      if ((cb->send_buffer_init_time + timeout) > now)
 -      return (0);
 +      return 0;
    }
  
    if (cb->format == WH_FORMAT_COMMAND) {
      if (cb->send_buffer_fill == 0) {
        cb->send_buffer_init_time = cdtime();
 -      return (0);
 +      return 0;
      }
  
      status = wh_post_nolock(cb, cb->send_buffer);
    } else if (cb->format == WH_FORMAT_JSON || cb->format == WH_FORMAT_KAIROSDB) {
      if (cb->send_buffer_fill <= 2) {
        cb->send_buffer_init_time = cdtime();
 -      return (0);
 +      return 0;
      }
  
      status = format_json_finalize(cb->send_buffer, &cb->send_buffer_fill,
        ERROR("write_http: wh_flush_nolock: "
              "format_json_finalize failed.");
        wh_reset_buffer(cb);
 -      return (status);
 +      return status;
      }
  
      status = wh_post_nolock(cb, cb->send_buffer);
      ERROR("write_http: wh_flush_nolock: "
            "Unknown format: %i",
            cb->format);
 -    return (-1);
 +    return -1;
    }
  
 -  return (status);
 +  return status;
  } /* }}} wh_flush_nolock */
  
  static int wh_flush(cdtime_t timeout, /* {{{ */
    int status;
  
    if (user_data == NULL)
 -    return (-EINVAL);
 +    return -EINVAL;
  
    cb = user_data->data;
  
    if (wh_callback_init(cb) != 0) {
      ERROR("write_http plugin: wh_callback_init failed.");
      pthread_mutex_unlock(&cb->send_lock);
 -    return (-1);
 +    return -1;
    }
  
    status = wh_flush_nolock(timeout, cb);
    pthread_mutex_unlock(&cb->send_lock);
  
 -  return (status);
 +  return status;
  } /* }}} int wh_flush */
  
  static void wh_callback_free(void *data) /* {{{ */
@@@ -356,7 -351,7 +356,7 @@@ static int wh_write_command(const data_
    status = FORMAT_VL(key, sizeof(key), vl);
    if (status != 0) {
      ERROR("write_http plugin: error with format_name");
 -    return (status);
 +    return status;
    }
    escape_string(key, sizeof(key));
  
    if (status != 0) {
      ERROR("write_http plugin: error with "
            "wh_value_list_to_string");
 -    return (status);
 +    return status;
    }
  
    command_len = (size_t)ssnprintf(command, sizeof(command),
      ERROR("write_http plugin: Command buffer too small: "
            "Need %zu bytes.",
            command_len + 1);
 -    return (-1);
 +    return -1;
    }
  
    pthread_mutex_lock(&cb->send_lock);
    if (wh_callback_init(cb) != 0) {
      ERROR("write_http plugin: wh_callback_init failed.");
      pthread_mutex_unlock(&cb->send_lock);
 -    return (-1);
 +    return -1;
    }
  
    if (command_len >= cb->send_buffer_free) {
      status = wh_flush_nolock(/* timeout = */ 0, cb);
      if (status != 0) {
        pthread_mutex_unlock(&cb->send_lock);
 -      return (status);
 +      return status;
      }
    }
    assert(command_len < cb->send_buffer_free);
    /* Check if we have enough space for this command. */
    pthread_mutex_unlock(&cb->send_lock);
  
 -  return (0);
 +  return 0;
  } /* }}} int wh_write_command */
  
  static int wh_write_json(const data_set_t *ds, const value_list_t *vl, /* {{{ */
    if (wh_callback_init(cb) != 0) {
      ERROR("write_http plugin: wh_callback_init failed.");
      pthread_mutex_unlock(&cb->send_lock);
 -    return (-1);
 +    return -1;
    }
  
    status =
      if (status != 0) {
        wh_reset_buffer(cb);
        pthread_mutex_unlock(&cb->send_lock);
 -      return (status);
 +      return status;
      }
  
      status =
    }
    if (status != 0) {
      pthread_mutex_unlock(&cb->send_lock);
 -    return (status);
 +    return status;
    }
  
    DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%)", cb->location,
    /* Check if we have enough space for this command. */
    pthread_mutex_unlock(&cb->send_lock);
  
 -  return (0);
 +  return 0;
  } /* }}} int wh_write_json */
  
  static int wh_write_kairosdb(const data_set_t *ds,
      if (status != 0) {
        ERROR("write_http plugin: wh_callback_init failed.");
        pthread_mutex_unlock(&cb->send_lock);
 -      return (-1);
 +      return -1;
      }
    }
  
 -  status = format_kairosdb_value_list(cb->send_buffer, &cb->send_buffer_fill,
 -                                      &cb->send_buffer_free, ds, vl,
 -                                      cb->store_rates);
 +  status = format_kairosdb_value_list(
 +      cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
 +      cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
 +      cb->data_ttl);
    if (status == -ENOMEM) {
      status = wh_flush_nolock(/* timeout = */ 0, cb);
      if (status != 0) {
        wh_reset_buffer(cb);
        pthread_mutex_unlock(&cb->send_lock);
 -      return (status);
 +      return status;
      }
  
 -    status = format_kairosdb_value_list(cb->send_buffer, &cb->send_buffer_fill,
 -                                        &cb->send_buffer_free, ds, vl,
 -                                        cb->store_rates);
 +    status = format_kairosdb_value_list(
 +        cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
 +        cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
 +        cb->data_ttl);
    }
    if (status != 0) {
      pthread_mutex_unlock(&cb->send_lock);
 -    return (status);
 +    return status;
    }
  
    DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%)", cb->location,
    /* Check if we have enough space for this command. */
    pthread_mutex_unlock(&cb->send_lock);
  
 -  return (0);
 +  return 0;
  } /* }}} int wh_write_kairosdb */
  
  static int wh_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
    int status;
  
    if (user_data == NULL)
 -    return (-EINVAL);
 +    return -EINVAL;
  
    cb = user_data->data;
    assert(cb->send_metrics);
      status = wh_write_command(ds, vl, cb);
      break;
    }
 -  return (status);
 +  return status;
  } /* }}} int wh_write */
  
  static int wh_notify(notification_t const *n, user_data_t *ud) /* {{{ */
    int status;
  
    if ((ud == NULL) || (ud->data == NULL))
 -    return (EINVAL);
 +    return EINVAL;
  
    cb = ud->data;
    assert(cb->send_notifications);
    if (wh_callback_init(cb) != 0) {
      ERROR("write_http plugin: wh_callback_init failed.");
      pthread_mutex_unlock(&cb->send_lock);
 -    return (-1);
 +    return -1;
    }
  
    status = wh_post_nolock(cb, alert);
    pthread_mutex_unlock(&cb->send_lock);
  
 -  return (status);
 +  return status;
  } /* }}} int wh_notify */
  
  static int config_set_format(wh_callback_t *cb, /* {{{ */
      WARNING("write_http plugin: The `%s' config option "
              "needs exactly one string argument.",
              ci->key);
 -    return (-1);
 +    return -1;
    }
  
    string = ci->values[0].value.string;
      cb->format = WH_FORMAT_KAIROSDB;
    else {
      ERROR("write_http plugin: Invalid format string: %s", string);
 -    return (-1);
 +    return -1;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int config_set_format */
  
  static int wh_config_append_string(const char *name,
    struct curl_slist *temp = NULL;
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
      WARNING("write_http plugin: `%s' needs exactly one string argument.", name);
 -    return (-1);
 +    return -1;
    }
  
    temp = curl_slist_append(*dest, ci->values[0].value.string);
    if (temp == NULL)
 -    return (-1);
 +    return -1;
  
    *dest = temp;
  
 -  return (0);
 +  return 0;
  } /* }}} int wh_config_append_string */
  
  static int wh_config_node(oconfig_item_t *ci) /* {{{ */
    cb = calloc(1, sizeof(*cb));
    if (cb == NULL) {
      ERROR("write_http plugin: calloc failed.");
 -    return (-1);
 +    return -1;
    }
    cb->verify_peer = 1;
    cb->verify_host = 1;
    cb->headers = NULL;
    cb->send_metrics = 1;
    cb->send_notifications = 0;
 +  cb->data_ttl = 0;
  
    pthread_mutex_init(&cb->send_lock, /* attr = */ NULL);
  
        status = cf_util_get_boolean(child, &cb->log_http_error);
      else if (strcasecmp("Header", child->key) == 0)
        status = wh_config_append_string("Header", &cb->headers, child);
 -    else {
 +    else if (strcasecmp("Attribute", child->key) == 0) {
 +      char *key = NULL;
 +      char *val = NULL;
 +
 +      if (child->values_num != 2) {
 +        WARNING("write_http plugin: Attribute need both a key and a value.");
 +        break;
 +      }
 +      if (child->values[0].type != OCONFIG_TYPE_STRING ||
 +          child->values[1].type != OCONFIG_TYPE_STRING) {
 +        WARNING("write_http plugin: Attribute needs string arguments.");
 +        break;
 +      }
 +      if ((key = strdup(child->values[0].value.string)) == NULL) {
 +        WARNING("cannot allocate memory for attribute key.");
 +        break;
 +      }
 +      if ((val = strdup(child->values[1].value.string)) == NULL) {
 +        WARNING("cannot allocate memory for attribute value.");
 +        sfree(key);
 +        break;
 +      }
 +      strarray_add(&http_attrs, &http_attrs_num, key);
 +      strarray_add(&http_attrs, &http_attrs_num, val);
 +      DEBUG("write_http plugin: got attribute: %s => %s", key, val);
 +      sfree(key);
 +      sfree(val);
 +    } else if (strcasecmp("TTL", child->key) == 0) {
 +      status = cf_util_get_int(child, &cb->data_ttl);
 +    } else {
        ERROR("write_http plugin: Invalid configuration "
              "option: %s.",
              child->key);
  
    if (status != 0) {
      wh_callback_free(cb);
 -    return (status);
 +    return status;
    }
  
    if (cb->location == NULL) {
      ERROR("write_http plugin: no URL defined for instance '%s'", cb->name);
      wh_callback_free(cb);
 -    return (-1);
 +    return -1;
    }
  
    if (!cb->send_metrics && !cb->send_notifications) {
            "are enabled for \"%s\".",
            cb->name);
      wh_callback_free(cb);
 -    return (-1);
 +    return -1;
    }
  
    if (cb->low_speed_limit > 0)
    if (cb->send_buffer == NULL) {
      ERROR("write_http plugin: malloc(%zu) failed.", cb->send_buffer_size);
      wh_callback_free(cb);
 -    return (-1);
 +    return -1;
    }
    /* Nulls the buffer and sets ..._free and ..._fill. */
    wh_reset_buffer(cb);
      user_data.free_func = NULL;
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int wh_config_node */
  
  static int wh_config(oconfig_item_t *ci) /* {{{ */
      }
    }
  
 -  return (0);
 +  return 0;
  } /* }}} int wh_config */
  
  static int wh_init(void) /* {{{ */
    /* Call this while collectd is still single-threaded to avoid
     * initialization issues in libgcrypt. */
    curl_global_init(CURL_GLOBAL_SSL);
 -  return (0);
 +  return 0;
  } /* }}} int wh_init */
  
  void module_register(void) /* {{{ */
    plugin_register_complex_config("write_http", wh_config);
    plugin_register_init("write_http", wh_init);
  } /* }}} void module_register */
 -
 -/* vim: set fdm=marker sw=8 ts=8 tw=78 et : */