Merge branch 'collectd-5.4'
authorMarc Fournier <marc.fournier@camptocamp.com>
Fri, 10 Jan 2014 14:39:31 +0000 (15:39 +0100)
committerMarc Fournier <marc.fournier@camptocamp.com>
Fri, 10 Jan 2014 14:39:31 +0000 (15:39 +0100)
Conflicts:
src/write_graphite.c

27 files changed:
.gitignore
README
bindings/java/org/collectd/java/GenericJMXConfConnection.java
configure.ac
src/apache.c
src/ascent.c
src/bind.c
src/collectd-snmp.pod
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/cpu.c
src/curl.c
src/curl_json.c
src/curl_xml.c
src/dbi.c
src/filter_chain.c
src/libcollectdclient/collectd/network.h
src/libcollectdclient/network.c
src/load.c
src/mysql.c
src/netlink.c
src/nginx.c
src/postgresql.c
src/snmp.c
src/write_http.c
src/write_riemann.c

index 7c7c848..e1120d3 100644 (file)
@@ -38,7 +38,9 @@ src/collectdctl
 src/collectdmon
 src/*.1
 src/*.5
+src/.pod2man.tmp.*
 src/libcollectdclient/collectd/lcc_features.h
+src/utils_vl_lookup_test
 
 # patch stuff
 *.rej
@@ -62,6 +64,7 @@ bindings/.perl-directory-stamp
 bindings/perl/Collectd/pm_to_blib
 bindings/perl/blib/
 bindings/perl/pm_to_blib
+bindings/buildperl
 
 # java stuff
 bindings/java/java-build-stamp
diff --git a/README b/README
index c0d7036..fa88f38 100644 (file)
--- a/README
+++ b/README
@@ -565,7 +565,7 @@ Prerequisites
 
   * A POSIX-threads (pthread) implementation.
     Since gathering some statistics is slow (network connections, slow devices,
-    etc) the collectd is parallelized. The POSIX threads interface is being
+    etc) collectd is parallelized. The POSIX threads interface is being
     used and should be found in various implementations for hopefully all
     platforms.
 
index 7fad08f..99b140a 100644 (file)
@@ -118,6 +118,7 @@ private void connect () /* {{{ */
 
     environment = new HashMap ();
     environment.put (JMXConnector.CREDENTIALS, credentials);
+    environment.put(JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER, this.getClass().getClassLoader());
   }
 
   try
