Merge branch 'collectd-4.10' into collectd-5.0
authorFlorian Forster <octo@collectd.org>
Sun, 1 Apr 2012 10:01:58 +0000 (12:01 +0200)
committerFlorian Forster <octo@collectd.org>
Sun, 1 Apr 2012 10:01:58 +0000 (12:01 +0200)
Conflicts:
ChangeLog
src/collectd.conf.pod
src/common.c
src/network.c
src/processes.c
version-gen.sh

14 files changed:
ChangeLog
configure.in
src/Makefile.am
src/collectd.conf.pod
src/common.c
src/common.h
src/conntrack.c
src/libcollectdclient/client.c
src/memcached.c
src/network.c
src/perl.c
src/processes.c
src/snmp.c
src/tcpconns.c

index 4a99a15..1beef6e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
        * v5upgrade target: Target for converting v4 data sets to the v5
          schema.
 
+2012-03-23, Version 4.10.7
+       * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang
+         for fixing this. Adresses some issues with building the iptables
+         plugin under Gentoo.
+       * libcollectdclient: A memory leak in the lcc_getval() function has
+         been fixed. Thanks to Jason Schmidlapp for finding and fixing this
+         issue.
+       * bind plugin: The use of 'QType" types has been fixed.
+       * df plugin: Fixed compiler issue under Mac OS X 10.7.
+       * conntrack plugin: Support zero as legitimate value. Thanks to Louis
+         Opter for his patch.
+       * memcached plugin: Increased the size of a static buffer, which was
+         truncating status messages form memcached. Thanks to Timon for the
+         patch.
+       * network plugin: Forwarding of notifications has been disabled. This
+         was a contition not checked for before, which may retult in an
+         endless loop.
+       * processes plugin: Support for process names with spaces has been
+         added to the Linux implementation. Thanks to Darrell Bishop for his
+         patch.
+       * perl plugin: A race condition in several callbacks, including log and
+         write callbacks, has been fixed. Thanks to "Rrpv" for reporting this
+         bug.
+       * snmp plugin: A bug when casting unsigned integers to gauge values has
+         been fixed: Unsigned integers would be cast to a signed integer and
+         then to a gauge, possibly resulting in a negative value.
+       * tcpconns plugin: Compilation with newer versions of the FreeBSD
+         runtime has been fixed.
+
 2012-02-19, Version 4.10.6
        * Build system: Fix problems when building the ipvs and iptables
          plugins. Thanks to Sebastian Harl for his patch. A bashism in the
index e30d0f8..bb26716 100644 (file)
@@ -1380,6 +1380,7 @@ AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "x
 AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"])
 if test "x$with_kvm_nlist" = "xyes"
 then
+       AC_CHECK_HEADERS(bsd/nlist.h nlist.h)
        AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
                  [Define to 1 if you have the 'kvm' library with the 'kvm_nlist' symbol (-lkvm)])
        with_libkvm="yes"
@@ -1853,9 +1854,10 @@ then
        AC_CHECK_TYPES([iptc_handle_t, ip6tc_handle_t], [], [])
 fi
 # Check for the iptc_init symbol in the library.
+# This could be in iptc or ip4tc
 if test "x$with_libiptc" = "xpkgconfig"
 then
