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 --cc src/amqp.c
Simple merge
diff --cc src/curl_json.c
Simple merge
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 --cc src/email.c
Simple merge
diff --cc src/exec.c
Simple merge
diff --cc src/gmond.c
Simple merge
Simple merge
diff --cc src/modbus.c
Simple merge
diff --cc src/network.c
Simple merge
diff --cc src/ntpd.c
Simple merge
diff --cc src/openvpn.c
Simple merge
diff --cc src/ping.c
Simple merge
diff --cc src/powerdns.c
Simple merge
diff --cc src/snmp.c
@@@ -402,12 -402,11 +402,11 @@@ static int csnmp_config_add_data (oconf
    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)
diff --cc src/tail.c
@@@ -221,10 -209,8 +221,9 @@@ static int ctail_config_add_match (cu_t
  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))
    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.. */
diff --cc src/threshold.c
Simple merge