Merge branch 'collectd-5.4' into collectd-5.5
authorFlorian Forster <octo@collectd.org>
Thu, 18 Jun 2015 11:05:14 +0000 (13:05 +0200)
committerFlorian Forster <octo@collectd.org>
Thu, 18 Jun 2015 11:05:14 +0000 (13:05 +0200)
17 files changed:
1  2 
src/amqp.c
src/curl_json.c
src/daemon/filter_chain.c
src/daemon/utils_avltree.c
src/email.c
src/exec.c
src/gmond.c
src/libcollectdclient/client.c
src/modbus.c
src/network.c
src/ntpd.c
src/openvpn.c
src/ping.c
src/powerdns.c
src/snmp.c
src/tail.c
src/threshold.c

diff --combined src/amqp.c
@@@ -23,7 -23,7 +23,7 @@@
   *
   * Authors:
   *   Sebastien Pahl <sebastien.pahl at dotcloud.com>
 - *   Florian Forster <octo at verplant.org>
 + *   Florian Forster <octo at collectd.org>
   **/
  
  #include "collectd.h"
@@@ -80,9 -80,6 +80,9 @@@ struct camqp_config_
      char   *exchange;
      char   *routing_key;
  
 +    /* Number of seconds to wait before connection is retried */
 +    int     connection_retry_delay;
 +
      /* publish only */
      uint8_t delivery_mode;
      _Bool   store_rates;
@@@ -96,8 -93,6 +96,8 @@@
      /* subscribe only */
      char   *exchange_type;
      char   *queue;
 +    _Bool   queue_durable;
 +    _Bool   queue_auto_delete;
  
      amqp_connection_state_t connection;
      pthread_mutex_t lock;
