Merge remote-tracking branch 'github/pr/2453'
authorFlorian Forster <octo@collectd.org>
Fri, 6 Oct 2017 13:32:01 +0000 (15:32 +0200)
committerFlorian Forster <octo@collectd.org>
Fri, 6 Oct 2017 13:32:01 +0000 (15:32 +0200)
25 files changed:
ChangeLog
contrib/curl_json/php-fpm.conf [new file with mode: 0644]
contrib/redhat/collectd.spec
src/ceph.c
src/collectd.conf.in
src/collectd.conf.pod
src/curl.c
src/curl_json.c
src/curl_xml.c
src/dbi.c
src/email.c
src/exec.c
src/intel_pmu.c
src/ipmi.c
src/memcachec.c
src/oracle.c
src/postgresql.c
src/processes.c
src/snmp.c
src/tail.c
src/tail_csv.c
src/unixsock.c
src/utils_match.c
src/varnish.c
src/write_prometheus.c

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.
diff --git a/contrib/curl_json/php-fpm.conf b/contrib/curl_json/php-fpm.conf
new file mode 100644 (file)
index 0000000..34b8b67
--- /dev/null
@@ -0,0 +1,27 @@
+# Example configuration for PHP-FPM
+<Plugin "curl_json">
+  <URL "http://nginx-status/php-fpm-status?json">
+    Plugin "phpfpm"
+    Instance "main"
+    <Key "accepted conn">
+      Type "total_requests"
+      Instance "accepted"
+    </Key>
+    <Key "slow requests">
+      Type "total_requests"
+      Instance "slow"
+    </Key>
+    <Key "listen queue">
+      Type "queue_length"
+      Instance "listen"
+    </Key>
+    <Key "active processes">
+      Type "vs_processes"
+      Instance "active"
+    </Key>
+    <Key "total processes">
+      Type "vs_processes"
+      Instance "total"
+    </Key>
+  </URL>
+</Plugin>
index 23cac9a..7862663 100644 (file)
@@ -16,7 +16,7 @@
 #
 # - fetch the desired collectd release file from https://collectd.org/files/
 #   and save it in your ~/rpmbuild/SOURCES/ directory (or build your own out of
-#   the git repository: ./build.sh && ./configure && make-dist-bz2)
+#   the git repository: ./build.sh && ./configure && make dist)
 #
 # - copy this file in your ~/rpmbuild/SPECS/ directory. Make sure the
 #   "Version:" tag matches the version from the tarball.
index c2284cb..8bee8e8 100644 (file)
@@ -418,7 +418,7 @@ static void ceph_daemon_free(struct ceph_daemon *d) {
 }
 
 /* compact_ds_name removed the special characters ":", "_", "-" and "+" from the
- * intput string. Characters following these special characters are capitalized.
+ * input string. Characters following these special characters are capitalized.
  * Trailing "+" and "-" characters are replaces with the strings "Plus" and
  * "Minus". */
 static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) {
index 4db9f9f..826b486 100644 (file)
 #<Plugin curl_xml>
 #  <URL "http://localhost/stats.xml">
 #    Host "my_host"
+#    #Plugin "stats"
 #    Instance "some_instance"
 #    User "collectd"
 #    Password "thaiNg0I"
 #      Type "magic_level"
 #      #InstancePrefix "prefix-"
 #      InstanceFrom "td[1]"
+#      #PluginInstanceFrom "td[1]"
 #      ValuesFrom "td[2]/span[@class=\"level\"]"
 #    </XPath>
 #  </URL>
 #              </Result>
 #      </Query>
 #      <Database "customers_db">
+#              #Plugin "mycompany"
 #              Driver "mysql"
 #              DriverOption "host" "localhost"
 #              DriverOption "username" "collectd"
 #      NotifySensorAdd false
 #      NotifySensorRemove true
 #      NotifySensorNotPresent false
+#      SELEnabled false
+#      SELClearEvent false
 #</Plugin>
 
 #<Plugin iptables>
 #    </Result>
 #  </Query>
 #  <Database "product_information">
+#    #Plugin "warehouse"
 #    ConnectID "db01"
 #    Username "oracle"
 #    Password "secret"
 #              StoreRates true
 #      </Writer>
 #      <Database foo>
+#              #Plugin "kingdom"
 #              Host "hostname"
 #              Port "5432"
 #              User "username"
 #<Plugin processes>
 #      CollectFileDescriptor true
 #      CollectContextSwitch true
+#      CollectMemoryMaps true
 #      Process "name"
 #      ProcessMatch "name" "regex"
 #      <Process "collectd">
index c33243c..7b037bc 100644 (file)
@@ -1624,6 +1624,7 @@ finance page and dispatch the value to collectd.
 
   <Plugin curl>
     <Page "stock_quotes">
+      Plugin "quotes"
       URL "http://finance.google.com/finance?q=NYSE%3AAMD"
       User "foo"
       Password "bar"
@@ -1655,6 +1656,11 @@ The following options are valid within B<Page> blocks:
 
 =over 4
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<curl>.
+
 =item B<URL> I<URL>
 
 URL of the web site to retrieve. Since a regular expression will be used to
@@ -1815,6 +1821,11 @@ The following options are valid within B<URL> blocks:
 Use I<Name> as the host name when submitting values. Defaults to the global
 host name setting.
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<curl_json>.
+
 =item B<Instance> I<Instance>
 
 Sets the plugin instance to I<Instance>.
@@ -1877,6 +1888,7 @@ The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
  <Plugin "curl_xml">
    <URL "http://localhost/stats.xml">
      Host "my_host"
+     #Plugin "curl_xml"
      Instance "some_instance"
      User "collectd"
      Password "thaiNg0I"
@@ -1890,6 +1902,7 @@ The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
        Type "magic_level"
        #InstancePrefix "prefix-"
        InstanceFrom "td[1]"
+       #PluginInstanceFrom "td[1]"
        ValuesFrom "td[2]/span[@class=\"level\"]"
      </XPath>
    </URL>
@@ -1915,10 +1928,16 @@ Within the B<URL> block the following options are accepted:
 Use I<Name> as the host name when submitting values. Defaults to the global
 host name setting.
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to 'curl_xml'.
+
 =item B<Instance> I<Instance>
 
-Use I<Instance> as the plugin instance when submitting values. Defaults to an
-empty string (no plugin instance).
+Use I<Instance> as the plugin instance when submitting values.
+May be overridden by B<PluginInstanceFrom> option inside B<XPath> blocks.
+Defaults to an empty string (no plugin instance).
 
 =item B<Namespace> I<Prefix> I<URL>
 
@@ -1989,9 +2008,19 @@ Specifies a XPath expression to use for determining the I<type instance>. The
 XPath expression must return exactly one element. The element's value is then
 used as I<type instance>, possibly prefixed with I<InstancePrefix> (see above).
 
-This value is required. As a special exception, if the "base XPath expression"
-(the argument to the B<XPath> block) returns exactly one argument, then this
-option may be omitted.
+=item B<PluginInstanceFrom> I<PluginInstanceFrom>
+
+Specifies a XPath expression to use for determining the I<plugin instance>. The
+XPath expression must return exactly one element. The element's value is then
+used as I<plugin instance>.
+
+=back
+
+If the "base XPath expression" (the argument to the B<XPath> block) returns
+exactly one argument, then I<InstanceFrom> and I<PluginInstanceFrom> may be omitted.
+Otherwise, at least one of I<InstanceFrom> or I<PluginInstanceFrom> is required.
+
+=over 4
 
 =item B<ValuesFrom> I<ValuesFrom> [I<ValuesFrom> ...]
 
@@ -2000,6 +2029,7 @@ number of XPath expressions must match the number of data sources in the
 I<type> specified with B<Type> (see above). Each XPath expression must return
 exactly one element. The element's value is then parsed as a number and used as
 value for the appropriate value in the value list dispatched to the daemon.
+This option is required.
 
 =back
 
@@ -2030,6 +2060,7 @@ than those of other plugins. It usually looks something like this:
       </Result>
     </Query>
     <Database "product_information">
+      #Plugin "warehouse"
       Driver "mysql"
       Interval 120
       DriverOption "host" "localhost"
@@ -2211,6 +2242,11 @@ the daemon. Other than that, that name is not used.
 
 =over 4
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting query results from
+this B<Database>. Defaults to C<dbi>.
+
 =item B<Interval> I<Interval>
 
 Sets the interval (in seconds) in which the values will be collected from this
@@ -3345,6 +3381,19 @@ 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<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>
@@ -3673,6 +3722,7 @@ Synopsis of the configuration:
    <Page "plugin_instance">
      Server "localhost"
      Key "page_key"
+     Plugin "plugin_name"
      <Match>
        Regex "(\\d+) bytes sent"
        DSType CounterAdd
@@ -3700,6 +3750,11 @@ B<Page> block.
 
 When connected to the memcached server, asks for the page I<Key>.
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<memcachec>.
+
 =item E<lt>B<Match>E<gt>
 
 Match blocks define which strings to look for and how matches substrings are
@@ -5748,6 +5803,7 @@ plugin's documentation above for details.
       </Result>
     </Query>
     <Database "product_information">
+      #Plugin "warehouse"
       ConnectID "db01"
       Username "oracle"
       Password "secret"
@@ -5770,6 +5826,11 @@ values submitted to the daemon. Other than that, that name is not used.
 
 =over 4
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting query results from
+this B<Database>. Defaults to C<oracle>.
+
 =item B<ConnectID> I<ID>
 
 Defines the "database alias" or "service name" to connect to. Usually, these
@@ -5825,7 +5886,7 @@ The address of the OVS DB server JSON-RPC interface used by the plugin. To
 enable the interface, OVS DB daemon should be running with C<--remote=ptcp:>
 option. See L<ovsdb-server(1)> for more details. The option may be either
 network hostname, IPv4 numbers-and-dots notation or IPv6 hexadecimal string
-format. Defaults to B<'localhost'>.
+format. Defaults to C<localhost>.
 
 =item B<Port> I<service>
 
@@ -5891,7 +5952,7 @@ The address of the OVS DB server JSON-RPC interface used by the plugin. To
 enable the interface, OVS DB daemon should be running with C<--remote=ptcp:>
 option. See L<ovsdb-server(1)> for more details. The option may be either
 network hostname, IPv4 numbers-and-dots notation or IPv6 hexadecimal string
-format. Defaults to B<'localhost'>.
+format. Defaults to C<localhost>.
 
 =item B<Port> I<service>
 
@@ -6123,6 +6184,7 @@ L<http://www.postgresql.org/docs/manuals/>.
     </Writer>
 
     <Database foo>
+      Plugin "kingdom"
       Host "hostname"
       Port "5432"
       User "username"
@@ -6444,6 +6506,11 @@ activating this option. The draw-back is, that data covering the specified
 amount of time will be lost, for example, if a single statement within the
 transaction fails or if the database server crashes.
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting query results from
+this B<Database>. Defaults to C<postgresql>.
+
 =item B<Instance> I<name>
 
 Specify the plugin instance name that should be used instead of the database
@@ -6708,6 +6775,7 @@ collected for these selected processes are:
  - number of processes
  - number of threads
  - number of open files (under Linux)
+ - number of memory mapped files (under Linux)
  - io data (where available)
  - context switches (under Linux)
  - minor and major pagefaults.
@@ -6728,6 +6796,12 @@ slashes.
 
 Collect context switch of the process.
 
+=item B<CollectMemoryMaps> I<Boolean>
+
+Collect the number of memory mapped files of the process.
+The limit for this number is configured via F</proc/sys/vm/max_map_count> in
+the Linux kernel.
+
 =back
 
 =head2 Plugin C<protocols>
@@ -7703,6 +7777,7 @@ user using (extended) regular expressions, as described in L<regex(7)>.
 
   <Plugin "tail">
     <File "/var/log/exim4/mainlog">
+      Plugin "mail"
       Instance "exim"
       Interval 60
       <Match>
@@ -7735,11 +7810,13 @@ The config consists of one or more B<File> blocks, each of which configures one
 logfile to parse. Within each B<File> block, there are one or more B<Match>
 blocks, which configure a regular expression to search for.
 
-The B<Instance> option in the B<File> block may be used to set the plugin
-instance. So in the above example the plugin name C<tail-foo> would be used.
-This plugin instance is for all B<Match> blocks that B<follow> it, until the
-next B<Instance> option. This way you can extract several plugin instances from
-one logfile, handy when parsing syslog and the like.
+The B<Plugin> and B<Instance> options in the B<File> block may be used to set
+the plugin name and instance respectively. So in the above example the plugin name
+C<mail-exim> would be used.
+
+These options are applied for all B<Match> blocks that B<follow> it, until the
+next B<Plugin> or B<Instance> option. This way you can extract several plugin
+instances from one logfile, handy when parsing syslog and the like.
 
 The B<Interval> option allows you to define the length of time between reads. If
 this is not set, the default Interval will be used.
@@ -7927,7 +8004,8 @@ B<Synopsis:>
        Index 1
    </Metric>
    <File "/var/log/snort/snort.stats">
-       Instance "snort-eth0"
+       Plugin "snortstats"
+       Instance "eth0"
        Interval 600
        Collect "snort-dropped"
    </File>
@@ -7979,6 +8057,11 @@ I<File> block but there can be multiple if you have multiple CSV files.
 
 =over 4
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<tail_csv>.
+
 =item B<Instance> I<PluginInstance>
 
 Sets the I<plugin instance> used when dispatching the values.
@@ -9328,6 +9411,7 @@ Synopsis:
         Prefix "collectd/"
         Database 1
         MaxSetSize -1
+        MaxSetDuration -1
         StoreRates true
     </Node>
   </Plugin>
@@ -9390,6 +9474,12 @@ to C<0>.
 The B<MaxSetSize> option limits the number of items that the I<Sorted Sets> can
 hold. Negative values for I<Items> sets no limit, which is the default behavior.
 
+=item B<MaxSetDuration> I<Seconds>
+
+The B<MaxSetDuration> option limits the duration of items that the
+I<Sorted Sets> can hold. Negative values for I<Items> sets no duration, which
+is the default behavior.
+
 =item B<StoreRates> B<true>|B<false>
 
 If set to B<true> (the default), convert counter values to rates. If set to
index 288c974..35ec1f8 100644 (file)
@@ -53,6 +53,7 @@ struct web_page_s;
 typedef struct web_page_s web_page_t;
 struct web_page_s /* {{{ */
 {
+  char *plugin_name;
   char *instance;
 
   char *url;
@@ -146,6 +147,7 @@ static void cc_web_page_free(web_page_t *wp) /* {{{ */
     curl_easy_cleanup(wp->curl);
   wp->curl = NULL;
 
+  sfree(wp->plugin_name);
   sfree(wp->instance);
 
   sfree(wp->url);
@@ -412,6 +414,7 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
     ERROR("curl plugin: calloc failed.");
     return -1;
   }
+  page->plugin_name = NULL;
   page->url = NULL;
   page->user = NULL;
   page->pass = NULL;
@@ -435,7 +438,9 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
 
-    if (strcasecmp("URL", child->key) == 0)
+    if (strcasecmp("Plugin", child->key) == 0)
+      status = cf_util_get_string(child, &page->plugin_name);
+    else if (strcasecmp("URL", child->key) == 0)
       status = cf_util_get_string(child, &page->url);
     else if (strcasecmp("User", child->key) == 0)
       status = cf_util_get_string(child, &page->user);
@@ -566,7 +571,8 @@ static void cc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */
 
   vl.values = &value;
   vl.values_len = 1;
-  sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl",
+           sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, wm->type, sizeof(vl.type));
   if (wm->instance != NULL)
