Merge remote-tracking branch 'github/pr/2467'
authorFlorian Forster <octo@collectd.org>
Thu, 19 Oct 2017 07:21:50 +0000 (09:21 +0200)
committerFlorian Forster <octo@collectd.org>
Thu, 19 Oct 2017 07:21:50 +0000 (09:21 +0200)
29 files changed:
AUTHORS
ChangeLog
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd.conf.in
src/collectd.conf.pod
src/cpython.h
src/curl_json.c
src/daemon/common.c
src/daemon/common.h
src/filecount.c
src/intel_pmu.c
src/ipmi.c
src/libcollectdclient/network_parse.c
src/mcelog.c
src/memcached.c
src/multimeter.c
src/ntpd.c
src/python.c
src/pyvalues.c
src/rrdtool.c
src/snmp.c
src/ted.c
src/utils_format_kairosdb.c
src/utils_format_kairosdb.h
src/varnish.c
src/write_http.c
src/write_prometheus.c
src/write_tsdb.c

diff --git a/AUTHORS b/AUTHORS
index b99c156..4df743c 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -116,6 +116,9 @@ Dan Ryder <daryder at cisco.com>
 David Bacher <drbacher at gmail.com>
  - serial plugin.
 
+Denis Pompilio <denis.pompilio at gmail.com>
+ - Improvements to the write_http plugin.
+
 Doug MacEachern <dougm at hyperic.com>
  - The `-T' option (config testing mode).
  - OpenVPN plugin.
index b8755d9..f990dd4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
          embedded HTTP server, in a format compatible with Prometheus'
          collectd_exporter. Thanks to Florian Forster. #1967
 
+2017-10-06, Version 5.6.3
+       * collectd: support for boolean string config values has been
+         reintroduced. Thanks to Sebastian Harl. #2083, #2098
+       * collectd: The capability checking has been changed to use
+         "cap_get_proc()". Thanks to Marc Fournier. #2151
+       * Documentation: A section documenting ignore lists has been added to
+         collectd.conf(5). Thanks to Florian Forster.
+       * AMQP plugin: The "ExchangeType" option is now also valid for
+         publishers. Thanks to Florian Forster. #2286
+       * Apache, Ascent, BIND, cURL, cURL-JSON, cURL-XML, nginx, Write HTTP
+         plugins: Handling of URLs that redirect elsewhere has been fixed.
+         Thanks to Pavel Rochnyack. #2328
+       * BIND plugin: Fix parsing of the sample time provided by BIND.
+         Previously, the time was assumed to be in the local time zone when in
+         fact it was in UTC. Thanks to Ed Ravin. #1268
+       * BIND plugin: Memory leaks have been fixed. Thanks to Ruben Kerkhof.
+         #2303
+       * Chrony plugin: Build flags have been fixed. Thanks to Thomas Jost and
+         Marc Fournier. #2133
+       * cURL-JSON plugin: The timeout value has been changed to default to the
+         collection interval. This fixes a regression. Thanks to Marc Fournier.
+       * cURL-JSON plugin: Handling of arrays has been fixed. Thanks to Florian
+         Forster. #2266
+       * DBI plugin: Memory leaks at shutdown have been fixes. Thanks to Pavel
+         Rochnyack and Florian Forster.
+       * E-Mail, Exec, UnixSock plugins: Group ID lookup on systems with many
+         groups has been fixed. Thanks to Ruben Kerkhof and Florian Forster.
+         #2208
+       * IPC plugin: A compilation error on AIX has been fixed. Thanks to Pavel
+         Rochnyack. #2305
+       * LogFile plugin: If writing to the file fails, print log messages on
+         "STDERR" instead. Thanks to Marc Fournier.
+       * Log Logstash plugin: If writing the log entry fails, print it to
+         "STDERR" instead. Thanks to Marc Fournier.
+       * memcachec, Tail plugins: A resource leak in the matching
+         infrastructure has been fixed. Thanks to Krzysztof Matczak. #2192
+       * MQTT plugin: Invalid symbols in topic names are now replaced and a
+         resource leak has been fixed. Thanks to Denys Fedoryshchenko. #2123
+       * Network plugin: A potential endless-loop has been fixed. This can be
+         triggered remotely by sending a signed network packet to a server
+         which is not set up to check signatures. Thanks to Marcin Kozlowski
+         and Pavel Rochnyack. #2174, #2233, CVE-2017-7401
+       * Network plugin: A use-after-free has been fixed. Thanks to Pavel
+         Rochnyack. #2375
+       * Notify Email plugin: The plugin is no longer explicitly linked against
+         libssl and libcrypto, relies on libesmtp being linked correctly.
+         Thanks to Marc Fournier. Debian#852924
+       * NTPd plugin: Calculation of loop offset and error has been fixed.
+         Thanks to Neil Wilson. #2188
+       * OpenLDAP plugin: An incorrect use of the ldap library, leading to a
+         crash, has been fixed. Thanks to Marc Fournier. #2331
+       * Perl plugin: A potential double-free has been fixed. Thanks to Florian
+         Forster. #2278
+       * Perl plugin: Print an error when an incorrect configuration is
+         encountered. Thanks to Pavel Rochnyack. #927
+       * RRDtool plugin: Incorrect handling of the flushes timeout option has
+         been fixed. Handling of the "RandomTimeout" has been fixed. Thanks to
+         Pavel Rochnyack. #2363
+       * SMART plugin: Some warning messages have been removed and the code has
+         been cleaned up. Thanks to Florian Forster. #2062
+       * SMART plugin: A check for the "CAP_SYS_RAWIO" capability has been
+         added. Thanks to Marc Fournier.
+       * SNMP plugin: A double free has been fixed. Thanks to Pavel Rochnyack.
+         #2291
+       * Write Graphite plugin: Error handling in the case that calculating a
+         metric's rate fails has been improved. Previously, the raw counter
+         values were sent to Graphite. Thanks to Iain Buclaw. #2209
+       * Write Kafka plugin: A 32 bit random number is now used when formatting
+         a random key. Thanks to Florian Forster. #2074
+
+
 2016-11-30, Version 5.6.2
        * collectd: A compile error on AIX has been fixed: "MSG_DONTWAIT" is not
          available on AIX. Thanks to Chao Yang.
index 6a83b97..7a19f45 100644 (file)
@@ -475,7 +475,7 @@ Methods defined here:
 
 =over 4
 
-=item B<dispatch>([type][, values][, plugin_instance][, type_instance][, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.
+=item B<dispatch>([type][, message][, plugin_instance][, type_instance][, plugin][, host][, time][, severity][, meta]) -> None.  Dispatch a notification.
 
 Dispatch this instance to the collectd process. The object has members for each
 of the possible arguments for this method. For a detailed explanation of these
@@ -501,6 +501,16 @@ generated.
 The severity of this notification. Assign or compare to I<NOTIF_FAILURE>,
 I<NOTIF_WARNING> or I<NOTIF_OKAY>.
 
+=item meta
+
+These are the meta data for the Notification object.
+It has to be a dictionary of numbers, strings or bools. All keys must be
+strings. I<int> and I<long> objects will be dispatched as signed integers unless
+they are between 2**63 and 2**64-1, which will result in a unsigned integer.
+One of these storage classes can be forced by using the classes
+B<collectd.Signed> and B<collectd.Unsigned>. A meta object received by a
+notification callback will always contain B<Signed> or B<Unsigned> objects.
+
 =back
 
 =head1 FUNCTIONS
index edb9506..d615088 100644 (file)
@@ -36,6 +36,8 @@ collectd-snmp - Documentation of collectd's C<snmp plugin>
       Community "community_string"
       Collect "std_traffic"
       Interval 120
+      Timeout 10
+      Retries 1
     </Host>
     <Host "some.server.mydomain.org">
       Address "192.168.0.42"
@@ -60,6 +62,8 @@ collectd-snmp - Documentation of collectd's C<snmp plugin>
       Community "more_communities"
       Collect "powerplus_voltge_input"
       Interval 300
+      Timeout 5
+      Retries 5
     </Host>
   </Plugin>
 
@@ -78,7 +82,7 @@ and ten threads are used.
 =head1 CONFIGURATION
 
 Since the aim of the C<snmp plugin> is to provide a generic interface to SNMP,
-it's configuration is not trivial and may take some time.
+its configuration is not trivial and may take some time.
 
 Since the C<Net-SNMP> library is used you can use all the environment variables
 that are interpreted by that package. See L<snmpcmd(1)> for more details.
@@ -281,6 +285,15 @@ switches, embedded devices, rack monitoring systems and so on. Since the
 B<Step> of generated RRD files depends on this setting it's wise to select a
 reasonable value once and never change it.
 
+=item B<Timeout> I<Seconds>
+
+How long to wait for a response. The C<Net-SNMP> library default is 1 second.
+
+=item B<Retries> I<Integer>
+
+The number of times that a query should be retried after the Timeout expires.
+The C<Net-SNMP> library default is 5.
+
 =back
 
 =head1 SEE ALSO
index 3287e0d..261abdf 100644 (file)
 #              Size "+10k"
 #              Recursive true
 #              IncludeHidden false
+#              RegularOnly true
 #              #FilesSizeType "bytes"
 #              #FilesCountType "files"
 #              #TypeInstance "instance"
 #</Plugin>
 
 #<Plugin ipmi>
-#      Sensor "some_sensor"
-#      Sensor "another_one"
-#      IgnoreSelected false
-#      NotifySensorAdd false
-#      NotifySensorRemove true
-#      NotifySensorNotPresent false
+#      <Instance "local">
+#              Sensor "some_sensor"
+#              Sensor "another_one"
+#              IgnoreSelected false
+#              NotifySensorAdd false
+#              NotifySensorRemove true
+#              NotifySensorNotPresent false
+#              NotifyIPMIConnectionState false
+#              SELEnabled false
+#              SELClearEvent false
+#      </Instance>
+#      <Instance "remote">
+#              Host "server.example.com"
+#              Address  "1.2.3.4"
+#              Username "user"
+#              Password "secret"
+#              #AuthType "md5"
+#              Sensor "some_sensor"
+#              Sensor "another_one"
+#              IgnoreSelected false
+#              NotifySensorAdd false
+#              NotifySensorRemove true
+#              NotifySensorNotPresent false
+#              NotifyIPMIConnectionState false
+#              SELEnabled false
+#              SELClearEvent false
+#      </Instance>
 #</Plugin>
 
 #<Plugin iptables>
 #       Community "community_string"
 #       Collect "std_traffic"
 #       Interval 120
+#      Timeout 10
+#      Retries 1
 #   </Host>
 #   <Host "some.server.mydomain.org">
 #       Address "192.168.0.42"
 #       Community "more_communities"
 #       Collect "powerplus_voltge_input"
 #       Interval 300
+#      Timeout 5
+#      Retries 5
 #   </Host>
 #</Plugin>
 
 #      CollectManagement false    # Varnish 4 only
 #      CollectSMF false           # Varnish 4 only
 #      CollectVBE false           # Varnish 4 only
+#      CollectMSE false           # Varnish-Plus 4 only
 #   </Instance>
 #</Plugin>
 
 #              Header "X-Custom-Header: custom_value"
 #              SSLVersion "TLSv1"
 #              Format "Command"
+#              Prefix "collectd"  # metric prefix, only available for KAIROSDB format
 #              Attribute "key" "value"     # only available for KAIROSDB format
 #              TTL 0   # data ttl, only available for KAIROSDB format
 #              Metrics true
index 3fa9159..4c4c261 100644 (file)
@@ -2834,6 +2834,11 @@ Controls whether or not to include "hidden" files and directories in the count.
 "Hidden" files and directories are those, whose name begins with a dot.
 Defaults to I<false>, i.e. by default hidden files and directories are ignored.
 
+=item B<RegularOnly> I<true>|I<false>
+
+Controls whether or not to include only regular files in the count.
+Defaults to I<true>, i.e. by default non regular files are ignored.
+
 =item B<FilesSizeType> I<Type>
 
 Sets the type used to dispatch files combined size. Empty value ("") disables
@@ -3351,8 +3356,43 @@ This option is only available on Solaris.
 
 =head2 Plugin C<ipmi>
 
+The B<ipmi plugin> allows to monitor server platform status using the Intelligent
+Platform Management Interface (IPMI). Local and remote interfaces are supported.
+
+The plugin configuration consists of one or more B<Instance> blocks which
+specify one I<ipmi> connection each. Each block requires one unique string
+argument as the instance name. If instances are not configured, an instance with
+the default option values will be created.
+
+For backwards compatibility, any option other than B<Instance> block will trigger
+legacy config handling and it will be treated as an option within B<Instance>
+block. This support will go away in the next major version of Collectd.
+
+Within the B<Instance> blocks, the following options are allowed:
+
 =over 4
 
+=item B<Address> I<Address>
+
+Hostname or IP to connect to. If not specified, plugin will try to connect to
+local management controller (BMC).
+
+=item B<Username> I<Username>
+
+=item B<Password> I<Password>
+
+The username and the password to use for the connection to remote BMC.
+
+=item B<AuthType> I<MD5>|I<rmcp+>
+
+Forces the authentication type to use for the connection to remote BMC.
+By default most secure type is seleted.
+
+=item B<Host> I<Hostname>
+
+Sets the B<host> field of dispatched values. Defaults to the global hostname
+setting.
+
 =item B<Sensor> I<Sensor>
 
 Selects sensors to collect or to ignore, depending on B<IgnoreSelected>.
@@ -3381,6 +3421,24 @@ If a sensor disappears a notification is sent.
 If you have for example dual power supply and one of them is (un)plugged then
 a notification is sent.
 
+=item B<NotifyIPMIConnectionState> I<true>|I<false>
+
+If a IPMI connection state changes after initialization time of a minute
+a notification is sent. Defaults to B<false>.
+
+=item B<SELEnabled> I<true>|I<false>
+
+If system event log (SEL) is enabled, plugin will listen for sensor threshold
+and discrete events. When event is received the notification is sent.
+Defaults to B<false>.
+
+=item B<SELClearEvent> I<true>|I<false>
+
+If SEL clear event is enabled, plugin will delete event from SEL list after
+it is received and successfully handled. In this case other tools that are
+subscribed for SEL events will receive an empty event.
+Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<iptables>
@@ -6751,12 +6809,15 @@ C<I<prefix>/var/run/collectd-powerdns>.
 
 =head2 Plugin C<processes>
 
-=over 4
+Collects information about processes of local system.
 
-=item B<Process> I<Name>
+By default, with no process matches configured, only general statistics is
+collected: the number of processes in each state and fork rate.
 
-Select more detailed statistics of processes matching this name. The statistics
-collected for these selected processes are:
+Process matches can be configured by B<Process> and B<ProcessMatch> options.
+These may also be a block in which further options may be specified.
+
+The statistics collected for matched processes are:
  - size of the resident segment size (RSS)
  - user- and system-time used
  - number of processes
@@ -6767,21 +6828,49 @@ collected for these selected processes are:
  - context switches (under Linux)
  - minor and major pagefaults.
 
-Some platforms have a limit on the length of process names. I<Name> must stay
-below this limit.
+B<Synopsis:>
+
+ <Plugin processes>
+   CollectFileDescriptor true
+   CollectContextSwitch true
+   Process "name"
+   ProcessMatch "name" "regex"
+   <Process "collectd">
+     CollectFileDescriptor false
+     CollectContextSwitch false
+   </Process>
+   <ProcessMatch "name" "regex">
+     CollectFileDescriptor false
+     CollectContextSwitch true
+   </Process>
+ </Plugin>
+
+=over 4
+
+=item B<Process> I<Name>
+
+Select more detailed statistics of processes matching this name.
+
+Some platforms have a limit on the length of process names.
+I<Name> must stay below this limit.
 
 =item B<ProcessMatch> I<name> I<regex>
 
-Similar to the B<Process> option this allows one to select more detailed
-statistics of processes matching the specified I<regex> (see L<regex(7)> for
-details). The statistics of all matching processes are summed up and
-dispatched to the daemon using the specified I<name> as an identifier. This
-allows one to "group" several processes together. I<name> must not contain
-slashes.
+Select more detailed statistics of processes matching the specified I<regex>
+(see L<regex(7)> for details). The statistics of all matching processes are
+summed up and dispatched to the daemon using the specified I<name> as an
+identifier. This allows one to "group" several processes together.
+I<name> must not contain slashes.
 
 =item B<CollectContextSwitch> I<Boolean>
 
-Collect context switch of the process.
+Collect the number of context switches for matched processes.
+Disabled by default.
+
+=item B<CollectFileDescriptor> I<Boolean>
+
+Collect number of file descriptors of matched processes.
+Disabled by default.
 
 =item B<CollectMemoryMaps> I<Boolean>
 
@@ -6791,6 +6880,10 @@ the Linux kernel.
 
 =back
 
+Options B<CollectContextSwitch> and B<CollectFileDescriptor> may be used inside
+B<Process> and B<ProcessMatch> blocks - then they affect corresponding match
+only. Otherwise they set the default value for subsequent matches.
+
 =head2 Plugin C<protocols>
 
 Collects a lot of information about various network protocols, such as I<IP>,
@@ -8425,6 +8518,7 @@ Synopsis:
      CollectManagement  false
      CollectSMF         false
      CollectVBE         false
+     CollectMSE         false
    </Instance>
  </Plugin>
 
@@ -8500,11 +8594,9 @@ log messages which is flushed to disk when full. True by default.
 =item B<CollectSMA> B<true>|B<false>
 
 malloc or umem (umem_alloc(3MALLOC) based) storage statistics. The umem storage
-component is Solaris specific.
-Note: SMA and SMF share counters, enable only the one used by the Varnish
-instance.
-Only available with Varnish 2.x. False by
-default.
+component is Solaris specific. Note: SMA, SMF and MSE share counters, enable
+only the one used by the Varnish instance. Only available with Varnish 2.x.
+False by default.
 
 =item B<CollectSMS> B<true>|B<false>
 
@@ -8552,9 +8644,8 @@ Backend counters. Only available with Varnish 4.x. False by default.
 =item B<CollectSMF> B<true>|B<false>
 
 file (memory mapped file) storage statistics. Only available with Varnish 4.x.
-Note: SMA and SMF share counters, enable only the one used by the Varnish
-instance.
-Used to be called SM in Varnish 2.x. False by default.
+Note: SMA, SMF and MSE share counters, enable only the one used by the Varnish
+instance. Used to be called SM in Varnish 2.x. False by default.
 
 =item B<CollectManagement> B<true>|B<false>
 
@@ -8568,6 +8659,13 @@ Lock counters. Only available with Varnish 4.x. False by default.
 
 Memory pool counters. Only available with Varnish 4.x. False by default.
 
+=item B<CollectMSE> B<true>|B<false>
+
+Varnish Massive Storage Engine 2.0 (MSE2) is an improved storage backend for
+Varnish, replacing the traditional malloc and file storages. Only available
+with Varnish-Plus 4.x. Note: SMA, SMF and MSE share counters, enable only the
+one used by the Varnish instance. False by default.
+
 =back
 
 =head2 Plugin C<virt>
@@ -9212,6 +9310,12 @@ Sets the Cassandra ttl for the data points.
 
 Please refer to L<http://kairosdb.github.io/docs/build/html/restapi/AddDataPoints.html?highlight=ttl>
 
+=item B<Prefix> I<String>
+
+Only available for the KAIROSDB output format.
+
+Sets the metrics prefix I<string>. Defaults to I<collectd>.
+
 =item B<Metrics> B<true>|B<false>
 
 Controls whether I<metrics> are POSTed to this location. Defaults to B<true>.
index 4117040..04fad0c 100644 (file)
@@ -177,6 +177,7 @@ extern PyTypeObject ValuesType;
 
 typedef struct {
   PluginData data;
+  PyObject *meta;   /* dict */
   int severity;
   char message[NOTIF_MAX_MSG_LEN];
 } Notification;
index 756f24f..a2f287a 100644 (file)
@@ -226,11 +226,6 @@ static void cj_advance_array(cj_t *db) {
 #define CJ_CB_ABORT 0
 #define CJ_CB_CONTINUE 1
 
-static int cj_cb_boolean(void *ctx, int boolVal) {
-  cj_advance_array(ctx);
-  return CJ_CB_CONTINUE;
-}
-
 static int cj_cb_null(void *ctx) {
   cj_advance_array(ctx);
   return CJ_CB_CONTINUE;
@@ -292,6 +287,13 @@ static int cj_cb_string(void *ctx, const unsigned char *val, yajl_len_t len) {
   return cj_cb_number(ctx, (const char *)val, len);
 } /* int cj_cb_string */
 
+static int cj_cb_boolean(void *ctx, int boolVal) {
+  if (boolVal)
+    return cj_cb_number(ctx, "1", 1);
+  else
+    return cj_cb_number(ctx, "0", 1);
+} /* int cj_cb_boolean */
+
 static int cj_cb_end(void *ctx) {
   cj_t *db = (cj_t *)ctx;
   memset(&db->state[db->depth], 0, sizeof(db->state[db->depth]));
index 6c856a6..cf981dc 100644 (file)
@@ -212,7 +212,7 @@ void sfree (void **ptr)
 }
 #endif
 
-ssize_t sread(int fd, void *buf, size_t count) {
+int sread(int fd, void *buf, size_t count) {
   char *ptr;
   size_t nleft;
   ssize_t status;
@@ -230,10 +230,7 @@ ssize_t sread(int fd, void *buf, size_t count) {
       return status;
 
     if (status == 0) {
-      DEBUG("Received EOF from fd %i. "
-            "Closing fd and returning error.",
-            fd);
-      close(fd);
+      DEBUG("Received EOF from fd %i. ", fd);
       return -1;
     }
 
@@ -246,7 +243,7 @@ ssize_t sread(int fd, void *buf, size_t count) {
   return 0;
 }
 
-ssize_t swrite(int fd, const void *buf, size_t count) {
+int swrite(int fd, const void *buf, size_t count) {
   const char *ptr;
   size_t nleft;
   ssize_t status;
index afd292a..7f86052 100644 (file)
@@ -79,8 +79,7 @@ char *sstrerror(int errnum, char *buf, size_t buflen);
  *
  * DESCRIPTION
  *   Reads exactly `n' bytes or fails. Syntax and other behavior is analogous