-       AC_CHECK_LIB(iptc, iptc_init,
+       AC_SEARCH_LIBS(iptc_init, [iptc ip4tc],
                        [with_libiptc="pkgconfig"],
                        [with_libiptc="no"],
                        [$with_libiptc_libs])
index 259a381..ba59bf5 100644 (file)
@@ -85,7 +85,7 @@ endif
 
 if BUILD_WITH_OWN_LIBOCONFIG
 collectd_LDADD += $(LIBLTDL) liboconfig/liboconfig.la
-collectd_DEPENDENCIES += $(LIBLTDL) liboconfig/liboconfig.la
+collectd_DEPENDENCIES += liboconfig/liboconfig.la
 else
 collectd_LDADD += -loconfig
 endif
index 1b0c2fd..1c0f4f0 100644 (file)
@@ -2654,10 +2654,18 @@ The default IPv6 multicast group is C<ff18::efc0:4a42>. The default IPv4
 multicast group is C<239.192.74.66>. The default I<UDP> port is B<25826>.
 
 Both, B<Server> and B<Listen> can be used as single option or as block. When
-used as block, given options are valid for this socket only. For example:
+used as block, given options are valid for this socket only. The following
+example will export the metrics twice: Once to an "internal" server (without
+encryption and signing) and one to an external server (with cryptographic
+signature):
 
  <Plugin "network">
+   # Export to an internal server
+   # (demonstrates usage without additional options)
    Server "collectd.internal.tld"
+   
+   # Export to an external server
+   # (demonstrates usage with signature options)
    <Server "collectd.external.tld">
      SecurityLevel "sign"
      Username "myhostname"
@@ -4831,6 +4839,170 @@ number.
 
 =back
 
+=head1 THRESHOLD CONFIGURATION
+
+Starting with version C<4.3.0> collectd has support for B<monitoring>. By that
+we mean that the values are not only stored or sent somewhere, but that they
+are judged and, if a problem is recognized, acted upon. The only action
+collectd takes itself is to generate and dispatch a "notification". Plugins can
+register to receive notifications and perform appropriate further actions.
+
+Since systems and what you expect them to do differ a lot, you can configure
+B<thresholds> for your values freely. This gives you a lot of flexibility but
+also a lot of responsibility.
+
+Every time a value is out of range a notification is dispatched. This means
+that the idle percentage of your CPU needs to be less then the configured
+threshold only once for a notification to be generated. There's no such thing
+as a moving average or similar - at least not now.
+
+Also, all values that match a threshold are considered to be relevant or
+"interesting". As a consequence collectd will issue a notification if they are
+not received for B<Timeout> iterations. The B<Timeout> configuration option is
+explained in section L<"GLOBAL OPTIONS">. If, for example, B<Timeout> is set to
+"2" (the default) and some hosts sends it's CPU statistics to the server every
+60 seconds, a notification will be dispatched after about 120 seconds. It may
+take a little longer because the timeout is checked only once each B<Interval>
+on the server.
+
+When a value comes within range again or is received after it was missing, an
+"OKAY-notification" is dispatched.
+
+Here is a configuration example to get you started. Read below for more
+information.
+
+ <Threshold>
+   <Type "foo">
+     WarningMin    0.00
+     WarningMax 1000.00
+     FailureMin    0.00
+     FailureMax 1200.00
+     Invert false
+     Instance "bar"
+   </Type>
+
+   <Plugin "interface">
+     Instance "eth0"
+     <Type "if_octets">
+       FailureMax 10000000
+       DataSource "rx"
+     </Type>
+   </Plugin>
+
+   <Host "hostname">
+     <Type "cpu">
+       Instance "idle"
+       FailureMin 10
+     </Type>
+
+     <Plugin "memory">
+       <Type "memory">
+         Instance "cached"
+         WarningMin 100000000
+       </Type>
+     </Plugin>
+   </Host>
+ </Threshold>
+
+There are basically two types of configuration statements: The C<Host>,
+C<Plugin>, and C<Type> blocks select the value for which a threshold should be
+configured. The C<Plugin> and C<Type> blocks may be specified further using the
+C<Instance> option. You can combine the block by nesting the blocks, though
+they must be nested in the above order, i.E<nbsp>e. C<Host> may contain either
+C<Plugin> and C<Type> blocks, C<Plugin> may only contain C<Type> blocks and
+C<Type> may not contain other blocks. If multiple blocks apply to the same
+value the most specific block is used.
+
+The other statements specify the threshold to configure. They B<must> be
+included in a C<Type> block. Currently the following statements are recognized:
+
+=over 4
+
+=item B<FailureMax> I<Value>
+
+=item B<WarningMax> I<Value>
+
+Sets the upper bound of acceptable values. If unset defaults to positive
+infinity. If a value is greater than B<FailureMax> a B<FAILURE> notification
+will be created. If the value is greater than B<WarningMax> but less than (or
+equal to) B<FailureMax> a B<WARNING> notification will be created.
+
+=item B<FailureMin> I<Value>
+
+=item B<WarningMin> I<Value>
+
+Sets the lower bound of acceptable values. If unset defaults to negative
+infinity. If a value is less than B<FailureMin> a B<FAILURE> notification will
+be created. If the value is less than B<WarningMin> but greater than (or equal
+to) B<FailureMin> a B<WARNING> notification will be created.
+
+=item B<DataSource> I<DSName>
+
+Some data sets have more than one "data source". Interesting examples are the
+C<if_octets> data set, which has received (C<rx>) and sent (C<tx>) bytes and
+the C<disk_ops> data set, which holds C<read> and C<write> operations. The
+system load data set, C<load>, even has three data sources: C<shortterm>,
+C<midterm>, and C<longterm>.
+
+Normally, all data sources are checked against a configured threshold. If this
+is undesirable, or if you want to specify different limits for each data
+source, you can use the B<DataSource> option to have a threshold apply only to
+one data source.
+
+=item B<Invert> B<true>|B<false>
+
+If set to B<true> the range of acceptable values is inverted, i.E<nbsp>e.
+values between B<FailureMin> and B<FailureMax> (B<WarningMin> and
+B<WarningMax>) are not okay. Defaults to B<false>.
+
+=item B<Persist> B<true>|B<false>
+
+Sets how often notifications are generated. If set to B<true> one notification
+will be generated for each value that is out of the acceptable range. If set to
+B<false> (the default) then a notification is only generated if a value is out
+of range but the previous value was okay.
+
+This applies to missing values, too: If set to B<true> a notification about a
+missing value is generated once every B<Interval> seconds. If set to B<false>
+only one such notification is generated until the value appears again.
+
+=item B<Percentage> B<true>|B<false>
+
+If set to B<true>, the minimum and maximum values given are interpreted as
+percentage value, relative to the other data sources. This is helpful for
+example for the "df" type, where you may want to issue a warning when less than
+5E<nbsp>% of the total space is available. Defaults to B<false>.
+
+=item B<Hits> I<Number>
+
+Delay creating the notification until the threshold has been passed I<Number>
+times. When a notification has been generated, or when a subsequent value is
+inside the threshold, the counter is reset. If, for example, a value is
+collected once every 10E<nbsp>seconds and B<Hits> is set to 3, a notification
+will be dispatched at most once every 30E<nbsp>seconds.
+
+This is useful when short bursts are not a problem. If, for example, 100% CPU
+usage for up to a minute is normal (and data is collected every
+10E<nbsp>seconds), you could set B<Hits> to B<6> to account for this.
+
+=item B<Hysteresis> I<Number>
+
+When set to non-zero, a hysteresis value is applied when checking minimum and
+maximum bounds. This is useful for values that increase slowly and fluctuate a
+bit while doing so. When these values come close to the threshold, they may
+"flap", i.e. switch between failure / warning case and okay case repeatedly.
+
+If, for example, the threshold is configures as
+
+  WarningMax 100.0
+  Hysteresis 1.0
+
+then a I<Warning> notification is created when the value exceeds I<101> and the
+corresponding I<Okay> notification is only created once the value falls below
+I<99>, thus avoiding the "flapping".
+
+=back
+
 =head1 FILTER CONFIGURATION
 
 Starting with collectd 4.6 there is a powerful filtering infrastructure
index 0069a8b..459c702 100644 (file)
@@ -953,9 +953,25 @@ int parse_identifier_vl (const char *str, value_list_t *vl) /* {{{ */
        return (0);
 } /* }}} int parse_identifier_vl */
 
