Merge branch 'collectd-5.6'
authorFlorian Forster <octo@collectd.org>
Tue, 13 Sep 2016 20:27:34 +0000 (22:27 +0200)
committerFlorian Forster <octo@collectd.org>
Tue, 13 Sep 2016 20:27:34 +0000 (22:27 +0200)
Conflicts:
src/collectd.conf.in

57 files changed:
NEWS [deleted file]
README
TODO [deleted file]
configure.ac
contrib/redhat/collectd.spec
proto/collectd.proto
src/Makefile.am
src/amqp.c
src/apache.c
src/battery.c
src/battery_statefs.c [new file with mode: 0644]
src/ceph.c
src/collectd.conf.in
src/collectd.conf.pod
src/conntrack.c
src/cpufreq.c
src/curl_json.c
src/curl_xml.c
src/daemon/common.c
src/daemon/common.h
src/daemon/meta_data.c
src/daemon/meta_data.h
src/dbi.c
src/df.c
src/disk.c
src/entropy.c
src/grpc.cc
src/hugepages.c [new file with mode: 0644]
src/java.c
src/lua.c
src/match_regex.c
src/memcached.c
src/modbus.c
src/mqtt.c
src/mysql.c
src/netapp.c
src/onewire.c
src/openldap.c
src/python.c
src/routeros.c
src/snmp.c
src/tail.c
src/tail_csv.c
src/target_replace.c
src/target_set.c
src/thermal.c
src/turbostat.c
src/types.db
src/utils_format_graphite.c
src/utils_format_graphite.h
src/varnish.c
src/write_graphite.c
src/write_http.c
src/write_kafka.c
src/write_log.c
src/write_mongodb.c
src/write_redis.c

diff --git a/NEWS b/NEWS
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/README b/README
index ee909d6..e8f0ca1 100644 (file)
--- a/README
+++ b/README
@@ -134,6 +134,11 @@ Features
     - hddtemp
       Hard disk temperatures using hddtempd.
 
+    - hugepages
+      Report the number of used and free hugepages. More info on
+      hugepages can be found here:
+      https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt.
+
     - interface
       Interface traffic: Number of octets, packets and errors for each
       interface.
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 009eb7f..0000000
--- a/TODO
+++ /dev/null
@@ -1,21 +0,0 @@
-* Finalize the onewire plugin.
-* Custom notification messages?
-* Implement moving-average calculation for the threshold stuff.
-
-src/battery.c: commend not working code.
-
-Wishlist:
-* Port nfs module to solaris
-* Port tape module to Linux
-* Port the apple_sensors plugin to Linux/PPC.
-* Maybe look into porting the serial module
-* Build Darwin package
-* Maybe let the network plugin configure whether or not notifications should be
-  sent/received.
-* Maybe find a way for processes connected to the unixsock plugin to receive
-  notifications, too.
-
-http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_IOKitLib_API/chapter_5_section_1.html
-http://developer.apple.com/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/index.html#//apple_ref/doc/uid/TP0000011
-http://www.gauchosoft.com/Software/X%20Resource%20Graph/
-http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification/
index 9a6da11..929205b 100644 (file)
@@ -5709,6 +5709,7 @@ plugin_fhcount="no"
 plugin_fscache="no"
 plugin_gps="no"
 plugin_grpc="no"
+plugin_hugepages="no"
 plugin_interface="no"
 plugin_ipmi="no"
 plugin_ipvs="no"
@@ -5757,6 +5758,7 @@ then
        plugin_entropy="yes"
        plugin_fhcount="yes"
        plugin_fscache="yes"
+       plugin_hugepages="yes"
        plugin_interface="yes"
        plugin_ipc="yes"
        plugin_irq="yes"
@@ -6163,6 +6165,7 @@ AC_PLUGIN([gmond],               [$with_libganglia],        [Ganglia plugin])
 AC_PLUGIN([gps],                 [$plugin_gps],             [GPS plugin])
 AC_PLUGIN([grpc],                [$plugin_grpc],            [gRPC plugin])
 AC_PLUGIN([hddtemp],             [yes],                     [Query hddtempd])
+AC_PLUGIN([hugepages],           [$plugin_hugepages],       [Hugepages statistics])
 AC_PLUGIN([interface],           [$plugin_interface],       [Interface traffic statistics])
 AC_PLUGIN([ipc],                 [$plugin_ipc],             [IPC statistics])
 AC_PLUGIN([ipmi],                [$plugin_ipmi],            [IPMI sensor statistics])
@@ -6593,6 +6596,7 @@ AC_MSG_RESULT([    gmond . . . . . . . . $enable_gmond])
 AC_MSG_RESULT([    gps . . . . . . . . . $enable_gps])
 AC_MSG_RESULT([    grpc  . . . . . . . . $enable_grpc])
 AC_MSG_RESULT([    hddtemp . . . . . . . $enable_hddtemp])
+AC_MSG_RESULT([    hugepages . . . . . . $enable_hugepages])
 AC_MSG_RESULT([    interface . . . . . . $enable_interface])
 AC_MSG_RESULT([    ipc . . . . . . . . . $enable_ipc])
 AC_MSG_RESULT([    ipmi  . . . . . . . . $enable_ipmi])
index 2061573..73e69c7 100644 (file)
@@ -73,6 +73,7 @@
 %define with_gmond 0%{!?_without_gmond:1}
 %define with_gps 0%{!?_without_gps:1}
 %define with_hddtemp 0%{!?_without_hddtemp:1}
+%define with_hugepages 0%{!?_without_hugepages:1}
 %define with_interface 0%{!?_without_interface:1}
 %define with_ipc 0%{!?_without_ipc:1}
 %define with_ipmi 0%{!?_without_ipmi:1}
 
 Summary:       Statistics collection and monitoring daemon
 Name:          collectd
-Version:       5.6.0
+Version:       5.7.0
 Release:       2%{?dist}
 URL:           https://collectd.org
 Source:                https://collectd.org/files/%{name}-%{version}.tar.bz2
@@ -1134,6 +1135,12 @@ Collectd utilities
 %define _with_hddtemp --disable-hddtemp
 %endif
 
+%if %{with_hugepages}
+%define _with_hugepages --enable-hugepages
+%else
+%define _with_hugepages --disable-hugepages
+%endif
+
 %if %{with_interface}
 %define _with_interface --enable-interface
 %else
@@ -1762,6 +1769,7 @@ Collectd utilities
        %{?_with_gps} \
        %{?_with_grpc} \
        %{?_with_hddtemp} \
+       %{?_with_hugepages} \
        %{?_with_interface} \
        %{?_with_ipc} \
        %{?_with_ipmi} \
@@ -2045,6 +2053,9 @@ fi
 %if %{with_fscache}
 %{_libdir}/%{name}/fscache.so
 %endif
+%if %{with_hugepages}
+%{_libdir}/%{name}/hugepages.so
+%endif
 %if %{with_interface}
 %{_libdir}/%{name}/interface.so
 %endif
@@ -2512,6 +2523,10 @@ fi
 %doc contrib/
 
 %changelog
+* Tue Aug 23 2016 Marc Fournier <marc.fournier@camptocamp.com> - 5.7.0-1
+- New PRE-RELEASE version
+- New plugins enabled by default: hugepages
+
 * Sun Aug 14 2016 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.6.0-1
 - New PRE-RELEASE version
 - New plugins enabled by default: chrony, cpusleep, gps, lua, mqtt, notify_nagios
index 917c5de..614d1bd 100644 (file)
@@ -30,25 +30,25 @@ option go_package = "collectd.org/rpc/proto";
 import "types.proto";
 
 service Collectd {
-  // DispatchValues reads the value lists from the DispatchValuesRequest stream.
+  // PutValues reads the value lists from the PutValuesRequest stream.
   // The gRPC server embedded into collectd will inject them into the system
   // just like the network plugin.
-  rpc DispatchValues(stream DispatchValuesRequest)
-      returns (DispatchValuesResponse);
+  rpc PutValues(stream PutValuesRequest)
+      returns (PutValuesResponse);
 
   // QueryValues returns a stream of matching value lists from collectd's
   // internal cache.
   rpc QueryValues(QueryValuesRequest) returns (stream QueryValuesResponse);
 }
 