index 3b9b8eb..f1c7b8a 100644 (file)
@@ -1122,6 +1122,17 @@ else
   AC_MSG_ERROR([Didn't find out how doubles are stored in memory. Sorry.])
 fi; fi; fi
 
+# --with-useragent {{{
+AC_ARG_WITH(useragent, [AS_HELP_STRING([--with-useragent@<:@=AGENT@:>@], [User agent to use on http requests])],
+[
+    if test "x$withval" != "xno" && test "x$withval" != "xyes"
+    then
+        AC_DEFINE_UNQUOTED(COLLECTD_USERAGENT, ["$withval"], [User agent for http requests])
+    fi
+])
+
+# }}}
+
 have_getfsstat="no"
 AC_CHECK_FUNCS(getfsstat, [have_getfsstat="yes"])
 have_getvfsstat="no"
@@ -2651,6 +2662,15 @@ return (retval);
 fi
 if test "x$with_libmnl" = "xyes"
 then
+       AC_CHECK_MEMBERS([struct rtnl_link_stats64.tx_window_errors],
+       [AC_DEFINE(HAVE_RTNL_LINK_STATS64, 1, [Define if struct rtnl_link_stats64 exists and is usable.])],
+       [],
+       [
+       #include <linux/if_link.h>
+       ])
+fi
+if test "x$with_libmnl" = "xyes"
+then
        AC_CHECK_LIB(mnl, mnl_nlmsg_get_payload,
                     [with_libmnl="yes"],
                     [with_libmnl="no (symbol 'mnl_nlmsg_get_payload' not found)"],
index 8458ce1..c5f099f 100644 (file)
@@ -402,7 +402,7 @@ static int init_host (apache_t *st) /* {{{ */
                curl_easy_setopt (st->curl, CURLOPT_WRITEHEADER, st);
        }
 
-       curl_easy_setopt (st->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+       curl_easy_setopt (st->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
        curl_easy_setopt (st->curl, CURLOPT_ERRORBUFFER, st->apache_curl_error);
 
        if (st->user != NULL)
index 94a3938..6809bac 100644 (file)
@@ -541,7 +541,7 @@ static int ascent_init (void) /* {{{ */
 
   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ascent_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, ascent_curl_error);
 
   if (user != NULL)
index ddde840..2f990a2 100644 (file)
@@ -1395,7 +1395,7 @@ static int bind_init (void) /* {{{ */
 
   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, bind_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, bind_curl_error);
   curl_easy_setopt (curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL);
   curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
index fd7a508..db955d6 100644 (file)
@@ -43,6 +43,17 @@ collectd-snmp - Documentation of collectd's C<snmp plugin>
       Community "another_string"
       Collect "std_traffic" "hr_users"
     </Host>
+    <Host "secure.router.mydomain.org">
+      Address "192.168.0.7"
+      Version 3
+      SecurityLevel "authPriv"
+      Username "cosmo"
+      AuthProtocol "SHA"
+      AuthPassphrase "setec_astronomy"
+      PrivacyProtocol "AES"
+      PrivacyPassphrase "too_many_secrets"
+      Collect "std_traffic"
+    </Host>
     <Host "some.ups.mydomain.org">
       Address "192.168.0.3"
       Version 1
@@ -191,6 +202,18 @@ degrees Celsius. The default value is, of course, B<0.0>.
 
 This value is not applied to counter-values.
 
+=item B<Ignore> I<Value> [, I<Value> ...]
+
+The ignore values allows to ignore Instances based on their name and the patterns
+specified by the various values you've entered. The match is a glob-type shell
+matching.
+
+=item B<InvertMatch> I<true|false(default)>
+
+The invertmatch value should be use in combination of the Ignore option.
+It changes the behaviour of the Ignore option, from a blacklist behaviour
+when InvertMatch is set to false, to a whitelist when specified to true.
+
 =back
 
 =head2 The Host block
@@ -207,14 +230,41 @@ stored by collectd.
 
 Set the address to connect to.
 
-=item B<Version> B<1>|B<2>
+=item B<Version> B<1>|B<2>|B<3>
 
 Set the SNMP version to use. When giving B<2> version C<2c> is actually used.
-Version 3 is not supported by this plugin.
 
 =item B<Community> I<Community>
 
-Pass I<Community> to the host.
+Pass I<Community> to the host. (Ignored for SNMPv3).
+
+=item B<Username> I<Username>
+
+Sets the I<Username> to use for SNMPv3 security.
+
+=item B<SecurityLevel> I<authPriv>|I<authNoPriv>|I<noAuthNoPriv>
+
+Selects the security level for SNMPv3 security.
+
+=item B<Context> I<Context>
+
+Sets the I<Context> for SNMPv3 security.
+
+=item B<AuthProtocol> I<MD5>|I<SHA>
+
+Selects the authentication protocol for SNMPv3 security.
+
+=item B<AuthPassphrase> I<Passphrase>
+
+Sets the authentication passphrase for SNMPv3 security. 
+
+=item B<PrivacyProtocol> I<AES>|I<DES>
+
+Selects the privacy (encryption) protocol for SNMPv3 security.
+
+=item B<PrivacyPassphrase> I<Passphrase>
+
+Sets the privacy (encryption) passphrase for SNMPv3 security. 
 
 =item B<Collect> I<Data> [I<Data> ...]
 
@@ -241,8 +291,9 @@ L<snmpgetnext(1)>,
 L<variables(5)>,
 L<unix(7)>
 
-=head1 AUTHOR
+=head1 AUTHORS
 
 Florian Forster E<lt>octo@verplant.orgE<gt>
+Michael Pilat E<lt>mike@mikepilat.comE<gt>
 
 =cut
index 6d19c32..4de39f0 100644 (file)
 #      InterfaceFormat name
 #</Plugin>
 
+#<Plugin load>
+#        ReportRelative true
+#</Plugin>
+
 #<Plugin lpar>
 #      CpuPoolStats   false
 #      ReportBySerial false
 #              TTLFactor 2.0
 #      </Node>
 #      Tag "foobar"
+#       Attribute "foo" "bar"
 #</Plugin>
 
 ##############################################################################
index f597c49..3a79b0f 100644 (file)
@@ -2199,6 +2199,25 @@ interface path might change between reboots of a guest or across migrations.
 
 =back
 
++=head2 Plugin C<load>
+
+The I<Load plugin> collects the system load. These numbers give a rough overview
+over the utilization of a machine. The system load is defined as the number of
+runnable tasks in the run-queue and is provided by many operating systems as a
+one, five or fifteen minute average.
+
+The following configuration options are available:
+
+=over 4
+
+=item B<ReportRelative> B<false>|B<true>
+
+When enabled, system load divided by number of available CPU cores is reported
+for intervals 1 min, 5 min and 15 min. Defaults to false.
+
+=back
+
+
 =head2 Plugin C<logfile>
 
 =over 4
@@ -6288,6 +6307,7 @@ Synopsis:
      TTLFactor 2.0
    </Node>
    Tag "foobar"
+   Attribute "foo" "bar"
  </Plugin>
 
 The following options are understood by the I<write_riemann plugin>:
@@ -6348,6 +6368,11 @@ default value.
 Add the given string as an additional tag to the metric being sent to
 I<Riemann>.
 
+=item B<Attribute> I<String> I<String>
+
+Consider the two given strings to be the key and value of an additional
+attribute for each metric being sent out to I<Riemann>.
+
 =back
 
 =head1 THRESHOLD CONFIGURATION
index 7cb405c..969aeda 100644 (file)
@@ -262,6 +262,10 @@ typedef int _Bool;
 # define COLLECTD_DEFAULT_INTERVAL 10.0
 #endif
 
+ #ifndef COLLECTD_USERAGENT
+ # define COLLECTD_USERAGENT PACKAGE_NAME"/"PACKAGE_VERSION
+ #endif
+
 /* Remove GNU specific __attribute__ settings when using another compiler */
 #if !__GNUC__
 # define __attribute__(x) /**/
index 2247d5f..f1aa4ab 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -268,6 +268,7 @@ static int cpu_read (void)
        kern_return_t status;
        
 #if PROCESSOR_CPU_LOAD_INFO
+       derive_t                       cpu_active;
        processor_cpu_load_info_data_t cpu_info;
        mach_msg_type_number_t         cpu_info_len;
 #endif
@@ -302,6 +303,11 @@ static int cpu_read (void)
                submit (cpu, "nice", (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE]);
                submit (cpu, "system", (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM]);
                submit (cpu, "idle", (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE]);
+               cpu_active = (derive_t) (cpu_info.cpu_ticks[CPU_STATE_USER] +
+                                        cpu_info.cpu_ticks[CPU_STATE_NICE] +
+                                        cpu_info.cpu_ticks[CPU_STATE_SYSTEM]);
+               submit (cpu, "active", cpu_active);
+                                        
 #endif /* PROCESSOR_CPU_LOAD_INFO */
 #if PROCESSOR_TEMPERATURE
                /*
@@ -350,6 +356,7 @@ static int cpu_read (void)
 
 #elif defined(KERNEL_LINUX)
        int cpu;
+       derive_t cpu_active;
        derive_t user, nice, syst, idle;
        derive_t wait, intr, sitr; /* sitr == soft interrupt */
        FILE *fh;
@@ -387,6 +394,7 @@ static int cpu_read (void)
                submit (cpu, "nice", nice);
                submit (cpu, "system", syst);
                submit (cpu, "idle", idle);
+               cpu_active = user + nice + syst;
 
                if (numfields >= 8)
                {
@@ -397,10 +405,14 @@ static int cpu_read (void)
                        submit (cpu, "wait", wait);
                        submit (cpu, "interrupt", intr);
                        submit (cpu, "softirq", sitr);
+                       
+                       cpu_active += wait + intr + sitr;
 
                        if (numfields >= 9)
+                               cpu_active += (derive_t) atoll (fields[8]);
                                submit (cpu, "steal", atoll (fields[8]));
                }
+               submit (cpu, "active", cpu_active);
        }
 
        fclose (fh);
@@ -428,6 +440,7 @@ static int cpu_read (void)
                submit (ksp[cpu]->ks_instance, "system", syst);
                submit (ksp[cpu]->ks_instance, "idle", idle);
                submit (ksp[cpu]->ks_instance, "wait", wait);
+               submit (ksp[cpu]->ks_instance, "active", user + syst + wait);
        }
 /* #endif defined(HAVE_LIBKSTAT) */
 