-int parse_value (const char *value, value_t *ret_value, int ds_type)
+int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
 {
+  char *value;
   char *endptr = NULL;
+  size_t value_len;
+
+  if (value_orig == NULL)
+    return (EINVAL);
+
+  value = strdup (value_orig);
+  if (value == NULL)
+    return (ENOMEM);
+  value_len = strlen (value);
+
+  while ((value_len > 0) && isspace ((int) value[value_len - 1]))
+  {
+    value[value_len - 1] = 0;
+    value_len--;
+  }
 
   switch (ds_type)
   {
@@ -976,11 +992,13 @@ int parse_value (const char *value, value_t *ret_value, int ds_type)
       break;
 
     default:
+      sfree (value);
       ERROR ("parse_value: Invalid data source type: %i.", ds_type);
       return -1;
   }
 
   if (value == endptr) {
+    sfree (value);
     ERROR ("parse_value: Failed to parse string as %s: %s.",
         DS_TYPE_TO_STRING (ds_type), value);
     return -1;
@@ -988,8 +1006,9 @@ int parse_value (const char *value, value_t *ret_value, int ds_type)
   else if ((NULL != endptr) && ('\0' != *endptr))
     INFO ("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
         "Input string was \"%s\".",
-        endptr, DS_TYPE_TO_STRING (ds_type), value);
+        endptr, DS_TYPE_TO_STRING (ds_type), value_orig);
 
+  sfree (value);
   return 0;
 } /* int parse_value */
 
