Merge branch 'collectd-4.7'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 19 Jul 2009 15:39:06 +0000 (17:39 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 19 Jul 2009 15:39:06 +0000 (17:39 +0200)
41 files changed:
.gitignore
AUTHORS
README
configure.in
src/Makefile.am
src/collectd-perl.pod
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/common.h
src/couchdb.c [new file with mode: 0644]
src/csv.c
src/curl.c
src/dns.c
src/gmond.c
src/java.c
src/libcollectdclient/client.c
src/libcollectdclient/client.h
src/meta_data.c
src/meta_data.h
src/network.c
src/olsrd.c [new file with mode: 0644]
src/perl.c
src/plugin.c
src/plugin.h
src/powerdns.c
src/rrdcached.c
src/rrdtool.c
src/snmp.c
src/table.c
src/tail.c
src/tokyotyrant.c [new file with mode: 0644]
src/types.db
src/types_list.c
src/utils_cache.c
src/utils_cmd_putval.c
src/utils_db_query.c
src/utils_match.c
src/utils_match.h
src/utils_rrdcreate.c
src/utils_threshold.c

index 1f491f3..46c8c04 100644 (file)
@@ -1,29 +1,19 @@
-# svn stuff:
-.svn
-contrib/.svn
-debian/.svn
-src/.svn
-src/libconfig/.svn
-src/liboping/.svn
-
 # build.sh stuff:
-INSTALL
 Makefile.in
-aclocal.m4
-autom4te.cache
-compile
-config.guess
-config.sub
-configure
-depcomp
-install-sh
-libltdl
-ltmain.sh
-missing
-src/Makefile.in
+/INSTALL
+/aclocal.m4
+/autom4te.cache
+/autom4te.cache
+/compile
+/config.guess
+/config.sub
+/configure
+/depcomp
+/install-sh
+/libltdl/
+/ltmain.sh
+/missing
 src/config.h.in
-src/libconfig/Makefile.in
-src/liboping/Makefile.in
 
 # configure stuff:
 Makefile
@@ -37,30 +27,20 @@ src/libcollectdclient/libcollectdclient.pc
 src/stamp-h1
 
 # make stuff:
-src/.libs
-src/*.la
-src/*.lo
-src/*.o
+*.la
+*.lo
+*.o
+.libs/
 src/collectd
 src/collectd-nagios
 src/collectdmon
-src/collectd*.1
-src/collectd*.5
-src/types.db.5
-src/config.h.in~
-src/libcollectdclient/.libs
-src/libcollectdclient/*.la
-src/libcollectdclient/*.lo
+src/*.1
+src/*.5
 src/libcollectdclient/lcc_features.h
-src/libiptc/.libs
-src/libiptc/*.la
-src/libiptc/*.lo
-src/liboconfig/.libs
-src/liboconfig/*.la
-src/liboconfig/*.lo
-src/liboping/.libs
-src/liboping/*.la
-src/liboping/*.lo
+
+# patch stuff
+*.rej
+*.orig
 
 # lex / yacc stuff:
 ylwrap
@@ -68,12 +48,16 @@ src/liboconfig/parser.c
 src/liboconfig/parser.h
 src/liboconfig/scanner.c
 
+# make dist stuff:
+/collectd-*.tar.gz
+/collectd-*.tar.bz2
+
 # perl stuff:
 bindings/.perl-directory-stamp
 bindings/perl/Collectd/pm_to_blib
 bindings/perl/blib/
 bindings/perl/pm_to_blib
 
-# make dist stuff:
-collectd-*.tar.gz
-collectd-*.tar.bz2
+# java stuff
+bindings/java/java-build-stamp
+bindings/java/org/collectd/api/*.class
diff --git a/AUTHORS b/AUTHORS
index 9f82ee1..eedf3f0 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -103,6 +103,9 @@ Oleg King <king2 at kaluga.ru>
    + the disk plugin, and
    + the users plugin.
 
+Paul Sadauskas <psadauskas at gmail.com>
+ - tokyotyrant plugin.
+
 Peter Holik <peter at holik.at>
  - cpufreq plugin.
  - multimeter plugin.
diff --git a/README b/README
index ee7163c..83eede2 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.
@@ -238,6 +241,9 @@ Features
     - thermal
       Linux ACPI thermal zone information.
 
+    - tokyotyrant
+      Record in and file size of a Tokyo Cabinet database file.
+
     - uptime
       System uptime statistics.
 
@@ -539,6 +545,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 3033d4e..4ef308e 100644 (file)
@@ -2763,6 +2763,79 @@ then
 fi
 # }}}
 
+# --with-libtokyotyrant {{{
+with_libtokyotyrant_cppflags=""
+with_libtokyotyrant_ldflags=""
+with_libtokyotyrant_libs=""
+AC_ARG_WITH(libtokyotyrant, [AS_HELP_STRING([--with-libtokyotyrant@<:@=PREFIX@:>@], [Path to libtokyotyrant.])],
+[
+  if test "x$withval" = "xno"
+  then
+    with_libtokyotyrant="no"
+  else if test "x$withval" = "xyes"
+  then
+    with_libtokyotyrant="yes"
+  else
+    with_libtokyotyrant_cppflags="-I$withval/include"
+    with_libtokyotyrant_ldflags="-L$withval/include"
+    with_libtokyotyrant_libs="-ltokyotyrant"
+    with_libtokyotyrant="yes"
+  fi; fi
+],
+[
+  with_libtokyotyrant="yes"
+])
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+  if $PKG_CONFIG --exists tokyotyrant
+  then
+    with_libtokyotyrant_cppflags="$with_libtokyotyrant_cppflags `$PKG_CONFIG --cflags tokyotyrant`"
+    with_libtokyotyrant_ldflags="$with_libtokyotyrant_ldflags `pkg-config --libs-only-L tokyotyrant`"
+    with_libtokyotyrant_libs="$with_libtokyotyrant_libs `pkg-config --libs-only-l tokyotyrant`"
+  fi
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $with_libtokyotyrant_cppflags"
+LDFLAGS="$LDFLAGS $with_libtokyotyrant_ldflags"
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+  AC_CHECK_HEADERS(tcrdb.h,
+  [
+          AC_DEFINE(HAVE_TCRDB_H, 1,
+                    [Define to 1 if you have the <tcrdb.h> header file.])
+  ], [with_libtokyotyrant="no (tcrdb.h not found)"])
+fi
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+  AC_CHECK_LIB(tokyotyrant, tcrdbrnum,
+  [
+          AC_DEFINE(HAVE_LIBTOKYOTYRANT, 1,
+                    [Define to 1 if you have the tokyotyrant library (-ltokyotyrant).])
+  ],
+  [with_libtokyotyrant="no (symbol tcrdbrnum not found)"],
+  [$with_libtokyotyrant_libs])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libtokyotyrant" = "xyes"
+then 
+  BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS="$with_libtokyotyrant_cppflags"
+  BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS="$with_libtokyotyrant_ldflags"
+  BUILD_WITH_LIBTOKYOTYRANT_LIBS="$with_libtokyotyrant_libs"
+  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS)
+  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
+  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBTOKYOTYRANT, test "x$with_libtokyotyrant" = "xyes")
+# }}}
+
 # --with-libupsclient {{{
 with_libupsclient_config=""
 with_libupsclient_cflags=""
@@ -2966,6 +3039,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 +3422,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 +3540,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 +3692,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 +3734,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])
@@ -3629,6 +3761,7 @@ AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
 AC_PLUGIN([teamspeak2],  [yes],                [TeamSpeak2 server statistics])
 AC_PLUGIN([ted],         [$plugin_ted],        [Read The Energy Detective values])
 AC_PLUGIN([thermal],     [$plugin_thermal],    [Linux ACPI thermal zone statistics])
+AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant],  [TokyoTyrant database statistics])
 AC_PLUGIN([unixsock],    [yes],                [Unixsock communication plugin])
 AC_PLUGIN([uptime],      [$plugin_uptime],     [Uptime statistics])
 AC_PLUGIN([users],       [$plugin_users],      [User statistics])
@@ -3832,10 +3965,12 @@ Configuration:
     librrd  . . . . . . . $with_librrd
     libsensors  . . . . . $with_libsensors
     libstatgrab . . . . . $with_libstatgrab
+    libtokyotyrant  . . . $with_libtokyotyrant
     libupsclient  . . . . $with_libupsclient
     libvirt . . . . . . . $with_libvirt
     libxml2 . . . . . . . $with_libxml2
     libxmms . . . . . . . $with_libxmms
+    libyajl . . . . . . . $with_libyajl
     oracle  . . . . . . . $with_oracle
 
   Features:
@@ -3853,6 +3988,7 @@ Configuration:
     battery . . . . . . . $enable_battery
     bind  . . . . . . . . $enable_bind
     conntrack . . . . . . $enable_conntrack
+    couchdb . . . . . . . $enable_couchdb
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
@@ -3894,6 +4030,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
@@ -3919,6 +4056,7 @@ Configuration:
     tcpconns  . . . . . . $enable_tcpconns
     teamspeak2  . . . . . $enable_teamspeak2
     ted . . . . . . . . . $enable_ted
+    tokyotyrant . . . . . $enable_tokyotyrant
     thermal . . . . . . . $enable_thermal
     unixsock  . . . . . . $enable_unixsock
     uptime  . . . . . . . $enable_uptime
index aca1af7..e6d9073 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
@@ -911,6 +938,19 @@ collectd_LDADD += "-dlopen" thermal.la
 collectd_DEPENDENCIES += thermal.la
 endif
 
+if BUILD_PLUGIN_TOKYOTYRANT
+pkglib_LTLIBRARIES += tokyotyrant.la
+tokyotyrant_la_SOURCES = tokyotyrant.c
+tokyotyrant_la_CPPFLAGS  = $(AM_CPPFLAGS) $(BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS)
+tokyotyrant_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
+tokyotyrant_la_LIBADD  = $(BUILD_WITH_LIBTOKYOTYRANT_LIBS)
+if BUILD_WITH_LIBSOCKET
+tokyotyrant_la_LIBADD += -lsocket
+endif
+collectd_LDADD += "-dlopen" tokyotyrant.la
+collectd_DEPENDENCIES += tokyotyrant.la
+endif
+
 if BUILD_PLUGIN_UNIXSOCK
 pkglib_LTLIBRARIES += unixsock.la
 unixsock_la_SOURCES = unixsock.c \
index 0f48ba5..b8d72f0 100644 (file)
@@ -182,7 +182,7 @@ structure. The general layout looks like this:
 
   [{
     name => 'data_source_name',
-    type => DS_TYPE_COUNTER || DS_TYPE_GAUGE,
+    type => DS_TYPE_COUNTER || DS_TYPE_GAUGE || DS_TYPE_DERIVE || DS_TYPE_ABSOLUTE,
     min  => value || undef,
     max  => value || undef
   }, ...]
@@ -569,6 +569,10 @@ available (B<:all> will export all of them):
 
 =item B<DS_TYPE_GAUGE>
 
+=item B<DS_TYPE_DERIVE>
+
+=item B<DS_TYPE_ABSOLUTE>
+
 =back
 
 =item B<:log>
index 382d6d3..0f01f4e 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
@@ -116,6 +118,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2
 #@BUILD_PLUGIN_TED_TRUE@LoadPlugin ted
 #@BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal
+#@BUILD_PLUGIN_TOKYOTYRANT_TRUE@LoadPlugin tokyotyrant
 #@BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock
 #@BUILD_PLUGIN_UPTIME_TRUE@LoadPlugin uptime
 #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
@@ -170,6 +173,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
@@ -415,6 +449,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"
@@ -567,7 +609,7 @@ FQDNLookup   true
 #       Version 1
 #       Community "community_string"
 #       Collect "std_traffic"
-#       Inverval 120
+#       Interval 120
 #   </Host>
 #   <Host "some.server.mydomain.org">
 #       Address "192.168.0.42"
@@ -644,6 +686,11 @@ FQDNLookup   true
 #      IgnoreSelected false
 #</Plugin>
 
+#<Plugin tokyotyrant>
+#      Host "localhost"
+#      Port "1978"
+#</Plugin>
+
 #<Plugin unixsock>
 #      SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
 #      SocketGroup "collectd"
index 97093c8..e401d42 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.
@@ -3163,6 +3316,26 @@ selection is configured at all, B<all> devices are selected.
 
 =back
 
+=head2 Plugin C<tokyotyrant>
+
+The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a 
+couple metrics: number of records, and database size on disk.
+
+=over 4
+
+=item B<Host> I<Hostname/IP>
+
+The hostname or ip which identifies the server.
+Default: B<127.0.0.1>
+
+=item B<Port> I<Service/Port>
+
+The query port of the server. This needs to be a string, even if the port is
+given in its numeric form.
+Default: B<1978>
+
+=back
+
 =head2 Plugin C<unixsock>
 
 =over 4
@@ -3384,6 +3557,13 @@ 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>.
+
 =back
 
 =head1 FILTER CONFIGURATION
index 8f6cad2..7c2c30e 100644 (file)
@@ -837,28 +837,41 @@ int parse_identifier (char *str, char **ret_host,
        return (0);
 } /* int parse_identifier */
 
-int parse_value (const char *value, value_t *ret_value, const data_source_t ds)
+int parse_value (const char *value, value_t *ret_value, int ds_type)
 {
-       char *endptr = NULL;
-
-       if (DS_TYPE_COUNTER == ds.type)
-               ret_value->counter = (counter_t)strtoll (value, &endptr, 0);
-       else if (DS_TYPE_GAUGE == ds.type)
-               ret_value->gauge = (gauge_t)strtod (value, &endptr);
-       else {
-               ERROR ("parse_value: Invalid data source \"%s\" "
-                               "(type = %i).", ds.name, ds.type);
-               return -1;
-       }
-
-       if (value == endptr) {
-               ERROR ("parse_value: Failed to parse string as number: %s.", value);
-               return -1;
-       }
-       else if ((NULL != endptr) && ('\0' != *endptr))
-               WARNING ("parse_value: Ignoring trailing garbage after number: %s.",
-                               endptr);
-       return 0;
+  char *endptr = NULL;
+
+  switch (ds_type)
+  {
+    case DS_TYPE_COUNTER:
+      ret_value->counter = (counter_t) strtoull (value, &endptr, 0);
+      break;
+
+    case DS_TYPE_GAUGE:
+      ret_value->gauge = (gauge_t) strtod (value, &endptr);
+      break;
+
+    case DS_TYPE_DERIVE:
+      ret_value->counter = (derive_t) strtoll (value, &endptr, 0);
+      break;
+
+    case DS_TYPE_ABSOLUTE:
+      ret_value->counter = (absolute_t) strtoull (value, &endptr, 0);
+      break;
+
+    default:
+      ERROR ("parse_value: Invalid data source type: %i.", ds_type);
+      return -1;
+  }
+
+  if (value == endptr) {
+    ERROR ("parse_value: Failed to parse string as number: %s.", value);
+    return -1;
+  }
+  else if ((NULL != endptr) && ('\0' != *endptr))
+    WARNING ("parse_value: Ignoring trailing garbage after number: %s.",
+        endptr);
+  return 0;
 } /* int parse_value */
 
 int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
@@ -893,7 +906,7 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
                {
                        if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
                                vl->values[i].gauge = NAN;
-                       else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i]))
+                       else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type))
                                return -1;
                }
 