@@@ -337,9 -332,9 +337,9 @@@ static int camqp_setup_queue (camqp_con
              ? amqp_cstring_bytes (conf->queue)
              : AMQP_EMPTY_BYTES,
              /* passive     = */ 0,
 -            /* durable     = */ 0,
 +            /* durable     = */ conf->queue_durable,
              /* exclusive   = */ 0,
 -            /* auto_delete = */ 1,
 +            /* auto_delete = */ conf->queue_auto_delete,
              /* arguments   = */ AMQP_EMPTY_TABLE);
      if (qd_ret == NULL)
      {
  
  static int camqp_connect (camqp_config_t *conf) /* {{{ */
  {
 +    static time_t last_connect_time = 0;
 +
      amqp_rpc_reply_t reply;
      int status;
  #ifdef HAVE_AMQP_TCP_SOCKET
      if (conf->connection != NULL)
          return (0);
  
 +    time_t now = time(NULL);
 +    if (now < (last_connect_time + conf->connection_retry_delay))
 +    {
 +        DEBUG("amqp plugin: skipping connection retry, "
 +            "ConnectionRetryDelay: %d", conf->connection_retry_delay);
 +        return(1);
 +    }
 +    else
 +    {
 +        DEBUG ("amqp plugin: retrying connection");
 +        last_connect_time = now;
 +    }
 +
      conf->connection = amqp_new_connection ();
      if (conf->connection == NULL)
      {
@@@ -716,7 -696,7 +716,7 @@@ static void *camqp_subscribe_thread (vo
              continue;
          }
  
-         status = camqp_read_header (conf);
+         camqp_read_header (conf);
  
          amqp_maybe_release_buffers (conf->connection);
      } /* while (subscriber_threads_running) */
@@@ -811,7 -791,7 +811,7 @@@ static int camqp_write (const data_set_
  {
      camqp_config_t *conf = user_data->data;
      char routing_key[6 * DATA_MAX_NAME_LEN];
 -    char buffer[4096];
 +    char buffer[8192];
      int status;
  
      if ((ds == NULL) || (vl == NULL) || (conf == NULL))
@@@ -944,8 -924,6 +944,8 @@@ static int camqp_config_connection (oco
      conf->password = NULL;
      conf->exchange = NULL;
      conf->routing_key = NULL;
 +    conf->connection_retry_delay = 0;
 +
      /* publish only */
      conf->delivery_mode = CAMQP_DM_VOLATILE;
      conf->store_rates = 0;
      /* subscribe only */
      conf->exchange_type = NULL;
      conf->queue = NULL;
 +    conf->queue_durable = 0;
 +    conf->queue_auto_delete = 1;
      /* general */
      conf->connection = NULL;
      pthread_mutex_init (&conf->lock, /* attr = */ NULL);
              status = cf_util_get_string (child, &conf->exchange_type);
          else if ((strcasecmp ("Queue", child->key) == 0) && !publish)
              status = cf_util_get_string (child, &conf->queue);
 +        else if ((strcasecmp ("QueueDurable", child->key) == 0) && !publish)
 +            status = cf_util_get_boolean (child, &conf->queue_durable);
 +        else if ((strcasecmp ("QueueAutoDelete", child->key) == 0) && !publish)
 +            status = cf_util_get_boolean (child, &conf->queue_auto_delete);
          else if (strcasecmp ("RoutingKey", child->key) == 0)
              status = cf_util_get_string (child, &conf->routing_key);
          else if ((strcasecmp ("Persistent", child->key) == 0) && publish)
              conf->escape_char = tmp_buff[0];
              sfree (tmp_buff);
          }
 +        else if (strcasecmp ("ConnectionRetryDelay", child->key) == 0)
 +            status = cf_util_get_int (child, &conf->connection_retry_delay);
          else
              WARNING ("amqp plugin: Ignoring unknown "
                      "configuration option \"%s\".", child->key);
diff --combined src/curl_json.c
@@@ -71,14 -71,11 +71,14 @@@ struct cj_s /* {{{ *
    char *user;
    char *pass;
    char *credentials;
 +  _Bool digest;
    _Bool verify_peer;
    _Bool verify_host;
    char *cacert;
    struct curl_slist *headers;
    char *post_body;
 +  cdtime_t interval;
 +  int timeout;
  
    CURL *curl;
    char curl_errbuf[CURL_ERROR_SIZE];
@@@ -232,7 -229,7 +232,7 @@@ static int cj_cb_number (void *ctx
    buffer[sizeof (buffer) - 1] = 0;
  
    if ((key == NULL) || !CJ_IS_KEY (key)) {
 -    if (key != NULL)
 +    if (key != NULL && !db->state[db->depth].in_array/*can be inhomogeneous*/)
        NOTICE ("curl_json plugin: Found \"%s\", but the configuration expects"
                " a map.", buffer);
      cj_cb_inc_array_index (ctx, /* update_key = */ 1);
@@@ -552,7 -549,6 +552,6 @@@ static int cj_config_add_key (cj_t *db
        db->tree = cj_avl_create();
  
      tree = db->tree;
-     name = key->path;
      ptr = key->path;
      if (*ptr == '/')
        ++ptr;
@@@ -606,19 -602,13 +605,19 @@@ static int cj_init_curl (cj_t *db) /* {
    curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cj_curl_callback);
    curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
 -  curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
 -                    PACKAGE_NAME"/"PACKAGE_VERSION);
 +  curl_easy_setopt (db->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
    curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
    curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
 +  curl_easy_setopt (db->curl, CURLOPT_FOLLOWLOCATION, 1L);
 +  curl_easy_setopt (db->curl, CURLOPT_MAXREDIRS, 50L);
  
    if (db->user != NULL)
    {
 +#ifdef HAVE_CURLOPT_USERNAME
 +    curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
 +    curl_easy_setopt (db->curl, CURLOPT_PASSWORD,
 +        (db->pass == NULL) ? "" : db->pass);
 +#else
      size_t credentials_size;
  
      credentials_size = strlen (db->user) + 2;
      ssnprintf (db->credentials, credentials_size, "%s:%s",
                 db->user, (db->pass == NULL) ? "" : db->pass);
      curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
 +#endif
 +
 +    if (db->digest)
 +      curl_easy_setopt (db->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
    }
  
    curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, (long) db->verify_peer);
    if (db->post_body != NULL)
      curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
  
 +#ifdef HAVE_CURLOPT_TIMEOUT_MS
 +  if (db->timeout >= 0)
 +    curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS, (long) db->timeout);
 +  else if (db->interval > 0)
 +    curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS,
 +        CDTIME_T_TO_MS(db->timeout));
 +  else
 +    curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS,
 +        CDTIME_T_TO_MS(plugin_get_interval()));
 +#endif
 +
    return (0);
  } /* }}} int cj_init_curl */
  
@@@ -687,8 -662,6 +686,8 @@@ static int cj_config_add_url (oconfig_i
    }
    memset (db, 0, sizeof (*db));
  
 +  db->timeout = -1;
 +
    if (strcasecmp ("URL", ci->key) == 0)
      status = cf_util_get_string (ci, &db->url);
    else if (strcasecmp ("Sock", ci->key) == 0)
        status = cf_util_get_string (child, &db->user);
      else if (db->url && strcasecmp ("Password", child->key) == 0)
        status = cf_util_get_string (child, &db->pass);
 +    else if (strcasecmp ("Digest", child->key) == 0)
 +      status = cf_util_get_boolean (child, &db->digest);
      else if (db->url && strcasecmp ("VerifyPeer", child->key) == 0)
        status = cf_util_get_boolean (child, &db->verify_peer);
      else if (db->url && strcasecmp ("VerifyHost", child->key) == 0)
        status = cf_util_get_string (child, &db->post_body);
      else if (strcasecmp ("Key", child->key) == 0)
        status = cj_config_add_key (db, child);
 +    else if (strcasecmp ("Interval", child->key) == 0)
 +      status = cf_util_get_cdtime(child, &db->interval);
 +    else if (strcasecmp ("Timeout", child->key) == 0)
 +      status = cf_util_get_int (child, &db->timeout);
      else
      {
        WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
    {
      user_data_t ud;
      char *cb_name;
 +    struct timespec interval = { 0, 0 };
 +
 +    CDTIME_T_TO_TIMESPEC (db->interval, &interval);
  
      if (db->instance == NULL)
        db->instance = strdup("default");
      cb_name = ssnprintf_alloc ("curl_json-%s-%s",
                 db->instance, db->url ? db->url : db->sock);
  
 -    plugin_register_complex_read (/* group = */ "curl_json", cb_name, cj_read,
 -                                  /* interval = */ NULL, &ud);
 +    plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
 +                                  /* interval = */ (db->interval > 0) ? &interval : NULL,
 +                                  &ud);
      sfree (cb_name);
    }
    else
@@@ -853,10 -816,11 +852,10 @@@ static void cj_submit (cj_t *db, cj_key
  
    if (key->instance == NULL)
    {
 -    if ((db->depth == 0) || (strcmp ("", db->state[db->depth-1].name) == 0))
 -      sstrncpy (vl.type_instance, db->state[db->depth].name, sizeof (vl.type_instance));
 -    else
 -      ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
 -          db->state[db->depth-1].name, db->state[db->depth].name);
 +    int i, len = 0;
 +    for (i = 0; i < db->depth; i++)
 +      len += ssnprintf(vl.type_instance+len, sizeof(vl.type_instance)-len,
 +                       i ? "-%s" : "%s", db->state[i+1].name);
    }
    else
      sstrncpy (vl.type_instance, key->instance, sizeof (vl.type_instance));
    sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance));
    sstrncpy (vl.type, key->type, sizeof (vl.type));
  
 +  if (db->interval > 0)
 +    vl.interval = db->interval;
 +
    plugin_dispatch_values (&vl);
  } /* }}} int cj_submit */
  
index 0fd4a73,0000000..5042913
mode 100644,000000..100644
--- /dev/null
@@@ -1,1083 -1,0 +1,1081 @@@
-     status = 0;
 +/**
 + * collectd - src/filter_chain.c
 + * Copyright (C) 2008-2010  Florian octo Forster
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
 + *
 + * Authors:
 + *   Florian octo Forster <octo at collectd.org>
 + **/
 +
 +#include "collectd.h"
 +#include "configfile.h"
 +#include "plugin.h"
 +#include "utils_complain.h"
 +#include "common.h"
 +#include "filter_chain.h"
 +
 +/*
 + * Data types
 + */
 +/* List of matches, used in fc_rule_t and for the global `match_list_head'
 + * variable. */
 +struct fc_match_s;
 +typedef struct fc_match_s fc_match_t; /* {{{ */
 +struct fc_match_s
 +{
 +  char name[DATA_MAX_NAME_LEN];
 +  match_proc_t proc;
 +  void *user_data;
 +  fc_match_t *next;
 +}; /* }}} */
 +
 +/* List of targets, used in fc_rule_t and for the global `target_list_head'
 + * variable. */
 +struct fc_target_s;
 +typedef struct fc_target_s fc_target_t; /* {{{ */
 +struct fc_target_s
 +{
 +  char name[DATA_MAX_NAME_LEN];
 +  void *user_data;
 +  target_proc_t proc;
 +  fc_target_t *next;
 +}; /* }}} */
 +
 +/* List of rules, used in fc_chain_t */
 +struct fc_rule_s;
 +typedef struct fc_rule_s fc_rule_t; /* {{{ */
 +struct fc_rule_s
 +{
 +  char name[DATA_MAX_NAME_LEN];
 +  fc_match_t  *matches;
 +  fc_target_t *targets;
 +  fc_rule_t *next;
 +}; /* }}} */
 +
 +/* List of chains, used for `chain_list_head' */
 +struct fc_chain_s /* {{{ */
 +{
 +  char name[DATA_MAX_NAME_LEN];
 +  fc_rule_t   *rules;
 +  fc_target_t *targets;
 +  fc_chain_t  *next;
 +}; /* }}} */
 +
 +/* Writer configuration. */
 +struct fc_writer_s;
 +typedef struct fc_writer_s fc_writer_t; /* {{{ */
 +struct fc_writer_s
 +{
 +  char *plugin;
 +  c_complain_t complaint;
 +}; /* }}} */
 +
 +/*
 + * Global variables
 + */
 +static fc_match_t  *match_list_head;
 +static fc_target_t *target_list_head;
 +static fc_chain_t  *chain_list_head;
 +
 +/*
 + * Private functions
 + */
 +static void fc_free_matches (fc_match_t *m) /* {{{ */
 +{
 +  if (m == NULL)
 +    return;
 +
 +  if (m->proc.destroy != NULL)
 +    (*m->proc.destroy) (&m->user_data);
 +  else if (m->user_data != NULL)
 +  {
 +    ERROR ("Filter subsystem: fc_free_matches: There is user data, but no "
 +        "destroy functions has been specified. "
 +        "Memory will probably be lost!");
 +  }
 +
 +  if (m->next != NULL)
 +    fc_free_matches (m->next);
 +
 +  free (m);
 +} /* }}} void fc_free_matches */
 +
 +static void fc_free_targets (fc_target_t *t) /* {{{ */
 +{
 +  if (t == NULL)
 +    return;
 +
 +  if (t->proc.destroy != NULL)
 +    (*t->proc.destroy) (&t->user_data);
 +  else if (t->user_data != NULL)
 +  {
 +    ERROR ("Filter subsystem: fc_free_targets: There is user data, but no "
 +        "destroy functions has been specified. "
 +        "Memory will probably be lost!");
 +  }
 +
 +  if (t->next != NULL)
 +    fc_free_targets (t->next);
 +
 +  free (t);
 +} /* }}} void fc_free_targets */
 +
 +static void fc_free_rules (fc_rule_t *r) /* {{{ */
 +{
 +  if (r == NULL)
 +    return;
 +
 +  fc_free_matches (r->matches);
 +  fc_free_targets (r->targets);
 +
 +  if (r->next != NULL)
 +    fc_free_rules (r->next);
 +
 +  free (r);
 +} /* }}} void fc_free_rules */
 +
 +static void fc_free_chains (fc_chain_t *c) /* {{{ */
 +{
 +  if (c == NULL)
 +    return;
 +
 +  fc_free_rules (c->rules);
 +  fc_free_targets (c->targets);
 +
 +  if (c->next != NULL)
 +    fc_free_chains (c->next);
 +
 +  free (c);
 +} /* }}} void fc_free_chains */
 +
 +static char *fc_strdup (const char *orig) /* {{{ */
 +{
 +  size_t sz;
 +  char *dest;
 +
 +  if (orig == NULL)
 +    return (NULL);
 +
 +  sz = strlen (orig) + 1;
 +  dest = (char *) malloc (sz);
 +  if (dest == NULL)
 +    return (NULL);
 +
 +  memcpy (dest, orig, sz);
 +
 +  return (dest);
 +} /* }}} char *fc_strdup */
 +
 +/*
 + * Configuration.
 + *
 + * The configuration looks somewhat like this:
 + *
 + *  <Chain "PreCache">
 + *    <Rule>
 + *      <Match "regex">
 + *        Plugin "^mysql$"
 + *        Type "^mysql_command$"
 + *        TypeInstance "^show_"
 + *      </Match>
 + *      <Target "drop">
 + *      </Target>
 + *    </Rule>
 + *
 + *    <Target "write">
 + *      Plugin "rrdtool"
 + *    </Target>
 + *  </Chain>
 + */
 +static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */
 +    oconfig_item_t *ci)
 +{
 +  fc_match_t *m;
 +  fc_match_t *ptr;
 +  int status;
 +
 +  if ((ci->values_num != 1)
 +      || (ci->values[0].type != OCONFIG_TYPE_STRING))
 +  {
 +    WARNING ("Filter subsystem: `Match' blocks require "
 +        "exactly one string argument.");
 +    return (-1);
 +  }
 +
 +  ptr = match_list_head;
 +  while (ptr != NULL)
 +  {
 +    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
 +      break;
 +    ptr = ptr->next;
 +  }
 +
 +  if (ptr == NULL)
 +  {
 +    WARNING ("Filter subsystem: Cannot find a \"%s\" match. "
 +        "Did you load the appropriate plugin?",
 +        ci->values[0].value.string);
 +    return (-1);
 +  }
 +
 +  m = (fc_match_t *) malloc (sizeof (*m));
 +  if (m == NULL)
 +  {
 +    ERROR ("fc_config_add_match: malloc failed.");
 +    return (-1);
 +  }
 +  memset (m, 0, sizeof (*m));
 +
 +  sstrncpy (m->name, ptr->name, sizeof (m->name));
 +  memcpy (&m->proc, &ptr->proc, sizeof (m->proc));
 +  m->user_data = NULL;
 +  m->next = NULL;
 +
 +  if (m->proc.create != NULL)
 +  {
 +    status = (*m->proc.create) (ci, &m->user_data);
 +    if (status != 0)
 +    {
 +      WARNING ("Filter subsystem: Failed to create a %s match.",
 +          m->name);
 +      fc_free_matches (m);
 +      return (-1);
 +    }
 +  }
 +
 +  if (*matches_head != NULL)
 +  {
 +    ptr = *matches_head;
 +    while (ptr->next != NULL)
 +      ptr = ptr->next;
 +
 +    ptr->next = m;
 +  }
 +  else
 +  {
 +    *matches_head = m;
 +  }
 +
 +  return (0);
 +} /* }}} int fc_config_add_match */
 +
 +static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */
 +    oconfig_item_t *ci)
 +{
 +  fc_target_t *t;
 +  fc_target_t *ptr;
 +  int status;
 +
 +  if ((ci->values_num != 1)
 +      || (ci->values[0].type != OCONFIG_TYPE_STRING))
 +  {
 +    WARNING ("Filter subsystem: `Target' blocks require "
 +        "exactly one string argument.");
 +    return (-1);
 +  }
 +
 +  ptr = target_list_head;
 +  while (ptr != NULL)
 +  {
 +    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
 +      break;
 +    ptr = ptr->next;
 +  }
 +
 +  if (ptr == NULL)
 +  {
 +    WARNING ("Filter subsystem: Cannot find a \"%s\" target. "
 +        "Did you load the appropriate plugin?",
 +        ci->values[0].value.string);
 +    return (-1);
 +  }
 +
 +  t = (fc_target_t *) malloc (sizeof (*t));
 +  if (t == NULL)
 +  {
 +    ERROR ("fc_config_add_target: malloc failed.");
 +    return (-1);
 +  }
 +  memset (t, 0, sizeof (*t));
 +
 +  sstrncpy (t->name, ptr->name, sizeof (t->name));
 +  memcpy (&t->proc, &ptr->proc, sizeof (t->proc));
 +  t->user_data = NULL;
 +  t->next = NULL;
 +
 +  if (t->proc.create != NULL)
 +  {
 +    status = (*t->proc.create) (ci, &t->user_data);
 +    if (status != 0)
 +    {
 +      WARNING ("Filter subsystem: Failed to create a %s target.",
 +          t->name);
 +      fc_free_targets (t);
 +      return (-1);
 +    }
 +  }
 +  else
 +  {
 +    t->user_data = NULL;
 +  }
 +  
 +  if (*targets_head != NULL)
 +  {
 +    ptr = *targets_head;
 +    while (ptr->next != NULL)
 +      ptr = ptr->next;
 +
 +    ptr->next = t;
 +  }
 +  else
 +  {
 +    *targets_head = t;
 +  }
 +
 +  return (0);
 +} /* }}} int fc_config_add_target */
 +
 +static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
 +    oconfig_item_t *ci)
 +{
 +  fc_rule_t *rule;
 +  char rule_name[2*DATA_MAX_NAME_LEN] = "Unnamed rule";
 +  int status = 0;
 +  int i;
 +
 +  if (ci->values_num > 1)
 +  {
 +    WARNING ("Filter subsystem: `Rule' blocks have at most one argument.");
 +    return (-1);
 +  }
 +  else if ((ci->values_num == 1)
 +      && (ci->values[0].type != OCONFIG_TYPE_STRING))
 +  {
 +    WARNING ("Filter subsystem: `Rule' blocks expect one string argument "
 +        "or no argument at all.");
 +    return (-1);
 +  }
 +
 +  rule = (fc_rule_t *) malloc (sizeof (*rule));
 +  if (rule == NULL)
 +  {
 +    ERROR ("fc_config_add_rule: malloc failed.");
 +    return (-1);
 +  }
 +  memset (rule, 0, sizeof (*rule));
 +  rule->next = NULL;
 +
 +  if (ci->values_num == 1)
 +  {
 +    sstrncpy (rule->name, ci->values[0].value.string, sizeof (rule->name));
 +    ssnprintf (rule_name, sizeof (rule_name), "Rule \"%s\"",
 +        ci->values[0].value.string);
 +  }
 +
 +  for (i = 0; i < ci->children_num; i++)
 +  {
 +    oconfig_item_t *option = ci->children + i;
-     status = 0;
 +
 +    if (strcasecmp ("Match", option->key) == 0)
 +      status = fc_config_add_match (&rule->matches, option);
 +    else if (strcasecmp ("Target", option->key) == 0)
 +      status = fc_config_add_target (&rule->targets, option);
 +    else
 +    {
 +      WARNING ("Filter subsystem: %s: Option `%s' not allowed "
 +          "inside a <Rule> block.", rule_name, option->key);
 +      status = -1;
 +    }
 +
 +    if (status != 0)
 +      break;
 +  } /* for (ci->children) */
 +
 +  /* Additional sanity checking. */
 +  while (status == 0)
 +  {
 +    if (rule->targets == NULL)
 +    {
 +      WARNING ("Filter subsystem: %s: No target has been specified.",
 +          rule_name);
 +      status = -1;
 +      break;
 +    }
 +
 +    break;
 +  } /* while (status == 0) */
 +
 +  if (status != 0)
 +  {
 +    fc_free_rules (rule);
 +    return (-1);
 +  }
 +
 +  if (chain->rules != NULL)
 +  {
 +    fc_rule_t *ptr;
 +
 +    ptr = chain->rules;
 +    while (ptr->next != NULL)
 +      ptr = ptr->next;
 +
 +    ptr->next = rule;
 +  }
 +  else
 +  {
 +    chain->rules = rule;
 +  }
 +
 +  return (0);
 +} /* }}} int fc_config_add_rule */
 +
 +static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
 +{
 +  fc_chain_t *chain = NULL;
 +  int status = 0;
 +  int i;
 +  int new_chain = 1;
 +
 +  if ((ci->values_num != 1)
 +      || (ci->values[0].type != OCONFIG_TYPE_STRING))
 +  {
 +    WARNING ("Filter subsystem: <Chain> blocks require exactly one "
 +        "string argument.");
 +    return (-1);
 +  }
 +
 +  if (chain_list_head != NULL)
 +  {
 +    if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL)
 +      new_chain = 0;
 +  }
 +
 +  if (chain == NULL)
 +  {
 +    chain = (fc_chain_t *) malloc (sizeof (*chain));
 +    if (chain == NULL)
 +    {
 +      ERROR ("fc_config_add_chain: malloc failed.");
 +      return (-1);
 +    }
 +    memset (chain, 0, sizeof (*chain));
 +    sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
 +    chain->rules = NULL;
 +    chain->targets = NULL;
 +    chain->next = NULL;
 +  }
 +
 +  for (i = 0; i < ci->children_num; i++)
 +  {
 +    oconfig_item_t *option = ci->children + i;
 +
 +    if (strcasecmp ("Rule", option->key) == 0)
 +      status = fc_config_add_rule (chain, option);
 +    else if (strcasecmp ("Target", option->key) == 0)
 +      status = fc_config_add_target (&chain->targets, option);
 +    else
 +    {
 +      WARNING ("Filter subsystem: Chain %s: Option `%s' not allowed "
 +          "inside a <Chain> block.", chain->name, option->key);
 +      status = -1;
 +    }
 +
 +    if (status != 0)
 +      break;
 +  } /* for (ci->children) */
 +
 +  if (status != 0)
 +  {
 +    fc_free_chains (chain);
 +    return (-1);
 +  }
 +
 +  if (chain_list_head != NULL)
 +  {
 +    if (!new_chain)
 +      return (0);
 +
 +    fc_chain_t *ptr;
 +
 +    ptr = chain_list_head;
 +    while (ptr->next != NULL)
 +      ptr = ptr->next;
 +
 +    ptr->next = chain;
 +  }
 +  else
 +  {
 +    chain_list_head = chain;
 +  }
 +
 +  return (0);
 +} /* }}} int fc_config_add_chain */
 +
 +/*
 + * Built-in target "jump"
 + *
 + * Prefix `bit' like `_b_uilt-_i_n _t_arget'
 + */
 +static int fc_bit_jump_create (const oconfig_item_t *ci, /* {{{ */
 +    void **user_data)
 +{
 +  oconfig_item_t *ci_chain;
 +
 +  if (ci->children_num != 1)
 +  {
 +    ERROR ("Filter subsystem: The built-in target `jump' needs exactly "
 +        "one `Chain' argument!");
 +    return (-1);
 +  }
 +
 +  ci_chain = ci->children;
 +  if (strcasecmp ("Chain", ci_chain->key) != 0)
 +  {
 +    ERROR ("Filter subsystem: The built-in target `jump' does not "
 +        "support the configuration option `%s'.",
 +        ci_chain->key);
 +    return (-1);
 +  }
 +
 +  if ((ci_chain->values_num != 1)
 +      || (ci_chain->values[0].type != OCONFIG_TYPE_STRING))
 +  {
 +    ERROR ("Filter subsystem: Built-in target `jump': The `Chain' option "
 +        "needs exactly one string argument.");
 +    return (-1);
 +  }
 +
 +  *user_data = fc_strdup (ci_chain->values[0].value.string);
 +  if (*user_data == NULL)
 +  {
 +    ERROR ("fc_bit_jump_create: fc_strdup failed.");
 +    return (-1);
 +  }
 +
 +  return (0);
 +} /* }}} int fc_bit_jump_create */
 +
 +static int fc_bit_jump_destroy (void **user_data) /* {{{ */
 +{
 +  if (user_data != NULL)
 +  {
 +    free (*user_data);
 +    *user_data = NULL;
 +  }
 +
 +  return (0);
 +} /* }}} int fc_bit_jump_destroy */
 +
 +static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */
 +    value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
 +    void **user_data)
 +{
 +  char *chain_name;
 +  fc_chain_t *chain;
 +  int status;
 +
 +  chain_name = *user_data;
 +
 +  for (chain = chain_list_head; chain != NULL; chain = chain->next)
 +    if (strcasecmp (chain_name, chain->name) == 0)
 +      break;
 +
 +  if (chain == NULL)
 +  {
 +    ERROR ("Filter subsystem: Built-in target `jump': There is no chain "
 +        "named `%s'.", chain_name);
 +    return (-1);
 +  }
 +
 +  status = fc_process_chain (ds, vl, chain);
 +  if (status < 0)
 +    return (status);
 +  else if (status == FC_TARGET_STOP)
 +    return (FC_TARGET_STOP);
 +  else
 +    return (FC_TARGET_CONTINUE);
 +} /* }}} int fc_bit_jump_invoke */
 +
 +static int fc_bit_stop_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
 +    value_list_t __attribute__((unused)) *vl,
 +    notification_meta_t __attribute__((unused)) **meta,
 +    void __attribute__((unused)) **user_data)
 +{
 +  return (FC_TARGET_STOP);
 +} /* }}} int fc_bit_stop_invoke */
 +
 +static int fc_bit_return_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
 +    value_list_t __attribute__((unused)) *vl,
 +    notification_meta_t __attribute__((unused)) **meta,
 +    void __attribute__((unused)) **user_data)
 +{
 +  return (FC_TARGET_RETURN);
 +} /* }}} int fc_bit_return_invoke */
 +
 +static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
 +    void **user_data)
 +{
 +  int i;
 +
 +  fc_writer_t *plugin_list = NULL;
 +  size_t plugin_list_len = 0;
 +
 +  for (i = 0; i < ci->children_num; i++)
 +  {
 +    oconfig_item_t *child = ci->children + i;
 +    fc_writer_t *temp;
 +    int j;
 +
 +    if (strcasecmp ("Plugin", child->key) != 0)
 +    {
 +      ERROR ("Filter subsystem: The built-in target `write' does not "
 +          "support the configuration option `%s'.",
 +          child->key);
 +      continue;
 +    }
 +
 +    for (j = 0; j < child->values_num; j++)
 +    {
 +      char *plugin;
 +
 +      if (child->values[j].type != OCONFIG_TYPE_STRING)
 +      {
 +        ERROR ("Filter subsystem: Built-in target `write': "
 +            "The `Plugin' option accepts only string arguments.");
 +        continue;
 +      }
 +      plugin = child->values[j].value.string;
 +
 +      temp = (fc_writer_t *) realloc (plugin_list, (plugin_list_len + 2)
 +          * (sizeof (*plugin_list)));
 +      if (temp == NULL)
 +      {
 +        ERROR ("fc_bit_write_create: realloc failed.");
 +        continue;
 +      }
 +      plugin_list = temp;
 +
 +      plugin_list[plugin_list_len].plugin = fc_strdup (plugin);
 +      if (plugin_list[plugin_list_len].plugin == NULL)
 +      {
 +        ERROR ("fc_bit_write_create: fc_strdup failed.");
 +        continue;
 +      }
 +      C_COMPLAIN_INIT (&plugin_list[plugin_list_len].complaint);
 +      plugin_list_len++;
 +      plugin_list[plugin_list_len].plugin = NULL;
 +    } /* for (j = 0; j < child->values_num; j++) */
 +  } /* for (i = 0; i < ci->children_num; i++) */
 +
 +  *user_data = plugin_list;
 +
 +  return (0);
 +} /* }}} int fc_bit_write_create */
 +
 +static int fc_bit_write_destroy (void **user_data) /* {{{ */
 +{
 +  fc_writer_t *plugin_list;
 +  size_t i;
 +
 +  if ((user_data == NULL) || (*user_data == NULL))
 +    return (0);
 +
 +  plugin_list = *user_data;
 +
 +  for (i = 0; plugin_list[i].plugin != NULL; i++)
 +    free (plugin_list[i].plugin);
 +  free (plugin_list);
 +
 +  return (0);
 +} /* }}} int fc_bit_write_destroy */
 +
 +static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
 +    value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
 +    void **user_data)
 +{
 +  fc_writer_t *plugin_list;
 +  int status;
 +
 +  plugin_list = NULL;
 +  if (user_data != NULL)
 +    plugin_list = *user_data;
 +
 +  if ((plugin_list == NULL) || (plugin_list[0].plugin == NULL))
 +  {
 +    static c_complain_t write_complaint = C_COMPLAIN_INIT_STATIC;
 +
 +    status = plugin_write (/* plugin = */ NULL, ds, vl);
 +    if (status == ENOENT)
 +    {
 +      /* in most cases this is a permanent error, so use the complain
 +       * mechanism rather than spamming the logs */
 +      c_complain (LOG_INFO, &write_complaint,
 +          "Filter subsystem: Built-in target `write': Dispatching value to "
 +          "all write plugins failed with status %i (ENOENT). "
 +          "Most likely this means you didn't load any write plugins.",
 +          status);
 +
 +      plugin_log_available_writers ();
 +    }
 +    else if (status != 0)
 +    {
 +      /* often, this is a permanent error (e.g. target system unavailable),
 +       * so use the complain mechanism rather than spamming the logs */
 +      c_complain (LOG_INFO, &write_complaint,
 +          "Filter subsystem: Built-in target `write': Dispatching value to "
 +          "all write plugins failed with status %i.", status);
 +    }
 +    else
 +    {
 +      assert (status == 0);
 +      c_release (LOG_INFO, &write_complaint, "Filter subsystem: "
 +          "Built-in target `write': Some write plugin is back to normal "
 +          "operation. `write' succeeded.");
 +    }
 +  }
 +  else
 +  {
 +    size_t i;
 +
 +    for (i = 0; plugin_list[i].plugin != NULL; i++)
 +    {
 +      status = plugin_write (plugin_list[i].plugin, ds, vl);
 +      if (status != 0)
 +      {
 +        c_complain (LOG_INFO, &plugin_list[i].complaint,
 +            "Filter subsystem: Built-in target `write': Dispatching value to "
 +            "the `%s' plugin failed with status %i.",
 +            plugin_list[i].plugin, status);
 +
 +        plugin_log_available_writers ();
 +      }
 +      else
 +      {
 +        c_release (LOG_INFO, &plugin_list[i].complaint,
 +            "Filter subsystem: Built-in target `write': Plugin `%s' is back "
 +            "to normal operation. `write' succeeded.", plugin_list[i].plugin);
 +      }
 +    } /* for (i = 0; plugin_list[i] != NULL; i++) */
 +  }
 +
 +  return (FC_TARGET_CONTINUE);
 +} /* }}} int fc_bit_write_invoke */
 +
 +static int fc_init_once (void) /* {{{ */
 +{
 +  static int done = 0;
 +  target_proc_t tproc;
 +
 +  if (done != 0)
 +    return (0);
 +
 +  memset (&tproc, 0, sizeof (tproc));
 +  tproc.create  = fc_bit_jump_create;
 +  tproc.destroy = fc_bit_jump_destroy;
 +  tproc.invoke  = fc_bit_jump_invoke;
 +  fc_register_target ("jump", tproc);
 +
 +  memset (&tproc, 0, sizeof (tproc));
 +  tproc.create  = NULL;
 +  tproc.destroy = NULL;
 +  tproc.invoke  = fc_bit_stop_invoke;
 +  fc_register_target ("stop", tproc);
 +
 +  memset (&tproc, 0, sizeof (tproc));
 +  tproc.create  = NULL;
 +  tproc.destroy = NULL;
 +  tproc.invoke  = fc_bit_return_invoke;
 +  fc_register_target ("return", tproc);
 +
 +  memset (&tproc, 0, sizeof (tproc));
 +  tproc.create  = fc_bit_write_create;
 +  tproc.destroy = fc_bit_write_destroy;
 +  tproc.invoke  = fc_bit_write_invoke;
 +  fc_register_target ("write", tproc);
 +
 +  done++;
 +  return (0);
 +} /* }}} int fc_init_once */
 +
 +/*
 + * Public functions
 + */
 +/* Add a match to list of available matches. */
 +int fc_register_match (const char *name, match_proc_t proc) /* {{{ */
 +{
 +  fc_match_t *m;
 +
 +  DEBUG ("fc_register_match (%s);", name);
 +
 +  m = (fc_match_t *) malloc (sizeof (*m));
 +  if (m == NULL)
 +    return (-ENOMEM);
 +  memset (m, 0, sizeof (*m));
 +
 +  sstrncpy (m->name, name, sizeof (m->name));
 +  memcpy (&m->proc, &proc, sizeof (m->proc));
 +  m->next = NULL;
 +
 +  if (match_list_head == NULL)
 +  {
 +    match_list_head = m;
 +  }
 +  else
 +  {
 +    fc_match_t *ptr;
 +
 +    ptr = match_list_head;
 +    while (ptr->next != NULL)
 +      ptr = ptr->next;
 +
 +    ptr->next = m;
 +  }
 +
 +  return (0);
 +} /* }}} int fc_register_match */
 +
 +/* Add a target to list of available targets. */
 +int fc_register_target (const char *name, target_proc_t proc) /* {{{ */
 +{
 +  fc_target_t *t;
 +
 +  DEBUG ("fc_register_target (%s);", name);
 +
 +  t = (fc_target_t *) malloc (sizeof (*t));
 +  if (t == NULL)
 +    return (-ENOMEM);
 +  memset (t, 0, sizeof (*t));
 +
 +  sstrncpy (t->name, name, sizeof (t->name));
 +  memcpy (&t->proc, &proc, sizeof (t->proc));
 +  t->next = NULL;
 +
 +  if (target_list_head == NULL)
 +  {
 +    target_list_head = t;
 +  }
 +  else
 +  {
 +    fc_target_t *ptr;
 +
 +    ptr = target_list_head;
 +    while (ptr->next != NULL)
 +      ptr = ptr->next;
 +
 +    ptr->next = t;
 +  }
 +
 +  return (0);
 +} /* }}} int fc_register_target */
 +
 +fc_chain_t *fc_chain_get_by_name (const char *chain_name) /* {{{ */
 +{
 +  fc_chain_t *chain;
 +
 +  if (chain_name == NULL)
 +    return (NULL);
 +
 +  for (chain = chain_list_head; chain != NULL; chain = chain->next)
 +    if (strcasecmp (chain_name, chain->name) == 0)
 +      return (chain);
 +
 +  return (NULL);
 +} /* }}} int fc_chain_get_by_name */
 +
 +int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */
 +    fc_chain_t *chain)
 +{
 +  fc_rule_t *rule;
 +  fc_target_t *target;
 +  int status;
 +
 +  if (chain == NULL)
 +    return (-1);
 +
 +  DEBUG ("fc_process_chain (chain = %s);", chain->name);
 +
 +  status = FC_TARGET_CONTINUE;
 +  for (rule = chain->rules; rule != NULL; rule = rule->next)
 +  {
 +    fc_match_t *match;
 +
 +    if (rule->name[0] != 0)
 +    {
 +      DEBUG ("fc_process_chain (%s): Testing the `%s' rule.",
 +          chain->name, rule->name);
 +    }
 +
 +    /* N. B.: rule->matches may be NULL. */
 +    for (match = rule->matches; match != NULL; match = match->next)
 +    {
 +      /* FIXME: Pass the meta-data to match targets here (when implemented). */
 +      status = (*match->proc.match) (ds, vl, /* meta = */ NULL,
 +          &match->user_data);
 +      if (status < 0)
 +      {
 +        WARNING ("fc_process_chain (%s): A match failed.", chain->name);
 +        break;
 +      }
 +      else if (status != FC_MATCH_MATCHES)
 +        break;
 +    }
 +
 +    /* for-loop has been aborted: Either error or no match. */
 +    if (match != NULL)
 +    {
 +      status = FC_TARGET_CONTINUE;
 +      continue;
 +    }
 +
 +    if (rule->name[0] != 0)
 +    {
 +      DEBUG ("fc_process_chain (%s): Rule `%s' matches.",
 +          chain->name, rule->name);
 +    }
 +
 +    for (target = rule->targets; target != NULL; target = target->next)
 +    {
 +      /* If we get here, all matches have matched the value. Execute the
 +       * target. */
 +      /* FIXME: Pass the meta-data to match targets here (when implemented). */
 +      status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
 +          &target->user_data);
 +      if (status < 0)
 +      {
 +        WARNING ("fc_process_chain (%s): A target failed.", chain->name);
 +        continue;
 +      }
 +      else if (status == FC_TARGET_CONTINUE)
 +        continue;
 +      else if (status == FC_TARGET_STOP)
 +        break;
 +      else if (status == FC_TARGET_RETURN)
 +        break;
 +      else
 +      {
 +        WARNING ("fc_process_chain (%s): Unknown return value "
 +            "from target `%s': %i",
 +            chain->name, target->name, status);
 +      }
 +    }
 +
 +    if ((status == FC_TARGET_STOP)
 +        || (status == FC_TARGET_RETURN))
 +    {
 +      if (rule->name[0] != 0)
 +      {
 +        DEBUG ("fc_process_chain (%s): Rule `%s' signaled "
 +            "the %s condition.",
 +            chain->name, rule->name,
 +            (status == FC_TARGET_STOP) ? "stop" : "return");
 +      }
 +      break;
 +    }
 +    else
 +    {
 +      status = FC_TARGET_CONTINUE;
 +    }
 +  } /* for (rule) */
 +
 +  if (status == FC_TARGET_STOP)
 +    return (FC_TARGET_STOP);
 +  else if (status == FC_TARGET_RETURN)
 +    return (FC_TARGET_CONTINUE);
 +
 +  /* for-loop has been aborted: A target returned `FC_TARGET_STOP' */
 +  if (rule != NULL)
 +    return (FC_TARGET_CONTINUE);
 +
 +  DEBUG ("fc_process_chain (%s): Executing the default targets.",
 +      chain->name);
 +
 +  status = FC_TARGET_CONTINUE;
 +  for (target = chain->targets; target != NULL; target = target->next)
 +  {
 +    /* If we get here, all matches have matched the value. Execute the
 +     * target. */
 +    /* FIXME: Pass the meta-data to match targets here (when implemented). */
 +    status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
 +        &target->user_data);
 +    if (status < 0)
 +    {
 +      WARNING ("fc_process_chain (%s): The default target failed.",
 +          chain->name);
 +    }
 +    else if (status == FC_TARGET_CONTINUE)
 +      continue;
 +    else if (status == FC_TARGET_STOP)
 +      break;
 +    else if (status == FC_TARGET_RETURN)
 +      break;
 +    else
 +    {
 +      WARNING ("fc_process_chain (%s): Unknown return value "
 +          "from target `%s': %i",
 +          chain->name, target->name, status);
 +    }
 +  }
 +
 +  if ((status == FC_TARGET_STOP)
 +      || (status == FC_TARGET_RETURN))
 +  {
 +    assert (target != NULL);
 +    DEBUG ("fc_process_chain (%s): Default target `%s' signaled "
 +        "the %s condition.",
 +        chain->name, target->name,
 +        (status == FC_TARGET_STOP) ? "stop" : "return");
 +    if (status == FC_TARGET_STOP)
 +      return (FC_TARGET_STOP);
 +    else
 +      return (FC_TARGET_CONTINUE);
 +  }
 +
 +  DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.",
 +      chain->name);
 +
 +  return (FC_TARGET_CONTINUE);
 +} /* }}} int fc_process_chain */
 +
 +/* Iterate over all rules in the chain and execute all targets for which all
 + * matches match. */
 +int fc_default_action (const data_set_t *ds, value_list_t *vl) /* {{{ */
 +{
 +  /* FIXME: Pass the meta-data to match targets here (when implemented). */
 +  return (fc_bit_write_invoke (ds, vl,
 +        /* meta = */ NULL, /* user_data = */ NULL));
 +} /* }}} int fc_default_action */
 +
 +int fc_configure (const oconfig_item_t *ci) /* {{{ */
 +{
 +  fc_init_once ();
 +
 +  if (ci == NULL)
 +    return (-EINVAL);
 +
 +  if (strcasecmp ("Chain", ci->key) == 0)
 +    return (fc_config_add_chain (ci));
 +
 +  WARNING ("Filter subsystem: Unknown top level config option `%s'.",
 +      ci->key);
 +
 +  return (-1);
 +} /* }}} int fc_configure */
 +
 +/* vim: set sw=2 sts=2 et fdm=marker : */