index e6b899d..6b11b53 100644 (file)
@@ -287,6 +287,7 @@ typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
                void *user_data);
 int walk_directory (const char *dir, dirwalk_callback_f callback,
                void *user_data, int hidden);
+/* Returns the number of bytes read or negative on error. */
 int read_file_contents (const char *filename, char *buf, int bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
index e70ff5f..33236c4 100644 (file)
 
 #define CONNTRACK_FILE "/proc/sys/net/netfilter/nf_conntrack_count"
 
-static void conntrack_submit (double conntrack)
+static void conntrack_submit (value_t conntrack)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = conntrack;
-
-       vl.values = values;
+       vl.values = &conntrack;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "conntrack", sizeof (vl.plugin));
@@ -49,14 +46,16 @@ static void conntrack_submit (double conntrack)
 
 static int conntrack_read (void)
 {
-       double conntrack;
+       value_t conntrack;
        FILE *fh;
        char buffer[64];
+       size_t buffer_len;
 
        fh = fopen (CONNTRACK_FILE, "r");
        if (fh == NULL)
                return (-1);
 
+       memset (buffer, 0, sizeof (buffer));
        if (fgets (buffer, sizeof (buffer), fh) == NULL)
        {
                fclose (fh);
@@ -64,10 +63,18 @@ static int conntrack_read (void)
        }
        fclose (fh);
 
-       conntrack = atof (buffer);
+       /* strip trailing newline. */
+       buffer_len = strlen (buffer);
+       while ((buffer_len > 0) && isspace ((int) buffer[buffer_len - 1]))
+       {
+               buffer[buffer_len - 1] = 0;
+               buffer_len--;
+       }
+
+       if (parse_value (buffer, &conntrack, DS_TYPE_GAUGE) != 0)
+               return (-1);
 
-       if (conntrack > 0.0)
-               conntrack_submit (conntrack);
+       conntrack_submit (conntrack);
 
        return (0);
 } /* static int conntrack_read */
index 3eb0d05..f5fe204 100644 (file)
@@ -733,6 +733,8 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
   if (ret_values_names != NULL)
     *ret_values_names = values_names;
 
+  lcc_response_free (&res);
+
   return (0);
 } /* }}} int lcc_getval */
 