index d0cc4e8..e424f5d 100644 (file)
@@ -260,7 +260,7 @@ int format_name (char *ret, int ret_len,
 int parse_identifier (char *str, char **ret_host,
                char **ret_plugin, char **ret_plugin_instance,
                char **ret_type, char **ret_type_instance);
-int parse_value (const char *value, value_t *ret_value, const data_source_t ds);
+int parse_value (const char *value, value_t *ret_value, int ds_type);
 int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds);
 
 #if !HAVE_GETPWNAM_R
diff --git a/src/couchdb.c b/src/couchdb.c
new file mode 100644 (file)
index 0000000..30f56da
--- /dev/null
@@ -0,0 +1,820 @@
+/**
+ * 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 = (counter_t) val;
+    else if (type == DS_TYPE_GAUGE)
+      vt.gauge = (gauge_t) val;
+    else if (type == DS_TYPE_DERIVE)
+      vt.derive = (derive_t) val;
+    else if (type == DS_TYPE_ABSOLUTE)
+      vt.absolute = (absolute_t) val;
+    else
+      return 0;
+
+    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_COUNTER)
+      vt.counter = (counter_t) val;
+    else if (type == DS_TYPE_GAUGE)
+      vt.gauge = (gauge_t) val;
+    else if (type == DS_TYPE_DERIVE)
+      vt.derive = (derive_t) val;
+    else if (type == DS_TYPE_ABSOLUTE)
+      vt.absolute = (absolute_t) val;
+    else
+      return 0;
+
+    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 5c43b8a..78037a9 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -61,37 +61,50 @@ static int value_list_to_string (char *buffer, int buffer_len,
        for (i = 0; i < ds->ds_num; i++)
        {
                if ((ds->ds[i].type != DS_TYPE_COUNTER)
-                               && (ds->ds[i].type != DS_TYPE_GAUGE))
+                               && (ds->ds[i].type != DS_TYPE_GAUGE)
+                               && (ds->ds[i].type != DS_TYPE_DERIVE)
+                               && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
                        return (-1);
 
-               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               if (ds->ds[i].type == DS_TYPE_GAUGE) 
                {
-                       if (store_rates == 0)
-                       {
-                               status = ssnprintf (buffer + offset,
-                                               buffer_len - offset,
-                                               ",%llu",
-                                               vl->values[i].counter);
-                       }
-                       else /* if (store_rates == 1) */
+                       status = ssnprintf (buffer + offset, buffer_len - offset,
+                                       ",%lf", vl->values[i].gauge);
+               } 
+               else if (store_rates != 0)
+               {
+                       if (rates == NULL)
+                               rates = uc_get_rate (ds, vl);
+                       if (rates == NULL)
                        {
-                               if (rates == NULL)
-                                       rates = uc_get_rate (ds, vl);
-                               if (rates == NULL)
-                               {
-                                       WARNING ("csv plugin: "
-                                                       "uc_get_rate failed.");
-                                       return (-1);
-                               }
-                               status = ssnprintf (buffer + offset,
-                                               buffer_len - offset,
-                                               ",%lf", rates[i]);
+                               WARNING ("csv plugin: "
+                                               "uc_get_rate failed.");
+                               return (-1);
                        }
+                       status = ssnprintf (buffer + offset,
+                                       buffer_len - offset,
+                                       ",%lf", rates[i]);
                }