@@ -581,7 +587,8 @@ static void cc_submit_response_code(const web_page_t *wp, long code) /* {{{ */
 
   vl.values = &(value_t){.gauge = (gauge_t)code};
   vl.values_len = 1;
-  sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl",
+           sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, "response_code", sizeof(vl.type));
 
@@ -594,7 +601,8 @@ static void cc_submit_response_time(const web_page_t *wp, /* {{{ */
 
   vl.values = &(value_t){.gauge = response_time};
   vl.values_len = 1;
-  sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl",
+           sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, "response_time", sizeof(vl.type));
 
index 35b11c0..756f24f 100644 (file)
@@ -82,6 +82,7 @@ typedef struct {
 struct cj_s /* {{{ */
 {
   char *instance;
+  char *plugin_name;
   char *host;
 
   char *sock;
@@ -396,6 +397,7 @@ static void cj_free(void *arg) /* {{{ */
   db->tree = NULL;
 
   sfree(db->instance);
+  sfree(db->plugin_name);
   sfree(db->host);
 
   sfree(db->sock);
@@ -672,6 +674,8 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
 
     if (strcasecmp("Instance", child->key) == 0)
       status = cf_util_get_string(child, &db->instance);
+    else if (strcasecmp("Plugin", child->key) == 0)
+      status = cf_util_get_string(child, &db->plugin_name);
     else if (strcasecmp("Host", child->key) == 0)
       status = cf_util_get_string(child, &db->host);
     else if (db->url && strcasecmp("User", child->key) == 0)
@@ -805,7 +809,8 @@ static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
     sstrncpy(vl.type_instance, key->instance, sizeof(vl.type_instance));
 
   sstrncpy(vl.host, cj_host(db), sizeof(vl.host));
-  sstrncpy(vl.plugin, "curl_json", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, (db->plugin_name != NULL) ? db->plugin_name : "curl_json",
+           sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, key->type, sizeof(vl.type));
 
index e83de73..c99e3f1 100644 (file)
@@ -54,6 +54,7 @@ struct cx_xpath_s /* {{{ */
   size_t values_len;
   char *instance_prefix;
   char *instance;
+  char *plugin_instance_from;
   int is_table;
   unsigned long magic;
 };
@@ -71,6 +72,7 @@ typedef struct cx_namespace_s cx_namespace_t;
 struct cx_s /* {{{ */
 {
   char *instance;
+  char *plugin_name;
   char *host;
 
   char *url;
@@ -95,7 +97,7 @@ struct cx_s /* {{{ */
   size_t buffer_size;
   size_t buffer_fill;
 
-  llist_t *list; /* list of xpath blocks */
+  llist_t *xpath_list; /* list of xpath blocks */
 };
 typedef struct cx_s cx_t; /* }}} */
 
@@ -105,9 +107,8 @@ typedef struct cx_s cx_t; /* }}} */
 static size_t cx_curl_callback(void *buf, /* {{{ */
                                size_t size, size_t nmemb, void *user_data) {
   size_t len = size * nmemb;
-  cx_t *db;
 
-  db = user_data;
+  cx_t *db = user_data;
   if (db == NULL) {
     ERROR("curl_xml plugin: cx_curl_callback: "
           "user_data pointer is NULL.");
@@ -118,9 +119,7 @@ static size_t cx_curl_callback(void *buf, /* {{{ */
     return len;
 
   if ((db->buffer_fill + len) >= db->buffer_size) {
-    char *temp;
-
-    temp = realloc(db->buffer, db->buffer_fill + len + 1);
+    char *temp = realloc(db->buffer, db->buffer_fill + len + 1);
     if (temp == NULL) {
       ERROR("curl_xml plugin: realloc failed.");
       return 0;
@@ -144,29 +143,28 @@ static void cx_xpath_free(cx_xpath_t *xpath) /* {{{ */
   sfree(xpath->path);
   sfree(xpath->type);
   sfree(xpath->instance_prefix);
+  sfree(xpath->plugin_instance_from);
   sfree(xpath->instance);
   sfree(xpath->values);
   sfree(xpath);
 } /* }}} void cx_xpath_free */
 
-static void cx_list_free(llist_t *list) /* {{{ */
+static void cx_xpath_list_free(llist_t *list) /* {{{ */
 {
   llentry_t *le;
 
   le = llist_head(list);
   while (le != NULL) {
-    llentry_t *le_next;
+    llentry_t *le_next = le->next;
 
-    le_next = le->next;
-
-    sfree(le->key);
+    /* this also frees xpath->path used for le->key */
     cx_xpath_free(le->value);
 
     le = le_next;
   }
 
   llist_destroy(list);
-} /* }}} void cx_list_free */
+} /* }}} void cx_xpath_list_free */
 
 static void cx_free(void *arg) /* {{{ */
 {
@@ -183,11 +181,12 @@ static void cx_free(void *arg) /* {{{ */
     curl_easy_cleanup(db->curl);
   db->curl = NULL;
 
-  if (db->list != NULL)
-    cx_list_free(db->list);
+  if (db->xpath_list != NULL)
+    cx_xpath_list_free(db->xpath_list);
 
   sfree(db->buffer);
   sfree(db->instance);
+  sfree(db->plugin_name);
   sfree(db->host);
 
   sfree(db->url);
@@ -208,7 +207,7 @@ static void cx_free(void *arg) /* {{{ */
   sfree(db);
 } /* }}} void cx_free */
 
-static const char *cx_host(cx_t *db) /* {{{ */
+static const char *cx_host(const cx_t *db) /* {{{ */
 {
   if (db->host == NULL)
     return hostname_g;
@@ -250,13 +249,11 @@ static int cx_check_type(const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
   return 0;
 } /* }}} cx_check_type */
 
-static xmlXPathObjectPtr
-cx_evaluate_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
-                  xmlChar *expr) {
-  xmlXPathObjectPtr xpath_obj;
-
-  /* XXX: When to free this? */
-  xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+static xmlXPathObjectPtr cx_evaluate_xpath(xmlXPathContextPtr xpath_ctx,
+                                           char *expr) /* {{{ */
+{
+  xmlXPathObjectPtr xpath_obj =
+      xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
   if (xpath_obj == NULL) {
     WARNING("curl_xml plugin: "
             "Error unable to evaluate xpath expression \"%s\". Skipping...",
@@ -279,51 +276,65 @@ static int cx_if_not_text_node(xmlNodePtr node) /* {{{ */
   return -1;
 } /* }}} cx_if_not_text_node */
 
-static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
-                                        cx_xpath_t *xpath, const data_set_t *ds,
-                                        value_list_t *vl, int index) {
-  xmlXPathObjectPtr values_node_obj;
-  xmlNodeSetPtr values_node;
-  int tmp_size;
-  char *node_value;
-
-  values_node_obj =
-      cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->values[index].path);
+/*
+ * Returned value should be freed with xmlFree().
+ */
+static char *cx_get_text_node_value(xmlXPathContextPtr xpath_ctx, /* {{{ */
+                                    char *expr, const char *from_option) {
+  xmlXPathObjectPtr values_node_obj = cx_evaluate_xpath(xpath_ctx, expr);
   if (values_node_obj == NULL)
-    return -1; /* Error already logged. */
+    return NULL; /* Error already logged. */
 
-  values_node = values_node_obj->nodesetval;
-  tmp_size = (values_node) ? values_node->nodeNr : 0;
+  xmlNodeSetPtr values_node = values_node_obj->nodesetval;
+  size_t tmp_size = (values_node) ? values_node->nodeNr : 0;
 
   if (tmp_size == 0) {
     WARNING("curl_xml plugin: "
-            "relative xpath expression \"%s\" doesn't match any of the nodes. "
-            "Skipping...",
-            xpath->values[index].path);
+            "relative xpath expression \"%s\" from '%s' doesn't match "
+            "any of the nodes.",
+            expr, from_option);
     xmlXPathFreeObject(values_node_obj);
-    return -1;
+    return NULL;
   }
 
   if (tmp_size > 1) {
     WARNING("curl_xml plugin: "
-            "relative xpath expression \"%s\" is expected to return "
-            "only one node. Skipping...",
-            xpath->values[index].path);
+            "relative xpath expression \"%s\" from '%s' is expected to return "
+            "only one text node. Skipping the node.",
+            expr, from_option);
     xmlXPathFreeObject(values_node_obj);
-    return -1;
+    return NULL;
   }
 
   /* ignoring the element if other than textnode/attribute*/
   if (cx_if_not_text_node(values_node->nodeTab[0])) {
     WARNING("curl_xml plugin: "
-            "relative xpath expression \"%s\" is expected to return "
-            "only text/attribute node which is not the case. Skipping...",
-            xpath->values[index].path);
+            "relative xpath expression \"%s\" from '%s' is expected to return "
+            "only text/attribute node which is not the case. "
+            "Skipping the node.",
+            expr, from_option);
     xmlXPathFreeObject(values_node_obj);
-    return -1;
+    return NULL;
   }
 
-  node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+  char *node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+
+  /* free up object */
+  xmlXPathFreeObject(values_node_obj);
+
+  return node_value;
+} /* }}} char * cx_get_text_node_value */
+
+static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
+                                        cx_xpath_t *xpath, const data_set_t *ds,
+                                        value_list_t *vl, int index) {
+
+  char *node_value = cx_get_text_node_value(
+      xpath_ctx, xpath->values[index].path, "ValuesFrom");
+
+  if (node_value == NULL)
+    return -1;
+
   switch (ds->ds[index].type) {
   case DS_TYPE_COUNTER:
     vl->values[index].counter =
@@ -345,9 +356,7 @@ static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
                                               /* endptr = */ NULL);
   }
 
-  /* free up object */
-  xmlXPathFreeObject(values_node_obj);
-  sfree(node_value);
+  xmlFree(node_value);
 
   /* We have reached here which means that
    * we have got something to work */
@@ -358,7 +367,6 @@ static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */
                                       cx_xpath_t *xpath, const data_set_t *ds,
                                       value_list_t *vl) {
   value_t values[xpath->values_len];
-  int status;
 
   assert(xpath->values_len > 0);
   assert(xpath->values_len == vl->values_len);
@@ -366,8 +374,7 @@ static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */
   vl->values = values;
 
   for (size_t i = 0; i < xpath->values_len; i++) {
-    status = cx_handle_single_value_xpath(xpath_ctx, xpath, ds, vl, i);
-    if (status != 0)
+    if (cx_handle_single_value_xpath(xpath_ctx, xpath, ds, vl, i) != 0)
       return -1; /* An error has been printed. */
   }              /* for (i = 0; i < xpath->values_len; i++) */
 
@@ -378,150 +385,96 @@ static int cx_handle_all_value_xpaths(xmlXPathContextPtr xpath_ctx, /* {{{ */
 } /* }}} int cx_handle_all_value_xpaths */
 
 static int cx_handle_instance_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
-                                    cx_xpath_t *xpath, value_list_t *vl,
-                                    _Bool is_table) {
-  xmlXPathObjectPtr instance_node_obj = NULL;
-  xmlNodeSetPtr instance_node = NULL;
-
-  memset(vl->type_instance, 0, sizeof(vl->type_instance));
-
-  /* If the base xpath returns more than one block, the result is assumed to be
-   * a table. The `Instance' option is not optional in this case. Check for the
-   * condition and inform the user. */
-  if (is_table && (xpath->instance == NULL)) {
-    WARNING("curl_xml plugin: "
-            "Base-XPath %s is a table (more than one result was returned), "
-            "but no instance-XPath has been defined.",
-            xpath->path);
-    return -1;
-  }
+                                    cx_xpath_t *xpath, value_list_t *vl) {
 
-  /* instance has to be an xpath expression */
+  /* Handle type instance */
   if (xpath->instance != NULL) {
-    int tmp_size;
-
-    instance_node_obj = cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->instance);
-    if (instance_node_obj == NULL)
-      return -1; /* error is logged already */
-
-    instance_node = instance_node_obj->nodesetval;
-    tmp_size = (instance_node) ? instance_node->nodeNr : 0;
-
-    if (tmp_size <= 0) {
-      WARNING(
-          "curl_xml plugin: "
-          "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
-          "any of the nodes. Skipping the node.",
-          xpath->instance);
-      xmlXPathFreeObject(instance_node_obj);
+    char *node_value =
+        cx_get_text_node_value(xpath_ctx, xpath->instance, "InstanceFrom");
+    if (node_value == NULL)
       return -1;
-    }
 
-    if (tmp_size > 1) {
-      WARNING("curl_xml plugin: "
-              "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
-              "to return only one text node. Skipping the node.",
-              xpath->instance);
-      xmlXPathFreeObject(instance_node_obj);
-      return -1;
-    }
-
-    /* ignoring the element if other than textnode/attribute */
-    if (cx_if_not_text_node(instance_node->nodeTab[0])) {
-      WARNING("curl_xml plugin: "
-              "relative xpath expression \"%s\" is expected to return only "
-              "text node "
-              "which is not the case. Skipping the node.",
-              xpath->instance);
-      xmlXPathFreeObject(instance_node_obj);
-      return -1;
-    }
-  } /* if (xpath->instance != NULL) */
-
-  if (xpath->instance_prefix != NULL) {
-    if (instance_node != NULL) {
-      char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+    if (xpath->instance_prefix != NULL)
       snprintf(vl->type_instance, sizeof(vl->type_instance), "%s%s",
                xpath->instance_prefix, node_value);
-      sfree(node_value);
-    } else
-      sstrncpy(vl->type_instance, xpath->instance_prefix,
-               sizeof(vl->type_instance));
-  } else {
-    /* If instance_prefix and instance_node are NULL, then
-     * don't set the type_instance */
-    if (instance_node != NULL) {
-      char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+    else
       sstrncpy(vl->type_instance, node_value, sizeof(vl->type_instance));
-      sfree(node_value);
-    }
-  }
 
-  /* Free `instance_node_obj' this late, because `instance_node' points to
-   * somewhere inside this structure. */
-  xmlXPathFreeObject(instance_node_obj);
+    xmlFree(node_value);
+  } else if (xpath->instance_prefix != NULL)
+    sstrncpy(vl->type_instance, xpath->instance_prefix,
+             sizeof(vl->type_instance));
+
+  /* Handle plugin instance */
+  if (xpath->plugin_instance_from != NULL) {
+    char *node_value = cx_get_text_node_value(
+        xpath_ctx, xpath->plugin_instance_from, "PluginInstanceFrom");
+
+    if (node_value == NULL)
+      return -1;
+
+    sstrncpy(vl->plugin_instance, node_value, sizeof(vl->plugin_instance));
+    xmlFree(node_value);
+  }
 
   return 0;
 } /* }}} int cx_handle_instance_xpath */
 