index ee3dbe1..48fa713 100644 (file)
@@ -357,7 +357,7 @@ static void submit_gauge2 (const char *type, const char *type_inst,
 
 static int memcached_read (void) /* {{{ */
 {
-       char buf[1024];
+       char buf[4096];
        char *fields[3];
        char *ptr;
        char *line;
index e0c329c..5fed1b1 100644 (file)
@@ -352,6 +352,43 @@ static _Bool check_send_okay (const value_list_t *vl) /* {{{ */
   return (!received);
 } /* }}} _Bool check_send_okay */
 
+static _Bool check_notify_received (const notification_t *n) /* {{{ */
+{
+  notification_meta_t *ptr;
+
+  for (ptr = n->meta; ptr != NULL; ptr = ptr->next)
+    if ((strcmp ("network:received", ptr->name) == 0)
+        && (ptr->type == NM_TYPE_BOOLEAN))
+      return ((_Bool) ptr->nm_value.nm_boolean);
+
+  return (0);
+} /* }}} _Bool check_notify_received */
+
+static _Bool check_send_notify_okay (const notification_t *n) /* {{{ */
+{
+  static c_complain_t complain_forwarding = C_COMPLAIN_INIT_STATIC;
+  _Bool received = 0;
+
+  if (n->meta == NULL)
+    return (1);
+
+  received = check_notify_received (n);
+
+  if (network_config_forward && received)
+  {
+    c_complain_once (LOG_ERR, &complain_forwarding,
+        "network plugin: A notification has been received via the network "
+        "forwarding if enabled. Forwarding of notifications is currently "
+        "not supported, because there is not loop-deteciton available. "
+        "Please contact the collectd mailing list if you need this "
+        "feature.");
+  }
+
+  /* By default, only *send* value lists that were not *received* by the
+   * network plugin. */
+  return (!received);
+} /* }}} _Bool check_send_notify_okay */
+
 static int network_dispatch_values (value_list_t *vl, /* {{{ */
     const char *username)
 {
@@ -415,6 +452,29 @@ static int network_dispatch_values (value_list_t *vl, /* {{{ */
   return (0);
 } /* }}} int network_dispatch_values */
 
+static int network_dispatch_notification (notification_t *n) /* {{{ */
+{
+  int status;
+
+  assert (n->meta == NULL);
+
+  status = plugin_notification_meta_add_boolean (n, "network:received", 1);
+  if (status != 0)
+  {
+    ERROR ("network plugin: plugin_notification_meta_add_boolean failed.");
+    plugin_notification_meta_free (n->meta);
+    n->meta = NULL;
+    return (status);
+  }
+
+  status = plugin_dispatch_notification (n);
+
+  plugin_notification_meta_free (n->meta);
+  n->meta = NULL;
+
+  return (status);
+} /* }}} int network_dispatch_notification */
+
 #if HAVE_LIBGCRYPT
 static gcry_cipher_hd_t network_get_aes256_cypher (sockent_t *se, /* {{{ */
     const void *iv, size_t iv_size, const char *username)
@@ -705,7 +765,7 @@ static int parse_part_values (void **ret_buffer, size_t *ret_buffer_len,
 
        exp_size = 3 * sizeof (uint16_t)
                + pkg_numval * (sizeof (uint8_t) + sizeof (value_t));
-       if ((buffer_len < 0) || (buffer_len < exp_size))
+       if (buffer_len < exp_size)
        {
                WARNING ("network plugin: parse_part_values: "
                                "Packet too short: "
@@ -790,7 +850,7 @@ static int parse_part_number (void **ret_buffer, size_t *ret_buffer_len,
 
        uint16_t pkg_length;
 
-       if ((buffer_len < 0) || ((size_t) buffer_len < exp_size))
+       if (buffer_len < exp_size)
        {
                WARNING ("network plugin: parse_part_number: "
                                "Packet too short: "
@@ -829,7 +889,7 @@ static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
 
        uint16_t pkg_length;
 
-       if ((buffer_len < 0) || (buffer_len < header_size))
+       if (buffer_len < header_size)
        {
                WARNING ("network plugin: parse_part_string: "
                                "Packet too short: "
@@ -1484,7 +1544,7 @@ static int parse_packet (sockent_t *se, /* {{{ */
                        }
                        else
                        {
-                               plugin_dispatch_notification (&n);
+                               network_dispatch_notification (&n);
                        }
                }
                else if (pkg_type == TYPE_SEVERITY)
@@ -3087,14 +3147,17 @@ static int network_config (oconfig_item_t *ci) /* {{{ */
 } /* }}} int network_config */
 
 static int network_notification (const notification_t *n,
-               user_data_t __attribute__((unused)) *user_data)
+    user_data_t __attribute__((unused)) *user_data)
 {
   char  buffer[network_config_packet_size];
   char *buffer_ptr = buffer;
   int   buffer_free = sizeof (buffer);
   int   status;
 
-  memset (buffer, '\0', sizeof (buffer));
+  if (!check_send_notify_okay (n))
+    return (0);
+
+  memset (buffer, 0, sizeof (buffer));
 
   status = write_part_number (&buffer_ptr, &buffer_free, TYPE_TIME_HR,
       (uint64_t) n->time);
@@ -3109,7 +3172,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->host) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_HOST,
-       n->host, strlen (n->host));
+        n->host, strlen (n->host));
     if (status != 0)
       return (-1);
   }
@@ -3117,7 +3180,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->plugin) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_PLUGIN,
-       n->plugin, strlen (n->plugin));
+        n->plugin, strlen (n->plugin));
     if (status != 0)
       return (-1);
   }
@@ -3125,8 +3188,8 @@ static int network_notification (const notification_t *n,
   if (strlen (n->plugin_instance) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free,
-       TYPE_PLUGIN_INSTANCE,
-       n->plugin_instance, strlen (n->plugin_instance));
+        TYPE_PLUGIN_INSTANCE,
+        n->plugin_instance, strlen (n->plugin_instance));
     if (status != 0)
       return (-1);
   }
@@ -3134,7 +3197,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->type) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_TYPE,
-       n->type, strlen (n->type));
+        n->type, strlen (n->type));
     if (status != 0)
       return (-1);
   }
@@ -3142,7 +3205,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->type_instance) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_TYPE_INSTANCE,
-       n->type_instance, strlen (n->type_instance));
+        n->type_instance, strlen (n->type_instance));
     if (status != 0)
       return (-1);
   }