index da793b3,0000000..e251975
mode 100644,000000..100644
--- /dev/null
@@@ -1,738 -1,0 +1,744 @@@
- } /* void rotate_left */
 +/**
 + * collectd - src/utils_avltree.c
 + * Copyright (C) 2006,2007  Florian octo Forster
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
 + *
 + * Authors:
 + *   Florian octo Forster <octo at collectd.org>
 + **/
 +
 +#include "config.h"
 +
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <string.h>
 +#include <assert.h>
 +
 +#include "utils_avltree.h"
 +
 +#define BALANCE(n) ((((n)->left == NULL) ? 0 : (n)->left->height) \
 +              - (((n)->right == NULL) ? 0 : (n)->right->height))
 +
 +/*
 + * private data types
 + */
 +struct c_avl_node_s
 +{
 +      void *key;
 +      void *value;
 +
 +      int height;
 +      struct c_avl_node_s *left;
 +      struct c_avl_node_s *right;
 +      struct c_avl_node_s *parent;
 +};
 +typedef struct c_avl_node_s c_avl_node_t;
 +
 +struct c_avl_tree_s
 +{
 +      c_avl_node_t *root;
 +      int (*compare) (const void *, const void *);
 +      int size;
 +};
 +
 +struct c_avl_iterator_s
 +{
 +      c_avl_tree_t *tree;
 +      c_avl_node_t *node;
 +};
 +
 +/*
 + * private functions
 + */
 +#if 0
 +static void verify_tree (c_avl_node_t *n)
 +{
 +      if (n == NULL)
 +              return;
 +
 +      verify_tree (n->left);
 +      verify_tree (n->right);
 +
 +      assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1));
 +      assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n));
 +} /* void verify_tree */
 +#else
 +# define verify_tree(n) /**/
 +#endif
 +
 +static void free_node (c_avl_node_t *n)
 +{
 +      if (n == NULL)
 +              return;
 +
 +      if (n->left != NULL)
 +              free_node (n->left);
 +      if (n->right != NULL)
 +              free_node (n->right);
 +
 +      free (n);
 +}
 +
 +static int calc_height (c_avl_node_t *n)
 +{
 +      int height_left;
 +      int height_right;
 +
 +      if (n == NULL)
 +              return (0);
 +
 +      height_left  = (n->left == NULL)  ? 0 : n->left->height;
 +      height_right = (n->right == NULL) ? 0 : n->right->height;
 +
 +      return (((height_left > height_right)
 +                              ? height_left
 +                              : height_right) + 1);
 +} /* int calc_height */
 +
 +static c_avl_node_t *search (c_avl_tree_t *t, const void *key)
 +{
 +      c_avl_node_t *n;
 +      int cmp;
 +
 +      n = t->root;
 +      while (n != NULL)
 +      {
 +              cmp = t->compare (key, n->key);
 +              if (cmp == 0)
 +                      return (n);
 +              else if (cmp < 0)
 +                      n = n->left;
 +              else
 +                      n = n->right;
 +      }
 +
 +      return (NULL);
 +}
 +
 +/*         (x)             (y)
 + *        /   \           /   \
 + *     (y)    /\         /\    (x)
 + *    /   \  /_c\  ==>  / a\  /   \
 + *   /\   /\           /____\/\   /\
 + *  / a\ /_b\               /_b\ /_c\
 + * /____\
 + */
 +static c_avl_node_t *rotate_right (c_avl_tree_t *t, c_avl_node_t *x)
 +{
 +      c_avl_node_t *p;
 +      c_avl_node_t *y;
 +      c_avl_node_t *b;
 +
++      assert (x != NULL);
++      assert (x->left != NULL);
++
 +      p = x->parent;
 +      y = x->left;
 +      b = y->right;
 +
 +      x->left = b;
 +      if (b != NULL)
 +              b->parent = x;
 +
 +      x->parent = y;
 +      y->right = x;
 +
 +      y->parent = p;
 +      assert ((p == NULL) || (p->left == x) || (p->right == x));
 +      if (p == NULL)
 +              t->root = y;
 +      else if (p->left == x)
 +              p->left = y;
 +      else
 +              p->right = y;
 +
 +      x->height = calc_height (x);
 +      y->height = calc_height (y);
 +
 +      return (y);
++} /* void rotate_right */
 +
 +/*
 + *    (x)                   (y)
 + *   /   \                 /   \
 + *  /\    (y)           (x)    /\
 + * /_a\  /   \   ==>   /   \  / c\
 + *      /\   /\       /\   /\/____\
 + *     /_b\ / c\     /_a\ /_b\
 + *         /____\
 + */
 +static c_avl_node_t *rotate_left (c_avl_tree_t *t, c_avl_node_t *x)
 +{
 +      c_avl_node_t *p;
 +      c_avl_node_t *y;
 +      c_avl_node_t *b;
 +
++      assert (x != NULL);
++      assert (x->right != NULL);
++
 +      p = x->parent;
 +      y = x->right;
 +      b = y->left;
 +
 +      x->right = b;
 +      if (b != NULL)
 +              b->parent = x;
 +
 +      x->parent = y;
 +      y->left = x;
 +
 +      y->parent = p;
 +      assert ((p == NULL) || (p->left == x) || (p->right == x));
 +      if (p == NULL)
 +              t->root = y;
 +      else if (p->left == x)
 +              p->left = y;
 +      else
 +              p->right = y;
 +
 +      x->height = calc_height (x);
 +      y->height = calc_height (y);
 +
 +      return (y);
 +} /* void rotate_left */
 +
 +static c_avl_node_t *rotate_left_right (c_avl_tree_t *t, c_avl_node_t *x)
 +{
 +      rotate_left (t, x->left);
 +      return (rotate_right (t, x));
 +} /* void rotate_left_right */
 +
 +static c_avl_node_t *rotate_right_left (c_avl_tree_t *t, c_avl_node_t *x)
 +{
 +      rotate_right (t, x->right);
 +      return (rotate_left (t, x));
 +} /* void rotate_right_left */
 +
 +static void rebalance (c_avl_tree_t *t, c_avl_node_t *n)
 +{
 +      int b_top;
 +      int b_bottom;
 +
 +      while (n != NULL)
 +      {
 +              b_top = BALANCE (n);
 +              assert ((b_top >= -2) && (b_top <= 2));
 +
 +              if (b_top == -2)
 +              {
 +                      assert (n->right != NULL);
 +                      b_bottom = BALANCE (n->right);
 +                      assert ((b_bottom >= -1) || (b_bottom <= 1));
 +                      if (b_bottom == 1)
 +                              n = rotate_right_left (t, n);
 +                      else
 +                              n = rotate_left (t, n);
 +              }
 +              else if (b_top == 2)
 +              {
 +                      assert (n->left != NULL);
 +                      b_bottom = BALANCE (n->left);
 +                      assert ((b_bottom >= -1) || (b_bottom <= 1));
 +                      if (b_bottom == -1)
 +                              n = rotate_left_right (t, n);
 +                      else
 +                              n = rotate_right (t, n);
 +              }
 +              else
 +              {
 +                      int height = calc_height (n);
 +                      if (height == n->height)
 +                              break;
 +                      n->height = height;
 +              }
 +
 +              assert (n->height == calc_height (n));
 +
 +              n = n->parent;
 +      } /* while (n != NULL) */
 +} /* void rebalance */
 +
 +static c_avl_node_t *c_avl_node_next (c_avl_node_t *n)
 +{
 +      c_avl_node_t *r; /* return node */
 +
 +      if (n == NULL)
 +      {
 +              return (NULL);
 +      }
 +
 +      /* If we can't descent any further, we have to backtrack to the first
 +       * parent that's bigger than we, i. e. who's _left_ child we are. */
 +      if (n->right == NULL)
 +      {
 +              r = n->parent;
 +              while ((r != NULL) && (r->parent != NULL))
 +              {
 +                      if (r->left == n)
 +                              break;
 +                      n = r;
 +                      r = n->parent;
 +              }
 +
 +              /* n->right == NULL && r == NULL => t is root and has no next
 +               * r->left != n => r->right = n => r->parent == NULL */
 +              if ((r == NULL) || (r->left != n))
 +              {
 +                      assert ((r == NULL) || (r->parent == NULL));
 +                      return (NULL);
 +              }
 +              else
 +              {
 +                      assert (r->left == n);
 +                      return (r);
 +              }
 +      }
 +      else
 +      {
 +              r = n->right;
 +              while (r->left != NULL)
 +                      r = r->left;
 +      }
 +
 +      return (r);
 +} /* c_avl_node_t *c_avl_node_next */
 +
 +static c_avl_node_t *c_avl_node_prev (c_avl_node_t *n)
 +{
 +      c_avl_node_t *r; /* return node */
 +
 +      if (n == NULL)
 +      {
 +              return (NULL);
 +      }
 +
 +      /* If we can't descent any further, we have to backtrack to the first
 +       * parent that's smaller than we, i. e. who's _right_ child we are. */
 +      if (n->left == NULL)
 +      {
 +              r = n->parent;
 +              while ((r != NULL) && (r->parent != NULL))
 +              {
 +                      if (r->right == n)
 +                              break;
 +                      n = r;
 +                      r = n->parent;
 +              }
 +
 +              /* n->left == NULL && r == NULL => t is root and has no next
 +               * r->right != n => r->left = n => r->parent == NULL */
 +              if ((r == NULL) || (r->right != n))
 +              {
 +                      assert ((r == NULL) || (r->parent == NULL));
 +                      return (NULL);
 +              }
 +              else
 +              {
 +                      assert (r->right == n);
 +                      return (r);
 +              }
 +      }
 +      else
 +      {
 +              r = n->left;
 +              while (r->right != NULL)
 +                      r = r->right;
 +      }
 +
 +      return (r);
 +} /* c_avl_node_t *c_avl_node_prev */
 +
 +static int _remove (c_avl_tree_t *t, c_avl_node_t *n)
 +{
 +      assert ((t != NULL) && (n != NULL));
 +
 +      if ((n->left != NULL) && (n->right != NULL))
 +      {
 +              c_avl_node_t *r; /* replacement node */
 +              if (BALANCE (n) > 0) /* left subtree is higher */
 +              {
 +                      assert (n->left != NULL);
 +                      r = c_avl_node_prev (n);
 +                      
 +              }
 +              else /* right subtree is higher */
 +              {
 +                      assert (n->right != NULL);
 +                      r = c_avl_node_next (n);
 +              }
 +
 +              assert ((r->left == NULL) || (r->right == NULL));
 +
 +              /* copy content */
 +              n->key   = r->key;
 +              n->value = r->value;
 +
 +              n = r;
 +      }
 +
 +      assert ((n->left == NULL) || (n->right == NULL));
 +
 +      if ((n->left == NULL) && (n->right == NULL))
 +      {
 +              /* Deleting a leave is easy */
 +              if (n->parent == NULL)
 +              {
 +                      assert (t->root == n);
 +                      t->root = NULL;
 +              }
 +              else
 +              {
 +                      assert ((n->parent->left == n)
 +                                      || (n->parent->right == n));
 +                      if (n->parent->left == n)
 +                              n->parent->left = NULL;
 +                      else
 +                              n->parent->right = NULL;
 +
 +                      rebalance (t, n->parent);
 +              }
 +
 +              free_node (n);
 +      }
 +      else if (n->left == NULL)
 +      {
 +              assert (BALANCE (n) == -1);
 +              assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
 +              if (n->parent == NULL)
 +              {
 +                      assert (t->root == n);
 +                      t->root = n->right;
 +              }
 +              else if (n->parent->left == n)
 +              {
 +                      n->parent->left = n->right;
 +              }
 +              else
 +              {
 +                      n->parent->right = n->right;
 +              }
 +              n->right->parent = n->parent;
 +
 +              if (n->parent != NULL)
 +                      rebalance (t, n->parent);
 +
 +              n->right = NULL;
 +              free_node (n);
 +      }
 +      else if (n->right == NULL)
 +      {
 +              assert (BALANCE (n) == 1);
 +              assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
 +              if (n->parent == NULL)
 +              {
 +                      assert (t->root == n);
 +                      t->root = n->left;
 +              }
 +              else if (n->parent->left == n)
 +              {
 +                      n->parent->left = n->left;
 +              }
 +              else
 +              {
 +                      n->parent->right = n->left;
 +              }
 +              n->left->parent = n->parent;
 +
 +              if (n->parent != NULL)
 +                      rebalance (t, n->parent);
 +
 +              n->left = NULL;
 +              free_node (n);
 +      }
 +      else
 +      {
 +              assert (0);
 +      }
 +
 +      return (0);
 +} /* void *_remove */
 +
 +/*
 + * public functions
 + */
 +c_avl_tree_t *c_avl_create (int (*compare) (const void *, const void *))
 +{
 +      c_avl_tree_t *t;
 +
 +      if (compare == NULL)
 +              return (NULL);
 +
 +      if ((t = (c_avl_tree_t *) malloc (sizeof (c_avl_tree_t))) == NULL)
 +              return (NULL);
 +
 +      t->root = NULL;
 +      t->compare = compare;
 +      t->size = 0;
 +
 +      return (t);
 +}
 +
 +void c_avl_destroy (c_avl_tree_t *t)
 +{
 +      if (t == NULL)
 +              return;
 +      free_node (t->root);
 +      free (t);
 +}
 +
 +int c_avl_insert (c_avl_tree_t *t, void *key, void *value)
 +{
 +      c_avl_node_t *new;
 +      c_avl_node_t *nptr;
 +      int cmp;
 +
 +      if ((new = (c_avl_node_t *) malloc (sizeof (c_avl_node_t))) == NULL)
 +              return (-1);
 +
 +      new->key = key;
 +      new->value = value;
 +      new->height = 1;
 +      new->left = NULL;
 +      new->right = NULL;
 +
 +      if (t->root == NULL)
 +      {
 +              new->parent = NULL;
 +              t->root = new;
 +              t->size = 1;
 +              return (0);
 +      }
 +
 +      nptr = t->root;
 +      while (42)
 +      {
 +              cmp = t->compare (nptr->key, new->key);
 +              if (cmp == 0)
 +              {
 +                      free_node (new);
 +                      return (1);
 +              }
 +              else if (cmp < 0)
 +              {
 +                      /* nptr < new */
 +                      if (nptr->right == NULL)
 +                      {
 +                              nptr->right = new;
 +                              new->parent = nptr;
 +                              rebalance (t, nptr);
 +                              break;
 +                      }
 +                      else
 +                      {
 +                              nptr = nptr->right;
 +                      }
 +              }
 +              else /* if (cmp > 0) */
 +              {
 +                      /* nptr > new */
 +                      if (nptr->left == NULL)
 +                      {
 +                              nptr->left = new;
 +                              new->parent = nptr;
 +                              rebalance (t, nptr);
 +                              break;
 +                      }
 +                      else
 +                      {
 +                              nptr = nptr->left;
 +                      }
 +              }
 +      } /* while (42) */
 +
 +      verify_tree (t->root);
 +      ++t->size;
 +      return (0);
 +} /* int c_avl_insert */
 +
 +int c_avl_remove (c_avl_tree_t *t, const void *key, void **rkey, void **rvalue)
 +{
 +      c_avl_node_t *n;
 +      int status;
 +
 +      assert (t != NULL);
 +
 +      n = search (t, key);
 +      if (n == NULL)
 +              return (-1);
 +
 +      if (rkey != NULL)
 +              *rkey = n->key;
 +      if (rvalue != NULL)
 +              *rvalue = n->value;
 +
 +      status = _remove (t, n);
 +      verify_tree (t->root);
 +      --t->size;
 +      return (status);
 +} /* void *c_avl_remove */
 +
 +int c_avl_get (c_avl_tree_t *t, const void *key, void **value)
 +{
 +      c_avl_node_t *n;
 +
 +      assert (t != NULL);
 +
 +      n = search (t, key);
 +      if (n == NULL)
 +              return (-1);
 +
 +      if (value != NULL)
 +              *value = n->value;
 +
 +      return (0);
 +}
 +
 +int c_avl_pick (c_avl_tree_t *t, void **key, void **value)
 +{
 +      c_avl_node_t *n;
 +      c_avl_node_t *p;
 +
 +      if ((key == NULL) || (value == NULL))
 +              return (-1);
 +      if (t->root == NULL)
 +              return (-1);
 +
 +      n = t->root;
 +      while ((n->left != NULL) || (n->right != NULL))
 +      {
 +              if (n->left == NULL)
 +              {
 +                      n = n->right;
 +                      continue;
 +              }
 +              else if (n->right == NULL)
 +              {
 +                      n = n->left;
 +                      continue;
 +              }
 +
 +              if (n->left->height > n->right->height)
 +                      n = n->left;
 +              else
 +                      n = n->right;
 +      }
 +
 +      p = n->parent;
 +      if (p == NULL)
 +              t->root = NULL;
 +      else if (p->left == n)
 +              p->left = NULL;
 +      else
 +              p->right = NULL;
 +
 +      *key   = n->key;
 +      *value = n->value;
 +
 +      free_node (n);
 +      rebalance (t, p);
 +
 +      return (0);
 +} /* int c_avl_pick */
 +
 +c_avl_iterator_t *c_avl_get_iterator (c_avl_tree_t *t)
 +{
 +      c_avl_iterator_t *iter;
 +
 +      if (t == NULL)
 +              return (NULL);
 +
 +      iter = (c_avl_iterator_t *) malloc (sizeof (c_avl_iterator_t));
 +      if (iter == NULL)
 +              return (NULL);
 +      memset (iter, '\0', sizeof (c_avl_iterator_t));
 +      iter->tree = t;
 +
 +      return (iter);
 +} /* c_avl_iterator_t *c_avl_get_iterator */
 +
 +int c_avl_iterator_next (c_avl_iterator_t *iter, void **key, void **value)
 +{
 +      c_avl_node_t *n;
 +
 +      if ((iter == NULL) || (key == NULL) || (value == NULL))
 +              return (-1);
 +
 +      if (iter->node == NULL)
 +      {
 +              for (n = iter->tree->root; n != NULL; n = n->left)
 +                      if (n->left == NULL)
 +                              break;
 +              iter->node = n;
 +      }
 +      else
 +      {
 +              n = c_avl_node_next (iter->node);
 +      }
 +
 +      if (n == NULL)
 +              return (-1);
 +
 +      iter->node = n;
 +      *key = n->key;
 +      *value = n->value;
 +
 +      return (0);
 +} /* int c_avl_iterator_next */
 +
 +int c_avl_iterator_prev (c_avl_iterator_t *iter, void **key, void **value)
 +{
 +      c_avl_node_t *n;
 +
 +      if ((iter == NULL) || (key == NULL) || (value == NULL))
 +              return (-1);
 +
 +      if (iter->node == NULL)
 +      {
 +              for (n = iter->tree->root; n != NULL; n = n->left)
 +                      if (n->right == NULL)
 +                              break;
 +              iter->node = n;
 +      }
 +      else
 +      {
 +              n = c_avl_node_prev (iter->node);
 +      }
 +
 +      if (n == NULL)
 +              return (-1);
 +
 +      iter->node = n;
 +      *key = n->key;
 +      *value = n->value;
 +
 +      return (0);
 +} /* int c_avl_iterator_prev */
 +
 +void c_avl_iterator_destroy (c_avl_iterator_t *iter)
 +{
 +      free (iter);
 +}
 +
 +int c_avl_size (c_avl_tree_t *t)
 +{
 +      if (t == NULL)
 +              return (0);
 +      return (t->size);
 +}
