Merge branch 'collectd-4.7'
authorFlorian Forster <octo@huhu.verplant.org>
Wed, 27 May 2009 13:03:53 +0000 (15:03 +0200)
committerFlorian Forster <octo@huhu.verplant.org>
Wed, 27 May 2009 13:03:53 +0000 (15:03 +0200)
README
configure.in
src/Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/couchdb.c [new file with mode: 0644]
src/dns.c
src/olsrd.c [new file with mode: 0644]
src/types.db

diff --git a/README b/README
index ee7163c..c3a5940 100644 (file)
--- a/README
+++ b/README
@@ -43,6 +43,9 @@ Features
     - conntrack
       Number of nf_conntrack entries.
 
+    - couchdb
+      Parse statistics from CouchDB JSON documents.
+
     - cpu
       CPU utilization: Time spent in the system, user, nice, idle, and related
       states.
@@ -539,6 +542,9 @@ Prerequisites
   * libxmms (optional)
     <http://www.xmms.org/>
 
+  * libyajl (optional)
+    Parse JSON data. This is needed for the `couchdb' plugin.
+    <http://www.lloydforge.org/projects/yajl/>
 
 Configuring / Compiling / Installing
 ------------------------------------
index b032b6f..26a921b 100644 (file)
@@ -2966,6 +2966,57 @@ AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you ha
 AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes")
 # }}}
 
+# --with-libyajl {{{
+with_libyajl_cppflags=""
+with_libyajl_ldflags=""
+AC_ARG_WITH(libyajl, [AS_HELP_STRING([--with-libyajl@<:@=PREFIX@:>@], [Path to libyajl.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libyajl_cppflags="-I$withval/include"
+               with_libyajl_ldflags="-L$withval/lib"
+               with_libyajl="yes"
+       else
+               with_libyajl="$withval"
+       fi
+],
+[
+       with_libyajl="yes"
+])
+if test "x$with_libyajl" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
+
+       AC_CHECK_HEADERS(yajl/yajl_parse.h, [with_libyajl="yes"], [with_libyajl="no (yajl/yajl_parse.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libyajl" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
+       LDFLAGS="$LDFLAGS $with_libyajl_ldflags"
+
+       AC_CHECK_LIB(yajl, yajl_alloc, [with_libyajl="yes"], [with_libyajl="no (Symbol 'yajl_alloc' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libyajl" = "xyes"
+then
+       BUILD_WITH_LIBYAJL_CPPFLAGS="$with_libyajl_cppflags"
+       BUILD_WITH_LIBYAJL_LDFLAGS="$with_libyajl_ldflags"
+       BUILD_WITH_LIBYAJL_LIBS="-lyajl"
+       AC_SUBST(BUILD_WITH_LIBYAJL_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBYAJL_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBYAJL_LIBS)
+       AC_DEFINE(HAVE_LIBYAJL, 1, [Define if libyajl is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes")
+# }}}
+
 # pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{
 with_libxml2="no (pkg-config isn't available)"
 with_libxml2_cflags=""
@@ -3298,6 +3349,7 @@ plugin_ascent="no"
 plugin_battery="no"
 plugin_bind="no"
 plugin_conntrack="no"
+plugin_couchdb="no"
 plugin_cpu="no"
 plugin_cpufreq="no"
 plugin_df="no"
@@ -3415,6 +3467,11 @@ then
        plugin_ipmi="yes"
 fi
 
+if test "x$with_libcurl" = "xyes" && test "x$with_libyajl" = "xyes"
+then
+       plugin_couchdb="yes"
+fi
+
 if test "x$have_processor_info" = "xyes"
 then
        plugin_cpu="yes"
@@ -3562,6 +3619,7 @@ AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
 AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
 AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
 AC_PLUGIN([conntrack],   [$plugin_conntrack],  [nf_conntrack statistics])
+AC_PLUGIN([couchdb],     [$plugin_couchdb],    [CouchDB statistics])
 AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
 AC_PLUGIN([cpu],         [$plugin_cpu],        [CPU usage statistics])
 AC_PLUGIN([csv],         [yes],                [CSV output plugin])
@@ -3603,6 +3661,7 @@ AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications])
 AC_PLUGIN([notify_email], [$with_libesmtp],    [Email notifier])
 AC_PLUGIN([ntpd],        [yes],                [NTPd statistics])
 AC_PLUGIN([nut],         [$with_libupsclient], [Network UPS tools statistics])
+AC_PLUGIN([olsrd],       [yes],                [olsrd statistics])
 AC_PLUGIN([onewire],     [$with_libowcapi],    [OneWire sensor statistics])
 AC_PLUGIN([openvpn],     [yes],                [OpenVPN client statistics])
 AC_PLUGIN([oracle],      [$with_oracle],       [Oracle plugin])
@@ -3835,6 +3894,7 @@ Configuration:
     libvirt . . . . . . . $with_libvirt
     libxml2 . . . . . . . $with_libxml2
     libxmms . . . . . . . $with_libxmms
+    libyajl . . . . . . . $with_libyajl
     oracle  . . . . . . . $with_oracle
 
   Features:
@@ -3852,6 +3912,7 @@ Configuration:
     battery . . . . . . . $enable_battery
     bind  . . . . . . . . $enable_bind
     conntrack . . . . . . $enable_conntrack
+    couchdb . . . . . . . $enable_couchdb
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
@@ -3893,6 +3954,7 @@ Configuration:
     notify_email  . . . . $enable_notify_email
     ntpd  . . . . . . . . $enable_ntpd
     nut . . . . . . . . . $enable_nut
+    olsrd . . . . . . . . $enable_olsrd
     onewire . . . . . . . $enable_onewire
     openvpn . . . . . . . $enable_openvpn
     oracle  . . . . . . . $enable_oracle
index 95975a4..1979e4c 100644 (file)
@@ -173,6 +173,21 @@ collectd_LDADD += "-dlopen" conntrack.la
 collectd_DEPENDENCIES += conntrack.la
 endif
 
+if BUILD_PLUGIN_COUCHDB
+pkglib_LTLIBRARIES += couchdb.la
+couchdb_la_SOURCES = couchdb.c
+couchdb_la_CFLAGS = $(AM_CFLAGS)
+couchdb_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBYAJL_LDFLAGS)
+couchdb_la_CPPFLAGS = $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+couchdb_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
+if BUILD_WITH_LIBCURL
+couchdb_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
+couchdb_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
+endif
+collectd_LDADD += "-dlopen" couchdb.la
+collectd_DEPENDENCIES += couchdb.la
+endif
+
 if BUILD_PLUGIN_CPU
 pkglib_LTLIBRARIES += cpu.la
 cpu_la_SOURCES = cpu.c
@@ -638,6 +653,18 @@ collectd_LDADD += "-dlopen" nut.la
 collectd_DEPENDENCIES += nut.la
 endif
 
+if BUILD_PLUGIN_OLSRD
+pkglib_LTLIBRARIES += olsrd.la
+olsrd_la_SOURCES = olsrd.c
+olsrd_la_LDFLAGS = -module -avoid-version
+olsrd_la_LIBADD = 
+if BUILD_WITH_LIBSOCKET
+olsrd_la_LIBADD += -lsocket
+endif
+collectd_LDADD += "-dlopen" olsrd.la
+collectd_DEPENDENCIES += olsrd.la
+endif
+
 if BUILD_PLUGIN_ONEWIRE
 pkglib_LTLIBRARIES += onewire.la
 onewire_la_SOURCES = onewire.c
index 4cbaca8..84a71fa 100644 (file)
@@ -57,6 +57,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
+#@BUILD_PLUGIN_COUCHDB_TRUE@LoadPlugin couchdb
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @LOAD_PLUGIN_CSV@LoadPlugin csv
@@ -94,6 +95,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email
 #@BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd
 #@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
+#@BUILD_PLUGIN_OLSRD_TRUE@LoadPlugin olsrd
 #@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire
 #@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn
 #@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle
@@ -170,6 +172,37 @@ FQDNLookup   true
 #  </View>
 #</Plugin>
 
+#<Plugin couchdb>
+## See: http://wiki.apache.org/couchdb/Runtime_Statistics
+#  <URL "http://localhost:5984/_stats">
+#    Instance "httpd"
+#    <Key "httpd/requests/count">
+#      Type "http_requests"
+#    </Key>
+#
+#    <Key "httpd_request_methods/*/count">
+#      Type "http_request_methods"
+#    </Key>
+#
+#    <Key "httpd_status_codes/*/count">
+#      Type "http_response_codes"
+#    </Key>
+#  </URL>
+## Database status metrics:
+#  <URL "http://localhost:5984/_all_dbs">
+#    Instance "dbs"
+#    <Key "*/doc_count">
+#      Type "gauge"
+#    </Key>
+#    <Key "*/doc_del_count">
+#      Type "counter"
+#    </Key>
+#    <Key "*/disk_size">
+#      Type "bytes"
+#    </Key>
+#  </URL>
+#</Plugin>
+
 #<Plugin csv>
 #      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv"
 #      StoreRates false
@@ -406,6 +439,14 @@ FQDNLookup   true
 #      UPS "upsname@hostname:port"
 #</Plugin>
 
+#<Plugin olsrd>
+#      Host "127.0.0.1"
+#      Port "2006"
+#      CollectLinks "Summary"
+#      CollectRoutes "Summary"
+#      CollectTopology "Summary"
+#</Plugin>
+
 #<Plugin onewire>
 #      Device "-s localhost:4304"
 #      Sensor "F10FCA000800"
index 899daf1..a279d0b 100644 (file)
@@ -409,6 +409,106 @@ By default no detailed zone information is collected.
 
 =back
 
+=head2 Plugin C<couchdb>
+
+The couchdb plugin uses B<libcurl> (L<http://curl.haxx.se/>) and B<libyajl>
+(L<http://www.lloydforge.org/projects/yajl/>) to collect values from CouchDB
+documents (stored JSON notation).
+
+The following example will collect several values from the built-in `_stats'
+runtime statistics module (L<http://wiki.apache.org/couchdb/Runtime_Statistics>).
+
+  <Plugin couchdb>
+    <URL "http://localhost:5984/_stats">
+      Instance "httpd"
+      <Key "httpd/requests/count">
+        Type "http_requests"
+      </Key>
+
+      <Key "httpd_request_methods/*/count">
+        Type "http_request_methods"
+      </Key>
+
+      <Key "httpd_status_codes/*/count">
+        Type "http_response_codes"
+      </Key>
+    </URL>
+  </Plugin>
+
+The following example will collect the status values from each database:
+
+  <URL "http://localhost:5984/_all_dbs">
+    Instance "dbs"
+    <Key "*/doc_count">
+      Type "gauge"
+    </Key>
+    <Key "*/doc_del_count">
+      Type "counter"
+    </Key>
+    <Key "*/disk_size">
+      Type "bytes"
+    </Key>
+  </URL>
+
+In the B<Plugin> block, there may be one or more B<URL> blocks, each defining
+a URL to be fetched via HTTP (libcurl) and one or more B<Key> blocks.
+The B<Key> string argument must be in a path format, of which is used to collect
+a value from a JSON map object.  If a B<Key> path element is that of a I<*> wildcard,
+the values for all keys will be collectd.
+
+The following options are valid within B<URL> blocks:
+
+=over 4
+
+=item B<Instance> I<Instance>
+
+Sets the plugin instance to I<Instance>.
+
+=item B<User> I<Name>
+
+Username to use if authorization is required to read the page.
+
+=item B<Password> I<Password>
+
+Password to use if authorization is required to read the page.
+
+=item B<VerifyPeer> B<true>|B<false>
+
+Enable or disable peer SSL certificate verification. See
+L<http://curl.haxx.se/docs/sslcerts.html> for details. Enabled by default.
+
+=item B<VerifyHost> B<true>|B<false>
+
+Enable or disable peer host name verification. If enabled, the plugin checks if
+the C<Common Name> or a C<Subject Alternate Name> field of the SSL certificate
+matches the host name provided by the B<URL> option. If this identity check
+fails, the connection is aborted. Obviously, only works when connecting to a
+SSL enabled server. Enabled by default.
+
+=item B<CACert> I<file>
+
+File that holds one or more SSL certificates. If you want to use HTTPS you will
+possibly need this option. What CA certificates come bundled with C<libcurl>
+and are checked by default depends on the distribution you use.
+
+=back
+
+The following options are valid within B<Key> blocks:
+
+=over 4
+
+=item B<Type> I<Type>
+
+Sets the type used to dispatch the values to the daemon. Detailed information
+about types and their configuration can be found in L<types.db(5)>. This
+option is mandatory.
+
+=item B<Instance> I<Instance>
+
+Type-instance to use. Defaults to the current map key or current string array element value.
+
+=back
+
 =head2 Plugin C<cpufreq>
 
 This plugin doesn't have any options. It reads
@@ -821,6 +921,10 @@ may not work on certain platforms, such as MacE<nbsp>OSE<nbsp>X.
 
 Ignore packets that originate from this address.
 
+=item B<SelectNumericQueryTypes> B<true>|B<false>
+
+Enabled by default, collects unknown (and thus presented as numeric only) query types.
+
 =back
 
 =head2 Plugin C<email>
@@ -1902,6 +2006,55 @@ L<upsc(8)>.
 
 =back
 
+=head2 Plugin C<olsrd>
+
+The I<olsrd> plugin connects to the TCP port opened by the I<txtinfo> plugin of
+the Optimized Link State Routing daemon and reads information about the current
+state of the meshed network.
+
+The following configuration options are understood:
+
+=over 4
+
+=item B<Host> I<Host>
+
+Connect to I<Host>. Defaults to B<"localhost">.
+
+=item B<Port> I<Port>
+
+Specifies the port to connect to. This must be a string, even if you give the
+port as a number rather than a service name. Defaults to B<"2006">.
+
+=item B<CollectLinks> B<No>|B<Summary>|B<Detail>
+
+Specifies what information to collect about links, i.E<nbsp>e. direct
+connections of the daemon queried. If set to B<No>, no information is
+collected. If set to B<Summary>, the number of links and the average of all
+I<link quality> (LQ) and I<neighbor link quality> (NLQ) values is calculated.
+If set to B<Detail> LQ and NLQ are collected per link.
+
+Defaults to B<Detail>.
+
+=item B<CollectRoutes> B<No>|B<Summary>|B<Detail>
+
+Specifies what information to collect about routes of the daemon queried. If
+set to B<No>, no information is collected. If set to B<Summary>, the number of
+routes and the average I<metric> and I<ETX> is calculated. If set to B<Detail>
+metric and ETX are collected per route.
+
+Defaults to B<Summary>.
+
+=item B<CollectTopology> B<No>|B<Summary>|B<Detail>
+
+Specifies what information to collect about the global topology. If set to
+B<No>, no information is collected. If set to B<Summary>, the number of links
+in the entire topology and the average I<link quality> (LQ) is calculated.
+If set to B<Detail> LQ and NLQ are collected for each link in the entire topology.
+
+Defaults to B<Summary>.
+
+=back
+
 =head2 Plugin C<onewire>
 
 B<EXPERIMENTAL!> See notes below.
diff --git a/src/couchdb.c b/src/couchdb.c
new file mode 100644 (file)
index 0000000..361e9e9
--- /dev/null
@@ -0,0 +1,808 @@
+/**
+ * collectd - src/couchdb.c
+ * Copyright (C) 2009       Doug MacEachern
+ * Copyright (C) 2006-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:
+ *   Doug MacEachern <dougm at hyperic.com>
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_avltree.h"
+
+#include <curl/curl.h>
+#include <yajl/yajl_parse.h>
+
+#define COUCHDB_DEFAULT_HOST "localhost"
+#define COUCHDB_KEY_MAGIC 0x43484b59UL /* CHKY */
+#define COUCHDB_IS_KEY(key) (key)->magic == COUCHDB_KEY_MAGIC
+#define COUCHDB_ANY "*"
+#define COUCH_MIN(x,y) ((x) < (y) ? (x) : (y))
+
+struct couchdb_key_s;
+typedef struct couchdb_key_s couchdb_key_t;
+struct couchdb_key_s /* {{{ */
+{
+  char *path;
+  char *type;
+  char *instance;
+  unsigned long magic;
+};
+/* }}} */
+
+struct couchdb_s /* {{{ */
+{
+  char *instance;
+  char *host;
+
+  char *url;
+  char *user;
+  char *pass;
+  char *credentials;
+  int   verify_peer;
+  int   verify_host;
+  char *cacert;
+
+  CURL *curl;
+  char curl_errbuf[CURL_ERROR_SIZE];
+
+  yajl_handle yajl;
+  c_avl_tree_t *tree;
+  couchdb_key_t *key;
+  int depth;
+  struct {
+    union {
+      c_avl_tree_t *tree;
+      couchdb_key_t *key;
+    };
+    char name[DATA_MAX_NAME_LEN];
+  } state[YAJL_MAX_DEPTH];
+};
+typedef struct couchdb_s couchdb_t; /* }}} */
+
+static int couchdb_read (user_data_t *ud);
+static int couchdb_curl_perform (couchdb_t *db, CURL *curl);
+static void couchdb_submit (couchdb_t *db, couchdb_key_t *key, value_t *value);
+
+static size_t couchdb_curl_callback (void *buf, /* {{{ */
+    size_t size, size_t nmemb, void *user_data)
+{
+  couchdb_t *db;
+  size_t len;
+  yajl_status status;
+
+  len = size * nmemb;
+
+  if (len <= 0)
+    return (len);
+
+  db = user_data;
+  if (db == NULL)
+    return (0);
+
+  status = yajl_parse(db->yajl, (unsigned char *)buf, len);
+  if (status == yajl_status_ok)
+  {
+    status = yajl_parse_complete(db->yajl);
+    return (len);
+  }
+  else if (status == yajl_status_insufficient_data)
+    return (len);
+
+  if (status != yajl_status_ok)
+  {
+    unsigned char *msg =
+      yajl_get_error(db->yajl, 1, (unsigned char *)buf, len);
+    ERROR ("couchdb plugin: yajl_parse failed: %s", msg);
+    yajl_free_error(db->yajl, msg);
+    return (0); /* abort write callback */
+  }
+
+  return (len);
+} /* }}} size_t couchdb_curl_callback */
+
+static int couchdb_get_type (couchdb_key_t *key)
+{
+  const data_set_t *ds;
+
+  ds = plugin_get_ds (key->type);
+  if (ds == NULL)
+    return -1; /* let plugin_write do the complaining */
+  else
+    return ds->ds[0].type; /* XXX support ds->ds_len > 1 */
+}
+
+/* yajl callbacks */
+static int couchcb_integer (void *ctx, long val)
+{
+  couchdb_t *db = (couchdb_t *)ctx;
+  couchdb_key_t *key = db->state[db->depth].key;
+
+  if (key != NULL)
+  {
+    value_t vt;
+    int type;
+
+    type = couchdb_get_type (key);
+    if (type == DS_TYPE_COUNTER)
+      vt.counter = val;
+    else
+      vt.gauge = (double)val;
+
+    couchdb_submit (db, key, &vt);
+  }
+  return 1;
+}
+
+static int couchcb_double (void *ctx, double val)
+{
+  couchdb_t *db = (couchdb_t *)ctx;
+  couchdb_key_t *key = db->state[db->depth].key;
+
+  if (key != NULL)
+  {
+    value_t vt;
+    int type;
+
+    type = couchdb_get_type (key);
+    if (type == DS_TYPE_GAUGE)
+      vt.gauge = val;
+    else
+      vt.counter = val;
+
+    couchdb_submit (db, key, &vt);
+  }
+  return 1;
+}
+
+static int couchcb_map_key (void *ctx, const unsigned char *val,
+                            unsigned int len)
+{
+  couchdb_t *db = (couchdb_t *)ctx;
+  c_avl_tree_t *tree;
+
+  tree = db->state[db->depth-1].tree;
+
+  if (tree != NULL)
+  {
+    couchdb_key_t *value;
+    char *name;
+
+    name = db->state[db->depth].name;
+    len = COUCH_MIN(len, sizeof (db->state[db->depth].name)-1);
+    sstrncpy (name, (char *)val, len+1);
+
+    if (c_avl_get (tree, name, (void *) &value) == 0)
+      db->state[db->depth].key = value;
+    else if (c_avl_get (tree, COUCHDB_ANY, (void *) &value) == 0)
+      db->state[db->depth].key = value;
+    else
+      db->state[db->depth].key = NULL;
+  }
+
+  return 1;
+}
+
+static int couchcb_string (void *ctx, const unsigned char *val,
+                           unsigned int len)
+{
+  couchdb_t *db = (couchdb_t *)ctx;
+  c_avl_tree_t *tree;
+  char *ptr;
+
+  if (db->depth != 1) /* e.g. _all_dbs */
+    return 1;
+
+  couchcb_map_key (ctx, val, len); /* same logic */
+
+  tree = db->state[db->depth].tree;
+
+  if ((tree != NULL) && (ptr = rindex (db->url, '/')))
+  {
+    char url[PATH_MAX];
+    CURL *curl;
+
+    /* url =~ s,[^/]+$,$name, */
+    len = (ptr - db->url) + 1;
+    ptr = url;
+    sstrncpy (ptr, db->url, sizeof (url));
+    sstrncpy (ptr + len, db->state[db->depth].name, sizeof (url) - len);
+
+    curl = curl_easy_duphandle (db->curl);
+    curl_easy_setopt (curl, CURLOPT_URL, url);
+    couchdb_curl_perform (db, curl);
+    curl_easy_cleanup (curl);
+  }
+  return 1;
+}
+
+static int couchcb_start (void *ctx)
+{
+  couchdb_t *db = (couchdb_t *)ctx;
+  if (++db->depth >= YAJL_MAX_DEPTH)
+  {
+    ERROR ("couchdb plugin: %s depth exceeds max, aborting.", db->url);
+    return 0;
+  }
+  return 1;
+}
+
+static int couchcb_end (void *ctx)
+{
+  couchdb_t *db = (couchdb_t *)ctx;
+  db->state[db->depth].tree = NULL;
+  --db->depth;
+  return 1;
+}
+
+static int couchcb_start_map (void *ctx)
+{
+  return couchcb_start (ctx);
+}
+
+static int couchcb_end_map (void *ctx)
+{
+  return couchcb_end (ctx);
+}
+
+static int couchcb_start_array (void * ctx)
+{
+  return couchcb_start (ctx);
+}
+
+static int couchcb_end_array (void * ctx)
+{
+  return couchcb_start (ctx);
+}
+
+static yajl_callbacks ycallbacks = {
+  NULL, /* null */
+  NULL, /* boolean */
+  couchcb_integer,
+  couchcb_double,
+  NULL, /* number */
+  couchcb_string,
+  couchcb_start_map,
+  couchcb_map_key,
+  couchcb_end_map,
+  couchcb_start_array,
+  couchcb_end_array
+};
+
+/* end yajl callbacks */
+
+static void couchdb_key_free (couchdb_key_t *key) /* {{{ */
+{
+  if (key == NULL)
+    return;
+
+  sfree (key->path);
+  sfree (key->type);
+  sfree (key->instance);
+
+  sfree (key);
+} /* }}} void couchdb_key_free */
+
+static void couchdb_tree_free (c_avl_tree_t *tree) /* {{{ */
+{
+  char *name;
+  void *value;
+
+  while (c_avl_pick (tree, (void *) &name, (void *) &value) == 0)
+  {
+    couchdb_key_t *key = (couchdb_key_t *)value;
+
+    if (COUCHDB_IS_KEY(key))
+      couchdb_key_free (key);
+    else
+      couchdb_tree_free ((c_avl_tree_t *)value);
+
+    sfree (name);
+  }
+
+  c_avl_destroy (tree);
+} /* }}} void couchdb_tree_free */
+
+static void couchdb_free (void *arg) /* {{{ */
+{
+  couchdb_t *db;
+
+  DEBUG ("couchdb plugin: couchdb_free (arg = %p);", arg);
+
+  db = (couchdb_t *) arg;
+
+  if (db == NULL)
+    return;
+
+  if (db->curl != NULL)
+    curl_easy_cleanup (db->curl);
+  db->curl = NULL;
+
+  if (db->tree != NULL)
+    couchdb_tree_free (db->tree);
+  db->tree = NULL;
+
+  sfree (db->instance);
+  sfree (db->host);
+
+  sfree (db->url);
+  sfree (db->user);
+  sfree (db->pass);
+  sfree (db->credentials);
+  sfree (db->cacert);
+
+  sfree (db);
+} /* }}} void couchdb_free */
+
+/* Configuration handling functions {{{ */
+
+static int couchdb_config_add_string (const char *name, char **dest, /* {{{ */
+                                      oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("couchdb plugin: `%s' needs exactly one string argument.", name);
+    return (-1);
+  }
+
+  sfree (*dest);
+  *dest = strdup (ci->values[0].value.string);
+  if (*dest == NULL)
+    return (-1);
+
+  return (0);
+} /* }}} int couchdb_config_add_string */
+
+static int couchdb_config_set_boolean (const char *name, int *dest, /* {{{ */
+                                       oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+  {
+    WARNING ("couchdb plugin: `%s' needs exactly one boolean argument.", name);
+    return (-1);
+  }
+
+  *dest = ci->values[0].value.boolean ? 1 : 0;
+
+  return (0);
+} /* }}} int couchdb_config_set_boolean */
+
+static c_avl_tree_t *couchdb_avl_create(void)
+{
+  return c_avl_create ((int (*) (const void *, const void *)) strcmp);
+}
+
+static int couchdb_config_add_key (couchdb_t *db, /* {{{ */
+                                   oconfig_item_t *ci)
+{
+  couchdb_key_t *key;
+  int status;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("couchdb plugin: The `Key' block "
+             "needs exactly one string argument.");
+    return (-1);
+  }
+
+  key = (couchdb_key_t *) malloc (sizeof (*key));
+  if (key == NULL)
+  {
+    ERROR ("couchdb plugin: malloc failed.");
+    return (-1);
+  }
+  memset (key, 0, sizeof (*key));
+  key->magic = COUCHDB_KEY_MAGIC;
+
+  if (strcasecmp ("Key", ci->key) == 0)
+  {
+    status = couchdb_config_add_string ("Key", &key->path, ci);
+    if (status != 0)
+    {
+      sfree (key);
+      return (status);
+    }
+  }
+  else
+  {
+    ERROR ("couchdb plugin: couchdb_config: "
+           "Invalid key: %s", ci->key);
+    return (-1);
+  }
+
+  status = 0;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Type", child->key) == 0)
+      status = couchdb_config_add_string ("Type", &key->type, child);
+    else if (strcasecmp ("Instance", child->key) == 0)
+      status = couchdb_config_add_string ("Instance", &key->instance, child);
+    else
+    {
+      WARNING ("couchdb plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  while (status == 0)
+  {
+    if (key->type == NULL)
+    {
+      WARNING ("couchdb plugin: `Type' missing in `Key' block.");
+      status = -1;
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  /* store path in a tree that will match the json map structure, example:
+   * "httpd/requests/count",
+   * "httpd/requests/current" ->
+   * { "httpd": { "requests": { "count": $key, "current": $key } } }
+   */
+  if (status == 0)
+  {
+    char *ptr;
+    char *name;
+    char ent[PATH_MAX];
+    c_avl_tree_t *tree;
+
+    if (db->tree == NULL)
+      db->tree = couchdb_avl_create();
+
+    tree = db->tree;
+    name = key->path;
+    ptr = key->path;
+    if (*ptr == '/')
+      ++ptr;
+
+    name = ptr;
+    while (*ptr)
+    {
+      if (*ptr == '/')
+      {
+        c_avl_tree_t *value;
+        int len;
+
+        len = ptr-name;
+        if (len == 0)
+          break;
+        sstrncpy (ent, name, len+1);
+
+        if (c_avl_get (tree, ent, (void *) &value) != 0)
+        {
+          value = couchdb_avl_create ();
+          c_avl_insert (tree, strdup (ent), value);
+        }
+
+        tree = value;
+        name = ptr+1;
+      }
+      ++ptr;
+    }
+    if (*name)
+      c_avl_insert (tree, strdup(name), key);
+    else
+    {
+      ERROR ("couchdb plugin: invalid key: %s", key->path);
+      status = -1;
+    }
+  }
+
+  return (status);
+} /* }}} int couchdb_config_add_key */
+
+static int couchdb_init_curl (couchdb_t *db) /* {{{ */
+{
+  db->curl = curl_easy_init ();
+  if (db->curl == NULL)
+  {
+    ERROR ("couchdb plugin: curl_easy_init failed.");
+    return (-1);
+  }
+
+  curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, couchdb_curl_callback);
+  curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
+  curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
+                    PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
+  curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
+
+  if (db->user != NULL)
+  {
+    size_t credentials_size;
+
+    credentials_size = strlen (db->user) + 2;
+    if (db->pass != NULL)
+      credentials_size += strlen (db->pass);
+
+    db->credentials = (char *) malloc (credentials_size);
+    if (db->credentials == NULL)
+    {
+      ERROR ("couchdb plugin: malloc failed.");
+      return (-1);
+    }
+
+    ssnprintf (db->credentials, credentials_size, "%s:%s",
+               db->user, (db->pass == NULL) ? "" : db->pass);
+    curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
+  }
+
+  curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer);
+  curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
+                    db->verify_host ? 2 : 0);
+  if (db->cacert != NULL)
+    curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+
+  return (0);
+} /* }}} int couchdb_init_curl */
+
+static int couchdb_config_add_url (oconfig_item_t *ci) /* {{{ */
+{
+  couchdb_t *db;
+  int status = 0;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("couchdb plugin: The `URL' block "
+             "needs exactly one string argument.");
+    return (-1);
+  }
+
+  db = (couchdb_t *) malloc (sizeof (*db));
+  if (db == NULL)
+  {
+    ERROR ("couchdb plugin: malloc failed.");
+    return (-1);
+  }
+  memset (db, 0, sizeof (*db));
+
+  if (strcasecmp ("URL", ci->key) == 0)
+  {
+    status = couchdb_config_add_string ("URL", &db->url, ci);
+    if (status != 0)
+    {
+      sfree (db);
+      return (status);
+    }
+  }
+  else
+  {
+    ERROR ("couchdb plugin: couchdb_config: "
+           "Invalid key: %s", ci->key);
+    return (-1);
+  }
+
+  /* Fill the `couchdb_t' structure.. */
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Instance", child->key) == 0)
+      status = couchdb_config_add_string ("Instance", &db->instance, child);
+    else if (strcasecmp ("Host", child->key) == 0)
+      status = couchdb_config_add_string ("Host", &db->host, child);
+    else if (strcasecmp ("User", child->key) == 0)
+      status = couchdb_config_add_string ("User", &db->user, child);
+    else if (strcasecmp ("Password", child->key) == 0)
+      status = couchdb_config_add_string ("Password", &db->pass, child);
+    else if (strcasecmp ("VerifyPeer", child->key) == 0)
+      status = couchdb_config_set_boolean ("VerifyPeer", &db->verify_peer, child);
+    else if (strcasecmp ("VerifyHost", child->key) == 0)
+      status = couchdb_config_set_boolean ("VerifyHost", &db->verify_host, child);
+    else if (strcasecmp ("CACert", child->key) == 0)
+      status = couchdb_config_add_string ("CACert", &db->cacert, child);
+    else if (strcasecmp ("Key", child->key) == 0)
+      status = couchdb_config_add_key (db, child);
+    else
+    {
+      WARNING ("couchdb plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status == 0)
+  {
+    if (db->tree == NULL)
+    {
+      WARNING ("couchdb plugin: No (valid) `Key' block "
+               "within `URL' block `%s'.", db->url);
+      status = -1;
+    }
+    if (status == 0)
+      status = couchdb_init_curl (db);
+  }
+
+  /* If all went well, register this database for reading */
+  if (status == 0)
+  {
+    user_data_t ud;
+    char cb_name[DATA_MAX_NAME_LEN];
+
+    if (db->instance == NULL)
+      db->instance = strdup("default");
+
+    DEBUG ("couchdb plugin: Registering new read callback: %s",
+           db->instance);
+
+    memset (&ud, 0, sizeof (ud));
+    ud.data = (void *) db;
+    ud.free_func = couchdb_free;
+
+    ssnprintf (cb_name, sizeof (cb_name), "couchdb-%s-%s",
+               db->instance, db->url);
+
+    plugin_register_complex_read (cb_name, couchdb_read,
+                                  /* interval = */ NULL, &ud);
+  }
+  else
+  {
+    couchdb_free (db);
+    return (-1);
+  }
+
+  return (0);
+}
+ /* }}} int couchdb_config_add_database */
+
+static int couchdb_config (oconfig_item_t *ci) /* {{{ */
+{
+  int success;
+  int errors;
+  int status;
+  int i;
+
+  success = 0;
+  errors = 0;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("URL", child->key) == 0)
+    {
+      status = couchdb_config_add_url (child);
+      if (status == 0)
+        success++;
+      else
+        errors++;
+    }
+    else
+    {
+      WARNING ("couchdb plugin: Option `%s' not allowed here.", child->key);
+      errors++;
+    }
+  }
+
+  if ((success == 0) && (errors > 0))
+  {
+    ERROR ("couchdb plugin: All statements failed.");
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int couchdb_config */
+
+/* }}} End of configuration handling functions */
+
+static void couchdb_submit (couchdb_t *db, couchdb_key_t *key, value_t *value) /* {{{ */
+{
+  value_list_t vl = VALUE_LIST_INIT;
+  char *host;
+
+  vl.values     = value;
+  vl.values_len = 1;
+
+  if ((db->host == NULL)
+      || (strcmp ("", db->host) == 0)
+      || (strcmp (COUCHDB_DEFAULT_HOST, db->host) == 0))
+    host = hostname_g;
+  else
+    host = db->host;
+
+  if (key->instance == NULL)
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
+               db->state[db->depth-1].name, db->state[db->depth].name);
+  else
+    sstrncpy (vl.type_instance, key->instance, sizeof (vl.type_instance));
+
+  sstrncpy (vl.host, host, sizeof (vl.host));
+  sstrncpy (vl.plugin, "couchdb", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, key->type, sizeof (vl.type));
+
+  plugin_dispatch_values (&vl);
+} /* }}} int couchdb_submit */
+
+static int couchdb_curl_perform (couchdb_t *db, CURL *curl) /* {{{ */
+{
+  int status;
+  long rc;
+  char *url;
+  yajl_handle yprev = db->yajl;
+
+  db->yajl = yajl_alloc (&ycallbacks, NULL, NULL, (void *)db);
+  if (db->yajl == NULL)
+  {
+    ERROR ("couchdb plugin: yajl_alloc failed.");
+    return (-1);
+  }
+
+  status = curl_easy_perform (curl);
+
+  yajl_free (db->yajl);
+  db->yajl = yprev;
+
+  curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+
+  if (rc != 200)
+  {
+    ERROR ("couchdb plugin: curl_easy_perform failed with response code %ld (%s)",
+           rc, url);
+    return (-1);
+  }
+
+  if (status != 0)
+  {
+    ERROR ("couchdb plugin: curl_easy_perform failed with status %i: %s (%s)",
+           status, db->curl_errbuf, url);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int couchdb_curl_perform */
+
+static int couchdb_read (user_data_t *ud) /* {{{ */
+{
+  couchdb_t *db;
+
+  if ((ud == NULL) || (ud->data == NULL))
+  {
+    ERROR ("couchdb plugin: couchdb_read: Invalid user data.");
+    return (-1);
+  }
+
+  db = (couchdb_t *) ud->data;
+
+  db->depth = 0;
+  memset (&db->state, 0, sizeof(db->state));
+  db->state[db->depth].tree = db->tree;
+  db->key = NULL;
+
+  return couchdb_curl_perform (db, db->curl);
+} /* }}} int couchdb_read */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("couchdb", couchdb_config);
+} /* void module_register */
index 8339377..74fab98 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -49,9 +49,10 @@ static const char *config_keys[] =
 {
        "Interface",
        "IgnoreSource",
-       NULL
+       "SelectNumericQueryTypes"
 };
-static int config_keys_num = 2;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+static int select_numeric_qtype = 1;
 
 #define PCAP_SNAPLEN 1460
 static char   *pcap_device = NULL;
@@ -158,6 +159,13 @@ static int dns_config (const char *key, const char *value)
                if (value != NULL)
                        ignore_list_add_name (value);
        }