-               else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+               else if (ds->ds[i].type == DS_TYPE_COUNTER)
                {
-                       status = ssnprintf (buffer + offset, buffer_len - offset,
-                                       ",%lf", vl->values[i].gauge);
+                       status = ssnprintf (buffer + offset,
+                                       buffer_len - offset,
+                                       ",%llu",
+                                       vl->values[i].counter);
+               }
+               else if (ds->ds[i].type == DS_TYPE_DERIVE)
+               {
+                       status = ssnprintf (buffer + offset,
+                                       buffer_len - offset,
+                                       ",%"PRIi64,
+                                       vl->values[i].derive);
+               }
+               else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+               {
+                       status = ssnprintf (buffer + offset,
+                                       buffer_len - offset,
+                                       ",%"PRIu64,
+                                       vl->values[i].absolute);
                }
 
                if ((status < 1) || (status >= (buffer_len - offset)))
index 88b5496..41f11f6 100644 (file)
@@ -222,6 +222,29 @@ static int cc_config_add_match_dstype (int *dstype_ret, /* {{{ */
     else
       dstype = 0;
   }
+else if (strncasecmp ("Derive", ci->values[0].value.string,
+        strlen ("Derive")) == 0)
+  {
+    dstype = UTILS_MATCH_DS_TYPE_DERIVE;
+    if (strcasecmp ("DeriveSet", ci->values[0].value.string) == 0)
+      dstype |= UTILS_MATCH_CF_DERIVE_SET;
+    else if (strcasecmp ("DeriveAdd", ci->values[0].value.string) == 0)
+      dstype |= UTILS_MATCH_CF_DERIVE_ADD;
+    else if (strcasecmp ("DeriveInc", ci->values[0].value.string) == 0)
+      dstype |= UTILS_MATCH_CF_DERIVE_INC;
+    else
+      dstype = 0;
+  }
+else if (strncasecmp ("Absolute", ci->values[0].value.string,
+        strlen ("Absolute")) == 0)
+  {
+    dstype = UTILS_MATCH_DS_TYPE_ABSOLUTE;
+    if (strcasecmp ("AbsoluteSet", ci->values[0].value.string) == 0) /* Absolute DS is reset-on-read so no sense doin anything else but set */
+      dstype |= UTILS_MATCH_CF_ABSOLUTE_SET;
+    else
+      dstype = 0;
+  }
+
   else
   {
     dstype = 0;
index 3de7c34..8a2e87e 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
        {
index 006f5b8..3357ea0 100644 (file)
@@ -541,6 +541,11 @@ static int staging_entry_update (const char *host, const char *name, /* {{{ */
     se->vl.values[ds_index].counter += value.counter;
   else if (ds_type == DS_TYPE_GAUGE)
     se->vl.values[ds_index].gauge = value.gauge;
+  else if (ds_type == DS_TYPE_DERIVE)
+    se->vl.values[ds_index].DERIVE += value.derive;
+  else if (ds_type == DS_TYPE_ABSOLUTE)
+    se->vl.values[ds_index].ABSOLUTE = value.absolute;
+
   se->flags |= (0x01 << ds_index);
 
   /* Check if all values have been set and submit if so. */
@@ -565,6 +570,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
 
   value_t value_counter;
   value_t value_gauge;
+  value_t value_derive;
 
   /* Fill in `host', `name', `value_counter', and `value_gauge' according to
    * the value type, or return with an error. */
@@ -580,6 +586,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       name = msg_uint.metric_id.name;
       value_counter.counter = (counter_t) msg_uint.ui;
       value_gauge.gauge = (gauge_t) msg_uint.ui;
+      value_derive.derive = (derive_t) msg_uint.ui;
       break;
     }
 
@@ -606,6 +613,13 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       if ((endptr == msg_string.str) || (errno != 0))
         value_gauge.gauge = NAN;
 
+      endptr = NULL;
+      errno = 0;
+      value_derive.derive = (derive_t) strtoll (msg_string.str,
+          &endptr, /* base = */ 0);
+      if ((endptr == msg_string.str) || (errno != 0))
+        value_derive.derive = 0;
+
       break;
     }
 
@@ -619,6 +633,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       name = msg_float.metric_id.name;
       value_counter.counter = (counter_t) msg_float.f;
       value_gauge.gauge = (gauge_t) msg_float.f;
+      value_derive.derive = (derive_t) msg_float.f;
       break;
     }
 
@@ -632,6 +647,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       name = msg_double.metric_id.name;
       value_counter.counter = (counter_t) msg_double.d;
       value_gauge.gauge = (gauge_t) msg_double.d;
+      value_derive.derive = (derive_t) msg_double.d;
       break;
     }
     default:
@@ -644,10 +660,20 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
 
   map = metric_lookup (name);
   if (map != NULL)
+  {
+    value_t val_copy;
+
+    val_copy = value_counter;
+    if (map->ds_type == DS_TYPE_GAUGE)
+      val_copy = value_gauge;
+    else if (map->ds_type == DS_TYPE_DERIVE)
+      val_copy = value_derive;
+
     return (staging_entry_update (host, name,
           map->type, map->type_instance,
           map->ds_index, map->ds_type,
-          (map->ds_type == DS_TYPE_COUNTER) ? value_counter : value_gauge));
+          val_copy));
+  }
 
   DEBUG ("gmond plugin: Cannot find a translation for %s.", name);
   return (-1);
index 6a98d82..03df2b7 100644 (file)
@@ -284,6 +284,10 @@ static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
     return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter));
   else if (ds_type == DS_TYPE_GAUGE)
     return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge));
+  if (ds_type == DS_TYPE_DERIVE)
+    return (ctoj_jlong_to_number (jvm_env, (jlong) value.derive));
+  if (ds_type == DS_TYPE_ABSOLUTE)
+    return (ctoj_jlong_to_number (jvm_env, (jlong) value.absolute));
   else
     return (NULL);
 } /* }}} jobject ctoj_value_to_number */
@@ -1042,33 +1046,39 @@ static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
 
   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
 
-  if (ds_type == DS_TYPE_COUNTER)
+  if (ds_type == DS_TYPE_GAUGE)
   {
-    jlong tmp_long;
+    jdouble tmp_double;
 
-    status = jtoc_long (jvm_env, &tmp_long,
-        class_ptr, object_ptr, "longValue");
+    status = jtoc_double (jvm_env, &tmp_double,
+        class_ptr, object_ptr, "doubleValue");
     if (status != 0)
     {
       ERROR ("java plugin: jtoc_value: "
-          "jtoc_long failed.");
+          "jtoc_double failed.");
       return (-1);
     }
-    (*ret_value).counter = (counter_t) tmp_long;
+    (*ret_value).gauge = (gauge_t) tmp_double;
   }
   else
   {
-    jdouble tmp_double;
+    jlong tmp_long;
 
-    status = jtoc_double (jvm_env, &tmp_double,
-        class_ptr, object_ptr, "doubleValue");
+    status = jtoc_long (jvm_env, &tmp_long,
+        class_ptr, object_ptr, "longValue");
     if (status != 0)
     {
       ERROR ("java plugin: jtoc_value: "
-          "jtoc_double failed.");
+          "jtoc_long failed.");
       return (-1);
     }
-    (*ret_value).gauge = (gauge_t) tmp_double;
+
+    if (ds_type == DS_TYPE_DERIVE)
+      (*ret_value).derive = (derive_t) tmp_long;
+    else if (ds_type == DS_TYPE_ABSOLUTE)
+      (*ret_value).absolute = (absolute_t) tmp_long;
+    else
+      (*ret_value).counter = (counter_t) tmp_long;
   }
 
   return (0);
index 407493c..63d4e9d 100644 (file)
@@ -792,6 +792,11 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */
       else
         SSTRCATF (command, ":%g", vl->values[i].gauge);
     }
+    else if (vl->values_types[i] == LCC_TYPE_DERIVE)
+       SSTRCATF (command, ":%"PRIu64, vl->values[i].derive);
+    else if (vl->values_types[i] == LCC_TYPE_ABSOLUTE)
+       SSTRCATF (command, ":%"PRIu64, vl->values[i].absolute);
+
   } /* for (i = 0; i < vl->values_len; i++) */
 
   status = lcc_sendreceive (c, command, &res);
index d5371fb..b0d092d 100644 (file)
  */
 #define LCC_TYPE_COUNTER 0
 #define LCC_TYPE_GAUGE   1
+#define LCC_TYPE_DERIVE   2
+#define LCC_TYPE_ABSOLUTE   3
 
 LCC_BEGIN_DECLS
 
 typedef uint64_t counter_t;
 typedef double gauge_t;
+typedef uint64_t derive_t;
+typedef uint64_t absolute_t;
 
 union value_u
 {
   counter_t counter;
   gauge_t   gauge;
+  derive_t  derive;
+  absolute_t absolute;
 };
 typedef union value_u value_t;
 