-// The arguments to DispatchValues.
-message DispatchValuesRequest {
+// The arguments to PutValues.
+message PutValuesRequest {
   // value_list is the metric to be sent to the server.
   collectd.types.ValueList value_list = 1;
 }
 
-// The response from DispatchValues.
-message DispatchValuesResponse {}
+// The response from PutValues.
+message PutValuesResponse {}
 
 // The arguments to QueryValues.
 message QueryValuesRequest {
index dbd5bb9..3477dc2 100644 (file)
@@ -213,7 +213,7 @@ endif
 
 if BUILD_PLUGIN_BATTERY
 pkglib_LTLIBRARIES += battery.la
-battery_la_SOURCES = battery.c
+battery_la_SOURCES = battery.c battery_statefs.c
 battery_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 if BUILD_WITH_LIBIOKIT
 battery_la_LDFLAGS += -framework IOKit
@@ -470,6 +470,12 @@ hddtemp_la_LIBADD += -lsocket
 endif
 endif
 
+if BUILD_PLUGIN_HUGEPAGES
+pkglib_LTLIBRARIES += hugepages.la
+hugepages_la_SOURCES = hugepages.c
+hugepages_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+endif
+
 if BUILD_PLUGIN_INTERFACE
 pkglib_LTLIBRARIES += interface.la
 interface_la_SOURCES = interface.c
@@ -1279,6 +1285,7 @@ pkglib_LTLIBRARIES += write_log.la
 write_log_la_SOURCES = write_log.c \
                         utils_format_graphite.c utils_format_graphite.h
 write_log_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+write_log_la_LIBADD = libformat_json.la
 endif
 
 if BUILD_PLUGIN_WRITE_MONGODB
index 89f051e..06fd1f1 100644 (file)
@@ -1070,11 +1070,13 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
     if (publish)
     {
         char cbname[128];
-        user_data_t ud = { conf, camqp_config_free };
-
         ssnprintf (cbname, sizeof (cbname), "amqp/%s", conf->name);
 
-        status = plugin_register_write (cbname, camqp_write, &ud);
+        status = plugin_register_write (cbname, camqp_write,
+                &(user_data_t) {
+                    .data = conf,
+                    .free_func = camqp_config_free,
+                });
         if (status != 0)
         {
             camqp_config_free (conf);
index b57d08e..f0af8c3 100644 (file)
@@ -239,11 +239,6 @@ static int config_add (oconfig_item_t *ci)
 
        if (status == 0)
        {
-               user_data_t ud = {
-                       .data = st,
-                       .free_func = apache_free
-               };
-
                char callback_name[3*DATA_MAX_NAME_LEN];
 
                ssnprintf (callback_name, sizeof (callback_name),
@@ -255,7 +250,11 @@ static int config_add (oconfig_item_t *ci)
                                /* name      = */ callback_name,
                                /* callback  = */ apache_read_host,
                                /* interval  = */ 0,
-                               /* user_data = */ &ud);
+                               &(user_data_t) {
+                                       .data = st,
+                                       .free_func = apache_free,
+                               });
+
        }
 
        if (status != 0)
index a084319..d227d55 100644 (file)
 # define SYSFS_FACTOR 0.000001
 #endif /* KERNEL_LINUX */
 
+int battery_read_statefs (void); /* defined in battery_statefs; used by StateFS backend */
+
 static _Bool report_percent = 0;
 static _Bool report_degraded = 0;
+static _Bool query_statefs = 0;
 
 static void battery_submit2 (char const *plugin_instance, /* {{{ */
                char const *type, char const *type_instance, gauge_t value)
@@ -359,6 +362,9 @@ static int battery_read (void) /* {{{ */
        gauge_t capacity_full = NAN; /* Total capacity */
        gauge_t capacity_design = NAN; /* Full design capacity */
 
+       if (query_statefs)
+               return battery_read_statefs ();
+
 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H
        get_via_io_power_sources (&charge_rel, &current, &voltage);
 #endif
@@ -386,47 +392,17 @@ static int sysfs_file_to_buffer(char const *dir, /* {{{ */
                char const *basename,
                char *buffer, size_t buffer_size)
 {
-       int status;
-       FILE *fp;
        char filename[PATH_MAX];
+       int status;
 
        ssnprintf (filename, sizeof (filename), "%s/%s/%s",
                        dir, power_supply, basename);
 
-       /* No file isn't the end of the world -- not every system will be
-        * reporting the same set of statistics */
-       if (access (filename, R_OK) != 0)
-               return ENOENT;
-
-       fp = fopen (filename, "r");
-       if (fp == NULL)
-       {
-               status = errno;
-               if (status != ENOENT)
-               {
-                       char errbuf[1024];
-                       WARNING ("battery plugin: fopen (%s) failed: %s", filename,
-                                       sstrerror (status, errbuf, sizeof (errbuf)));
-               }
+       status = (int) read_file_contents (filename, buffer, buffer_size);
+       if (status < 0)
                return status;
-       }
-
-       if (fgets (buffer, buffer_size, fp) == NULL)
-       {
-               status = errno;
-               if (status != ENODEV)
-               {
-                       char errbuf[1024];
-                       WARNING ("battery plugin: fgets (%s) failed: %s", filename,
-                                       sstrerror (status, errbuf, sizeof (errbuf)));
-               }
-               fclose (fp);
-               return status;
-       }
 
        strstripnewline (buffer);
-
-       fclose (fp);
        return 0;
 } /* }}} int sysfs_file_to_buffer */
 
@@ -778,6 +754,9 @@ static int battery_read (void) /* {{{ */
 {
        int status;
 
+       if (query_statefs)
+               return battery_read_statefs ();
+
        DEBUG ("battery plugin: Trying sysfs ...");
        status = read_sysfs ();
        if (status == 0)
@@ -808,6 +787,8 @@ static int battery_config (oconfig_item_t *ci)
                        cf_util_get_boolean (child, &report_percent);
                else if (strcasecmp ("ReportDegraded", child->key) == 0)
                        cf_util_get_boolean (child, &report_degraded);
+               else if (strcasecmp ("QueryStateFS", child->key) == 0)
+                       cf_util_get_boolean (child, &query_statefs);
                else
                        WARNING ("battery plugin: Ignoring unknown "
                                        "configuration option \"%s\".",
diff --git a/src/battery_statefs.c b/src/battery_statefs.c
new file mode 100644 (file)
index 0000000..ee739bf
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ * collectd - src/statefs_battery.c
+ * Copyright (C) 2016 rinigus
+ *
+ *
+The MIT License (MIT)
+
+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:
+ *   rinigus <http://github.com/rinigus>
+
+ Battery stats are collected from StateFS Battery namespace. Reported
+ units are as follows:
+
+ capacity %
+ charge %
+ current A
+ energy Wh
+ power W
+ temperature C
+ timefull and timelow seconds
+ voltage V
+
+ Provider at
+ https://git.merproject.org/mer-core/statefs-providers/blob/master/src/power_udev/provider_power_udev.cpp
+
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <stdio.h>
+
+#define STATEFS_ROOT "/run/state/namespaces/Battery/"
+
+static void battery_submit(const char *type, gauge_t value,
+                           const char *type_instance) {
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+  sstrncpy(vl.plugin, "battery", sizeof(vl.plugin));
+  /* statefs supports 1 battery at present */
+  sstrncpy(vl.plugin_instance, "0", sizeof(vl.plugin_instance));
+  sstrncpy(vl.type, type, sizeof(vl.type));
+  if (type_instance != NULL)
+    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+  plugin_dispatch_values(&vl);
+}
+
+/* cannot be static, is referred to from battery.c */
+int battery_read_statefs(void) {
+  value_t v;
+  int success = 0;
+
+  if (parse_value_file(STATEFS_ROOT "ChargePercentage", &v, DS_TYPE_GAUGE) == 0) {
+    battery_submit("charge", v.gauge, NULL);
+    success++;
+  } else if (parse_value_file(STATEFS_ROOT "Capacity", &v, DS_TYPE_GAUGE) == 0) {
+    // Use capacity as a charge estimate if ChargePercentage is not available
+    battery_submit("charge", v.gauge, NULL);
+    success++;
+  } else {
+    WARNING("battery plugin: Neither \""STATEFS_ROOT"ChargePercentage\" "
+            "nor \""STATEFS_ROOT"Capacity\" could be read.");
+  }
+
+  struct {
+    char *path;
+    char *type;
+    char *type_instance;
+    gauge_t factor;
+  } metrics[] = {
+    {STATEFS_ROOT "Current",       "current",     NULL,  1e-6}, // from uA to A
+    {STATEFS_ROOT "Energy",        "energy_wh",   NULL,  1e-6}, // from uWh to Wh
+    {STATEFS_ROOT "Power",         "power",       NULL,  1e-6}, // from uW to W
+    {STATEFS_ROOT "Temperature",   "temperature", NULL,   0.1}, // from 10xC to C
+    {STATEFS_ROOT "TimeUntilFull", "duration",    "full", 1.0},
+    {STATEFS_ROOT "TimeUntilLow",  "duration",    "low",  1.0},
+    {STATEFS_ROOT "Voltage",       "voltage",     NULL,  1e-6}, // from uV to V
+  };
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(metrics); i++) {
+    if (parse_value_file(metrics[i].path, &v, DS_TYPE_GAUGE) != 0) {
+      WARNING("battery plugin: Reading \"%s\" failed.", metrics[i].path);
+      continue;
+    }
+
+    battery_submit(metrics[i].type, v.gauge * metrics[i].factor, metrics[i].type_instance);
+    success++;
+  }
+
+  if (success == 0) {
+    ERROR("battery plugin: statefs backend: none of the statistics are available");
+    return (-1);
+  }
+
+  return (0);
+}
index e6d3767..64d8785 100644 (file)
@@ -1468,15 +1468,22 @@ static int cconn_main_loop(uint32_t request_type)
     struct timeval end_tv;
     struct cconn io_array[g_num_daemons];
 
-    DEBUG("ceph plugin: entering cconn_main_loop(request_type = %d)", request_type);
+    DEBUG ("ceph plugin: entering cconn_main_loop(request_type = %"PRIu32")", request_type);
+
+    if (g_num_daemons < 1)
+    {
+        ERROR ("ceph plugin: No daemons configured. See the \"Daemon\" config option.");
+        return ENOENT;
+    }
 
     /* create cconn array */
-    memset(io_array, 0, sizeof(io_array));
-    for(size_t i = 0; i < g_num_daemons; ++i)
+    for (size_t i = 0; i < g_num_daemons; i++)
     {
-        io_array[i].d = g_daemons[i];
-        io_array[i].request_type = request_type;
-        io_array[i].state = CSTATE_UNCONNECTED;
+        io_array[i] = (struct cconn) {
+            .d = g_daemons[i],
+            .request_type = request_type,
+            .state = CSTATE_UNCONNECTED,
+        };
     }
 
     /** Calculate the time at which we should give up */
index f0ac287..6a207ab 100644 (file)
 #@BUILD_PLUGIN_GPS_TRUE@LoadPlugin gps
 #@BUILD_PLUGIN_GRPC_TRUE@LoadPlugin grpc
 #@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
+#@BUILD_PLUGIN_HUGEPAGES_TRUE@LoadPlugin hugepages
 @BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
 #@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc
 #@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi
 #<Plugin "battery">
 #  ValuesPercentage false
 #  ReportDegraded false
+#  QueryStateFS false
 #</Plugin>
 
 #<Plugin "bind">
 #  Port "7634"
 #</Plugin>
 
+#<Plugin hugepages>
+#    ReportPerNodeHP "true"
+#    ReportRootHP "true"
+#</Plugin>
+
 #<Plugin interface>
 #      Interface "eth0"
 #      IgnoreSelected false
 #    AlwaysAppendDS false
 #    EscapeCharacter "_"
 #    SeparateInstances false
+#    DropDuplicateFields false
 #  </Node>
 #</Plugin>
 
index 090f18e..baef1c3 100644 (file)
@@ -1114,6 +1114,13 @@ When set to B<true>, the battery plugin will report three values: B<charged>
 and "remaining capacity") and B<degraded> (difference between "design capacity"
 and "last full capacity").
 
+=item B<QueryStateFS> B<false>|B<true>
+
+When set to B<true>, the battery plugin will only read statistics
+related to battery performance as exposed by StateFS at
+/run/state. StateFS is used in Mer-based Sailfish OS, for
+example.
+
 =back
 
 =head2 Plugin C<bind>
@@ -2819,6 +2826,32 @@ TCP-Port to connect to. Defaults to B<7634>.
 
 =back
 
+=head2 Plugin C<hugepages>
+
+To collect B<hugepages> information, collectd reads directories
+"/sys/devices/system/node/*/hugepages" and
+"/sys/kernel/mm/hugepages".
+Reading of these directories can be disabled by the following
+options (default is enabled).
+
+=over 4
+
+=item B<ReportPerNodeHP> I<true>|I<false>
+
+If enabled, information will be collected from the hugepage
+counters in "/sys/devices/system/node/*/hugepages".
+This is used to check the per-node hugepage statistics on
+a NUMA system.
+
+=item B<ReportRootHP> I<true>|I<false>
+
+If enabled, information will be collected from the hugepage
+counters in "/sys/kernel/mm/hugepages".
+This can be used on both NUMA and non-NUMA systems to check
+the overall hugepage statistics.
+
+=back
+
 =head2 Plugin C<interface>
 
 =over 4
@@ -7791,6 +7824,32 @@ If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
 identifier. If set to B<false> (the default), this is only done when there is
 more than one DS.
 
+=item B<DropDuplicateFields> B<false>|B<true>
+
+If set to B<true>, detect and remove duplicate components in Graphite metric
+names. For example, the metric name  C<host.load.load.shortterm> will
+be shortened to C<host.load.shortterm>.
+
+=back
+
+=head2 Plugin C<write_log>
+
+The C<write_log> plugin writes metrics as INFO log messages.
+
+This plugin supports two output formats: I<Graphite> and I<JSON>.
+
+Synopsis:
+
+ <Plugin write_log>
+   Format Graphite
+ </Plugin>
+
+=over 4
+
+=item B<Format> I<Format>
+
+The output format to use. Can be one of C<Graphite> or C<JSON>.
+
 =back
 
 =head2 Plugin C<write_tsdb>
@@ -9066,6 +9125,8 @@ Available options:
 
 =item B<TypeInstance> I<Regex>
 
+=item B<MetaData> I<String> I<Regex>
+
 Match values where the given regular expressions match the various fields of
 the identifier of a value. If multiple regular expressions are given, B<all>
 regexen must match for a value to match.
@@ -9354,6 +9415,10 @@ Available options:
 
 =item B<TypeInstance> I<Regex> I<Replacement>
 
+=item B<MetaData> I<String> I<Regex> I<Replacement>
+
+=item B<DeleteMetaData> I<String> I<Regex>
+
 Match the appropriate field with the given regular expression I<Regex>. If the
 regular expression matches, that part that matches is replaced with
 I<Replacement>. If multiple places of the input buffer match a given regular
@@ -9392,9 +9457,37 @@ Available options:
 
 =item B<MetaData> I<String> I<String>
 
-Set the appropriate field to the given string. The strings for plugin instance
-and type instance may be empty, the strings for host and plugin may not be
-empty. It's currently not possible to set the type of a value this way.
+Set the appropriate field to the given string. The strings for plugin instance,
+type instance, and meta data may be empty, the strings for host and plugin may
+not be empty. It's currently not possible to set the type of a value this way.
+
+The following placeholders will be replaced by an appropriate value:
+
+=over 4
+
+=item B<%{host}>
+
+=item B<%{plugin}>
+
+=item B<%{plugin_instance}>
+
+=item B<%{type}>
+
+=item B<%{type_instance}>
+
+These placeholders are replaced by the identifier field of the same name.
+
+=item B<%{meta:>I<name>B<}>
+
+These placeholders are replaced by the meta data value with the given name.
+
+=back
+
+Please note that these placeholders are B<case sensitive>!
+
+=item B<DeleteMetaData> I<String>
+
+Delete the named meta data field.
 
 =back
 
index ce90ede..4a72b2f 100644 (file)
@@ -74,61 +74,26 @@ static void conntrack_submit (const char *type, const char *type_instance,
 static int conntrack_read (void)
 {
        value_t conntrack, conntrack_max, conntrack_pct;
-       FILE *fh;
-       char buffer[64] = { 0 };
-       size_t buffer_len;
 
-       fh = fopen (old_files?CONNTRACK_FILE_OLD:CONNTRACK_FILE, "r");
-       if (fh == NULL)
-               return (-1);
-
-       if (fgets (buffer, sizeof (buffer), fh) == NULL)
+       char const *path = old_files ? CONNTRACK_FILE_OLD : CONNTRACK_FILE;
+       if (parse_value_file (path, &conntrack, DS_TYPE_GAUGE) != 0)
        {
-               fclose (fh);
+               ERROR ("conntrack plugin: Reading \"%s\" failed.", path);
                return (-1);
        }
-       fclose (fh);
 
-       /* strip trailing newline. */
-       buffer_len = strlen (buffer);
-       while ((buffer_len > 0) && isspace ((int) buffer[buffer_len - 1]))
+       path = old_files ? CONNTRACK_MAX_FILE_OLD : CONNTRACK_MAX_FILE;
+       if (parse_value_file (path, &conntrack_max, DS_TYPE_GAUGE) != 0)
        {
-               buffer[buffer_len - 1] = 0;
-               buffer_len--;
-       }
-
-       if (parse_value (buffer, &conntrack, DS_TYPE_GAUGE) != 0)
-               return (-1);
-
-       conntrack_submit ("conntrack", NULL, conntrack);
-
-       fh = fopen (old_files?CONNTRACK_MAX_FILE_OLD:CONNTRACK_MAX_FILE, "r");
-       if (fh == NULL)
-               return (-1);
-
-       memset (buffer, 0, sizeof (buffer));
-       if (fgets (buffer, sizeof (buffer), fh) == NULL)
-       {
-               fclose (fh);
+               ERROR ("conntrack plugin: Reading \"%s\" failed.", path);
                return (-1);
        }
-       fclose (fh);
-
-       /* strip trailing newline. */
-       buffer_len = strlen (buffer);
-       while ((buffer_len > 0) && isspace ((int) buffer[buffer_len - 1]))
-       {
-               buffer[buffer_len - 1] = 0;
-               buffer_len--;
-       }
-
-       if (parse_value (buffer, &conntrack_max, DS_TYPE_GAUGE) != 0)
-               return (-1);
 
-       conntrack_submit ("conntrack", "max", conntrack_max);
        conntrack_pct.gauge = (conntrack.gauge / conntrack_max.gauge) * 100;
-       conntrack_submit ("percent", "used", conntrack_pct);
 
+       conntrack_submit ("conntrack", NULL,   conntrack);
+       conntrack_submit ("conntrack", "max",  conntrack_max);
+       conntrack_submit ("percent",   "used", conntrack_pct);
 
        return (0);
 } /* static int conntrack_read */
index 1e9e857..a2cc3ad 100644 (file)
@@ -25,8 +25,6 @@
 #include "common.h"
 #include "plugin.h"
 
-#define MODULE_NAME "cpufreq"
-
 static int num_cpu = 0;
 
 static int cpufreq_init (void)
@@ -59,72 +57,39 @@ static int cpufreq_init (void)
        return (0);
 } /* int cpufreq_init */
 
-static void cpufreq_submit (int cpu_num, double value)
+static void cpufreq_submit (int cpu_num, value_t value)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = value;
-
-       vl.values = values;
+       vl.values = &value;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "cpufreq", sizeof (vl.plugin));
        sstrncpy (vl.type, "cpufreq", sizeof (vl.type));
-       ssnprintf (vl.type_instance, sizeof (vl.type_instance),
-                       "%i", cpu_num);
+       ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%i", cpu_num);
 
        plugin_dispatch_values (&vl);
 }
 
 static int cpufreq_read (void)
 {
-        int status;
-       unsigned long long val;
-       FILE *fp;
-       char filename[256];
-       char buffer[16];
-
        for (int i = 0; i < num_cpu; i++)
        {
-               status = ssnprintf (filename, sizeof (filename),
-                               "/sys/devices/system/cpu/cpu%d/cpufreq/"
-                               "scaling_cur_freq", i);
-               if ((status < 1) || ((unsigned int)status >= sizeof (filename)))
-                       return (-1);
+               char filename[PATH_MAX];
+               ssnprintf (filename, sizeof (filename),
+                               "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
 
-               if ((fp = fopen (filename, "r")) == NULL)
+               value_t v;
+               if (parse_value_file (filename, &v, DS_TYPE_GAUGE) != 0)
                {
-                       char errbuf[1024];
-                       WARNING ("cpufreq: fopen (%s): %s", filename,
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       return (-1);
+                       WARNING ("cpufreq plugin: Reading \"%s\" failed.", filename);
+                       continue;
                }
 
-               if (fgets (buffer, 16, fp) == NULL)
-               {
-                       char errbuf[1024];
-                       WARNING ("cpufreq: fgets: %s",
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       fclose (fp);
-                       return (-1);
-               }
-
-               if (fclose (fp))
-               {
-                       char errbuf[1024];
-                       WARNING ("cpufreq: fclose: %s",
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-               }
-
-
-               /* You're seeing correctly: The file is reporting kHz values.. */
-               val = atoll (buffer) * 1000;
+               /* convert kHz to Hz */
+               v.gauge *= 1000.0;
 
-               cpufreq_submit (i, val);
+               cpufreq_submit (i, v);
        }
 
        return (0);
index 7d1d8e9..4dab1ab 100644 (file)
@@ -775,14 +775,12 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     cb_name = ssnprintf_alloc ("curl_json-%s-%s",
                db->instance, db->url ? db->url : db->sock);
 
-    user_data_t ud = {
-      .data = db,
-      .free_func = cj_free
-    };
-
     plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
                                   /* interval = */ db->interval,
-                                  &ud);
+                                  &(user_data_t) {
+                                    .data = db,
+                                    .free_func = cj_free,
+                                  });
     sfree (cb_name);
   }
   else
index c8a1313..924665c 100644 (file)
@@ -1026,13 +1026,12 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
 
     cb_name = ssnprintf_alloc ("curl_xml-%s-%s", db->instance, db->url);
 
-    user_data_t ud = {
-      .data = db,
-      .free_func = cx_free
-    };
-
     plugin_register_complex_read (/* group = */ "curl_xml", cb_name, cx_read,
-                                  /* interval = */ 0, &ud);
+        /* interval = */ 0,
+        &(user_data_t) {
+          .data = db,
+          .free_func = cx_free,
+        });
     sfree (cb_name);
   }
   else
index 05b1199..7806c71 100644 (file)
@@ -1199,6 +1199,18 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
        return (0);
 } /* int parse_values */
 
+int parse_value_file (char const *path, value_t *ret_value, int ds_type)
+{
+       char buffer[256];
+
+       if (read_file_contents (path, buffer, sizeof (buffer)) < 0)
+               return errno;
+
+       strstripnewline (buffer);
+
+       return parse_value (buffer, ret_value, ds_type);
+} /* int parse_value_file */
+
 #if !HAVE_GETPWNAM_R
 int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
                size_t buflen, struct passwd **pwbufp)
index 720e5f1..9e25293 100644 (file)
@@ -322,6 +322,11 @@ int parse_identifier_vl (const char *str, value_list_t *vl);
 int parse_value (const char *value, value_t *ret_value, int ds_type);
 int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds);
 
+/* parse_value_file reads "path" and parses its content as an integer or
+ * floating point, depending on "ds_type". On success, the value is stored in
+ * "ret_value" and zero is returned. On failure, a non-zero value is returned. */
+int parse_value_file (char const *path, value_t *ret_value, int ds_type);
+
 #if !HAVE_GETPWNAM_R
 int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
                size_t buflen, struct passwd **pwbufp);
index 9e4fd07..a11fccb 100644 (file)
 
 #include "collectd.h"
 
+#include "common.h"
 #include "plugin.h"
 #include "meta_data.h"
 
+#define MD_MAX_NONSTRING_CHARS 128
+
 /*
  * Data types
  */
@@ -729,4 +732,68 @@ int meta_data_get_boolean (meta_data_t *md, /* {{{ */
   return (0);
 } /* }}} int meta_data_get_boolean */
 
+int meta_data_as_string (meta_data_t *md, /* {{{ */
+    const char *key, char **value)
+{
+  meta_entry_t *e;
+  char *actual;
+  char buffer[MD_MAX_NONSTRING_CHARS];  /* For non-string types. */
+  char *temp;
+  int type;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  type = e->type;
+
+  switch (type)
+  {
+    case MD_TYPE_STRING:
+      actual = e->value.mv_string;
+      break;
+    case MD_TYPE_SIGNED_INT:
+      ssnprintf (buffer, sizeof (buffer), "%"PRIi64, e->value.mv_signed_int);
+      actual = buffer;
+      break;
+    case MD_TYPE_UNSIGNED_INT:
+      ssnprintf (buffer, sizeof (buffer), "%"PRIu64, e->value.mv_unsigned_int);
+      actual = buffer;
+      break;
+    case MD_TYPE_DOUBLE:
+      ssnprintf (buffer, sizeof (buffer), GAUGE_FORMAT, e->value.mv_double);
+      actual = buffer;
+      break;
+    case MD_TYPE_BOOLEAN:
+      actual = e->value.mv_boolean ? "true" : "false";
+      break;
+    default:
+      pthread_mutex_unlock (&md->lock);
+      ERROR ("meta_data_as_string: unknown type %d for key `%s'", type, key);
+      return (-ENOENT);
+  }
+
+  pthread_mutex_unlock (&md->lock);
+
+  temp = md_strdup (actual);
+  if (temp == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    ERROR ("meta_data_as_string: md_strdup failed for key `%s'.", key);
+    return (-ENOMEM);
+  }
+
+  *value = temp;
+
+  return (0);
+} /* }}} int meta_data_as_string */
+
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 0398c54..f4a4f21 100644 (file)
@@ -84,5 +84,10 @@ int meta_data_get_boolean (meta_data_t *md,
     const char *key,
     _Bool *value);
 
+/* Returns the value as a string, regardless of the type. */
+int meta_data_as_string (meta_data_t *md,
+    const char *key,
+    char **value);
+
 #endif /* META_DATA_H */
 /* vim: set sw=2 sts=2 et : */
index 068bf4d..fefbf87 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -390,24 +390,19 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     }
     else
     {
-      char *name = NULL;
-
       databases = temp;
       databases[databases_num] = db;
       databases_num++;
 
-      name = ssnprintf_alloc("dbi:%s", db->name);
-
-      user_data_t ud = {
-        .data = db
-      };
-
+      char *name = ssnprintf_alloc("dbi:%s", db->name);
       plugin_register_complex_read (/* group = */ NULL,
           /* name = */ name ? name : db->name,
           /* callback = */ cdbi_read_database,
           /* interval = */ (db->interval > 0) ? db->interval : 0,
-          /* user_data = */ &ud);
-      free (name);
+          &(user_data_t) {
+            .data = db,
+         });
+      sfree (name);
     }
   }
 
@@ -452,14 +447,14 @@ static int cdbi_init (void) /* {{{ */
   if (queries_num == 0)
   {
     ERROR ("dbi plugin: No <Query> blocks have been found. Without them, "
-        "this plugin can't do anything useful, so we will returns an error.");
+        "this plugin can't do anything useful, so we will return an error.");
     return (-1);
   }
 
   if (databases_num == 0)
   {
     ERROR ("dbi plugin: No <Database> blocks have been found. Without them, "
-        "this plugin can't do anything useful, so we will returns an error.");
+        "this plugin can't do anything useful, so we will return an error.");
     return (-1);
   }
 
index 4a86799..af54c92 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -229,7 +229,10 @@ static int df_read (void)
                        }
 
                        /* Duplicate found: leave non-NULL dup_ptr. */
-                       if (by_device && (strcmp (mnt_ptr->spec_device, dup_ptr->spec_device) == 0))
+                       if (by_device
+                            && (mnt_ptr->spec_device != NULL)
+                            && (dup_ptr->spec_device != NULL)
+                            && (strcmp (mnt_ptr->spec_device, dup_ptr->spec_device) == 0))
                                break;
                        else if (!by_device && (strcmp (mnt_ptr->dir, dup_ptr->dir) == 0))
                                break;
index d7eb0a5..465b005 100644 (file)
@@ -128,7 +128,7 @@ static int numdisk = 0;
 /* #endif HAVE_LIBKSTAT */
 
 #elif defined(HAVE_LIBSTATGRAB)
-/* #endif HAVE_LIBKSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
 
 #elif HAVE_PERFSTAT
 static perfstat_disk_t * stat_disk;
index f4f4ac4..b385c2a 100644 (file)
 
 #define ENTROPY_FILE "/proc/sys/kernel/random/entropy_avail"
 
-static void entropy_submit (double entropy)
+static void entropy_submit (value_t value)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = entropy;
-
-       vl.values = values;
+       vl.values = &value;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "entropy", sizeof (vl.plugin));
@@ -53,26 +50,14 @@ static void entropy_submit (double entropy)
 
 static int entropy_read (void)
 {
-       double entropy;
-       FILE *fh;
-       char buffer[64];
-
-       fh = fopen (ENTROPY_FILE, "r");
-       if (fh == NULL)
-               return (-1);
-
-       if (fgets (buffer, sizeof (buffer), fh) == NULL)
+       value_t v;
+       if (parse_value_file (ENTROPY_FILE, &v, DS_TYPE_GAUGE) != 0)
        {
-               fclose (fh);
+               ERROR ("entropy plugin: Reading \""ENTROPY_FILE"\" failed.");
                return (-1);
        }
-       fclose (fh);
-
-       entropy = atof (buffer);
-       
-       if (entropy > 0.0)
-               entropy_submit (entropy);
 
+       entropy_submit (v);
        return (0);
 }
 
index 8b76954..3b1891f 100644 (file)
@@ -47,8 +47,8 @@ extern "C" {
 
 using collectd::Collectd;
 
-using collectd::DispatchValuesRequest;
-using collectd::DispatchValuesResponse;
+using collectd::PutValuesRequest;
+using collectd::PutValuesResponse;
 using collectd::QueryValuesRequest;
 using collectd::QueryValuesResponse;
 
@@ -283,10 +283,10 @@ public:
                return status;
        }
 
-       grpc::Status DispatchValues(grpc::ServerContext *ctx,
-                                                               grpc::ServerReader<DispatchValuesRequest> *reader,
-                                                               DispatchValuesResponse *res) override {
-               DispatchValuesRequest req;
+       grpc::Status PutValues(grpc::ServerContext *ctx,
+                                                  grpc::ServerReader<PutValuesRequest> *reader,
+                                                  PutValuesResponse *res) override {
+               PutValuesRequest req;
 
                while (reader->Read(&req)) {
                        value_list_t vl = VALUE_LIST_INIT;
@@ -426,18 +426,18 @@ public:
        CollectdClient(std::shared_ptr<grpc::ChannelInterface> channel) : stub_(Collectd::NewStub(channel)) {
        }
 
-       int DispatchValues(value_list_t const *vl) {
+       int PutValues(value_list_t const *vl) {
                grpc::ClientContext ctx;
 
-               DispatchValuesRequest req;
+               PutValuesRequest req;
                auto status = marshal_value_list(vl, req.mutable_value_list());
                if (!status.ok()) {
                        ERROR("grpc: Marshalling value_list_t failed.");
                        return -1;
                }
 
-               DispatchValuesResponse res;
-               auto stream = stub_->DispatchValues(&ctx, &res);
+               PutValuesResponse res;
+               auto stream = stub_->PutValues(&ctx, &res);
                if (!stream->Write(req)) {
                        NOTICE("grpc: Broken stream.");
                        /* intentionally not returning. */
@@ -451,7 +451,7 @@ public:
                }
 
                return 0;
-       } /* int DispatchValues */
+       } /* int PutValues */
 
 private:
        std::unique_ptr<Collectd::Stub> stub_;
@@ -471,7 +471,7 @@ extern "C" {
                        value_list_t const *vl,
                        user_data_t *ud) {
                CollectdClient *c = (CollectdClient *) ud->data;
-               return c->DispatchValues(vl);
+               return c->PutValues(vl);
        }
 
        static int c_grpc_config_listen(oconfig_item_t *ci)