-static int cx_handle_base_xpath(char const *plugin_instance, /* {{{ */
-                                char const *host, xmlXPathContextPtr xpath_ctx,
-                                const data_set_t *ds, char *base_xpath,
-                                cx_xpath_t *xpath) {
-  int total_nodes;
+static int cx_handle_xpath(const cx_t *db, /* {{{ */
+                           xmlXPathContextPtr xpath_ctx, cx_xpath_t *xpath) {
 
-  xmlXPathObjectPtr base_node_obj = NULL;
-  xmlNodeSetPtr base_nodes = NULL;
-
-  value_list_t vl = VALUE_LIST_INIT;
+  const data_set_t *ds = plugin_get_ds(xpath->type);
+  if (cx_check_type(ds, xpath) != 0)
+    return -1;
 
-  base_node_obj = cx_evaluate_xpath(xpath_ctx, BAD_CAST base_xpath);
+  xmlXPathObjectPtr base_node_obj = cx_evaluate_xpath(xpath_ctx, xpath->path);
   if (base_node_obj == NULL)
     return -1; /* error is logged already */
 
-  base_nodes = base_node_obj->nodesetval;
-  total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+  xmlNodeSetPtr base_nodes = base_node_obj->nodesetval;
+  int total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
 
   if (total_nodes == 0) {
     ERROR("curl_xml plugin: "
           "xpath expression \"%s\" doesn't match any of the nodes. "
           "Skipping the xpath block...",
-          base_xpath);
+          xpath->path);
     xmlXPathFreeObject(base_node_obj);
     return -1;
   }
 
   /* If base_xpath returned multiple results, then */
-  /* Instance in the xpath block is required */
-  if (total_nodes > 1 && xpath->instance == NULL) {
+  /* InstanceFrom or PluginInstanceFrom in the xpath block is required */
+  if (total_nodes > 1 && xpath->instance == NULL &&
+      xpath->plugin_instance_from == NULL) {
     ERROR("curl_xml plugin: "
-          "InstanceFrom is must in xpath block since the base xpath expression "
-          "\"%s\" "
+          "InstanceFrom or PluginInstanceFrom is must in xpath block "
+          "since the base xpath expression \"%s\" "
           "returned multiple results. Skipping the xpath block...",
-          base_xpath);
+          xpath->path);
+    xmlXPathFreeObject(base_node_obj);
     return -1;
   }
 
+  value_list_t vl = VALUE_LIST_INIT;
+
   /* set the values for the value_list */
   vl.values_len = ds->ds_num;
   sstrncpy(vl.type, xpath->type, sizeof(vl.type));
-  sstrncpy(vl.plugin, "curl_xml", sizeof(vl.plugin));
-  sstrncpy(vl.host, host, sizeof(vl.host));
-  if (plugin_instance != NULL)
-    sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+  sstrncpy(vl.plugin, (db->plugin_name != NULL) ? db->plugin_name : "curl_xml",
+           sizeof(vl.plugin));
+  sstrncpy(vl.host, cx_host(db), sizeof(vl.host));
 
   for (int i = 0; i < total_nodes; i++) {
-    int status;
-
     xpath_ctx->node = base_nodes->nodeTab[i];
 
-    status = cx_handle_instance_xpath(xpath_ctx, xpath, &vl,
-                                      /* is_table = */ (total_nodes > 1));
-    if (status != 0)
+    if (db->instance != NULL)
+      sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
+
+    if (cx_handle_instance_xpath(xpath_ctx, xpath, &vl) != 0)
       continue; /* An error has already been reported. */
 
-    status = cx_handle_all_value_xpaths(xpath_ctx, xpath, ds, &vl);
-    if (status != 0)
+    if (cx_handle_all_value_xpaths(xpath_ctx, xpath, ds, &vl) != 0)
       continue; /* An error has been logged. */
   }             /* for (i = 0; i < total_nodes; i++) */
 
@@ -529,24 +482,17 @@ static int cx_handle_base_xpath(char const *plugin_instance, /* {{{ */
   xmlXPathFreeObject(base_node_obj);
 
   return 0;
-} /* }}} cx_handle_base_xpath */
+} /* }}} cx_handle_xpath */
 