index 2c085e3..d7fe2eb 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/meta_data.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008,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
@@ -32,6 +32,7 @@
 #define MD_TYPE_SIGNED_INT   2
 #define MD_TYPE_UNSIGNED_INT 3
 #define MD_TYPE_DOUBLE       4
+#define MD_TYPE_BOOLEAN      5
 
 /*
  * Data types
@@ -42,6 +43,7 @@ union meta_value_u
   int64_t  mv_signed_int;
   uint64_t mv_unsigned_int;
   double   mv_double;
+  _Bool    mv_boolean;
 };
 typedef union meta_value_u meta_value_t;
 
@@ -287,6 +289,9 @@ int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
   return (0);
 } /* }}} int meta_data_delete */
 
+/*
+ * Add functions
+ */
 int meta_data_add_string (meta_data_t *md, /* {{{ */
     const char *key, const char *value)
 {
@@ -365,6 +370,27 @@ int meta_data_add_double (meta_data_t *md, /* {{{ */
   return (md_entry_insert (md, e));
 } /* }}} int meta_data_add_double */
 
+int meta_data_add_boolean (meta_data_t *md, /* {{{ */
+    const char *key, _Bool value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_boolean = value;
+  e->type = MD_TYPE_BOOLEAN;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_boolean */
+
+/*
+ * Get functions
+ */
 int meta_data_get_string (meta_data_t *md, /* {{{ */
     const char *key, char **value)
 {
@@ -495,4 +521,34 @@ int meta_data_get_double (meta_data_t *md, /* {{{ */
   return (0);
 } /* }}} int meta_data_get_double */
 
+int meta_data_get_boolean (meta_data_t *md, /* {{{ */
+    const char *key, _Bool *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_BOOLEAN)
+  {
+    ERROR ("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_boolean;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_boolean */
+
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 30e7285..8e5a785 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/meta_data.h
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008,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
@@ -45,6 +45,9 @@ int meta_data_add_unsigned_int (meta_data_t *md,
 int meta_data_add_double (meta_data_t *md,
     const char *key,
     double value);
+int meta_data_add_boolean (meta_data_t *md,
+    const char *key,
+    _Bool value);
 
 int meta_data_get_string (meta_data_t *md,
     const char *key,
@@ -58,6 +61,9 @@ int meta_data_get_unsigned_int (meta_data_t *md,
 int meta_data_get_double (meta_data_t *md,
     const char *key,
     double *value);
+int meta_data_get_boolean (meta_data_t *md,
+    const char *key,
+    _Bool *value);
 
 #endif /* META_DATA_H */
 /* vim: set sw=2 sts=2 et : */
index 0a74a19..75f52df 100644 (file)
@@ -527,17 +527,34 @@ static int write_part_values (char **ret_buffer, int *ret_buffer_len,
 
        for (i = 0; i < num_values; i++)
        {
-               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               pkg_values_types[i] = (uint8_t) ds->ds[i].type;
+               switch (ds->ds[i].type)
                {
-                       pkg_values_types[i] = DS_TYPE_COUNTER;
-                       pkg_values[i].counter = htonll (vl->values[i].counter);
-               }
-               else
-               {
-                       pkg_values_types[i] = DS_TYPE_GAUGE;
-                       pkg_values[i].gauge = htond (vl->values[i].gauge);
-               }
-       }
+                       case DS_TYPE_COUNTER:
+                               pkg_values[i].counter = htonll (vl->values[i].counter);
+                               break;
+
+                       case DS_TYPE_GAUGE:
+                               pkg_values[i].gauge = htond (vl->values[i].gauge);
+                               break;
+
+                       case DS_TYPE_DERIVE:
+                               pkg_values[i].derive = htonll (vl->values[i].derive);
+                               break;
+
+                       case DS_TYPE_ABSOLUTE:
+                               pkg_values[i].absolute = htonll (vl->values[i].absolute);
+                               break;
+
+                       default:
+                               free (pkg_values_types);
+                               free (pkg_values);
+                               ERROR ("network plugin: write_part_values: "
+                                               "Unknown data source type: %i",
+                                               ds->ds[i].type);
+                               return (-1);
+               } /* switch (ds->ds[i].type) */
+       } /* for (num_values) */
 
        /*
         * Use `memcpy' to write everything to the buffer, because the pointer
@@ -713,10 +730,32 @@ static int parse_part_values (void **ret_buffer, size_t *ret_buffer_len,
 
        for (i = 0; i < pkg_numval; i++)
        {
-               if (pkg_types[i] == DS_TYPE_COUNTER)
-                       pkg_values[i].counter = ntohll (pkg_values[i].counter);
-               else if (pkg_types[i] == DS_TYPE_GAUGE)
-                       pkg_values[i].gauge = ntohd (pkg_values[i].gauge);
+               switch (pkg_types[i])
+               {
+                 case DS_TYPE_COUNTER:
+                   pkg_values[i].counter = (counter_t) ntohll (pkg_values[i].counter);
+                   break;
+
+                 case DS_TYPE_GAUGE:
+                   pkg_values[i].gauge = (gauge_t) ntohd (pkg_values[i].gauge);
+                   break;
+
+                 case DS_TYPE_DERIVE:
+                   pkg_values[i].derive = (derive_t) ntohll (pkg_values[i].derive);
+                   break;
+
+                 case DS_TYPE_ABSOLUTE:
+                   pkg_values[i].absolute = (absolute_t) ntohll (pkg_values[i].absolute);
+                   break;
+
+                 default:
+                   sfree (pkg_types);
+                   sfree (pkg_values);
+                   NOTICE ("network plugin: parse_part_values: "
+                       "Don't know how to handle data source type %"PRIu8,
+                       pkg_types[i]);
+                   return (-1);
+               } /* switch (pkg_types[i]) */
        }
 
        *ret_buffer     = buffer;
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 b6e7b22..50def1f 100644 (file)
@@ -194,6 +194,8 @@ struct {
        { "Collectd::TYPE_DATASET",       PLUGIN_DATASET },
        { "Collectd::DS_TYPE_COUNTER",    DS_TYPE_COUNTER },
        { "Collectd::DS_TYPE_GAUGE",      DS_TYPE_GAUGE },
+       { "Collectd::DS_TYPE_DERIVE",     DS_TYPE_DERIVE },
+       { "Collectd::DS_TYPE_ABSOLUTE",   DS_TYPE_ABSOLUTE },
        { "Collectd::LOG_ERR",            LOG_ERR },
        { "Collectd::LOG_WARNING",        LOG_WARNING },
        { "Collectd::LOG_NOTICE",         LOG_NOTICE },
@@ -267,7 +269,10 @@ static int hv2data_source (pTHX_ HV *hash, data_source_t *ds)
        if (NULL != (tmp = hv_fetch (hash, "type", 4, 0))) {
                ds->type = SvIV (*tmp);
 
-               if ((DS_TYPE_COUNTER != ds->type) && (DS_TYPE_GAUGE != ds->type)) {
+               if ((DS_TYPE_COUNTER != ds->type)
+                               && (DS_TYPE_GAUGE != ds->type)
+                               && (DS_TYPE_DERIVE != ds->type)
+                               && (DS_TYPE_ABSOLUTE != ds->type)) {
                        log_err ("hv2data_source: Invalid DS type.");
                        return -1;
                }
@@ -320,8 +325,12 @@ static int av2value (pTHX_ char *name, AV *array, value_t *value, int len)
                if (NULL != tmp) {
                        if (DS_TYPE_COUNTER == ds->ds[i].type)
                                value[i].counter = SvIV (*tmp);
-                       else
+                       else if (DS_TYPE_GAUGE == ds->ds[i].type)
                                value[i].gauge = SvNV (*tmp);
+                       else if (DS_TYPE_DERIVE == ds->ds[i].type)
+                               value[i].derive = SvIV (*tmp);
+                       else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
+                               value[i].absolute = SvIV (*tmp);
                }
                else {
                        return -1;
@@ -637,8 +646,12 @@ static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash)
 
                if (DS_TYPE_COUNTER == ds->ds[i].type)
                        val = newSViv (vl->values[i].counter);
-               else
+               else if (DS_TYPE_GAUGE == ds->ds[i].type)
                        val = newSVnv (vl->values[i].gauge);
+               else if (DS_TYPE_DERIVE == ds->ds[i].type)
+                       val = newSViv (vl->values[i].derive);
+               else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
+                       val = newSViv (vl->values[i].absolute);
 
                if (NULL == av_store (values, i, val)) {
                        av_undef (values);
index 6139baf..a5ae97b 100644 (file)
@@ -1136,6 +1136,8 @@ int plugin_dispatch_values (value_list_t *vl)
 
        data_set_t *ds;
 
+       int free_meta_data = 0;
+
        if ((vl == NULL) || (vl->type[0] == 0)
                        || (vl->values == NULL) || (vl->values_len < 1))
        {
@@ -1143,6 +1145,12 @@ int plugin_dispatch_values (value_list_t *vl)
                return (-1);
        }
 
+       /* Free meta data only if the calling function didn't specify any. In
+        * this case matches and targets may add some and the calling function
+        * may not expect (and therefore free) that data. */
+       if (vl->meta == NULL)
+               free_meta_data = 1;
+
        if (list_write == NULL)
                c_complain_once (LOG_WARNING, &no_write_complaint,
                                "plugin_dispatch_values: No write callback has been "
@@ -1283,6 +1291,12 @@ int plugin_dispatch_values (value_list_t *vl)
                vl->values_len = saved_values_len;
        }
 
+       if ((free_meta_data != 0) && (vl->meta != NULL))
+       {
+               meta_data_destroy (vl->meta);
+               vl->meta = NULL;
+       }
+
        return (0);
 } /* int plugin_dispatch_values */
 
index e54b27d..b35fcf1 100644 (file)
 
 #include "collectd.h"
 #include "configfile.h"
+#include "meta_data.h"
 
 #define DATA_MAX_NAME_LEN 64
 
-#define DS_TYPE_COUNTER 0
-#define DS_TYPE_GAUGE   1
+#define DS_TYPE_COUNTER  0
+#define DS_TYPE_GAUGE    1
+#define DS_TYPE_DERIVE   2
+#define DS_TYPE_ABSOLUTE 3
+
+#define DS_TYPE_TO_STRING(t) (t == DS_TYPE_COUNTER)     ? "counter"  : \
+                               (t == DS_TYPE_GAUGE)    ? "gauge"    : \
+                               (t == DS_TYPE_DERIVE)   ? "derive"   : \
+                               (t == DS_TYPE_ABSOLUTE) ? "absolute" : \
+                               "unknown"
+
 
 #ifndef LOG_ERR
 # define LOG_ERR 3
  */
 typedef unsigned long long counter_t;
 typedef double gauge_t;
+typedef int64_t derive_t;
+typedef uint64_t absolute_t;
 
 union value_u
 {
-       counter_t counter;
-       gauge_t   gauge;
+       counter_t  counter;
+       gauge_t    gauge;
+       derive_t   derive;
+       absolute_t absolute;
 };
 typedef union value_u value_t;
 
@@ -76,11 +90,12 @@ struct value_list_s
        char     plugin_instance[DATA_MAX_NAME_LEN];
        char     type[DATA_MAX_NAME_LEN];
        char     type_instance[DATA_MAX_NAME_LEN];
+       meta_data_t *meta;
 };
 typedef struct value_list_s value_list_t;
 
-#define VALUE_LIST_INIT { NULL, 0, 0, interval_g, "localhost", "", "", "", "" }
-#define VALUE_LIST_STATIC { NULL, 0, 0, 0, "localhost", "", "", "", "" }
+#define VALUE_LIST_INIT { NULL, 0, 0, interval_g, "localhost", "", "", "", "", NULL }
+#define VALUE_LIST_STATIC { NULL, 0, 0, 0, "localhost", "", "", "", "", NULL }
 
 struct data_source_s
 {
index beb49fb..8799702 100644 (file)
@@ -289,7 +289,7 @@ static void submit (const char *plugin_instance, /* {{{ */
     return;
   }
 
-  if (0 != parse_value (value, &values[0], ds->ds[0]))
+  if (0 != parse_value (value, &values[0], ds->ds[0].type))
   {
     ERROR ("powerdns plugin: Cannot convert `%s' "
         "to a number.", value);
index 326a889..728ada4 100644 (file)
@@ -75,7 +75,9 @@ static int value_list_to_string (char *buffer, int buffer_len,
   for (i = 0; i < ds->ds_num; i++)
   {
     if ((ds->ds[i].type != DS_TYPE_COUNTER)
-        && (ds->ds[i].type != DS_TYPE_GAUGE))
+        && (ds->ds[i].type != DS_TYPE_GAUGE)
+       && (ds->ds[i].type != DS_TYPE_DERIVE)
+       && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
       return (-1);
 
     if (ds->ds[i].type == DS_TYPE_COUNTER)
@@ -83,10 +85,19 @@ static int value_list_to_string (char *buffer, int buffer_len,
       status = ssnprintf (buffer + offset, buffer_len - offset,
           ":%llu", vl->values[i].counter);
     }
-    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+    else if (ds->ds[i].type == DS_TYPE_GAUGE) 
     {
       status = ssnprintf (buffer + offset, buffer_len - offset,
-          ":%lf", vl->values[i].gauge);
+          ":%f", vl->values[i].gauge);
+    }
+    else if (ds->ds[i].type == DS_TYPE_DERIVE) {
+      status = ssnprintf (buffer + offset, buffer_len - offset,
+         ":%"PRIi64, vl->values[i].derive);
+    }
+    else /* if (ds->ds[i].type == DS_TYPE_ABSOLUTE) */ {
+      status = ssnprintf (buffer + offset, buffer_len - offset,
+         ":%"PRIu64, vl->values[i].absolute);
     }
 
     if ((status < 1) || (status >= (buffer_len - offset)))
index 243a8c8..c63f883 100644 (file)
@@ -204,15 +204,23 @@ static int value_list_to_string (char *buffer, int buffer_len,
        for (i = 0; i < ds->ds_num; i++)
        {
                if ((ds->ds[i].type != DS_TYPE_COUNTER)
-                               && (ds->ds[i].type != DS_TYPE_GAUGE))
+                               && (ds->ds[i].type != DS_TYPE_GAUGE)
+                               && (ds->ds[i].type != DS_TYPE_DERIVE)
+                               && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
                        return (-1);
 
                if (ds->ds[i].type == DS_TYPE_COUNTER)
                        status = ssnprintf (buffer + offset, buffer_len - offset,
                                        ":%llu", vl->values[i].counter);
-               else
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
                        status = ssnprintf (buffer + offset, buffer_len - offset,
                                        ":%lf", vl->values[i].gauge);
+               else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                       status = ssnprintf (buffer + offset, buffer_len - offset,
+                                       ":%"PRIi64, vl->values[i].derive);
+               else /*if (ds->ds[i].type == DS_TYPE_ABSOLUTE) */
+                       status = ssnprintf (buffer + offset, buffer_len - offset,
+                                       ":%"PRIu64, vl->values[i].absolute);
 
                if ((status < 1) || (status >= (buffer_len - offset)))
                        return (-1);
index 23e199e..7568cf6 100644 (file)
@@ -757,9 +757,8 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
 
   if (vl->type == ASN_OCTET_STR)
   {
-    char *endptr;
+    int status = -1;
 
-    endptr = NULL;
     if (vl->val.string != NULL)
     {
       char string[64];
@@ -770,45 +769,59 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
        string_length = vl->val_len;
 
       /* The strings we get from the Net-SNMP library may not be null
-       * terminated. That is why we're using `membpy' here and not `strcpy'.
+       * terminated. That is why we're using `memcpy' here and not `strcpy'.
        * `string_length' is set to `vl->val_len' which holds the length of the
        * string.  -octo */
       memcpy (string, vl->val.string, string_length);
       string[string_length] = 0;
 
-      if (type == DS_TYPE_COUNTER)
+      status = parse_value (string, &ret, type);
+      if (status != 0)
       {
-       ret.counter = (counter_t) strtoll (string, &endptr, /* base = */ 0);
-       DEBUG ("snmp plugin: csnmp_value_list_to_value: String to counter: %s -> %llu",
-           string, (unsigned long long) ret.counter);
-      }
-      else if (type == DS_TYPE_GAUGE)
-      {
-       ret.gauge = (gauge_t) strtod (string, &endptr);
-       DEBUG ("snmp plugin: csnmp_value_list_to_value: String to gauge: %s -> %g",
-           string, (double) ret.gauge);
+       ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
+           DS_TYPE_TO_STRING (type), string);
       }
     }
 
-    /* Check if an error occurred */
-    if ((vl->val.string == NULL) || (endptr == (char *) vl->val.string))
+    if (status != 0)
     {
-      if (type == DS_TYPE_COUNTER)
-       ret.counter = 0;
-      else if (type == DS_TYPE_GAUGE)
-       ret.gauge = NAN;
+      switch (type)
+      {
+       case DS_TYPE_COUNTER:
+       case DS_TYPE_DERIVE:
+       case DS_TYPE_ABSOLUTE:
+         memset (&ret, 0, sizeof (ret));
+         break;
+
+       case DS_TYPE_GAUGE:
+         ret.gauge = NAN;
+         break;
+
+       default:
+         ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown "
+             "data source type: %i.", type);
+         ret.gauge = NAN;
+      }
     }
-  }
+  } /* if (vl->type == ASN_OCTET_STR) */
   else if (type == DS_TYPE_COUNTER)
-  {
     ret.counter = temp;
-  }
   else if (type == DS_TYPE_GAUGE)
   {
     ret.gauge = NAN;
     if (defined != 0)
       ret.gauge = (scale * temp) + shift;
   }
+  else if (type == DS_TYPE_DERIVE)
+    ret.derive = (derive_t) temp;
+  else if (type == DS_TYPE_ABSOLUTE)
+    ret.absolute = (absolute_t) temp;
+  else
+  {
+    ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source "
+       "type: %i.", type);
+    ret.gauge = NAN;
+  }
 
   return (ret);
 } /* value_t csnmp_value_list_to_value */
index 2911bf0..9641c75 100644 (file)
@@ -383,7 +383,7 @@ static int tbl_result_dispatch (tbl_t *tbl, tbl_result_t *res,
                assert (res->values[i] < fields_num);
                value = fields[res->values[i]];
 
-               if (0 != parse_value (value, &values[i], res->ds->ds[i]))
+               if (0 != parse_value (value, &values[i], res->ds->ds[i].type))
                        return -1;
        }
 
index 02afd79..8becc05 100644 (file)
@@ -101,6 +101,26 @@ static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
     else
       cm->flags = 0;
   }
+  else if (strncasecmp ("Derive", ci->values[0].value.string, strlen ("Derive")) == 0)
+  {
+    cm->flags = UTILS_MATCH_DS_TYPE_DERIVE;
+    if (strcasecmp ("DeriveSet", ci->values[0].value.string) == 0)
+      cm->flags |= UTILS_MATCH_CF_DERIVE_SET;
+    else if (strcasecmp ("DeriveAdd", ci->values[0].value.string) == 0)
+      cm->flags |= UTILS_MATCH_CF_DERIVE_ADD;
+    else if (strcasecmp ("DeriveInc", ci->values[0].value.string) == 0)
+      cm->flags |= UTILS_MATCH_CF_DERIVE_INC;
+    else
+      cm->flags = 0;
+  }
+  else if (strncasecmp ("Absolute", ci->values[0].value.string, strlen ("Absolute")) == 0)
+  {
+    cm->flags = UTILS_MATCH_DS_TYPE_ABSOLUTE;
+    if (strcasecmp ("AbsoluteSet", ci->values[0].value.string) == 0)
+      cm->flags |= UTILS_MATCH_CF_ABSOLUTE_SET;
+    else
+      cm->flags = 0;
+  }
   else
   {
     cm->flags = 0;
diff --git a/src/tokyotyrant.c b/src/tokyotyrant.c
new file mode 100644 (file)
index 0000000..26366c9
--- /dev/null
@@ -0,0 +1,234 @@
+/**
+ * collectd - src/tokyotyrant.c
+ * Copyright (C) 2009 Paul Sadauskas
+ *
+ * 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:
+ *   Paul Sadauskas <psadauskas@gmail.com>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_cache.h"
+#include "utils_parse_option.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <tcrdb.h>
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 1978
+
+static const char *config_keys[] =
+{
+       "Host",
+       "Port"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char *config_host = NULL;
+static char *config_port = NULL;
+
+static TCRDB *rdb = NULL;
+
+static int parse_service_name (const char *service_name)
+{
+       struct addrinfo *ai_list;
+       struct addrinfo *ai_ptr;
+       struct addrinfo ai_hints;
+       int status;
+       int service_number;
+
+       ai_list = NULL;
+       memset (&ai_hints, 0, sizeof (ai_hints));
+       ai_hints.ai_family = AF_UNSPEC;
+
+       status = getaddrinfo (/* node = */ NULL, service_name,
+                       &ai_hints, &ai_list);
+       if (status != 0)
+       {
+               ERROR ("tokyotyrant plugin: getaddrinfo failed: %s",
+                               gai_strerror (status));
+               return (-1);
+       }
+
+       service_number = -1;
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+       {
+               if (ai_ptr->ai_family == AF_INET)
+               {
+                       struct sockaddr_in *sa;
+
+                       sa = (void *) ai_ptr->ai_addr;
+                       service_number = (int) ntohs (sa->sin_port);
+               }
+               else if (ai_ptr->ai_family == AF_INET6)
+               {
+                       struct sockaddr_in6 *sa;
+
+                       sa = (void *) ai_ptr->ai_addr;
+                       service_number = (int) ntohs (sa->sin6_port);
+               }
+
+               if ((service_number > 0) && (service_number <= 65535))
+                       break;
+       }
+
+       freeaddrinfo (ai_list);
+
+       if ((service_number > 0) && (service_number <= 65535))
+               return (service_number);
+       return (-1);
+} /* int parse_service_name */
+
+static int tt_config (const char *key, const char *value)
+{
+       if (strcasecmp ("Host", key) == 0)
+       {
+               char *temp;
+
+               temp = strdup (value);
+               if (temp == NULL)
+               {
+                       ERROR("tokyotyrant plugin: Host strdup failed.");
+                       return (1);
+               }
+               sfree (config_host);
+               config_host = temp;
+       }
+       else if (strcasecmp ("Port", key) == 0)
+       {
+               char *temp;
+
+               temp = strdup (value);
+               if (temp == NULL)
+               {
+                       ERROR("tokyotyrant plugin: Port strdup failed.");
+                       return (1);
+               }
+               sfree (config_port);
+               config_port = temp;
+       }
+       else
+       {
+               ERROR ("tokyotyrant plugin: error: unrecognized configuration key %s", key);
+               return (-1);
+       }
+
+       return (0);
+}
+
+static void printerr()
+{
+       int ecode = tcrdbecode(rdb);
+       ERROR ("tokyotyrant plugin: error: %d, %s",
+                       ecode, tcrdberrmsg(ecode));
+}
+
+static void tt_submit (gauge_t val, const char* type)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = val;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+
+       sstrncpy (vl.host, config_host, sizeof (vl.host));
+       sstrncpy (vl.plugin, "tokyotyrant", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, config_port,
+                       sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+}
+
+static void tt_open_db (void)
+{
+       char* host = NULL;
+       int   port = DEFAULT_PORT;
+
+       if (rdb != NULL)
+               return;
+
+       host = ((config_host != NULL) ? config_host : DEFAULT_HOST);
+
+       if (config_port != NULL)
+       {
+               port = parse_service_name (config_port);
+               if (port <= 0)
+                       return;
+       }
+
+       rdb = tcrdbnew ();
+       if (rdb == NULL)
+               return;
+       else if (!tcrdbopen(rdb, host, port))
+       {
+               printerr ();
+               tcrdbdel (rdb);
+               rdb = NULL;
+       }
+} /* void tt_open_db */
+
+static int tt_read (void) {
+       gauge_t rnum, size;
+
+       tt_open_db ();
+       if (rdb == NULL)
+               return (-1);
+
+       rnum = tcrdbrnum(rdb);
+       tt_submit (rnum, "records");
+
+       size = tcrdbsize(rdb);
+       tt_submit (size, "file_size");
+
+       return (0);
+}
+
+static int tt_shutdown(void)
+{
+       sfree(config_host);
+       sfree(config_port);
+
+       if (rdb != NULL)
+       {
+               if (!tcrdbclose(rdb))
+               {
+                       printerr ();
+                       tcrdbdel (rdb);
+                       return (1);
+               }
+               tcrdbdel (rdb);
+               rdb = NULL;
+       }
+
+       return(0);
+}
+
+void module_register (void)
+{
+       plugin_register_config("tokyotyrant", tt_config,
+                       config_keys, config_keys_num);
+       plugin_register_read("tokyotyrant", tt_read);
+       plugin_register_shutdown("tokyotyrant", tt_shutdown);
+}
+
+/* vim: set sw=8 ts=8 tw=78 : */
index a92153c..1acdaf6 100644 (file)
@@ -1,3 +1,4 @@
+absolute               count:ABSOLUTE:0:U
 apache_bytes           count:COUNTER:0:134217728
 apache_connections     count:GAUGE:0:65535
 apache_requests                count:COUNTER:0:134217728
@@ -15,6 +16,7 @@ cpufreq                       value:GAUGE:0:U
 cpu                    value:COUNTER:0:4294967295
 current                        value:GAUGE:U:U
 delay                  seconds:GAUGE:-1000000:1000000
+derive                 value:DERIVE:0:U
 df                     used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
 disk_merged            read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
 disk_octets            read:COUNTER:0:17179869183, write:COUNTER:0:17179869183
@@ -24,8 +26,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
@@ -41,10 +43,15 @@ email_count         value:GAUGE:0:U
 email_size             value:GAUGE:0:U
 entropy                        entropy:GAUGE:0:4294967295
 fanspeed               value:GAUGE:0:U
+file_size               bytes: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 +67,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 +84,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 +95,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 +109,10 @@ ps_stacksize               value:GAUGE:0:9223372036854775807
 ps_state               value:GAUGE:0:65535
 ps_vm                  value:GAUGE:0:9223372036854775807
 queue_length           value:GAUGE:0:U
+records                 count: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
index ed832fe..4971417 100644 (file)
@@ -68,9 +68,13 @@ static int parse_ds (data_source_t *dsrc, char *buf, size_t buf_len)
     dsrc->type = DS_TYPE_GAUGE;
   else if (strcasecmp (fields[1], "COUNTER") == 0)
     dsrc->type = DS_TYPE_COUNTER;
+  else if (strcasecmp (fields[1], "DERIVE") == 0)
+    dsrc->type = DS_TYPE_DERIVE;
+  else if (strcasecmp (fields[1], "ABSOLUTE") == 0)
+    dsrc->type = DS_TYPE_ABSOLUTE;
   else
   {
-    ERROR ("(fields[1] = %s) != (GAUGE || COUNTER)", fields[1]);
+    ERROR ("(fields[1] = %s) != (GAUGE || COUNTER || DERIVE || ABSOLUTE)", fields[1]);
     return (-1);
   }
 
index 60d3283..d4ffe10 100644 (file)
@@ -34,7 +34,7 @@ typedef struct cache_entry_s
        char name[6 * DATA_MAX_NAME_LEN];
        int        values_num;
        gauge_t   *values_gauge;
-       counter_t *values_counter;
+       value_t   *values_raw;
        /* Time contained in the package
         * (for calculating rates) */
        time_t last_time;
@@ -69,12 +69,12 @@ static cache_entry_t *cache_alloc (int values_num)
   memset (ce, '\0', sizeof (cache_entry_t));
   ce->values_num = values_num;
 
-  ce->values_gauge = (gauge_t *) calloc (values_num, sizeof (gauge_t));
-  ce->values_counter = (counter_t *) calloc (values_num, sizeof (counter_t));
-  if ((ce->values_gauge == NULL) || (ce->values_counter == NULL))
+  ce->values_gauge = calloc (values_num, sizeof (*ce->values_gauge));
+  ce->values_raw   = calloc (values_num, sizeof (*ce->values_raw));
+  if ((ce->values_gauge == NULL) || (ce->values_raw == NULL))
   {
     sfree (ce->values_gauge);
-    sfree (ce->values_counter);
+    sfree (ce->values_raw);
     sfree (ce);
     ERROR ("utils_cache: cache_alloc: calloc failed.");
     return (NULL);
@@ -89,7 +89,7 @@ static void cache_free (cache_entry_t *ce)
     return;
 
   sfree (ce->values_gauge);
-  sfree (ce->values_counter);
+  sfree (ce->values_raw);
   sfree (ce);
 } /* void cache_free */
 
@@ -167,6 +167,21 @@ static int uc_send_notification (const char *name)
   return (0);
 } /* int uc_send_notification */
 
+static void uc_check_range (const data_set_t *ds, cache_entry_t *ce)
+{
+  int i;
+
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (isnan (ce->values_gauge[i]))
+      continue;
+    else if (ce->values_gauge[i] < ds->ds[i].min)
+      ce->values_gauge[i] = NAN;
+    else if (ce->values_gauge[i] > ds->ds[i].max)
+      ce->values_gauge[i] = NAN;
+  }
+} /* void uc_check_range */
+
 static int uc_insert (const data_set_t *ds, const value_list_t *vl,
     const char *key)
 {
@@ -195,17 +210,42 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl,
 
   for (i = 0; i < ds->ds_num; i++)
   {
-    if (ds->ds[i].type == DS_TYPE_COUNTER)
+    switch (ds->ds[i].type)
     {
-      ce->values_gauge[i] = NAN;
-      ce->values_counter[i] = vl->values[i].counter;
-    }
-    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
-    {
-      ce->values_gauge[i] = vl->values[i].gauge;
-    }
+      case DS_TYPE_COUNTER:
+       ce->values_gauge[i] = NAN;
+       ce->values_raw[i].counter = vl->values[i].counter;
+       break;
+
+      case DS_TYPE_GAUGE:
+       ce->values_gauge[i] = vl->values[i].gauge;
+       ce->values_raw[i].gauge = vl->values[i].gauge;
+       break;
+
+      case DS_TYPE_DERIVE:
+       ce->values_gauge[i] = NAN;
+       ce->values_raw[i].derive = vl->values[i].derive;
+       break;
+
+      case DS_TYPE_ABSOLUTE:
+       ce->values_gauge[i] = NAN;
+       if (vl->interval > 0)
+         ce->values_gauge[i] = ((double) vl->values[i].absolute)
+           / ((double) vl->interval);
+       ce->values_raw[i].absolute = vl->values[i].absolute;
+       break;
+       
+      default:
+       /* This shouldn't happen. */
+       ERROR ("uc_insert: Don't know how to handle data source type %i.",
+           ds->ds[i].type);
+       return (-1);
+    } /* switch (ds->ds[i].type) */
   } /* for (i) */
 
+  /* Prune invalid gauge data */
+  uc_check_range (ds, ce);
+
   ce->last_time = vl->time;
   ce->last_update = time (NULL);
   ce->interval = vl->interval;
@@ -423,36 +463,70 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
 
   for (i = 0; i < ds->ds_num; i++)
   {
-    if (ds->ds[i].type == DS_TYPE_COUNTER)
+    switch (ds->ds[i].type)
     {
-      counter_t diff;
+      case DS_TYPE_COUNTER:
+       {
+         counter_t diff;
+
+         /* check if the counter has wrapped around */
+         if (vl->values[i].counter < ce->values_raw[i].counter)
+         {
+           if (ce->values_raw[i].counter <= 4294967295U)
+             diff = (4294967295U - ce->values_raw[i].counter)
+               + vl->values[i].counter;
+           else
+             diff = (18446744073709551615ULL - ce->values_raw[i].counter)
+               + vl->values[i].counter;
+         }
+         else /* counter has NOT wrapped around */
+         {
+           diff = vl->values[i].counter - ce->values_raw[i].counter;
+         }
+
+         ce->values_gauge[i] = ((double) diff)
+           / ((double) (vl->time - ce->last_time));
+         ce->values_raw[i].counter = vl->values[i].counter;
+       }
+       break;
 
-      /* check if the counter has wrapped around */
-      if (vl->values[i].counter < ce->values_counter[i])
-      {
-       if (ce->values_counter[i] <= 4294967295U)
-         diff = (4294967295U - ce->values_counter[i])
-           + vl->values[i].counter;
-       else
-         diff = (18446744073709551615ULL - ce->values_counter[i])
-           + vl->values[i].counter;
-      }
-      else /* counter has NOT wrapped around */
-      {
-       diff = vl->values[i].counter - ce->values_counter[i];
-      }
+      case DS_TYPE_GAUGE:
+       ce->values_raw[i].gauge = vl->values[i].gauge;
+       ce->values_gauge[i] = vl->values[i].gauge;
+       break;
+
+      case DS_TYPE_DERIVE:
+       {
+         derive_t diff;
+
+         diff = vl->values[i].derive - ce->values_raw[i].derive;
+
+         ce->values_gauge[i] = ((double) diff)
+           / ((double) (vl->time - ce->last_time));
+         ce->values_raw[i].derive = vl->values[i].derive;
+       }
+       break;
+
+      case DS_TYPE_ABSOLUTE:
+       ce->values_gauge[i] = ((double) vl->values[i].absolute)
+         / ((double) (vl->time - ce->last_time));
+       ce->values_raw[i].absolute = vl->values[i].absolute;
+       break;
+
+      default:
+       /* This shouldn't happen. */
+       pthread_mutex_unlock (&cache_lock);
+       ERROR ("uc_update: Don't know how to handle data source type %i.",
+           ds->ds[i].type);
+       return (-1);
+    } /* switch (ds->ds[i].type) */
 
-      ce->values_gauge[i] = ((double) diff)
-       / ((double) (vl->time - ce->last_time));
-      ce->values_counter[i] = vl->values[i].counter;
-    }
-    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
-    {
-      ce->values_gauge[i] = vl->values[i].gauge;
-    }
     DEBUG ("uc_update: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]);
   } /* for (i) */
 
+  /* Prune invalid gauge data */
+  uc_check_range (ds, ce);
+
   ce->last_time = vl->time;
   ce->last_update = time (NULL);
   ce->interval = vl->interval;
index 826e1b0..ec2b5f8 100644 (file)
 static int dispatch_values (const data_set_t *ds, value_list_t *vl,
                FILE *fh, char *buffer)
 {
-       char *dummy;
-       char *ptr;
-       char *saveptr;
-       int i;
-
-       char *time_str = buffer;
-       char *value_str = strchr (time_str, ':');
-       if (value_str == NULL)
-       {
-               print_to_socket (fh, "-1 No time found.\n");
-               return (-1);
-       }
-       *value_str = '\0'; value_str++;
-
-       vl->time = (time_t) atoi (time_str);
+       int status;
 
-       i = 0;
-       dummy = value_str;
-       saveptr = NULL;
-       while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
-       {
-               dummy = NULL;
-
-               if (i >= vl->values_len)
-               {
-                       i = vl->values_len + 1;
-                       break;
-               }
-
-               if ((strcmp (ptr, "U") == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
-                       vl->values[i].gauge = NAN;
-               else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i]))
-               {
-                       print_to_socket (fh, "-1 Failed to parse value `%s'.\n", ptr);
-                       return (-1);
-               }
-
-               i++;
-       } /* while (strtok_r) */
-
-       if (i != vl->values_len)
+       status = parse_values (buffer, vl, ds);
+       if (status != 0)
        {
-               char identifier[128];
-               FORMAT_VL (identifier, sizeof (identifier), vl, ds);
-               ERROR ("cmd putval: dispatch_values: "
-                               "Number of values incorrect: "
-                               "Got %i, expected %i. Identifier is `%s'.",
-                               i, vl->values_len, identifier);
-               print_to_socket (fh, "-1 Number of values incorrect: "
-                               "Got %i, expected %i.\n",
-                               i, vl->values_len);
+               print_to_socket (fh, "-1 Parsing the values string failed.\n");
                return (-1);
        }
 
index 5531b25..5f892a4 100644 (file)
@@ -207,11 +207,11 @@ static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
   vl.values_len = 1;
 
   value_str = column_values[r->legacy_position];
-  if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0]))
+  if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0].type))
   {
     ERROR ("db query utils: udb_legacy_result_handle_result: "
         "Parsing `%s' as %s failed.", value_str,
-        (r->ds->ds[0].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
+        DS_TYPE_TO_STRING (r->ds->ds[0].type));
     errno = EINVAL;
     return (-1);
   }
@@ -364,11 +364,10 @@ static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
   {
     char *value_str = r->values_buffer[i];
 
-    if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i]))
+    if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i].type))
     {
       ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
-          value_str,
-          (r->ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
+          value_str, DS_TYPE_TO_STRING (r->ds->ds[i].type));
       errno = EINVAL;
       return (-1);
     }