diff --git a/src/hugepages.c b/src/hugepages.c
new file mode 100644 (file)
index 0000000..ac9441d
--- /dev/null
@@ -0,0 +1,290 @@
+/*-
+ * collectd - src/hugepages.c
+ * MIT License
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * 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:
+ *   Jaroslav Safka <jaroslavx.safka@intel.com>
+ *   Kim-Marie Jones <kim-marie.jones@intel.com>
+ */
+
+#include "collectd.h"
+#include "common.h" /* auxiliary functions */
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+
+static const char g_plugin_name[] = "hugepages";
+static const char g_cfg_rpt_numa[] = "ReportPerNodeHP";
+static const char g_cfg_rpt_mm[] = "ReportRootHP";
+
+static const char *g_config_keys[] = {
+  g_cfg_rpt_numa,
+  g_cfg_rpt_mm,
+};
+static size_t g_config_keys_num = STATIC_ARRAY_SIZE(g_config_keys);
+static int g_flag_rpt_numa = 1;
+static int g_flag_rpt_mm = 1;
+
+struct entry_info {
+  char *d_name;
+  const char *node;
+};
+
+static int huge_config_callback(const char *key, const char *val)
+{
+  DEBUG("%s: HugePages config key='%s', val='%s'", g_plugin_name, key, val);
+
+  if (strcasecmp(key, g_cfg_rpt_numa) == 0) {
+    g_flag_rpt_numa = IS_TRUE(val);
+    return 0;
+  }
+  if (strcasecmp(key, g_cfg_rpt_mm) == 0) {
+    g_flag_rpt_mm = IS_TRUE(val);
+    return 0;
+  }
+
+  return -1;
+}
+
+static void submit_hp(const char *plug_inst, const char *type,
+  const char *type_instance, gauge_t free_value, gauge_t used_value)
+{
+  value_t values[2];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = free_value;
+  values[1].gauge = used_value;
+
+  vl.values = values;
+  vl.values_len = 2;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, g_plugin_name, sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, plug_inst, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, type, sizeof (vl.type));
+
+  if (type_instance != NULL) {
+    sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+  }
+
+  DEBUG("submit_hp pl_inst:%s, inst_type %s, type %s, free=%lf, used=%lf",
+      plug_inst, type_instance, type, free_value, used_value);
+
+  plugin_dispatch_values (&vl);
+}
+
+static int read_hugepage_entry(const char *path, const char *entry,
+    void *e_info)
+{
+  char path2[PATH_MAX];
+  static const char type[] = "hugepages";
+  static const char partial_type_inst[] = "free_used";
+  char type_instance[PATH_MAX];
+  char *strin;
+  struct entry_info *hpsize_plinst = e_info;
+  static int flag = 0;
+  static double used_hp = 0;
+  static double free_hp = 0;
+  double value;
+
+  ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
+
+  FILE *fh = fopen(path2, "rt");
+  if (fh == NULL) {
+    ERROR("%s: cannot open %s", g_plugin_name, path2);
+    return -1;
+  }
+
+  if (fscanf(fh, "%lf", &value) !=1) {
+    ERROR("%s: cannot parse file %s", g_plugin_name, path2);
+    fclose(fh);
+    return -1;
+  }
+
+  if (strcmp(entry, "nr_hugepages") == 0) {
+    used_hp += value;
+    flag++;
+  } else if (strcmp(entry, "surplus_hugepages") == 0) {
+    used_hp += value;
+    flag++;
+  } else if (strcmp(entry, "free_hugepages") == 0) {
+    used_hp -= value;
+    free_hp = value;
+    flag++;
+  }
+
+  if (flag == 3) {
+    /* Can now submit "used" and "free" values.
+     * 0x2D is the ASCII "-" character, after which the string
+     *   contains "<size>kB"
+     * The string passed as param 3 to submit_hp is of the format:
+     *   <type>-<partial_type_inst>-<size>kB
+     */
+    strin = strchr(hpsize_plinst->d_name, 0x2D);
+    if (strin != NULL) {
+      ssnprintf(type_instance, sizeof(type_instance), "%s%s", partial_type_inst, strin);
+    } else {
+      ssnprintf(type_instance, sizeof(type_instance), "%s%s", partial_type_inst,
+          hpsize_plinst->d_name);
+    }
+    submit_hp(hpsize_plinst->node, type, type_instance, free_hp, used_hp);
+
+    /* Reset for next time */
+    flag = 0;
+    used_hp = 0;
+    free_hp = 0;
+  }
+
+  fclose(fh);
+  return 0;
+}
+
+static int read_syshugepages(const char* path, const char* node)
+{
+  static const char hugepages_dir[] = "hugepages";
+  DIR *dir;
+  struct dirent *result;
+  char path2[PATH_MAX];
+  struct entry_info e_info;
+  long lim;
+
+  dir = opendir(path);
+  if (dir == NULL) {
+    ERROR("%s: cannot open directory %s", g_plugin_name, path);
+    return -1;
+  }
+
+  errno = 0;
+  if ((lim = pathconf(path, _PC_NAME_MAX)) == -1) {
+    /* Limit not defined if errno == 0, otherwise error */
+    if (errno != 0) {
+      ERROR("%s: pathconf failed", g_plugin_name);
+      closedir(dir);
+      return -1;
+    } else {
+      lim = PATH_MAX;
+    }
+  }
+
+  /* read "hugepages-XXXXXkB" entries */
+  while ((result = readdir(dir)) != NULL) {
+    if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir)-1)) {
+      /* not node dir */
+      errno = 0;
+      continue;
+    }
+
+    /* /sys/devices/system/node/node?/hugepages/ */
+    ssnprintf(path2, (size_t) lim, "%s/%s", path, result->d_name);
+
+    e_info.d_name = result->d_name;
+    e_info.node = node;
+    walk_directory(path2, read_hugepage_entry, &e_info, 0);
+    errno = 0;
+  }
+
+  /* Check if NULL return from readdir() was an error */
+  if (errno != 0) {
+      ERROR("%s: readdir failed", g_plugin_name);
+      closedir(dir);
+      return -1;
+  }
+
+  closedir(dir);
+  return 0;
+}
+
+static int read_nodes(void)
+{
+  static const char sys_node[] = "/sys/devices/system/node";
+  static const char node_string[] = "node";
+  static const char sys_node_hugepages[] = "/sys/devices/system/node/%s/hugepages";
+  DIR *dir;
+  struct dirent *result;
+  char path[PATH_MAX];
+  long lim;
+
+  dir = opendir(sys_node);
+  if (dir == NULL) {
+    ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
+    return -1;
+  }
+
+  errno = 0;
+  if ((lim = pathconf(sys_node, _PC_NAME_MAX)) == -1) {
+    /* Limit not defined if errno == 0, otherwise error */
+    if (errno != 0) {
+      ERROR("%s: pathconf failed", g_plugin_name);
+      closedir(dir);
+      return -1;
+    } else {
+      lim = PATH_MAX;
+    }
+  }
+
+  while ((result = readdir(dir)) != NULL) {
+    if (strncmp(result->d_name, node_string, sizeof(node_string)-1)) {
+      /* not node dir */
+      errno = 0;
+      continue;
+    }
+
+    ssnprintf(path, (size_t) lim, sys_node_hugepages, result->d_name);
+    read_syshugepages(path, result->d_name);
+    errno = 0;
+  }
+
+  /* Check if NULL return from readdir() was an error */
+  if (errno != 0) {
+      ERROR("%s: readdir failed", g_plugin_name);
+      closedir(dir);
+      return -1;
+  }
+
+  closedir(dir);
+  return 0;
+}
+
+
+static int huge_read(void)
+{
+  static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
+
+  if (g_flag_rpt_mm) {
+    if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
+      return -1;
+    }
+  }
+  if (g_flag_rpt_numa) {
+    if (read_nodes() != 0) {
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+void module_register(void)
+{
+  plugin_register_config(g_plugin_name, huge_config_callback, g_config_keys,
+      g_config_keys_num);
+  plugin_register_read(g_plugin_name, huge_read);
+}
+
index 67740ac..cd173c5 100644 (file)
@@ -1425,13 +1425,11 @@ static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */
 
   DEBUG ("java plugin: Registering new read callback: %s", cbi->name);
 
-  user_data_t ud = {
-    .data = cbi,
-    .free_func = cjni_callback_info_destroy
-  };
-
   plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
-      /* interval = */ 0, &ud);
+      /* interval = */ 0, &(user_data_t) {
+        .data = cbi,
+        .free_func = cjni_callback_info_destroy,
+      });
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_read);
 
@@ -1449,12 +1447,10 @@ static jint JNICALL cjni_api_register_write (JNIEnv *jvm_env, /* {{{ */
 
   DEBUG ("java plugin: Registering new write callback: %s", cbi->name);
 
-  user_data_t ud = {
-    .data = cbi,
-    .free_func = cjni_callback_info_destroy
-  };
-
-  plugin_register_write (cbi->name, cjni_write, &ud);
+  plugin_register_write (cbi->name, cjni_write, &(user_data_t) {
+        .data = cbi,
+        .free_func = cjni_callback_info_destroy,
+      });
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_write);
 
@@ -1472,12 +1468,10 @@ static jint JNICALL cjni_api_register_flush (JNIEnv *jvm_env, /* {{{ */
 
   DEBUG ("java plugin: Registering new flush callback: %s", cbi->name);
 
-  user_data_t ud = {
-    .data = cbi,
-    .free_func = cjni_callback_info_destroy
-  };
-
-  plugin_register_flush (cbi->name, cjni_flush, &ud);
+  plugin_register_flush (cbi->name, cjni_flush, &(user_data_t) {
+        .data = cbi,
+        .free_func = cjni_callback_info_destroy,
+      });
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_flush);
 
@@ -1502,12 +1496,10 @@ static jint JNICALL cjni_api_register_log (JNIEnv *jvm_env, /* {{{ */
 
   DEBUG ("java plugin: Registering new log callback: %s", cbi->name);
 
-  user_data_t ud = {
-    .data = cbi,
-    .free_func = cjni_callback_info_destroy
-  };
-
-  plugin_register_log (cbi->name, cjni_log, &ud);
+  plugin_register_log (cbi->name, cjni_log, &(user_data_t) {
+        .data = cbi,
+        .free_func = cjni_callback_info_destroy,
+      });
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_log);
 
@@ -1526,12 +1518,10 @@ static jint JNICALL cjni_api_register_notification (JNIEnv *jvm_env, /* {{{ */
 
   DEBUG ("java plugin: Registering new notification callback: %s", cbi->name);
 
-  user_data_t ud = {
-    .data = cbi,
-    .free_func = cjni_callback_info_destroy
-  };
-
-  plugin_register_notification (cbi->name, cjni_notification, &ud);
+  plugin_register_notification (cbi->name, cjni_notification, &(user_data_t) {
+        .data = cbi,
+        .free_func = cjni_callback_info_destroy,
+      });
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
 
index ba5fbce..d10b4ea 100644 (file)
--- a/src/lua.c
+++ b/src/lua.c
@@ -303,15 +303,13 @@ static int lua_cb_register_read(lua_State *L) /* {{{ */
   cb->lua_function_name = strdup(function_name);
   pthread_mutex_init(&cb->lock, NULL);
 
-  user_data_t ud = {
-    .data = cb
-  };
-
   int status = plugin_register_complex_read(/* group = */ "lua",
                                             /* name      = */ function_name,
                                             /* callback  = */ clua_read,
                                             /* interval  = */ 0,
-                                            /* user_data = */ &ud);
+                                            &(user_data_t) {
+                                              .data = cb,
+                                            });
 
   if (status != 0)
     return luaL_error(L, "%s", "plugin_register_complex_read failed");
@@ -349,13 +347,11 @@ static int lua_cb_register_write(lua_State *L) /* {{{ */
   cb->lua_function_name = strdup(function_name);
   pthread_mutex_init(&cb->lock, NULL);
 
-  user_data_t ud = {
-    .data = cb
-  };
-
   int status = plugin_register_write(/* name = */ function_name,
                                     /* callback  = */ clua_write,
-                                    /* user_data = */ &ud);
+                                    &(user_data_t) {
+                                      .data = cb,
+                                    });
 
   if (status != 0)
     return luaL_error(L, "%s", "plugin_register_write failed");
index cd63016..dd8319e 100644 (file)
 
 #include "collectd.h"
 
+#include "common.h"
 #include "filter_chain.h"
+#include "meta_data.h"
+#include "utils_llist.h"
 
 #include <sys/types.h>
 #include <regex.h>
@@ -64,6 +67,7 @@ struct mr_match_s
        mr_regex_t *plugin_instance;
        mr_regex_t *type;
        mr_regex_t *type_instance;
+       llist_t *meta;  /* Maps each meta key into mr_regex_t* */
        _Bool invert;
 };
 
@@ -77,7 +81,7 @@ static void mr_free_regex (mr_regex_t *r) /* {{{ */
 
        regfree (&r->re);
        memset (&r->re, 0, sizeof (r->re));
-       free (r->re_str);
+       sfree (r->re_str);
 
        if (r->next != NULL)
                mr_free_regex (r->next);
@@ -93,8 +97,14 @@ static void mr_free_match (mr_match_t *m) /* {{{ */
        mr_free_regex (m->plugin_instance);
        mr_free_regex (m->type);
        mr_free_regex (m->type_instance);
+       for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next)
+       {
+               sfree (e->key);
+               mr_free_regex ((mr_regex_t *) e->value);
+       }
+       llist_destroy (m->meta);
 
-       free (m);
+       sfree (m);
 } /* }}} void mr_free_match */
 
 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
@@ -127,31 +137,25 @@ static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
        return (FC_MATCH_MATCHES);
 } /* }}} int mr_match_regexen */
 
-static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
-               oconfig_item_t *ci)
+static int mr_add_regex (mr_regex_t **re_head, const char *re_str, /* {{{ */
+               const char *option)
 {
        mr_regex_t *re;
        int status;
 
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-       {
-               log_warn ("`%s' needs exactly one string argument.", ci->key);
-               return (-1);
-       }
-
        re = calloc (1, sizeof (*re));
        if (re == NULL)
        {
-               log_err ("mr_config_add_regex: calloc failed.");
+               log_err ("mr_add_regex: calloc failed.");
                return (-1);
        }
        re->next = NULL;
 
-       re->re_str = strdup (ci->values[0].value.string);
+       re->re_str = strdup (re_str);
        if (re->re_str == NULL)
        {
-               free (re);
-               log_err ("mr_config_add_regex: strdup failed.");
+               sfree (re);
+               log_err ("mr_add_regex: strdup failed.");
                return (-1);
        }
 
@@ -162,9 +166,9 @@ static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
                regerror (status, &re->re, errmsg, sizeof (errmsg));
                errmsg[sizeof (errmsg) - 1] = 0;
                log_err ("Compiling regex `%s' for `%s' failed: %s.",
-                               re->re_str, ci->key, errmsg);
-               free (re->re_str);
-               free (re);
+                               re->re_str, option, errmsg);
+               sfree (re->re_str);
+               sfree (re);
                return (-1);
        }
 
@@ -184,8 +188,78 @@ static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
        }
 
        return (0);
+} /* }}} int mr_add_regex */
+
+static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
+               oconfig_item_t *ci)
+{
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               log_warn ("`%s' needs exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       return mr_add_regex (re_head, ci->values[0].value.string, ci->key);
 } /* }}} int mr_config_add_regex */
 
+static int mr_config_add_meta_regex (llist_t **meta, /* {{{ */
+               oconfig_item_t *ci)
+{
+       char *meta_key;
+       llentry_t *entry;
+       mr_regex_t *re_head;
+       int status;
+       char buffer[1024];
+
+       if ((ci->values_num != 2)
+               || (ci->values[0].type != OCONFIG_TYPE_STRING)
+               || (ci->values[1].type != OCONFIG_TYPE_STRING))
+       {
+               log_warn ("`%s' needs exactly two string arguments.", ci->key);
+               return (-1);
+       }
+
+       if (*meta == NULL)
+       {
+               *meta = llist_create();
+               if (*meta == NULL)
+               {
+                       log_err ("mr_config_add_meta_regex: llist_create failed.");
+                       return (-1);
+               }
+       }
+
+       meta_key = ci->values[0].value.string;
+       entry = llist_search (*meta, meta_key);
+       if (entry == NULL)
+       {
+               meta_key = strdup (meta_key);
+               if (meta_key == NULL)
+               {
+                       log_err ("mr_config_add_meta_regex: strdup failed.");
+                       return (-1);
+               }
+               entry = llentry_create (meta_key, NULL);
+               if (entry == NULL)
+               {
+                       log_err ("mr_config_add_meta_regex: llentry_create failed.");
+                       sfree (meta_key);
+                       return (-1);
+               }
+               /* meta_key and entry will now be freed by mr_free_match(). */
+               llist_append (*meta, entry);
+       }
+
+       ssnprintf (buffer, sizeof (buffer), "%s `%s'", ci->key, meta_key);
+       /* Can't pass &entry->value into mr_add_regex, so copy in/out. */
+       re_head = entry->value;
+       status = mr_add_regex (&re_head, ci->values[1].value.string, buffer);
+       if (status == 0) {
+               entry->value = re_head;
+       }
+       return status;
+} /* }}} int mr_config_add_meta_regex */
+
 static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
 {
        mr_match_t *m;
@@ -216,6 +290,8 @@ static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                        status = mr_config_add_regex (&m->type, child);
                else if (strcasecmp ("TypeInstance", child->key) == 0)
                        status = mr_config_add_regex (&m->type_instance, child);
+               else if (strcasecmp ("MetaData", child->key) == 0)
+                       status = mr_config_add_meta_regex (&m->meta, child);
                else if (strcasecmp ("Invert", child->key) == 0)
                        status = cf_util_get_boolean(child, &m->invert);
                else
@@ -236,7 +312,8 @@ static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                                && (m->plugin == NULL)
                                && (m->plugin_instance == NULL)
                                && (m->type == NULL)
-                               && (m->type_instance == NULL))
+                               && (m->type_instance == NULL)
+                               && (m->meta == NULL))
                {
                        log_err ("No (valid) regular expressions have been configured. "
                                        "This match will be ignored.");
@@ -295,6 +372,25 @@ static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
        if (mr_match_regexen (m->type_instance,
                                vl->type_instance) == FC_MATCH_NO_MATCH)
                return (nomatch_value);
+       if (vl->meta != NULL)
+       {
+               for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next)
+               {
+                       mr_regex_t *meta_re = (mr_regex_t *) e->value;
+                       char *value;
+                       int status = meta_data_get_string (vl->meta, e->key, &value);
+                       if (status == (-ENOENT))  /* key is not present */
+                               return (nomatch_value);
+                       if (status != 0)  /* some other problem */
+                               continue;  /* error will have already been printed. */
+                       if (mr_match_regexen (meta_re, value) == FC_MATCH_NO_MATCH)
+                       {
+                               sfree (value);
+                               return (nomatch_value);
+                       }
+                       sfree (value);
+               }
+       }
 
        return (match_value);
 } /* }}} int mr_match */
index 9e6e725..d83d4c8 100644 (file)
@@ -554,16 +554,15 @@ static int memcached_add_read_callback (memcached_t *st)
   assert (st->name != NULL);
   ssnprintf (callback_name, sizeof (callback_name), "memcached/%s", st->name);
 
-  user_data_t ud = {
-    .data = st,
-    .free_func = memcached_free
-  };
-
   status = plugin_register_complex_read (/* group = */ "memcached",
       /* name      = */ callback_name,
       /* callback  = */ memcached_read,
       /* interval  = */ 0,
-      /* user_data = */ &ud);
+      &(user_data_t) {
+        .data = st,
+        .free_func = memcached_free,
+      });
+
   return (status);
 } /* int memcached_add_read_callback */
 
index 93fd54a..a7d1b58 100644 (file)
@@ -470,11 +470,11 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
   else if (host->conntype == MBCONN_TCP)
   {
-    struct sockaddr sockaddr;
-    socklen_t saddrlen = sizeof (sockaddr);
-
+    /* getpeername() is used only to determine if the socket is connected, not
+     * because we're really interested in the peer's IP address. */
     status = getpeername (modbus_get_socket (host->connection),
-        &sockaddr, &saddrlen);
+        (struct sockaddr *) &(struct sockaddr_storage) { 0 },
+        &(socklen_t) { sizeof (struct sockaddr_storage) });
     if (status != 0)
       status = errno;
   }
@@ -1003,18 +1003,17 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */
 
   if (status == 0)
   {
-    user_data_t ud;
     char name[1024];
 
-    ud.data = host;
-    ud.free_func = host_free;
-
     ssnprintf (name, sizeof (name), "modbus-%s", host->host);
 
     plugin_register_complex_read (/* group = */ NULL, name,
         /* callback = */ mb_read,
         /* interval = */ host->interval,
-        &ud);
+        &(user_data_t) {
+          .data = host,
+          .free_func = host_free,
+        });
   }
   else
   {
index ad88995..c08fd29 100644 (file)
@@ -631,11 +631,9 @@ static int mqtt_config_publisher (oconfig_item_t *ci)
     }
 
     ssnprintf (cb_name, sizeof (cb_name), "mqtt/%s", conf->name);
-    user_data_t user_data = {
-        .data = conf
-    };
-
-    plugin_register_write (cb_name, mqtt_write, &user_data);
+    plugin_register_write (cb_name, mqtt_write, &(user_data_t) {
+                .data = conf,
+            });
     return (0);
 } /* mqtt_config_publisher */
 
index 32b72e2..3bd8cb9 100644 (file)
@@ -233,14 +233,11 @@ static int mysql_config_database (oconfig_item_t *ci) /* {{{ */
                else
                        sstrncpy (cb_name, "mysql", sizeof (cb_name));
 
-               user_data_t ud = {
-                       .data = db,
-                       .free_func = mysql_database_free
-               };
-
                plugin_register_complex_read (/* group = */ NULL, cb_name,
-                                             mysql_read,
-                                             /* interval = */ 0, &ud);
+                               mysql_read, /* interval = */ 0, &(user_data_t) {
+                                       .data = db,
+                                       .free_func = mysql_database_free,
+                               });
        }
        else
        {
index d739696..bf4c286 100644 (file)
@@ -2879,15 +2879,13 @@ static int cna_register_host (host_config_t *host) /* {{{ */
        else
                ssnprintf (cb_name, sizeof (cb_name), "netapp-%s", host->name);
 
-       user_data_t ud = {
-               .data = host,
-               .free_func = (void (*) (void *)) free_host_config
-       };
-
        plugin_register_complex_read (/* group = */ NULL, cb_name,
                        /* callback  = */ cna_read,
                        /* interval  = */ host->interval,
-                       /* user data = */ &ud);
+                       &(user_data_t) {
+                               .data = host,
+                               .free_func = (void *) free_host_config,
+                       });
 
        return (0);
 } /* }}} int cna_register_host */
index 235eff8..8bb2d81 100644 (file)
@@ -359,6 +359,7 @@ static int cow_read_values (const char *path, const char *name,
     char *buffer;
     size_t buffer_size;
     int status;
+    char errbuf[1024];
 
     char file[4096];
     char *endptr;
@@ -373,8 +374,8 @@ static int cow_read_values (const char *path, const char *name,
     status = OW_get (file, &buffer, &buffer_size);
     if (status < 0)
     {
-      ERROR ("onewire plugin: OW_get (%s/%s) failed. status = %#x;",
-          path, family_info->features[i].filename, status);
+      ERROR ("onewire plugin: OW_get (%s/%s) failed. error = %s;",
+          path, family_info->features[i].filename, sstrerror(errno, errbuf, sizeof (errbuf)));
       return (-1);
     }
     DEBUG ("Read onewire device %s as %s", file, buffer);
@@ -430,6 +431,7 @@ static int cow_read_bus (const char *path)
   char *buffer;
   size_t buffer_size;
   int status;
+  char errbuf[1024];
 
   char *buffer_ptr;
   char *dummy;
@@ -439,8 +441,8 @@ static int cow_read_bus (const char *path)
   status = OW_get (path, &buffer, &buffer_size);
   if (status < 0)
   {
-    ERROR ("onewire plugin: OW_get (%s) failed. status = %#x;",
-        path, status);
+    ERROR ("onewire plugin: OW_get (%s) failed. error = %s;",
+        path, sstrerror(errno, errbuf, sizeof (errbuf)));
     return (-1);
   }
   DEBUG ("onewire plugin: OW_get (%s) returned: %s",
@@ -498,6 +500,7 @@ static int cow_simple_read (void)
   char        *buffer;
   size_t       buffer_size;
   int          status;
+  char         errbuf[1024];
   char        *endptr;
   direct_access_element_t *traverse;
 
@@ -514,9 +517,9 @@ static int cow_simple_read (void)
       status = OW_get (traverse->path, &buffer, &buffer_size);
       if (status < 0)
       {
-          ERROR ("onewire plugin: OW_get (%s) failed. status = %#x;",
+          ERROR ("onewire plugin: OW_get (%s) failed. status = %s;",
                  traverse->path,
-                 status);
+                 sstrerror(errno, errbuf, sizeof (errbuf)));
           return (-1);
       }
       DEBUG ("onewire plugin: Read onewire device %s as %s", traverse->path, buffer);
@@ -590,6 +593,7 @@ static int cow_shutdown (void)
 static int cow_init (void)
 {
   int status;
+  char errbuf[1024];
 
   if (device_g == NULL)
   {
@@ -601,7 +605,7 @@ static int cow_init (void)
   status = (int) OW_init (device_g);
   if (status != 0)
   {
-    ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status);
+    ERROR ("onewire plugin: OW_init(%s) failed: %s.", device_g, sstrerror(errno, errbuf, sizeof (errbuf)));
     return (1);
   }
 
index d424cb4..066dabc 100644 (file)
@@ -663,15 +663,13 @@ static int cldap_config_add (oconfig_item_t *ci) /* {{{ */
                                        (st->host != NULL) ? st->host : hostname_g,
                                        (st->name != NULL) ? st->name : "default");
 
-                       user_data_t ud = {
-                               .data = st
-                       };
-
                        status = plugin_register_complex_read (/* group = */ NULL,
                                        /* name      = */ callback_name,
                                        /* callback  = */ cldap_read_host,
                                        /* interval  = */ 0,
-                                       /* user_data = */ &ud);
+                                       &(user_data_t) {
+                                               .data = st,
+                                       });
                }
        }
 
index ec2317b..5274262 100644 (file)
@@ -665,12 +665,11 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObjec
        c->data = data;
        c->next = NULL;
 
-       user_data_t user_data = {
-               .data = c,
-               .free_func = cpy_destroy_user_data
-       };
+       register_function(buf, handler, &(user_data_t) {
+                               .data = c,
+                               .free_func = cpy_destroy_user_data,
+                       });
 
-       register_function(buf, handler, &user_data);
        ++cpy_num_callbacks;
        return cpy_string_to_unicode_or_bytes(buf);
 }
@@ -704,13 +703,12 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
        c->data = data;
        c->next = NULL;
 
-       user_data_t user_data = {
-               .data = c,
-               .free_func = cpy_destroy_user_data
-       };
-
        plugin_register_complex_read(/* group = */ "python", buf,
-                       cpy_read_callback, DOUBLE_TO_CDTIME_T (interval), &user_data);
+                       cpy_read_callback, DOUBLE_TO_CDTIME_T (interval),
+                       &(user_data_t) {
+                               .data = c,
+                               .free_func = cpy_destroy_user_data,
+                       });
        ++cpy_num_callbacks;
        return cpy_string_to_unicode_or_bytes(buf);
 }
index a270f48..5fc14db 100644 (file)
@@ -325,7 +325,6 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
 {
   cr_data_t *router_data;
   char read_name[128];
-  user_data_t user_data;
   int status;
 
   router_data = calloc (1, sizeof (*router_data));
@@ -409,11 +408,12 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
   }
 
   ssnprintf (read_name, sizeof (read_name), "routeros/%s", router_data->node);
-  user_data.data = router_data;
-  user_data.free_func = (void *) cr_free_data;
   if (status == 0)
     status = plugin_register_complex_read (/* group = */ NULL, read_name,
-       cr_read, /* interval = */ 0, &user_data);
+        cr_read, /* interval = */ 0, &(user_data_t) {
+          .data = router_data,
+          .free_func = (void *) cr_free_data,
+        });
 
   if (status != 0)
     cr_free_data (router_data);
index abeda43..d80ee92 100644 (file)
@@ -764,13 +764,11 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
 
   ssnprintf (cb_name, sizeof (cb_name), "snmp-%s", hd->name);
 
-  user_data_t ud = {
-    .data = hd,
-    .free_func = csnmp_host_definition_destroy
-  };
-
   status = plugin_register_complex_read (/* group = */ NULL, cb_name,
-      csnmp_read_host, hd->interval, /* user_data = */ &ud);
+      csnmp_read_host, hd->interval, &(user_data_t) {
+        .data = hd,
+        .free_func = csnmp_host_definition_destroy,
+      });
   if (status != 0)
   {
     ERROR ("snmp plugin: Registering complex read function failed.");
index e8cde1e..74fdf84 100644 (file)
@@ -339,11 +339,10 @@ static int ctail_init (void)
   {
     ssnprintf(str, sizeof(str), "tail-%zu", i);
 
-    user_data_t ud = {
-     .data = tail_match_list[i]
-    };
-
-    plugin_register_complex_read (NULL, str, ctail_read, tail_match_list_intervals[i], &ud);
+    plugin_register_complex_read (NULL, str, ctail_read, tail_match_list_intervals[i],
+        &(user_data_t) {
+          .data = tail_match_list[i],
+        });
   }
 
   return (0);
index 1bdf8b2..faeb56f 100644 (file)
@@ -482,13 +482,11 @@ static int tcsv_config_add_file(oconfig_item_t *ci)
 
     ssnprintf (cb_name, sizeof (cb_name), "tail_csv/%s", id->path);
 
-    user_data_t ud = {
-        .data = id,
-        .free_func = tcsv_instance_definition_destroy
-    };
-
-    status = plugin_register_complex_read(NULL, cb_name, tcsv_read, id->interval, &ud);
-
+    status = plugin_register_complex_read(NULL, cb_name, tcsv_read, id->interval,
+            &(user_data_t) {
+                .data = id,
+                .free_func = tcsv_instance_definition_destroy,
+            });
     if (status != 0){
         ERROR("tail_csv plugin: Registering complex read function failed.");
         tcsv_instance_definition_destroy(id);
index 40a6fec..dba3a8c 100644 (file)
@@ -38,11 +38,22 @@ struct tr_action_s
 {
   regex_t re;
   char *replacement;
-  int may_be_empty;
+  _Bool may_be_empty;
 
   tr_action_t *next;
 };
 
+struct tr_meta_data_action_s;
+typedef struct tr_meta_data_action_s tr_meta_data_action_t;
+struct tr_meta_data_action_s
+{
+  char *key;
+  regex_t re;
+  char *replacement;
+
+  tr_meta_data_action_t *next;
+};
+
 struct tr_data_s
 {
   tr_action_t *host;
@@ -50,6 +61,7 @@ struct tr_data_s
   tr_action_t *plugin_instance;
   /* tr_action_t *type; */
   tr_action_t *type_instance;
+  tr_meta_data_action_t *meta;
 };
 typedef struct tr_data_s tr_data_t;
 
@@ -85,8 +97,23 @@ static void tr_action_destroy (tr_action_t *act) /* {{{ */
   sfree (act);
 } /* }}} void tr_action_destroy */
 
+static void tr_meta_data_action_destroy (tr_meta_data_action_t *act) /* {{{ */
+{
+  if (act == NULL)
+    return;
+
+  sfree (act->key);
+  regfree (&act->re);
+  sfree (act->replacement);
+
+  if (act->next != NULL)
+    tr_meta_data_action_destroy (act->next);
+
+  sfree (act);
+} /* }}} void tr_meta_data_action_destroy */
+
 static int tr_config_add_action (tr_action_t **dest, /* {{{ */
-    const oconfig_item_t *ci, int may_be_empty)
+    const oconfig_item_t *ci, _Bool may_be_empty)
 {
   tr_action_t *act;
   int status;
@@ -131,8 +158,7 @@ static int tr_config_add_action (tr_action_t **dest, /* {{{ */
   if (act->replacement == NULL)
   {
     ERROR ("tr_config_add_action: tr_strdup failed.");
-    regfree (&act->re);
-    sfree (act);
+    tr_action_destroy (act);
     return (-ENOMEM);
   }
 
@@ -153,8 +179,108 @@ static int tr_config_add_action (tr_action_t **dest, /* {{{ */
   return (0);
 } /* }}} int tr_config_add_action */
 
+static int tr_config_add_meta_action (tr_meta_data_action_t **dest, /* {{{ */
+    const oconfig_item_t *ci, _Bool should_delete)
+{
+  tr_meta_data_action_t *act;
+  int status;
+
+  if (dest == NULL)
+    return (-EINVAL);
+
+  if (should_delete)
+  {
+    if ((ci->values_num != 2)
+        || (ci->values[0].type != OCONFIG_TYPE_STRING)
+        || (ci->values[1].type != OCONFIG_TYPE_STRING))
+    {
+      ERROR ("Target `replace': The `%s' option requires exactly two string "
+          "arguments.", ci->key);
+      return (-1);
+    }
+  }
+  else
+  {
+    if ((ci->values_num != 3)
+        || (ci->values[0].type != OCONFIG_TYPE_STRING)
+        || (ci->values[1].type != OCONFIG_TYPE_STRING)
+        || (ci->values[2].type != OCONFIG_TYPE_STRING))
+    {
+      ERROR ("Target `replace': The `%s' option requires exactly three string "
+          "arguments.", ci->key);
+      return (-1);
+    }
+  }
+
+  if (strlen (ci->values[0].value.string) == 0)
+  {
+    ERROR ("Target `replace': The `%s' option does not accept empty string as "
+        "first argument.", ci->key);
+    return (-1);
+  }
+
+  act = calloc (1, sizeof (*act));
+  if (act == NULL)
+  {
+    ERROR ("tr_config_add_meta_action: calloc failed.");
+    return (-ENOMEM);
+  }
+
+  act->key = NULL;
+  act->replacement = NULL;
+
+  status = regcomp (&act->re, ci->values[1].value.string, REG_EXTENDED);
+  if (status != 0)
+  {
+    char errbuf[1024] = "";
+
+    /* regerror assures null termination. */
+    regerror (status, &act->re, errbuf, sizeof (errbuf));
+    ERROR ("Target `replace': Compiling the regular expression `%s' "
+        "failed: %s.",
+        ci->values[1].value.string, errbuf);
+    sfree (act->key);
+    sfree (act);
+    return (-EINVAL);
+  }
+
+  act->key = tr_strdup (ci->values[0].value.string);
+  if (act->key == NULL)
+  {
+    ERROR ("tr_config_add_meta_action: tr_strdup failed.");
+    tr_meta_data_action_destroy (act);
+    return (-ENOMEM);
+  }
+
+  if (!should_delete) {
+    act->replacement = tr_strdup (ci->values[2].value.string);
+    if (act->replacement == NULL)
+    {
+      ERROR ("tr_config_add_meta_action: tr_strdup failed.");
+      tr_meta_data_action_destroy (act);
+      return (-ENOMEM);
+    }
+  }
+
+  /* Insert action at end of list. */
+  if (*dest == NULL)
+    *dest = act;
+  else
+  {
+    tr_meta_data_action_t *prev;
+
+    prev = *dest;
+    while (prev->next != NULL)
+      prev = prev->next;
+
+    prev->next = act;
+  }
+
+  return (0);
+} /* }}} int tr_config_add_meta_action */
+
 static int tr_action_invoke (tr_action_t *act_head, /* {{{ */
-    char *buffer_in, size_t buffer_in_size, int may_be_empty)
+    char *buffer_in, size_t buffer_in_size, _Bool may_be_empty)
 {
   int status;
   char buffer[DATA_MAX_NAME_LEN];
@@ -215,6 +341,119 @@ static int tr_action_invoke (tr_action_t *act_head, /* {{{ */
   return (0);
 } /* }}} int tr_action_invoke */
 
+static int tr_meta_data_action_invoke ( /* {{{ */
+    tr_meta_data_action_t *act_head, meta_data_t **dest)
+{
+  int status;
+  regmatch_t matches[8] = { [0] = { 0 } };
+
+  if (act_head == NULL)
+    return (-EINVAL);
+
+  if ((*dest) == NULL)  /* nothing to do */
+    return (0);
+
+  for (tr_meta_data_action_t *act = act_head; act != NULL; act = act->next)
+  {
+    char temp[DATA_MAX_NAME_LEN];
+    char *subst_status;
+    int value_type;
+    int meta_data_status;
+    char *value;
+    meta_data_t *result;
+
+    value_type = meta_data_type (*dest, act->key);
+    if (value_type == 0)  /* not found */
+      continue;
+    if (value_type != MD_TYPE_STRING)
+    {
+      WARNING ("Target `replace': Attempting replace on metadata key `%s', "
+          "which isn't a string.",
+          act->key);
+      continue;
+    }
+
+    meta_data_status = meta_data_get_string (*dest, act->key, &value);
+    if (meta_data_status != 0)
+    {
+      ERROR ("Target `replace': Unable to retrieve metadata value for `%s'.",
+          act->key);
+      return (meta_data_status);
+    }
+
+    DEBUG ("target_replace plugin: tr_meta_data_action_invoke: `%s' "
+        "old value = `%s'", act->key, value);
+
+    status = regexec (&act->re, value,
+        STATIC_ARRAY_SIZE (matches), matches,
+        /* flags = */ 0);
+    if (status == REG_NOMATCH)
+    {
+      sfree (value);
+      continue;
+    }
+    else if (status != 0)
+    {
+      char errbuf[1024] = "";
+
+      regerror (status, &act->re, errbuf, sizeof (errbuf));
+      ERROR ("Target `replace': Executing a regular expression failed: %s.",
+          errbuf);
+      sfree (value);
+      continue;
+    }
+
+    if (act->replacement == NULL)
+    {
+      /* no replacement; delete the key */
+      DEBUG ("target_replace plugin: tr_meta_data_action_invoke: "
+          "deleting `%s'", act->key);
+      meta_data_delete (*dest, act->key);
+      sfree (value);
+      continue;
+    }
+
+    subst_status = subst (temp, sizeof (temp), value,
+        (size_t) matches[0].rm_so, (size_t) matches[0].rm_eo, act->replacement);
+    if (subst_status == NULL)
+    {
+      ERROR ("Target `replace': subst (value = %s, start = %zu, end = %zu, "
+          "replacement = %s) failed.",
+          value, (size_t) matches[0].rm_so, (size_t) matches[0].rm_eo,
+          act->replacement);
+      sfree (value);
+      continue;
+    }
+
+    DEBUG ("target_replace plugin: tr_meta_data_action_invoke: `%s' "
+        "value `%s' -> `%s'", act->key, value, temp);
+
+    if ((result = meta_data_create()) == NULL)
+    {
+      ERROR ("Target `replace': failed to create metadata for `%s'.",
+          act->key);
+      sfree (value);
+      return (-ENOMEM);
+    }
+
+    meta_data_status = meta_data_add_string (result, act->key, temp);
+    if (meta_data_status != 0)
+    {
+      ERROR ("Target `replace': Unable to set metadata value for `%s'.",
+          act->key);
+      meta_data_destroy (result);
+      sfree (value);
+      return (meta_data_status);
+    }
+
+    meta_data_clone_merge (dest, result);
+    meta_data_destroy (result);
+    sfree (value);
+  } /* for (act = act_head; act != NULL; act = act->next) */
+
+  return (0);
+} /* }}} int tr_meta_data_action_invoke */
+
 static int tr_destroy (void **user_data) /* {{{ */
 {
   tr_data_t *data;
@@ -231,6 +470,7 @@ static int tr_destroy (void **user_data) /* {{{ */
   tr_action_destroy (data->plugin_instance);
   /* tr_action_destroy (data->type); */
   tr_action_destroy (data->type_instance);
+  tr_meta_data_action_destroy (data->meta);
   sfree (data);
 
   return (0);
@@ -253,6 +493,7 @@ static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
   data->plugin_instance = NULL;
   /* data->type = NULL; */
   data->type_instance = NULL;
+  data->meta = NULL;
 
   status = 0;
   for (int i = 0; i < ci->children_num; i++)
@@ -277,6 +518,12 @@ static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
     else if (strcasecmp ("TypeInstance", child->key) == 0)
       status = tr_config_add_action (&data->type_instance, child,
           /* may be empty = */ 1);
+    else if (strcasecmp ("MetaData", child->key) == 0)
+      status = tr_config_add_meta_action (&data->meta, child,
+          /* should delete = */ 0);
+    else if (strcasecmp ("DeleteMetaData", child->key) == 0)
+      status = tr_config_add_meta_action (&data->meta, child,
+          /* should delete = */ 1);
     else
     {
       ERROR ("Target `replace': The `%s' configuration option is not understood "
@@ -295,7 +542,8 @@ static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
         && (data->plugin == NULL)
         && (data->plugin_instance == NULL)
         /* && (data->type == NULL) */
-        && (data->type_instance == NULL))
+        && (data->type_instance == NULL)
+        && (data->meta == NULL))
     {
       ERROR ("Target `replace': You need to set at least one of `Host', "
           "`Plugin', `PluginInstance' or `TypeInstance'.");
@@ -330,13 +578,18 @@ static int tr_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
     return (-EINVAL);
   }
 
+  if (data->meta != NULL)
+  {
+    tr_meta_data_action_invoke (data->meta, &(vl->meta));
+  }
+
 #define HANDLE_FIELD(f,e) \
   if (data->f != NULL) \
     tr_action_invoke (data->f, vl->f, sizeof (vl->f), e)
   HANDLE_FIELD (host, 0);
   HANDLE_FIELD (plugin, 0);
   HANDLE_FIELD (plugin_instance, 1);
-  /* HANDLE_FIELD (type); */
+  /* HANDLE_FIELD (type, 0); */
   HANDLE_FIELD (type_instance, 1);
 
   return (FC_TARGET_CONTINUE);
index 4f00cbb..3de86e4 100644 (file)
 
 #include "common.h"
 #include "filter_chain.h"
+#include "meta_data.h"
+#include "utils_subst.h"
+
+struct ts_key_list_s
+{
+  char *key;
+  struct ts_key_list_s *next;
+};
+typedef struct ts_key_list_s ts_key_list_t;
+
+static void ts_key_list_free (ts_key_list_t *l) /* {{{ */
+{
+  if (l == NULL)
+    return;
+
+  sfree (l->key);
+
+  if (l->next != NULL)
+    ts_key_list_free (l->next);
+
+  sfree (l);
+} /* }}} void ts_name_list_free */
 
 struct ts_data_s
 {
@@ -37,6 +59,7 @@ struct ts_data_s
   /* char *type; */
   char *type_instance;
   meta_data_t *meta;
+  ts_key_list_t *meta_delete;
 };
 typedef struct ts_data_s ts_data_t;
 
@@ -47,7 +70,7 @@ static int ts_util_get_key_and_string_wo_strdup (const oconfig_item_t *ci, char
       || (ci->values[1].type != OCONFIG_TYPE_STRING))
   {
     ERROR ("ts_util_get_key_and_string_wo_strdup: The %s option requires "
-        "exactly two string argument.", ci->key);
+        "exactly two string arguments.", ci->key);
     return (-1);
   }
 
@@ -92,31 +115,105 @@ static int ts_config_add_meta (meta_data_t **dest, /* {{{ */
 
   if (strlen (key) == 0)
   {
-    ERROR ("Target `set': The `%s' option does not accept empty string as first argument.",
-        ci->key);
+    ERROR ("Target `set': The `%s' option does not accept empty string as "
+        "first argument.", ci->key);
     return (-1);
   }
 
   if (!may_be_empty && (strlen (string) == 0))
   {
-    ERROR ("Target `set': The `%s' option does not accept empty string as second argument.",
-        ci->key);
+    ERROR ("Target `set': The `%s' option does not accept empty string as "
+        "second argument.", ci->key);
     return (-1);
   }
 
   if ((*dest) == NULL)
   {
-    // Create a new meta_data_t
+    /* Create a new meta_data_t */
     if ((*dest = meta_data_create()) == NULL)
     {
       ERROR ("Target `set': failed to create a meta data for `%s'.", ci->key);
-      return (-1);
+      return (-ENOMEM);
     }
   }
 
   return (meta_data_add_string (*dest, key, string));
 } /* }}} int ts_config_add_meta */
 
+static int ts_config_add_meta_delete (ts_key_list_t **dest, /* {{{ */
+    const oconfig_item_t *ci)
+{
+  ts_key_list_t *entry = NULL;
+
+  entry = calloc (1, sizeof (*entry));
+  if (entry == NULL)
+  {
+    ERROR ("ts_config_add_meta_delete: calloc failed.");
+    return (-ENOMEM);
+  }
+
+  if (cf_util_get_string (ci, &entry->key) != 0)
+  {
+    ts_key_list_free (entry);
+    return (-1);  /* An error has already been reported. */
+  }
+
+  if (strlen (entry->key) == 0)
+  {
+    ERROR ("Target `set': The `%s' option does not accept empty string as "
+        "first argument.", ci->key);
+    ts_key_list_free (entry);
+    return (-1);
+  }
+
+  entry->next = *dest;
+  *dest = entry;
+
+  return (0);
+} /* }}} int ts_config_add_meta_delete */
+
+static void ts_subst (char *dest, size_t size, const char *string, /* {{{ */
+    const value_list_t *vl)
+{
+  char temp[DATA_MAX_NAME_LEN];
+
+  /* Initialize the field with the template. */
+  sstrncpy (dest, string, size);
+
+  if (strchr (dest, '%') == NULL)
+    return;
+
+#define REPLACE_FIELD(t, v) \
+  if (subst_string (temp, sizeof (temp), dest, t, v) != NULL) \
+    sstrncpy (dest, temp, size);
+  REPLACE_FIELD ("%{host}", vl->host);
+  REPLACE_FIELD ("%{plugin}", vl->plugin);
+  REPLACE_FIELD ("%{plugin_instance}", vl->plugin_instance);
+  REPLACE_FIELD ("%{type}", vl->type);
+  REPLACE_FIELD ("%{type_instance}", vl->type_instance);
+
+  if (vl->meta != NULL)
+  {
+    char **meta_toc;
+    int meta_entries = meta_data_toc (vl->meta, &meta_toc);
+    for (int i = 0; i < meta_entries; i++)
+    {
+      char meta_name[DATA_MAX_NAME_LEN];
+      char *value_str;
+      const char *key = meta_toc[i];
+
+      ssnprintf (meta_name, sizeof (meta_name), "%%{meta:%s}", key);
+      if (meta_data_as_string (vl->meta, key, &value_str) != 0)
+        continue;
+
+      REPLACE_FIELD (meta_name, value_str);
+      sfree (value_str);
+    }
+
+    strarray_free (meta_toc, (size_t) meta_entries);
+  }
+} /* }}} int ts_subst */
+
 static int ts_destroy (void **user_data) /* {{{ */
 {
   ts_data_t *data;
@@ -134,6 +231,7 @@ static int ts_destroy (void **user_data) /* {{{ */
   /* free (data->type); */
   free (data->type_instance);
   meta_data_destroy(data->meta);
+  ts_key_list_free (data->meta_delete);
   free (data);
 
   return (0);
@@ -157,6 +255,7 @@ static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
   /* data->type = NULL; */
   data->type_instance = NULL;
   data->meta = NULL;
+  data->meta_delete = NULL;
 
   status = 0;
   for (int i = 0; i < ci->children_num; i++)
@@ -184,6 +283,8 @@ static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
     else if (strcasecmp ("MetaData", child->key) == 0)
       status = ts_config_add_meta (&data->meta, child,
           /* may be empty = */ 1);
+    else if (strcasecmp ("DeleteMetaData", child->key) == 0)
+      status = ts_config_add_meta_delete (&data->meta_delete, child);
     else
     {
       ERROR ("Target `set': The `%s' configuration option is not understood "
@@ -203,13 +304,30 @@ static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
         && (data->plugin_instance == NULL)
         /* && (data->type == NULL) */
         && (data->type_instance == NULL)
-        && (data->meta == NULL))
+        && (data->meta == NULL)
+        && (data->meta_delete == NULL))
     {
       ERROR ("Target `set': You need to set at least one of `Host', "
-          "`Plugin', `PluginInstance', `TypeInstance', `MetaData'.");
+          "`Plugin', `PluginInstance', `TypeInstance', "
+          "`MetaData', or `DeleteMetaData'.");
       status = -1;
     }
 
+    if (data->meta != NULL)
+    {
+      /* If data->meta_delete is NULL, this loop is a no-op. */
+      for (ts_key_list_t *l=data->meta_delete; l != NULL; l = l->next)
+      {
+        if (meta_data_type (data->meta, l->key) != 0)
+        {
+          /* MetaData and DeleteMetaData for the same key. */
+          ERROR ("Target `set': Can only have one of `MetaData' or "
+              "`DeleteMetaData' for any given key.");
+          status = -1;
+        }
+      }
+    }
+
     break;
   }
 
@@ -227,6 +345,8 @@ static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
     notification_meta_t __attribute__((unused)) **meta, void **user_data)
 {
   ts_data_t *data;
+  value_list_t orig;
+  meta_data_t *new_meta = NULL;
 
   if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
     return (-EINVAL);
@@ -238,17 +358,79 @@ static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
     return (-EINVAL);
   }
 