index f8a4822..a2568da 100644 (file)
@@ -1929,6 +1929,11 @@ static int perl_read (void)
                aTHX = t->interp;
        }
 
+       /* Assert that we're not running as the base thread. Otherwise, we might
+        * run into concurrency issues with c_ithread_create(). See
+        * https://github.com/collectd/collectd/issues/9 for details. */
+       assert (aTHX != perl_threads->head->interp);
+
        log_debug ("perl_read: c_ithread: interp = %p (active threads: %i)",
                        aTHX, perl_threads->number_of_threads);
        return pplugin_call_all (aTHX_ PLUGIN_READ);
@@ -1937,6 +1942,7 @@ static int perl_read (void)
 static int perl_write (const data_set_t *ds, const value_list_t *vl,
                user_data_t __attribute__((unused)) *user_data)
 {
+       int status;
        dTHX;
 
        if (NULL == perl_threads)
@@ -1952,9 +1958,20 @@ static int perl_write (const data_set_t *ds, const value_list_t *vl,
                aTHX = t->interp;
        }
 
+       /* Lock the base thread if this is not called from one of the read threads
+        * to avoid race conditions with c_ithread_create(). See
+        * https://github.com/collectd/collectd/issues/9 for details. */
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_lock (&perl_threads->mutex);
+
        log_debug ("perl_write: c_ithread: interp = %p (active threads: %i)",
                        aTHX, perl_threads->number_of_threads);
-       return pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
+       status = pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
+
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_unlock (&perl_threads->mutex);
+
+       return status;
 } /* static int perl_write (const data_set_t *, const value_list_t *) */
 
 static void perl_log (int level, const char *msg,
@@ -1975,7 +1992,17 @@ static void perl_log (int level, const char *msg,
                aTHX = t->interp;
        }
 
+       /* Lock the base thread if this is not called from one of the read threads
+        * to avoid race conditions with c_ithread_create(). See
+        * https://github.com/collectd/collectd/issues/9 for details. */
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_lock (&perl_threads->mutex);
+
        pplugin_call_all (aTHX_ PLUGIN_LOG, level, msg);
+
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_unlock (&perl_threads->mutex);
+
        return;
 } /* static void perl_log (int, const char *) */
 
index 8f4eb88..8a1436e 100644 (file)
@@ -881,9 +881,12 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        char *fields[64];
        char  fields_len;
 
-       int   i;
+       int   buffer_len;
 
-       int   name_len;
+       char *buffer_ptr;
+       size_t name_start_pos;
+       size_t name_end_pos;
+       size_t name_len;
 
        derive_t cpu_user_counter;
        derive_t cpu_system_counter;
@@ -895,33 +898,56 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
 
        ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
 
-       i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
-       if (i <= 0)
+       buffer_len = read_file_contents (filename,
+                       buffer, sizeof(buffer) - 1);
+       if (buffer_len <= 0)
                return (-1);
-       buffer[i] = 0;
-
-       fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
-       if (fields_len < 24)
+       buffer[buffer_len] = 0;
+
+       /* The name of the process is enclosed in parens. Since the name can
+        * contain parens itself, spaces, numbers and pretty much everything
+        * else, use these to determine the process name. We don't use
+        * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
+        * otherwise be required to determine name_len. */
+       name_start_pos = 0;
+       while ((buffer[name_start_pos] != '(')
+                       && (name_start_pos < buffer_len))
+               name_start_pos++;
+
+       name_end_pos = buffer_len;
+       while ((buffer[name_end_pos] != ')')
+                       && (name_end_pos > 0))
+               name_end_pos--;
+
+       /* Either '(' or ')' is not found or they are in the wrong order.
+        * Anyway, something weird that shouldn't happen ever. */
+       if (name_start_pos >= name_end_pos)
        {
-               DEBUG ("processes plugin: ps_read_process (pid = %i):"
-                               " `%s' has only %i fields..",
-                               (int) pid, filename, fields_len);
+               ERROR ("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
+                               name_start_pos, name_end_pos);
                return (-1);
        }
 