index c19c5ff..bdbad3f 100644 (file)
@@ -145,6 +145,59 @@ static int default_callback (const char __attribute__((unused)) *str,
 
     data->values_num++;
   }
+  else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
+  {
+    derive_t value;
+    char *endptr = NULL;
+
+    if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
+    {
+      data->value.counter++;
+      data->values_num++;
+      return (0);
+    }
+
+    if (matches_num < 2)
+      return (-1);
+
+    value = strtoll (matches[1], &endptr, 0);
+    if (matches[1] == endptr)
+      return (-1);
+
+    if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
+      data->value.derive = value;
+    else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
+      data->value.derive += value;
+    else
+    {
+      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+      return (-1);
+    }
+
+    data->values_num++;
+  }
+  else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
+  {
+    absolute_t value;
+    char *endptr = NULL;
+
+    if (matches_num < 2)
+      return (-1);
+
+    value = strtoll (matches[1], &endptr, 0);
+    if (matches[1] == endptr)
+      return (-1);
+
+    if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
+      data->value.absolute = value;
+    else
+    {
+      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+      return (-1);
+    }
+
+    data->values_num++;
+  }
   else
   {
     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
index a32a6fe..5a0c033 100644 (file)
@@ -30,6 +30,8 @@
  */
 #define UTILS_MATCH_DS_TYPE_GAUGE   0x10
 #define UTILS_MATCH_DS_TYPE_COUNTER 0x20
+#define UTILS_MATCH_DS_TYPE_DERIVE 0x30
+#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x40
 
 #define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
 #define UTILS_MATCH_CF_GAUGE_MIN     0x02
 #define UTILS_MATCH_CF_COUNTER_ADD   0x02
 #define UTILS_MATCH_CF_COUNTER_INC   0x04
 
+#define UTILS_MATCH_CF_DERIVE_SET   0x01
+#define UTILS_MATCH_CF_DERIVE_ADD   0x02
+#define UTILS_MATCH_CF_DERIVE_INC   0x04
+
+#define UTILS_MATCH_CF_ABSOLUTE_SET   0x01
+#define UTILS_MATCH_CF_ABSOLUTE_ADD   0x02
+#define UTILS_MATCH_CF_ABSOLUTE_INC   0x04
+
 /*
  * Data types
  */
index c4e9d8b..4ecec59 100644 (file)
@@ -213,6 +213,10 @@ static int ds_get (char ***ret, /* {{{ */
       type = "COUNTER";
     else if (d->type == DS_TYPE_GAUGE)
       type = "GAUGE";
+    else if (d->type == DS_TYPE_DERIVE)
+      type = "DERIVE";
+    else if (d->type == DS_TYPE_ABSOLUTE)
+      type = "ABSOLUTE";
     else
     {
       ERROR ("rrdtool plugin: Unknown DS type: %i",
@@ -225,14 +229,14 @@ static int ds_get (char ***ret, /* {{{ */
       sstrncpy (min, "U", sizeof (min));
     }
     else
-      ssnprintf (min, sizeof (min), "%lf", d->min);
+      ssnprintf (min, sizeof (min), "%f", d->min);
 
     if (isnan (d->max))
     {
       sstrncpy (max, "U", sizeof (max));
     }
     else
-      ssnprintf (max, sizeof (max), "%lf", d->max);
+      ssnprintf (max, sizeof (max), "%f", d->max);
 
     status = ssnprintf (buffer, sizeof (buffer),
         "DS:%s:%s:%i:%s:%s",
index 03a3f2d..fa8c8f9 100644 (file)
@@ -33,6 +33,7 @@
  * {{{ */
 #define UT_FLAG_INVERT  0x01
 #define UT_FLAG_PERSIST 0x02
+#define UT_FLAG_PERCENTAGE 0x04
 
 typedef struct threshold_s
 {
@@ -262,6 +263,24 @@ static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
   return (0);
 } /* int ut_config_type_persist */
 
+static int ut_config_type_percentage(threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+  {
+    WARNING ("threshold values: The `Percentage' option needs exactly one "
+       "boolean argument.");
+    return (-1);
+  }
+
+  if (ci->values[0].value.boolean)
+    th->flags |= UT_FLAG_PERCENTAGE;
+  else
+    th->flags &= ~UT_FLAG_PERCENTAGE;
+
+  return (0);
+} /* int ut_config_type_percentage */
+
 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
 {
   int i;
@@ -309,6 +328,8 @@ static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
       status = ut_config_type_invert (&th, option);
     else if (strcasecmp ("Persist", option->key) == 0)
       status = ut_config_type_persist (&th, option);
+    else if (strcasecmp ("Percentage", option->key) == 0)
+      status = ut_config_type_percentage (&th, option);
     else
     {
       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
@@ -467,7 +488,7 @@ int ut_config (const oconfig_item_t *ci)
   th.warning_max = NAN;
   th.failure_min = NAN;
   th.failure_max = NAN;
-    
+
   for (i = 0; i < ci->children_num; i++)
   {
     oconfig_item_t *option = ci->children + i;
@@ -643,30 +664,33 @@ static int ut_report_state (const data_set_t *ds,
     {
       if (!isnan (min) && !isnan (max))
       {
-       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
-           "%f. That is within the %s region of %f and %f.",
-           ds->ds[ds_index].name, values[ds_index],
-           (state == STATE_ERROR) ? "failure" : "warning",
-           min, max);
+        status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
+            "%f. That is within the %s region of %f%s and %f%s.",
+            ds->ds[ds_index].name, values[ds_index],
+            (state == STATE_ERROR) ? "failure" : "warning",
+            min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
+            max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
       }
       else
       {
        status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
-           "%f. That is %s the %s threshold of %f.",
+           "%f. That is %s the %s threshold of %f%s.",
            ds->ds[ds_index].name, values[ds_index],
            isnan (min) ? "below" : "above",
            (state == STATE_ERROR) ? "failure" : "warning",
-           isnan (min) ? max : min);
+           isnan (min) ? max : min,
+           ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
       }
     }
     else /* is not inverted */
     {
       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
-         "%f. That is %s the %s threshold of %f.",
+         "%f. That is %s the %s threshold of %f%s.",
          ds->ds[ds_index].name, values[ds_index],
          (values[ds_index] < min) ? "below" : "above",
          (state == STATE_ERROR) ? "failure" : "warning",
-         (values[ds_index] < min) ? min : max);
+         (values[ds_index] < min) ? min : max,
+         ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
     }
     buf += status;
     bufsize -= status;
@@ -699,10 +723,13 @@ static int ut_check_one_data_source (const data_set_t *ds,
   int is_failure = 0;
 
   /* check if this threshold applies to this data source */
-  ds_name = ds->ds[ds_index].name;
-  if ((th->data_source[0] != 0)
-      && (strcmp (ds_name, th->data_source) != 0))
-    return (STATE_OKAY);
+  if (ds != NULL)
+  {
+    ds_name = ds->ds[ds_index].name;
+    if ((th->data_source[0] != 0)
+       && (strcmp (ds_name, th->data_source) != 0))
+      return (STATE_OKAY);
+  }
 
   if ((th->flags & UT_FLAG_INVERT) != 0)
   {
@@ -742,12 +769,49 @@ static int ut_check_one_threshold (const data_set_t *ds,
   int ret = -1;
   int ds_index = -1;
   int i;
+  gauge_t values_copy[ds->ds_num];
+
+  memcpy (values_copy, values, sizeof (values_copy));
+
+  if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
+  {
+    int num = 0;
+    gauge_t sum=0.0;
+
+    if (ds->ds_num == 1)
+    {
+      WARNING ("ut_check_one_threshold: The %s type has only one data "
+          "source, but you have configured to check this as a percentage. "
+          "That doesn't make much sense, because the percentage will always "
+          "be 100%%!", ds->type);
+    }
+
+    /* Prepare `sum' and `num'. */
+    for (i = 0; i < ds->ds_num; i++)
+      if (!isnan (values[i]))
+      {
+        num++;
+       sum += values[i];
+      }
+
+    if ((num == 0) /* All data sources are undefined. */
+        || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
+    {
+      for (i = 0; i < ds->ds_num; i++)
+        values_copy[i] = NAN;
+    }
+    else /* We can actually calculate the percentage. */
+    {
+      for (i = 0; i < ds->ds_num; i++)
+        values_copy[i] = 100.0 * values[i] / sum;
+    }
+  } /* if (UT_FLAG_PERCENTAGE) */
 
   for (i = 0; i < ds->ds_num; i++)
   {
     int status;
 
-    status = ut_check_one_data_source (ds, vl, th, values, i);
+    status = ut_check_one_data_source (ds, vl, th, values_copy, i);
     if (ret < status)
     {
       ret = status;
@@ -899,4 +963,4 @@ int ut_check_interesting (const char *name)
   return (2);
 } /* }}} int ut_check_interesting */
 
-/* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */
+/* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */