protocols plugin: Add a plugin which collets protocol information.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 28 Feb 2009 11:12:54 +0000 (12:12 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 28 Feb 2009 11:18:42 +0000 (12:18 +0100)
Currently a Linux only plugin. This plugin collects information about
IP, TCP, UDP, etc.

Thanks to Ulf Zimmermann for his (indirect) suggestion ;)

README
configure.in
src/Makefile.am
src/collectd.conf.pod
src/common.c
src/common.h
src/protocols.c [new file with mode: 0644]
src/types.db

diff --git a/README b/README
index a6a5354..7889d2c 100644 (file)
--- a/README
+++ b/README
@@ -179,6 +179,9 @@ Features
     - processes
       Process counts: Number of running, sleeping, zombie, ... processes.
 
+    - protocols
+      Counts various aspects of network protocols such as IP, TCP, UDP, etc.
+
     - rrdcached
       RRDtool caching daemon (RRDcacheD) statistics.
 
index 401604f..c772f36 100644 (file)
@@ -2947,6 +2947,7 @@ plugin_multimeter="no"
 plugin_nfs="no"
 plugin_perl="no"
 plugin_processes="no"
+plugin_protocols="no"
 plugin_serial="no"
 plugin_swap="no"
 plugin_tape="no"
@@ -2971,6 +2972,7 @@ then
        plugin_memory="yes"
        plugin_nfs="yes"
        plugin_processes="yes"
+       plugin_protocols="yes"
        plugin_serial="yes"
        plugin_swap="yes"
        plugin_tcpconns="yes"
@@ -3180,6 +3182,7 @@ AC_PLUGIN([ping],        [$with_liboping],     [Network latency statistics])
 AC_PLUGIN([postgresql],  [$with_libpq],        [PostgreSQL database statistics])
 AC_PLUGIN([powerdns],    [yes],                [PowerDNS statistics])
 AC_PLUGIN([processes],   [$plugin_processes],  [Process statistics])
+AC_PLUGIN([protocols],   [$plugin_protocols],  [Protocol (IP, TCP, ...) statistics])
 AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
 AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
 AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
index f4f2be9..5401bba 100644 (file)
@@ -691,6 +691,14 @@ processes_la_LIBADD += -lkvm
 endif
 endif
 
+if BUILD_PLUGIN_PROTOCOLS
+pkglib_LTLIBRARIES += protocols.la
+protocols_la_SOURCES = protocols.c
+protocols_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" protocols.la
+collectd_DEPENDENCIES += protocols.la
+endif
+
 if BUILD_PLUGIN_RRDCACHED
 pkglib_LTLIBRARIES += rrdcached.la
 rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h
index 1ab4360..391f0f3 100644 (file)
@@ -2287,6 +2287,39 @@ slashes.
 
 =back
 
+=head2 Plugin C<protocols>
+
+Collects a lot of information about various network protocols, such as I<IP>,
+I<TCP>, I<UDP>, etc.
+
+Available configuration options:
+
+=over 4
+
+=item B<Value> I<Selector>
+
+Selects whether or not to select a specific value. The string being matched is
+of the form "I<Protocol>:I<ValueName>", where I<Protocol> will be used as the
+plugin instance and I<ValueName> will be used as type instance. An example of
+the string being used would be C<Tcp:RetransSegs>.
+
+You can use regular expressions to match a large number of values with just one
+configuration option. To select all "extended" I<TCP> values, you could use the
+following statement:
+
+  Value "/^TcpExt:/"
+
+Whether only matched values are selected or all matched values are ignored
+depends on the B<IgnoreSelected>. By default, only matched values are selected.
+If no value is configured at all, all values will be selected.
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+If set to B<true>, inverts the selection made by B<Value>, i.E<nbsp>e. all
+matching values will be ignored.
+
+=back
+
 =head2 Plugin C<rrdcached>
 
 The C<rrdcached> plugin uses the RRDTool accelerator daemon, L<rrdcached(1)>,
index ef80885..b37db88 100644 (file)
@@ -253,7 +253,7 @@ int strsplit (char *string, char **fields, size_t size)
                        break;
        }
 