+  orig = *vl;
+
   if (data->meta != NULL)
   {
-    meta_data_clone_merge(&(vl->meta), data->meta);
+    char temp[DATA_MAX_NAME_LEN*2];
+    int meta_entries;
+    char **meta_toc;
+
+    if ((new_meta = meta_data_create()) == NULL)
+    {
+      ERROR ("Target `set': failed to create replacement metadata.");
+      return (-ENOMEM);
+    }
+
+    meta_entries = meta_data_toc (data->meta, &meta_toc);
+    for (int i = 0; i < meta_entries; i++)
+    {
+      const char *key = meta_toc[i];
+      char *string;
+      int status;
+
+      status = meta_data_get_string (data->meta, key, &string);
+      if (status)
+      {
+        ERROR ("Target `set': Unable to get replacement metadata value `%s'.",
+            key);
+        strarray_free (meta_toc, (size_t) meta_entries);
+        return (status);
+      }
+
+      ts_subst (temp, sizeof (temp), string, &orig);
+
+      DEBUG ("target_set: ts_invoke: setting metadata value for key `%s': "
+          "`%s'.", key, temp);
+
+      status = meta_data_add_string (new_meta, key, temp);
+
+      if (status)
+      {
+        ERROR ("Target `set': Unable to set metadata value `%s'.", key);
+        strarray_free (meta_toc, (size_t) meta_entries);
+        return (status);
+      }
+    }
+
+    strarray_free (meta_toc, (size_t) meta_entries);
+  }
+
+#define SUBST_FIELD(f) \
+  if (data->f != NULL) { \
+    ts_subst (vl->f, sizeof (vl->f), data->f, &orig); \
+    DEBUG ("target_set: ts_invoke: setting "#f": `%s'.", vl->f); \
+  }
+  SUBST_FIELD (host);
+  SUBST_FIELD (plugin);
+  SUBST_FIELD (plugin_instance);
+  /* SUBST_FIELD (type); */
+  SUBST_FIELD (type_instance);
+
+  /* Need to merge the metadata in now, because of the shallow copy. */
+  if (new_meta != NULL)
+  {
+    meta_data_clone_merge(&(vl->meta), new_meta);
+    meta_data_destroy(new_meta);
   }
 
-#define SET_FIELD(f) if (data->f != NULL) { sstrncpy (vl->f, data->f, sizeof (vl->f)); }
-  SET_FIELD (host);
-  SET_FIELD (plugin);
-  SET_FIELD (plugin_instance);
-  /* SET_FIELD (type); */
-  SET_FIELD (type_instance);
+  /* If data->meta_delete is NULL, this loop is a no-op. */
+  for (ts_key_list_t *l=data->meta_delete; l != NULL; l = l->next)
+  {
+    DEBUG ("target_set: ts_invoke: deleting metadata value for key `%s'.",
+        l->key);
+    meta_data_delete(vl->meta, l->key);
+  }
 
   return (FC_TARGET_CONTINUE);
 } /* }}} int ts_invoke */