@@ -492,6 +505,10 @@ static int cpu_read (void)
                submit (i, "system",    cpuinfo[i][CP_SYS]);
                submit (i, "idle",      cpuinfo[i][CP_IDLE]);
                submit (i, "interrupt", cpuinfo[i][CP_INTR]);
+               submit (i, "active",    cpuinfo[i][CP_USER] +
+                                       cpuinfo[i][CP_NICE] +
+                                       cpuinfo[i][CP_SYS] +
+                                       cpuinfo[i][CP_INTR]);
        }
 /* #endif CAN_USE_SYSCTL */
 #elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES)
@@ -516,6 +533,10 @@ static int cpu_read (void)
                submit (i, "system", cpuinfo[i][CP_SYS]);
                submit (i, "idle", cpuinfo[i][CP_IDLE]);
                submit (i, "interrupt", cpuinfo[i][CP_INTR]);
+               submit (i, "active", cpuinfo[i][CP_USER] +
+                       cpuinfo[i][CP_NICE] +
+                       cpuinfo[i][CP_SYS] +
+                       cpuinfo[i][CP_INTR]);
        }
 /* #endif HAVE_SYSCTL_KERN_CP_TIMES */
 #elif defined(HAVE_SYSCTLBYNAME)
@@ -537,6 +558,10 @@ static int cpu_read (void)
        submit (0, "system", cpuinfo[CP_SYS]);
        submit (0, "idle", cpuinfo[CP_IDLE]);
        submit (0, "interrupt", cpuinfo[CP_INTR]);
+       submit (0, "active", cpuinfo[CP_USER] +
+               cpuinfo[CP_NICE] +
+               cpuinfo[CP_SYS] +
+               cpuinfo[CP_INTR]);
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif defined(HAVE_LIBSTATGRAB)
@@ -555,6 +580,12 @@ static int cpu_read (void)
        submit (0, "system", (derive_t) cs->kernel);
        submit (0, "user",   (derive_t) cs->user);
        submit (0, "wait",   (derive_t) cs->iowait);
+       submit (0, "active", (derive_t) cs->nice + 
+               cs->swap +
+               cs->kernel +
+               cs->user +
+               cs->iowait +
+               cs->nice);
 /* #endif HAVE_LIBSTATGRAB */
 
 #elif defined(HAVE_PERFSTAT)
@@ -593,6 +624,9 @@ static int cpu_read (void)
                submit (i, "system", (derive_t) perfcpu[i].sys);
                submit (i, "user",   (derive_t) perfcpu[i].user);
                submit (i, "wait",   (derive_t) perfcpu[i].wait);
+               submit (i, "active", (derive_t) perfcpu[i].sys +
+                       perfcpu[i].user +
+                       perfcpu[i].wait);
        }
 #endif /* HAVE_PERFSTAT */
 
index e35b258..ae23834 100644 (file)
@@ -364,8 +364,7 @@ static int cc_page_init_curl (web_page_t *wp) /* {{{ */
   curl_easy_setopt (wp->curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (wp->curl, CURLOPT_WRITEFUNCTION, cc_curl_callback);
   curl_easy_setopt (wp->curl, CURLOPT_WRITEDATA, wp);
-  curl_easy_setopt (wp->curl, CURLOPT_USERAGENT,
-      PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (wp->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (wp->curl, CURLOPT_ERRORBUFFER, wp->curl_errbuf);
   curl_easy_setopt (wp->curl, CURLOPT_URL, wp->url);
   curl_easy_setopt (wp->curl, CURLOPT_FOLLOWLOCATION, 1L);
index 1ef8a26..36cc468 100644 (file)
@@ -587,8 +587,7 @@ 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);
 
index 5d14a1d..8d50561 100644 (file)
@@ -838,8 +838,7 @@ static int cx_init_curl (cx_t *db) /* {{{ */
   curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
   curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
-  curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
-                    PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (db->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
 
index 80488d8..779c723 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -68,6 +68,8 @@ static size_t            queries_num   = 0;
 static cdbi_database_t **databases     = NULL;
 static size_t            databases_num = 0;
 
+static int cdbi_read_database (user_data_t *ud);
+
 /*
  * Functions
  */
@@ -369,9 +371,24 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     }
     else
     {
+      user_data_t ud;
+      char *name = NULL;
+
       databases = temp;
       databases[databases_num] = db;
       databases_num++;
+
+      memset (&ud, 0, sizeof (ud));
+      ud.data = (void *) db;
+      ud.free_func = NULL;
+      name = ssnprintf_alloc("dbi:%s", db->name);
+
+      plugin_register_complex_read (/* group = */ NULL,
+          /* name = */ name ? name : db->name,
+          /* callback = */ cdbi_read_database,
+          /* interval = */ NULL,
+          /* user_data = */ &ud);
+      free (name);
     }
   }
 
@@ -398,7 +415,7 @@ static int cdbi_config (oconfig_item_t *ci) /* {{{ */
       cdbi_config_add_database (child);
     else
     {
-      WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
+      WARNING ("dbi plugin: Ignoring unknown config option `%s'.", child->key);
     }
   } /* for (ci->children) */
 
@@ -758,8 +775,9 @@ static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */
   return (0);
 } /* }}} int cdbi_connect_database */
 
-static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
+static int cdbi_read_database (user_data_t *ud) /* {{{ */
 {
+  cdbi_database_t *db = (cdbi_database_t *) ud->data;
   size_t i;
   int success;
   int status;
@@ -798,29 +816,6 @@ static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
   return (0);
 } /* }}} int cdbi_read_database */
 
-static int cdbi_read (void) /* {{{ */
-{
-  size_t i;
-  int success = 0;
-  int status;
-
-  for (i = 0; i < databases_num; i++)
-  {
-    status = cdbi_read_database (databases[i]);
-    if (status == 0)
-      success++;
-  }
-
-  if (success == 0)
-  {
-    ERROR ("dbi plugin: No database could be read. Will return an error so "
-        "the plugin will be delayed.");
-    return (-1);
-  }
-
-  return (0);
-} /* }}} int cdbi_read */
-
 static int cdbi_shutdown (void) /* {{{ */
 {
   size_t i;
@@ -848,7 +843,6 @@ void module_register (void) /* {{{ */
 {
   plugin_register_complex_config ("dbi", cdbi_config);
   plugin_register_init ("dbi", cdbi_init);
-  plugin_register_read ("dbi", cdbi_read);
   plugin_register_shutdown ("dbi", cdbi_shutdown);
 } /* }}} void module_register */
 
index 7d8369b..f5606e9 100644 (file)
@@ -435,9 +435,10 @@ static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
 
 static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
 {
-  fc_chain_t *chain;
+  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))
@@ -447,17 +448,26 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  chain = (fc_chain_t *) malloc (sizeof (*chain));
+  if (chain_list_head != NULL)
+  {
+    if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL)
+      new_chain = 0;
+  }
+
   if (chain == NULL)
   {
-    ERROR ("fc_config_add_chain: malloc failed.");
-    return (-1);
+    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;
   }
-  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++)
   {
@@ -487,6 +497,9 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
 
   if (chain_list_head != NULL)
   {
+    if (!new_chain)
+      return (0);
+
     fc_chain_t *ptr;
 
     ptr = chain_list_head;
index 3962666..049f7f0 100644 (file)
@@ -65,6 +65,7 @@ int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv);
 
 /* Configure servers */
 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl);
+int lcc_server_set_interface (lcc_server_t *srv, char const *interface);
 int lcc_server_set_security_level (lcc_server_t *srv,
     lcc_security_level_t level,
     const char *username, const char *password);
index 6b6450c..ddb3b5b 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/libcollectdclient/network.c
- * Copyright (C) 2005-2012  Florian octo Forster
+ * Copyright (C) 2005-2013  Florian Forster
+ * Copyright (C) 2010       Max Henkel
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -21,7 +22,8 @@
  * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <octo at collectd.org>
+ *   Florian Forster <octo at collectd.org>
+ *   Max Henkel <henkel at gmx.at>
  **/
 
 #include "collectd.h"
 # include <netinet/in.h>
 #endif
 
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
 #include "collectd/network.h"
 #include "collectd/network_buffer.h"
 
@@ -379,6 +385,81 @@ int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
   return (0);
 } /* }}} int lcc_server_set_ttl */
 