-       return (i);
+       return ((int) i);
 }
 
 int strjoin (char *dst, size_t dst_len,
index 7209808..113f044 100644 (file)
@@ -98,7 +98,7 @@ ssize_t swrite (int fd, const void *buf, size_t count);
  *
  * DESCRIPTION
  *   Splits a string into parts and stores pointers to the parts in `fields'.
- *   The characters split at are ` ' (space) and "\t" (tab).
+ *   The characters split at are: " ", "\t", "\r", and "\n".
  *
  * PARAMETERS
  *   `string'      String to split. This string will be modified. `fields' will
diff --git a/src/protocols.c b/src/protocols.c
new file mode 100644 (file)
index 0000000..75e9a1c
--- /dev/null
@@ -0,0 +1,248 @@
+/**
+ * collectd - src/protocols.c
+ * Copyright (C) 2009  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_ignorelist.h"
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+#define SNMP_FILE "/proc/net/snmp"
+#define NETSTAT_FILE "/proc/net/netstat"
+
+/*
+ * Global variables
+ */
+static const char *config_keys[] =
+{
+  "Value",
+  "IgnoreSelected",
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *values_list = NULL;
+
+/* 
+ * Functions
+ */
+static void submit (const char *protocol_name,
+    const char *str_key, const char *str_value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  char *tmp_ptr;
+
+  errno = 0;
+  tmp_ptr = NULL;
+  values[0].counter = (counter_t) strtoll (str_value, &tmp_ptr,
+      /* base = */ 0);
+  if ((errno != 0) || (tmp_ptr == str_value))
+  {
+    ERROR ("protocols plugin: Parsing string as integer failed: %s",
+        str_value);
+    return;
+  }
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "protocols", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, protocol_name, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, "protocol_counter", sizeof (vl.type));
+  sstrncpy (vl.type_instance, str_key, sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+} /* void submit */
+
+static int read_file (const char *path)
+{
+  FILE *fh;
+  char key_buffer[4096];
+  char value_buffer[4096];
+  char *key_ptr;
+  char *value_ptr;
+  char *key_fields[256];
+  char *value_fields[256];
+  int key_fields_num;
+  int value_fields_num;
+  int status;
+  int i;
+
+  fh = fopen (path, "r");
+  if (fh == NULL)
+  {
+    ERROR ("protocols plugin: fopen (%s) failed: %s.",
+        path, sstrerror (errno, key_buffer, sizeof (key_buffer)));
+    return (-1);
+  }
+
+  status = -1;
+  while (42)
+  {
+    clearerr (fh);
+    key_ptr = fgets (key_buffer, sizeof (key_buffer), fh);
+    if (key_ptr == NULL)
+    {
+      if (feof (fh) != 0)
+      {
+        status = 0;
+        break;
+      }
+      else if (ferror (fh) != 0)
+      {
+        ERROR ("protocols plugin: Reading from %s failed.", path);
+        break;
+      }
+      else
+      {
+        ERROR ("protocols plugin: fgets failed for an unknown reason.");
+        break;
+      }
+    } /* if (key_ptr == NULL) */
+
+    value_ptr = fgets (value_buffer, sizeof (value_buffer), fh);
+    if (value_ptr == NULL)
+    {
+      ERROR ("protocols plugin: read_file (%s): Could not read values line.",
+          path);
+      break;
+    }
+
+    key_ptr = strchr (key_buffer, ':');
+    if (key_ptr == NULL)
+    {
+      ERROR ("protocols plugin: Could not find protocol name in keys line.");
+      break;
+    }
+    *key_ptr = 0;
+    key_ptr++;
+
+    value_ptr = strchr (value_buffer, ':');
+    if (value_ptr == NULL)
+    {
+      ERROR ("protocols plugin: Could not find protocol name "
+          "in values line.");
+      break;
+    }
+    *value_ptr = 0;
+    value_ptr++;
+
+    if (strcmp (key_buffer, value_buffer) != 0)
+    {
+      ERROR ("protocols plugin: Protocol names in keys and values lines "
+          "don't match: `%s' vs. `%s'.",
+          key_buffer, value_buffer);
+      break;
+    }
+
+
+    key_fields_num = strsplit (key_ptr,
+        key_fields, STATIC_ARRAY_SIZE (key_fields));
+    value_fields_num = strsplit (value_ptr,
+        value_fields, STATIC_ARRAY_SIZE (value_fields));
+
+    if (key_fields_num != value_fields_num)
+    {
+      ERROR ("protocols plugin: Number of fields in keys and values lines "
+          "dont match: %i vs %i.",
+          key_fields_num, value_fields_num);
+      break;
+    }
+
+    for (i = 0; i < key_fields_num; i++)
+    {
+      if (values_list != NULL)
+      {
+        char match_name[2 * DATA_MAX_NAME_LEN];
+
+        ssnprintf (match_name, sizeof (match_name), "%s:%s",
+            key_buffer, key_fields[i]);
+
+        if (ignorelist_match (values_list, match_name))
+          continue;
+      } /* if (values_list != NULL) */
+
+      submit (key_buffer, key_fields[i], value_fields[i]);
+    } /* for (i = 0; i < key_fields_num; i++) */
+  } /* while (42) */
+
+  fclose (fh);
+
+  return (status);
+} /* int read_file */
+
+static int protocols_read (void)
+{
+  int status;
+  int success = 0;
+
+  status = read_file (SNMP_FILE);
+  if (status == 0)
+    success++;
+
+  status = read_file (NETSTAT_FILE);
+  if (status == 0)
+    success++;
+
+  if (success == 0)
+    return (-1);
+
+  return (0);
+} /* int protocols_read */
+
+static int protocols_config (const char *key, const char *value)
+{
+  if (values_list == NULL)
+    values_list = ignorelist_create (/* invert = */ 1);
+
+  if (strcasecmp (key, "Value") == 0)
+  {
+    ignorelist_add (values_list, value);
+  }
+  else if (strcasecmp (key, "IgnoreSelected") == 0)
+  {
+    int invert = 1;
+    if ((strcasecmp (value, "True") == 0)
+        || (strcasecmp (value, "Yes") == 0)
+        || (strcasecmp (value, "On") == 0))
+      invert = 0;
+    ignorelist_set_invert (values_list, invert);
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+} /* int protocols_config */
+
+void module_register (void)
+{
+  plugin_register_config ("protocols", protocols_config,
+      config_keys, config_keys_num);
+  plugin_register_read ("protocols", protocols_read);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et : */
index 82b5b4f..cde4e30 100644 (file)
@@ -86,6 +86,7 @@ pg_xact                       value:COUNTER:0:U
 ping                   ping:GAUGE:0:65535
 players                        value:GAUGE:0:1000000
 power                  value:GAUGE:0:U
+protocol_counter       value:COUNTER:0:U
 ps_count               processes:GAUGE:0:1000000, threads:GAUGE:0:1000000
 ps_cputime             user:COUNTER:0:16000000, syst:COUNTER:0:16000000
 ps_pagefaults          minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807