- *   to `read(2)'. If EOF is received the file descriptor is closed and an
- *   error is returned.
+ *   to `read(2)'.
  *
  * PARAMETERS
  *   `fd'          File descriptor to write to.
@@ -91,7 +90,7 @@ char *sstrerror(int errnum, char *buf, size_t buflen);
  *   Zero upon success or non-zero if an error occurred. `errno' is set in this
  *   case.
  */
-ssize_t sread(int fd, void *buf, size_t count);
+int sread(int fd, void *buf, size_t count);
 
 /*
  * NAME
@@ -110,7 +109,7 @@ ssize_t sread(int fd, void *buf, size_t count);
  *   Zero upon success or non-zero if an error occurred. `errno' is set in this
  *   case.
  */
-ssize_t swrite(int fd, const void *buf, size_t count);
+int swrite(int fd, const void *buf, size_t count);
 
 /*
  * NAME
index 5b812b8..7842aa6 100644 (file)
@@ -34,6 +34,7 @@
 
 #define FC_RECURSIVE 1
 #define FC_HIDDEN 2
+#define FC_REGULAR 4
 
 struct fc_directory_conf_s {
   char *path;
@@ -340,7 +341,7 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
     return -1;
   }
 
-  dir->options = FC_RECURSIVE;
+  dir->options = FC_RECURSIVE | FC_REGULAR;
 
   dir->name = NULL;
   dir->plugin_name = strdup("filecount");
@@ -377,6 +378,8 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
       status = fc_config_add_dir_option(dir, option, FC_RECURSIVE);
     else if (strcasecmp("IncludeHidden", option->key) == 0)
       status = fc_config_add_dir_option(dir, option, FC_HIDDEN);
+    else if (strcasecmp("RegularOnly", option->key) == 0)
+      status = fc_config_add_dir_option(dir, option, FC_REGULAR);
     else if (strcasecmp("FilesSizeType", option->key) == 0)
       status = cf_util_get_string(option, &dir->files_size_type);
     else if (strcasecmp("FilesCountType", option->key) == 0)
@@ -488,7 +491,7 @@ static int fc_read_dir_callback(const char *dirname, const char *filename,
         abs_path, fc_read_dir_callback, dir,
         /* include hidden = */ (dir->options & FC_HIDDEN) ? 1 : 0);
     return status;
-  } else if (!S_ISREG(statbuf.st_mode)) {
+  } else if ((dir->options & FC_REGULAR) && !S_ISREG(statbuf.st_mode)) {
     return 0;
   }
 
@@ -498,6 +501,11 @@ static int fc_read_dir_callback(const char *dirname, const char *filename,
       return 0;
   }
 