+int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ */
+{
+  int if_index;
+  int status;
+
+  if ((srv == NULL) || (interface == NULL))
+    return (EINVAL);
+
+  if_index = if_nametoindex (interface);
+  if (if_index == 0)
+    return (ENOENT);
+
+  /* IPv4 multicast */
+  if (srv->sa->sa_family == AF_INET)
+  {
+    struct sockaddr_in *addr = (struct sockaddr_in *) srv->sa;
+
+    if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+    {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+      /* If possible, use the "ip_mreqn" structure which has
+       * an "interface index" member. Using the interface
+       * index is preferred here, because of its similarity
+       * to the way IPv6 handles this. Unfortunately, it
+       * appears not to be portable. */
+      struct ip_mreqn mreq;
+
+      memset (&mreq, 0, sizeof (mreq));
+      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+      mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+      mreq.imr_ifindex = if_index;
+#else
+      struct ip_mreq mreq;
+
+      memset (&mreq, 0, sizeof (mreq));
+      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+      mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
+
+      status = setsockopt (srv->fd, IPPROTO_IP, IP_MULTICAST_IF,
+          &mreq, sizeof (mreq));
+      if (status != 0)
+        return (status);
+
+      return (0);
+    }
+  }
+
+  /* IPv6 multicast */
+  if (srv->sa->sa_family == AF_INET6)
+  {
+    struct sockaddr_in6 *addr = (struct sockaddr_in6 *) srv->sa;
+
+    if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+    {
+      status = setsockopt (srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+          &if_index, sizeof (if_index));
+      if (status != 0)
+        return (status);
+
+      return (0);
+    }
+  }
+
+  /* else: Not a multicast interface. */
+#if defined(SO_BINDTODEVICE)
+  status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
+      interface, strlen (interface) + 1);
+  if (status != 0)
+    return (-1);
+#endif
+
+  return (0);
+} /* }}} int lcc_server_set_interface */
+
 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
     lcc_security_level_t level,
     const char *username, const char *password)
index 0188da7..9b5dd3d 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/load.c
  * Copyright (C) 2005-2008  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
+ * Copyright (C) 2013       Vedran Bartonicek
  *
  * 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
@@ -19,6 +20,7 @@
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
  *   Manuel Sanmartin
+ *   Vedran Bartonicek <vbartoni at gmail.com>
  **/
 
 #define _BSD_SOURCE
@@ -27,6 +29,8 @@
 #include "common.h"
 #include "plugin.h"
 
+#include <unistd.h>
+
 #ifdef HAVE_SYS_LOADAVG_H
 #include <sys/loadavg.h>
 #endif
 # include <libperfstat.h>
 #endif /* HAVE_PERFSTAT */
 
+static _Bool report_relative_load = 0;
+
+static const char *config_keys[] =
+{
+       "ReportRelative"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int load_config (const char *key, const char *value)
+{
+       if (strcasecmp (key, "ReportRelative") == 0)
+#ifdef _SC_NPROCESSORS_ONLN
+               report_relative_load = IS_TRUE (value) ? 1 : 0;
+#else
+                WARNING ("load plugin: The \"ReportRelative\" configuration "
+                         "is not available, because I can't determine the "
+                         "number of CPUS on this system. Sorry.");
+#endif
+       return (-1);
+
+}
 static void load_submit (gauge_t snum, gauge_t mnum, gauge_t lnum)
 {
        value_t values[3];
        value_list_t vl = VALUE_LIST_INIT;
+        int cores = 0;
+        char errbuf[1024];
+
+#ifdef  _SC_NPROCESSORS_ONLN
+        if (report_relative_load) {
+                if ((cores = sysconf(_SC_NPROCESSORS_ONLN)) < 1) {
+                       WARNING ("load: sysconf failed : %s",
+                                sstrerror (errno, errbuf, sizeof (errbuf)));
+               }
+       }
+#endif
+       if (cores > 0) {
+               snum /= cores;
+               mnum /= cores;
+               lnum /= cores;
+       }
 
        values[0].gauge = snum;
        values[1].gauge = mnum;
@@ -60,10 +101,16 @@ static void load_submit (gauge_t snum, gauge_t mnum, gauge_t lnum)
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
+
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "load", sizeof (vl.plugin));
        sstrncpy (vl.type, "load", sizeof (vl.type));
 
+       if (cores > 0) {
+               sstrncpy(vl.type_instance, "relative",
+                        sizeof (vl.type_instance));
+       }
+
        plugin_dispatch_values (&vl);
 }
 
@@ -73,17 +120,17 @@ static int load_read (void)
        double load[3];
 
        if (getloadavg (load, 3) == 3)
-               load_submit (load[LOADAVG_1MIN], load[LOADAVG_5MIN], load[LOADAVG_15MIN]);
-       else
-       {
-               char errbuf[1024];
-               WARNING ("load: getloadavg failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                load_submit (load[LOADAVG_1MIN], load[LOADAVG_5MIN], load[LOADAVG_15MIN]);
+        else
+        {
+                char errbuf[1024];
+                WARNING ("load: getloadavg failed: %s",
+                         sstrerror (errno, errbuf, sizeof (errbuf)));
        }
 /* #endif HAVE_GETLOADAVG */
 
 #elif defined(KERNEL_LINUX)
-       gauge_t snum, mnum, lnum;
+        gauge_t snum, mnum, lnum;
        FILE *loadavg;
        char buffer[16];
 
@@ -123,11 +170,11 @@ static int load_read (void)
        mnum = atof (fields[1]);
        lnum = atof (fields[2]);
 
-       load_submit (snum, mnum, lnum);
+        load_submit(snum, mnum, lnum);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBSTATGRAB
-       gauge_t snum, mnum, lnum;
+        gauge_t snum, mnum, lnum;
        sg_load_stats *ls;
 
        if ((ls = sg_get_load_stats ()) == NULL)
@@ -136,12 +183,11 @@ static int load_read (void)
        snum = ls->min1;
        mnum = ls->min5;
        lnum = ls->min15;
-
-       load_submit (snum, mnum, lnum);
+        load_submit(snum, mnum, lnum);
 /* #endif HAVE_LIBSTATGRAB */
 
 #elif HAVE_PERFSTAT
-       gauge_t snum, mnum, lnum;
+        gauge_t snum, mnum, lnum;
        perfstat_cpu_total_t cputotal;
 
        if (perfstat_cpu_total(NULL,  &cputotal, sizeof(perfstat_cpu_total_t), 1) < 0)
@@ -155,8 +201,7 @@ static int load_read (void)
        snum = (float)cputotal.loadavg[0]/(float)(1<<SBITS);
        mnum = (float)cputotal.loadavg[1]/(float)(1<<SBITS);
        lnum = (float)cputotal.loadavg[2]/(float)(1<<SBITS);
-
-       load_submit (snum, mnum, lnum);
+        load_submit(snum, mnum, lnum);
 /* #endif HAVE_PERFSTAT */
 
 #else
@@ -168,5 +213,6 @@ static int load_read (void)
 
 void module_register (void)
 {
+       plugin_register_config ("load", load_config, config_keys, config_keys_num);
        plugin_register_read ("load", load_read);
 } /* void module_register */
index d15a055..6337ab4 100644 (file)
@@ -287,6 +287,7 @@ static void set_host (mysql_database_t *db, char *buf, size_t buflen)
 {
        if ((db->host == NULL)
                        || (strcmp ("", db->host) == 0)
+                       || (strcmp ("127.0.0.1", db->host) == 0)
                        || (strcmp ("localhost", db->host) == 0))
                sstrncpy (buf, hostname_g, buflen);
        else
index 422dc8c..5e670d4 100644 (file)
 
 #include <libmnl/libmnl.h>
 
+struct ir_link_stats_storage_s {
+
+  uint64_t rx_packets;
+  uint64_t tx_packets;
+  uint64_t rx_bytes;
+  uint64_t tx_bytes;
+  uint64_t rx_errors;
+  uint64_t tx_errors;
+
+  uint64_t rx_dropped;
+  uint64_t tx_dropped;
+  uint64_t multicast;
+  uint64_t collisions;
+
+  uint64_t rx_length_errors;
+  uint64_t rx_over_errors;
+  uint64_t rx_crc_errors;
+  uint64_t rx_frame_errors;
+  uint64_t rx_fifo_errors;
+  uint64_t rx_missed_errors;
+
+  uint64_t tx_aborted_errors;
+  uint64_t tx_carrier_errors;
+  uint64_t tx_fifo_errors;
+  uint64_t tx_heartbeat_errors;
+  uint64_t tx_window_errors;
+};
+
+union ir_link_stats_u {
+  struct rtnl_link_stats *stats32;
+#ifdef HAVE_RTNL_LINK_STATS64
+  struct rtnl_link_stats64 *stats64;
+#endif
+};
+
 typedef struct ir_ignorelist_s
 {
   char *device;
@@ -235,7 +270,7 @@ static int update_iflist (struct ifinfomsg *msg, const char *dev)
 } /* int update_iflist */
 
 static void check_ignorelist_and_submit (const char *dev,
-    struct rtnl_link_stats *stats)
+    struct ir_link_stats_storage_s *stats)
 {
 
   if (check_ignorelist (dev, "interface", NULL) == 0)
@@ -275,13 +310,61 @@ static void check_ignorelist_and_submit (const char *dev,
 
 } /* void check_ignorelist_and_submit */
 
+#define COPY_RTNL_LINK_VALUE (dst_stats, src_stats, value_name) \
+  (dst_stats)->value_name = (src_stats)->value_name
+
+#define COPY_RTNL_LINK_STATS (dst_stats, src_stats) \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_packets); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_packets); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_bytes); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_bytes); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_dropped); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_dropped); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, multicast); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, collisions); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_length_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_over_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_crc_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_frame_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_fifo_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_missed_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_aborted_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_carrier_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_fifo_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_heartbeat_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_window_errors)
+
+#ifdef HAVE_RTNL_LINK_STATS64
+static void check_ignorelist_and_submit64 (const char *dev,
+    struct rtnl_link_stats64 *stats)
+{
+  struct ir_link_stats_storage_s s;
+
+  COPY_RTNL_LINK_STATS (&s, stats);
+
+  check_ignorelist_and_submit (dev, &s);
+}
+#endif
+
+static void check_ignorelist_and_submit32 (const char *dev,
+    struct rtnl_link_stats *stats)
+{
+  struct ir_link_stats_storage_s s;
+
+  COPY_RTNL_LINK_STATS(&s, stats);
+
+  check_ignorelist_and_submit (dev, &s);
+}
+
 static int link_filter_cb (const struct nlmsghdr *nlh,
     void *args __attribute__((unused)))
 {
   struct ifinfomsg *ifm = mnl_nlmsg_get_payload (nlh);
   struct nlattr *attr;
-  struct rtnl_link_stats *stats = NULL;
   const char *dev = NULL;
+  union ir_link_stats_u stats;
 
   if (nlh->nlmsg_type != RTM_NEWLINK)
   {
@@ -313,30 +396,44 @@ static int link_filter_cb (const struct nlmsghdr *nlh,
     ERROR ("netlink plugin: link_filter_cb: dev == NULL");
     return MNL_CB_ERROR;
   }
+#ifdef HAVE_RTNL_LINK_STATS64
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_STATS64)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats64)) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_STATS64 mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    stats.stats64 = mnl_attr_get_payload (attr);
+
+    check_ignorelist_and_submit64 (dev, stats.stats64);
 
+    return MNL_CB_OK;
+  }
+#endif
   mnl_attr_for_each (attr, nlh, sizeof (*ifm))
   {
     if (mnl_attr_get_type (attr) != IFLA_STATS)
       continue;
 
-    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats)) < 0)
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats32)) < 0)
     {
       ERROR ("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 failed.");
       return MNL_CB_ERROR;
     }
-    stats = mnl_attr_get_payload (attr);
+    stats.stats32 = mnl_attr_get_payload (attr);
 
-    check_ignorelist_and_submit (dev, stats);
-    break;
-  }
+    check_ignorelist_and_submit32 (dev, stats.stats32);
 
-  if (stats == NULL)
-  {
-    DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
     return MNL_CB_OK;
   }
 
+  DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
   return MNL_CB_OK;
+
 } /* int link_filter_cb */
 
 #if HAVE_TCA_STATS2
index de52262..c356353 100644 (file)
@@ -122,7 +122,7 @@ static int init (void)
 
   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, nginx_curl_error);
 
   if (user != NULL)
index 1eaf6bd..16e539a 100644 (file)
@@ -571,6 +571,7 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
        }
 
        if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
+                       || (0 == strcmp (db->host, "127.0.0.1"))
                        || (0 == strcmp (db->host, "localhost")))
                host = hostname_g;
        else
index ad81c89..2bb7c2a 100644 (file)
@@ -29,6 +29,8 @@
 #include <net-snmp/net-snmp-config.h>
 #include <net-snmp/net-snmp-includes.h>
 
+#include <fnmatch.h>
+
 /*
  * Private data structes
  */
@@ -50,7 +52,7 @@ struct data_definition_s
 {
   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;
@@ -58,6 +60,9 @@ struct data_definition_s
   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;
 
@@ -65,8 +70,22 @@ struct host_definition_s
 {
   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;
@@ -183,6 +202,10 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */
   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);
@@ -197,16 +220,15 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */
  *  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)
 {
@@ -217,60 +239,31 @@ 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));
   }
 
@@ -280,11 +273,7 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
 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)
   {
@@ -293,12 +282,8 @@ static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
     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)
@@ -344,33 +329,49 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *
   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)
 {
@@ -378,24 +379,18 @@ 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;
 
@@ -405,9 +400,9 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
     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)
@@ -415,9 +410,13 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
     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);
@@ -451,6 +450,7 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
     sfree (dd->name);
     sfree (dd->instance_prefix);
     sfree (dd->values);
+    sfree (dd->ignores);
     sfree (dd);
     return (-1);
   }
@@ -472,50 +472,6 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
   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;
@@ -528,9 +484,9 @@ static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t
   }
 
   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);
   }
 
@@ -590,6 +546,92 @@ static int csnmp_config_add_host_collect (host_definition_t *host,
   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;
@@ -601,12 +643,6 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   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);
@@ -614,12 +650,9 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   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;
@@ -630,15 +663,29 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
     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);
@@ -657,12 +704,57 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
       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) */
@@ -724,15 +816,75 @@ static int csnmp_config (oconfig_item_t *ci)
 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);
@@ -971,6 +1123,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   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;
@@ -1004,7 +1158,29 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     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))
@@ -1655,6 +1831,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;
index 7f5943a..25133c3 100644 (file)
@@ -112,7 +112,7 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */
         }
 
         curl_easy_setopt (cb->curl, CURLOPT_NOSIGNAL, 1L);
-        curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+        curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
 
         headers = NULL;
         headers = curl_slist_append (headers, "Accept:  */*");
index 3345d04..3a7738f 100644 (file)
@@ -57,6 +57,8 @@ struct riemann_host {
 
 static char    **riemann_tags;
 static size_t    riemann_tags_num;
+static char    **riemann_attrs;
+static size_t     riemann_attrs_num;
 
 static void riemann_event_protobuf_free (Event *event) /* {{{ */
 {
@@ -178,32 +180,30 @@ riemann_disconnect (struct riemann_host *host)
        return (0);
 }
 
-static int
-riemann_send(struct riemann_host *host, Msg const *msg)
+static inline int
+riemann_send_msg(struct riemann_host *host, const Msg *msg)
 {
-       u_char *buffer;
+       int status = 0;
+       u_char *buffer = NULL;
        size_t  buffer_len;
-       int status;
-
-       pthread_mutex_lock (&host->lock);
 
        status = riemann_connect (host);
+
        if (status != 0)
-       {
-               pthread_mutex_unlock (&host->lock);
                return status;
-       }
 
        buffer_len = msg__get_packed_size(msg);
+
        if (host->use_tcp)
                buffer_len += 4;
 
        buffer = malloc (buffer_len);
+
        if (buffer == NULL) {
-               pthread_mutex_unlock (&host->lock);
                ERROR ("write_riemann plugin: malloc failed.");
                return ENOMEM;
        }
+
        memset (buffer, 0, buffer_len);
 
        if (host->use_tcp)
@@ -218,26 +218,105 @@ riemann_send(struct riemann_host *host, Msg const *msg)
        }
 
        status = (int) swrite (host->s, buffer, buffer_len);
+
        if (status != 0)
        {
                char errbuf[1024];
 
-               riemann_disconnect (host);
-               pthread_mutex_unlock (&host->lock);
-
                ERROR ("write_riemann plugin: Sending to Riemann at %s:%s failed: %s",
                                (host->node != NULL) ? host->node : RIEMANN_HOST,
                                (host->service != NULL) ? host->service : RIEMANN_PORT,
                                sstrerror (errno, errbuf, sizeof (errbuf)));
+
                sfree (buffer);
                return -1;
        }
 
-       pthread_mutex_unlock (&host->lock);
        sfree (buffer);
        return 0;
 }
 