-static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
-                                xmlXPathContextPtr xpath_ctx, cx_t *db) {
-  llentry_t *le;
-  const data_set_t *ds;
-  cx_xpath_t *xpath;
+static int cx_handle_parsed_xml(cx_t *db, xmlDocPtr doc, /* {{{ */
+                                xmlXPathContextPtr xpath_ctx) {
   int status = -1;
 
-  le = llist_head(db->list);
+  llentry_t *le = llist_head(db->xpath_list);
   while (le != NULL) {
-    /* get the ds */
-    xpath = (cx_xpath_t *)le->value;
-    ds = plugin_get_ds(xpath->type);
+    cx_xpath_t *xpath = (cx_xpath_t *)le->value;
 
-    if ((cx_check_type(ds, xpath) == 0) &&
-        (cx_handle_base_xpath(db->instance, cx_host(db), xpath_ctx, ds, le->key,
-                              xpath) == 0))
+    if (cx_handle_xpath(db, xpath_ctx, xpath) == 0)
       status = 0; /* we got atleast one success */
 
     le = le->next;
@@ -555,20 +501,16 @@ static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
   return status;
 } /* }}} cx_handle_parsed_xml */
 
-static int cx_parse_stats_xml(xmlChar *xml, cx_t *db) /* {{{ */
+static int cx_parse_xml(cx_t *db, char *xml) /* {{{ */
 {
-  int status;
-  xmlDocPtr doc;
-  xmlXPathContextPtr xpath_ctx;
-
   /* Load the XML */
-  doc = xmlParseDoc(xml);
+  xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
   if (doc == NULL) {
     ERROR("curl_xml plugin: Failed to parse the xml document  - %s", xml);
     return -1;
   }
 
-  xpath_ctx = xmlXPathNewContext(doc);
+  xmlXPathContextPtr xpath_ctx = xmlXPathNewContext(doc);
   if (xpath_ctx == NULL) {
     ERROR("curl_xml plugin: Failed to create the xml context");
     xmlFreeDoc(doc);
@@ -577,7 +519,7 @@ static int cx_parse_stats_xml(xmlChar *xml, cx_t *db) /* {{{ */
 
   for (size_t i = 0; i < db->namespaces_num; i++) {
     cx_namespace_t const *ns = db->namespaces + i;
-    status =
+    int status =
         xmlXPathRegisterNs(xpath_ctx, BAD_CAST ns->prefix, BAD_CAST ns->url);
     if (status != 0) {
       ERROR("curl_xml plugin: "
@@ -589,25 +531,29 @@ static int cx_parse_stats_xml(xmlChar *xml, cx_t *db) /* {{{ */
     }
   }
 
-  status = cx_handle_parsed_xml(doc, xpath_ctx, db);
+  int status = cx_handle_parsed_xml(db, doc, xpath_ctx);
   /* Cleanup */
   xmlXPathFreeContext(xpath_ctx);
   xmlFreeDoc(doc);
   return status;
-} /* }}} cx_parse_stats_xml */
+} /* }}} cx_parse_xml */
 
-static int cx_curl_perform(cx_t *db, CURL *curl) /* {{{ */
+static int cx_read(user_data_t *ud) /* {{{ */
 {
-  int status;
+  if ((ud == NULL) || (ud->data == NULL)) {
+    ERROR("curl_xml plugin: cx_read: Invalid user data.");
+    return -1;
+  }
+
   long rc;
-  char *ptr;
   char *url;
+  cx_t *db = (cx_t *)ud->data;
 
   db->buffer_fill = 0;
 
   curl_easy_setopt(db->curl, CURLOPT_URL, db->url);
 
-  status = curl_easy_perform(curl);
+  int status = curl_easy_perform(db->curl);
   if (status != CURLE_OK) {
     ERROR("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
           status, db->curl_errbuf, db->url);
@@ -617,8 +563,8 @@ static int cx_curl_perform(cx_t *db, CURL *curl) /* {{{ */
     curl_stats_dispatch(db->stats, db->curl, cx_host(db), "curl_xml",
                         db->instance);
 
-  curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
-  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+  curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &url);
+  curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
 
   /* The response code is zero if a non-HTTP transport was used. */
   if ((rc != 0) && (rc != 200)) {
@@ -628,26 +574,10 @@ static int cx_curl_perform(cx_t *db, CURL *curl) /* {{{ */
     return -1;
   }
 
-  ptr = db->buffer;
-
-  status = cx_parse_stats_xml(BAD_CAST ptr, db);
+  status = cx_parse_xml(db, db->buffer);
   db->buffer_fill = 0;
 
   return status;
-} /* }}} int cx_curl_perform */
-
-static int cx_read(user_data_t *ud) /* {{{ */
-{
-  cx_t *db;
-
-  if ((ud == NULL) || (ud->data == NULL)) {
-    ERROR("curl_xml plugin: cx_read: Invalid user data.");
-    return -1;
-  }
-
-  db = (cx_t *)ud->data;
-
-  return cx_curl_perform(db, db->curl);
 } /* }}} int cx_read */
 
 /* Configuration handling functions {{{ */
@@ -685,18 +615,13 @@ static int cx_config_add_values(const char *name, cx_xpath_t *xpath, /* {{{ */
 
 static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */
 {
-  cx_xpath_t *xpath;
-  char *name;
-  llentry_t *le;
-  int status;
-
-  xpath = calloc(1, sizeof(*xpath));
+  cx_xpath_t *xpath = calloc(1, sizeof(*xpath));
   if (xpath == NULL) {
     ERROR("curl_xml plugin: calloc failed.");
     return -1;
   }
 
-  status = cf_util_get_string(ci, &xpath->path);
+  int status = cf_util_get_string(ci, &xpath->path);
   if (status != 0) {
     cx_xpath_free(xpath);
     return status;
@@ -720,6 +645,8 @@ static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string(child, &xpath->instance_prefix);
     else if (strcasecmp("InstanceFrom", child->key) == 0)
       status = cf_util_get_string(child, &xpath->instance);
+    else if (strcasecmp("PluginInstanceFrom", child->key) == 0)
+      status = cf_util_get_string(child, &xpath->plugin_instance_from);
     else if (strcasecmp("ValuesFrom", child->key) == 0)
       status = cx_config_add_values("ValuesFrom", xpath, child);
     else {
@@ -742,37 +669,25 @@ static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */
     return -1;
   }
 
-  if (db->list == NULL) {
-    db->list = llist_create();
-    if (db->list == NULL) {
-      ERROR("curl_xml plugin: list creation failed.");
-      cx_xpath_free(xpath);
-      return -1;
-    }
-  }
-
-  name = strdup(xpath->path);
-  if (name == NULL) {
-    ERROR("curl_xml plugin: strdup failed.");
+  if (xpath->values_len == 0) {
+    WARNING("curl_xml plugin: `ValuesFrom' missing in `xpath' block.");
     cx_xpath_free(xpath);
     return -1;
   }
 
-  le = llentry_create(name, xpath);
+  llentry_t *le = llentry_create(xpath->path, xpath);
   if (le == NULL) {
     ERROR("curl_xml plugin: llentry_create failed.");
     cx_xpath_free(xpath);
-    sfree(name);
     return -1;
   }
 
-  llist_append(db->list, le);
+  llist_append(db->xpath_list, le);
   return 0;
 } /* }}} int cx_config_add_xpath */
 
 static int cx_config_add_namespace(cx_t *db, /* {{{ */
                                    oconfig_item_t *ci) {
-  cx_namespace_t *ns;
 
   if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
       (ci->values[1].type != OCONFIG_TYPE_STRING)) {
@@ -781,8 +696,8 @@ static int cx_config_add_namespace(cx_t *db, /* {{{ */
     return EINVAL;
   }
 
-  ns = realloc(db->namespaces,
-               sizeof(*db->namespaces) * (db->namespaces_num + 1));
+  cx_namespace_t *ns = realloc(
+      db->namespaces, sizeof(*db->namespaces) * (db->namespaces_num + 1));
   if (ns == NULL) {
     ERROR("curl_xml plugin: realloc failed.");
     return ENOMEM;
@@ -871,43 +786,51 @@ static int cx_init_curl(cx_t *db) /* {{{ */
 
 static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
 {
-  cx_t *db;
-  int status = 0;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("curl_xml plugin: The `URL' block "
             "needs exactly one string argument.");
     return -1;
   }
 
-  db = calloc(1, sizeof(*db));
+  cx_t *db = calloc(1, sizeof(*db));
   if (db == NULL) {
     ERROR("curl_xml plugin: calloc failed.");
     return -1;
   }
 
-  db->timeout = -1;
+  db->instance = strdup("default");
+  if (db->instance == NULL) {
+    ERROR("curl_xml plugin: strdup failed.");
+    sfree(db);
+    return -1;
+  }
 
-  if (strcasecmp("URL", ci->key) == 0) {
-    status = cf_util_get_string(ci, &db->url);
-    if (status != 0) {
-      sfree(db);
-      return status;
-    }
-  } else {
-    ERROR("curl_xml plugin: cx_config: "
-          "Invalid key: %s",
-          ci->key);
-    cx_free(db);
+  db->xpath_list = llist_create();
+  if (db->xpath_list == NULL) {
+    ERROR("curl_xml plugin: list creation failed.");
+    sfree(db->instance);
+    sfree(db);
     return -1;
   }
 
+  db->timeout = -1;
+
+  int status = cf_util_get_string(ci, &db->url);
+  if (status != 0) {
+    llist_destroy(db->xpath_list);
+    sfree(db->instance);
+    sfree(db);
+    return status;
+  }
+
   /* Fill the `cx_t' structure.. */
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Instance", child->key) == 0)
       status = cf_util_get_string(child, &db->instance);
+    else if (strcasecmp("Plugin", child->key) == 0)
+      status = cf_util_get_string(child, &db->plugin_name);
     else if (strcasecmp("Host", child->key) == 0)
       status = cf_util_get_string(child, &db->host);
     else if (strcasecmp("User", child->key) == 0)
@@ -945,39 +868,34 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
       break;
   }
 
-  if (status == 0) {
-    if (db->list == NULL) {
-      WARNING("curl_xml plugin: No (valid) `Key' block "
-              "within `URL' block `%s'.",
-              db->url);
-      status = -1;
-    }
-    if (status == 0)
-      status = cx_init_curl(db);
+  if (status != 0) {
+    cx_free(db);
+    return status;
   }
 
-  /* If all went well, register this database for reading */
-  if (status == 0) {
-    char *cb_name;
-
-    if (db->instance == NULL)
-      db->instance = strdup("default");
-
-    DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
-
-    cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
+  if (llist_size(db->xpath_list) == 0) {
+    WARNING("curl_xml plugin: No `xpath' block within `URL' block `%s'.",
+            db->url);
+    cx_free(db);
+    return -1;
+  }
 
-    plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
-                                 /* interval = */ 0,
-                                 &(user_data_t){
-                                     .data = db, .free_func = cx_free,
-                                 });
-    sfree(cb_name);
-  } else {
+  if (cx_init_curl(db) != 0) {
     cx_free(db);
     return -1;
   }
 
+  /* If all went well, register this database for reading */
+  DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
+
+  char *cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
+
+  plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
+                               /* interval = */ 0,
+                               &(user_data_t){
+                                   .data = db, .free_func = cx_free,
+                               });
+  sfree(cb_name);
   return 0;
 } /* }}} int cx_config_add_url */
 
@@ -985,19 +903,14 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
 
 static int cx_config(oconfig_item_t *ci) /* {{{ */
 {
-  int success;
-  int errors;
-  int status;
-
-  success = 0;
-  errors = 0;
+  int success = 0;
+  int errors = 0;
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("URL", child->key) == 0) {
-      status = cx_config_add_url(child);
-      if (status == 0)
+      if (cx_config_add_url(child) == 0)
         success++;
       else
         errors++;
index a577aef..62ef1dc 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -62,6 +62,7 @@ struct cdbi_database_s /* {{{ */
 {
   char *name;
   char *select_db;
+  char *plugin_name;
 
   cdtime_t interval;
 
@@ -172,7 +173,10 @@ static void cdbi_database_free(cdbi_database_t *db) /* {{{ */
     return;
 
   sfree(db->name);
+  sfree(db->select_db);
+  sfree(db->plugin_name);
   sfree(db->driver);
+  sfree(db->host);
 
   for (size_t i = 0; i < db->driver_options_num; i++) {
     sfree(db->driver_options[i].key);
@@ -184,7 +188,10 @@ static void cdbi_database_free(cdbi_database_t *db) /* {{{ */
   if (db->q_prep_areas)
     for (size_t i = 0; i < db->queries_num; ++i)
       udb_query_delete_preparation_area(db->q_prep_areas[i]);
-  free(db->q_prep_areas);
+  sfree(db->q_prep_areas);
+  /* N.B.: db->queries references objects "owned" by the global queries
+   * variable. Free the array here, but not the content. */
+  sfree(db->queries);
 
   sfree(db);
 } /* }}} void cdbi_database_free */
@@ -298,6 +305,8 @@ static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string(child, &db->host);
     else if (strcasecmp("Interval", child->key) == 0)
       status = cf_util_get_cdtime(child, &db->interval);
+    else if (strcasecmp("Plugin", child->key) == 0)
+      status = cf_util_get_string(child, &db->plugin_name);
     else {
       WARNING("dbi plugin: Option `%s' not allowed here.", child->key);
       status = -1;
@@ -540,7 +549,8 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
 
   udb_query_prepare_result(
       q, prep_area, (db->host ? db->host : hostname_g),
-      /* plugin = */ "dbi", db->name, column_names, column_num,
+      /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "dbi",
+      db->name, column_names, column_num,
       /* interval = */ (db->interval > 0) ? db->interval : 0);
 
   /* 0 = error; 1 = success; */
index e5f015b..00e7413 100644 (file)
@@ -402,9 +402,15 @@ static void *open_connection(void __attribute__((unused)) * arg) {
   {
     struct group sg;
     struct group *grp;
-    char grbuf[4096];
     int status;
 
+    long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+    if (grbuf_size <= 0)
+      grbuf_size = sysconf(_SC_PAGESIZE);
+    if (grbuf_size <= 0)
+      grbuf_size = 4096;
+    char grbuf[grbuf_size];
+
     grp = NULL;
     status = getgrnam_r(group, &sg, grbuf, sizeof(grbuf), &grp);
     if (status != 0) {
index 22da316..a9f7be5 100644 (file)
@@ -368,11 +368,17 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
 
   struct passwd *sp_ptr;
   struct passwd sp;
-  char nambuf[4096];
 
   if (pl->pid != 0)
     return -1;
 
+  long int nambuf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+  if (nambuf_size <= 0)
+    nambuf_size = sysconf(_SC_PAGESIZE);
+  if (nambuf_size <= 0)
+    nambuf_size = 4096;
+  char nambuf[nambuf_size];
+
   if ((create_pipe(fd_pipe_in) == -1) || (create_pipe(fd_pipe_out) == -1) ||
       (create_pipe(fd_pipe_err) == -1))
     goto failed;
@@ -405,7 +411,14 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
       struct group *gr_ptr = NULL;
       struct group gr;
 
-      status = getgrnam_r(pl->group, &gr, nambuf, sizeof(nambuf), &gr_ptr);
+      long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+      if (grbuf_size <= 0)
+        grbuf_size = sysconf(_SC_PAGESIZE);
+      if (grbuf_size <= 0)
+        grbuf_size = 4096;
+      char grbuf[grbuf_size];
+
+      status = getgrnam_r(pl->group, &gr, grbuf, sizeof(grbuf), &gr_ptr);
       if (status != 0) {
         ERROR("exec plugin: Failed to get group information "
               "for group ``%s'': %s",
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..87f8b44 100644 (file)
@@ -59,16 +59,22 @@ 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 const char *config_keys[] = {"Sensor",
+                                    "IgnoreSelected",
+                                    "NotifySensorAdd",
+                                    "NotifySensorRemove",
+                                    "NotifySensorNotPresent",
+                                    "SELEnabled",
+                                    "SELClearEvent"};
 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 _Bool c_ipmi_notify_add = 0;
+static _Bool c_ipmi_notify_remove = 0;
+static _Bool c_ipmi_notify_notpresent = 0;
+static _Bool c_ipmi_sel_enabled = 0;
+static _Bool c_ipmi_sel_clear_event = 0;
 
 /*
  * Misc private functions
@@ -115,7 +121,7 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
              "not present.",
              list_item->sensor_name);
 
-        if (c_ipmi_nofiy_notpresent) {
+        if (c_ipmi_notify_notpresent) {
           notification_t n = {
               NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL};
 
@@ -164,7 +170,7 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
     INFO("ipmi plugin: sensor_read_handler: sensor %s present.",
          list_item->sensor_name);
 
-    if (c_ipmi_nofiy_notpresent) {
+    if (c_ipmi_notify_notpresent) {
       notification_t n = {NOTIF_OKAY, cdtime(), "", "",  "ipmi",
                           "",         "",       "", NULL};
 
@@ -200,56 +206,66 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
   plugin_dispatch_values(&vl);
 } /* void sensor_read_handler */
 
+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(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);
-
-  ipmi_sensor_get_name(sensor, buffer, sizeof(buffer));
-  buffer[sizeof(buffer) - 1] = 0;
-
-  entity_id_string = ipmi_entity_get_entity_id_string(ent);
-
-  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. */
-  }
-  sensor_name_ptr = sensor_name;
+  sensor_get_name(sensor, buffer, sizeof(buffer));
 
   /* Both `ignorelist' and `plugin_instance' may be NULL. */
   if (ignorelist_match(ignorelist, sensor_name_ptr) != 0)
@@ -321,7 +337,7 @@ static int sensor_list_add(ipmi_sensor_t *sensor) {
 
   pthread_mutex_unlock(&sensor_list_lock);
 
-  if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0)) {
+  if (c_ipmi_notify_add && (c_ipmi_init_in_progress == 0)) {
     notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
 
     sstrncpy(n.host, hostname_g, sizeof(n.host));
@@ -368,7 +384,7 @@ static int sensor_list_remove(ipmi_sensor_t *sensor) {
 
   pthread_mutex_unlock(&sensor_list_lock);
 
-  if (c_ipmi_nofiy_remove && c_ipmi_active) {
+  if (c_ipmi_notify_remove && c_ipmi_active) {
     notification_t n = {NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "",
                         NULL};
 
@@ -420,6 +436,160 @@ 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) {
+
+  /* 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;
+
+  /* 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 */
+  notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
+  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.host, hostname_g, sizeof(n.host));
+  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 (c_ipmi_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) {
+  /* 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 = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
+  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.host, hostname_g, sizeof(n.host));
+  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 (c_ipmi_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
  */
@@ -431,8 +601,39 @@ static void entity_sensor_update_handler(
   if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
     /* Will check for duplicate entries.. */
     sensor_list_add(sensor);
+
+    if (c_ipmi_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, NULL);
+      /* 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, NULL);
+
+      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);
+
+    if (c_ipmi_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, NULL);
+      else
+        ipmi_sensor_remove_discrete_event_handler(
+            sensor, sensor_discrete_event_handler, NULL);
+    }
   }
 } /* void entity_sensor_update_handler */
 
@@ -459,6 +660,23 @@ static void domain_entity_update_handler(
   }
 } /* 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,
@@ -476,6 +694,13 @@ static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
   if (status != 0) {
     c_ipmi_error("ipmi_domain_add_entity_update_handler", status);
   }
+
+  ipmi_con_t *smi_connection = user_data;
+  status = smi_connection->add_event_handler(smi_connection, smi_event_handler,
+                                             (void *)domain);
+
+  if (status != 0)
+    c_ipmi_error("Failed to register smi event handler", status);
 } /* void domain_connection_change_handler */
 
 static int thread_init(os_handler_t **ret_os_handler) {
@@ -504,7 +729,8 @@ static int thread_init(os_handler_t **ret_os_handler) {
 
   status = ipmi_open_domain(
       "mydomain", &smi_connection, /* num_con = */ 1,
-      domain_connection_change_handler, /* user data = */ NULL,
+      domain_connection_change_handler,
+      /* user data = */ (void *)smi_connection,
       /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, open_option,
       sizeof(open_option) / sizeof(open_option[0]), &domain_id);
   if (status != 0) {
@@ -545,19 +771,17 @@ static int c_ipmi_config(const char *key, const char *value) {
   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);
+    ignorelist_set_invert(ignorelist, !IS_TRUE(value));
   } else if (strcasecmp("NotifySensorAdd", key) == 0) {
-    if (IS_TRUE(value))
-      c_ipmi_nofiy_add = 1;
+    c_ipmi_notify_add = IS_TRUE(value);
   } else if (strcasecmp("NotifySensorRemove", key) == 0) {
-    if (IS_TRUE(value))
-      c_ipmi_nofiy_remove = 1;
+    c_ipmi_notify_remove = IS_TRUE(value);
   } else if (strcasecmp("NotifySensorNotPresent", key) == 0) {
-    if (IS_TRUE(value))
-      c_ipmi_nofiy_notpresent = 1;
+    c_ipmi_notify_notpresent = IS_TRUE(value);
+  } else if (strcasecmp("SELEnabled", key) == 0) {
+    c_ipmi_sel_enabled = IS_TRUE(value);
+  } else if (strcasecmp("SELClearEvent", key) == 0) {
+    c_ipmi_sel_clear_event = IS_TRUE(value);
   } else {
     return -1;
   }
index c2147fd..bd088ec 100644 (file)
@@ -51,6 +51,7 @@ struct web_page_s;
 typedef struct web_page_s web_page_t;
 struct web_page_s /* {{{ */
 {
+  char *plugin_name;
   char *instance;
 
   char *server;
@@ -94,6 +95,7 @@ static void cmc_web_page_free(web_page_t *wp) /* {{{ */
     memcached_free(wp->memc);
   wp->memc = NULL;
 
+  sfree(wp->plugin_name);
   sfree(wp->instance);
   sfree(wp->server);
   sfree(wp->key);
@@ -302,6 +304,8 @@ static int cmc_config_add_page(oconfig_item_t *ci) /* {{{ */
       status = cmc_config_add_string("Server", &page->server, child);
     else if (strcasecmp("Key", child->key) == 0)
       status = cmc_config_add_string("Key", &page->key, child);
+    else if (strcasecmp("Plugin", child->key) == 0)
+      status = cmc_config_add_string("Plugin", &page->plugin_name, child);
     else if (strcasecmp("Match", child->key) == 0)
       /* Be liberal with failing matches => don't set `status'. */
       cmc_config_add_match(page, child);
@@ -407,7 +411,8 @@ static void cmc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */
 
   vl.values = &value;
   vl.values_len = 1;
-  sstrncpy(vl.plugin, "memcachec", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "memcachec",
+           sizeof (vl.plugin));
   sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, wm->type, sizeof(vl.type));
   sstrncpy(vl.type_instance, wm->instance, sizeof(vl.type_instance));
index 44bd564..099013e 100644 (file)
@@ -62,6 +62,7 @@ struct o_database_s {
   char *connect_id;
   char *username;
   char *password;
+  char *plugin_name;
 
   udb_query_preparation_area_t **q_prep_areas;
   udb_query_t **queries;
@@ -141,6 +142,7 @@ static void o_database_free(o_database_t *db) /* {{{ */
   sfree(db->username);
   sfree(db->password);
   sfree(db->queries);
+  sfree(db->plugin_name);
 
   if (db->q_prep_areas != NULL)
     for (size_t i = 0; i < db->queries_num; ++i)
@@ -192,6 +194,7 @@ static int o_config_add_database(oconfig_item_t *ci) /* {{{ */
   db->connect_id = NULL;
   db->username = NULL;
   db->password = NULL;
+  db->plugin_name = NULL;
 
   status = cf_util_get_string(ci, &db->name);
   if (status != 0) {
@@ -211,6 +214,8 @@ static int o_config_add_database(oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string(child, &db->username);
     else if (strcasecmp("Password", child->key) == 0)
       status = cf_util_get_string(child, &db->password);
+    else if (strcasecmp("Plugin", child->key) == 0)
+      status = cf_util_get_string(child, &db->plugin_name);
     else if (strcasecmp("Query", child->key) == 0)
       status = udb_query_pick_from_list(child, queries, queries_num,
                                         &db->queries, &db->queries_num);
@@ -544,7 +549,8 @@ static int o_read_database_query(o_database_t *db, /* {{{ */
 
   status = udb_query_prepare_result(
       q, prep_area, (db->host != NULL) ? db->host : hostname_g,
-      /* plugin = */ "oracle", db->name, column_names, column_num,
+      /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "oracle",
+      db->name, column_names, column_num,
       /* interval = */ 0);
   if (status != 0) {
     ERROR("oracle plugin: o_read_database_query (%s, %s): "
index 56730b4..3b702ae 100644 (file)
@@ -139,6 +139,7 @@ typedef struct {
   char *password;
 
   char *instance;
+  char *plugin_name;
 
   char *sslmode;
 
@@ -250,6 +251,8 @@ static c_psql_database_t *c_psql_database_new(const char *name) {
 
   db->instance = sstrdup(name);
 
+  db->plugin_name = NULL;
+
   db->sslmode = NULL;
 
   db->krbsrvname = NULL;
@@ -300,6 +303,8 @@ static void c_psql_database_delete(void *data) {
 
   sfree(db->instance);
 
+  sfree(db->plugin_name);
+
   sfree(db->sslmode);
 
   sfree(db->krbsrvname);
@@ -539,9 +544,11 @@ static int c_psql_exec_query(c_psql_database_t *db, udb_query_t *q,
   else
     host = db->host;
 
-  status =
-      udb_query_prepare_result(q, prep_area, host, "postgresql", db->instance,
-                               column_names, (size_t)column_num, db->interval);
+  status = udb_query_prepare_result(
+      q, prep_area, host,
+      (db->plugin_name != NULL) ? db->plugin_name : "postgresql",
+      db->instance, column_names, (size_t)column_num, db->interval);
+
   if (0 != status) {
     log_err("udb_query_prepare_result failed with status %i.", status);
     BAIL_OUT(-1);
@@ -1139,6 +1146,8 @@ static int c_psql_config_database(oconfig_item_t *ci) {
       cf_util_get_string(c, &db->password);
     else if (0 == strcasecmp(c->key, "Instance"))
       cf_util_get_string(c, &db->instance);
+    else if (0 == strcasecmp (c->key, "Plugin"))
+      cf_util_get_string (c, &db->plugin_name);
     else if (0 == strcasecmp(c->key, "SSLMode"))
       cf_util_get_string(c, &db->sslmode);
     else if (0 == strcasecmp(c->key, "KRBSrvName"))
index 727ec7f..89cf3e0 100644 (file)
@@ -33,6 +33,7 @@
  *   Clément Stenac <clement.stenac at diwi.org>
  *   Cosmin Ioiart <cioiart at gmail.com>
  *   Pavel Rochnyack <pavel2000 at ngs.ru>
+ *   Wilfried Goesgens <dothebart at citadel.org>
  **/
 
 #include "collectd.h"
@@ -168,6 +169,7 @@ typedef struct process_entry_s {
   unsigned long num_proc;
   unsigned long num_lwp;
   unsigned long num_fd;
+  unsigned long num_maps;
   unsigned long vmem_size;
   unsigned long vmem_rss;
   unsigned long vmem_data;
@@ -194,6 +196,8 @@ typedef struct process_entry_s {
   _Bool has_cswitch;
 
   _Bool has_fd;
+
+  _Bool has_maps;
 } process_entry_t;
 
 typedef struct procstat_entry_s {
@@ -229,6 +233,7 @@ typedef struct procstat {
   unsigned long num_proc;
   unsigned long num_lwp;
   unsigned long num_fd;
+  unsigned long num_maps;
   unsigned long vmem_size;
   unsigned long vmem_rss;
   unsigned long vmem_data;
@@ -253,6 +258,7 @@ typedef struct procstat {
   derive_t cswitch_invol;
 
   _Bool report_fd_num;
+  _Bool report_maps_num;
   _Bool report_ctx_switch;
 
   struct procstat *next;
@@ -264,6 +270,7 @@ static procstat_t *list_head_g = NULL;
 static _Bool want_init = 1;
 static _Bool report_ctx_switch = 0;
 static _Bool report_fd_num = 0;
+static _Bool report_maps_num = 0;
 
 #if HAVE_THREAD_INFO
 static mach_port_t port_host_self;
@@ -322,6 +329,7 @@ static procstat_t *ps_list_register(const char *name, const char *regexp) {
   new->cswitch_invol = -1;
 
   new->report_fd_num = report_fd_num;
+  new->report_maps_num = report_maps_num;
   new->report_ctx_switch = report_ctx_switch;
 
 #if HAVE_REGEX_H
@@ -472,6 +480,7 @@ static void ps_list_add(const char *name, const char *cmdline,
     ps->num_proc += entry->num_proc;
     ps->num_lwp += entry->num_lwp;
     ps->num_fd += entry->num_fd;
+    ps->num_maps += entry->num_maps;
     ps->vmem_size += entry->vmem_size;
     ps->vmem_rss += entry->vmem_rss;
     ps->vmem_data += entry->vmem_data;
@@ -521,6 +530,7 @@ static void ps_list_reset(void) {
     ps->num_proc = 0;
     ps->num_lwp = 0;
     ps->num_fd = 0;
+    ps->num_maps = 0;
     ps->vmem_size = 0;
     ps->vmem_rss = 0;
     ps->vmem_data = 0;
@@ -561,6 +571,8 @@ static void ps_tune_instance(oconfig_item_t *ci, procstat_t *ps) {
       cf_util_get_boolean(c, &ps->report_ctx_switch);
     else if (strcasecmp(c->key, "CollectFileDescriptor") == 0)
       cf_util_get_boolean(c, &ps->report_fd_num);
+    else if (strcasecmp(c->key, "CollectMemoryMaps") == 0)
+      cf_util_get_boolean(c, &ps->report_maps_num);
     else {
       ERROR("processes plugin: Option `%s' not allowed here.", c->key);
     }
@@ -619,6 +631,8 @@ static int ps_config(oconfig_item_t *ci) {
       cf_util_get_boolean(c, &report_ctx_switch);
     } else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) {
       cf_util_get_boolean(c, &report_fd_num);
+    } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) {
+      cf_util_get_boolean(c, &report_maps_num);
     } else {
       ERROR("processes plugin: The `%s' configuration option is not "
             "understood and will be ignored.",
@@ -768,6 +782,14 @@ static void ps_submit_proc_list(procstat_t *ps) {
     plugin_dispatch_values(&vl);
   }
 
+  if (ps->num_maps > 0) {
+    sstrncpy(vl.type, "file_handles", sizeof(vl.type));
+    sstrncpy(vl.type_instance, "mapped", sizeof(vl.type_instance));
+    vl.values[0].gauge = ps->num_maps;
+    vl.values_len = 1;
+    plugin_dispatch_values(&vl);
+  }
+
   if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) {
     sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
     sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
@@ -782,20 +804,23 @@ static void ps_submit_proc_list(procstat_t *ps) {
     plugin_dispatch_values(&vl);
   }
 
-  DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; "
-        "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
-        "vmem_code = %lu; "
-        "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
-        "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
-        "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
-        "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
-        "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
-        "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
-        ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->vmem_size,
-        ps->vmem_rss, ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter,
-        ps->vmem_majflt_counter, ps->cpu_user_counter, ps->cpu_system_counter,
-        ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, ps->io_diskr,
-        ps->io_diskw, ps->cswitch_vol, ps->cswitch_invol);
+  DEBUG(
+      "name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; "
+      "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+      "vmem_code = %lu; "
+      "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
+      "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
+      "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
+      "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
+      "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
+      "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
+      ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps,
+      ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code,
+      ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
+      ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr,
+      ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol,
+      ps->cswitch_invol);
+
 } /* void ps_submit_proc_list */
 
 #if KERNEL_LINUX || KERNEL_SOLARIS
@@ -1004,6 +1029,31 @@ static int ps_read_io(process_entry_t *ps) {
   return 0;
 } /* int ps_read_io (...) */
 
+static int ps_count_maps(pid_t pid) {
+  FILE *fh;
+  char buffer[1024];
+  char filename[64];
+  int count = 0;
+
+  snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+  if ((fh = fopen(filename, "r")) == NULL) {
+    DEBUG("ps_count_maps: Failed to open file `%s'", filename);
+    return -1;
+  }
+
+  while (fgets(buffer, sizeof(buffer), fh) != NULL) {
+    if (strchr(buffer, '\n')) {
+      count++;
+    }
+  } /* while (fgets) */
+
+  if (fclose(fh)) {
+    char errbuf[1024];
+    WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+  }
+  return count;
+} /* int ps_count_maps (...) */
+
 static int ps_count_fd(int pid) {
   char dirname[64];
   DIR *dh;
@@ -1040,6 +1090,14 @@ static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) {
     }
   }
 
+  if (ps->report_maps_num) {
+    int num_maps;
+    if (entry->has_maps == 0 && (num_maps = ps_count_maps(entry->id)) > 0) {
+      entry->num_maps = num_maps;
+    }
+    entry->has_maps = 1;
+  }
+
   if (ps->report_fd_num) {
     int num_fd;
     if (entry->has_fd == 0 && (num_fd = ps_count_fd(entry->id)) > 0) {
@@ -1421,6 +1479,9 @@ static int ps_read_process(long pid, process_entry_t *ps, char *state) {
    */
   ps->num_fd = 0;
 
+  /* Number of memory mappings */
+  ps->num_maps = 0;
+
   /*
    * Calculating input/ouput chars
    * Formula used is total chars / total blocks => chars/block
@@ -1656,6 +1717,9 @@ static int ps_read(void) {
         /* File descriptor count not implemented */
         pse.num_fd = 0;
 
+        /* Number of memory mappings */
+        pse.num_maps = 0;
+
         pse.vmem_minflt_counter = task_events_info.cow_faults;
         pse.vmem_majflt_counter = task_events_info.faults;
 
@@ -1961,6 +2025,9 @@ static int ps_read(void) {
       /* file descriptor count not implemented */
       pse.num_fd = 0;
 
+      /* Number of memory mappings */
+      pse.num_maps = 0;
+
       /* context switch counters not implemented */
       pse.cswitch_vol = -1;
       pse.cswitch_invol = -1;
@@ -2102,6 +2169,9 @@ static int ps_read(void) {
       /* file descriptor count not implemented */
       pse.num_fd = 0;
 
+      /* Number of memory mappings */
+      pse.num_maps = 0;
+
       /* context switch counters not implemented */
       pse.cswitch_vol = -1;
       pse.cswitch_invol = -1;
@@ -2265,6 +2335,7 @@ static int ps_read(void) {
       pse.io_diskw = -1;
 
       pse.num_fd = 0;
+      pse.num_maps = 0;
 
       pse.cswitch_vol = -1;
       pse.cswitch_invol = -1;
index aa3c9dd..1ac65c8 100644 (file)
@@ -1327,8 +1327,6 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
 
   status = 0;
   while (status == 0) {
-    int oid_list_todo_num;
-
     req = snmp_pdu_create(SNMP_MSG_GETNEXT);
     if (req == NULL) {
       ERROR("snmp plugin: snmp_pdu_create failed.");
@@ -1336,24 +1334,33 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
       break;
     }
 
-    oid_list_todo_num = 0;
+    size_t oid_list_todo_num = 0;
+    size_t var_idx[oid_list_len];
+    memset(var_idx, 0, sizeof(var_idx));
+
     for (i = 0; i < oid_list_len; i++) {
       /* Do not rerequest already finished OIDs */
       if (!oid_list_todo[i])
         continue;
-      oid_list_todo_num++;
       snmp_add_null_var(req, oid_list[i].oid, oid_list[i].oid_len);
+      var_idx[oid_list_todo_num] = i;
+      oid_list_todo_num++;
     }
 
     if (oid_list_todo_num == 0) {
       /* The request is still empty - so we are finished */
       DEBUG("snmp plugin: all variables have left their subtree");
+      snmp_free_pdu(req);
       status = 0;
       break;
     }
 
     res = NULL;
     status = snmp_sess_synch_response(host->sess_handle, req, &res);
+
+    /* snmp_sess_synch_response always frees our req PDU */
+    req = NULL;
+
     if ((status != STAT_SUCCESS) || (res == NULL)) {
       char *errstr = NULL;
 
@@ -1367,8 +1374,6 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
         snmp_free_pdu(res);
       res = NULL;
 
-      /* snmp_synch_response already freed our PDU */
-      req = NULL;
       sfree(errstr);
       csnmp_host_close_session(host);
 
@@ -1388,6 +1393,39 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
       break;
     }
 
+    if (res->errstat != SNMP_ERR_NOERROR) {
+      if (res->errindex != 0) {
+        /* Find the OID which caused error */
+        for (i = 1, vb = res->variables; vb != NULL && i != res->errindex;
+             vb = vb->next_variable, i++)
+          /* do nothing */;
+      }
+
+      if ((res->errindex == 0) || (vb == NULL)) {
+        ERROR("snmp plugin: host %s; data %s: response error: %s (%li) ",
+              host->name, data->name, snmp_errstring(res->errstat),
+              res->errstat);
+        status = -1;
+        break;
+      }
+
+      char oid_buffer[1024] = {0};
+      snprint_objid(oid_buffer, sizeof(oid_buffer) - 1, vb->name,
+                    vb->name_length);
+      NOTICE("snmp plugin: host %s; data %s: OID `%s` failed: %s", host->name,
+             data->name, oid_buffer, snmp_errstring(res->errstat));
+
+      /* Get value index from todo list and skip OID found */
+      assert(res->errindex <= oid_list_todo_num);
+      i = var_idx[res->errindex - 1];
+      assert(i < oid_list_len);
+      oid_list_todo[i] = 0;
+
+      snmp_free_pdu(res);
+      res = NULL;
+      continue;
+    }
+
     for (vb = res->variables, i = 0; (vb != NULL);
          vb = vb->next_variable, i++) {
       /* Calculate value index from todo list */
@@ -1483,9 +1521,6 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
     snmp_free_pdu(res);
   res = NULL;
 
-  if (req != NULL)
-    snmp_free_pdu(req);
-  req = NULL;
 
   if (status == 0)
     csnmp_dispatch_table(host, data, instance_list_head, value_list_head);
index 407970a..fbba478 100644 (file)
@@ -34,7 +34,8 @@
 /*
  *  <Plugin tail>
  *    <File "/var/log/exim4/mainlog">
- *     Instance "exim"
+ *      Plugin "mail"
+ *      Instance "exim"
  *      Interval 60
  *     <Match>
  *       Regex "S=([1-9][0-9]*)"
@@ -134,6 +135,7 @@ static int ctail_config_add_match_dstype(ctail_config_match_t *cm,
 } /* int ctail_config_add_match_dstype */
 
 static int ctail_config_add_match(cu_tail_match_t *tm,
+                                  const char *plugin_name,
                                   const char *plugin_instance,
                                   oconfig_item_t *ci, cdtime_t interval) {
   ctail_config_match_t cm = {0};
@@ -191,7 +193,8 @@ static int ctail_config_add_match(cu_tail_match_t *tm,
   if (status == 0) {
     // TODO(octo): there's nothing "simple" about the latency stuff …
     status = tail_match_add_match_simple(
-        tm, cm.regex, cm.excluderegex, cm.flags, "tail", plugin_instance,
+        tm, cm.regex, cm.excluderegex, cm.flags,
+        (plugin_name != NULL) ? plugin_name : "tail", plugin_instance,
         cm.type, cm.type_instance, cm.latency, interval);
 
     if (status != 0)
@@ -210,6 +213,7 @@ static int ctail_config_add_match(cu_tail_match_t *tm,
 static int ctail_config_add_file(oconfig_item_t *ci) {
   cu_tail_match_t *tm;
   cdtime_t interval = 0;
+  char *plugin_name = NULL;
   char *plugin_instance = NULL;
   int num_matches = 0;
 
@@ -229,12 +233,15 @@ static int ctail_config_add_file(oconfig_item_t *ci) {
     oconfig_item_t *option = ci->children + i;
     int status = 0;
 
-    if (strcasecmp("Instance", option->key) == 0)
+    if (strcasecmp("Plugin", option->key) == 0)
+      status = cf_util_get_string (option, &plugin_name);
+    else if (strcasecmp("Instance", option->key) == 0)
       status = cf_util_get_string(option, &plugin_instance);
     else if (strcasecmp("Interval", option->key) == 0)
       cf_util_get_cdtime(option, &interval);
     else if (strcasecmp("Match", option->key) == 0) {
-      status = ctail_config_add_match(tm, plugin_instance, option, interval);
+      status = ctail_config_add_match(tm, plugin_name, plugin_instance, option,
+                                      interval);
       if (status == 0)
         num_matches++;
       /* Be mild with failed matches.. */
@@ -247,6 +254,7 @@ static int ctail_config_add_file(oconfig_item_t *ci) {
       break;
   } /* for (i = 0; i < ci->children_num; i++) */
 
+  sfree(plugin_name);
   sfree(plugin_instance);
 
   if (num_matches == 0) {
index a9ce5d1..2e3ac5f 100644 (file)
@@ -44,6 +44,7 @@ struct metric_definition_s {
 typedef struct metric_definition_s metric_definition_t;
 
 struct instance_definition_s {
+  char *plugin_name;
   char *instance;
   char *path;
   cu_tail_t *tail;
@@ -67,7 +68,8 @@ static int tcsv_submit(instance_definition_t *id, metric_definition_t *md,
   vl.values_len = 1;
   vl.values = &v;
 
-  sstrncpy(vl.plugin, "tail_csv", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, (id->plugin_name != NULL) ? id->plugin_name : "tail_csv",
+           sizeof(vl.plugin));
   if (id->instance != NULL)
     sstrncpy(vl.plugin_instance, id->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, md->type, sizeof(vl.type));
@@ -358,6 +360,7 @@ static void tcsv_instance_definition_destroy(void *arg) {
     cu_tail_destroy(id->tail);
   id->tail = NULL;
 
+  sfree(id->plugin_name);
   sfree(id->instance);
   sfree(id->path);
   sfree(id->metric_list);
@@ -420,6 +423,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
   id = calloc(1, sizeof(*id));
   if (id == NULL)
     return -1;
+  id->plugin_name = NULL;
   id->instance = NULL;
   id->path = NULL;
   id->metric_list = NULL;
@@ -447,6 +451,8 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
       cf_util_get_cdtime(option, &id->interval);
     else if (strcasecmp("TimeFrom", option->key) == 0)
       status = tcsv_config_get_index(option, &id->time_from);
+    else if (strcasecmp("Plugin", option->key) == 0)
+      status = cf_util_get_string(option, &id->plugin_name);
     else {
       WARNING("tail_csv plugin: Option `%s' not allowed here.", option->key);
       status = -1;
index 00b930e..99e39ee 100644 (file)
@@ -134,7 +134,13 @@ static int us_open_socket(void) {
     const char *grpname;
     struct group *g;
     struct group sg;
-    char grbuf[4096];
+
+    long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+    if (grbuf_size <= 0)
+      grbuf_size = sysconf(_SC_PAGESIZE);
+    if (grbuf_size <= 0)
+      grbuf_size = 4096;
+    char grbuf[grbuf_size];
 
     grpname = (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME;
     g = NULL;
index df70fbe..d3edb57 100644 (file)
@@ -294,7 +294,7 @@ void match_value_reset(cu_match_value_t *mv) {
   /* Reset GAUGE metrics only and except GAUGE_PERSIST. */
   if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
       !(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
-    mv->value.gauge = NAN;
+    mv->value.gauge = (mv->ds_type & UTILS_MATCH_CF_GAUGE_INC) ? 0 : NAN;
     mv->values_num = 0;
   }
 } /* }}} void match_value_reset */
index a0286d3..e4daf4b 100644 (file)
@@ -367,10 +367,10 @@ static int varnish_monitor(void *priv,
                                    "workspace_overflow", val);
 #if HAVE_VARNISH_V4
     else if (strcmp(name, "exp_mailed") == 0)
-      return varnish_submit_derive(conf->instance, "struct", "objects",
+      return varnish_submit_gauge(conf->instance, "struct", "objects",
                                   "exp_mailed", val);
     else if (strcmp(name, "exp_received") == 0)
-      return varnish_submit_derive(conf->instance, "struct", "objects",
+      return varnish_submit_gauge(conf->instance, "struct", "objects",
                                   "exp_received", val);
 #endif
   }
@@ -442,10 +442,10 @@ static int varnish_monitor(void *priv,
       return varnish_submit_derive(conf->instance, "ban", "total_operations",
                                    "obj_killed", val);
     else if (strcmp(name, "bans_persisted_bytes") == 0)
-      return varnish_submit_gauge(conf->instance, "ban", "total_bytes",
+      return varnish_submit_derive(conf->instance, "ban", "total_bytes",
                                    "persisted_bytes", val);
     else if (strcmp(name, "bans_persisted_fragmentation") == 0)
-      return varnish_submit_gauge(conf->instance, "ban", "total_bytes",
+      return varnish_submit_derive(conf->instance, "ban", "total_bytes",
                                    "persisted_fragmentation", val);
     else if (strcmp(name, "bans_tests_tested") == 0)
       return varnish_submit_derive(conf->instance, "ban", "total_operations",
@@ -668,7 +668,7 @@ static int varnish_monitor(void *priv,
       return varnish_submit_derive(conf->instance, "workers", "total_threads",
                                    "dropped", val);
     else if (strcmp(name, "thread_queue_len") == 0)
-      return varnish_submit_derive(conf->instance, "workers", "queue_length",
+      return varnish_submit_gauge(conf->instance, "workers", "queue_length",
                                    "threads", val);
     else if (strcmp(name, "n_wrk") == 0)
       return varnish_submit_gauge(conf->instance, "workers", "threads",
@@ -761,7 +761,7 @@ static int varnish_monitor(void *priv,
       return varnish_submit_derive(conf->instance, "vbe", "connections",
                                   "c_conns", val);
     else if (strcmp(name, "req") == 0)
-      return varnish_submit_gauge(conf->instance, "vbe", "http_requests",
+      return varnish_submit_derive(conf->instance, "vbe", "http_requests",
                                   "b_reqs", val);
   }
 
@@ -839,10 +839,10 @@ static int varnish_monitor(void *priv,
 
   if (conf->collect_lck) {
     if (strcmp(name, "creat") == 0)
-      return varnish_submit_derive(conf->instance, "lck", "objects",
+      return varnish_submit_gauge(conf->instance, "lck", "objects",
                                   "created", val);
     else if (strcmp(name, "destroy") == 0)
-      return varnish_submit_derive(conf->instance, "lck", "objects",
+      return varnish_submit_gauge(conf->instance, "lck", "objects",
                                   "destroyed", val);
     else if (strcmp(name, "locks") == 0)
       return varnish_submit_derive(conf->instance, "lck", "total_operations",
@@ -869,19 +869,19 @@ static int varnish_monitor(void *priv,
       return varnish_submit_derive(conf->instance, "mempool",
                                    "total_operations", "frees", val);
     else if (strcmp(name, "recycle") == 0)
-      return varnish_submit_derive(conf->instance, "mempool",
+      return varnish_submit_gauge(conf->instance, "mempool",
                                    "objects", "recycled", val);
     else if (strcmp(name, "timeout") == 0)
-      return varnish_submit_derive(conf->instance, "mempool",
+      return varnish_submit_gauge(conf->instance, "mempool",
                                    "objects", "timed_out", val);
     else if (strcmp(name, "toosmall") == 0)
-      return varnish_submit_derive(conf->instance, "mempool",
+      return varnish_submit_gauge(conf->instance, "mempool",
                                    "objects", "too_small", val);
     else if (strcmp(name, "surplus") == 0)
-      return varnish_submit_derive(conf->instance, "mempool",
+      return varnish_submit_gauge(conf->instance, "mempool",
                                    "objects", "surplus", val);
     else if (strcmp(name, "randry") == 0)
-      return varnish_submit_derive(conf->instance, "mempool",
+      return varnish_submit_gauge(conf->instance, "mempool",
                                    "objects", "ran_dry", val);
   }
 
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;