+  if (!S_ISREG(statbuf.st_mode)) {
+    dir->files_num++;
+    return 0;
+  }
+
   if (dir->mtime != 0) {
     time_t mtime = dir->now;
 
index e5f19ce..fd2bd6f 100644 (file)
@@ -67,7 +67,7 @@ struct intel_pmu_ctx_s {
   _Bool hw_cache_events;
   _Bool kernel_pmu_events;
   _Bool sw_events;
-  char  event_list_fn[PATH_MAX];
+  char event_list_fn[PATH_MAX];
   char **hw_events;
   size_t hw_events_count;
   struct eventlist *event_list;
@@ -265,7 +265,8 @@ static int pmu_config(oconfig_item_t *ci) {
   return 0;
 }
 
-static void pmu_submit_counter(int cpu, char *event, counter_t value) {
+static void pmu_submit_counter(int cpu, char *event, counter_t value,
+                               meta_data_t *meta) {
   value_list_t vl = VALUE_LIST_INIT;
 
   vl.values = &(value_t){.counter = value};
@@ -275,6 +276,7 @@ static void pmu_submit_counter(int cpu, char *event, counter_t value) {
   if (cpu == -1) {
     snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
   } else {
+    vl.meta = meta;
     snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
   }
   sstrncpy(vl.type, "counter", sizeof(vl.type));
@@ -283,6 +285,27 @@ static void pmu_submit_counter(int cpu, char *event, counter_t value) {
   plugin_dispatch_values(&vl);
 }
 
+meta_data_t *pmu_meta_data_create(const struct efd *efd) {
+  meta_data_t *meta = NULL;
+
+  /* create meta data only if value was scaled */
+  if (efd->val[1] == efd->val[2] || !efd->val[2]) {
+    return NULL;
+  }
+
+  meta = meta_data_create();
+  if (meta == NULL) {
+    ERROR(PMU_PLUGIN ": meta_data_create failed.");
+    return NULL;
+  }
+
+  meta_data_add_unsigned_int(meta, "intel_pmu:raw_count", efd->val[0]);
+  meta_data_add_unsigned_int(meta, "intel_pmu:time_enabled", efd->val[1]);
+  meta_data_add_unsigned_int(meta, "intel_pmu:time_running", efd->val[2]);
+
+  return meta;
+}
+
 static void pmu_dispatch_data(void) {
 
   struct event *e;
@@ -297,17 +320,27 @@ static void pmu_dispatch_data(void) {
 
       event_enabled++;
 
+      /* If there are more events than counters, the kernel uses time
+       * multiplexing. With multiplexing, at the end of the run,
+       * the counter is scaled basing on total time enabled vs time running.
+       * final_count = raw_count * time_enabled/time_running
+       */
       uint64_t value = event_scaled_value(e, i);
       all_value += value;
 
+      /* get meta data with information about scaling */
+      meta_data_t *meta = pmu_meta_data_create(&e->efd[i]);
+
       /* dispatch per CPU value */
-      pmu_submit_counter(i, e->event, value);
+      pmu_submit_counter(i, e->event, value, meta);
+
+      meta_data_destroy(meta);
     }
 
     if (event_enabled > 0) {
       DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
       /* dispatch all CPU value */
-      pmu_submit_counter(-1, e->event, all_value);
+      pmu_submit_counter(-1, e->event, all_value, NULL);
     }
   }
 }
@@ -539,7 +572,6 @@ init_error:
   sfree(g_ctx.hw_events);
   g_ctx.hw_events_count = 0;
 
-
   return ret;
 }
 
index c8c8042..a206521 100644 (file)
@@ -21,6 +21,7 @@
  *   Florian octo Forster <octo at collectd.org>
  *   Peter Holik <peter at holik.at>
  *   Bruno Prémont <bonbons at linux-vserver.org>
+ *   Pavel Rochnyak <pavel2000 ngs.ru>
  **/
 
 #include "collectd.h"
 #include "plugin.h"
 #include "utils_ignorelist.h"
 
+#include <OpenIPMI/ipmi_auth.h>
 #include <OpenIPMI/ipmi_conn.h>
 #include <OpenIPMI/ipmi_err.h>
+#include <OpenIPMI/ipmi_lan.h>
 #include <OpenIPMI/ipmi_posix.h>
 #include <OpenIPMI/ipmi_smi.h>
 #include <OpenIPMI/ipmiif.h>
 
+#define ERR_BUF_SIZE 1024
+
 /*
  * Private data types
  */
 struct c_ipmi_sensor_list_s;
 typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
 
+struct c_ipmi_instance_s {
+  char *name;
+  ignorelist_t *ignorelist;
+  _Bool notify_add;
+  _Bool notify_remove;
+  _Bool notify_notpresent;
+  _Bool notify_conn;
+  _Bool sel_enabled;
+  _Bool sel_clear_event;
+
+  char *host;
+  char *connaddr;
+  char *username;
+  char *password;
+  unsigned int authtype;
+
+  _Bool connected;
+  ipmi_con_t *connection;
+  pthread_mutex_t sensor_list_lock;
+  c_ipmi_sensor_list_t *sensor_list;
+
+  _Bool active;
+  pthread_t thread_id;
+  int init_in_progress;
+
+  struct c_ipmi_instance_s *next;
+};
+typedef struct c_ipmi_instance_s c_ipmi_instance_t;
+
 struct c_ipmi_sensor_list_s {
   ipmi_sensor_id_t sensor_id;
   char sensor_name[DATA_MAX_NAME_LEN];
   char sensor_type[DATA_MAX_NAME_LEN];
   int sensor_not_present;
   c_ipmi_sensor_list_t *next;
+  c_ipmi_instance_t *instance;
+  unsigned int use;
 };
 
 /*
  * Module global variables
  */
-static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static c_ipmi_sensor_list_t *sensor_list = NULL;
-
-static int c_ipmi_init_in_progress = 0;
-static int c_ipmi_active = 0;
-static pthread_t thread_id = (pthread_t)0;
-
-static const char *config_keys[] = {"Sensor", "IgnoreSelected",
-                                    "NotifySensorAdd", "NotifySensorRemove",
-                                    "NotifySensorNotPresent"};
-static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
-
-static ignorelist_t *ignorelist = NULL;
-
-static int c_ipmi_nofiy_add = 0;
-static int c_ipmi_nofiy_remove = 0;
-static int c_ipmi_nofiy_notpresent = 0;
+static os_handler_t *os_handler = NULL;
+static c_ipmi_instance_t *instances = NULL;
 
 /*
  * Misc private functions
  */
-static void c_ipmi_error(const char *func, int status) {
-  char errbuf[4096] = {0};
+static void c_ipmi_error(c_ipmi_instance_t *st, const char *func, int status) {
+  char errbuf[ERR_BUF_SIZE] = {0};
 
-  if (IPMI_IS_OS_ERR(status)) {
-    sstrerror(IPMI_GET_OS_ERR(status), errbuf, sizeof(errbuf));
-  } else if (IPMI_IS_IPMI_ERR(status)) {
-    ipmi_get_error_string(IPMI_GET_IPMI_ERR(status), errbuf, sizeof(errbuf));
+  if (IPMI_IS_OS_ERR(status) || IPMI_IS_RMCPP_ERR(status) ||
+      IPMI_IS_IPMI_ERR(status)) {
+    ipmi_get_error_string(status, errbuf, sizeof(errbuf));
   }
 
   if (errbuf[0] == 0) {
@@ -87,39 +107,89 @@ static void c_ipmi_error(const char *func, int status) {
   }
   errbuf[sizeof(errbuf) - 1] = 0;
 
-  ERROR("ipmi plugin: %s failed: %s", func, errbuf);
+  ERROR("ipmi plugin: %s failed for `%s`: %s", func, st->name, errbuf);
 } /* void c_ipmi_error */
 
+static void c_ipmi_log(os_handler_t *handler, const char *format,
+                       enum ipmi_log_type_e log_type, va_list ap) {
+  char msg[ERR_BUF_SIZE];
+
+  vsnprintf(msg, sizeof(msg), format, ap);
+
+  switch (log_type) {
+  case IPMI_LOG_INFO:
+    INFO("ipmi plugin: %s", msg);
+    break;
+  case IPMI_LOG_WARNING:
+    NOTICE("ipmi plugin: %s", msg);
+    break;
+  case IPMI_LOG_SEVERE:
+    WARNING("ipmi plugin: %s", msg);
+    break;
+  case IPMI_LOG_FATAL:
+    ERROR("ipmi plugin: %s", msg);
+    break;
+  case IPMI_LOG_ERR_INFO:
+    ERROR("ipmi plugin: %s", msg);
+    break;
+#if COLLECT_DEBUG
+  case IPMI_LOG_DEBUG_START:
+  case IPMI_LOG_DEBUG:
+    DEBUG("ipmi plugin: %s", msg);
+    break;
+  case IPMI_LOG_DEBUG_CONT:
+  case IPMI_LOG_DEBUG_END:
+    DEBUG("%s", msg);
+    break;
+#else
+  case IPMI_LOG_DEBUG_START:
+  case IPMI_LOG_DEBUG:
+  case IPMI_LOG_DEBUG_CONT:
+  case IPMI_LOG_DEBUG_END:
+    break;
+#endif
+  }
+} /* void c_ipmi_log */
+
+static notification_t c_ipmi_notification_init(c_ipmi_instance_t const *st,
+                                               int severity) {
+  notification_t n = {severity, cdtime(), "", "", "ipmi", "", "", "", NULL};
+
+  sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g, sizeof(n.host));
+  return n;
+} /* notification_t c_ipmi_notification_init */
+
 /*
  * Sensor handlers
  */
 /* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
-static int sensor_list_remove(ipmi_sensor_t *sensor);
+static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor);
 
 static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
                                 enum ipmi_value_present_e value_present,
                                 unsigned int __attribute__((unused)) raw_value,
-                                double value,
-                                ipmi_states_t __attribute__((unused)) * states,
+                                double value, ipmi_states_t *states,
                                 void *user_data) {
   value_list_t vl = VALUE_LIST_INIT;
 
-  c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
+  c_ipmi_sensor_list_t *list_item = user_data;
+  c_ipmi_instance_t *st = list_item->instance;
+
+  list_item->use--;
 
   if (err != 0) {
-    if ((err & 0xff) == IPMI_NOT_PRESENT_CC) {
+    if (IPMI_IS_IPMI_ERR(err) &&
+        IPMI_GET_IPMI_ERR(err) == IPMI_NOT_PRESENT_CC) {
       if (list_item->sensor_not_present == 0) {
         list_item->sensor_not_present = 1;
 
-        INFO("ipmi plugin: sensor_read_handler: sensor %s "
+        INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` "
              "not present.",
-             list_item->sensor_name);
+             list_item->sensor_name, st->name);
 
-        if (c_ipmi_nofiy_notpresent) {
-          notification_t n = {
-              NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL};
+        if (st->notify_notpresent) {
+          notification_t n = c_ipmi_notification_init(st, NOTIF_WARNING);
 
-          sstrncpy(n.host, hostname_g, sizeof(n.host));
           sstrncpy(n.type_instance, list_item->sensor_name,
                    sizeof(n.type_instance));
           sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
@@ -132,43 +202,47 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
     } else if (IPMI_IS_IPMI_ERR(err) &&
                IPMI_GET_IPMI_ERR(err) ==
                    IPMI_NOT_SUPPORTED_IN_PRESENT_STATE_CC) {
-      INFO("ipmi plugin: sensor_read_handler: Sensor %s not ready",
-           list_item->sensor_name);
+      INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` not ready.",
+           list_item->sensor_name, st->name);
+    } else if (IPMI_IS_IPMI_ERR(err) &&
+               IPMI_GET_IPMI_ERR(err) == IPMI_TIMEOUT_CC) {
+      INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` timed out.",
+           list_item->sensor_name, st->name);
     } else {
+      char errbuf[ERR_BUF_SIZE] = {0};
+      ipmi_get_error_string(err, errbuf, sizeof(errbuf) - 1);
+
       if (IPMI_IS_IPMI_ERR(err))
-        INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
-             "because it failed with IPMI error %#x.",
-             list_item->sensor_name, IPMI_GET_IPMI_ERR(err));
+        INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+             "%s.",
+             list_item->sensor_name, st->name, errbuf);
       else if (IPMI_IS_OS_ERR(err))
-        INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
-             "because it failed with OS error %#x.",
-             list_item->sensor_name, IPMI_GET_OS_ERR(err));
+        INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+             "%s (%#x).",
+             list_item->sensor_name, st->name, errbuf, IPMI_GET_OS_ERR(err));
       else if (IPMI_IS_RMCPP_ERR(err))
-        INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
-             "because it failed with RMCPP error %#x.",
-             list_item->sensor_name, IPMI_GET_RMCPP_ERR(err));
+        INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+             "%s.",
+             list_item->sensor_name, st->name, errbuf);
       else if (IPMI_IS_SOL_ERR(err))
-        INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
-             "because it failed with RMCPP error %#x.",
-             list_item->sensor_name, IPMI_GET_SOL_ERR(err));
+        INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+             "%s (%#x).",
+             list_item->sensor_name, st->name, errbuf, IPMI_GET_SOL_ERR(err));
       else
-        INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
-             "because it failed with error %#x. of class %#x",
-             list_item->sensor_name, err & 0xff, err & 0xffffff00);
-      sensor_list_remove(sensor);
+        INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed "
+             "with error %#x. of class %#x",
+             list_item->sensor_name, st->name, err & 0xff, err & 0xffffff00);
     }
     return;
   } else if (list_item->sensor_not_present == 1) {
     list_item->sensor_not_present = 0;
 
-    INFO("ipmi plugin: sensor_read_handler: sensor %s present.",
-         list_item->sensor_name);
+    INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` present.",
+         list_item->sensor_name, st->name);
 
-    if (c_ipmi_nofiy_notpresent) {
-      notification_t n = {NOTIF_OKAY, cdtime(), "", "",  "ipmi",
-                          "",         "",       "", NULL};
+    if (st->notify_notpresent) {
+      notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
 
-      sstrncpy(n.host, hostname_g, sizeof(n.host));
       sstrncpy(n.type_instance, list_item->sensor_name,
                sizeof(n.type_instance));
       sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
@@ -180,19 +254,35 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
   }
 
   if (value_present != IPMI_BOTH_VALUES_PRESENT) {
-    INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
+    INFO("ipmi plugin: sensor_read_handler: Removing sensor `%s` of `%s`, "
          "because it provides %s. If you need this sensor, "
          "please file a bug report.",
-         list_item->sensor_name,
+         list_item->sensor_name, st->name,
          (value_present == IPMI_RAW_VALUE_PRESENT) ? "only the raw value"
                                                    : "no value");
-    sensor_list_remove(sensor);
+    sensor_list_remove(st, sensor);
+    return;
+  }
+
+  if (!ipmi_is_sensor_scanning_enabled(states)) {
+    DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
+          "it is in 'scanning disabled' state.",
+          list_item->sensor_name, st->name);
+    return;
+  }
+
+  if (ipmi_is_initial_update_in_progress(states)) {
+    DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
+          "it is in 'initial update in progress' state.",
+          list_item->sensor_name, st->name);
     return;
   }
 
   vl.values = &(value_t){.gauge = value};
   vl.values_len = 1;
 
+  if (st->host != NULL)
+    sstrncpy(vl.host, st->host, sizeof(vl.host));
   sstrncpy(vl.plugin, "ipmi", sizeof(vl.plugin));
   sstrncpy(vl.type, list_item->sensor_type, sizeof(vl.type));
   sstrncpy(vl.type_instance, list_item->sensor_name, sizeof(vl.type_instance));
@@ -200,64 +290,112 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
   plugin_dispatch_values(&vl);
 } /* void sensor_read_handler */
 
-static int sensor_list_add(ipmi_sensor_t *sensor) {
+static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) {
+  char temp[DATA_MAX_NAME_LEN] = {0};
+  ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
+  const char *entity_id_string = ipmi_entity_get_entity_id_string(ent);
+  char sensor_name[DATA_MAX_NAME_LEN] = "";
+  char *sensor_name_ptr;
+
+  if ((buffer == NULL) || (buf_len == 0))
+    return;
+
+  ipmi_sensor_get_name(sensor, temp, sizeof(temp));
+  temp[sizeof(temp) - 1] = 0;
+
+  if (entity_id_string != NULL && strlen(temp))
+    snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string);
+  else if (entity_id_string != NULL)
+    sstrncpy(sensor_name, entity_id_string, sizeof(sensor_name));
+  else
+    sstrncpy(sensor_name, temp, sizeof(sensor_name));
+
+  if (strlen(temp)) {
+    sstrncpy(temp, sensor_name, sizeof(temp));
+    sensor_name_ptr = strstr(temp, ").");
+    if (sensor_name_ptr != NULL) {
+      /* If name is something like "foo (123).bar",
+       * change that to "bar (123)".
+       * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
+       * `temp' array, which holds a copy of the current `sensor_name'. */
+      char *sensor_id_ptr;
+
+      /* `sensor_name_ptr' points to ").bar". */
+      sensor_name_ptr[1] = 0;
+      /* `temp' holds "foo (123)\0bar\0". */
+      sensor_name_ptr += 2;
+      /* `sensor_name_ptr' now points to "bar". */
+
+      sensor_id_ptr = strstr(temp, "(");
+      if (sensor_id_ptr != NULL) {
+        /* `sensor_id_ptr' now points to "(123)". */
+        snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
+                 sensor_id_ptr);
+      }
+      /* else: don't touch sensor_name. */
+    }
+  }
+  sstrncpy(buffer, sensor_name, buf_len);
+}
+
+static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
   ipmi_sensor_id_t sensor_id;
   c_ipmi_sensor_list_t *list_item;
   c_ipmi_sensor_list_t *list_prev;
 
   char buffer[DATA_MAX_NAME_LEN] = {0};
-  const char *entity_id_string;
-  char sensor_name[DATA_MAX_NAME_LEN];
-  char *sensor_name_ptr;
+  char *sensor_name_ptr = buffer;
   int sensor_type;
   const char *type;
-  ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
 
   sensor_id = ipmi_sensor_convert_to_id(sensor);
+  sensor_get_name(sensor, buffer, sizeof(buffer));
+
+  DEBUG("ipmi plugin: sensor_list_add: Found sensor `%s` of `%s`,"
+        " Type: %#x"
+        " Event reading type: %#x"
+        " Direction: %#x"
+        " Event support: %#x",
+        sensor_name_ptr, st->name, ipmi_sensor_get_sensor_type(sensor),
+        ipmi_sensor_get_event_reading_type(sensor),
+        ipmi_sensor_get_sensor_direction(sensor),
+        ipmi_sensor_get_event_support(sensor));
+
+  /* Both `ignorelist' and `sensor_name_ptr' may be NULL. */
+  if (ignorelist_match(st->ignorelist, sensor_name_ptr) != 0)
+    return 0;
 
-  ipmi_sensor_get_name(sensor, buffer, sizeof(buffer));
-  buffer[sizeof(buffer) - 1] = 0;
+  /* FIXME: Use rate unit or base unit to scale the value */
 
-  entity_id_string = ipmi_entity_get_entity_id_string(ent);
+  sensor_type = ipmi_sensor_get_sensor_type(sensor);
 
-  if (entity_id_string == NULL)
-    sstrncpy(sensor_name, buffer, sizeof(sensor_name));
-  else
-    snprintf(sensor_name, sizeof(sensor_name), "%s %s", buffer,
-             entity_id_string);
-
-  sstrncpy(buffer, sensor_name, sizeof(buffer));
-  sensor_name_ptr = strstr(buffer, ").");
-  if (sensor_name_ptr != NULL) {
-    /* If name is something like "foo (123).bar",
-     * change that to "bar (123)".
-     * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
-     * `buffer' array, which holds a copy of the current `sensor_name'. */
-    char *sensor_id_ptr;
-
-    /* `sensor_name_ptr' points to ").bar". */
-    sensor_name_ptr[1] = 0;
-    /* `buffer' holds "foo (123)\0bar\0". */
-    sensor_name_ptr += 2;
-    /* `sensor_name_ptr' now points to "bar". */
-
-    sensor_id_ptr = strstr(buffer, "(");
-    if (sensor_id_ptr != NULL) {
-      /* `sensor_id_ptr' now points to "(123)". */
-      snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
-               sensor_id_ptr);
-    }
-    /* else: don't touch sensor_name. */
+  /*
+   * ipmitool/lib/ipmi_sdr.c sdr_sensor_has_analog_reading() has a notice
+   * about 'Threshold sensors' and 'analog readings'. Discrete sensor may
+   * have analog data, but discrete sensors support is not implemented
+   * in Collectd yet.
+   *
+   * ipmi_sensor_id_get_reading() supports only 'Threshold' sensors.
+   * See lib/sensor.c:4842, stand_ipmi_sensor_get_reading() for details.
+  */
+  if (!ipmi_sensor_get_is_readable(sensor)) {
+    INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+         "because it don't readable! Its type: (%#x, %s). ",
+         sensor_name_ptr, st->name, sensor_type,
+         ipmi_sensor_get_sensor_type_string(sensor));
+    return -1;
   }
-  sensor_name_ptr = sensor_name;
 
-  /* Both `ignorelist' and `plugin_instance' may be NULL. */
-  if (ignorelist_match(ignorelist, sensor_name_ptr) != 0)
-    return 0;
-
-  /* FIXME: Use rate unit or base unit to scale the value */
+  if (ipmi_sensor_get_event_reading_type(sensor) !=
+      IPMI_EVENT_READING_TYPE_THRESHOLD) {
+    INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+         "because it is discrete (%#x)! Its type: (%#x, %s). ",
+         sensor_name_ptr, st->name, sensor_type,
+         ipmi_sensor_get_event_reading_type(sensor),
+         ipmi_sensor_get_sensor_type_string(sensor));
+    return -1;
+  }
 
-  sensor_type = ipmi_sensor_get_sensor_type(sensor);
   switch (sensor_type) {
   case IPMI_SENSOR_TYPE_TEMPERATURE:
     type = "temperature";
@@ -276,21 +414,19 @@ static int sensor_list_add(ipmi_sensor_t *sensor) {
     break;
 
   default: {
-    const char *sensor_type_str;
-
-    sensor_type_str = ipmi_sensor_get_sensor_type_string(sensor);
-    INFO("ipmi plugin: sensor_list_add: Ignore sensor %s, "
+    INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
          "because I don't know how to handle its type (%#x, %s). "
          "If you need this sensor, please file a bug report.",
-         sensor_name_ptr, sensor_type, sensor_type_str);
+         sensor_name_ptr, st->name, sensor_type,
+         ipmi_sensor_get_sensor_type_string(sensor));
     return -1;
   }
   } /* switch (sensor_type) */
 
-  pthread_mutex_lock(&sensor_list_lock);
+  pthread_mutex_lock(&st->sensor_list_lock);
 
   list_prev = NULL;
-  for (list_item = sensor_list; list_item != NULL;
+  for (list_item = st->sensor_list; list_item != NULL;
        list_item = list_item->next) {
     if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
       break;
@@ -298,33 +434,33 @@ static int sensor_list_add(ipmi_sensor_t *sensor) {
   } /* for (list_item) */
 
   if (list_item != NULL) {
-    pthread_mutex_unlock(&sensor_list_lock);
+    pthread_mutex_unlock(&st->sensor_list_lock);
     return 0;
   }
 
   list_item = (c_ipmi_sensor_list_t *)calloc(1, sizeof(c_ipmi_sensor_list_t));
   if (list_item == NULL) {
-    pthread_mutex_unlock(&sensor_list_lock);
+    pthread_mutex_unlock(&st->sensor_list_lock);
     return -1;
   }
 
+  list_item->instance = st;
   list_item->sensor_id = ipmi_sensor_convert_to_id(sensor);
 
   if (list_prev != NULL)
     list_prev->next = list_item;
   else
-    sensor_list = list_item;
+    st->sensor_list = list_item;
 
   sstrncpy(list_item->sensor_name, sensor_name_ptr,
            sizeof(list_item->sensor_name));
   sstrncpy(list_item->sensor_type, type, sizeof(list_item->sensor_type));
 
-  pthread_mutex_unlock(&sensor_list_lock);
+  pthread_mutex_unlock(&st->sensor_list_lock);
 
-  if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0)) {
-    notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
+  if (st->notify_add && (st->init_in_progress == 0)) {
+    notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
 
-    sstrncpy(n.host, hostname_g, sizeof(n.host));
     sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
     sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
     snprintf(n.message, sizeof(n.message), "sensor %s added",
@@ -336,17 +472,17 @@ static int sensor_list_add(ipmi_sensor_t *sensor) {
   return 0;
 } /* int sensor_list_add */
 
-static int sensor_list_remove(ipmi_sensor_t *sensor) {
+static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
   ipmi_sensor_id_t sensor_id;
   c_ipmi_sensor_list_t *list_item;
   c_ipmi_sensor_list_t *list_prev;
 
   sensor_id = ipmi_sensor_convert_to_id(sensor);
 
-  pthread_mutex_lock(&sensor_list_lock);
+  pthread_mutex_lock(&st->sensor_list_lock);
 
   list_prev = NULL;
-  for (list_item = sensor_list; list_item != NULL;
+  for (list_item = st->sensor_list; list_item != NULL;
        list_item = list_item->next) {
     if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
       break;
@@ -354,25 +490,23 @@ static int sensor_list_remove(ipmi_sensor_t *sensor) {
   } /* for (list_item) */
 
   if (list_item == NULL) {
-    pthread_mutex_unlock(&sensor_list_lock);
+    pthread_mutex_unlock(&st->sensor_list_lock);
     return -1;
   }
 
   if (list_prev == NULL)
-    sensor_list = list_item->next;
+    st->sensor_list = list_item->next;
   else
     list_prev->next = list_item->next;
 
   list_prev = NULL;
   list_item->next = NULL;
 
-  pthread_mutex_unlock(&sensor_list_lock);
+  pthread_mutex_unlock(&st->sensor_list_lock);
 
-  if (c_ipmi_nofiy_remove && c_ipmi_active) {
-    notification_t n = {NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "",
-                        NULL};
+  if (st->notify_remove && st->active) {
+    notification_t n = c_ipmi_notification_init(st, NOTIF_WARNING);
 
-    sstrncpy(n.host, hostname_g, sizeof(n.host));
     sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
     sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
     snprintf(n.message, sizeof(n.message), "sensor %s removed",
@@ -385,29 +519,37 @@ static int sensor_list_remove(ipmi_sensor_t *sensor) {
   return 0;
 } /* int sensor_list_remove */
 
-static int sensor_list_read_all(void) {
-  pthread_mutex_lock(&sensor_list_lock);
+static int sensor_list_read_all(c_ipmi_instance_t *st) {
+  pthread_mutex_lock(&st->sensor_list_lock);
 
-  for (c_ipmi_sensor_list_t *list_item = sensor_list; list_item != NULL;
+  for (c_ipmi_sensor_list_t *list_item = st->sensor_list; list_item != NULL;
        list_item = list_item->next) {
+    DEBUG("ipmi plugin: try read sensor `%s` of `%s`, use: %d",
+          list_item->sensor_name, st->name, list_item->use);
+
+    /* Reading already initiated */
+    if (list_item->use)
+      continue;
+
+    list_item->use++;
     ipmi_sensor_id_get_reading(list_item->sensor_id, sensor_read_handler,
-                               /* user data = */ list_item);
+                               /* user data = */ (void *)list_item);
   } /* for (list_item) */
 
-  pthread_mutex_unlock(&sensor_list_lock);
+  pthread_mutex_unlock(&st->sensor_list_lock);
 
   return 0;
 } /* int sensor_list_read_all */
 
-static int sensor_list_remove_all(void) {
+static int sensor_list_remove_all(c_ipmi_instance_t *st) {
   c_ipmi_sensor_list_t *list_item;
 
-  pthread_mutex_lock(&sensor_list_lock);
+  pthread_mutex_lock(&st->sensor_list_lock);
 
-  list_item = sensor_list;
-  sensor_list = NULL;
+  list_item = st->sensor_list;
+  st->sensor_list = NULL;
 
-  pthread_mutex_unlock(&sensor_list_lock);
+  pthread_mutex_unlock(&st->sensor_list_lock);
 
   while (list_item != NULL) {
     c_ipmi_sensor_list_t *list_next = list_item->next;
@@ -420,204 +562,679 @@ static int sensor_list_remove_all(void) {
   return 0;
 } /* int sensor_list_remove_all */
 
+static int sensor_convert_threshold_severity(enum ipmi_thresh_e severity) {
+  switch (severity) {
+  case IPMI_LOWER_NON_CRITICAL:
+  case IPMI_UPPER_NON_CRITICAL:
+    return NOTIF_OKAY;
+  case IPMI_LOWER_CRITICAL:
+  case IPMI_UPPER_CRITICAL:
+    return NOTIF_WARNING;
+  case IPMI_LOWER_NON_RECOVERABLE:
+  case IPMI_UPPER_NON_RECOVERABLE:
+    return NOTIF_FAILURE;
+  default:
+    return NOTIF_OKAY;
+  } /* switch (severity) */
+} /* int sensor_convert_threshold_severity */
+
+static void add_event_common_data(notification_t *n, ipmi_sensor_t *sensor,
+                                  enum ipmi_event_dir_e dir,
+                                  ipmi_event_t *event) {
+  ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
+
+  plugin_notification_meta_add_string(n, "entity_name",
+                                      ipmi_entity_get_entity_id_string(ent));
+  plugin_notification_meta_add_signed_int(n, "entity_id",
+                                          ipmi_entity_get_entity_id(ent));
+  plugin_notification_meta_add_signed_int(n, "entity_instance",
+                                          ipmi_entity_get_entity_instance(ent));
+  plugin_notification_meta_add_boolean(n, "assert", dir == IPMI_ASSERTION);
+
+  if (event)
+    plugin_notification_meta_add_signed_int(n, "event_type",
+                                            ipmi_event_get_type(event));
+} /* void add_event_sensor_meta_data */
+
+static int sensor_threshold_event_handler(
+    ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir,
+    enum ipmi_thresh_e threshold, enum ipmi_event_value_dir_e high_low,
+    enum ipmi_value_present_e value_present, unsigned int raw_value,
+    double value, void *cb_data, ipmi_event_t *event) {
+
+  c_ipmi_instance_t *st = cb_data;
+
+  /* From the IPMI specification Chapter 2: Events.
+   * If a callback handles the event, then all future callbacks called due to
+   * the event will receive a NULL for the event. So be ready to handle a NULL
+   * event in all your event handlers. A NULL may also be passed to an event
+   * handler if the callback was not due to an event. */
+  if (event == NULL)
+    return IPMI_EVENT_NOT_HANDLED;
+
+  notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
+  /* offset is a table index and it's represented as enum of strings that are
+     organized in the way - high and low for each threshold severity level */
+  unsigned int offset = (2 * threshold) + high_low;
+  unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
+  unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
+  const char *event_state =
+      ipmi_get_reading_name(event_type, sensor_type, offset);
+  sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
+  if (value_present != IPMI_NO_VALUES_PRESENT)
+    snprintf(n.message, sizeof(n.message),
+             "sensor %s received event: %s, value is %f", n.type_instance,
+             event_state, value);
+  else
+    snprintf(n.message, sizeof(n.message),
+             "sensor %s received event: %s, value not provided",
+             n.type_instance, event_state);
+
+  DEBUG("Threshold event received for sensor %s", n.type_instance);
+
+  sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
+  n.severity = sensor_convert_threshold_severity(threshold);
+  n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
+
+  plugin_notification_meta_add_string(&n, "severity",
+                                      ipmi_get_threshold_string(threshold));
+  plugin_notification_meta_add_string(&n, "direction",
+                                      ipmi_get_value_dir_string(high_low));
+
+  switch (value_present) {
+  case IPMI_BOTH_VALUES_PRESENT:
+    plugin_notification_meta_add_double(&n, "val", value);
+  /* both values present, so fall-through to add raw value too */
+  case IPMI_RAW_VALUE_PRESENT: {
+    char buf[DATA_MAX_NAME_LEN] = {0};
+    snprintf(buf, sizeof(buf), "0x%2.2x", raw_value);
+    plugin_notification_meta_add_string(&n, "raw", buf);
+  } break;
+  default:
+    break;
+  } /* switch (value_present) */
+
+  add_event_common_data(&n, sensor, dir, event);
+
+  plugin_dispatch_notification(&n);
+  plugin_notification_meta_free(n.meta);
+
+  /* Delete handled ipmi event from the list */
+  if (st->sel_clear_event) {
+    ipmi_event_delete(event, NULL, NULL);
+    return IPMI_EVENT_HANDLED;
+  }
+
+  return IPMI_EVENT_NOT_HANDLED;
+} /* int sensor_threshold_event_handler */
+
+static int sensor_discrete_event_handler(ipmi_sensor_t *sensor,
+                                         enum ipmi_event_dir_e dir, int offset,
+                                         int severity, int prev_severity,
+                                         void *cb_data, ipmi_event_t *event) {
+
+  c_ipmi_instance_t *st = cb_data;
+
+  /* From the IPMI specification Chapter 2: Events.
+   * If a callback handles the event, then all future callbacks called due to
+   * the event will receive a NULL for the event. So be ready to handle a NULL
+   * event in all your event handlers. A NULL may also be passed to an event
+   * handler if the callback was not due to an event. */
+  if (event == NULL)
+    return IPMI_EVENT_NOT_HANDLED;
+
+  notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
+  unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
+  unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
+  const char *event_state =
+      ipmi_get_reading_name(event_type, sensor_type, offset);
+  sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
+  snprintf(n.message, sizeof(n.message), "sensor %s received event: %s",
+           n.type_instance, event_state);
+
+  DEBUG("Discrete event received for sensor %s", n.type_instance);
+
+  sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
+  n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
+
+  plugin_notification_meta_add_signed_int(&n, "offset", offset);
+
+  if (severity != -1)
+    plugin_notification_meta_add_signed_int(&n, "severity", severity);
+
+  if (prev_severity != -1)
+    plugin_notification_meta_add_signed_int(&n, "prevseverity", prev_severity);
+
+  add_event_common_data(&n, sensor, dir, event);
+
+  plugin_dispatch_notification(&n);
+  plugin_notification_meta_free(n.meta);
+
+  /* Delete handled ipmi event from the list */
+  if (st->sel_clear_event) {
+    ipmi_event_delete(event, NULL, NULL);
+    return IPMI_EVENT_HANDLED;
+  }
+
+  return IPMI_EVENT_NOT_HANDLED;
+} /* int sensor_discrete_event_handler */
+
 /*
  * Entity handlers
  */
-static void entity_sensor_update_handler(
-    enum ipmi_update_e op, ipmi_entity_t __attribute__((unused)) * entity,
-    ipmi_sensor_t *sensor, void __attribute__((unused)) * user_data) {
-  /* TODO: Ignore sensors we cannot read */
+static void
+entity_sensor_update_handler(enum ipmi_update_e op,
+                             ipmi_entity_t __attribute__((unused)) * entity,
+                             ipmi_sensor_t *sensor, void *user_data) {
+  c_ipmi_instance_t *st = user_data;
 
   if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
     /* Will check for duplicate entries.. */
-    sensor_list_add(sensor);
+    sensor_list_add(st, sensor);
+
+    if (st->sel_enabled) {
+      int status = 0;
+      /* register threshold event if threshold sensor support events */
+      if ((ipmi_sensor_get_event_reading_type(sensor) ==
+           IPMI_EVENT_READING_TYPE_THRESHOLD) &&
+          (ipmi_sensor_get_threshold_access(sensor) !=
+           IPMI_THRESHOLD_ACCESS_SUPPORT_NONE))
+        status = ipmi_sensor_add_threshold_event_handler(
+            sensor, sensor_threshold_event_handler, st);
+      /* register discrete handler if discrete/specific sensor support events */
+      else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
+        status = ipmi_sensor_add_discrete_event_handler(
+            sensor, sensor_discrete_event_handler, st);
+
+      if (status) {
+        char buf[DATA_MAX_NAME_LEN] = {0};
+        sensor_get_name(sensor, buf, sizeof(buf));
+        ERROR("Unable to add sensor %s event handler, status: %d", buf, status);
+      }
+    }
   } else if (op == IPMI_DELETED) {
-    sensor_list_remove(sensor);
+    sensor_list_remove(st, sensor);
+
+    if (st->sel_enabled) {
+      if (ipmi_sensor_get_event_reading_type(sensor) ==
+          IPMI_EVENT_READING_TYPE_THRESHOLD)
+        ipmi_sensor_remove_threshold_event_handler(
+            sensor, sensor_threshold_event_handler, st);
+      else
+        ipmi_sensor_remove_discrete_event_handler(
+            sensor, sensor_discrete_event_handler, st);
+    }
   }
 } /* void entity_sensor_update_handler */
 
 /*
  * Domain handlers
  */
-static void domain_entity_update_handler(
-    enum ipmi_update_e op, ipmi_domain_t __attribute__((unused)) * domain,
-    ipmi_entity_t *entity, void __attribute__((unused)) * user_data) {
+static void
+domain_entity_update_handler(enum ipmi_update_e op,
+                             ipmi_domain_t __attribute__((unused)) * domain,
+                             ipmi_entity_t *entity, void *user_data) {
   int status;
+  c_ipmi_instance_t *st = user_data;
 
   if (op == IPMI_ADDED) {
     status = ipmi_entity_add_sensor_update_handler(
-        entity, entity_sensor_update_handler, /* user data = */ NULL);
+        entity, entity_sensor_update_handler, /* user data = */ (void *)st);
     if (status != 0) {
-      c_ipmi_error("ipmi_entity_add_sensor_update_handler", status);
+      c_ipmi_error(st, "ipmi_entity_add_sensor_update_handler", status);
     }
   } else if (op == IPMI_DELETED) {
     status = ipmi_entity_remove_sensor_update_handler(
-        entity, entity_sensor_update_handler, /* user data = */ NULL);
+        entity, entity_sensor_update_handler, /* user data = */ (void *)st);
     if (status != 0) {
-      c_ipmi_error("ipmi_entity_remove_sensor_update_handler", status);
+      c_ipmi_error(st, "ipmi_entity_remove_sensor_update_handler", status);
     }
   }
 } /* void domain_entity_update_handler */
 
+static void smi_event_handler(ipmi_con_t __attribute__((unused)) * ipmi,
+                              const ipmi_addr_t __attribute__((unused)) * addr,
+                              unsigned int __attribute__((unused)) addr_len,
+                              ipmi_event_t *event, void *cb_data) {
+  unsigned int type = ipmi_event_get_type(event);
+  ipmi_domain_t *domain = cb_data;
+
+  DEBUG("%s: Event received: type %u", __FUNCTION__, type);
+
+  if (type != 0x02)
+    /* It's not a standard IPMI event. */
+    return;
+
+  /* force domain to reread SELs */
+  ipmi_domain_reread_sels(domain, NULL, NULL);
+}
+
 static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
                                              unsigned int conn_num,
                                              unsigned int port_num,
                                              int still_connected,
                                              void *user_data) {
-  int status;
 
   DEBUG("domain_connection_change_handler (domain = %p, err = %i, "
         "conn_num = %u, port_num = %u, still_connected = %i, "
-        "user_data = %p);\n",
+        "user_data = %p);",
         (void *)domain, err, conn_num, port_num, still_connected, user_data);
 
-  status = ipmi_domain_add_entity_update_handler(
-      domain, domain_entity_update_handler, /* user data = */ NULL);
-  if (status != 0) {
-    c_ipmi_error("ipmi_domain_add_entity_update_handler", status);
+  c_ipmi_instance_t *st = user_data;
+
+  if (err != 0)
+    c_ipmi_error(st, "domain_connection_change_handler", err);
+
+  if (!still_connected) {
+
+    if (st->notify_conn && st->connected && st->init_in_progress == 0) {
+      notification_t n = c_ipmi_notification_init(st, NOTIF_FAILURE);
+
+      sstrncpy(n.message, "IPMI connection lost", sizeof(n.plugin));
+
+      plugin_dispatch_notification(&n);
+    }
+
+    st->connected = 0;
+    return;
   }
-} /* void domain_connection_change_handler */
 
-static int thread_init(os_handler_t **ret_os_handler) {
-  os_handler_t *os_handler;
-  ipmi_con_t *smi_connection = NULL;
-  ipmi_domain_id_t domain_id;
-  int status;
+  if (st->notify_conn && !st->connected && st->init_in_progress == 0) {
+    notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
 
-  os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
-  if (os_handler == NULL) {
-    ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
-    return -1;
+    sstrncpy(n.message, "IPMI connection restored", sizeof(n.plugin));
+
+    plugin_dispatch_notification(&n);
   }
 
-  ipmi_init(os_handler);
+  st->connected = 1;
 
-  status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
-                              /* user data = */ NULL, &smi_connection);
+  int status = ipmi_domain_add_entity_update_handler(
+      domain, domain_entity_update_handler, /* user data = */ st);
   if (status != 0) {
-    c_ipmi_error("ipmi_smi_setup_con", status);
-    return -1;
+    c_ipmi_error(st, "ipmi_domain_add_entity_update_handler", status);
   }
 
-  ipmi_open_option_t open_option[1] = {[0] = {.option = IPMI_OPEN_OPTION_ALL,
-                                              {.ival = 1}}};
+  status = st->connection->add_event_handler(st->connection, smi_event_handler,
+                                             (void *)domain);
+
+  if (status != 0)
+    c_ipmi_error(st, "Failed to register smi event handler", status);
+} /* void domain_connection_change_handler */
 
+static int c_ipmi_thread_init(c_ipmi_instance_t *st) {
+  ipmi_domain_id_t domain_id;
+  int status;
+
+  if (st->connaddr != NULL) {
+    status = ipmi_ip_setup_con(
+        &st->connaddr, &(char *){IPMI_LAN_STD_PORT_STR}, 1, st->authtype,
+        (unsigned int)IPMI_PRIVILEGE_USER, st->username, strlen(st->username),
+        st->password, strlen(st->password), os_handler,
+        /* user data = */ NULL, &st->connection);
+    if (status != 0) {
+      c_ipmi_error(st, "ipmi_ip_setup_con", status);
+      return -1;
+    }
+  } else {
+    status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
+                                /* user data = */ NULL, &st->connection);
+    if (status != 0) {
+      c_ipmi_error(st, "ipmi_smi_setup_con", status);
+      return -1;
+    }
+  }
+
+  ipmi_open_option_t opts[] = {
+      {.option = IPMI_OPEN_OPTION_ALL, {.ival = 1}},
+#ifdef IPMI_OPEN_OPTION_USE_CACHE
+      /* OpenIPMI-2.0.17 and later: Disable SDR cache in local file */
+      {.option = IPMI_OPEN_OPTION_USE_CACHE, {.ival = 0}},
+#endif
+  };
+
+  /*
+   * NOTE: Domain names must be unique. There is static `domains_list` common
+   * to all threads inside lib/domain.c and some ops are done by name.
+   */
   status = ipmi_open_domain(
-      "mydomain", &smi_connection, /* num_con = */ 1,
-      domain_connection_change_handler, /* user data = */ NULL,
-      /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, open_option,
-      sizeof(open_option) / sizeof(open_option[0]), &domain_id);
+      st->name, &st->connection, /* num_con = */ 1,
+      domain_connection_change_handler, /* user data = */ (void *)st,
+      /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, opts,
+      STATIC_ARRAY_SIZE(opts), &domain_id);
   if (status != 0) {
-    c_ipmi_error("ipmi_open_domain", status);
+    c_ipmi_error(st, "ipmi_open_domain", status);
     return -1;
   }
 
-  *ret_os_handler = os_handler;
   return 0;
-} /* int thread_init */
+} /* int c_ipmi_thread_init */
 
-static void *thread_main(void __attribute__((unused)) * user_data) {
-  int status;
-  os_handler_t *os_handler = NULL;
+static void *c_ipmi_thread_main(void *user_data) {
+  c_ipmi_instance_t *st = user_data;
 
-  status = thread_init(&os_handler);
+  int status = c_ipmi_thread_init(st);
   if (status != 0) {
-    ERROR("ipmi plugin: thread_init failed.\n");
+    ERROR("ipmi plugin: c_ipmi_thread_init failed.");
+    st->active = 0;
     return (void *)-1;
   }
 
-  while (c_ipmi_active != 0) {
+  while (st->active != 0) {
     struct timeval tv = {1, 0};
     os_handler->perform_one_op(os_handler, &tv);
   }
+  return (void *)0;
+} /* void *c_ipmi_thread_main */
 
-  ipmi_posix_thread_free_os_handler(os_handler);
+static c_ipmi_instance_t *c_ipmi_init_instance() {
+  c_ipmi_instance_t *st;
 
-  return (void *)0;
-} /* void *thread_main */
-
-static int c_ipmi_config(const char *key, const char *value) {
-  if (ignorelist == NULL)
-    ignorelist = ignorelist_create(/* invert = */ 1);
-  if (ignorelist == NULL)
-    return 1;
-
-  if (strcasecmp("Sensor", key) == 0) {
-    ignorelist_add(ignorelist, value);
-  } else if (strcasecmp("IgnoreSelected", key) == 0) {
-    int invert = 1;
-    if (IS_TRUE(value))
-      invert = 0;
-    ignorelist_set_invert(ignorelist, invert);
-  } else if (strcasecmp("NotifySensorAdd", key) == 0) {
-    if (IS_TRUE(value))
-      c_ipmi_nofiy_add = 1;
-  } else if (strcasecmp("NotifySensorRemove", key) == 0) {
-    if (IS_TRUE(value))
-      c_ipmi_nofiy_remove = 1;
-  } else if (strcasecmp("NotifySensorNotPresent", key) == 0) {
-    if (IS_TRUE(value))
-      c_ipmi_nofiy_notpresent = 1;
-  } else {
-    return -1;
+  st = calloc(1, sizeof(*st));
+  if (st == NULL) {
+    ERROR("ipmi plugin: calloc failed.");
+    return NULL;
   }
 
-  return 0;
-} /* int c_ipmi_config */
+  st->name = strdup("main");
+  if (st->name == NULL) {
+    sfree(st);
+    ERROR("ipmi plugin: strdup() failed.");
+    return NULL;
+  }
 
-static int c_ipmi_init(void) {
-  int status;
+  st->ignorelist = ignorelist_create(/* invert = */ 1);
+  if (st->ignorelist == NULL) {
+    sfree(st->name);
+    sfree(st);
+    ERROR("ipmi plugin: ignorelist_create() failed.");
+    return NULL;
+  }
 
-  /* Don't send `ADD' notifications during startup (~ 1 minute) */
-  time_t iv = CDTIME_T_TO_TIME_T(plugin_get_interval());
-  c_ipmi_init_in_progress = 1 + (60 / iv);
+  st->sensor_list = NULL;
+  pthread_mutex_init(&st->sensor_list_lock, /* attr = */ NULL);
+
+  st->host = NULL;
+  st->connaddr = NULL;
+  st->username = NULL;
+  st->password = NULL;
+  st->authtype = IPMI_AUTHTYPE_DEFAULT;
+
+  st->next = NULL;
 
-  c_ipmi_active = 1;
+  return st;
+} /* c_ipmi_instance_t *c_ipmi_init_instance */
+
+static void c_ipmi_free_instance(c_ipmi_instance_t *st) {
+  if (st == NULL)
+    return;
+
+  assert(st->next == NULL);
+
+  sfree(st->name);
+  sfree(st->host);
+  sfree(st->connaddr);
+  sfree(st->username);
+  sfree(st->password);
+
+  ignorelist_free(st->ignorelist);
+  pthread_mutex_destroy(&st->sensor_list_lock);
+  sfree(st);
+} /* void c_ipmi_free_instance */
+
+static void c_ipmi_add_instance(c_ipmi_instance_t *instance) {
+  if (instances == NULL) {
+    instances = instance;
+    return;
+  }
+
+  c_ipmi_instance_t *last = instances;
+
+  while (last->next != NULL)
+    last = last->next;
+
+  last->next = instance;
+} /* void c_ipmi_add_instance */
+
+static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
+  int status = 0;
+  c_ipmi_instance_t *st = c_ipmi_init_instance();
+  if (st == NULL)
+    return ENOMEM;
+
+  if (strcasecmp(ci->key, "Instance") == 0)
+    status = cf_util_get_string(ci, &st->name);
 
-  status = plugin_thread_create(&thread_id, /* attr = */ NULL, thread_main,
-                                /* user data = */ NULL, "ipmi");
   if (status != 0) {
-    c_ipmi_active = 0;
-    thread_id = (pthread_t)0;
-    ERROR("ipmi plugin: pthread_create failed.");
-    return -1;
+    c_ipmi_free_instance(st);
+    return status;
   }
 
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Sensor", child->key) == 0)
+      ignorelist_add(st->ignorelist, ci->values[0].value.string);
+    else if (strcasecmp("IgnoreSelected", child->key) == 0) {
+      _Bool t;
+      status = cf_util_get_boolean(child, &t);
+      if (status != 0)
+        break;
+      ignorelist_set_invert(st->ignorelist, /* invert = */ !t);
+    } else if (strcasecmp("NotifyIPMIConnectionState", child->key) == 0) {
+      status = cf_util_get_boolean(child, &st->notify_conn);
+    } else if (strcasecmp("NotifySensorAdd", child->key) == 0) {
+      status = cf_util_get_boolean(child, &st->notify_add);
+    } else if (strcasecmp("NotifySensorRemove", child->key) == 0) {
+      status = cf_util_get_boolean(child, &st->notify_remove);
+    } else if (strcasecmp("NotifySensorNotPresent", child->key) == 0) {
+      status = cf_util_get_boolean(child, &st->notify_notpresent);
+    } else if (strcasecmp("SELEnabled", child->key) == 0) {
+      status = cf_util_get_boolean(child, &st->sel_enabled);
+    } else if (strcasecmp("SELClearEvent", child->key) == 0) {
+      status = cf_util_get_boolean(child, &st->sel_clear_event);
+    } else if (strcasecmp("Host", child->key) == 0)
+      status = cf_util_get_string(child, &st->host);
+    else if (strcasecmp("Address", child->key) == 0)
+      status = cf_util_get_string(child, &st->connaddr);
+    else if (strcasecmp("Username", child->key) == 0)
+      status = cf_util_get_string(child, &st->username);
+    else if (strcasecmp("Password", child->key) == 0)
+      status = cf_util_get_string(child, &st->password);
+    else if (strcasecmp("AuthType", child->key) == 0) {
+      char tmp[8];
+      status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
+      if (status != 0)
+        break;
+
+      if (strcasecmp("MD5", tmp) == 0)
+        st->authtype = IPMI_AUTHTYPE_MD5;
+      else if (strcasecmp("rmcp+", tmp) == 0)
+        st->authtype = IPMI_AUTHTYPE_RMCP_PLUS;
+      else
+        WARNING("ipmi plugin: The value \"%s\" is not valid for the "
+                "\"AuthType\" option.",
+                tmp);
+    } else {
+      WARNING("ipmi plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status != 0) {
+    c_ipmi_free_instance(st);
+    return status;
+  }
+
+  c_ipmi_add_instance(st);
+
   return 0;
-} /* int c_ipmi_init */
+} /* int c_ipmi_config_add_instance */
+
+static int c_ipmi_config(oconfig_item_t *ci) {
+  _Bool have_instance_block = 0;
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Instance", child->key) == 0) {
+      int status = c_ipmi_config_add_instance(child);
+      if (status != 0)
+        return status;
+
+      have_instance_block = 1;
+    } else if (!have_instance_block) {
+      /* Non-instance option: Assume legacy configuration (without <Instance />
+       * blocks) and call c_ipmi_config_add_instance with the <Plugin /> block.
+       */
+      WARNING("ipmi plugin: Legacy configuration found! Please update your "
+              "config file.");
+      return c_ipmi_config_add_instance(ci);
+    } else {
+      WARNING("ipmi plugin: The configuration option "
+              "\"%s\" is not allowed here. Did you "
+              "forget to add an <Instance /> block "
+              "around the configuration?",
+              child->key);
+      return -1;
+    }
+  } /* for (ci->children) */
+
+  return 0;
+} /* int c_ipmi_config */
 
-static int c_ipmi_read(void) {
-  if ((c_ipmi_active == 0) || (thread_id == (pthread_t)0)) {
+static int c_ipmi_read(user_data_t *user_data) {
+  c_ipmi_instance_t *st = user_data->data;
+
+  if (st->active == 0) {
     INFO("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
     return -1;
   }
 
-  sensor_list_read_all();
+  if (st->connected == 0)
+    return 0;
+
+  sensor_list_read_all(st);
 
-  if (c_ipmi_init_in_progress > 0)
-    c_ipmi_init_in_progress--;
+  if (st->init_in_progress > 0)
+    st->init_in_progress--;
   else
-    c_ipmi_init_in_progress = 0;
+    st->init_in_progress = 0;
 
   return 0;
 } /* int c_ipmi_read */
 
+static int c_ipmi_init(void) {
+  c_ipmi_instance_t *st;
+  char callback_name[3 * DATA_MAX_NAME_LEN];
+
+  if (os_handler != NULL) {
+    return 0;
+  }
+
+  os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
+  if (os_handler == NULL) {
+    ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
+    return -1;
+  }
+
+  os_handler->set_log_handler(os_handler, c_ipmi_log);
+
+  if (ipmi_init(os_handler) != 0) {
+    ERROR("ipmi plugin: ipmi_init() failed.");
+    os_handler->free_os_handler(os_handler);
+    return -1;
+  };
+
+  if (instances == NULL) {
+    /* No instances were configured, let's start a default instance. */
+    st = c_ipmi_init_instance();
+    if (st == NULL)
+      return ENOMEM;
+
+    c_ipmi_add_instance(st);
+  }
+
+  /* Don't send `ADD' notifications during startup (~ 1 minute) */
+  int cycles = 1 + (60 / CDTIME_T_TO_TIME_T(plugin_get_interval()));
+
+  st = instances;
+  while (NULL != st) {
+    /* The `st->name` is used as "domain name" for ipmi_open_domain().
+     * That value should be unique, so we do plugin_register_complex_read()
+     * at first as it checks the uniqueness. */
+    snprintf(callback_name, sizeof(callback_name), "ipmi/%s", st->name);
+
+    user_data_t ud = {
+        .data = st,
+    };
+
+    int status = plugin_register_complex_read(
+        /* group     = */ "ipmi",
+        /* name      = */ callback_name,
+        /* callback  = */ c_ipmi_read,
+        /* interval  = */ 0,
+        /* user_data = */ &ud);
+
+    if (status != 0) {
+      st = st->next;
+      continue;
+    }
+
+    st->init_in_progress = cycles;
+    st->active = 1;
+
+    status = plugin_thread_create(&st->thread_id, /* attr = */ NULL,
+                                  c_ipmi_thread_main,
+                                  /* user data = */ (void *)st, "ipmi");
+
+    if (status != 0) {
+      st->active = 0;
+      st->thread_id = (pthread_t){0};
+
+      plugin_unregister_read(callback_name);
+
+      ERROR("ipmi plugin: pthread_create failed for `%s`.", callback_name);
+    }
+
+    st = st->next;
+  }
+
+  return 0;
+} /* int c_ipmi_init */
+
 static int c_ipmi_shutdown(void) {
-  c_ipmi_active = 0;
+  c_ipmi_instance_t *st = instances;
+  instances = NULL;
+
+  while (st != NULL) {
+    c_ipmi_instance_t *next = st->next;
+
+    st->next = NULL;
+    st->active = 0;
+
+    if (!pthread_equal(st->thread_id, (pthread_t){0})) {
+      pthread_join(st->thread_id, NULL);
+      st->thread_id = (pthread_t){0};
+    }
+
+    sensor_list_remove_all(st);
+    c_ipmi_free_instance(st);
 
-  if (thread_id != (pthread_t)0) {
-    pthread_join(thread_id, NULL);
-    thread_id = (pthread_t)0;
+    st = next;
   }
 
-  sensor_list_remove_all();
+  os_handler->free_os_handler(os_handler);
+  os_handler = NULL;
 
   return 0;
 } /* int c_ipmi_shutdown */
 
 void module_register(void) {
-  plugin_register_config("ipmi", c_ipmi_config, config_keys, config_keys_num);
+  plugin_register_complex_config("ipmi", c_ipmi_config);
   plugin_register_init("ipmi", c_ipmi_init);
-  plugin_register_read("ipmi", c_ipmi_read);
   plugin_register_shutdown("ipmi", c_ipmi_shutdown);
 } /* void module_register */
index 4083e1e..6703411 100644 (file)
@@ -302,8 +302,6 @@ static int parse_values(void *payload, size_t payload_size,
   state->values = calloc(sizeof(*state->values), state->values_len);
   state->values_types = calloc(sizeof(*state->values_types), state->values_len);
   if ((state->values == NULL) || (state->values_types == NULL)) {
-    free(state->values);
-    free(state->values_types);
     return ENOMEM;
   }
 
@@ -556,6 +554,8 @@ static int network_parse(void *data, size_t data_size, lcc_security_level_t sl,
     case TYPE_VALUES: {
       lcc_value_list_t vl = state;
       if (parse_values(payload, sizeof(payload), &vl)) {
+        free(vl.values);
+        free(vl.values_types);
         DEBUG("lcc_network_parse(): parse_values failed.\n");
         return EINVAL;
       }
index 6b10b04..ae5a7f5 100644 (file)
@@ -257,7 +257,7 @@ static int socket_write(socket_adapter_t *self, const char *msg,
                         const size_t len) {
   int ret = 0;
   pthread_rwlock_rdlock(&self->lock);
-  if (swrite(self->sock_fd, msg, len) < 0)
+  if (swrite(self->sock_fd, msg, len) != 0)
     ret = -1;
   pthread_rwlock_unlock(&self->lock);
   return ret;
index 90f323f..85794a5 100644 (file)
 #define MEMCACHED_CONNECT_TIMEOUT 10000
 #define MEMCACHED_IO_TIMEOUT 5000
 
+struct prev_s {
+  derive_t hits;
+  derive_t gets;
+  derive_t incr_hits;
+  derive_t incr_misses;
+  derive_t decr_hits;
+  derive_t decr_misses;
+};
+
+typedef struct prev_s prev_t;
+
 struct memcached_s {
   char *name;
   char *host;
@@ -54,6 +65,7 @@ struct memcached_s {
   char *connhost;
   char *connport;
   int fd;
+  prev_t prev;
 };
 typedef struct memcached_s memcached_t;
 
@@ -80,13 +92,12 @@ static void memcached_free(void *arg) {
 
 static int memcached_connect_unix(memcached_t *st) {
   struct sockaddr_un serv_addr = {0};
-  int fd;
 
   serv_addr.sun_family = AF_UNIX;
   sstrncpy(serv_addr.sun_path, st->socket, sizeof(serv_addr.sun_path));
 
   /* create our socket descriptor */
-  fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
   if (fd < 0) {
     char errbuf[1024];
     ERROR("memcached plugin: memcached_connect_unix: socket(2) failed: %s",
@@ -115,14 +126,13 @@ static int memcached_connect_unix(memcached_t *st) {
 
 static int memcached_connect_inet(memcached_t *st) {
   struct addrinfo *ai_list;
-  int status;
   int fd = -1;
 
   struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
                               .ai_flags = AI_ADDRCONFIG,
                               .ai_socktype = SOCK_STREAM};
 
-  status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
+  int status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
   if (status != 0) {
     char errbuf[1024];
     ERROR("memcached plugin: memcached_connect_inet: "
@@ -380,29 +390,74 @@ static void submit_gauge2(const char *type, const char *type_inst,
   plugin_dispatch_values(&vl);
 }
 
+static gauge_t calculate_ratio_percent(derive_t part, derive_t total,
+                                       derive_t *prev_part,
+                                       derive_t *prev_total) {
+  if ((*prev_part == 0) || (*prev_total == 0) || (part < *prev_part) ||
+      (total < *prev_total)) {
+    *prev_part = part;
+    *prev_total = total;
+    return NAN;
+  }
+
+  derive_t num = part - *prev_part;
+  derive_t denom = total - *prev_total;
+
+  *prev_part = part;
+  *prev_total = total;
+
+  if (denom == 0)
+    return NAN;
+
+  if (num == 0)
+    return 0;
+
+  return 100.0 * (gauge_t)num / (gauge_t)denom;
+}
+
+static gauge_t calculate_ratio_percent2(derive_t part1, derive_t part2,
+                                        derive_t *prev1, derive_t *prev2) {
+  if ((*prev1 == 0) || (*prev2 == 0) || (part1 < *prev1) || (part2 < *prev2)) {
+    *prev1 = part1;
+    *prev2 = part2;
+    return NAN;
+  }
+
+  derive_t num = part1 - *prev1;
+  derive_t denom = part2 - *prev2 + num;
+
+  *prev1 = part1;
+  *prev2 = part2;
+
+  if (denom == 0)
+    return NAN;
+
+  if (num == 0)
+    return 0;
+
+  return 100.0 * (gauge_t)num / (gauge_t)denom;
+}
+
 static int memcached_read(user_data_t *user_data) {
   char buf[4096];
   char *fields[3];
-  char *ptr;
   char *line;
-  char *saveptr;
-  int fields_num;
-
-  gauge_t bytes_used = NAN;
-  gauge_t bytes_total = NAN;
-  gauge_t hits = NAN;
-  gauge_t gets = NAN;
-  gauge_t incr_hits = NAN;
-  derive_t incr = 0;
-  gauge_t decr_hits = NAN;
-  derive_t decr = 0;
+
+  derive_t bytes_used = 0;
+  derive_t bytes_total = 0;
+  derive_t get_hits = 0;
+  derive_t cmd_get = 0;
+  derive_t incr_hits = 0;
+  derive_t incr_misses = 0;
+  derive_t decr_hits = 0;
+  derive_t decr_misses = 0;
   derive_t rusage_user = 0;
   derive_t rusage_syst = 0;
   derive_t octets_rx = 0;
   derive_t octets_tx = 0;
 
-  memcached_t *st;
-  st = user_data->data;
+  memcached_t *st = user_data->data;
+  prev_t *prev = &st->prev;
 
   /* get data from daemon */
   if (memcached_query_daemon(buf, sizeof(buf), st) < 0) {
@@ -412,18 +467,15 @@ static int memcached_read(user_data_t *user_data) {
 #define FIELD_IS(cnst)                                                         \
   (((sizeof(cnst) - 1) == name_len) && (strcmp(cnst, fields[1]) == 0))
 
-  ptr = buf;
-  saveptr = NULL;
+  char *ptr = buf;
+  char *saveptr = NULL;
   while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) {
-    int name_len;
-
     ptr = NULL;
 
-    fields_num = strsplit(line, fields, 3);
-    if (fields_num != 3)
+    if (strsplit(line, fields, 3) != 3)
       continue;
 
-    name_len = strlen(fields[1]);
+    int name_len = strlen(fields[1]);
     if (name_len == 0)
       continue;
 
@@ -436,9 +488,10 @@ static int memcached_read(user_data_t *user_data) {
      * CPU time consumed by the memcached process
      */
     if (FIELD_IS("rusage_user")) {
-      rusage_user = atoll(fields[2]);
+      /* Convert to useconds */
+      rusage_user = atof(fields[2]) * 1000000;
     } else if (FIELD_IS("rusage_system")) {
-      rusage_syst = atoll(fields[2]);
+      rusage_syst = atof(fields[2]) * 1000000;
     }
 
     /*
@@ -459,9 +512,9 @@ static int memcached_read(user_data_t *user_data) {
      * Number of bytes used and available (total - used)
      */
     else if (FIELD_IS("bytes")) {
-      bytes_used = atof(fields[2]);
+      bytes_used = atoll(fields[2]);
     } else if (FIELD_IS("limit_maxbytes")) {
-      bytes_total = atof(fields[2]);
+      bytes_total = atoll(fields[2]);
     }
 
     /*
@@ -470,14 +523,14 @@ static int memcached_read(user_data_t *user_data) {
     else if (FIELD_IS("curr_connections")) {
       submit_gauge("memcached_connections", "current", atof(fields[2]), st);
     } else if (FIELD_IS("listen_disabled_num")) {
-      submit_derive("connections", "listen_disabled", atof(fields[2]), st);
+      submit_derive("connections", "listen_disabled", atoll(fields[2]), st);
     }
     /*
      * Total number of connections opened since the server started running
      * Report this as connection rate.
      */
     else if (FIELD_IS("total_connections")) {
-      submit_derive("connections", "opened", atof(fields[2]), st);
+      submit_derive("connections", "opened", atoll(fields[2]), st);
     }
 
     /*
@@ -487,30 +540,24 @@ static int memcached_read(user_data_t *user_data) {
       const char *name = fields[1] + 4;
       submit_derive("memcached_command", name, atoll(fields[2]), st);
       if (strcmp(name, "get") == 0)
-        gets = atof(fields[2]);
+        cmd_get = atoll(fields[2]);
     }
 
     /*
      * Increment/Decrement
      */
     else if (FIELD_IS("incr_misses")) {
-      derive_t incr_count = atoll(fields[2]);
-      submit_derive("memcached_ops", "incr_misses", incr_count, st);
-      incr += incr_count;
+      incr_misses = atoll(fields[2]);
+      submit_derive("memcached_ops", "incr_misses", incr_misses, st);
     } else if (FIELD_IS("incr_hits")) {
-      derive_t incr_count = atoll(fields[2]);
-      submit_derive("memcached_ops", "incr_hits", incr_count, st);
-      incr_hits = atof(fields[2]);
-      incr += incr_count;
+      incr_hits = atoll(fields[2]);
+      submit_derive("memcached_ops", "incr_hits", incr_hits, st);
     } else if (FIELD_IS("decr_misses")) {
-      derive_t decr_count = atoll(fields[2]);
-      submit_derive("memcached_ops", "decr_misses", decr_count, st);
-      decr += decr_count;
+      decr_misses = atoll(fields[2]);
+      submit_derive("memcached_ops", "decr_misses", decr_misses, st);
     } else if (FIELD_IS("decr_hits")) {
-      derive_t decr_count = atoll(fields[2]);
-      submit_derive("memcached_ops", "decr_hits", decr_count, st);
-      decr_hits = atof(fields[2]);
-      decr += decr_count;
+      decr_hits = atoll(fields[2]);
+      submit_derive("memcached_ops", "decr_hits", decr_hits, st);
     }
 
     /*
@@ -520,8 +567,8 @@ static int memcached_read(user_data_t *user_data) {
      * - evictions
      */
     else if (FIELD_IS("get_hits")) {
-      submit_derive("memcached_ops", "hits", atoll(fields[2]), st);
-      hits = atof(fields[2]);
+      get_hits = atoll(fields[2]);
+      submit_derive("memcached_ops", "hits", get_hits, st);
     } else if (FIELD_IS("get_misses")) {
       submit_derive("memcached_ops", "misses", atoll(fields[2]), st);
     } else if (FIELD_IS("evictions")) {
@@ -542,7 +589,7 @@ static int memcached_read(user_data_t *user_data) {
     }
   } /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
 
-  if (!isnan(bytes_used) && !isnan(bytes_total) && (bytes_used <= bytes_total))
+  if ((bytes_total > 0) && (bytes_used <= bytes_total))
     submit_gauge2("df", "cache", bytes_used, bytes_total - bytes_used, st);
 
   if ((rusage_user != 0) || (rusage_syst != 0))
@@ -551,25 +598,24 @@ static int memcached_read(user_data_t *user_data) {
   if ((octets_rx != 0) || (octets_tx != 0))
     submit_derive2("memcached_octets", NULL, octets_rx, octets_tx, st);
 
-  if (!isnan(gets) && !isnan(hits)) {
-    gauge_t rate = NAN;
-
-    if (gets != 0.0)
-      rate = 100.0 * hits / gets;
-
-    submit_gauge("percent", "hitratio", rate, st);
+  if ((cmd_get != 0) && (get_hits != 0)) {
+    gauge_t ratio =
+        calculate_ratio_percent(get_hits, cmd_get, &prev->hits, &prev->gets);
+    submit_gauge("percent", "hitratio", ratio, st);
   }
 
-  if (!isnan(incr_hits) && incr != 0) {
-    gauge_t incr_rate = 100.0 * incr_hits / incr;
-    submit_gauge("percent", "incr_hitratio", incr_rate, st);
-    submit_derive("memcached_ops", "incr", incr, st);
+  if ((incr_hits != 0) && (incr_misses != 0)) {
+    gauge_t ratio = calculate_ratio_percent2(
+        incr_hits, incr_misses, &prev->incr_hits, &prev->incr_misses);
+    submit_gauge("percent", "incr_hitratio", ratio, st);
+    submit_derive("memcached_ops", "incr", incr_hits + incr_misses, st);
   }
 
-  if (!isnan(decr_hits) && decr != 0) {
-    gauge_t decr_rate = 100.0 * decr_hits / decr;
-    submit_gauge("percent", "decr_hitratio", decr_rate, st);
-    submit_derive("memcached_ops", "decr", decr, st);
+  if ((decr_hits != 0) && (decr_misses != 0)) {
+    gauge_t ratio = calculate_ratio_percent2(
+        decr_hits, decr_misses, &prev->decr_hits, &prev->decr_misses);
+    submit_gauge("percent", "decr_hitratio", ratio, st);
+    submit_derive("memcached_ops", "decr", decr_hits + decr_misses, st);
   }
 
   return 0;
@@ -612,6 +658,13 @@ static int memcached_set_defaults(memcached_t *st) {
   assert(st->connhost != NULL);
   assert(st->connport != NULL);
 
+  st->prev.hits = 0;
+  st->prev.gets = 0;
+  st->prev.incr_hits = 0;
+  st->prev.incr_misses = 0;
+  st->prev.decr_hits = 0;
+  st->prev.decr_misses = 0;
+
   return 0;
 } /* int memcached_set_defaults */
 
@@ -646,13 +699,12 @@ static int memcached_add_read_callback(memcached_t *st) {
  * </Plugin>
  */
 static int config_add_instance(oconfig_item_t *ci) {
-  memcached_t *st;
   int status = 0;
 
   /* Disable automatic generation of default instance in the init callback. */
   memcached_have_instances = 1;
 
-  st = calloc(1, sizeof(*st));
+  memcached_t *st = calloc(1, sizeof(*st));
   if (st == NULL) {
     ERROR("memcached plugin: calloc failed.");
     return ENOMEM;
@@ -727,14 +779,12 @@ static int memcached_config(oconfig_item_t *ci) {
 } /* int memcached_config */
 
 static int memcached_init(void) {
-  memcached_t *st;
-  int status;
 
   if (memcached_have_instances)
     return 0;
 
   /* No instances were configured, lets start a default instance. */
-  st = calloc(1, sizeof(*st));
+  memcached_t *st = calloc(1, sizeof(*st));
   if (st == NULL)
     return ENOMEM;
   st->name = NULL;
@@ -745,7 +795,7 @@ static int memcached_init(void) {
 
   st->fd = -1;
 
-  status = memcached_add_read_callback(st);
+  int status = memcached_add_read_callback(st);
   if (status == 0)
     memcached_have_instances = 1;
 
index 72b0fed..fc69e02 100644 (file)
@@ -62,7 +62,7 @@ static int multimeter_read_value(double *value) {
       struct timeval time_now;
 
       status = swrite(fd, "D", 1);
-      if (status < 0) {
+      if (status != 0) {
         ERROR("multimeter plugin: swrite failed.");
         return -1;
       }
index 48d7aa7..0faf2a2 100644 (file)
@@ -669,7 +669,7 @@ static int ntpd_send_request(int req_code, int req_items, int req_size,
         (void *)req_data);
 
   status = swrite(sd, (const char *)&req, REQ_LEN_NOMAC);
-  if (status < 0) {
+  if (status != 0) {
     DEBUG("`swrite' failed. Closing socket #%i", sd);
     close(sd);
     sock_descr = sd = -1;
index 34cab88..d27e958 100644 (file)
@@ -423,7 +423,7 @@ static int cpy_write_callback(const data_set_t *ds,
   }
   dict = PyDict_New(); /* New reference. */
   if (value_list->meta) {
-    char **table;
+    char **table = NULL;
     meta_data_t *meta = value_list->meta;
 
     int num = meta_data_toc(meta, &table);
@@ -446,19 +446,21 @@ static int cpy_write_callback(const data_set_t *ds,
       } else if (type == MD_TYPE_SIGNED_INT) {
         if (meta_data_get_signed_int(meta, table[i], &si))
           continue;
-        temp = PyObject_CallFunctionObjArgs((void *)&SignedType,
-                                            PyLong_FromLongLong(si),
+        PyObject *sival = PyLong_FromLongLong(si); /* New reference */
+        temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
                                             (void *)0); /* New reference. */
         PyDict_SetItemString(dict, table[i], temp);
         Py_XDECREF(temp);
+        Py_XDECREF(sival);
       } else if (type == MD_TYPE_UNSIGNED_INT) {
         if (meta_data_get_unsigned_int(meta, table[i], &ui))
           continue;
-        temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType,
-                                            PyLong_FromUnsignedLongLong(ui),
+        PyObject *uval = PyLong_FromUnsignedLongLong(ui); /* New reference */
+        temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
                                             (void *)0); /* New reference. */
         PyDict_SetItemString(dict, table[i], temp);
         Py_XDECREF(temp);
+        Py_XDECREF(uval);
       } else if (type == MD_TYPE_DOUBLE) {
         if (meta_data_get_double(meta, table[i], &d))
           continue;
@@ -510,6 +512,39 @@ static int cpy_notification_callback(const notification_t *notification,
   Notification *n;
 
   CPY_LOCK_THREADS
+  PyObject *dict = PyDict_New(); /* New reference. */
+  for (notification_meta_t *meta = notification->meta; meta != NULL;
+       meta = meta->next) {
+    PyObject *temp = NULL;
+    if (meta->type == NM_TYPE_STRING) {
+      temp = cpy_string_to_unicode_or_bytes(
+          meta->nm_value.nm_string); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+    } else if (meta->type == NM_TYPE_SIGNED_INT) {
+      PyObject *sival = PyLong_FromLongLong(meta->nm_value.nm_signed_int);
+      temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
+                                          (void *)0); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+      Py_XDECREF(sival);
+    } else if (meta->type == NM_TYPE_UNSIGNED_INT) {
+      PyObject *uval =
+          PyLong_FromUnsignedLongLong(meta->nm_value.nm_unsigned_int);
+      temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
+                                          (void *)0); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+      Py_XDECREF(uval);
+    } else if (meta->type == NM_TYPE_DOUBLE) {
+      temp = PyFloat_FromDouble(meta->nm_value.nm_double); /* New reference. */
+      PyDict_SetItemString(dict, meta->name, temp);
+      Py_XDECREF(temp);
+    } else if (meta->type == NM_TYPE_BOOLEAN) {
+      PyDict_SetItemString(dict, meta->name,
+                           meta->nm_value.nm_boolean ? Py_True : Py_False);
+    }
+  }
   notify = Notification_New(); /* New reference. */
   n = (Notification *)notify;
   sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
@@ -522,6 +557,8 @@ static int cpy_notification_callback(const notification_t *notification,
   n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
   sstrncpy(n->message, notification->message, sizeof(n->message));
   n->severity = notification->severity;
+  Py_CLEAR(n->meta);
+  n->meta = dict; /* Steals a reference. */
   ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
                                      (void *)0); /* New reference. */
   Py_XDECREF(notify);
@@ -651,8 +688,9 @@ static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
   for (size_t i = 0; i < ds->ds_num; ++i) {
     tuple = PyTuple_New(4);
     PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
-    PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(
-                                   DS_TYPE_TO_STRING(ds->ds[i].type)));
+    PyTuple_SET_ITEM(
+        tuple, 1,
+        cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
     PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
     PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
     PyList_SET_ITEM(list, i, tuple);
@@ -720,7 +758,8 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
 
   register_function(buf, handler,
                     &(user_data_t){
-                        .data = c, .free_func = cpy_destroy_user_data,
+                        .data = c,
+                        .free_func = cpy_destroy_user_data,
                     });
 
   ++cpy_num_callbacks;
@@ -763,7 +802,8 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args,
       /* group = */ "python", buf, cpy_read_callback,
       DOUBLE_TO_CDTIME_T(interval),
       &(user_data_t){
-          .data = c, .free_func = cpy_destroy_user_data,
+          .data = c,
+          .free_func = cpy_destroy_user_data,
       });
   ++cpy_num_callbacks;
   return cpy_string_to_unicode_or_bytes(buf);
@@ -1132,8 +1172,9 @@ static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
   values = PyTuple_New(ci->values_num); /* New reference. */
   for (int i = 0; i < ci->values_num; ++i) {
     if (ci->values[i].type == OCONFIG_TYPE_STRING) {
-      PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
-                                      ci->values[i].value.string));
+      PyTuple_SET_ITEM(
+          values, i,
+          cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
     } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
       PyTuple_SET_ITEM(values, i,
                        PyFloat_FromDouble(ci->values[i].value.number));
index e1856b8..d1bdedd 100644 (file)
 
 #include "cpython.h"
 
+typedef struct {
+  int (*add_string)(void *, const char *, const char *);
+  int (*add_signed_int)(void *, const char *, int64_t);
+  int (*add_unsigned_int)(void *, const char *, uint64_t);
+  int (*add_double)(void *, const char *, double);
+  int (*add_boolean)(void *, const char *, _Bool);
+} cpy_build_meta_handler_t;
+
 #define FreeAll()                                                              \
   do {                                                                         \
     PyMem_Free(type);                                                          \
@@ -457,26 +465,26 @@ static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
   return 0;
 }
 
-static meta_data_t *cpy_build_meta(PyObject *meta) {
+static int cpy_build_meta_generic(PyObject *meta,
+                                  cpy_build_meta_handler_t *meta_func,
+                                  void *m) {
   int s;
-  meta_data_t *m = NULL;
   PyObject *l;
 
   if ((meta == NULL) || (meta == Py_None))
-    return NULL;
+    return -1;
 
   l = PyDict_Items(meta); /* New reference. */
   if (!l) {
     cpy_log_exception("building meta data");
-    return NULL;
+    return -1;
   }
   s = PyList_Size(l);
   if (s <= 0) {
     Py_XDECREF(l);
-    return NULL;
+    return -1;
   }
 
-  m = meta_data_create();
   for (int i = 0; i < s; ++i) {
     const char *string, *keystring;
     PyObject *key, *value, *item, *tmp;
@@ -493,45 +501,45 @@ static meta_data_t *cpy_build_meta(PyObject *meta) {
     value = PyTuple_GET_ITEM(item, 1);
     Py_INCREF(value);
     if (value == Py_True) {
-      meta_data_add_boolean(m, keystring, 1);
+      meta_func->add_boolean(m, keystring, 1);
     } else if (value == Py_False) {
-      meta_data_add_boolean(m, keystring, 0);
+      meta_func->add_boolean(m, keystring, 0);
     } else if (PyFloat_Check(value)) {
-      meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
+      meta_func->add_double(m, keystring, PyFloat_AsDouble(value));
     } else if (PyObject_TypeCheck(value, &SignedType)) {
       long long int lli;
       lli = PyLong_AsLongLong(value);
       if (!PyErr_Occurred() && (lli == (int64_t)lli))
-        meta_data_add_signed_int(m, keystring, lli);
+        meta_func->add_signed_int(m, keystring, lli);
     } else if (PyObject_TypeCheck(value, &UnsignedType)) {
       long long unsigned llu;
       llu = PyLong_AsUnsignedLongLong(value);
       if (!PyErr_Occurred() && (llu == (uint64_t)llu))
-        meta_data_add_unsigned_int(m, keystring, llu);
+        meta_func->add_unsigned_int(m, keystring, llu);
     } else if (PyNumber_Check(value)) {
       long long int lli;
       long long unsigned llu;
       tmp = PyNumber_Long(value);
       lli = PyLong_AsLongLong(tmp);
       if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
-        meta_data_add_signed_int(m, keystring, lli);
+        meta_func->add_signed_int(m, keystring, lli);
       } else {
         PyErr_Clear();
         llu = PyLong_AsUnsignedLongLong(tmp);
         if (!PyErr_Occurred() && (llu == (uint64_t)llu))
-          meta_data_add_unsigned_int(m, keystring, llu);
+          meta_func->add_unsigned_int(m, keystring, llu);
       }
       Py_XDECREF(tmp);
     } else {
       string = cpy_unicode_or_bytes_to_string(&value);
       if (string) {
-        meta_data_add_string(m, keystring, string);
+        meta_func->add_string(m, keystring, string);
       } else {
         PyErr_Clear();
         tmp = PyObject_Str(value);
         string = cpy_unicode_or_bytes_to_string(&tmp);
         if (string)
-          meta_data_add_string(m, keystring, string);
+          meta_func->add_string(m, keystring, string);
         Py_XDECREF(tmp);
       }
     }
@@ -541,9 +549,44 @@ static meta_data_t *cpy_build_meta(PyObject *meta) {
     Py_DECREF(key);
   }
   Py_XDECREF(l);
+  return 0;
+}
+
+#define CPY_BUILD_META_FUNC(meta_type, func, val_type)                         \
+  static int cpy_##func(void *meta, const char *key, val_type val) {           \
+    return func((meta_type *)meta, key, val);                                  \
+  }
+
+#define CPY_BUILD_META_HANDLER(func_prefix, meta_type)                         \
+  CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_string, const char *)       \
+  CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_signed_int, int64_t)        \
+  CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_unsigned_int, uint64_t)     \
+  CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_double, double)             \
+  CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, _Bool)             \
+                                                                               \
+  static cpy_build_meta_handler_t cpy_##func_prefix = {                        \
+      .add_string = cpy_##func_prefix##_add_string,                            \
+      .add_signed_int = cpy_##func_prefix##_add_signed_int,                    \
+      .add_unsigned_int = cpy_##func_prefix##_add_unsigned_int,                \
+      .add_double = cpy_##func_prefix##_add_double,                            \
+      .add_boolean = cpy_##func_prefix##_add_boolean}
+
+CPY_BUILD_META_HANDLER(meta_data, meta_data_t);
+CPY_BUILD_META_HANDLER(plugin_notification_meta, notification_t);
+
+static meta_data_t *cpy_build_meta(PyObject *meta) {
+  meta_data_t *m = meta_data_create();
+  if (cpy_build_meta_generic(meta, &cpy_meta_data, (void *)m) < 0) {
+    meta_data_destroy(m);
+    return NULL;
+  }
   return m;
 }
 
+static void cpy_build_notification_meta(notification_t *n, PyObject *meta) {
+  cpy_build_meta_generic(meta, &cpy_plugin_notification_meta, (void *)n);
+}
+
 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
   int ret;
   const data_set_t *ds;
@@ -910,6 +953,17 @@ PyTypeObject ValuesType = {
     Values_new       /* tp_new */
 };
 
+static char notification_meta_doc[] =
+    "These are the meta data for the Notification object.\n"
+    "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
+    "strings. int and long objects will be dispatched as signed integers "
+    "unless\n"
+    "they are between 2**63 and 2**64-1, which will result in an unsigned "
+    "integer.\n"
+    "One of these storage classes can be forced by using the classes\n"
+    "collectd.Signed and collectd.Unsigned. A meta object received by a\n"
+    "notification callback will always contain Signed or Unsigned objects.";
+
 static char severity_doc[] =
     "The severity of this notification. Assign or compare to\n"
     "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
@@ -931,16 +985,17 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
   int severity = 0;
   double time = 0;
   char *message = NULL;
+  PyObject *meta = NULL;
   char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
        *plugin = NULL, *host = NULL;
-  static char *kwlist[] = {"type",          "message",  "plugin_instance",
-                           "type_instance", "plugin",   "host",
-                           "time",          "severity", NULL};
+  static char *kwlist[] = {
+      "type", "message", "plugin_instance", "type_instance", "plugin",
+      "host", "time",    "severity",        "meta",          NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
-                                   &type, NULL, &message, NULL,
-                                   &plugin_instance, NULL, &type_instance, NULL,
-                                   &plugin, NULL, &host, &time, &severity))
+  if (!PyArg_ParseTupleAndKeywords(
+          args, kwds, "|etetetetetetdiO", kwlist, NULL, &type, NULL, &message,
+          NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL,
+          &host, &time, &severity, &meta))
     return -1;
 
   if (type && plugin_get_ds(type) == NULL) {
@@ -963,6 +1018,18 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
 
   FreeAll();
   PyMem_Free(message);
+
+  if (meta == NULL) {
+    meta = PyDict_New();
+    PyErr_Clear();
+  } else {
+    Py_INCREF(meta);
+  }
+
+  PyObject *tmp = self->meta;
+  self->meta = meta;
+  Py_XDECREF(tmp);
+
   return 0;
 }
 
@@ -972,18 +1039,19 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args,
   const data_set_t *ds;
   notification_t notification;
   double t = self->data.time;
+  PyObject *meta = self->meta;
   int severity = self->severity;
   char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
        *type_instance = NULL;
   char *message = NULL;
 
-  static char *kwlist[] = {"type",          "message",  "plugin_instance",
-                           "type_instance", "plugin",   "host",
-                           "time",          "severity", NULL};
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
+  static char *kwlist[] = {
+      "type", "message", "plugin_instance", "type_instance", "plugin",
+      "host", "time",    "severity",        "meta",          NULL};
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
                                    &type, NULL, &message, NULL,
                                    &plugin_instance, NULL, &type_instance, NULL,
-                                   &plugin, NULL, &host, &t, &severity))
+                                   &plugin, NULL, &host, &t, &severity, &meta))
     return NULL;
 
   notification.time = DOUBLE_TO_CDTIME_T(t);
@@ -1015,6 +1083,11 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args,
     PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
     return NULL;
   }
+  if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
+    PyErr_Format(PyExc_TypeError, "meta must be a dict");
+    return NULL;
+  }
+  cpy_build_notification_meta(&notification, meta);
 
   if (notification.time == 0)
     notification.time = cdtime();
@@ -1024,6 +1097,8 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args,
     sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
   Py_BEGIN_ALLOW_THREADS;
   ret = plugin_dispatch_notification(&notification);
+  if (notification.meta)
+    plugin_notification_meta_free(notification.meta);
   Py_END_ALLOW_THREADS;
   if (ret != 0) {
     PyErr_SetString(PyExc_RuntimeError,
@@ -1041,6 +1116,7 @@ static PyObject *Notification_new(PyTypeObject *type, PyObject *args,
   if (self == NULL)
     return NULL;
 
+  self->meta = PyDict_New();
   self->message[0] = 0;
   self->severity = 0;
   return (PyObject *)self;
@@ -1068,17 +1144,21 @@ static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
 
 static PyObject *Notification_repr(PyObject *s) {
   PyObject *ret, *tmp;
-  static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
+  static PyObject *l_severity = NULL, *l_message = NULL, *l_meta = NULL,
+                  *l_closing = NULL;
   Notification *self = (Notification *)s;
 
   if (l_severity == NULL)
     l_severity = cpy_string_to_unicode_or_bytes(",severity=");
   if (l_message == NULL)
     l_message = cpy_string_to_unicode_or_bytes(",message=");
+  if (l_meta == NULL)
+    l_meta = cpy_string_to_unicode_or_bytes(",meta=");
   if (l_closing == NULL)
     l_closing = cpy_string_to_unicode_or_bytes(")");
 
-  if (l_severity == NULL || l_message == NULL || l_closing == NULL)
+  if (l_severity == NULL || l_message == NULL || l_meta == NULL ||
+      l_closing == NULL)
     return NULL;
 
   ret = cpy_common_repr(s);
@@ -1094,10 +1174,33 @@ static PyObject *Notification_repr(PyObject *s) {
     CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
     CPY_STRCAT_AND_DEL(&ret, tmp);
   }
+  if (self->meta &&
+      (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
+    CPY_STRCAT(&ret, l_meta);
+    tmp = PyObject_Repr(self->meta);
+    CPY_STRCAT_AND_DEL(&ret, tmp);
+  }
   CPY_STRCAT(&ret, l_closing);
   return ret;
 }
 
+static int Notification_traverse(PyObject *self, visitproc visit, void *arg) {
+  Notification *n = (Notification *)self;
+  Py_VISIT(n->meta);
+  return 0;
+}
+
+static int Notification_clear(PyObject *self) {
+  Notification *n = (Notification *)self;
+  Py_CLEAR(n->meta);
+  return 0;
+}
+
+static void Notification_dealloc(PyObject *self) {
+  Notification_clear(self);
+  self->ob_type->tp_free(self);
+}
+
 static PyMethodDef Notification_methods[] = {
     {"dispatch", (PyCFunction)Notification_dispatch,
      METH_VARARGS | METH_KEYWORDS, dispatch_doc},
@@ -1105,6 +1208,8 @@ static PyMethodDef Notification_methods[] = {
 
 static PyMemberDef Notification_members[] = {
     {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
+    {"meta", T_OBJECT_EX, offsetof(Notification, meta), 0,
+     notification_meta_doc},
     {NULL}};
 
 static PyGetSetDef Notification_getseters[] = {
@@ -1113,43 +1218,43 @@ static PyGetSetDef Notification_getseters[] = {
     {NULL}};
 
 PyTypeObject NotificationType = {
-    CPY_INIT_TYPE "collectd.Notification",    /* tp_name */
-    sizeof(Notification),                     /* tp_basicsize */
-    0,                                        /* Will be filled in later */
-    0,                                        /* tp_dealloc */
-    0,                                        /* tp_print */
-    0,                                        /* tp_getattr */
-    0,                                        /* tp_setattr */
-    0,                                        /* tp_compare */
-    Notification_repr,                        /* tp_repr */
-    0,                                        /* tp_as_number */
-    0,                                        /* tp_as_sequence */
-    0,                                        /* tp_as_mapping */
-    0,                                        /* tp_hash */
-    0,                                        /* tp_call */
-    0,                                        /* tp_str */
-    0,                                        /* tp_getattro */
-    0,                                        /* tp_setattro */
-    0,                                        /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
-    Notification_doc,                         /* tp_doc */
-    0,                                        /* tp_traverse */
-    0,                                        /* tp_clear */
-    0,                                        /* tp_richcompare */
-    0,                                        /* tp_weaklistoffset */
-    0,                                        /* tp_iter */
-    0,                                        /* tp_iternext */
-    Notification_methods,                     /* tp_methods */
-    Notification_members,                     /* tp_members */
-    Notification_getseters,                   /* tp_getset */
-    0,                                        /* tp_base */
-    0,                                        /* tp_dict */
-    0,                                        /* tp_descr_get */
-    0,                                        /* tp_descr_set */
-    0,                                        /* tp_dictoffset */
-    Notification_init,                        /* tp_init */
-    0,                                        /* tp_alloc */
-    Notification_new                          /* tp_new */
+    CPY_INIT_TYPE "collectd.Notification", /* tp_name */
+    sizeof(Notification),                  /* tp_basicsize */
+    0,                                     /* Will be filled in later */
+    Notification_dealloc,                  /* tp_dealloc */
+    0,                                     /* tp_print */
+    0,                                     /* tp_getattr */
+    0,                                     /* tp_setattr */
+    0,                                     /* tp_compare */
+    Notification_repr,                     /* tp_repr */
+    0,                                     /* tp_as_number */
+    0,                                     /* tp_as_sequence */
+    0,                                     /* tp_as_mapping */
+    0,                                     /* tp_hash */
+    0,                                     /* tp_call */
+    0,                                     /* tp_str */
+    0,                                     /* tp_getattro */
+    0,                                     /* tp_setattro */
+    0,                                     /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+    Notification_doc,                                              /* tp_doc */
+    Notification_traverse,  /* tp_traverse */
+    Notification_clear,     /* tp_clear */
+    0,                      /* tp_richcompare */
+    0,                      /* tp_weaklistoffset */
+    0,                      /* tp_iter */
+    0,                      /* tp_iternext */
+    Notification_methods,   /* tp_methods */
+    Notification_members,   /* tp_members */
+    Notification_getseters, /* tp_getset */
+    0,                      /* tp_base */
+    0,                      /* tp_dict */
+    0,                      /* tp_descr_get */
+    0,                      /* tp_descr_set */
+    0,                      /* tp_dictoffset */
+    Notification_init,      /* tp_init */
+    0,                      /* tp_alloc */
+    Notification_new        /* tp_new */
 };
 
 static char Signed_doc[] =
index 2dfa87a..5f42561 100644 (file)
 /*
  * Private types
  */
-struct rrd_cache_s {
+typedef struct rrd_cache_s {
   int values_num;
   char **values;
   cdtime_t first_value;
   cdtime_t last_value;
   int64_t random_variation;
   enum { FLAG_NONE = 0x00, FLAG_QUEUED = 0x01, FLAG_FLUSHQ = 0x02 } flags;
-};
-typedef struct rrd_cache_s rrd_cache_t;
+} rrd_cache_t;
 
 enum rrd_queue_dir_e { QUEUE_INSERT_FRONT, QUEUE_INSERT_BACK };
 typedef enum rrd_queue_dir_e rrd_queue_dir_t;
@@ -110,13 +109,10 @@ static int do_shutdown = 0;
 #if HAVE_THREADSAFE_LIBRRD
 static int srrd_update(char *filename, char *template, int argc,
                        const char **argv) {
-  int status;
-
   optind = 0; /* bug in librrd? */
   rrd_clear_error();
 
-  status = rrd_update_r(filename, template, argc, (void *)argv);
-
+  int status = rrd_update_r(filename, template, argc, (void *)argv);
   if (status != 0) {
     WARNING("rrdtool plugin: rrd_update_r (%s) failed: %s", filename,
             rrd_get_error());
@@ -794,10 +790,6 @@ static int rrd_compare_numeric(const void *a_ptr, const void *b_ptr) {
 
 static int rrd_write(const data_set_t *ds, const value_list_t *vl,
                      user_data_t __attribute__((unused)) * user_data) {
-  struct stat statbuf;
-  char filename[512];
-  char values[512];
-  int status;
 
   if (do_shutdown)
     return 0;
@@ -807,33 +799,34 @@ static int rrd_write(const data_set_t *ds, const value_list_t *vl,
     return -1;
   }
 
+  char filename[PATH_MAX];
   if (value_list_to_filename(filename, sizeof(filename), vl) != 0)
     return -1;
 
+  char values[32 * ds->ds_num];
   if (value_list_to_string(values, sizeof(values), ds, vl) != 0)
     return -1;
 
+  struct stat statbuf = {0};
   if (stat(filename, &statbuf) == -1) {
     if (errno == ENOENT) {
-      status = cu_rrd_create_file(filename, ds, vl, &rrdcreate_config);
-      if (status != 0)
+      if (cu_rrd_create_file(filename, ds, vl, &rrdcreate_config) != 0) {
         return -1;
-      else if (rrdcreate_config.async)
+      } else if (rrdcreate_config.async) {
         return 0;
+      }
     } else {
       char errbuf[1024];
-      ERROR("stat(%s) failed: %s", filename,
+      ERROR("rrdtool plugin: stat(%s) failed: %s", filename,
             sstrerror(errno, errbuf, sizeof(errbuf)));
       return -1;
     }
   } else if (!S_ISREG(statbuf.st_mode)) {
-    ERROR("stat(%s): Not a regular file!", filename);
+    ERROR("rrdtool plugin: stat(%s): Not a regular file!", filename);
     return -1;
   }
 
-  status = rrd_cache_insert(filename, values, vl->time);
-
-  return status;
+  return rrd_cache_insert(filename, values, vl->time);
 } /* int rrd_write */
 
 static int rrd_flush(cdtime_t timeout, const char *identifier,
@@ -1030,7 +1023,6 @@ static int rrd_shutdown(void) {
 
 static int rrd_init(void) {
   static int init_once = 0;
-  int status;
 
   if (init_once != 0)
     return 0;
@@ -1054,7 +1046,8 @@ static int rrd_init(void) {
     random_timeout = 0;
     cache_flush_timeout = 0;
   } else if (cache_flush_timeout < cache_timeout) {
-    INFO("rrdtool plugin: \"CacheFlush %.3f\" is less than \"CacheTimeout %.3f\". "
+    INFO("rrdtool plugin: \"CacheFlush %.3f\" is less than \"CacheTimeout "
+         "%.3f\". "
          "Ajusting \"CacheFlush\" to %.3f seconds.",
          CDTIME_T_TO_DOUBLE(cache_flush_timeout),
          CDTIME_T_TO_DOUBLE(cache_timeout),
@@ -1071,7 +1064,7 @@ static int rrd_init(void) {
 
   pthread_mutex_unlock(&cache_lock);
 
-  status =
+  int status =
       plugin_thread_create(&queue_thread, /* attr = */ NULL, rrd_queue_thread,
                            /* args = */ NULL, "rrdtool queue");
   if (status != 0) {
index 1ac65c8..0a20e34 100644 (file)
@@ -63,7 +63,7 @@ struct data_definition_s {
   struct data_definition_s *next;
   char **ignores;
   size_t ignores_len;
-  int invert_match;
+  _Bool invert_match;
 };
 typedef struct data_definition_s data_definition_t;
 
@@ -71,6 +71,8 @@ struct host_definition_s {
   char *name;
   char *address;
   int version;
+  cdtime_t timeout;
+  int retries;
 
   /* snmpv1/2 options */
   char *community;
@@ -327,29 +329,14 @@ static int csnmp_config_add_data_blacklist(data_definition_t *dd,
   return 0;
 } /* int csnmp_config_add_data_blacklist */
 
-static int csnmp_config_add_data_blacklist_match_inverted(data_definition_t *dd,
-                                                          oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
-    WARNING("snmp plugin: `InvertMatch' needs exactly one boolean argument.");
-    return -1;
-  }
-
-  dd->invert_match = ci->values[0].value.boolean ? 1 : 0;
-
-  return 0;
-} /* int csnmp_config_add_data_blacklist_match_inverted */
-
 static int csnmp_config_add_data(oconfig_item_t *ci) {
-  data_definition_t *dd;
-  int status = 0;
-
-  dd = calloc(1, sizeof(*dd));
+  data_definition_t *dd = calloc(1, sizeof(*dd));
   if (dd == NULL)
     return -1;
 
-  status = cf_util_get_string(ci, &dd->name);
+  int status = cf_util_get_string(ci, &dd->name);
   if (status != 0) {
-    free(dd);
+    sfree(dd);
     return -1;
   }
 
@@ -376,7 +363,7 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
     else if (strcasecmp("Ignore", option->key) == 0)
       status = csnmp_config_add_data_blacklist(dd, option);
     else if (strcasecmp("InvertMatch", option->key) == 0)
-      status = csnmp_config_add_data_blacklist_match_inverted(dd, option);
+      status = cf_util_get_boolean(option, &dd->invert_match);
     else {
       WARNING("snmp plugin: Option `%s' not allowed here.", option->key);
       status = -1;
@@ -597,6 +584,10 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
   hd->sess_handle = NULL;
   hd->interval = 0;
 
+  /* These mean that we have not set a timeout or retry value */
+  hd->timeout = 0;
+  hd->retries = -1;
+
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
     status = 0;
@@ -607,6 +598,10 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
       status = cf_util_get_string(option, &hd->community);
     else if (strcasecmp("Version", option->key) == 0)
       status = csnmp_config_add_host_version(hd, option);
+    else if (strcasecmp("Timeout", option->key) == 0)
+      cf_util_get_cdtime(option, &hd->timeout);
+    else if (strcasecmp("Retries", option->key) == 0)
+      cf_util_get_int(option, &hd->retries);
     else if (strcasecmp("Collect", option->key) == 0)
       csnmp_config_add_host_collect(hd, option);
     else if (strcasecmp("Interval", option->key) == 0)
@@ -803,6 +798,15 @@ static void csnmp_host_open_session(host_definition_t *host) {
     sess.community_len = strlen(host->community);
   }
 
+  /* Set timeout & retries, if they have been changed from the default */
+  if (host->timeout != 0) {
+    /* net-snmp expects microseconds */
+    sess.timeout = CDTIME_T_TO_US(host->timeout);
+  }
+  if (host->retries >= 0) {
+    sess.retries = host->retries;
+  }
+
   /* snmp_sess_open will copy the `struct snmp_session *'. */
   host->sess_handle = snmp_sess_open(&sess);
 
@@ -1032,7 +1036,6 @@ static int csnmp_instance_list_add(csnmp_list_instances_t **head,
   struct variable_list *vb;
   oid_t vb_name;
   int status;
-  uint32_t is_matched;
 
   /* Set vb on the last variable */
   for (vb = res->variables; (vb != NULL) && (vb->next_variable != NULL);
@@ -1062,11 +1065,11 @@ static int csnmp_instance_list_add(csnmp_list_instances_t **head,
     char *ptr;
 
     csnmp_strvbcopy(il->instance, vb, sizeof(il->instance));
-    is_matched = 0;
+    _Bool is_matched = 0;
     for (uint32_t i = 0; i < dd->ignores_len; i++) {
       status = fnmatch(dd->ignores[i], il->instance, 0);
       if (status == 0) {
-        if (dd->invert_match == 0) {
+        if (!dd->invert_match) {
           sfree(il);
           return 0;
         } else {
@@ -1075,7 +1078,7 @@ static int csnmp_instance_list_add(csnmp_list_instances_t **head,
         }
       }
     }
-    if (dd->invert_match != 0 && is_matched == 0) {
+    if (dd->invert_match && !is_matched) {
       sfree(il);
       return 0;
     }
@@ -1521,7 +1524,6 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
     snmp_free_pdu(res);
   res = NULL;
 
-
   if (status == 0)
     csnmp_dispatch_table(host, data, instance_list_head, value_list_head);
 
index 94b4e3a..3b64b75 100644 (file)
--- a/src/ted.c
+++ b/src/ted.c
@@ -88,7 +88,7 @@ static int ted_read_value(double *ret_power, double *ret_voltage) {
 
   status = write(fd, pkt_request, sizeof(pkt_request));
   if (status <= 0) {
-    ERROR("ted plugin: swrite failed.");
+    ERROR("ted plugin: write failed.");
     return -1;
   }
 
index 0128c57..460f807 100644 (file)
@@ -183,7 +183,8 @@ static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
                                   const data_set_t *ds, const value_list_t *vl,
                                   int store_rates,
                                   char const *const *http_attrs,
-                                  size_t http_attrs_num, int data_ttl) {
+                                  size_t http_attrs_num, int data_ttl,
+                                  char const *metrics_prefix) {
   char temp[512];
   size_t offset = 0;
   int status;
@@ -212,11 +213,13 @@ static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
   for (size_t i = 0; i < ds->ds_num; i++) {
     /* All value lists have a leading comma. The first one will be replaced with
      * a square bracket in `format_kairosdb_finalize'. */
-    BUFFER_ADD(",{");
+    BUFFER_ADD(",{\"name\":\"");
 
-    BUFFER_ADD("\"name\":\"collectd");
+    if (metrics_prefix != NULL) {
+      BUFFER_ADD("%s.", metrics_prefix);
+    }
 
-    BUFFER_ADD(".%s", vl->plugin);
+    BUFFER_ADD("%s", vl->plugin);
 
     status = values_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, i);
     if (status != 0)
@@ -263,12 +266,14 @@ static int format_kairosdb_value_list_nocheck(
     char *buffer, /* {{{ */
     size_t *ret_buffer_fill, size_t *ret_buffer_free, const data_set_t *ds,
     const value_list_t *vl, int store_rates, size_t temp_size,
-    char const *const *http_attrs, size_t http_attrs_num, int data_ttl) {
+    char const *const *http_attrs, size_t http_attrs_num, int data_ttl,
+    char const *metrics_prefix) {
   char temp[temp_size];
   int status;
 
   status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates,
-                                  http_attrs, http_attrs_num, data_ttl);
+                                  http_attrs, http_attrs_num, data_ttl,
+                                  metrics_prefix);
   if (status != 0)
     return status;
   temp_size = strlen(temp);
@@ -337,7 +342,8 @@ int format_kairosdb_value_list(char *buffer, /* {{{ */
                                size_t *ret_buffer_fill, size_t *ret_buffer_free,
                                const data_set_t *ds, const value_list_t *vl,
                                int store_rates, char const *const *http_attrs,
-                               size_t http_attrs_num, int data_ttl) {
+                               size_t http_attrs_num, int data_ttl,
+                               char const *metrics_prefix) {
   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
       (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
     return -EINVAL;
@@ -347,7 +353,8 @@ int format_kairosdb_value_list(char *buffer, /* {{{ */
 
   return format_kairosdb_value_list_nocheck(
       buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates,
-      (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl);
+      (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl,
+      metrics_prefix);
 } /* }}} int format_kairosdb_value_list */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 3a4c7c7..7b9e0e7 100644 (file)
@@ -41,7 +41,8 @@ int format_kairosdb_value_list(char *buffer, size_t *ret_buffer_fill,
                                size_t *ret_buffer_free, const data_set_t *ds,
                                const value_list_t *vl, int store_rates,
                                char const *const *http_attrs,
-                               size_t http_attrs_num, int data_ttl);
+                               size_t http_attrs_num, int data_ttl,
+                               char const *metrics_prefix);
 int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill,
                              size_t *ret_buffer_free);
 
index d57413c..e4daf4b 100644 (file)
@@ -88,6 +88,7 @@ struct user_config_s {
   _Bool collect_mgt;
   _Bool collect_smf;
   _Bool collect_vbe;
+  _Bool collect_mse;
 #endif
 };
 typedef struct user_config_s user_config_t; /* }}} */
@@ -227,6 +228,9 @@ static int varnish_monitor(void *priv,
     else if (strcmp(name, "esi_warnings") == 0)
       return varnish_submit_derive(conf->instance, "esi", "total_operations",
                                    "warning", val);
+    else if (strcmp(name, "esi_maxdepth") == 0)
+      return varnish_submit_derive(conf->instance, "esi", "total_operations",
+                                   "max_depth", val);
   }
 
   if (conf->collect_backend) {
@@ -588,12 +592,18 @@ static int varnish_monitor(void *priv,
     else if (strcmp(name, "s_req_bodybytes") == 0)
       return varnish_submit_derive(conf->instance, "totals", "total_bytes",
                                    "req_body", val);
+    else if (strcmp(name, "s_req_protobytes") == 0)
+      return varnish_submit_derive(conf->instance, "totals", "total_bytes",
+                                   "req_proto", val);
     else if (strcmp(name, "s_resp_hdrbytes") == 0)
       return varnish_submit_derive(conf->instance, "totals", "total_bytes",
                                    "resp_header", val);
     else if (strcmp(name, "s_resp_bodybytes") == 0)
       return varnish_submit_derive(conf->instance, "totals", "total_bytes",
                                    "resp_body", val);
+    else if (strcmp(name, "s_resp_protobytes") == 0)
+      return varnish_submit_derive(conf->instance, "totals", "total_bytes",
+                                   "resp_proto", val);
     else if (strcmp(name, "s_pipe_hdrbytes") == 0)
       return varnish_submit_derive(conf->instance, "totals", "total_bytes",
                                    "pipe_header", val);
@@ -694,7 +704,6 @@ static int varnish_monitor(void *priv,
     else if (strcmp(name, "busy_killed") == 0)
       return varnish_submit_derive(conf->instance, "workers", "http_requests",
                                    "busy_killed", val);
-
 #endif
   }
 
@@ -727,12 +736,18 @@ static int varnish_monitor(void *priv,
     else if (strcmp(name, "bereq_bodybytes") == 0)
       return varnish_submit_derive(conf->instance, "vbe",
                                    "total_bytes", "bereq_bodybytes", val);
+    else if (strcmp(name, "bereq_protobytes") == 0)
+      return varnish_submit_derive(conf->instance, "vbe",
+                                   "total_bytes", "bereq_protobytes", val);
     else if (strcmp(name, "beresp_hdrbytes") == 0)
       return varnish_submit_derive(conf->instance, "vbe",
                                    "total_bytes", "beresp_hdrbytes", val);
     else if (strcmp(name, "beresp_bodybytes") == 0)
       return varnish_submit_derive(conf->instance, "vbe",
                                    "total_bytes", "beresp_bodybytes", val);
+    else if (strcmp(name, "beresp_protobytes") == 0)
+      return varnish_submit_derive(conf->instance, "vbe",
+                                   "total_bytes", "beresp_protobytes", val);
     else if (strcmp(name, "pipe_hdrbytes") == 0)
       return varnish_submit_derive(conf->instance, "vbe",
                                    "total_bytes", "pipe_hdrbytes", val);
@@ -751,13 +766,15 @@ static int varnish_monitor(void *priv,
   }
 
   /* All Stevedores support these counters */
-  if (conf->collect_sma || conf->collect_smf) {
+  if (conf->collect_sma || conf->collect_smf || conf->collect_mse) {
 
     char category[4];
     if (conf->collect_sma)
       strncpy(category, "sma", 4);
-    else
+    else if (conf->collect_smf)
       strncpy(category, "smf", 4);
+    else
+      strncpy(category, "mse", 4);
 
     if (strcmp(name, "c_req") == 0)
       return varnish_submit_derive(conf->instance, category,
@@ -867,6 +884,99 @@ static int varnish_monitor(void *priv,
       return varnish_submit_gauge(conf->instance, "mempool",
                                    "objects", "ran_dry", val);
   }
+
+  if (conf->collect_mse) {
+    if (strcmp(name, "c_full") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_operations", "full_allocs", val);
+    else if (strcmp(name, "c_truncated") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_operations", "truncated_allocs", val);
+    else if (strcmp(name, "c_expanded") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_operations", "expanded_allocs", val);
+    else if (strcmp(name, "c_failed") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_operations", "failed_allocs", val);
+    else if (strcmp(name, "c_bytes") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_bytes", "bytes_allocated", val);
+    else if (strcmp(name, "c_freed") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_bytes", "bytes_freed", val);
+    else if (strcmp(name, "g_fo_alloc") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_operations", "fo_allocs_outstanding", val);
+    else if (strcmp(name, "g_fo_bytes") == 0)
+      return varnish_submit_gauge(conf->instance, "mse",
+                                  "bytes", "fo_bytes_outstanding", val);
+    else if (strcmp(name, "g_membuf_alloc") == 0)
+      return varnish_submit_gauge(conf->instance, "mse",
+                                  "objects", "membufs_allocated", val);
+    else if (strcmp(name, "g_membuf_inuse") == 0)
+      return varnish_submit_gauge(conf->instance, "mse",
+                                  "objects", "membufs_inuse", val);
+    else if (strcmp(name, "g_bans_bytes") == 0)
+      return varnish_submit_gauge(conf->instance, "mse",
+                                  "bytes", "persisted_banspace_used", val);
+    else if (strcmp(name, "g_bans_space") == 0)
+      return varnish_submit_gauge(conf->instance, "mse",
+                                  "bytes", "persisted_banspace_available", val);
+    else if (strcmp(name, "g_bans_persisted") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_operations", "bans_persisted", val);
+    else if (strcmp(name, "g_bans_lost") == 0)
+      return varnish_submit_derive(conf->instance, "mse",
+                                  "total_operations", "bans_lost", val);
+
+     /* mse seg */
+    else if (strcmp(name, "g_journal_bytes") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_reg",
+                                  "bytes", "journal_bytes_used", val);
+    else if (strcmp(name, "g_journal_space") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_reg",
+                                  "bytes", "journal_bytes_free", val);
+
+    /* mse segagg */
+    else if (strcmp(name, "g_bigspace") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_segagg",
+                                  "bytes", "big_extents_bytes_available", val);
+    else if (strcmp(name, "g_extfree") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_segagg",
+                                  "objects", "free_extents", val);
+    else if (strcmp(name, "g_sparenode") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_segagg",
+                                  "objects", "spare_nodes_available", val);
+    else if (strcmp(name, "g_objnode") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_segagg",
+                                  "objects", "object_nodes_in_use", val);
+    else if (strcmp(name, "g_extnode") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_segagg",
+                                  "objects", "extent_nodes_in_use", val);
+    else if (strcmp(name, "g_bigextfree") == 0)
+      return varnish_submit_gauge(conf->instance, "mse_segagg",
+                                  "objects", "free_big_extents", val);
+    else if (strcmp(name, "c_pruneloop") == 0)
+      return varnish_submit_derive(conf->instance, "mse_segagg",
+                                  "total_operations", "prune_loops", val);
+    else if (strcmp(name, "c_pruned") == 0)
+      return varnish_submit_derive(conf->instance, "mse_segagg",
+                                  "total_objects", "pruned_objects", val);
+    else if (strcmp(name, "c_spared") == 0)
+      return varnish_submit_derive(conf->instance, "mse_segagg",
+                                  "total_operations", "spared_objects", val);
+    else if (strcmp(name, "c_skipped") == 0)
+      return varnish_submit_derive(conf->instance, "mse_segagg",
+                                  "total_operations", "missed_objects", val);
+    else if (strcmp(name, "c_nuked") == 0)
+      return varnish_submit_derive(conf->instance, "mse_segagg",
+                                  "total_operations", "nuked_objects", val);
+    else if (strcmp(name, "c_sniped") == 0)
+      return varnish_submit_derive(conf->instance, "mse_segagg",
+                                  "total_operations", "sniped_objects", val);
+
+  }
+
 #endif
 
   return 0;
@@ -1355,6 +1465,7 @@ static int varnish_config_apply_default(user_config_t *conf) /* {{{ */
   conf->collect_mgt = 0;
   conf->collect_smf = 0;
   conf->collect_vbe = 0;
+  conf->collect_mse = 0;
 #endif
 
   return 0;
@@ -1543,6 +1654,13 @@ static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */
       WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
               child->key, "v4");
 #endif
+    else if (strcasecmp("CollectMSE", child->key) == 0)
+#if HAVE_VARNISH_V4
+      cf_util_get_boolean(child, &conf->collect_mse);
+#else
+      WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+              child->key, "Plus v4");
+#endif
     else {
       WARNING("Varnish plugin: Ignoring unknown "
               "configuration option: \"%s\". Did "
@@ -1578,6 +1696,7 @@ static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */
 #if HAVE_VARNISH_V4
       && !conf->collect_vsm && !conf->collect_vbe && !conf->collect_smf
       && !conf->collect_mgt && !conf->collect_lck && !conf->collect_mempool
+      && !conf->collect_mse
 #endif
       ) {
     WARNING("Varnish plugin: No metric has been configured for "
index 06327ed..87e518b 100644 (file)
 #define WRITE_HTTP_DEFAULT_BUFFER_SIZE 4096
 #endif
 
+#ifndef WRITE_HTTP_DEFAULT_PREFIX
+#define WRITE_HTTP_DEFAULT_PREFIX "collectd"
+#endif
+
 /*
  * Private variables
  */
@@ -80,6 +84,7 @@ struct wh_callback_s {
   pthread_mutex_t send_lock;
 
   int data_ttl;
+  char *metrics_prefix;
 };
 typedef struct wh_callback_s wh_callback_t;
 
@@ -328,6 +333,7 @@ static void wh_callback_free(void *data) /* {{{ */
   sfree(cb->clientcert);
   sfree(cb->clientkeypass);
   sfree(cb->send_buffer);
+  sfree(cb->metrics_prefix);
 
   sfree(cb);
 } /* }}} void wh_callback_free */
@@ -476,7 +482,7 @@ static int wh_write_kairosdb(const data_set_t *ds,
   status = format_kairosdb_value_list(
       cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
       cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
-      cb->data_ttl);
+      cb->data_ttl, cb->metrics_prefix);
   if (status == -ENOMEM) {
     status = wh_flush_nolock(/* timeout = */ 0, cb);
     if (status != 0) {
@@ -488,7 +494,7 @@ static int wh_write_kairosdb(const data_set_t *ds,
     status = format_kairosdb_value_list(
         cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
         cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
-        cb->data_ttl);
+        cb->data_ttl, cb->metrics_prefix);
   }
   if (status != 0) {
     pthread_mutex_unlock(&cb->send_lock);
@@ -629,6 +635,13 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */
   cb->send_metrics = 1;
   cb->send_notifications = 0;
   cb->data_ttl = 0;
+  cb->metrics_prefix = strdup(WRITE_HTTP_DEFAULT_PREFIX);
+
+  if (cb->metrics_prefix == NULL) {
+    ERROR("write_http plugin: strdup failed.");
+    sfree(cb);
+    return -1;
+  }
 
   pthread_mutex_init(&cb->send_lock, /* attr = */ NULL);
 
@@ -740,6 +753,8 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */
       sfree(val);
     } else if (strcasecmp("TTL", child->key) == 0) {
       status = cf_util_get_int(child, &cb->data_ttl);
+    } else if (strcasecmp("Prefix", child->key) == 0) {
+      status = cf_util_get_string(child, &cb->metrics_prefix);
     } else {
       ERROR("write_http plugin: Invalid configuration "
             "option: %s.",
@@ -770,6 +785,9 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */
     return -1;
   }
 
+  if (strlen(cb->metrics_prefix) == 0)
+    sfree(cb->metrics_prefix);
+
   if (cb->low_speed_limit > 0)
     cb->low_speed_time = CDTIME_T_TO_TIME_T(plugin_get_interval());
 
index eac0d68..a005ffb 100644 (file)
 
 #include <microhttpd.h>
 
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
 #ifndef PROMETHEUS_DEFAULT_STALENESS_DELTA
 #define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T_STATIC(300)
 #endif
@@ -727,6 +731,90 @@ metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index,
 }
 /* }}} */
 
+#if MHD_VERSION >= 0x00090000
+static int prom_open_socket(int addrfamily) {
+  /* {{{ */
+  char service[NI_MAXSERV];
+  snprintf(service, sizeof(service), "%hu", httpd_port);
+
+  struct addrinfo *res;
+  int status = getaddrinfo(NULL, service,
+                           &(struct addrinfo){
+                               .ai_flags = AI_PASSIVE | AI_ADDRCONFIG,
+                               .ai_family = addrfamily,
+                               .ai_socktype = SOCK_STREAM,
+                           },
+                           &res);
+  if (status != 0) {
+    return -1;
+  }
+
+  int fd = -1;
+  for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) {
+    fd = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC, 0);
+    if (fd == -1)
+      continue;
+
+    if (bind(fd, ai->ai_addr, ai->ai_addrlen) != 0) {
+      close(fd);
+      fd = -1;
+      continue;
+    }
+
+    if (listen(fd, /* backlog = */ 16) != 0) {
+      close(fd);
+      fd = -1;
+      continue;
+    }
+
+    break;
+  }
+
+  freeaddrinfo(res);
+
+  return fd;
+} /* }}} int prom_open_socket */
+
+static struct MHD_Daemon *prom_start_daemon() {
+  /* {{{ */
+  int fd = prom_open_socket(PF_INET6);
+  if (fd == -1)
+    fd = prom_open_socket(PF_INET);
+  if (fd == -1) {
+    ERROR("write_prometheus plugin: Opening a listening socket failed.");
+    return NULL;
+  }
+
+  struct MHD_Daemon *d =
+      MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0,
+                       /* MHD_AcceptPolicyCallback = */ NULL,
+                       /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler,
+                       NULL, MHD_OPTION_LISTEN_SOCKET, fd, MHD_OPTION_END);
+  if (d == NULL) {
+    ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
+    close(fd);
+    return NULL;
+  }
+
+  return d;
+} /* }}} struct MHD_Daemon *prom_start_daemon */
+#else /* if MHD_VERSION < 0x00090000 */
+static struct MHD_Daemon *prom_start_daemon() {
+  /* {{{ */
+  struct MHD_Daemon *d =
+      MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0,
+                       /* MHD_AcceptPolicyCallback = */ NULL,
+                       /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler,
+                       NULL, MHD_OPTION_END);
+  if (d == NULL) {
+    ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
+    return NULL;
+  }
+
+  return d;
+} /* }}} struct MHD_Daemon *prom_start_daemon */
+#endif
+
 /*
  * collectd callbacks
  */
@@ -760,15 +848,7 @@ static int prom_init() {
   }
 
   if (httpd == NULL) {
-    unsigned int flags = MHD_USE_THREAD_PER_CONNECTION;
-#if MHD_VERSION >= 0x00093300
-    flags |= MHD_USE_DUAL_STACK;
-#endif
-
-    httpd = MHD_start_daemon(flags, httpd_port,
-                             /* MHD_AcceptPolicyCallback = */ NULL,
-                             /* MHD_AcceptPolicyCallback arg = */ NULL,
-                             http_handler, NULL, MHD_OPTION_END);
+    httpd = prom_start_daemon();
     if (httpd == NULL) {
       ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
       return -1;
index 10f636c..eb6ceb3 100644 (file)
@@ -111,7 +111,7 @@ static int wt_send_buffer(struct wt_callback *cb) {
   ssize_t status = 0;
 
   status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf));
-  if (status < 0) {
+  if (status != 0) {
     char errbuf[1024];
     ERROR("write_tsdb plugin: send failed with status %zi (%s)", status,
           sstrerror(errno, errbuf, sizeof(errbuf)));