+static inline int
+riemann_recv_ack(struct riemann_host *host)
+{
+       int status = 0;
+       Msg *msg = NULL;
+       uint32_t header;
+
+       status = (int) sread (host->s, &header, 4);
+
+       if (status != 0)
+               return -1;
+
+       size_t size = ntohl(header);
+
+       // Buffer on the stack since acknowledges are typically small.
+       u_char buffer[size];
+       memset (buffer, 0, size);
+
+       status = (int) sread (host->s, buffer, size);
+
+       if (status != 0)
+               return status;
+
+       msg = msg__unpack (NULL, size, buffer);
+
+       if (msg == NULL)
+               return -1;
+
+       if (!msg->ok)
+       {
+               ERROR ("write_riemann plugin: Sending to Riemann at %s:%s acknowledgement message reported error: %s",
+                               (host->node != NULL) ? host->node : RIEMANN_HOST,
+                               (host->service != NULL) ? host->service : RIEMANN_PORT,
+                               msg->error);
+
+               msg__free_unpacked(msg, NULL);
+               return -1;
+       }
+
+       msg__free_unpacked (msg, NULL);
+       return 0;
+}
+
+/**
+ * Function to send messages (Msg) to riemann.
+ *
+ * Acquires the host lock, disconnects on errors.
+ */
+static int
+riemann_send(struct riemann_host *host, Msg const *msg)
+{
+       int status = 0;
+       pthread_mutex_lock (&host->lock);
+
+       status = riemann_send_msg(host, msg);
+
+       if (status != 0) {
+               riemann_disconnect (host);
+               pthread_mutex_unlock (&host->lock);
+               return status;
+       }
+
+       /*
+        * For TCP we need to receive message acknowledgemenent.
+        */
+       if (host->use_tcp)
+       {
+               status = riemann_recv_ack(host);
+
+               if (status != 0)
+               {
+                       riemann_disconnect (host);
+                       pthread_mutex_unlock (&host->lock);
+                       return status;
+               }
+       }
+
+       pthread_mutex_unlock (&host->lock);
+       return 0;
+}
+
 static int riemann_event_add_tag (Event *event, char const *tag) /* {{{ */
 {
        return (strarray_add (&event->tags, &event->n_tags, tag));
@@ -345,6 +424,11 @@ static Msg *riemann_notification_to_protobuf (struct riemann_host *host, /* {{{
                riemann_event_add_attribute (event, "type_instance",
                                n->type_instance);
 
+       for (i = 0; i < riemann_attrs_num; i += 2)
+               riemann_event_add_attribute(event,
+                                           riemann_attrs[i],
+                                           riemann_attrs[i +1]);
+
        for (i = 0; i < riemann_tags_num; i++)
                riemann_event_add_tag (event, riemann_tags[i]);
 
@@ -353,15 +437,20 @@ static Msg *riemann_notification_to_protobuf (struct riemann_host *host, /* {{{
                        n->type, n->type_instance);
        event->service = strdup (&service_buffer[1]);
 
-       /* Pull in values from threshold */
+       /* Pull in values from threshold and add extra attributes */
        for (meta = n->meta; meta != NULL; meta = meta->next)
        {
-               if (strcasecmp ("CurrentValue", meta->name) != 0)
+               if (strcasecmp ("CurrentValue", meta->name) == 0 && meta->type == NM_TYPE_DOUBLE)
+               {
+                       event->metric_d = meta->nm_value.nm_double;
+                       event->has_metric_d = 1;
                        continue;
+               }
 
-               event->metric_d = meta->nm_value.nm_double;
-               event->has_metric_d = 1;
-               break;
+               if (meta->type == NM_TYPE_STRING) {
+                       riemann_event_add_attribute (event, meta->name, meta->nm_value.nm_string);
+                       continue;
+               }
        }
 
        DEBUG ("write_riemann plugin: Successfully created protobuf for notification: "
@@ -429,6 +518,11 @@ static Event *riemann_value_to_protobuf (struct riemann_host const *host, /* {{{
                riemann_event_add_attribute (event, "ds_index", ds_index);
        }
 
+       for (i = 0; i < riemann_attrs_num; i += 2)
+               riemann_event_add_attribute(event,
+                                           riemann_attrs[i],
+                                           riemann_attrs[i +1]);
+
        for (i = 0; i < riemann_tags_num; i++)
                riemann_event_add_tag (event, riemann_tags[i]);
 
@@ -763,6 +857,32 @@ riemann_config(oconfig_item_t *ci)
 
                if (strcasecmp("Node", child->key) == 0) {
                        riemann_config_node (child);
+               } else if (strcasecmp(child->key, "attribute") == 0) {
+                       char *key = NULL;
+                       char *val = NULL;
+
+                       if (child->values_num != 2) {
+                               WARNING("riemann attributes need both a key and a value.");
+                               return (-1);
+                       }
+                       if (child->values[0].type != OCONFIG_TYPE_STRING ||
+                           child->values[1].type != OCONFIG_TYPE_STRING) {
+                               WARNING("riemann attribute needs string arguments.");
+                               return (-1);
+                       }
+                       if ((key = strdup(child->values[0].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute key.");
+                               return (-1);
+                       }
+                       if ((val = strdup(child->values[1].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute value.");
+                               return (-1);
+                       }
+                       strarray_add(&riemann_attrs, &riemann_attrs_num, key);
+                       strarray_add(&riemann_attrs, &riemann_attrs_num, val);
+                       DEBUG("write_riemann: got attr: %s => %s", key, val);
+                       sfree(key);
+                       sfree(val);
                } else if (strcasecmp(child->key, "tag") == 0) {
                        char *tmp = NULL;
                        status = cf_util_get_string(child, &tmp);