index e001a62..aa95dac 100644 (file)
@@ -47,14 +47,11 @@ enum dev_type {
 };
 
 static void thermal_submit (const char *plugin_instance, enum dev_type dt,
-               gauge_t value)
+               value_t value)
 {
        value_list_t vl = VALUE_LIST_INIT;
-       value_t v;
-
-       v.gauge = value;
-       vl.values = &v;
 
+       vl.values = &value;
        vl.values_len = 1;
 
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
@@ -72,52 +69,26 @@ static void thermal_submit (const char *plugin_instance, enum dev_type dt,
 static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
                const char *name, void __attribute__((unused)) *user_data)
 {
-       char filename[256];
-       char data[1024];
-       int len;
+       char filename[PATH_MAX];
        _Bool success = 0;
+       value_t value;
 
        if (device_list && ignorelist_match (device_list, name))
                return -1;
 
-       len = ssnprintf (filename, sizeof (filename),
-                       "%s/%s/temp", dirname_sysfs, name);
-       if ((len < 0) || ((size_t) len >= sizeof (filename)))
-               return -1;
-
-       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
-       if (len > 1 && data[--len] == '\n') {
-               char *endptr = NULL;
-               double temp;
-
-               data[len] = 0;
-               errno = 0;
-               temp = strtod (data, &endptr) / 1000.0;
-
-               if (endptr == data + len && errno == 0) {
-                       thermal_submit(name, TEMP, temp);
-                       success = 1;
-               }
+       ssnprintf (filename, sizeof (filename), "%s/%s/temp", dirname_sysfs, name);
+       if (parse_value_file (filename, &value, DS_TYPE_GAUGE) == 0)
+       {
+               value.gauge /= 1000.0;
+               thermal_submit(name, TEMP, value);
+               success = 1;
        }
 
-       len = ssnprintf (filename, sizeof (filename),
-                       "%s/%s/cur_state", dirname_sysfs, name);
-       if ((len < 0) || ((size_t) len >= sizeof (filename)))
-               return -1;
-
-       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
-       if (len > 1 && data[--len] == '\n') {
-               char *endptr = NULL;
-               double state;
-
-               data[len] = 0;
-               errno = 0;
-               state = strtod (data, &endptr);
-
-               if (endptr == data + len && errno == 0) {
-                       thermal_submit(name, COOLING_DEV, state);
-                       success = 1;
-               }
+       ssnprintf (filename, sizeof (filename), "%s/%s/cur_state", dirname_sysfs, name);
+       if (parse_value_file (filename, &value, DS_TYPE_GAUGE) == 0)
+       {
+               thermal_submit(name, COOLING_DEV, value);
+               success = 1;
        }
 
        return (success ? 0 : -1);
@@ -176,7 +147,7 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
                temp = (strtod (data + len, &endptr) + add) * factor;
 
                if (endptr != data + len && errno == 0) {
-                       thermal_submit(name, TEMP, temp);
+                       thermal_submit(name, TEMP, (value_t) { .gauge = temp });
                        return 0;
                }
        }
index 2d8a08e..7b813c9 100644 (file)
@@ -1018,8 +1018,7 @@ parse_int_file(const char *fmt, ...)
 {
        va_list args;
        char path[PATH_MAX];
-       FILE *filep;
-       int len, value;
+       int len;
 
        va_start(args, fmt);
        len = vsnprintf(path, sizeof(path), fmt, args);
@@ -1029,18 +1028,13 @@ parse_int_file(const char *fmt, ...)
                return -1;
        }
 
-       filep = fopen(path, "r");
-       if (!filep) {
-               ERROR("turbostat plugin: Failed to open '%s'", path);
-               return -1;
-       }
-       if (fscanf(filep, "%d", &value) != 1) {
-               ERROR("turbostat plugin: Failed to parse number from '%s'", path);
-               fclose(filep);
+       value_t v;
+       if (parse_value_file (path, &v, DS_TYPE_DERIVE) != 0) {
+               ERROR ("turbostat plugin: Parsing \"%s\" failed.", path);
                return -1;
        }
-       fclose(filep);
-       return value;
+
+       return (int) v.derive;
 }
 
 static int
index 9ebc5fc..6ec7812 100644 (file)
@@ -74,6 +74,8 @@ duration                seconds:GAUGE:0:U
 email_check             value:GAUGE:0:U
 email_count             value:GAUGE:0:U
 email_size              value:GAUGE:0:U
+energy                  value:GAUGE:U:U
+energy_wh               value:GAUGE:U:U
 entropy                 value:GAUGE:0:4294967295
 evicted_keys            value:DERIVE:0:U
 expired_keys            value:DERIVE:0:U
@@ -92,6 +94,7 @@ hash_collisions         value:DERIVE:0:U
 http_request_methods    value:DERIVE:0:U
 http_requests           value:DERIVE:0:U
 http_response_codes     value:DERIVE:0:U
+hugepages               free:GAUGE:0:4294967295, used:GAUGE:0:4294967295
 humidity                value:GAUGE:0:100
 if_collisions           value:DERIVE:0:U
 if_dropped              rx:DERIVE:0:U, tx:DERIVE:0:U
index e523420..0af586f 100644 (file)
@@ -150,18 +150,29 @@ static int gr_format_name (char *ret, int ret_len,
         sstrncpy (tmp_plugin, n_plugin, sizeof (tmp_plugin));
 
     if (n_type_instance[0] != '\0')
-        ssnprintf (tmp_type, sizeof (tmp_type), "%s%c%s",
-            n_type,
-            (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
-            n_type_instance);
+    {
+        if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+            sstrncpy (tmp_type, n_type_instance, sizeof (tmp_type));
+        else
+            ssnprintf (tmp_type, sizeof (tmp_type), "%s%c%s",
+                n_type,
+                (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+                n_type_instance);
+    }
     else
         sstrncpy (tmp_type, n_type, sizeof (tmp_type));
 
     /* Assert always_append_ds -> ds_name */
     assert (!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
     if (ds_name != NULL)
-        ssnprintf (ret, ret_len, "%s%s%s.%s.%s.%s",
-            prefix, n_host, postfix, tmp_plugin, tmp_type, ds_name);
+    {
+        if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(tmp_plugin, tmp_type) == 0)
+            ssnprintf (ret, ret_len, "%s%s%s.%s.%s",
+                prefix, n_host, postfix, tmp_plugin, ds_name);
+        else
+            ssnprintf (ret, ret_len, "%s%s%s.%s.%s.%s",
+                prefix, n_host, postfix, tmp_plugin, tmp_type, ds_name);
+    }
     else
         ssnprintf (ret, ret_len, "%s%s%s.%s.%s",
             prefix, n_host, postfix, tmp_plugin, tmp_type);
index 60576da..5165f9e 100644 (file)
@@ -29,6 +29,7 @@
 #define GRAPHITE_STORE_RATES        0x01
 #define GRAPHITE_SEPARATE_INSTANCES 0x02
 #define GRAPHITE_ALWAYS_APPEND_DS   0x04
+#define GRAPHITE_DROP_DUPE_FIELDS   0x08
 
 int format_graphite (char *buffer,
     size_t buffer_size, const data_set_t *ds,
index 6cc092c..c214912 100644 (file)
@@ -951,16 +951,14 @@ static int varnish_init (void) /* {{{ */
 
        varnish_config_apply_default (conf);
 
-       user_data_t ud = {
-               .data = conf,
-               .free_func = varnish_config_free
-       };
-
        plugin_register_complex_read (/* group = */ "varnish",
                        /* name      = */ "varnish/localhost",
                        /* callback  = */ varnish_read,
                        /* interval  = */ 0,
-                       /* user data = */ &ud);
+                       &(user_data_t) {
+                               .data = conf,
+                               .free_func = varnish_config_free,
+                       });
 
        return (0);
 } /* }}} int varnish_init */
@@ -968,7 +966,6 @@ static int varnish_init (void) /* {{{ */
 static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
 {
        user_config_t *conf;
-       user_data_t ud;
        char callback_name[DATA_MAX_NAME_LEN];
 
        conf = calloc (1, sizeof (*conf));
@@ -1132,14 +1129,14 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
        ssnprintf (callback_name, sizeof (callback_name), "varnish/%s",
                        (conf->instance == NULL) ? "localhost" : conf->instance);
 
-       ud.data = conf;
-       ud.free_func = varnish_config_free;
-
        plugin_register_complex_read (/* group = */ "varnish",
                        /* name      = */ callback_name,
                        /* callback  = */ varnish_read,
                        /* interval  = */ 0,
-                       /* user data = */ &ud);
+                       &(user_data_t) {
+                               .data = conf,
+                               .free_func = varnish_config_free,
+                       });
 
        have_instance = 1;
 
index fe2376a..ebc5ce2 100644 (file)
@@ -548,6 +548,9 @@ static int wg_config_node (oconfig_item_t *ci)
         else if (strcasecmp ("AlwaysAppendDS", child->key) == 0)
             cf_util_get_flag (child, &cb->format_flags,
                     GRAPHITE_ALWAYS_APPEND_DS);
+        else if (strcasecmp ("DropDuplicateFields", child->key) == 0)
+            cf_util_get_flag (child, &cb->format_flags,
+                    GRAPHITE_DROP_DUPE_FIELDS);
         else if (strcasecmp ("EscapeCharacter", child->key) == 0)
             config_set_char (&cb->escape_char, child);
         else
@@ -575,15 +578,13 @@ static int wg_config_node (oconfig_item_t *ci)
         ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s",
                 cb->name);
 
-    user_data_t ud = {
-        .data = cb,
-        .free_func = wg_callback_free
-    };
-
-    plugin_register_write (callback_name, wg_write, &ud);
+    plugin_register_write (callback_name, wg_write,
+            &(user_data_t) {
+                .data = cb,
+                .free_func = wg_callback_free,
+            });
 
-    ud.free_func = NULL;
-    plugin_register_flush (callback_name, wg_flush, &ud);
+    plugin_register_flush (callback_name, wg_flush, &(user_data_t) { .data = cb });
 
     return (0);
 }
index 41615d3..3d1fe3d 100644 (file)
@@ -419,6 +419,9 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{
         }
         assert (command_len < cb->send_buffer_free);
 
+        /* Make scan-build happy. */
+        assert (cb->send_buffer != NULL);
+
         /* `command_len + 1' because `command_len' does not include the
          * trailing null byte. Neither does `send_buffer_fill'. */
         memcpy (cb->send_buffer + cb->send_buffer_fill,
index 9fda2df..75da6aa 100644 (file)
@@ -383,12 +383,11 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, oconfig_item_t *ci) /* {{{
     ssnprintf(callback_name, sizeof(callback_name),
               "write_kafka/%s", tctx->topic_name);
 
-    user_data_t ud = {
-        .data = tctx,
-        .free_func = kafka_topic_context_free
-    };
-
-    status = plugin_register_write (callback_name, kafka_write, &ud);
+    status = plugin_register_write (callback_name, kafka_write,
+            &(user_data_t) {
+                .data = tctx,
+                .free_func = kafka_topic_context_free,
+            });
     if (status != 0) {
         WARNING ("write_kafka plugin: plugin_register_write (\"%s\") "
                 "failed with status %i.",
index b0dc6f1..e10a912 100644 (file)
 #include "plugin.h"
 
 #include "utils_format_graphite.h"
+#include "utils_format_json.h"
 
 #include <netdb.h>
 
-#define WL_BUF_SIZE 8192
+#define WL_BUF_SIZE 16384
 
-static int wl_write_messages (const data_set_t *ds, const value_list_t *vl)
+#define WL_FORMAT_GRAPHITE 1
+#define WL_FORMAT_JSON 2
+
+/* Plugin:WriteLog has to also operate without a config, so use a global. */
+int wl_format = WL_FORMAT_GRAPHITE;
+
+static int wl_write_graphite (const data_set_t *ds, const value_list_t *vl)
 {
     char buffer[WL_BUF_SIZE] = { 0 };
     int status;
 
     if (0 != strcmp (ds->type, vl->type))
     {
-        ERROR ("write_log plugin: DS type does not match "
-                "value list type");
+        ERROR ("write_log plugin: DS type does not match value list type");
         return -1;
     }
 
@@ -56,20 +62,95 @@ static int wl_write_messages (const data_set_t *ds, const value_list_t *vl)
     INFO ("write_log values:\n%s", buffer);
 
     return (0);
-} /* int wl_write_messages */
+} /* int wl_write_graphite */
+
+static int wl_write_json (const data_set_t *ds, const value_list_t *vl)
+{
+    char buffer[WL_BUF_SIZE] = { 0 };
+    size_t bfree = sizeof(buffer);
+    size_t bfill = 0;
+
+    if (0 != strcmp (ds->type, vl->type))
+    {
+        ERROR ("write_log plugin: DS type does not match value list type");
+        return -1;
+    }
+
+    format_json_initialize(buffer, &bfill, &bfree);
+    format_json_value_list(buffer, &bfill, &bfree, ds, vl,
+                           /* store rates = */ 0);
+    format_json_finalize(buffer, &bfill, &bfree);
+
+    INFO ("write_log values:\n%s", buffer);
+
+    return (0);
+} /* int wl_write_json */
 
 static int wl_write (const data_set_t *ds, const value_list_t *vl,
         __attribute__ ((unused)) user_data_t *user_data)
 {
-    int status;
+    int status = 0;
 
-    status = wl_write_messages (ds, vl);
+    if (wl_format == WL_FORMAT_GRAPHITE)
+    {
+        status = wl_write_graphite (ds, vl);
+    }
+    else if (wl_format == WL_FORMAT_JSON)
+    {
+        status = wl_write_json (ds, vl);
+    }
 
     return (status);
 }
 
+static int wl_config (oconfig_item_t *ci) /* {{{ */
+{
+    _Bool format_seen = 0;
+
+    for (int i = 0; i < ci->children_num; i++)
+    {
+        oconfig_item_t *child = ci->children + i;
+
+        if (strcasecmp ("Format", child->key) == 0)
+        {
+            char str[16];
+
+            if (cf_util_get_string_buffer (child, str, sizeof (str)) != 0)
+                continue;
+
+            if (format_seen)
+            {
+                WARNING ("write_log plugin: Redefining option `%s'.",
+                    child->key);
+            }
+            format_seen = 1;
+
+            if (strcasecmp ("Graphite", str) == 0)
+                wl_format = WL_FORMAT_GRAPHITE;
+            else if (strcasecmp ("JSON", str) == 0)
+                wl_format = WL_FORMAT_JSON;
+            else
+            {
+                ERROR ("write_log plugin: Unknown format `%s' for option `%s'.",
+                    str, child->key);
+                return (-EINVAL);
+            }
+        }
+        else
+        {
+            ERROR ("write_log plugin: Invalid configuration option: `%s'.",
+                child->key);
+            return (-EINVAL);
+        }
+    }
+
+    return (0);
+} /* }}} int wl_config */
+
 void module_register (void)
 {
+    plugin_register_complex_config ("write_log", wl_config);
+    /* If config is supplied, the global wl_format will be set. */
     plugin_register_write ("write_log", wl_write, NULL);
 }
 
index 6d5f379..796574e 100644 (file)
@@ -339,12 +339,11 @@ static int wm_config_node (oconfig_item_t *ci) /* {{{ */
 
     ssnprintf (cb_name, sizeof (cb_name), "write_mongodb/%s", node->name);
 
-    user_data_t ud = {
-      .data = node,
-      .free_func = wm_config_free
-    };
-
-    status = plugin_register_write (cb_name, wm_write, &ud);
+    status = plugin_register_write (cb_name, wm_write,
+                   &(user_data_t) {
+                           .data = node,
+                           .free_func = wm_config_free,
+                   });
     INFO ("write_mongodb plugin: registered write plugin %s %d",cb_name,status);
   }
 
index 4722416..def3d9c 100644 (file)
@@ -234,12 +234,11 @@ static int wr_config_node (oconfig_item_t *ci) /* {{{ */
 
     ssnprintf (cb_name, sizeof (cb_name), "write_redis/%s", node->name);
 
-    user_data_t ud = {
-      .data = node,
-      .free_func = wr_config_free
-    };
-
-    status = plugin_register_write (cb_name, wr_write, &ud);
+    status = plugin_register_write (cb_name, wr_write,
+        &(user_data_t) {
+          .data = node,
+          .free_func = wr_config_free,
+        });
   }
 
   if (status != 0)