diff --combined src/email.c
@@@ -2,25 -2,20 +2,25 @@@
   * collectd - src/email.c
   * Copyright (C) 2006-2008  Sebastian Harl
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; only version 2 of the License is applicable.
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
   *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
   *
 - * You should have received a copy of the GNU General Public License along
 - * with this program; if not, write to the Free Software Foundation, Inc.,
 - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
   *
 - * Author:
 + * Authors:
   *   Sebastian Harl <sh at tokkee.org>
   **/
  
@@@ -256,8 -251,6 +256,6 @@@ static void *collect (void *arg
        collector_t *this = (collector_t *)arg;
  
        while (1) {
-               int loop = 1;
                conn_t *connection;
  
                pthread_mutex_lock (&conns_mutex);
                log_debug ("collect: handling connection on fd #%i",
                                fileno (this->socket));
  
-               while (loop) {
+               while (42) {
                        /* 256 bytes ought to be enough for anybody ;-) */
                        char line[256 + 1]; /* line + '\0' */
                        int  len = 0;
  
                        errno = 0;
                        if (NULL == fgets (line, sizeof (line), this->socket)) {
-                               loop = 0;
                                if (0 != errno) {
                                        char errbuf[1024];
                                        log_err ("collect: reading from socket (fd #%i) "
                        else {
                                log_err ("collect: unknown type '%c'", line[0]);
                        }
-               } /* while (loop) */
+               } /* while (42) */
  
                log_debug ("Shutting down connection on fd #%i",
                                fileno (this->socket));
diff --combined src/exec.c
   * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
   *
   * Authors:
 - *   Florian octo Forster <octo at verplant.org>
 + *   Florian octo Forster <octo at collectd.org>
   *   Sebastian Harl <sh at tokkee.org>
   *   Peter Holik <peter at holik.at>
   **/
  
 +#define _DEFAULT_SOURCE
  #define _BSD_SOURCE /* For setgroups */
  
  #include "collectd.h"
@@@ -339,7 -338,7 +339,7 @@@ static void exec_child (program_list_t 
      exit (-1);
    }
  
-   status = execvp (pl->exec, pl->argv);
+   execvp (pl->exec, pl->argv);
  
    ERROR ("exec plugin: Failed to execute ``%s'': %s",
        pl->exec, sstrerror (errno, errbuf, sizeof (errbuf)));
diff --combined src/gmond.c
@@@ -2,23 -2,18 +2,23 @@@
   * collectd - src/gmond.c
   * Copyright (C) 2009,2010  Florian octo Forster
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; only version 2 of the License is applicable.
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
   *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
   *
 - * You should have received a copy of the GNU General Public License along
 - * with this program; if not, write to the Free Software Foundation, Inc.,
 - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
   *
   * Authors:
   *   Florian octo Forster <octo at collectd.org>
@@@ -665,7 -660,7 +665,7 @@@ static int mc_handle_value_msg (Ganglia
      if ((map->ds_type == DS_TYPE_COUNTER)
          || (map->ds_type == DS_TYPE_ABSOLUTE))
        val_copy = value_counter;
-     if (map->ds_type == DS_TYPE_GAUGE)
+     else if (map->ds_type == DS_TYPE_GAUGE)
        val_copy = value_gauge;
      else if (map->ds_type == DS_TYPE_DERIVE)
        val_copy = value_derive;
@@@ -84,7 -84,7 +84,7 @@@
      _b[sizeof (_b) - 1] = 0; \
      SSTRCAT ((d), _b); \
    } while (0)
 -    
 +
  
  #define LCC_SET_ERRSTR(c, ...) do { \
    snprintf ((c)->errbuf, sizeof ((c)->errbuf), __VA_ARGS__); \
@@@ -500,7 -500,6 +500,6 @@@ static int lcc_open_netsocket (lcc_conn
      if (fd < 0)
      {
        status = errno;
-       fd = -1;
        continue;
      }
  
      {
        status = errno;
        close (fd);
-       fd = -1;
        continue;
      }
  
      {
        status = errno;
        close (fd);
-       fd = -1;
        continue;
      }
  
diff --combined src/modbus.c
@@@ -47,7 -47,6 +47,7 @@@
  /*
   * <Data "data_name">
   *   RegisterBase 1234
 + *   RegisterCmd ReadHolding
   *   RegisterType float
   *   Type gauge
   *   Instance "..."
   * <Host "name">
   *   Address "addr"
   *   Port "1234"
 + *   # Or:
 + *   # Device "/dev/ttyUSB0"
 + *   # Baudrate 38400
 + *   # (Assumes 8N1)
   *   Interval 60
   *
   *   <Slave 1>
@@@ -80,21 -75,7 +80,21 @@@ enum mb_register_type_e /* {{{ *
    REG_TYPE_UINT32,
    REG_TYPE_FLOAT
  }; /* }}} */
 +enum mb_mreg_type_e /* {{{ */ 
 +{
 +  MREG_HOLDING,
 +  MREG_INPUT
 +}; /* }}} */
  typedef enum mb_register_type_e mb_register_type_t;
 +typedef enum mb_mreg_type_e mb_mreg_type_t;
 +
 +/* TCP or RTU depending on what is specified in host config block */
 +enum mb_conntype_e /* {{{ */
 +{
 +  MBCONN_TCP,
 +  MBCONN_RTU
 +}; /* }}} */
 +typedef enum mb_conntype_e mb_conntype_t;
  
  struct mb_data_s;
  typedef struct mb_data_s mb_data_t;
@@@ -103,7 -84,6 +103,7 @@@ struct mb_data_s /* {{{ *
    char *name;
    int register_base;
    mb_register_type_t register_type;
 +  mb_mreg_type_t modbus_register_type;
    char type[DATA_MAX_NAME_LEN];
    char instance[DATA_MAX_NAME_LEN];
  
@@@ -121,11 -101,9 +121,11 @@@ typedef struct mb_slave_s mb_slave_t
  struct mb_host_s /* {{{ */
  {
    char host[DATA_MAX_NAME_LEN];
 -  char node[NI_MAXHOST];
 +  char node[NI_MAXHOST];      /* TCP hostname or RTU serial device */
    /* char service[NI_MAXSERV]; */
 -  int port;
 +  int port;                   /* for Modbus/TCP */
 +  int baudrate;                       /* for Modbus/RTU */
 +  mb_conntype_t conntype;
    cdtime_t interval;
  
    mb_slave_t *slaves;
@@@ -315,33 -293,21 +315,33 @@@ static int mb_init_connection (mb_host_
    /* We'll do the error handling ourselves. */
    modbus_set_error_handling (&host->connection, NOP_ON_ERROR);
  
 -  if ((host->port < 1) || (host->port > 65535))
 -    host->port = MODBUS_TCP_DEFAULT_PORT;
 +  if (host->conntype == MBCONN_TCP)
 +  {
 +    if ((host->port < 1) || (host->port > 65535))
 +      host->port = MODBUS_TCP_DEFAULT_PORT;
 +
 +    DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
 +        host->node, host->port);
  
 -  DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
 -      host->node, host->port);
 +    modbus_init_tcp (&host->connection,
 +        /* host = */ host->node,
 +        /* port = */ host->port);
 +  }
 +  else        /* MBCONN_RTU */
 +  {
 +    DEBUG ("Modbus plugin: Trying to connect to \"%s\".", host->node);
  
 -  modbus_init_tcp (&host->connection,
 -      /* host = */ host->node,
 -      /* port = */ host->port);
 +    modbus_init_rtu (&host->connection,
 +       /* device = */ host->node,
 +     /* baudrate = */ host->baudrate,
 +                      'N', 8, 1, 0);
 +  }
  
    status = modbus_connect (&host->connection);
    if (status != 0)
    {
      ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
 -        host->node, host->port, status);
 +        host->node, host->port ? host->port : host->baudrate, status);
      return (status);
    }
  
@@@ -362,32 -328,17 +362,32 @@@ static int mb_init_connection (mb_host_
    if (host->connection != NULL)
      return (0);
  
 -  if ((host->port < 1) || (host->port > 65535))
 -    host->port = MODBUS_TCP_DEFAULT_PORT;
 +  if (host->conntype == MBCONN_TCP)
 +  {
 +    if ((host->port < 1) || (host->port > 65535))
 +      host->port = MODBUS_TCP_DEFAULT_PORT;
  
 -  DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
 -      host->node, host->port);
 +    DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
 +        host->node, host->port);
  
 -  host->connection = modbus_new_tcp (host->node, host->port);
 -  if (host->connection == NULL)
 +    host->connection = modbus_new_tcp (host->node, host->port);
 +    if (host->connection == NULL)
 +    {
 +      ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
 +      return (-1);
 +    }
 +  }
 +  else
    {
 -    ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
 -    return (-1);
 +    DEBUG ("Modbus plugin: Trying to connect to \"%s\", baudrate %i.",
 +        host->node, host->baudrate);
 +
 +    host->connection = modbus_new_rtu (host->node, host->baudrate, 'N', 8, 1);
 +    if (host->connection == NULL)
 +    {
 +      ERROR ("Modbus plugin: Creating new Modbus/RTU object failed.");
 +      return (-1);
 +    }
    }
  
    modbus_set_debug (host->connection, 1);
    if (status != 0)
    {
      ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
 -        host->node, host->port, status);
 +        host->node, host->port ? host->port : host->baudrate, status);
      modbus_free (host->connection);
      host->connection = NULL;
      return (status);
@@@ -463,12 -414,11 +463,11 @@@ static int mb_read_data (mb_host_t *hos
    else
      values_num = 1;
  
-   status = 0;
    if (host->connection == NULL)
    {
      status = EBADF;
    }
 -  else
 +  else if (host->conntype == MBCONN_TCP)
    {
      struct sockaddr sockaddr;
      socklen_t saddrlen = sizeof (sockaddr);
      return (-1);
    }
  #endif
 -
 -  status = modbus_read_registers (host->connection,
 +  if (data->modbus_register_type == MREG_INPUT){
 +    status = modbus_read_input_registers (host->connection,
          /* start_addr = */ data->register_base,
          /* num_registers = */ values_num, /* buffer = */ values);
 +  }
 +  else{
 +    status = modbus_read_registers (host->connection,
 +        /* start_addr = */ data->register_base,
 +        /* num_registers = */ values_num, /* buffer = */ values);
 +  }
    if (status != values_num)
    {
 -    ERROR ("Modbus plugin: modbus_read_registers (%s/%s) failed. status = %i, values_num = %i "
 -        "Giving up.", host->host, host->node, status, values_num);
 +    ERROR ("Modbus plugin: modbus read function (%s/%s) failed. "
 +           " status = %i, values_num = %i. Giving up.",
 +           host->host, host->node, status, values_num);
  #if LEGACY_LIBMODBUS
      modbus_close (&host->connection);
  #else
@@@ -733,7 -676,6 +732,6 @@@ static int mb_config_add_data (oconfig_
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *child = ci->children + i;
-     status = 0;
  
      if (strcasecmp ("Type", child->key) == 0)
        status = cf_util_get_string_buffer (child,
          status = -1;
        }
      }
 +    else if (strcasecmp ("RegisterCmd", child->key) == 0)
 +    {
 +#if LEGACY_LIBMODBUS
 +      ERROR("Modbus plugin: RegisterCmd parameter can not be used "
 +            "with your libmodbus version");
 +#else
 +      char tmp[16];
 +      status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
 +      if (status != 0)
 +        /* do nothing */;
 +      else if (strcasecmp ("ReadHolding", tmp) == 0)
 +        data.modbus_register_type = MREG_HOLDING;
 +      else if (strcasecmp ("ReadInput", tmp) == 0)
 +        data.modbus_register_type = MREG_INPUT;
 +      else
 +      {
 +        ERROR ("Modbus plugin: The modbus_register_type \"%s\" is unknown.",
 +               tmp);
 +        status = -1;
 +      }
 +#endif
 +    }
      else
      {
        ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
@@@ -894,7 -814,6 +892,6 @@@ static int mb_config_add_slave (mb_host
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *child = ci->children + i;
-     status = 0;
  
      if (strcasecmp ("Instance", child->key) == 0)
        status = cf_util_get_string_buffer (child,
@@@ -960,8 -879,6 +957,8 @@@ static int mb_config_add_host (oconfig_
        status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
        if (status == 0)
          status = mb_config_set_host_address (host, buffer);
 +      if (status == 0)
 +        host->conntype = MBCONN_TCP;
      }
      else if (strcasecmp ("Port", child->key) == 0)
      {
        if (host->port <= 0)
          status = -1;
      }
 +    else if (strcasecmp ("Device", child->key) == 0)
 +    {
 +      status = cf_util_get_string_buffer (child, host->node, sizeof (host->node));
 +      if (status == 0)
 +        host->conntype = MBCONN_RTU;
 +    }
 +    else if (strcasecmp ("Baudrate", child->key) == 0)
 +      status = cf_util_get_int(child, &host->baudrate);
      else if (strcasecmp ("Interval", child->key) == 0)
        status = cf_util_get_cdtime (child, &host->interval);
      else if (strcasecmp ("Slave", child->key) == 0)
    } /* for (i = 0; i < ci->children_num; i++) */
  
    assert (host->host[0] != 0);
 -  if (host->host[0] == 0)
 +  if (host->node[0] == 0)
    {
 -    ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
 +    ERROR ("Modbus plugin: Data block \"%s\": No address or device has been specified.",
 +        host->host);
 +    status = -1;
 +  }
 +  if (host->conntype == MBCONN_RTU && !host->baudrate)
 +  {
 +    ERROR ("Modbus plugin: Data block \"%s\": No serial baudrate has been specified.",
 +        host->host);
 +    status = -1;
 +  }
 +  if ((host->conntype == MBCONN_TCP && host->baudrate) ||
 +      (host->conntype == MBCONN_RTU && host->port))
 +  {
 +    ERROR ("Modbus plugin: Data block \"%s\": You've mixed up RTU and TCP options.",
          host->host);
      status = -1;
    }
diff --combined src/network.c
@@@ -22,7 -22,6 +22,7 @@@
   *   Aman Gupta <aman at tmm1.net>
   **/
  
 +#define _DEFAULT_SOURCE
  #define _BSD_SOURCE /* For struct ip_mreq */
  
  #include "collectd.h"
@@@ -120,8 -119,6 +120,8 @@@ struct sockent_clien
        gcry_cipher_hd_t cypher;
        unsigned char password_hash[32];
  #endif
 +      cdtime_t next_resolve_reconnect;
 +      cdtime_t resolve_interval;
  };
  
  struct sockent_server
@@@ -924,19 -921,15 +924,19 @@@ static int parse_part_number (void **re
  } /* int parse_part_number */
  
  static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
 -              char *output, int output_len)
 +              char *output, size_t const output_len)
  {
        char *buffer = *ret_buffer;
        size_t buffer_len = *ret_buffer_len;
  
        uint16_t tmp16;
 -      size_t header_size = 2 * sizeof (uint16_t);
 +      size_t const header_size = 2 * sizeof (uint16_t);
  
        uint16_t pkg_length;
 +      size_t payload_size;
 +
 +      if (output_len <= 0)
 +              return (EINVAL);
  
        if (buffer_len < header_size)
        {
        memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
        buffer += sizeof (tmp16);
        pkg_length = ntohs (tmp16);
 +      payload_size = ((size_t) pkg_length) - header_size;
  
        /* Check that packet fits in the input buffer */
        if (pkg_length > buffer_len)
        /* Check that the package data fits into the output buffer.
         * The previous if-statement ensures that:
         * `pkg_length > header_size' */
 -      if ((output_len < 0)
 -                      || ((size_t) output_len < ((size_t) pkg_length - header_size)))
 +      if (output_len < payload_size)
        {
                WARNING ("network plugin: parse_part_string: "
 -                              "Output buffer too small.");
 +                              "Buffer too small: "
 +                              "Output buffer holds %zu bytes, "
 +                              "which is too small to hold the received "
 +                              "%zu byte string.",
 +                              output_len, payload_size);
                return (-1);
        }
  
        /* All sanity checks successfull, let's copy the data over */
 -      output_len = pkg_length - header_size;
 -      memcpy ((void *) output, (void *) buffer, output_len);
 -      buffer += output_len;
 +      memcpy ((void *) output, (void *) buffer, payload_size);
 +      buffer += payload_size;
  
        /* For some very weird reason '\0' doesn't do the trick on SPARC in
         * this statement. */
 -      if (output[output_len - 1] != 0)
 +      if (output[payload_size - 1] != 0)
        {
                WARNING ("network plugin: parse_part_string: "
                                "Received string does not end "
@@@ -2039,8 -2029,6 +2039,8 @@@ static sockent_t *sockent_create (int t
        {
                se->data.client.fd = -1;
                se->data.client.addr = NULL;
 +              se->data.client.resolve_interval = 0;
 +              se->data.client.next_resolve_reconnect = 0;
  #if HAVE_LIBGCRYPT
                se->data.client.security_level = SECURITY_LEVEL_NONE;
                se->data.client.username = NULL;
@@@ -2107,26 -2095,6 +2107,26 @@@ static int sockent_init_crypto (sockent
        return (0);
  } /* }}} int sockent_init_crypto */
  
 +static int sockent_client_disconnect (sockent_t *se) /* {{{ */
 +{
 +      struct sockent_client *client;
 +
 +      if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
 +              return (EINVAL);
 +
 +      client = &se->data.client;
 +      if (client->fd >= 0) /* connected */
 +      {
 +              close (client->fd);
 +              client->fd = -1;
 +      }
 +
 +      sfree (client->addr);
 +      client->addrlen = 0;
 +
 +      return (0);
 +} /* }}} int sockent_client_disconnect */
 +
  static int sockent_client_connect (sockent_t *se) /* {{{ */
  {
        static c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
        struct addrinfo  ai_hints;
        struct addrinfo *ai_list = NULL, *ai_ptr;
        int status;
 +      _Bool reconnect = 0;
 +      cdtime_t now;
  
        if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
                return (EINVAL);
  
        client = &se->data.client;
 -      if (client->fd >= 0) /* already connected */
 +
 +      now = cdtime ();
 +      if (client->resolve_interval != 0 && client->next_resolve_reconnect < now) {
 +              DEBUG("network plugin: Reconnecting socket, resolve_interval = %lf, next_resolve_reconnect = %lf",
 +                      CDTIME_T_TO_DOUBLE(client->resolve_interval), CDTIME_T_TO_DOUBLE(client->next_resolve_reconnect));
 +              reconnect = 1;
 +      }
 +
 +      if (client->fd >= 0 && !reconnect) /* already connected and not stale*/
                return (0);
  
        memset (&ai_hints, 0, sizeof (ai_hints));
  
        for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
        {
 +              if (client->fd >= 0) /* when we reconnect */
 +                      sockent_client_disconnect(se);
 +
                client->fd = socket (ai_ptr->ai_family,
                                ai_ptr->ai_socktype,
                                ai_ptr->ai_protocol);
        freeaddrinfo (ai_list);
        if (client->fd < 0)
                return (-1);
 -      return (0);
 -} /* }}} int sockent_client_connect */
 -
 -static int sockent_client_disconnect (sockent_t *se) /* {{{ */
 -{
 -      struct sockent_client *client;
 -
 -      if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
 -              return (EINVAL);
 -
 -      client = &se->data.client;
 -      if (client->fd >= 0) /* connected */
 -      {
 -              close (client->fd);
 -              client->fd = -1;
 -      }
 -
 -      sfree (client->addr);
 -      client->addrlen = 0;
  
 +      if (client->resolve_interval > 0)
 +              client->next_resolve_reconnect = now + client->resolve_interval;
        return (0);
 -} /* }}} int sockent_client_disconnect */
 +} /* }}} int sockent_client_connect */
  
  /* Open the file descriptors for a initialized sockent structure. */
  static int sockent_server_listen (sockent_t *se) /* {{{ */
@@@ -2566,10 -2538,6 +2566,6 @@@ static int network_receive (void) /* {{
                receive_list_tail = private_list_tail;
                receive_list_length += private_list_length;
  
-               private_list_head = NULL;
-               private_list_tail = NULL;
-               private_list_length = 0;
                pthread_cond_signal (&receive_list_cond);
                pthread_mutex_unlock (&receive_list_lock);
        }
@@@ -3017,7 -2985,7 +3013,7 @@@ static int network_config_set_ttl (cons
      network_config_ttl = tmp;
    else {
      WARNING ("network plugin: The `TimeToLive' must be between 1 and 255.");
 -    return (-1);    
 +    return (-1);
    }
  
    return (0);
@@@ -3244,8 -3212,6 +3240,8 @@@ static int network_config_add_server (c
      if (strcasecmp ("Interface", child->key) == 0)
        network_config_set_interface (child,
            &se->interface);
 +              else if (strcasecmp ("ResolveInterval", child->key) == 0)
 +                      cf_util_get_cdtime(child, &se->data.client.resolve_interval);
      else
      {
        WARNING ("network plugin: Option `%s' is not allowed here.",
diff --combined src/ntpd.c
@@@ -2,29 -2,23 +2,29 @@@
   * collectd - src/ntpd.c
   * Copyright (C) 2006-2012  Florian octo Forster
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; only version 2 of the License is applicable.
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
   *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
   *
 - * You should have received a copy of the GNU General Public License along
 - * with this program; if not, write to the Free Software Foundation, Inc.,
 - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
   *
   * Authors:
   *   Florian octo Forster <octo at collectd.org>
   **/
  
 +#define _DEFAULT_SOURCE
  #define _BSD_SOURCE /* For NI_MAXHOST */
  
  #include "collectd.h"
@@@ -487,7 -481,7 +487,7 @@@ static int ntpd_receive_response (int *
                poll_s.fd      = sd;
                poll_s.events  = POLLIN | POLLPRI;
                poll_s.revents = 0;
 -              
 +
                DEBUG ("Polling for %ims", timeout);
                status = poll (&poll_s, 1, timeout);
  
  
                DEBUG ("recv'd %i bytes", status);
  
 -              /* 
 +              /*
                 * Do some sanity checks first
                 */
                if (status < RESP_HEADER_SIZE)
                                (items_num + pkt_item_num) * res_item_size);
                if (items == NULL)
                {
-                       items = *res_data;
                        ERROR ("ntpd plugin: realloc failed.");
                        continue;
                }
@@@ -737,7 -730,7 +736,7 @@@ static int ntpd_send_request (int req_c
  
        req.err_nitems   = ERR_NITEMS (0, req_items);
        req.mbz_itemsize = MBZ_ITEMSIZE (req_size);
 -      
 +
        if (req_data != NULL)
                memcpy ((void *) req.data, (const void *) req_data, req_data_len);
  
diff --combined src/openvpn.c
@@@ -32,7 -32,6 +32,7 @@@
  #define V1STRING "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n"
  #define V2STRING "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)\n"
  #define V3STRING "HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes Received Bytes Sent Connected Since Connected Since (time_t)\n"
 +#define V4STRING "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username\n"
  #define VSSTRING "OpenVPN STATISTICS\n"
  
  
@@@ -44,7 -43,6 +44,7 @@@ struct vpn_status_
                MULTI1 = 1, /* status-version 1 */
                MULTI2,     /* status-version 2 */
                MULTI3,     /* status-version 3 */
 +              MULTI4,     /* status-version 4 */
                SINGLE = 10 /* currently no versions for single mode, maybe in the future */
        } version;
        char *name;
@@@ -189,8 -187,6 +189,6 @@@ static int single_read (char *name, FIL
        post_compress = 0;
        pre_decompress = 0;
        post_decompress = 0;
-       overhead_rx = 0;
-       overhead_tx = 0;
  
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
@@@ -452,77 -448,13 +450,77 @@@ static int multi3_read (char *name, FIL
        return (read);
  } /* int multi3_read */
  
 +/* for reading status version 4 */
 +static int multi4_read (char *name, FILE *fh)
 +{
 +      char buffer[1024];
 +      char *fields[11];
 +      const int max_fields = STATIC_ARRAY_SIZE (fields);
 +      int  fields_num, read = 0;
 +      long long sum_users    = 0;
 +
 +      while (fgets (buffer, sizeof (buffer), fh) != NULL)
 +      {
 +              fields_num = openvpn_strsplit (buffer, fields, max_fields);
 +
 +              /* status file is generated by openvpn/multi.c:multi_print_status()
 +               * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
 +               *
 +               * The line we're expecting has 9 fields. We ignore all lines
 +               *  with more or less fields.
 +               */
 +              if (fields_num != 9)
 +                      continue;
 +
 +
 +              if (strcmp (fields[0], "CLIENT_LIST") != 0)
 +                      continue;
 +
 +
 +              if (collect_user_count)
 +                      /* If so, sum all users, ignore the individuals*/
 +              {
 +                      sum_users += 1;
 +              }
 +              if (collect_individual_users)
 +              {
 +                      if (new_naming_schema)
 +                      {
 +                              /* plugin inst = file name, type inst = fields[1] */
 +                              iostats_submit (name,               /* vpn instance */
 +                                              fields[1],          /* "Common Name" */
 +                                              atoll (fields[4]),  /* "Bytes Received" */
 +                                              atoll (fields[5])); /* "Bytes Sent" */
 +                      }
 +                      else
 +                      {
 +                              /* plugin inst = fields[1], type inst = "" */
 +                              iostats_submit (fields[1],          /* "Common Name" */
 +                                              NULL,               /* unused when in multimode */
 +                                              atoll (fields[4]),  /* "Bytes Received" */
 +                                              atoll (fields[5])); /* "Bytes Sent" */
 +                      }
 +              }
 +
 +              read = 1;
 +      }
 +
 +      if (collect_user_count)
 +      {
 +              numusers_submit(name, name, sum_users);
 +              read = 1;
 +      }
 +
 +      return (read);
 +} /* int multi4_read */
 +
  /* read callback */
  static int openvpn_read (void)
  {
        FILE *fh;
 -      int  i, read;
 +      int  i, vpn_read, read;
  
 -      read = 0;
 +      vpn_read = read = 0;
  
        /* call the right read function for every status entry in the list */
        for (i = 0; i < vpn_num; i++)
                switch (vpn_list[i]->version)
                {
                        case SINGLE:
 -                              read = single_read(vpn_list[i]->name, fh);
 +                              vpn_read = single_read(vpn_list[i]->name, fh);
                                break;
  
                        case MULTI1:
 -                              read = multi1_read(vpn_list[i]->name, fh);
 +                              vpn_read = multi1_read(vpn_list[i]->name, fh);
                                break;
  
                        case MULTI2:
 -                              read = multi2_read(vpn_list[i]->name, fh);
 +                              vpn_read = multi2_read(vpn_list[i]->name, fh);
                                break;
  
                        case MULTI3:
 -                              read = multi3_read(vpn_list[i]->name, fh);
 +                              vpn_read = multi3_read(vpn_list[i]->name, fh);
 +                              break;
 +
 +                      case MULTI4:
 +                              vpn_read = multi4_read(vpn_list[i]->name, fh);
                                break;
                }
  
                fclose (fh);
 +              read += vpn_read;
        }
  
        return (read ? 0 : -1);
@@@ -618,13 -545,6 +616,13 @@@ static int version_detect (const char *
                        version = MULTI3;
                        break;
                }
 +              /* searching for multi version 4 */
 +              else if (strcmp (buffer, V4STRING) == 0)
 +              {
 +                      DEBUG ("openvpn plugin: found status file version MULTI4");
 +                      version = MULTI4;
 +                      break;
 +              }
        }
  
        if (version == 0)
diff --combined src/ping.c
@@@ -2,23 -2,18 +2,23 @@@
   * collectd - src/ping.c
   * Copyright (C) 2005-2012  Florian octo Forster
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; only version 2 of the License is applicable.
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
   *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
   *
 - * You should have received a copy of the GNU General Public License along
 - * with this program; if not, write to the Free Software Foundation, Inc.,
 - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
   *
   * Authors:
   *   Florian octo Forster <octo at collectd.org>
@@@ -359,7 -354,7 +359,7 @@@ static void *ping_thread (void *arg) /
       * `ts_wait'. */
      time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
  
-     status = pthread_cond_timedwait (&ping_cond, &ping_lock, &ts_wait);
+     pthread_cond_timedwait (&ping_cond, &ping_lock, &ts_wait);
      if (ping_thread_loop <= 0)
        break;
    } /* while (ping_thread_loop > 0) */
diff --combined src/powerdns.c
@@@ -18,7 -18,7 +18,7 @@@
   *
   * Author:
   *   Luke Heberling <lukeh at c-ware.com>
 - *   Florian Forster <octo at verplant.org>
 + *   Florian Forster <octo at collectd.org>
   *
   * DESCRIPTION
   *   Queries a PowerDNS control socket for statistics
@@@ -147,34 -147,22 +147,34 @@@ statname_lookup_t lookup_table[] = /* {
    {"recursing-questions",    "dns_question", "recurse"},
    {"tcp-queries",            "dns_question", "tcp"},
    {"udp-queries",            "dns_question", "udp"},
 +  {"rd-queries",             "dns_question", "rd"},
  
    /* Answers */
    {"recursing-answers",      "dns_answer",   "recurse"},
    {"tcp-answers",            "dns_answer",   "tcp"},
    {"udp-answers",            "dns_answer",   "udp"},
 +  {"recursion-unanswered",   "dns_answer",   "recursion-unanswered"},
 +  {"udp-answers-bytes",      "total_bytes",  "udp-answers-bytes"},
  
    /* Cache stuff */
    {"packetcache-hit",        "cache_result", "packet-hit"},
    {"packetcache-miss",       "cache_result", "packet-miss"},
    {"packetcache-size",       "cache_size",   "packet"},
 +  {"key-cache-size",         "cache_size",   "key"},
 +  {"meta-cache-size",        "cache_size",   "meta"},
 +  {"signature-cache-size",   "cache_size",   "signature"},
    {"query-cache-hit",        "cache_result", "query-hit"},
    {"query-cache-miss",       "cache_result", "query-miss"},
  
    /* Latency */
    {"latency",                "latency",      NULL},
  
 +  /* DNS updates */
 +  {"dnsupdate-answers",      "dns_answer",   "dnsupdate-answer"},
 +  {"dnsupdate-changes",      "dns_question", "dnsupdate-changes"},
 +  {"dnsupdate-queries",      "dns_question", "dnsupdate-queries"},
 +  {"dnsupdate-refused",      "dns_answer",   "dnsupdate-refused"},
 +
    /* Other stuff.. */
    {"corrupt-packets",        "ipt_packets",  "corrupt"},
    {"deferred-cache-inserts", "counter",      "cache-deferred_insert"},
    {"udp4-queries",           "dns_question", "queries-udp4"},
    {"udp6-answers",           "dns_answer",   "udp6"},
    {"udp6-queries",           "dns_question", "queries-udp6"},
 +  {"security-status",        "dns_question", "security-status"},
 +  {"udp-do-queries",         "dns_question", "udp-do_queries"},
 +  {"signatures",             "counter",      "signatures"},
  
    /***********************
     * Recursor statistics *
    {"throttle-entries",    "gauge",        "entries-throttle"},
    {"unauthorized-tcp",    "counter",      "denied-unauthorized_tcp"},
    {"unauthorized-udp",    "counter",      "denied-unauthorized_udp"},
 -  {"unexpected-packets",  "dns_answer",   "unexpected"}
 -  /* {"uptime", "", ""} */
 +  {"unexpected-packets",  "dns_answer",   "unexpected"},
 +  {"uptime",              "uptime",       NULL}
  }; /* }}} */
  int lookup_table_length = STATIC_ARRAY_SIZE (lookup_table);
  
@@@ -462,6 -447,12 +462,12 @@@ static int powerdns_get_data_stream (li
    timeout.tv_sec=5;
    timeout.tv_usec=0;
    status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
+   if (status != 0)
+   {
+     FUNC_ERROR ("setsockopt");
+     close (sd);
+     return (-1);
+   }
  
    status = connect (sd, (struct sockaddr *) &item->sockaddr,
        sizeof (item->sockaddr));
      buffer[buffer_size] = 0;
    } /* while (42) */
    close (sd);
-   sd = -1;
  
    if (status < 0)
    {
diff --combined src/snmp.c
@@@ -2,23 -2,18 +2,23 @@@
   * collectd - src/snmp.c
   * Copyright (C) 2007-2012  Florian octo Forster
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; only version 2 of the License is applicable.
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
   *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
   *
 - * You should have received a copy of the GNU General Public License along
 - * with this program; if not, write to the Free Software Foundation, Inc.,
 - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
   *
   * Authors:
   *   Florian octo Forster <octo at collectd.org>
@@@ -34,8 -29,6 +34,8 @@@
  #include <net-snmp/net-snmp-config.h>
  #include <net-snmp/net-snmp-includes.h>
  
 +#include <fnmatch.h>
 +
  /*
   * Private data structes
   */
@@@ -57,7 -50,7 +57,7 @@@ struct data_definition_
  {
    char *name; /* used to reference this from the `Collect' option */
    char *type; /* used to find the data_set */
 -  int is_table;
 +  _Bool is_table;
    instance_t instance;
    char *instance_prefix;
    oid_t *values;
@@@ -65,9 -58,6 +65,9 @@@
    double scale;
    double shift;
    struct data_definition_s *next;
 +  char **ignores;
 +  size_t ignores_len;
 +  int invert_match;
  };
  typedef struct data_definition_s data_definition_t;
  
@@@ -75,22 -65,8 +75,22 @@@ struct host_definition_
  {
    char *name;
    char *address;
 -  char *community;
    int version;
 +
 +  /* snmpv1/2 options */
 +  char *community;
 +
 +  /* snmpv3 security options */
 +  char *username;
 +  oid *auth_protocol;
 +  size_t auth_protocol_len;
 +  char *auth_passphrase;
 +  oid *priv_protocol;
 +  size_t priv_protocol_len;
 +  char *priv_passphrase;
 +  int security_level;
 +  char *context;
 +
    void *sess_handle;
    c_complain_t complaint;
    cdtime_t interval;
@@@ -207,10 -183,6 +207,10 @@@ static void csnmp_host_definition_destr
    sfree (hd->name);
    sfree (hd->address);
    sfree (hd->community);
 +  sfree (hd->username);
 +  sfree (hd->auth_passphrase);
 +  sfree (hd->priv_passphrase);
 +  sfree (hd->context);
    sfree (hd->data_list);
  
    sfree (hd);
   *  csnmp_config
   *  +-> call_snmp_init_once
   *  +-> csnmp_config_add_data
 - *  !   +-> csnmp_config_add_data_type
 - *  !   +-> csnmp_config_add_data_table
   *  !   +-> csnmp_config_add_data_instance
   *  !   +-> csnmp_config_add_data_instance_prefix
   *  !   +-> csnmp_config_add_data_values
   *  +-> csnmp_config_add_host
 - *      +-> csnmp_config_add_host_address
 - *      +-> csnmp_config_add_host_community
   *      +-> csnmp_config_add_host_version
   *      +-> csnmp_config_add_host_collect
 + *      +-> csnmp_config_add_host_auth_protocol
 + *      +-> csnmp_config_add_host_priv_protocol
 + *      +-> csnmp_config_add_host_security_level
   */
  static void call_snmp_init_once (void)
  {
    have_init = 1;
  } /* void call_snmp_init_once */
  
 -static int csnmp_config_add_data_type (data_definition_t *dd, oconfig_item_t *ci)
 -{
 -  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
 -  {
 -    WARNING ("snmp plugin: `Type' needs exactly one string argument.");
 -    return (-1);
 -  }
 -
 -  sfree (dd->type);
 -  dd->type = strdup (ci->values[0].value.string);
 -  if (dd->type == NULL)
 -    return (-1);
 -
 -  return (0);
 -} /* int csnmp_config_add_data_type */
 -
 -static int csnmp_config_add_data_table (data_definition_t *dd, oconfig_item_t *ci)
 -{
 -  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
 -  {
 -    WARNING ("snmp plugin: `Table' needs exactly one boolean argument.");
 -    return (-1);
 -  }
 -
 -  dd->is_table = ci->values[0].value.boolean ? 1 : 0;
 -
 -  return (0);
 -} /* int csnmp_config_add_data_table */
 -
  static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t *ci)
  {
 -  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
 -  {
 -    WARNING ("snmp plugin: `Instance' needs exactly one string argument.");
 -    return (-1);
 -  }
 +  char buffer[DATA_MAX_NAME_LEN];
 +  int status;
 +
 +  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
 +  if (status != 0)
 +    return status;
  
    if (dd->is_table)
    {
      /* Instance is an OID */
      dd->instance.oid.oid_len = MAX_OID_LEN;
  
 -    if (!read_objid (ci->values[0].value.string,
 +    if (!read_objid (buffer,
            dd->instance.oid.oid, &dd->instance.oid.oid_len))
      {
 -      ERROR ("snmp plugin: read_objid (%s) failed.",
 -          ci->values[0].value.string);
 +      ERROR ("snmp plugin: read_objid (%s) failed.", buffer);
        return (-1);
      }
    }
    else
    {
      /* Instance is a simple string */
 -    sstrncpy (dd->instance.string, ci->values[0].value.string,
 +    sstrncpy (dd->instance.string, buffer,
          sizeof (dd->instance.string));
    }
  
  static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
      oconfig_item_t *ci)
  {
 -  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
 -  {
 -    WARNING ("snmp plugin: `InstancePrefix' needs exactly one string argument.");
 -    return (-1);
 -  }
 +  int status;
  
    if (!dd->is_table)
    {
      return (-1);
    }
  
 -  sfree (dd->instance_prefix);
 -  dd->instance_prefix = strdup (ci->values[0].value.string);
 -  if (dd->instance_prefix == NULL)
 -    return (-1);
 -
 -  return (0);
 +  status = cf_util_get_string(ci, &dd->instance_prefix);
 +  return status;
  } /* int csnmp_config_add_data_instance_prefix */
  
  static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *ci)
    return (0);
  } /* int csnmp_config_add_data_instance */
  
 -static int csnmp_config_add_data_shift (data_definition_t *dd, oconfig_item_t *ci)
 +static int csnmp_config_add_data_blacklist(data_definition_t *dd, oconfig_item_t *ci)
  {
 -  if ((ci->values_num != 1)
 -      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
 +  int i;
 +
 +  if (ci->values_num < 1)
 +    return (0);
 +
 +  for (i = 0; i < ci->values_num; i++)
    {
 -    WARNING ("snmp plugin: The `Shift' config option needs exactly one number argument.");
 -    return (-1);
 +    if (ci->values[i].type != OCONFIG_TYPE_STRING)
 +    {
 +      WARNING ("snmp plugin: `Ignore' needs only string argument.");
 +      return (-1);
 +    }
    }
  
 -  dd->shift = ci->values[0].value.number;
 +  dd->ignores_len = 0;
 +  dd->ignores = NULL;
  
 -  return (0);
 -} /* int csnmp_config_add_data_shift */
 +  for (i = 0; i < ci->values_num; ++i)
 +  {
 +    if (strarray_add(&(dd->ignores), &(dd->ignores_len), ci->values[i].value.string) != 0)
 +    {
 +      ERROR("snmp plugin: Can't allocate memory");
 +      strarray_free(dd->ignores, dd->ignores_len);
 +      return (ENOMEM);
 +    }
 +  }
 +  return 0;
 +} /* int csnmp_config_add_data_blacklist */
  
 -static int csnmp_config_add_data_scale (data_definition_t *dd, oconfig_item_t *ci)
 +static int csnmp_config_add_data_blacklist_match_inverted(data_definition_t *dd, oconfig_item_t *ci)
  {
 -  if ((ci->values_num != 1)
 -      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
 +  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
    {
 -    WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument.");
 +    WARNING ("snmp plugin: `InvertMatch' needs exactly one boolean argument.");
      return (-1);
    }
  
 -  dd->scale = ci->values[0].value.number;
 +  dd->invert_match = ci->values[0].value.boolean ? 1 : 0;
  
    return (0);
 -} /* int csnmp_config_add_data_scale */
 +} /* int csnmp_config_add_data_blacklist_match_inverted */
  
  static int csnmp_config_add_data (oconfig_item_t *ci)
  {
    int status = 0;
    int i;
  
 -  if ((ci->values_num != 1)
 -      || (ci->values[0].type != OCONFIG_TYPE_STRING))
 -  {
 -    WARNING ("snmp plugin: The `Data' config option needs exactly one string argument.");
 -    return (-1);
 -  }
 -
    dd = (data_definition_t *) malloc (sizeof (data_definition_t));
    if (dd == NULL)
      return (-1);
    memset (dd, '\0', sizeof (data_definition_t));
  
 -  dd->name = strdup (ci->values[0].value.string);
 -  if (dd->name == NULL)
 +  status = cf_util_get_string(ci, &dd->name);
 +  if (status != 0)
    {
      free (dd);
      return (-1);
    }
 +
    dd->scale = 1.0;
    dd->shift = 0.0;
  
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *option = ci->children + i;
-     status = 0;
  
      if (strcasecmp ("Type", option->key) == 0)
 -      status = csnmp_config_add_data_type (dd, option);
 +      status = cf_util_get_string(option, &dd->type);
      else if (strcasecmp ("Table", option->key) == 0)
 -      status = csnmp_config_add_data_table (dd, option);
 +      status = cf_util_get_boolean(option, &dd->is_table);
      else if (strcasecmp ("Instance", option->key) == 0)
        status = csnmp_config_add_data_instance (dd, option);
      else if (strcasecmp ("InstancePrefix", option->key) == 0)
      else if (strcasecmp ("Values", option->key) == 0)
        status = csnmp_config_add_data_values (dd, option);
      else if (strcasecmp ("Shift", option->key) == 0)
 -      status = csnmp_config_add_data_shift (dd, option);
 +      status = cf_util_get_double(option, &dd->shift);
      else if (strcasecmp ("Scale", option->key) == 0)
 -      status = csnmp_config_add_data_scale (dd, option);
 +      status = cf_util_get_double(option, &dd->scale);
 +    else if (strcasecmp ("Ignore", option->key) == 0)
 +      status = csnmp_config_add_data_blacklist(dd, option);
 +    else if (strcasecmp ("InvertMatch", option->key) == 0)
 +      status = csnmp_config_add_data_blacklist_match_inverted(dd, option);
      else
      {
        WARNING ("snmp plugin: Option `%s' not allowed here.", option->key);
      sfree (dd->name);
      sfree (dd->instance_prefix);
      sfree (dd->values);
 +    sfree (dd->ignores);
      sfree (dd);
      return (-1);
    }
    return (0);
  } /* int csnmp_config_add_data */
  
 -static int csnmp_config_add_host_address (host_definition_t *hd, oconfig_item_t *ci)
 -{
 -  if ((ci->values_num != 1)
 -      || (ci->values[0].type != OCONFIG_TYPE_STRING))
 -  {
 -    WARNING ("snmp plugin: The `Address' config option needs exactly one string argument.");
 -    return (-1);
 -  }
 -
 -  if (hd->address == NULL)
 -    free (hd->address);
 -
 -  hd->address = strdup (ci->values[0].value.string);
 -  if (hd->address == NULL)
 -    return (-1);
 -
 -  DEBUG ("snmp plugin: host = %s; host->address = %s;",
 -      hd->name, hd->address);
 -
 -  return (0);
 -} /* int csnmp_config_add_host_address */
 -
 -static int csnmp_config_add_host_community (host_definition_t *hd, oconfig_item_t *ci)
 -{
 -  if ((ci->values_num != 1)
 -      || (ci->values[0].type != OCONFIG_TYPE_STRING))
 -  {
 -    WARNING ("snmp plugin: The `Community' config option needs exactly one string argument.");
 -    return (-1);
 -  }
 -
 -  if (hd->community == NULL)
 -    free (hd->community);
 -
 -  hd->community = strdup (ci->values[0].value.string);
 -  if (hd->community == NULL)
 -    return (-1);
 -
 -  DEBUG ("snmp plugin: host = %s; host->community = %s;",
 -      hd->name, hd->community);
 -
 -  return (0);
 -} /* int csnmp_config_add_host_community */
 -
  static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t *ci)
  {
    int version;
    }
  
    version = (int) ci->values[0].value.number;
 -  if ((version != 1) && (version != 2))
 +  if ((version < 1) || (version > 3))
    {
 -    WARNING ("snmp plugin: `Version' must either be `1' or `2'.");
 +    WARNING ("snmp plugin: `Version' must either be `1', `2', or `3'.");
      return (-1);
    }
  
@@@ -551,92 -589,6 +550,92 @@@ static int csnmp_config_add_host_collec
    return (0);
  } /* int csnmp_config_add_host_collect */
  
 +static int csnmp_config_add_host_auth_protocol (host_definition_t *hd, oconfig_item_t *ci)
 +{
 +  char buffer[4];
 +  int status;
 +
 +  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
 +  if (status != 0)
 +    return status;
 +
 +  if (strcasecmp("MD5", buffer) == 0) {
 +    hd->auth_protocol = usmHMACMD5AuthProtocol;
 +    hd->auth_protocol_len = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
 +  }
 +  else if (strcasecmp("SHA", buffer) == 0) {
 +    hd->auth_protocol = usmHMACSHA1AuthProtocol;
 +    hd->auth_protocol_len = sizeof(usmHMACSHA1AuthProtocol)/sizeof(oid);
 +  }
 +  else
 +  {
 +    WARNING ("snmp plugin: The `AuthProtocol' config option must be `MD5' or `SHA'.");
 +    return (-1);
 +  }
 +
 +  DEBUG ("snmp plugin: host = %s; host->auth_protocol = %s;",
 +      hd->name, hd->auth_protocol == usmHMACMD5AuthProtocol ? "MD5" : "SHA");
 +
 +  return (0);
 +} /* int csnmp_config_add_host_auth_protocol */
 +
 +static int csnmp_config_add_host_priv_protocol (host_definition_t *hd, oconfig_item_t *ci)
 +{
 +  char buffer[4];
 +  int status;
 +
 +  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
 +  if (status != 0)
 +    return status;
 +
 +  if (strcasecmp("AES", buffer) == 0)
 +  {
 +    hd->priv_protocol = usmAESPrivProtocol;
 +    hd->priv_protocol_len = sizeof(usmAESPrivProtocol)/sizeof(oid);
 +  }
 +  else if (strcasecmp("DES", buffer) == 0) {
 +    hd->priv_protocol = usmDESPrivProtocol;
 +    hd->priv_protocol_len = sizeof(usmDESPrivProtocol)/sizeof(oid);
 +  }
 +  else
 +  {
 +    WARNING ("snmp plugin: The `PrivProtocol' config option must be `AES' or `DES'.");
 +    return (-1);
 +  }
 +
 +  DEBUG ("snmp plugin: host = %s; host->priv_protocol = %s;",
 +      hd->name, hd->priv_protocol == usmAESPrivProtocol ? "AES" : "DES");
 +
 +  return (0);
 +} /* int csnmp_config_add_host_priv_protocol */
 +
 +static int csnmp_config_add_host_security_level (host_definition_t *hd, oconfig_item_t *ci)
 +{
 +  char buffer[16];
 +  int status;
 +
 +  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
 +  if (status != 0)
 +    return status;
 +
 +  if (strcasecmp("noAuthNoPriv", buffer) == 0)
 +    hd->security_level = SNMP_SEC_LEVEL_NOAUTH;
 +  else if (strcasecmp("authNoPriv", buffer) == 0)
 +    hd->security_level = SNMP_SEC_LEVEL_AUTHNOPRIV;
 +  else if (strcasecmp("authPriv", buffer) == 0)
 +    hd->security_level = SNMP_SEC_LEVEL_AUTHPRIV;
 +  else
 +  {
 +    WARNING ("snmp plugin: The `SecurityLevel' config option must be `noAuthNoPriv', `authNoPriv', or `authPriv'.");
 +    return (-1);
 +  }
 +
 +  DEBUG ("snmp plugin: host = %s; host->security_level = %d;",
 +      hd->name, hd->security_level);
 +
 +  return (0);
 +} /* int csnmp_config_add_host_security_level */
 +
  static int csnmp_config_add_host (oconfig_item_t *ci)
  {
    host_definition_t *hd;
    user_data_t cb_data;
    struct timespec cb_interval;
  
 -  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
 -  {
 -    WARNING ("snmp plugin: `Host' needs exactly one string argument.");
 -    return (-1);
 -  }
 -
    hd = (host_definition_t *) malloc (sizeof (host_definition_t));
    if (hd == NULL)
      return (-1);
    hd->version = 2;
    C_COMPLAIN_INIT (&hd->complaint);
  
 -  hd->name = strdup (ci->values[0].value.string);
 -  if (hd->name == NULL)
 -  {
 -    free (hd);
 -    return (-1);
 -  }
 +  status = cf_util_get_string(ci, &hd->name);
 +  if (status != 0)
 +    return status;
  
    hd->sess_handle = NULL;
    hd->interval = 0;
      status = 0;
  
      if (strcasecmp ("Address", option->key) == 0)
 -      status = csnmp_config_add_host_address (hd, option);
 +      status = cf_util_get_string(option, &hd->address);
      else if (strcasecmp ("Community", option->key) == 0)
 -      status = csnmp_config_add_host_community (hd, option);
 +      status = cf_util_get_string(option, &hd->community);
      else if (strcasecmp ("Version", option->key) == 0)
        status = csnmp_config_add_host_version (hd, option);
      else if (strcasecmp ("Collect", option->key) == 0)
        csnmp_config_add_host_collect (hd, option);
      else if (strcasecmp ("Interval", option->key) == 0)
        cf_util_get_cdtime (option, &hd->interval);
 +    else if (strcasecmp ("Username", option->key) == 0)
 +      status = cf_util_get_string(option, &hd->username);
 +    else if (strcasecmp ("AuthProtocol", option->key) == 0)
 +      status = csnmp_config_add_host_auth_protocol (hd, option);
 +    else if (strcasecmp ("PrivacyProtocol", option->key) == 0)
 +      status = csnmp_config_add_host_priv_protocol (hd, option);
 +    else if (strcasecmp ("AuthPassphrase", option->key) == 0)
 +      status = cf_util_get_string(option, &hd->auth_passphrase);
 +    else if (strcasecmp ("PrivacyPassphrase", option->key) == 0)
 +      status = cf_util_get_string(option, &hd->priv_passphrase);
 +    else if (strcasecmp ("SecurityLevel", option->key) == 0)
 +      status = csnmp_config_add_host_security_level (hd, option);
 +    else if (strcasecmp ("Context", option->key) == 0)
 +      status = cf_util_get_string(option, &hd->context);
      else
      {
        WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key);
        status = -1;
        break;
      }
 -    if (hd->community == NULL)
 +    if (hd->community == NULL && hd->version < 3)
      {
        WARNING ("snmp plugin: `Community' not given for host `%s'", hd->name);
        status = -1;
        break;
      }
 +    if (hd->version == 3)
 +    {
 +      if (hd->username == NULL)
 +      {
 +        WARNING ("snmp plugin: `Username' not given for host `%s'", hd->name);
 +        status = -1;
 +        break;
 +      }
 +      if (hd->security_level == 0)
 +      {
 +        WARNING ("snmp plugin: `SecurityLevel' not given for host `%s'", hd->name);
 +        status = -1;
 +        break;
 +      }
 +      if (hd->security_level == SNMP_SEC_LEVEL_AUTHNOPRIV || hd->security_level == SNMP_SEC_LEVEL_AUTHPRIV)
 +      {
 +      if (hd->auth_protocol == NULL)
 +      {
 +        WARNING ("snmp plugin: `AuthProtocol' not given for host `%s'", hd->name);
 +        status = -1;
 +        break;
 +      }
 +      if (hd->auth_passphrase == NULL)
 +      {
 +        WARNING ("snmp plugin: `AuthPassphrase' not given for host `%s'", hd->name);
 +        status = -1;
 +        break;
 +      }
 +      }
 +      if (hd->security_level == SNMP_SEC_LEVEL_AUTHPRIV)
 +      {
 +      if (hd->priv_protocol == NULL)
 +      {
 +        WARNING ("snmp plugin: `PrivacyProtocol' not given for host `%s'", hd->name);
 +        status = -1;
 +        break;
 +      }
 +      if (hd->priv_passphrase == NULL)
 +      {
 +        WARNING ("snmp plugin: `PrivacyPassphrase' not given for host `%s'", hd->name);
 +        status = -1;
 +        break;
 +      }
 +      }
 +    }
  
      break;
    } /* while (status == 0) */
@@@ -821,75 -723,15 +820,75 @@@ static int csnmp_config (oconfig_item_
  static void csnmp_host_open_session (host_definition_t *host)
  {
    struct snmp_session sess;
 +  int error;
  
    if (host->sess_handle != NULL)
      csnmp_host_close_session (host);
  
    snmp_sess_init (&sess);
    sess.peername = host->address;
 -  sess.community = (u_char *) host->community;
 -  sess.community_len = strlen (host->community);
 -  sess.version = (host->version == 1) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
 +  switch (host->version)
 +  {
 +    case 1:
 +      sess.version = SNMP_VERSION_1;
 +      break;
 +    case 3:
 +      sess.version = SNMP_VERSION_3;
 +      break;
 +    default:
 +      sess.version = SNMP_VERSION_2c;
 +      break;
 +  }
 +
 +  if (host->version == 3)
 +  {
 +    sess.securityName = host->username;
 +    sess.securityNameLen = strlen (host->username);
 +    sess.securityLevel = host->security_level;
 +
 +    if (sess.securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV || sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV)
 +    {
 +      sess.securityAuthProto = host->auth_protocol;
 +      sess.securityAuthProtoLen = host->auth_protocol_len;
 +      sess.securityAuthKeyLen = USM_AUTH_KU_LEN;
 +      error = generate_Ku (sess.securityAuthProto,
 +          sess.securityAuthProtoLen,
 +          (u_char *) host->auth_passphrase,
 +          strlen(host->auth_passphrase),
 +          sess.securityAuthKey,
 +          &sess.securityAuthKeyLen);
 +      if (error != SNMPERR_SUCCESS) {
 +      ERROR ("snmp plugin: host %s: Error generating Ku from auth_passphrase. (Error %d)", host->name, error);
 +      }
 +    }
 +
 +    if (sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV)
 +    {
 +      sess.securityPrivProto = host->priv_protocol;
 +      sess.securityPrivProtoLen = host->priv_protocol_len;
 +      sess.securityPrivKeyLen = USM_PRIV_KU_LEN;
 +      error = generate_Ku (sess.securityAuthProto,
 +          sess.securityAuthProtoLen,
 +          (u_char *) host->priv_passphrase,
 +          strlen(host->priv_passphrase),
 +          sess.securityPrivKey,
 +          &sess.securityPrivKeyLen);
 +      if (error != SNMPERR_SUCCESS) {
 +      ERROR ("snmp plugin: host %s: Error generating Ku from priv_passphrase. (Error %d)", host->name, error);
 +      }
 +    }
 +
 +    if (host->context != NULL)
 +    {
 +      sess.contextName = host->context;
 +      sess.contextNameLen = strlen (host->context);
 +    }
 +  }
 +  else /* SNMPv1/2 "authenticates" with community string */
 +  {
 +    sess.community = (u_char *) host->community;
 +    sess.community_len = strlen (host->community);
 +  }
  
    /* snmp_sess_open will copy the `struct snmp_session *'. */
    host->sess_handle = snmp_sess_open (&sess);
@@@ -993,8 -835,7 +992,8 @@@ static value_t csnmp_value_list_to_valu
        status = parse_value (string, &ret, type);
        if (status != 0)
        {
 -        ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
 +        ERROR ("snmp plugin: host %s: csnmp_value_list_to_value: Parsing string as %s failed: %s",
 +            (host_name != NULL) ? host_name : "UNKNOWN",
              DS_TYPE_TO_STRING (type), string);
        }
      }
@@@ -1129,8 -970,6 +1128,8 @@@ static int csnmp_instance_list_add (csn
    struct variable_list *vb;
    oid_t vb_name;
    int status;
 +  uint32_t i;
 +  uint32_t is_matched;
  
    /* Set vb on the last variable */
    for (vb = res->variables;
      char *ptr;
  
      csnmp_strvbcopy (il->instance, vb, sizeof (il->instance));
 -
 +    is_matched = 0;
 +    for (i = 0; i < dd->ignores_len; i++)
 +    {
 +      status = fnmatch(dd->ignores[i], il->instance, 0);
 +      if (status == 0)
 +      {
 +        if (dd->invert_match == 0)
 +        {
 +          sfree(il);
 +          return 0;
 +        }
 +      else
 +      {
 +          is_matched = 1;
 +        break;
 +      }
 +      }
 +    }
 +    if (dd->invert_match != 0 && is_matched == 0)
 +    {
 +      sfree(il);
 +      return 0;
 +    }
      for (ptr = il->instance; *ptr != '\0'; ptr++)
      {
        if ((*ptr > 0) && (*ptr < 32))
@@@ -1523,7 -1340,7 +1522,7 @@@ static int csnmp_read_table (host_defin
      for (vb = res->variables, i = 0; (vb != NULL); vb = vb->next_variable, i++)
      {
        /* Calculate value index from todo list */
-       while (!oid_list_todo[i] && (i < oid_list_len))
+       while ((i < oid_list_len) && !oid_list_todo[i])
          i++;
  
        /* An instance is configured and the res variable we process is the
          if (csnmp_instance_list_add (&instance_list_head, &instance_list_tail,
                res, host, data) != 0)
          {
 -          ERROR ("snmp plugin: csnmp_instance_list_add failed.");
 +          ERROR ("snmp plugin: host %s: csnmp_instance_list_add failed.",
 +              host->name);
            status = -1;
            break;
          }
@@@ -1844,7 -1660,6 +1843,7 @@@ static int csnmp_shutdown (void
      sfree (data_this->name);
      sfree (data_this->type);
      sfree (data_this->values);
 +    sfree (data_this->ignores);
      sfree (data_this);
  
      data_this = data_next;
diff --combined src/tail.c
@@@ -1,27 -1,22 +1,27 @@@
  /**
   * collectd - src/tail.c
 - * Copyright (C) 2008  Florian octo Forster
 + * Copyright (C) 2008       Florian octo Forster
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; only version 2 of the License is applicable.
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
   *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
   *
 - * You should have received a copy of the GNU General Public License along
 - * with this program; if not, write to the Free Software Foundation, Inc.,
 - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
   *
   * Authors:
 - *   Florian octo Forster <octo at verplant.org>
 + *   Florian octo Forster <octo at collectd.org>
   **/
  
  #include "collectd.h"
@@@ -33,7 -28,6 +33,7 @@@
   *  <Plugin tail>
   *    <File "/var/log/exim4/mainlog">
   *    Instance "exim"
 + *      Interval 60
   *    <Match>
   *      Regex "S=([1-9][0-9]*)"
   *      ExcludeRegex "U=root.*S="
@@@ -52,13 -46,11 +52,13 @@@ struct ctail_config_match_
    int flags;
    char *type;
    char *type_instance;
 +  cdtime_t interval;
  };
  typedef struct ctail_config_match_s ctail_config_match_t;
  
  cu_tail_match_t **tail_match_list = NULL;
  size_t tail_match_list_num = 0;
 +cdtime_t tail_match_list_intervals[255];
  
  static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
      oconfig_item_t *ci)
        cm->flags |= UTILS_MATCH_CF_GAUGE_MAX;
      else if (strcasecmp ("GaugeLast", ci->values[0].value.string) == 0)
        cm->flags |= UTILS_MATCH_CF_GAUGE_LAST;
 +    else if (strcasecmp ("GaugeInc", ci->values[0].value.string) == 0)
 +      cm->flags |= UTILS_MATCH_CF_GAUGE_INC;
 +    else if (strcasecmp ("GaugeAdd", ci->values[0].value.string) == 0)
 +      cm->flags |= UTILS_MATCH_CF_GAUGE_ADD;
      else
        cm->flags = 0;
    }
  } /* int ctail_config_add_match_dstype */
  
  static int ctail_config_add_match (cu_tail_match_t *tm,
 -    const char *plugin_instance, oconfig_item_t *ci)
 +    const char *plugin_instance, oconfig_item_t *ci, cdtime_t interval)
  {
    ctail_config_match_t cm;
    int status;
    if (status == 0)
    {
      status = tail_match_add_match_simple (tm, cm.regex, cm.excluderegex,
 -      cm.flags, "tail", plugin_instance, cm.type, cm.type_instance);
 +      cm.flags, "tail", plugin_instance, cm.type, cm.type_instance, interval);
  
      if (status != 0)
      {
  static int ctail_config_add_file (oconfig_item_t *ci)
  {
    cu_tail_match_t *tm;
 +  cdtime_t interval = 0;
    char *plugin_instance = NULL;
    int num_matches = 0;
-   int status;
    int i;
  
    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
      return (-1);
    }
  
-   status = 0;
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *option = ci->children + i;
+     int status;
  
 -    if (strcasecmp ("Match", option->key) == 0)
 +    if (strcasecmp ("Instance", option->key) == 0)
 +      status = cf_util_get_string (option, &plugin_instance);
 +    else if (strcasecmp ("Interval", option->key) == 0)
 +      cf_util_get_cdtime (option, &interval);
 +    else if (strcasecmp ("Match", option->key) == 0)
      {
 -      status = ctail_config_add_match (tm, plugin_instance, option);
 +      status = ctail_config_add_match (tm, plugin_instance, option, interval);
        if (status == 0)
        num_matches++;
        /* Be mild with failed matches.. */
        status = 0;
      }
 -    else if (strcasecmp ("Instance", option->key) == 0)
 -      status = cf_util_get_string (option, &plugin_instance);
      else
      {
 -      WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
        status = -1;
      }
  
  
      tail_match_list = temp;
      tail_match_list[tail_match_list_num] = tm;
 +    tail_match_list_intervals[tail_match_list_num] = interval;
      tail_match_list_num++;
    }
  
@@@ -315,45 -299,41 +314,45 @@@ static int ctail_config (oconfig_item_
    return (0);
  } /* int ctail_config */
  
 -static int ctail_init (void)
 +static int ctail_read (user_data_t *ud)
  {
 -  if (tail_match_list_num == 0)
 +  int status;
 +
 +  status = tail_match_read ((cu_tail_match_t *)ud->data);
 +  if (status != 0)
    {
 -    WARNING ("tail plugin: File list is empty. Returning an error.");
 +    ERROR ("tail plugin: tail_match_read failed.");
      return (-1);
    }
  
    return (0);
 -} /* int ctail_init */
 +} /* int ctail_read */
  
 -static int ctail_read (void)
 +static int ctail_init (void)
  {
 -  int success = 0;
 +  struct timespec cb_interval;
 +  char str[255];
 +  user_data_t ud;
    size_t i;
  
 -  for (i = 0; i < tail_match_list_num; i++)
 +  if (tail_match_list_num == 0)
    {
 -    int status;
 +    WARNING ("tail plugin: File list is empty. Returning an error.");
 +    return (-1);
 +  }
  
 -    status = tail_match_read (tail_match_list[i]);
 -    if (status != 0)
 -    {
 -      ERROR ("tail plugin: tail_match_read[%zu] failed.", i);
 -    }
 -    else
 -    {
 -      success++;
 -    }
 +  memset(&ud, '\0', sizeof(ud));
 +
 +  for (i = 0; i < tail_match_list_num; i++)
 +  {
 +    ud.data = (void *)tail_match_list[i];
 +    ssnprintf(str, sizeof(str), "tail-%zu", i);
 +    CDTIME_T_TO_TIMESPEC (tail_match_list_intervals[i], &cb_interval);
 +    plugin_register_complex_read (NULL, str, ctail_read, &cb_interval, &ud);
    }
  
 -  if (success == 0)
 -    return (-1);
    return (0);
 -} /* int ctail_read */
 +} /* int ctail_init */
  
  static int ctail_shutdown (void)
  {
@@@ -374,6 -354,7 +373,6 @@@ void module_register (void
  {
    plugin_register_complex_config ("tail", ctail_config);
    plugin_register_init ("tail", ctail_init);
 -  plugin_register_read ("tail", ctail_read);
    plugin_register_shutdown ("tail", ctail_shutdown);
  } /* void module_register */
  
diff --combined src/threshold.c
  #include "plugin.h"
  #include "utils_avltree.h"
  #include "utils_cache.h"
 +#include "utils_threshold.h"
  
  #include <assert.h>
  #include <pthread.h>
  
  /*
 - * Private data structures
 - * {{{ */
 -#define UT_FLAG_INVERT  0x01
 -#define UT_FLAG_PERSIST 0x02
 -#define UT_FLAG_PERCENTAGE 0x04
 -#define UT_FLAG_INTERESTING 0x08
 -#define UT_FLAG_PERSIST_OK 0x10
 -typedef struct threshold_s
 -{
 -  char host[DATA_MAX_NAME_LEN];
 -  char plugin[DATA_MAX_NAME_LEN];
 -  char plugin_instance[DATA_MAX_NAME_LEN];
 -  char type[DATA_MAX_NAME_LEN];
 -  char type_instance[DATA_MAX_NAME_LEN];
 -  char data_source[DATA_MAX_NAME_LEN];
 -  gauge_t warning_min;
 -  gauge_t warning_max;
 -  gauge_t failure_min;
 -  gauge_t failure_max;
 -  gauge_t hysteresis;
 -  unsigned int flags;
 -  int hits;
 -  struct threshold_s *next;
 -} threshold_t;
 -/* }}} */
 -
 -/*
 - * Private (static) variables
 - * {{{ */
 -static c_avl_tree_t   *threshold_tree = NULL;
 -static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
 -/* }}} */
 -
 -/*
   * Threshold management
   * ====================
   * The following functions add, delete, search, etc. configured thresholds to
   * the underlying AVL trees.
   */
 -/*
 - * threshold_t *threshold_get
 - *
 - * Retrieve one specific threshold configuration. For looking up a threshold
 - * matching a value_list_t, see "threshold_search" below. Returns NULL if the
 - * specified threshold doesn't exist.
 - */
 -static threshold_t *threshold_get (const char *hostname,
 -    const char *plugin, const char *plugin_instance,
 -    const char *type, const char *type_instance)
 -{ /* {{{ */
 -  char name[6 * DATA_MAX_NAME_LEN];
 -  threshold_t *th = NULL;
 -
 -  format_name (name, sizeof (name),
 -      (hostname == NULL) ? "" : hostname,
 -      (plugin == NULL) ? "" : plugin, plugin_instance,
 -      (type == NULL) ? "" : type, type_instance);
 -  name[sizeof (name) - 1] = '\0';
 -
 -  if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
 -    return (th);
 -  else
 -    return (NULL);
 -} /* }}} threshold_t *threshold_get */
  
  /*
   * int ut_threshold_add
@@@ -113,6 -171,58 +113,6 @@@ static int ut_threshold_add (const thre
    return (status);
  } /* }}} int ut_threshold_add */
  
 -/* 
 - * threshold_t *threshold_search
 - *
 - * Searches for a threshold configuration using all the possible variations of
 - * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be
 - * found.
 - * XXX: This is likely the least efficient function in collectd.
 - */
 -static threshold_t *threshold_search (const value_list_t *vl)
 -{ /* {{{ */
 -  threshold_t *th;
 -
 -  if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
 -        vl->type, vl->type_instance)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
 -        vl->type, NULL)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
 -        vl->type, vl->type_instance)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
 -        vl->type, NULL)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get (vl->host, "", NULL,
 -        vl->type, vl->type_instance)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get (vl->host, "", NULL,
 -        vl->type, NULL)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
 -        vl->type, vl->type_instance)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
 -        vl->type, NULL)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get ("", vl->plugin, NULL,
 -        vl->type, vl->type_instance)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get ("", vl->plugin, NULL,
 -        vl->type, NULL)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get ("", "", NULL,
 -        vl->type, vl->type_instance)) != NULL)
 -    return (th);
 -  else if ((th = threshold_get ("", "", NULL,
 -        vl->type, NULL)) != NULL)
 -    return (th);
 -
 -  return (NULL);
 -} /* }}} threshold_t *threshold_search */
 -
  /*
   * Configuration
   * =============
@@@ -251,7 -361,6 +251,6 @@@ static int ut_config_type (const thresh
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *option = ci->children + i;
-     status = 0;
  
      if (strcasecmp ("Instance", option->key) == 0)
        status = ut_config_type_instance (&th, option);
@@@ -339,7 -448,6 +338,6 @@@ static int ut_config_plugin (const thre
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *option = ci->children + i;
-     status = 0;
  
      if (strcasecmp ("Type", option->key) == 0)
        status = ut_config_type (&th, option);
@@@ -386,7 -494,6 +384,6 @@@ static int ut_config_host (const thresh
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *option = ci->children + i;
-     status = 0;
  
      if (strcasecmp ("Type", option->key) == 0)
        status = ut_config_type (&th, option);
@@@ -519,9 -626,7 +516,9 @@@ static int ut_report_state (const data_
            ": Value is no longer missing.");
      else
        status = ssnprintf (buf, bufsize,
 -          ": All data sources are within range again.");
 +          ": All data sources are within range again. "
 +          "Current value of \"%s\" is %f.",
 +          ds->ds[ds_index].name, values[ds_index]);
      buf += status;
      bufsize -= status;
    }
@@@ -639,40 -744,23 +636,40 @@@ static int ut_check_one_data_source (co
  
    /* XXX: This is an experimental code, not optimized, not fast, not reliable,
     * and probably, do not work as you expect. Enjoy! :D */
 -  if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
 -  {
 -    switch(prev_state)
 +  if (th->hysteresis > 0)
 +  {
 +    prev_state = uc_get_state(ds,vl);
 +    /* The purpose of hysteresis is elliminating flapping state when the value
 +     * oscilates around the thresholds. In other words, what is important is
 +     * the previous state; if the new value would trigger a transition, make
 +     * sure that we artificially widen the range which is considered to apply
 +     * for the previous state, and only trigger the notification if the value
 +     * is outside of this expanded range.
 +     *
 +     * There is no hysteresis for the OKAY state.
 +     * */
 +    gauge_t hysteresis_for_warning = 0, hysteresis_for_failure = 0;
 +    switch (prev_state)
      {
        case STATE_ERROR:
 -      if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
 -           (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
 -        return (STATE_OKAY);
 -      else
 -        is_failure++;
 +        hysteresis_for_failure = th->hysteresis;
 +        break;
        case STATE_WARNING:
 -      if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
 -           (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
 -        return (STATE_OKAY);
 -      else
 -        is_warning++;
 -     }
 +        hysteresis_for_warning = th->hysteresis;
 +        break;
 +      case STATE_OKAY:
 +        /* do nothing -- the hysteresis only applies to the non-normal states */
 +        break;
 +    }
 +
 +    if ((!isnan (th->failure_min) && (th->failure_min + hysteresis_for_failure > values[ds_index]))
 +      || (!isnan (th->failure_max) && (th->failure_max - hysteresis_for_failure < values[ds_index])))
 +      is_failure++;
 +
 +    if ((!isnan (th->warning_min) && (th->warning_min + hysteresis_for_warning > values[ds_index]))
 +      || (!isnan (th->warning_max) && (th->warning_max - hysteresis_for_warning < values[ds_index])))
 +      is_warning++;
 +
    }
    else { /* no hysteresis */
      if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
      if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
        || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
        is_warning++;
 - }
 +  }
  
    if (is_failure != 0)
      return (STATE_ERROR);
@@@ -771,7 -859,7 +768,7 @@@ static int ut_check_one_threshold (cons
   *
   * Gets a list of matching thresholds and searches for the worst status by one
   * of the thresholds. Then reports that status using the ut_report_state
 - * function above. 
 + * function above.
   * Returns zero on success and if no threshold has been configured. Returns
   * less than zero on failure.
   */
@@@ -902,11 -990,10 +899,10 @@@ int ut_config (oconfig_item_t *ci
    th.hits = 0;
    th.hysteresis = 0;
    th.flags = UT_FLAG_INTERESTING; /* interesting by default */
 -    
 +
    for (i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *option = ci->children + i;
-     status = 0;
  
      if (strcasecmp ("Type", option->key) == 0)
        status = ut_config_type (&th, option);