+       else if (strcasecmp (key, "SelectNumericQueryTypes") == 0)
+       {
+               if ((value != NULL) && IS_FALSE (value))
+                       select_numeric_qtype = 0;
+               else
+                       select_numeric_qtype = 1;
+       }
        else
        {
                return (-1);
@@ -171,13 +179,24 @@ static void dns_child_callback (const rfc1035_header_t *dns)
        if (dns->qr == 0)
        {
                /* This is a query */
+               int skip = 0;
+               if (!select_numeric_qtype)
+               {
+                       const char *str = qtype_str(dns->qtype);
+                       if ((str == NULL) || (str[0] == '#'))
+                               skip = 1;
+               }
+
                pthread_mutex_lock (&traffic_mutex);
                tr_queries += dns->length;
                pthread_mutex_unlock (&traffic_mutex);
 
-               pthread_mutex_lock (&qtype_mutex);
-               counter_list_add (&qtype_list,  dns->qtype,  1);
-               pthread_mutex_unlock (&qtype_mutex);
+               if (skip == 0)
+               {
+                       pthread_mutex_lock (&qtype_mutex);
+                       counter_list_add (&qtype_list, dns->qtype,  1);
+                       pthread_mutex_unlock (&qtype_mutex);
+               }
        }
        else
        {
diff --git a/src/olsrd.c b/src/olsrd.c
new file mode 100644 (file)
index 0000000..be422ab
--- /dev/null
@@ -0,0 +1,709 @@
+/**
+ * collectd - src/olsrd.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 <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#define OLSRD_DEFAULT_NODE "localhost"
+#define OLSRD_DEFAULT_SERVICE "2006"
+
+static const char *config_keys[] =
+{
+  "Host",
+  "Port",
+  "CollectLinks",
+  "CollectRoutes",
+  "CollectTopology"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char *config_node = NULL;
+static char *config_service = NULL;
+
+#define OLSRD_WANT_NOT     0
+#define OLSRD_WANT_SUMMARY 1
+#define OLSRD_WANT_DETAIL  2
+static int config_want_links    = OLSRD_WANT_DETAIL;
+static int config_want_routes   = OLSRD_WANT_SUMMARY;
+static int config_want_topology = OLSRD_WANT_SUMMARY;
+
+static const char *olsrd_get_node (void) /* {{{ */
+{
+  if (config_node != NULL)
+    return (config_node);
+  return (OLSRD_DEFAULT_NODE);
+} /* }}} const char *olsrd_get_node */
+
+static const char *olsrd_get_service (void) /* {{{ */
+{
+  if (config_service != NULL)
+    return (config_service);
+  return (OLSRD_DEFAULT_SERVICE);
+} /* }}} const char *olsrd_get_service */
+
+static void olsrd_set_node (const char *node) /* {{{ */
+{
+  char *tmp;
+  if (node == NULL)
+    return;
+  tmp = strdup (node);
+  if (tmp == NULL)
+    return;
+  config_node = tmp;
+} /* }}} void olsrd_set_node */
+
+static void olsrd_set_service (const char *service) /* {{{ */
+{
+  char *tmp;
+  if (service == NULL)
+    return;
+  tmp = strdup (service);
+  if (tmp == NULL)
+    return;
+  config_service = tmp;
+} /* }}} void olsrd_set_service */
+
+static void olsrd_set_detail (int *varptr, const char *detail, /* {{{ */
+    const char *key)
+{
+  if (strcasecmp ("No", detail) == 0)
+    *varptr = OLSRD_WANT_NOT;
+  else if (strcasecmp ("Summary", detail) == 0)
+    *varptr = OLSRD_WANT_SUMMARY;
+  else if (strcasecmp ("Detail", detail) == 0)
+    *varptr = OLSRD_WANT_DETAIL;
+  else
+  {
+    ERROR ("olsrd plugin: Invalid argument given to the `%s' configuration "
+        "option: `%s'. Expected: `No', `Summary', or `Detail'.",
+        key, detail);
+  }
+} /* }}} void olsrd_set_detail */
+
+/* Strip trailing newline characters. Returns length of string. */
+static size_t strchomp (char *buffer) /* {{{ */
+{
+  size_t buffer_len;
+
+  buffer_len = strlen (buffer);
+  while ((buffer_len > 0)
+      && ((buffer[buffer_len - 1] == '\r')
+        || (buffer[buffer_len - 1] == '\n')))
+  {
+    buffer_len--;
+    buffer[buffer_len] = 0;
+  }
+
+  return (buffer_len);
+} /* }}} size_t strchomp */
+
+static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */
+{
+  size_t i;
+  char *ptr;
+  char *saveptr;
+
+  i = 0;
+  ptr = string;
+  saveptr = NULL;
+  while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
+  {
+    ptr = NULL;
+    i++;
+
+    if (i >= size)
+      break;
+  }
+
+  return (i);
+} /* }}} size_t strtabsplit */
+
+static FILE *olsrd_connect (void) /* {{{ */
+{
+  struct addrinfo  ai_hints;
+  struct addrinfo *ai_list, *ai_ptr;
+  int              ai_return;
+
+  FILE *fh;
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags    = 0;
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags   |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family   = PF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_STREAM;
+  ai_hints.ai_protocol = IPPROTO_TCP;
+
+  ai_list = NULL;
+  ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
+      &ai_hints, &ai_list);
+  if (ai_return != 0)
+  {
+    ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
+        olsrd_get_node (), olsrd_get_service (),
+        gai_strerror (ai_return));
+    return (NULL);
+  }
+
+  fh = NULL;
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    int fd;
+    int status;
+    char errbuf[1024];
+
+    fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (fd < 0)
+    {
+      ERROR ("olsrd plugin: socket failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      continue;
+    }
+
+    status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      ERROR ("olsrd plugin: connect failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      close (fd);
+      continue;
+    }
+
+    fh = fdopen (fd, "r+");
+    if (fh == NULL)
+    {
+      ERROR ("olsrd plugin: fdopen failed.");
+      close (fd);
+      continue;
+    }
+
+    break;
+  } /* for (ai_ptr) */
+
+  freeaddrinfo (ai_list);
+
+  return (fh);
+} /* }}} FILE *olsrd_connect */
+
+__attribute__ ((nonnull(2)))
+static void olsrd_submit (const char *plugin_instance, /* {{{ */
+    const char *type, const char *type_instance, gauge_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "olsrd", sizeof (vl.plugin));
+  if (plugin_instance != NULL)
+    sstrncpy (vl.plugin_instance, plugin_instance,
+        sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_instance != NULL)
+    sstrncpy (vl.type_instance, type_instance,
+        sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+} /* }}} void olsrd_submit */
+
+static int olsrd_cb_ignore (int lineno, /* {{{ */
+    size_t fields_num, char **fields)
+{
+  return (0);
+} /* }}} int olsrd_cb_ignore */
+
+static int olsrd_cb_links (int lineno, /* {{{ */
+    size_t fields_num, char **fields)
+{
+  /* Fields:
+   *  0 = Local IP
+   *  1 = Remote IP
+   *  2 = Hyst.
+   *  3 = LQ
+   *  4 = NLQ
+   *  5 = Cost */
+
+  static uint32_t links_num;
+  static double    lq_sum;
+  static uint32_t  lq_num;
+  static double   nlq_sum;
+  static uint32_t nlq_num;
+
+  double lq;
+  double nlq;
+
+  char *endptr;
+
+  if (config_want_links == OLSRD_WANT_NOT)
+    return (0);
+
+  /* Special handling of the first line. */
+  if (lineno <= 0)
+  {
+    links_num = 0;
+    lq_sum = 0.0;
+    lq_num = 0;
+    nlq_sum = 0.0;
+    nlq_num = 0;
+
+    return (0);
+  }
+
+  /* Special handling of the last line. */
+  if (fields_num == 0)
+  {
+    DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
+    olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
+        /* t.-inst = */ NULL, (gauge_t) links_num);
+
+    lq = NAN;
+    if (lq_num > 0)
+      lq = lq_sum / ((double) lq_num);
+    DEBUG ("olsrd plugin: Average  LQ: %g", lq);
+    olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+        "average-lq", lq);
+
+    nlq = NAN;
+    if (nlq_num > 0)
+      nlq = nlq_sum / ((double) nlq_num);
+    DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
+    olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+        "average-nlq", nlq);
+
+    return (0);
+  }
+
+  if (fields_num != 6)
+    return (-1);
+
+  links_num++;
+
+  errno = 0;
+  endptr = NULL;
+  lq = strtod (fields[3], &endptr);
+  if ((errno != 0) || (endptr == fields[3]))
+  {
+    ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
+  }
+  else
+  {
+    if (!isnan (lq))
+    {
+      lq_sum += lq;
+      lq_num++;
+    }
+
+    if (config_want_links == OLSRD_WANT_DETAIL)
+    {
+      char type_instance[DATA_MAX_NAME_LEN];
+
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
+          fields[0], fields[1]);
+
+      DEBUG ("olsrd plugin: links: type_instance = %s;  lq = %g;",
+          type_instance, lq);
+      olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+          type_instance, lq);
+    }
+  }
+
+  errno = 0;
+  endptr = NULL;
+  nlq = strtod (fields[4], &endptr);
+  if ((errno != 0) || (endptr == fields[4]))
+  {
+    ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
+  }
+  else
+  {
+    if (!isnan (nlq))
+    {
+      nlq_sum += nlq;
+      nlq_num++;
+    }
+
+    if (config_want_links == OLSRD_WANT_DETAIL)
+    {
+      char type_instance[DATA_MAX_NAME_LEN];
+
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
+          fields[0], fields[1]);
+
+      DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
+          type_instance, lq);
+      olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+          type_instance, nlq);
+    }
+  }
+
+  return (0);
+} /* }}} int olsrd_cb_links */
+
+static int olsrd_cb_routes (int lineno, /* {{{ */
+    size_t fields_num, char **fields)
+{
+  /* Fields:
+   *  0 = Destination
+   *  1 = Gateway IP
+   *  2 = Metric
+   *  3 = ETX
+   *  4 = Interface */
+
+  static uint32_t routes_num;
+  static uint32_t metric_sum;
+  static uint32_t metric_num;
+  static double   etx_sum;
+  static uint32_t etx_num;
+
+  uint32_t metric;
+  double etx;
+  char *endptr;
+
+  if (config_want_routes == OLSRD_WANT_NOT)
+    return (0);
+
+  /* Special handling of the first line */
+  if (lineno <= 0)
+  {
+    routes_num = 0;
+    metric_num = 0;
+    metric_sum = 0;
+    etx_sum = 0.0;
+    etx_num = 0;
+
+    return (0);
+  }
+
+  /* Special handling after the last line */
+  if (fields_num == 0)
+  {
+    double metric_avg;
+
+    DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
+    olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
+        /* t.-inst = */ NULL, (gauge_t) routes_num);
+
+    metric_avg = NAN;
+    if (metric_num > 0)
+      metric_avg = ((double) metric_sum) / ((double) metric_num);
+    DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
+    olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
+        "average", metric_avg);
+
+    etx = NAN;
+    if (etx_num > 0)
+      etx = etx_sum / ((double) etx_sum);
+    DEBUG ("olsrd plugin: Average ETX: %g", etx);
+    olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
+        "average", etx);
+
+    return (0);
+  }
+
+  if (fields_num != 5)
+    return (-1);
+
+  routes_num++;
+
+  errno = 0;
+  endptr = NULL;
+  metric = (uint32_t) strtoul (fields[2], &endptr, 0);
+  if ((errno != 0) || (endptr == fields[2]))
+  {
+    ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
+  }
+  else
+  {
+    metric_num++;
+    metric_sum += metric;
+
+    if (config_want_routes == OLSRD_WANT_DETAIL)
+    {
+      DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
+          fields[0], metric);
+      olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
+          /* t.-inst = */ fields[0], (gauge_t) metric);
+    }
+  }
+
+  errno = 0;
+  endptr = NULL;
+  etx = strtod (fields[3], &endptr);
+  if ((errno != 0) || (endptr == fields[3]))
+  {
+    ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
+  }
+  else
+  {
+    if (!isnan (etx))
+    {
+      etx_sum += etx;
+      etx_num++;
+    }
+
+    if (config_want_routes == OLSRD_WANT_DETAIL)
+    {
+      DEBUG ("olsrd plugin: destination = %s; etx = %g;",
+          fields[0], etx);
+      olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
+          /* t.-inst = */ fields[0], etx);
+    }
+  }
+
+  return (0);
+} /* }}} int olsrd_cb_routes */
+
+static int olsrd_cb_topology (int lineno, /* {{{ */
+    size_t fields_num, char **fields)
+{
+  /* Fields:
+   *  0 = Dest. IP
+   *  1 = Last hop IP
+   *  2 = LQ
+   *  3 = NLQ
+   *  4 = Cost */
+
+  static double   lq_sum;
+  static uint32_t lq_num;
+
+  static uint32_t links_num;
+
+  double lq;
+  char *endptr;
+
+  if (config_want_topology == OLSRD_WANT_NOT)
+    return (0);
+
+  /* Special handling of the first line */
+  if (lineno <= 0)
+  {
+    lq_sum = 0.0;
+    lq_num = 0;
+    links_num = 0;
+
+    return (0);
+  }
+
+  /* Special handling after the last line */
+  if (fields_num == 0)
+  {
+    DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
+    olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
+        /* t.-inst = */ NULL, (gauge_t) links_num);
+
+    lq = NAN;
+    if (lq_num > 0)
+      lq = lq_sum / ((double) lq_sum);
+    DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
+    olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
+        /* t.-inst = */ "average", lq);
+
+    return (0);
+  }
+
+  if (fields_num != 5)
+    return (-1);
+
+  links_num++;
+
+  errno = 0;
+  endptr = NULL;
+  lq = strtod (fields[2], &endptr);
+  if ((errno != 0) || (endptr == fields[2]))
+  {
+    ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
+  }
+  else
+  {
+    if (!isnan (lq))
+    {
+      lq_sum += lq;
+      lq_num++;
+    }
+
+    if (config_want_topology == OLSRD_WANT_DETAIL)
+    {
+      char type_instance[DATA_MAX_NAME_LEN];
+
+      memset (type_instance, 0, sizeof (type_instance));
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
+          fields[0], fields[1]);
+      DEBUG ("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
+      olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
+          type_instance, lq);
+    }
+  }
+
+  if (config_want_topology == OLSRD_WANT_DETAIL)
+  {
+    double nlq;
+
+    errno = 0;
+    endptr = NULL;
+    nlq = strtod (fields[3], &endptr);
+    if ((errno != 0) || (endptr == fields[3]))
+    {
+      ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
+    }
+    else
+    {
+      char type_instance[DATA_MAX_NAME_LEN];
+
+      memset (type_instance, 0, sizeof (type_instance));
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s-nlq",
+          fields[0], fields[1]);
+      DEBUG ("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
+      olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
+          type_instance, nlq);
+    }
+  }
+
+  return (0);
+} /* }}} int olsrd_cb_topology */
+
+static int olsrd_read_table (FILE *fh, /* {{{ */
+    int (*callback) (int lineno, size_t fields_num, char **fields))
+{
+  char buffer[1024];
+  size_t buffer_len;
+
+  char *fields[32];
+  size_t fields_num;
+
+  int lineno;
+
+  lineno = 0;
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    /* An empty line ends the table. */
+    buffer_len = strchomp (buffer);
+    if (buffer_len <= 0)
+    {
+      (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL);
+      break;
+    }
+
+    fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+
+    (*callback) (lineno, fields_num, fields);
+    lineno++;
+  } /* while (fgets) */
+  
+  return (0);
+} /* }}} int olsrd_read_table */
+
+static int olsrd_config (const char *key, const char *value) /* {{{ */
+{
+  if (strcasecmp ("Host", key) == 0)
+    olsrd_set_node (value);
+  else if (strcasecmp ("Port", key) == 0)
+    olsrd_set_service (value);
+  else if (strcasecmp ("CollectLinks", key) == 0)
+    olsrd_set_detail (&config_want_links, value, key);
+  else if (strcasecmp ("CollectRoutes", key) == 0)
+    olsrd_set_detail (&config_want_routes, value, key);
+  else if (strcasecmp ("CollectTopology", key) == 0)
+    olsrd_set_detail (&config_want_topology, value, key);
+  else
+  {
+    ERROR ("olsrd plugin: Unknown configuration option given: %s", key);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int olsrd_config */
+
+static int olsrd_read (void) /* {{{ */
+{
+  FILE *fh;
+  char buffer[1024];
+  size_t buffer_len;
+
+  fh = olsrd_connect ();
+  if (fh == NULL)
+    return (-1);
+
+  fputs ("\r\n", fh);
+  fflush (fh);
+
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    buffer_len = strchomp (buffer);
+    if (buffer_len <= 0)
+      continue;
+    
+    if (strcmp ("Table: Links", buffer) == 0)
+      olsrd_read_table (fh, olsrd_cb_links);
+    else if (strcmp ("Table: Neighbors", buffer) == 0)
+      olsrd_read_table (fh, olsrd_cb_ignore);
+    else if (strcmp ("Table: Topology", buffer) == 0)
+      olsrd_read_table (fh, olsrd_cb_topology);
+    else if (strcmp ("Table: HNA", buffer) == 0)
+      olsrd_read_table (fh, olsrd_cb_ignore);
+    else if (strcmp ("Table: MID", buffer) == 0)
+      olsrd_read_table (fh, olsrd_cb_ignore);
+    else if (strcmp ("Table: Routes", buffer) == 0)
+      olsrd_read_table (fh, olsrd_cb_routes);
+    else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
+        || (strcmp ("Content-type: text/plain", buffer) == 0))
+    {
+      /* ignore */
+    }
+    else
+    {
+      DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
+    }
+  } /* while (fgets) */
+
+  fclose (fh);
+  
+  return (0);
+} /* }}} int olsrd_read */
+
+static int olsrd_shutdown (void) /* {{{ */
+{
+  sfree (config_node);
+  sfree (config_service);
+
+  return (0);
+} /* }}} int olsrd_shutdown */
+
+void module_register (void)
+{
+  plugin_register_config ("olsrd", olsrd_config,
+      config_keys, config_keys_num);
+  plugin_register_read ("olsrd", olsrd_read);
+  plugin_register_shutdown ("olsrd", olsrd_shutdown);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index a92153c..7028fe7 100644 (file)
@@ -24,8 +24,8 @@ dns_answer            value:COUNTER:0:65535
 dns_notify             value:COUNTER:0:65535
 dns_octets             queries:COUNTER:0:125000000, responses:COUNTER:0:125000000
 dns_opcode             value:COUNTER:0:65535
-dns_qtype              value:COUNTER:0:65535
 dns_qtype_cached       value:GAUGE:0:4294967295
+dns_qtype              value:COUNTER:0:65535
 dns_query              value:COUNTER:0:65535
 dns_question           value:COUNTER:0:65535
 dns_rcode              value:COUNTER:0:65535
@@ -44,7 +44,11 @@ fanspeed             value:GAUGE:0:U
 files                  value:GAUGE:0:U
 frequency              frequency:GAUGE:0:U
 frequency_offset       ppm:GAUGE:-1000000:1000000
+fscache_stat           value:COUNTER:0:4294967295
 gauge                  value:GAUGE:U:U
+http_request_methods   count:COUNTER:0:134217728
+http_requests          count:COUNTER:0:134217728
+http_response_codes    count:COUNTER:0:134217728
 humidity               value:GAUGE:0:100
 if_collisions          value:COUNTER:0:4294967295
 if_dropped             rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
@@ -60,6 +64,7 @@ ipt_bytes             value:COUNTER:0:134217728
 ipt_packets            value:COUNTER:0:134217728
 irq                    value:COUNTER:U:65535
 latency                        value:GAUGE:0:65535
+links                  value:GAUGE:0:U
 load                   shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
 memcached_command      value:COUNTER:0:U
 memcached_connections  value:GAUGE:0:U
@@ -76,7 +81,6 @@ mysql_octets          rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 mysql_qcache           hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U
 mysql_threads          running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U
 nfs_procedure          value:COUNTER:0:4294967295
-fscache_stat           value:COUNTER:0:4294967295
 nginx_connections      value:GAUGE:0:U
 nginx_requests         value:COUNTER:0:134217728
 operations             value:COUNTER:0:4294967295
@@ -88,8 +92,8 @@ pg_n_tup_g            value:GAUGE:0:U
 pg_numbackends         value:GAUGE:0:U
 pg_scan                        value:COUNTER:0:U
 pg_xact                        value:COUNTER:0:U
-ping                   ping:GAUGE:0:65535
 ping_droprate          value:GAUGE:0:100
+ping                   ping:GAUGE:0:65535
 ping_stddev            value:GAUGE:0:65535
 players                        value:GAUGE:0:1000000
 power                  value:GAUGE:0:U
@@ -102,6 +106,9 @@ ps_stacksize                value:GAUGE:0:9223372036854775807
 ps_state               value:GAUGE:0:65535
 ps_vm                  value:GAUGE:0:9223372036854775807
 queue_length           value:GAUGE:0:U
+route_etx              value:GAUGE:0:U
+route_metric           value:GAUGE:0:U
+routes                 value:GAUGE:0:U
 serial_octets          rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 signal_noise           value:GAUGE:U:0
 signal_power           value:GAUGE:U:0