-       /* copy the name, strip brackets in the process */
-       name_len = strlen (fields[1]) - 2;
-       if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
+       name_len = (name_end_pos - name_start_pos) - 1;
+       if (name_len >= sizeof (ps->name))
+               name_len = sizeof (ps->name) - 1;
+
+       sstrncpy (ps->name, &buffer[name_start_pos + 1], name_len + 1);
+
+       if ((buffer_len - name_end_pos) < 2)
+               return (-1);
+       buffer_ptr = &buffer[name_end_pos + 2];
+
+       fields_len = strsplit (buffer_ptr, fields, STATIC_ARRAY_SIZE (fields));
+       if (fields_len < 22)
        {
-               DEBUG ("No brackets found in process name: `%s'", fields[1]);
+               DEBUG ("processes plugin: ps_read_process (pid = %i):"
+                               " `%s' has only %i fields..",
+                               (int) pid, filename, fields_len);
                return (-1);
        }
-       fields[1] = fields[1] + 1;
-       fields[1][name_len] = '\0';
-       strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
-
 
-       *state = fields[2][0];
+       *state = fields[0][0];
 
        if (*state == 'Z')
        {
@@ -946,16 +972,16 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                return (0);
        }
 
-       cpu_user_counter   = atoll (fields[13]);
-       cpu_system_counter = atoll (fields[14]);
-       vmem_size          = atoll (fields[22]);
-       vmem_rss           = atoll (fields[23]);
-       ps->vmem_minflt_counter = atoll (fields[9]);
-       ps->vmem_majflt_counter = atoll (fields[11]);
+       cpu_user_counter   = atoll (fields[11]);
+       cpu_system_counter = atoll (fields[12]);
+       vmem_size          = atoll (fields[20]);
+       vmem_rss           = atoll (fields[21]);
+       ps->vmem_minflt_counter = atol (fields[7]);
+       ps->vmem_majflt_counter = atol (fields[9]);
 
        {
-               unsigned long long stack_start = atoll (fields[27]);
-               unsigned long long stack_ptr   = atoll (fields[28]);
+               unsigned long long stack_start = atoll (fields[25]);
+               unsigned long long stack_ptr   = atoll (fields[26]);
 
                stack_size = (stack_start > stack_ptr)
                        ? stack_start - stack_ptr
index 54bcf67..d24ca2c 100644 (file)
@@ -708,7 +708,9 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   value_t ret;
   uint64_t tmp_unsigned = 0;
   int64_t tmp_signed = 0;
-  int defined = 1;
+  _Bool defined = 1;
+  /* Set to true when the original SNMP type appears to have been signed. */
+  _Bool prefer_signed = 0;
 
   if ((vl->type == ASN_INTEGER)
       || (vl->type == ASN_UINTEGER)
@@ -720,7 +722,12 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   {
     tmp_unsigned = (uint32_t) *vl->val.integer;
     tmp_signed = (int32_t) *vl->val.integer;
-    DEBUG ("snmp plugin: Parsed int32 value is %"PRIi64".", tmp_signed);
+
+    if ((vl->type == ASN_INTEGER)
+        || (vl->type == ASN_GAUGE))
+      prefer_signed = 1;
+
+    DEBUG ("snmp plugin: Parsed int32 value is %"PRIu64".", tmp_unsigned);
   }
   else if (vl->type == ASN_COUNTER64)
   {
@@ -809,14 +816,24 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   }
   else if (type == DS_TYPE_GAUGE)
   {
-    ret.gauge = NAN;
-    if (defined != 0)
+    if (!defined)
+      ret.gauge = NAN;
+    else if (prefer_signed)
       ret.gauge = (scale * tmp_signed) + shift;
+    else
+      ret.gauge = (scale * tmp_unsigned) + shift;
   }
   else if (type == DS_TYPE_DERIVE)
-    ret.derive = (derive_t) tmp_signed;
+  {
+    if (prefer_signed)
+      ret.derive = (derive_t) tmp_signed;
+    else
+      ret.derive = (derive_t) tmp_unsigned;
+  }
   else if (type == DS_TYPE_ABSOLUTE)
+  {
     ret.absolute = (absolute_t) tmp_unsigned;
+  }
   else
   {
     ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source "
index d68cd09..78c337b 100644 (file)
 # include <netinet/tcp_var.h>
 # include <netdb.h>
 # include <arpa/inet.h>
-# include <nlist.h>
+# if !defined(HAVE_BSD_NLIST_H) || !HAVE_BSD_NLIST_H
+#  include <nlist.h>
+# else /* HAVE_BSD_NLIST_H */
+#  include <bsd/nlist.h>
+# endif
 # include <kvm.h>
 #endif /* HAVE_LIBKVM_NLIST */