Code

Merge branch 'ff/statsd'
authorFlorian Forster <octo@collectd.org>
Wed, 7 Aug 2013 06:39:28 +0000 (08:39 +0200)
committerFlorian Forster <octo@collectd.org>
Wed, 7 Aug 2013 06:39:28 +0000 (08:39 +0200)
44 files changed:
AUTHORS
README
configure.in
contrib/collectd_network.py
contrib/postgresql/collectd_insert.sql
src/Makefile.am
src/collectd-email.pod
src/collectd-exec.pod
src/collectd-java.pod
src/collectd-nagios.pod
src/collectd-perl.pod
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd-tg.pod
src/collectd-threshold.pod
src/collectd-unixsock.pod
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.pod
src/collectdctl.pod
src/collectdmon.pod
src/common.c
src/configfile.c
src/configfile.h
src/csv.c
src/df.c
src/dns.c
src/memcached.c
src/modbus.c
src/mysql.c
src/nginx.c
src/plugin.c
src/rrdcached.c
src/rrdtool.c
src/sigrok.c [new file with mode: 0644]
src/snmp.c
src/tail.c
src/threshold.c
src/types.db
src/types.db.pod
src/utils_mount.h
src/varnish.c
src/write_graphite.c
src/write_riemann.c

diff --git a/AUTHORS b/AUTHORS
index 36f70e788908e8963bbb69b40c7d48280312ef31..adb74813b3559cfbf3f677a05fe77ef195432ff2 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -94,6 +94,9 @@ Franck Lombardi
 Jason Pepas <cell at ices.utexas.edu>
  - nfs plugin.
 
+J. Javier Maestro <jjmaestro at ieee.org>
+ - Write-Graphite plugin: UDP support and LogSendErrors flag.
+
 Jérôme Renard <jerome.renard at gmail.com>
  - varnish plugin.
 
diff --git a/README b/README
index 04e87abfb5edc5a15b5e311c8d8d53ab72028149..3f65963ae353a7e4dc3294acb27801025f895f94 100644 (file)
--- a/README
+++ b/README
@@ -387,7 +387,9 @@ Features
       done.
 
     - write_graphite
-      Sends data to Carbon, the storage layer of Graphite.
+      Sends data to Carbon, the storage layer of Graphite using TCP or UDP. It
+      can be configured to avoid logging send errors (especially useful when
+      using UDP).
 
     - write_http
       Sends the values collected by collectd to a web-server using HTTP POST
index 3701d8ade781a62e988796d38bf9c81b3f1eeab4..dbdc0589a0fee67ca9566f43c07ecb40012c3ae2 100644 (file)
@@ -3767,6 +3767,74 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes")
 # }}}
 
+# --with-libsigrok {{{
+with_libsigrok_cflags=""
+with_libsigrok_ldflags=""
+AC_ARG_WITH(libsigrok, [AS_HELP_STRING([--with-libsigrok@<:@=PREFIX@:>@], [Path to libsigrok.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libsigrok="no"
+       else
+               with_libsigrok="yes"
+               if test "x$withval" != "xyes"
+               then
+                       with_libsigrok_cflags="-I$withval/include"
+                       with_libsigrok_ldflags="-L$withval/lib"
+               fi
+       fi
+],[])
+
+# libsigrok has a glib dependency
+if test "x$with_libsigrok" = "xyes"
+then
+       if test -z "m4_ifdef([AM_PATH_GLIB_2_0], [yes], [])"
+       then
+               with_libsigrok="no (glib not available)"
+       else
+               AM_PATH_GLIB_2_0([2.28.0],
+                       [with_libsigrok_cflags="$with_libsigrok_cflags $GLIB_CFLAGS"; with_libsigrok_ldflags="$with_libsigrok_ldflags $GLIB_LIBS"])
+       fi
+fi
+
+# libsigrok headers
+if test "x$with_libsigrok" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libsigrok_cflags"
+
+       AC_CHECK_HEADERS(libsigrok/libsigrok.h, [], [with_libsigrok="no (libsigrok/libsigrok.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+# libsigrok library
+if test "x$with_libsigrok" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libsigrok_cflags"
+       LDFLAGS="$LDFLAGS $with_libsigrok_ldflags"
+
+       AC_CHECK_LIB(sigrok, sr_init,
+       [
+               AC_DEFINE(HAVE_LIBSIGROK, 1, [Define to 1 if you have the sigrok library (-lsigrok).])
+       ],
+       [with_libsigrok="no (libsigrok not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libsigrok" = "xyes"
+then
+       BUILD_WITH_LIBSIGROK_CFLAGS="$with_libsigrok_cflags"
+       BUILD_WITH_LIBSIGROK_LDFLAGS="$with_libsigrok_ldflags"
+       AC_SUBST(BUILD_WITH_LIBSIGROK_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSIGROK_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBSIGROK, test "x$with_libsigrok" = "xyes")
+# }}}
+
 # --with-libstatgrab {{{
 with_libstatgrab_cflags=""
 with_libstatgrab_ldflags=""
@@ -5098,6 +5166,7 @@ AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
 AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
 AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
 AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
+AC_PLUGIN([sigrok],      [$with_libsigrok],    [sigrok acquisition sources])
 AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
 AC_PLUGIN([statsd],      [yes],                [StatsD plugin])
 AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
@@ -5332,6 +5401,7 @@ Configuration:
     librouteros . . . . . $with_librouteros
     librrd  . . . . . . . $with_librrd
     libsensors  . . . . . $with_libsensors
+    libsigrok   . . . . . $with_libsigrok
     libstatgrab . . . . . $with_libstatgrab
     libtokyotyrant  . . . $with_libtokyotyrant
     libupsclient  . . . . $with_libupsclient
@@ -5436,6 +5506,7 @@ Configuration:
     rrdtool . . . . . . . $enable_rrdtool
     sensors . . . . . . . $enable_sensors
     serial  . . . . . . . $enable_serial
+    sigrok  . . . . . . . $enable_sigrok
     snmp  . . . . . . . . $enable_snmp
     statsd  . . . . . . . $enable_statsd
     swap  . . . . . . . . $enable_swap
index 9af0fb008a72790e48562e7e6440c3bec8c6dd2a..809f19dee65d2ec89a030a762bacb5e98041d8aa 100644 (file)
@@ -17,10 +17,15 @@ Collectd network protocol implementation.
 """
 
 import socket,struct,sys
-try:
-  from io import StringIO
-except ImportError:
-  from cStringIO import StringIO
+import platform
+if platform.python_version() < '2.8.0':
+    # Python 2.7 and below io.StringIO does not like unicode
+    from StringIO import StringIO
+else:
+    try:
+      from io import StringIO
+    except ImportError:
+      from cStringIO import StringIO
 
 from datetime import datetime
 from copy import deepcopy
@@ -76,7 +81,7 @@ def decode_network_values(ptype, plen, buf):
     assert double.size == number.size
 
     result = []
-    for dstype in buf[header.size+short.size:off]:
+    for dstype in [ord(x) for x in buf[header.size+short.size:off]]:
         if dstype == DS_TYPE_COUNTER:
             result.append((dstype, number.unpack_from(buf, off)[0]))
             off += valskip
index fb44bb4fe82bdc7f68cfc53a8263e8c260208b93..00c5519b7a211cdfde7f283aa528805020f93dc3 100644 (file)
@@ -104,7 +104,7 @@ CREATE OR REPLACE VIEW collectd
                     WHEN type_inst IS NOT NULL THEN '-'
                     ELSE ''
                 END
-                || coalesce(plugin_inst, '') AS identifier,
+                || coalesce(type_inst, '') AS identifier,
             tstamp, name, value
         FROM identifiers
             JOIN values
index 9f16fd7cb7edb7486f7d4e06965a6b98bf389c31..f35b45a35c335cc6302df127f6f87a0891c5cacd 100644 (file)
@@ -1052,6 +1052,16 @@ collectd_LDADD += "-dlopen" serial.la
 collectd_DEPENDENCIES += serial.la
 endif
 
+if BUILD_PLUGIN_SIGROK
+pkglib_LTLIBRARIES += sigrok.la
+sigrok_la_SOURCES = sigrok.c
+sigrok_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSIGROK_CFLAGS)
+sigrok_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSIGROK_LDFLAGS)
+sigrok_la_LIBADD = -lsigrok
+collectd_LDADD += "-dlopen" sigrok.la
+collectd_DEPENDENCIES += sigrok.la
+endif
+
 if BUILD_PLUGIN_SNMP
 pkglib_LTLIBRARIES += snmp.la
 snmp_la_SOURCES = snmp.c
index e19d13ec21e8ddc7c087835a3aa05ee7105de711..6f58542784d51a206c685eedee1eed488cd3b101 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-email - Documentation of collectd's C<email plugin>
index 10f68290e9bbc8ae8c7479364068f0b3ccae5b30..2c5963025a4269687307cf9e5c1f3d43cae70e47 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-exec - Documentation of collectd's C<exec plugin>
@@ -116,12 +118,8 @@ case you're confused.
 
 Since examples usually let one understand a lot better, here are some:
 
-  leeloo/cpu-0/cpu-idle N:2299366
-  alice/interface/if_octets-eth0 interval=10 1180647081:421465:479194
-
-Since this action was the only one supported with older versions of the C<exec
-plugin> all lines were treated as if they were prefixed with B<PUTVAL>. This is
-still the case to maintain backwards compatibility but deprecated.
+  PUTVAL leeloo/cpu-0/cpu-idle N:2299366
+  PUTVAL alice/interface/if_octets-eth0 interval=10 1180647081:421465:479194
 
 =item B<PUTNOTIF> [I<OptionList>] B<message=>I<Message>
 
index 9e2f81aa9b9f0056d3aefa215605688b47f8ae81..00f209a56b7d69e3f35757f86876e194321dbab0 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-java - Documentation of collectd's "java plugin"
index d7c749cd3391aefc86369c208193063111655fe6..aab753c56539d8c2a9bcb5b2196a9b8ae6c5dfd7 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-nagios - Nagios plugin for querying collectd
index ee05e002d4fea57996ed5cf6543c38d166a35c13..669e3f415ef26ef6b196d4e2a15d22127fdf8c78 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-perl - Documentation of collectd's C<perl plugin>
index 39503ad0ba15dfb89ceaf66606ab3afeca442328..426a7118c99b8346d38e1d659ff2f4142090c594 100644 (file)
@@ -8,6 +8,8 @@
 # The above copyright notice and this permission notice shall be included in
 # all copies or substantial portions of the Software.
 
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-python - Documentation of collectd's C<python plugin>
index 3c6e79970dacff29c556c423899b480ca1d85bf8..fd7a50896486211c3fac9b0ef6d10304738dcb4c 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-snmp - Documentation of collectd's C<snmp plugin>
index 5f1b630d53704993486ea25ffbe547bac6cbcb35..97112e7b6bae1e7b15475f803228ed69004632b3 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-tg - Traffic generator for collectd.
index 02e41b8b83e90cc34d60faa263f558f7a54dbc89..35f8a9fcdd231a9ed4f5e8ca4086305016b83845 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-threshold - Documentation of collectd's I<Threshold plugin>
@@ -184,10 +186,12 @@ state will be keep.
 
 =item B<Interesting> B<true>|B<false>
 
-If set to B<true> (the default), the threshold must be treated as interesting
-and, when a number of B<Timeout> values will lost, then a missing notification
-will be dispatched. On the other hand, if set to B<false>, the missing
-notification will never dispatched for this threshold.
+If set to B<true> (the default), a notification with severity C<FAILURE> will
+be created when a matching value list is no longer updated and purged from the
+internal cache. When this happens depends on the I<interval> of the value list
+and the global B<Timeout> setting. See the B<Interval> and B<Timeout> settings
+in L<collectd.conf(5)> for details. If set to B<false>, this event will be
+ignored.
 
 =back
 
index 83802a14f7a0ba2576c94f0b223b1ddc07b54f67..40626d3739db266d3f10043485cbf45fd1e7b27f 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-unixsock - Documentation of collectd's C<unixsock plugin>
index c5ab3ce83a31ab4e063b3ff63fcaf6ccc1dea409..e8d4d28ef026c1c09546cdc0d143de52a19bbed5 100644 (file)
 #ReadThreads  5
 #WriteThreads 5
 
+# Limit the size of the write queue. Default is no limit. Setting up a limit is
+# recommended for servers handling a high volume of traffic.
+#WriteQueueLimitHigh 1000000
+#WriteQueueLimitLow   800000
+
 ##############################################################################
 # Logging                                                                    #
 #----------------------------------------------------------------------------#
 @LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool
 #@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors
 #@BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial
+#@BUILD_PLUGIN_SIGROK_TRUE@LoadPlugin sigrok
 #@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp
 #@BUILD_PLUGIN_STATSD_TRUE@LoadPlugin statsd
 #@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
 #      IgnoreSelected false
 #</Plugin>
 
+#<Plugin sigrok>
+#  LogLevel 3
+#  <Device "AC Voltage">
+#    Driver "fluke-dmm"
+#    MinimumInterval 10
+#    Conn "/dev/ttyUSB2"
+#  </Device>
+#  <Device "Sound Level">
+#    Driver "cem-dt-885x"
+#    Conn "/dev/ttyUSB1"
+#  </Device>
+#</Plugin>
+
 #<Plugin snmp>
 #   <Data "powerplus_voltge_input">
 #       Type "voltage"
 #  <Node "example">
 #    Host "localhost"
 #    Port "2003"
+#    Protocol "udp"
+#    LogSendErrors true
 #    Prefix "collectd"
 #    Postfix "collectd"
 #    StoreRates true
index 4a34fe5df3780ad76bb945be0a0ed1c926a883e7..1b73c417d9ebfc54211eefc252116d733a0b9c96 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd.conf - Configuration for the system statistics collection daemon B<collectd>
@@ -148,15 +150,29 @@ use statements like the following:
 
   Include "/etc/collectd.d/*.conf"
 
+Starting with version 5.3, this may also be a block in which further options
+affecting the behavior of B<Include> may be specified. The following option is
+currently allowed:
+
+  <Include "/etc/collectd.d">
+    Filter "*.conf"
+  </Include>
+
+=over 4
+
+=item B<Filter> I<pattern>
+
 If the C<fnmatch> function is available on your system, a shell-like wildcard
 I<pattern> may be specified to filter which files to include. This may be used
 in combination with recursively including a directory to easily be able to
 arbitrarily mix configuration files and other documents (e.g. README files).
-The following statement is similar to the example above but includes all files
+The given example is similar to the first example above but includes all files
 matching C<*.conf> in any subdirectory of C</etc/collectd.d>:
 
   Include "/etc/collectd.d" "*.conf"
 
+=back
+
 If more than one files are included by a single B<Include> option, the files
 will be included in lexicographical order (as defined by the C<strcmp>
 function). Thus, you can e.E<nbsp>g. use numbered prefixes to specify the
@@ -219,6 +235,36 @@ Number of threads to start for dispatching value lists to write plugins. The
 default value is B<5>, but you may want to increase this if you have more than
 five plugins that may take relatively long to write to.
 
+=item B<WriteQueueLimitHigh> I<HighNum>
+
+=item B<WriteQueueLimitLow> I<LowNum>
+
+Metrics are read by the I<read threads> and then put into a queue to be handled
+by the I<write threads>. If one of the I<write plugins> is slow (e.g. network
+timeouts, I/O saturation of the disk) this queue will grow. In order to avoid
+running into memory issues in such a case, you can limit the size of this
+queue.
+
+By default, there is no limit and memory may grow indefinitely. This is most
+likely not an issue for clients, i.e. instances that only handle the local
+metrics. For servers it is recommended to set this to a non-zero value, though.
+
+You can set the limits using B<WriteQueueLimitHigh> and B<WriteQueueLimitLow>.
+Each of them takes a numerical argument which is the number of metrics in the
+queue. If there are I<HighNum> metrics in the queue, any new metrics I<will> be
+dropped. If there are less than I<LowNum> metrics in the queue, all new metrics
+I<will> be enqueued. If the number of metrics currently in the queue is between
+I<LowNum> and I<HighNum>, the metric is dropped with a probability that is
+proportional to the number of metrics in the queue (i.e. it increases linearly
+until it reaches 100%.)
+
+If B<WriteQueueLimitHigh> is set to non-zero and B<WriteQueueLimitLow> is
+unset, the latter will default to half of B<WriteQueueLimitHigh>.
+
+If you do not want to randomly drop values when the queue size is between
+I<LowNum> and I<HighNum>, set If B<WriteQueueLimitHigh> and
+B<WriteQueueLimitLow> to same value.
+
 =item B<Hostname> I<Name>
 
 Sets the hostname that identifies a host. If you omit this setting, the
@@ -1511,14 +1557,19 @@ Enable this option if inodes are a scarce resource for you, usually because
 many small files are stored on the disk. This is a usual scenario for mail
 transfer agents and web caches.
 
-=item B<ReportPercentage> B<false>|B<true>
+=item B<ValuesAbsolute> B<true>|B<false>
 
-Enables or disables reporting of disk space and inodes as a percentage.
-Defaults to B<false>.
+Enables or disables reporting of free, used and used disk space in 1K-blocks. 
+Defaults to true.
 
-This is useful for deploying I<collectd> on the cloud, where machines with
-different disk size may exist. Then it is more practical to configure
-thresholds based on relative disk size.
+=item B<ValuesPercentage> B<true>|B<false>
+
+Enables or disables reporting of free, used and used disk space in percentage.
+Defaults to false.
+
+This is useful for deploying collectd on the cloud, where machines with
+different disk size may exist. Then it is more practical to configure thresholds
+based on relative disk size.
 
 =back
 
@@ -5097,6 +5148,74 @@ and all other sensors are collected.
 
 =back
 
+=head2 Plugin C<sigrok>
+
+The I<sigrok plugin> uses I<libsigrok> to retrieve measurements from any device
+supported by the L<sigrok|http://sigrok.org/> project.
+
+B<Synopsis>
+
+ <Plugin sigrok>
+   LogLevel 3
+   <Device "AC Voltage">
+      Driver "fluke-dmm"
+      MinimumInterval 10
+      Conn "/dev/ttyUSB2"
+   </Device>
+   <Device "Sound Level">
+      Driver "cem-dt-885x"
+      Conn "/dev/ttyUSB1"
+   </Device>
+ </Plugin>
+
+=over 4
+
+=item B<LogLevel> B<0-5>
+
+The I<sigrok> logging level to pass on to the I<collectd> log, as a number
+between B<0> and B<5> (inclusive). These levels correspond to C<None>,
+C<Errors>, C<Warnings>, C<Informational>, C<Debug >and C<Spew>, respectively.
+The default is B<2> (C<Warnings>). The I<sigrok> log messages, regardless of
+their level, are always submitted to I<collectd> at its INFO log level.
+
+=item E<lt>B<Device> I<Name>E<gt>
+
+A sigrok-supported device, uniquely identified by this section's options. The
+I<Name> is passed to I<collectd> as the I<plugin instance>.
+
+=item B<Driver> I<DriverName>
+
+The sigrok driver to use for this device.
+
+=item B<Conn> I<ConnectionSpec>
+
+If the device cannot be auto-discovered, or more than one might be discovered
+by the driver, I<ConnectionSpec> specifies the connection string to the device.
+It can be of the form of a device path (e.g.E<nbsp>C</dev/ttyUSB2>), or, in
+case of a non-serial USB-connected device, the USB I<VendorID>B<.>I<ProductID>
+separated by a period (e.g.E<nbsp>C<0403.6001>). A USB device can also be
+specified as I<Bus>B<.>I<Address> (e.g.E<nbsp>C<1.41>).
+
+=item B<SerialComm> I<SerialSpec>
+
+For serial devices with non-standard port settings, this option can be used
+to specify them in a form understood by I<sigrok>, e.g.E<nbsp>C<9600/8n1>.
+This should not be necessary; drivers know how to communicate with devices they
+support.
+
+=item B<MinimumInterval> I<Seconds>
+
+Specifies the minimum time between measurement dispatches to I<collectd>, in
+seconds. Since some I<sigrok> supported devices can acquire measurements many
+times per second, it may be necessary to throttle these. For example, the
+I<RRD plugin> cannot process writes more than once per second.
+
+The default B<MinimumInterval> is B<0>, meaning measurements received from the
+device are always dispatched to I<collectd>. When throttled, unused
+measurements are discarded.
+
+=back
+
 =head2 Plugin C<snmp>
 
 Since the configuration of the C<snmp plugin> is a little more complicated than
@@ -5745,7 +5864,33 @@ Take the UUID from the given file (default I</etc/uuid>).
 
 =head2 Plugin C<varnish>
 
-The Varnish plugin collects information about Varnish, an HTTP accelerator.
+The I<varnish plugin> collects information about Varnish, an HTTP accelerator.
+
+Synopsis:
+
+ <Plugin "varnish">
+   <Instance "example">
+     CollectCache       true
+     CollectConnections true
+     CollectBackend     true
+     CollectSHM         true
+     CollectESI         false
+     CollectFetch       false
+     CollectHCB         false
+     CollectSMA         false
+     CollectSMS         false
+     CollectSM          false
+     CollectTotals      false
+     CollectWorkers     false
+   </Instance>
+ </Plugin>
+
+The configuration consists of one or more E<lt>B<Instance>E<nbsp>I<Name>E<gt>
+blocks. I<Name> is the parameter passed to "varnishd -n". If left empty, it
+will collectd statistics from the default "varnishd" instance (this should work
+fine in most cases).
+
+Inside each E<lt>B<Instance>E<gt> blocks, the following options are recognized:
 
 =over 4
 
@@ -5886,9 +6031,9 @@ iptables to feed data for the guest IPs into the iptables plugin.
 
 The C<write_graphite> plugin writes data to I<Graphite>, an open-source metrics
 storage and graphing project. The plugin connects to I<Carbon>, the data layer
-of I<Graphite>, and sends data via the "line based" protocol (per default using
-portE<nbsp>2003). The data will be sent in blocks of at most 1428 bytes to
-minimize the number of network packets.
+of I<Graphite>, via I<TCP> or I<UDP> and sends data via the "line based"
+protocol (per default using portE<nbsp>2003). The data will be sent in blocks
+of at most 1428 bytes to minimize the number of network packets.
 
 Synopsis:
 
@@ -5896,6 +6041,8 @@ Synopsis:
    <Node "example">
      Host "localhost"
      Port "2003"
+     Protocol "udp"
+     LogSendErrors true
      Prefix "collectd"
    </Node>
  </Plugin>
@@ -5913,6 +6060,17 @@ Hostname or address to connect to. Defaults to C<localhost>.
 
 Service name or port number to connect to. Defaults to C<2003>.
 
+=item B<Protocol> I<String>
+
+Protocol to use when connecting to I<Graphite>. Defaults to C<tcp>.
+
+=item B<LogSendErrors> B<false>|B<true>
+
+If set to B<true> (the default), logs errors when sending data to I<Graphite>.
+If set to B<false>, it will not log the errors. This is especially useful when
+using Protocol UDP since many times we want to use the "fire-and-forget"
+approach and logging errors fills syslog with unneeded messages.
+
 =item B<Prefix> I<String>
 
 When set, I<String> is added in front of the host name. Dots and whitespace are
index e36dcdf3eb0c9ebe031970da77cf86d777a36059..2f7b9f707439c795fcf85acfaede29ec39821a2e 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd - System statistics collection daemon
index 21c0b500c37d36c55b7d90a97674206a8065f876..52b8ef23d625a956bc11ce7dd7e256a9ac1a6a91 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectdctl - Control interface for collectd
index 73ba6b8c166b98b22c4b2204739dd1d4167d1f3d..3151e1104c9e73b6f251de329a93cb258480cd4c 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectdmon - Monitoring daemon for collectd
index d963efa556a6f60ce6f71c3472f90e791dc48c4e..161b4d67fc063d693b1f319e016687cea4ae639d 100644 (file)
@@ -721,7 +721,7 @@ long long get_kstat_value (kstat_t *ksp, char *name)
                retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
        else
                WARNING ("get_kstat_value: Not a numeric value: %s", name);
-                
+
        return (retval);
 }
 #endif /* HAVE_LIBKSTAT */
@@ -828,36 +828,43 @@ int format_name (char *ret, int ret_len,
                const char *plugin, const char *plugin_instance,
                const char *type, const char *type_instance)
 {
-       int  status;
+  char *buffer;
+  size_t buffer_size;
+
+  buffer = ret;
+  buffer_size = (size_t) ret_len;
+
+#define APPEND(str) do {                                               \
+  size_t l = strlen (str);                                             \
+  if (l >= buffer_size)                                                \
+    return (ENOBUFS);                                                  \
+  memcpy (buffer, (str), l);                                           \
+  buffer += l; buffer_size -= l;                                       \
+} while (0)
 
-       assert (plugin != NULL);
-       assert (type != NULL);
+  assert (plugin != NULL);
+  assert (type != NULL);
 
-       if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s",
-                                       hostname, plugin, type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s-%s",
-                                       hostname, plugin, type,
-                                       type_instance);
-       }
-       else
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s",
-                                       hostname, plugin, plugin_instance,
-                                       type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s-%s",
-                                       hostname, plugin, plugin_instance,
-                                       type, type_instance);
-       }
+  APPEND (hostname);
+  APPEND ("/");
+  APPEND (plugin);
+  if ((plugin_instance != NULL) && (plugin_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (plugin_instance);
+  }
+  APPEND ("/");
+  APPEND (type);
+  if ((type_instance != NULL) && (type_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (type_instance);
+  }
+  assert (buffer_size > 0);
+  buffer[0] = 0;
 
-       if ((status < 1) || (status >= ret_len))
-               return (-1);
-       return (0);
+#undef APPEND
+  return (0);
 } /* int format_name */
 
 int format_values (char *ret, size_t ret_len, /* {{{ */
index 876ee23ee71c8c33d2f230170bf94147a6012404..0e54f267f9fdd08c15d4d4d11a427f409d708723 100644 (file)
@@ -109,6 +109,8 @@ static cf_global_option_t cf_global_options[] =
        {"Interval",    NULL, NULL},
        {"ReadThreads", NULL, "5"},
        {"WriteThreads", NULL, "5"},
+       {"WriteQueueLimitHigh", NULL, NULL},
+       {"WriteQueueLimitLow", NULL, NULL},
        {"Timeout",     NULL, "2"},
        {"AutoLoadPlugin", NULL, "false"},
        {"PreCacheChain",  NULL, "PreCache"},
@@ -586,7 +588,7 @@ static int cf_include_all (oconfig_item_t *root, int depth)
                sfree (pattern);
 
                if (new == NULL)
-                       continue;
+                       return (-1);
 
                /* Now replace the i'th child in `root' with `new'. */
                cf_ci_replace_child (root, new, i);
@@ -605,6 +607,7 @@ static oconfig_item_t *cf_read_file (const char *file,
                const char *pattern, int depth)
 {
        oconfig_item_t *root;
+       int status;
 
        assert (depth < CF_MAX_DEPTH);
 
@@ -636,7 +639,12 @@ static oconfig_item_t *cf_read_file (const char *file,
                return (NULL);
        }
 
-       cf_include_all (root, depth);
+       status = cf_include_all (root, depth);
+       if (status != 0)
+       {
+               oconfig_free (root);
+               return (NULL);
+       }
 
        return (root);
 } /* oconfig_item_t *cf_read_file */
@@ -829,12 +837,6 @@ static oconfig_item_t *cf_read_generic (const char *path,
 
        wordfree (&we);
 
-       if (root->children == NULL)
-       {
-               oconfig_free (root);
-               return (NULL);
-       }
-
        return (root);
 } /* oconfig_item_t *cf_read_generic */
 /* #endif HAVE_WORDEXP_H */
@@ -915,6 +917,23 @@ const char *global_option_get (const char *option)
                        : cf_global_options[i].def);
 } /* char *global_option_get */
 
+long global_option_get_long (const char *option, long default_value)
+{
+               const char *str;
+               long value;
+
+               str = global_option_get (option);
+               if (NULL == str)
+                       return (default_value);
+
+               errno = 0;
+               value = strtol (str, /* endptr = */ NULL, /* base = */ 0);
+               if (errno != 0)
+                       return (default_value);
+
+               return (value);
+} /* char *global_option_get_long */
+
 cdtime_t cf_get_default_interval (void)
 {
   char const *str = global_option_get ("Interval");
@@ -1046,6 +1065,12 @@ int cf_read (char *filename)
                ERROR ("Unable to read config file %s.", filename);
                return (-1);
        }
+       else if (conf->children_num == 0)
+       {
+               ERROR ("Configuration file %s is empty.", filename);
+               oconfig_free (conf);
+               return (-1);
+       }
 
        for (i = 0; i < conf->children_num; i++)
        {
index 5a719a421a66a25df0073d6bdc4f24e8cbe9ebac..c91fcd5f4abb0bf87bf2339ae4524ac8e3b1de99 100644 (file)
@@ -86,6 +86,8 @@ int cf_read (char *filename);
 
 int global_option_set (const char *option, const char *value);
 const char *global_option_get (const char *option);
+long global_option_get_long (const char *option, long default_value);
+long global_option_get_long_in_range (const char *option, long default_value, long min, long max);
 
 cdtime_t cf_get_default_interval (void);
 
index 02d62c1e014e89f98becd7993ea14a646287b31b..afb4f8338c5d7e2611e489338eda5a642f3c56b8 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -121,65 +121,49 @@ static int value_list_to_string (char *buffer, int buffer_len,
        return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-               const data_set_t *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+               value_list_t const *vl)
 {
-       int offset = 0;
        int status;
 
-       assert (0 == strcmp (ds->type, vl->type));
-
-       if (datadir != NULL)
-       {
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", datadir);
-               if ((status < 1) || (status >= buffer_len - offset))
-                       return (-1);
-               offset += status;
-       }
+       char *ptr;
+       size_t ptr_size;
+       time_t now;
+       struct tm struct_tm;
 
-       status = ssnprintf (buffer + offset, buffer_len - offset,
-                       "%s/", vl->host);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       status = FORMAT_VL (buffer, buffer_size, vl);
+       if (status != 0)
+               return (status);
 
-       if (strlen (vl->plugin_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s/", vl->plugin, vl->plugin_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", vl->plugin);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       /* Skip all the time formatting stuff when printing to STDOUT or
+        * STDERR. */
+       if (use_stdio)
+               return (0);
 
-       if (strlen (vl->type_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s", vl->type, vl->type_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s", vl->type);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       ptr_size = buffer_size - strlen (buffer);
+       ptr = buffer + strlen (buffer);
 
-       if (!use_stdio)
+       /* "-2013-07-12" => 11 bytes */
+       if (ptr_size < 12)
        {
-               time_t now;
-               struct tm stm;
+               ERROR ("csv plugin: Buffer too small.");
+               return (ENOMEM);
+       }
 
-               /* TODO: Find a way to minimize the calls to `localtime_r',
-                * since they are pretty expensive.. */
-               now = time (NULL);
-               if (localtime_r (&now, &stm) == NULL)
-               {
-                       ERROR ("csv plugin: localtime_r failed");
-                       return (1);
-               }
+       /* TODO: Find a way to minimize the calls to `localtime_r',
+        * since they are pretty expensive.. */
+       now = time (NULL);
+       if (localtime_r (&now, &struct_tm) == NULL)
+       {
+               ERROR ("csv plugin: localtime_r failed");
+               return (-1);
+       }
 
-               strftime (buffer + offset, buffer_len - offset,
-                               "-%Y-%m-%d", &stm);
+       status = strftime (ptr, ptr_size, "-%Y-%m-%d", &struct_tm);
+       if (status == 0) /* yep, it returns zero on error. */
+       {
+               ERROR ("csv plugin: strftime failed");
+               return (-1);
        }
 
        return (0);
@@ -275,7 +259,8 @@ static int csv_write (const data_set_t *ds, const value_list_t *vl,
                return -1;
        }
 
-       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+       status = value_list_to_filename (filename, sizeof (filename), vl);
+       if (status != 0)
                return (-1);
 
        DEBUG ("csv plugin: csv_write: filename = %s;", filename);
index 5ff3f59be8cbcb401dd288c17ec4b89777251b43..3d5a402c7949b8741b7568b78d987c1fe033dc6a 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -55,7 +55,8 @@ static const char *config_keys[] =
        "ReportByDevice",
        "ReportReserved",
        "ReportInodes",
-       "ReportPercentage"
+       "ValuesAbsolute",
+       "ValuesPercentage"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -65,7 +66,8 @@ static ignorelist_t *il_fstype = NULL;
 
 static _Bool by_device = 0;
 static _Bool report_inodes = 0;
-static _Bool report_percentage = 0;
+static _Bool values_absolute = 1;
+static _Bool values_percentage = 0;
 
 static int df_init (void)
 {
@@ -133,13 +135,21 @@ static int df_config (const char *key, const char *value)
 
                return (0);
        }
+       else if (strcasecmp (key, "ValuesAbsolute") == 0)
+       {
+               if (IS_TRUE (value))
+                       values_absolute = 1;
+               else
+                       values_absolute = 0;
 
-       else if (strcasecmp (key, "ReportPercentage") == 0)
+               return (0);
+       }
+       else if (strcasecmp (key, "ValuesPercentage") == 0)
        {
                if (IS_TRUE (value))
-                       report_percentage = 1;
+                       values_percentage = 1;
                else
-                       report_percentage = 0;
+                       values_percentage = 0;
 
                return (0);
        }
@@ -231,7 +241,7 @@ static int df_read (void)
 
                        if (strlen(disk_name) < 1)
                        {
-                               DEBUG("df: no device name name for mountpoint %s, skipping", mnt_ptr->dir);
+                               DEBUG("df: no device name for mountpoint %s, skipping", mnt_ptr->dir);
                                continue;
                        }
                }
@@ -285,22 +295,7 @@ static int df_read (void)
                blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail);
                blk_used     = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree);
 
-               if (report_percentage && (statbuf.f_blocks > 0))
-               {
-                       uint64_t blk_total = (uint64_t) statbuf.f_blocks;
-                       char plugin_instance[DATA_MAX_NAME_LEN];
-
-                       ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                       "%s-bytes", disk_name);
-
-                       df_submit_one (plugin_instance, "percent", "free",
-                                       100.0 * ((gauge_t) blk_free) / ((gauge_t) blk_total));
-                       df_submit_one (plugin_instance, "percent", "reserved",
-                                       100.0 * ((gauge_t) blk_reserved) / ((gauge_t) blk_total));
-                       df_submit_one (plugin_instance, "percent", "used",
-                                       100.0 * ((gauge_t) blk_used) / ((gauge_t) blk_total));
-               }
-               else if (!report_percentage)
+               if (values_absolute)
                {
                        df_submit_one (disk_name, "df_complex", "free",
                                (gauge_t) (blk_free * blocksize));
@@ -310,6 +305,20 @@ static int df_read (void)
                                (gauge_t) (blk_used * blocksize));
                }
 
+               if (values_percentage)
+               {
+                       if (statbuf.f_blocks > 0)
+                               {
+                               df_submit_one (disk_name, "percent_bytes", "free",
+                                       (gauge_t) ((float_t)(blk_free) / statbuf.f_blocks * 100));
+                               df_submit_one (disk_name, "percent_bytes", "reserved",
+                                       (gauge_t) ((float_t)(blk_reserved) / statbuf.f_blocks * 100));
+                               df_submit_one (disk_name, "percent_bytes", "used",
+                                       (gauge_t) ((float_t)(blk_used) / statbuf.f_blocks * 100));
+                               }
+                       else return (-1);
+               }
+
                /* inode handling */
                if (report_inodes)
                {
@@ -327,22 +336,20 @@ static int df_read (void)
                        inode_reserved = (uint64_t) (statbuf.f_ffree - statbuf.f_favail);
                        inode_used = (uint64_t) (statbuf.f_files - statbuf.f_ffree);
 
-                       if (report_percentage && (statbuf.f_files > 0))
+                       if (values_percentage)
                        {
-                               uint64_t inode_total = (uint64_t) statbuf.f_files;
-                               char plugin_instance[DATA_MAX_NAME_LEN];
-
-                               ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                               "%s-inodes", disk_name);
-
-                               df_submit_one (plugin_instance, "percent", "free",
-                                               100.0 * ((gauge_t) inode_free) / ((gauge_t) inode_total));
-                               df_submit_one (plugin_instance, "percent", "reserved",
-                                               100.0 * ((gauge_t) inode_reserved) / ((gauge_t) inode_total));
-                               df_submit_one (plugin_instance, "percent", "used",
-                                               100.0 * ((gauge_t) inode_used) / ((gauge_t) inode_total));
+                               if (statbuf.f_files > 0)
+                               {
+                                       df_submit_one (disk_name, "percent_inodes", "free",
+                                               (gauge_t) ((float_t)(inode_free) / statbuf.f_files * 100));
+                                       df_submit_one (disk_name, "percent_inodes", "reserved",
+                                               (gauge_t) ((float_t)(inode_reserved) / statbuf.f_files * 100));
+                                       df_submit_one (disk_name, "percent_inodes", "used",
+                                               (gauge_t) ((float_t)(inode_used) / statbuf.f_files * 100));
+                               }
+                               else return (-1);
                        }
-                       else if (!report_percentage)
+                       if (values_absolute)
                        {
                                df_submit_one (disk_name, "df_inodes", "free",
                                                (gauge_t) inode_free);
index fe3b672a21d844b2190c80e7b2f7bffd9adb9b50..00fcff191e0ccf0177ec7bea309eb2964b331228 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -208,7 +208,7 @@ static void dns_child_callback (const rfc1035_header_t *dns)
        pthread_mutex_unlock (&opcode_mutex);
 }
 
-static void *dns_child_loop (__attribute__((unused)) void *dummy)
+static int dns_run_pcap_loop (void)
 {
        pcap_t *pcap_obj;
        char    pcap_error[PCAP_ERRBUF_SIZE];
@@ -236,19 +236,24 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
                                "failed: %s",
                                (pcap_device != NULL) ? pcap_device : "any",
                                pcap_error);
-               return (NULL);
+               return (PCAP_ERROR);
        }
 
        memset (&fp, 0, sizeof (fp));
-       if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
+       status = pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0);
+       if (status < 0)
        {
-               ERROR ("dns plugin: pcap_compile failed");
-               return (NULL);
+               ERROR ("dns plugin: pcap_compile failed: %s",
+                               pcap_statustostr (status));
+               return (status);
        }
-       if (pcap_setfilter (pcap_obj, &fp) < 0)
+
+       status = pcap_setfilter (pcap_obj, &fp);
+       if (status < 0)
        {
-               ERROR ("dns plugin: pcap_setfilter failed");
-               return (NULL);
+               ERROR ("dns plugin: pcap_setfilter failed: %s",
+                               pcap_statustostr (status));
+               return (status);
        }
 
        DEBUG ("dns plugin: PCAP object created.");
@@ -259,19 +264,65 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
        status = pcap_loop (pcap_obj,
                        -1 /* loop forever */,
                        handle_pcap /* callback */,
-                       NULL /* Whatever this means.. */);
-       if (status < 0)
-               ERROR ("dns plugin: Listener thread is exiting "
-                               "abnormally: %s", pcap_geterr (pcap_obj));
-
-       DEBUG ("dns plugin: Child is exiting.");
+                       NULL /* user data */);
+       INFO ("dns plugin: pcap_loop exited with status %i.", status);
+       /* We need to handle "PCAP_ERROR" specially because libpcap currently
+        * doesn't return PCAP_ERROR_IFACE_NOT_UP for compatibility reasons. */
+       if (status == PCAP_ERROR)
+               status = PCAP_ERROR_IFACE_NOT_UP;
 
        pcap_close (pcap_obj);
-       listen_thread_init = 0;
-       pthread_exit (NULL);
+       return (status);
+} /* int dns_run_pcap_loop */
+
+static int dns_sleep_one_interval (void) /* {{{ */
+{
+       cdtime_t interval;
+       struct timespec ts = { 0, 0 };
+       int status = 0;
+
+       interval = plugin_get_interval ();
+       CDTIME_T_TO_TIMESPEC (interval, &ts);
+
+       while (42)
+       {
+               struct timespec rem = { 0, 0 };
+
+               status = nanosleep (&ts, &rem);
+               if (status == 0)
+                       break;
+               else if ((errno == EINTR) || (errno == EAGAIN))
+               {
+                       ts = rem;
+                       continue;
+               }
+               else
+                       break;
+       }
+
+       return (status);
+} /* }}} int dns_sleep_one_interval */
+
+static void *dns_child_loop (__attribute__((unused)) void *dummy) /* {{{ */
+{
+       int status;
+
+       while (42)
+       {
+               status = dns_run_pcap_loop ();
+               if (status != PCAP_ERROR_IFACE_NOT_UP)
+                       break;
 
+               dns_sleep_one_interval ();
+       }
+
+       if (status != PCAP_ERROR_BREAK)
+               ERROR ("dns plugin: PCAP returned error %s.",
+                               pcap_statustostr (status));
+
+       listen_thread_init = 0;
        return (NULL);
-} /* static void dns_child_loop (void) */
+} /* }}} void *dns_child_loop */
 
 static int dns_init (void)
 {
index a09f45ec6dae1a2e537fe986af7677f4130bd662..535ea84704b3dac328a5af495f69bccdbdfedb9a 100644 (file)
@@ -340,6 +340,10 @@ static int memcached_read (user_data_t *user_data)
   gauge_t bytes_total = NAN;
   gauge_t hits = NAN;
   gauge_t gets = NAN;
+  gauge_t incr_hits = NAN;
+  derive_t incr = 0;
+  gauge_t decr_hits = NAN;
+  derive_t decr = 0;
   derive_t rusage_user = 0;
   derive_t rusage_syst = 0;
   derive_t octets_rx = 0;
@@ -436,6 +440,36 @@ static int memcached_read (user_data_t *user_data)
         gets = atof (fields[2]);
     }
 
+    /*
+     * Increment/Decrement
+     */
+    else if (FIELD_IS("incr_misses"))
+    {
+      derive_t incr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "incr_misses", incr_count, st);
+      incr += incr_count;
+    }
+    else if (FIELD_IS ("incr_hits"))
+    {
+      derive_t incr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "incr_hits", incr_count, st);
+      incr_hits = atof (fields[2]);
+      incr += incr_count;
+    }
+    else if (FIELD_IS ("decr_misses"))
+    {
+      derive_t decr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "decr_misses", decr_count, st);
+      decr += decr_count;
+    }
+    else if (FIELD_IS ("decr_hits"))
+    {
+      derive_t decr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "decr_hits", decr_count, st);
+      decr_hits = atof (fields[2]);
+      decr += decr_count;
+    }
+
     /*
      * Operations on the cache, i. e. cache hits, cache misses and evictions of items
      */
@@ -485,6 +519,20 @@ static int memcached_read (user_data_t *user_data)
     submit_gauge ("percent", "hitratio", rate, st);
   }
 
+  if (!isnan (incr_hits) && incr != 0)
+  {
+    gauge_t incr_rate = 100.0 * incr_hits / incr;
+    submit_gauge ("percent", "incr_hitratio", incr_rate, st);
+    submit_derive ("memcached_ops", "incr", incr, st);
+  }
+
+  if (!isnan (decr_hits) && decr != 0)
+  {
+    gauge_t decr_rate = 100.0 * decr_hits / decr;
+    submit_gauge ("percent", "decr_hitratio", decr_rate, st);
+    submit_derive ("memcached_ops", "decr", decr, st);
+  }
+
   return 0;
 } /* int memcached_read */
 
index 8a9fe93fd83eaf3b6a32221ea9c92598c225802a..887c63c03235922c2f7c62eff1d7e4409eee4f80 100644 (file)
@@ -115,7 +115,6 @@ struct mb_host_s /* {{{ */
   modbus_t *connection;
 #endif
   _Bool is_connected;
-  _Bool have_reconnected;
 }; /* }}} */
 typedef struct mb_host_s mb_host_t;
 
@@ -260,6 +259,7 @@ static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
   union
   {
     uint8_t b[4];
+    uint16_t s[2];
     float f;
   } conv;
 
@@ -288,13 +288,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   if (host == NULL)
     return (EINVAL);
 
-  if (host->is_connected)
-    return (0);
-
-  /* Only reconnect once per interval. */
-  if (host->have_reconnected)
-    return (-1);
-
   modbus_set_debug (&host->connection, 1);
 
   /* We'll do the error handling ourselves. */
@@ -319,7 +312,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   }
 
   host->is_connected = 1;
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 /* #endif LEGACY_LIBMODBUS */
@@ -336,10 +328,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   if (host->connection != NULL)
     return (0);
 
-  /* Only reconnect once per interval. */
-  if (host->have_reconnected)
-    return (-1);
-
   if ((host->port < 1) || (host->port > 65535))
     host->port = MODBUS_TCP_DEFAULT_PORT;
 
@@ -349,7 +337,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   host->connection = modbus_new_tcp (host->node, host->port);
   if (host->connection == NULL)
   {
-    host->have_reconnected = 1;
     ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
     return (-1);
   }
@@ -369,7 +356,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
     return (status);
   }
 
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 #endif /* !LEGACY_LIBMODBUS */
@@ -392,7 +378,6 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   int values_num;
   const data_set_t *ds;
   int status;
-  int i;
 
   if ((host == NULL) || (slave == NULL) || (data == NULL))
     return (EINVAL);
@@ -429,6 +414,44 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   else
     values_num = 1;
 
+  status = 0;
+  if (host->connection == NULL)
+  {
+    status = EBADF;
+  }
+  else
+  {
+    struct sockaddr sockaddr;
+    socklen_t saddrlen = sizeof (sockaddr);
+
+    status = getpeername (modbus_get_socket (host->connection),
+        &sockaddr, &saddrlen);
+    if (status != 0)
+      status = errno;
+  }
+
+  if ((status == EBADF) || (status == ENOTSOCK) || (status == ENOTCONN))
+  {
+    status = mb_init_connection (host);
+    if (status != 0)
+    {
+      ERROR ("Modbus plugin: mb_init_connection (%s/%s) failed. ",
+          host->host, host->node);
+      host->is_connected = 0;
+      host->connection = NULL;
+      return (-1);
+    }
+  }
+  else if (status != 0)
+  {
+#if LEGACY_LIBMODBUS
+    modbus_close (&host->connection);
+#else
+    modbus_close (host->connection);
+    modbus_free (host->connection);
+#endif
+  }
 #if LEGACY_LIBMODBUS
   /* Version 2.0.3: Pass the connection struct as a pointer and pass the slave
    * id to each call of "read_holding_registers". */
@@ -445,51 +468,22 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 #endif
 
-  for (i = 0; i < 2; i++)
-  {
-    status = modbus_read_registers (host->connection,
+  status = modbus_read_registers (host->connection,
         /* start_addr = */ data->register_base,
         /* num_registers = */ values_num, /* buffer = */ values);
-    if (status > 0)
-      break;
-
-    if (host->is_connected)
-    {
+  if (status != values_num)
+  {
+    ERROR ("Modbus plugin: modbus_read_registers (%s/%s) failed. status = %i, values_num = %i "
+        "Giving up.", host->host, host->node, status, values_num);
 #if LEGACY_LIBMODBUS
-      modbus_close (&host->connection);
-      host->is_connected = 0;
+    modbus_close (&host->connection);
 #else
-      modbus_close (host->connection);
-      modbus_free (host->connection);
-      host->connection = NULL;
+    modbus_close (host->connection);
+    modbus_free (host->connection);
 #endif
-    }
-
-    /* If we already tried reconnecting this round, give up. */
-    if (host->have_reconnected)
-    {
-      ERROR ("Modbus plugin: modbus_read_registers (%s) failed. "
-          "Reconnecting has already been tried. Giving up.", host->host);
-      return (-1);
-    }
-
-    /* Maybe the device closed the connection during the waiting interval.
-     * Try re-establishing the connection. */
-    status = mb_init_connection (host);
-    if (status != 0)
-    {
-      ERROR ("Modbus plugin: modbus_read_registers (%s) failed. "
-          "While trying to reconnect, connecting to \"%s\" failed. "
-          "Giving up.",
-          host->host, host->node);
-      return (-1);
-    }
-
-    DEBUG ("Modbus plugin: Re-established connection to %s", host->host);
-
-    /* try again */
-    continue;
-  } /* for (i = 0, 1) */
+    host->connection = NULL;
+    return (-1);
+  }
 
   DEBUG ("Modbus plugin: mb_read_data: Success! "
       "modbus_read_registers returned with status %i.", status);
@@ -602,9 +596,6 @@ static int mb_read (user_data_t *user_data) /* {{{ */
 
   host = user_data->data;
 
-  /* Clear the reconnect flag. */
-  host->have_reconnected = 0;
-
   success = 0;
   for (i = 0; i < host->slaves_num; i++)
   {
index d2d0b4bfc57a9ad695b592835cadd4707935fac0..d15a055fe3296f70835e8e6daed68a46b37965bc 100644 (file)
@@ -534,7 +534,7 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
                        ssnprintf (n.message, sizeof (n.message),
                                        "slave SQL thread started");
                        plugin_dispatch_notification (&n);
-                       db->slave_sql_running = 0;
+                       db->slave_sql_running = 1;
                }
        }
 
index 7568a2c5ee2ded6439b6eb24927d64bb1b503e4a..de52262f4ec817b1b54ca876652b02e06a845252 100644 (file)
@@ -181,6 +181,8 @@ static void submit (char *type, char *inst, long long value)
     values[0].gauge = value;
   else if (strcmp (type, "nginx_requests") == 0)
     values[0].derive = value;
+  else if (strcmp (type, "connections") == 0)
+    values[0].derive = value;
   else
     return;
 
@@ -254,6 +256,8 @@ static int nginx_read (void)
          && (atoll (fields[1]) != 0)
          && (atoll (fields[2]) != 0))
       {
+       submit ("connections", "accepted", atoll (fields[0]));
+       submit ("connections", "handled", atoll (fields[1]));
        submit ("nginx_requests", NULL, atoll (fields[2]));
       }
     }
index 894b0e51d72731ba1f7faab5c032a122063e85be..6c7aa057cf2d272b126eadf6308a76188d3f3b1a 100644 (file)
@@ -31,6 +31,7 @@
 #include "utils_llist.h"
 #include "utils_heap.h"
 #include "utils_time.h"
+#include "utils_random.h"
 
 #if HAVE_PTHREAD_H
 # include <pthread.h>
@@ -108,6 +109,7 @@ static int             read_threads_num = 0;
 
 static write_queue_t  *write_queue_head;
 static write_queue_t  *write_queue_tail;
+static long            write_queue_length = 0;
 static _Bool           write_loop = 1;
 static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t  write_cond = PTHREAD_COND_INITIALIZER;
@@ -117,6 +119,9 @@ static size_t          write_threads_num = 0;
 static pthread_key_t   plugin_ctx_key;
 static _Bool           plugin_ctx_key_initialized = 0;
 
+static long            write_limit_high = 0;
+static long            write_limit_low = 0;
+
 /*
  * Static functions
  */
@@ -670,11 +675,13 @@ static int plugin_write_enqueue (value_list_t const *vl) /* {{{ */
        {
                write_queue_head = q;
                write_queue_tail = q;
+               write_queue_length = 1;
        }
        else
        {
                write_queue_tail->next = q;
                write_queue_tail = q;
+               write_queue_length += 1;
        }
 
        pthread_cond_signal (&write_cond);
@@ -701,8 +708,11 @@ static value_list_t *plugin_write_dequeue (void) /* {{{ */
 
        q = write_queue_head;
        write_queue_head = q->next;
-       if (write_queue_head == NULL)
+       write_queue_length -= 1;
+       if (write_queue_head == NULL) {
                write_queue_tail = NULL;
+               assert(0 == write_queue_length);
+               }
 
        pthread_mutex_unlock (&write_lock);
 
@@ -805,6 +815,7 @@ static void stop_write_threads (void) /* {{{ */
        }
        write_queue_head = NULL;
        write_queue_tail = NULL;
+       write_queue_length = 0;
        pthread_mutex_unlock (&write_lock);
 
        if (i > 0)
@@ -1206,6 +1217,27 @@ int plugin_register_shutdown (const char *name,
                                (void *) callback, /* user_data = */ NULL));
 } /* int plugin_register_shutdown */
 
+static void plugin_free_data_sets (void)
+{
+       void *key;
+       void *value;
+
+       if (data_sets == NULL)
+               return;
+
+       while (c_avl_pick (data_sets, &key, &value) == 0)
+       {
+               data_set_t *ds = value;
+               /* key is a pointer to ds->type */
+
+               sfree (ds->ds);
+               sfree (ds);
+       }
+
+       c_avl_destroy (data_sets);
+       data_sets = NULL;
+} /* void plugin_free_data_sets */
+
 int plugin_register_data_set (const data_set_t *ds)
 {
        data_set_t *ds_copy;
@@ -1423,7 +1455,8 @@ int plugin_unregister_notification (const char *name)
 
 void plugin_init_all (void)
 {
-       const char *chain_name;
+       char const *chain_name;
+       long write_threads_num;
        llentry_t *le;
        int status;
 
@@ -1436,16 +1469,38 @@ void plugin_init_all (void)
        chain_name = global_option_get ("PostCacheChain");
        post_cache_chain = fc_chain_get_by_name (chain_name);
 
+       write_limit_high = global_option_get_long ("WriteQueueLimitHigh",
+                       /* default = */ 0);
+       if (write_limit_high < 0)
        {
-               char const *tmp = global_option_get ("WriteThreads");
-               int num = atoi (tmp);
+               ERROR ("WriteQueueLimitHigh must be positive or zero.");
+               write_limit_high = 0;
+       }
 
-               if (num < 1)
-                       num = 5;
+       write_limit_low = global_option_get_long ("WriteQueueLimitLow",
+                       /* default = */ write_limit_high / 2);
+       if (write_limit_low < 0)
+       {
+               ERROR ("WriteQueueLimitLow must be positive or zero.");
+               write_limit_low = write_limit_high / 2;
+       }
+       else if (write_limit_low > write_limit_high)
+       {
+               ERROR ("WriteQueueLimitLow must not be larger than "
+                               "WriteQueueLimitHigh.");
+               write_limit_low = write_limit_high;
+       }
 
-               start_write_threads ((size_t) num);
+       write_threads_num = global_option_get_long ("WriteThreads",
+                       /* default = */ 5);
+       if (write_threads_num < 1)
+       {
+               ERROR ("WriteThreads must be positive.");
+               write_threads_num = 5;
        }
 
+       start_write_threads ((size_t) write_threads_num);
+
        if ((list_init == NULL) && (read_heap == NULL))
                return;
 
@@ -1730,6 +1785,7 @@ void plugin_shutdown_all (void)
        destroy_all_callbacks (&list_log);
 
        plugin_free_loaded ();
+       plugin_free_data_sets ();
 } /* void plugin_shutdown_all */
 
 int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
@@ -1950,10 +2006,76 @@ static int plugin_dispatch_values_internal (value_list_t *vl)
        return (0);
 } /* int plugin_dispatch_values_internal */
 
+static double get_drop_probability (void) /* {{{ */
+{
+       long pos;
+       long size;
+       long wql;
+
+       pthread_mutex_lock (&write_lock);
+       wql = write_queue_length;
+       pthread_mutex_unlock (&write_lock);
+
+       if (wql < write_limit_low)
+               return (0.0);
+       if (wql >= write_limit_high)
+               return (1.0);
+
+       pos = 1 + wql - write_limit_low;
+       size = 1 + write_limit_high - write_limit_low;
+
+       return (((double) pos) / ((double) size));
+} /* }}} double get_drop_probability */
+
+static _Bool check_drop_value (void) /* {{{ */
+{
+       static cdtime_t last_message_time = 0;
+       static pthread_mutex_t last_message_lock = PTHREAD_MUTEX_INITIALIZER;
+
+       double p;
+       double q;
+       int status;
+
+       if (write_limit_high == 0)
+               return (0);
+
+       p = get_drop_probability ();
+       if (p == 0.0)
+               return (0);
+
+       status = pthread_mutex_trylock (&last_message_lock);
+       if (status == 0)
+       {
+               cdtime_t now;
+
+               now = cdtime ();
+               if ((now - last_message_time) > TIME_T_TO_CDTIME_T (1))
+               {
+                       last_message_time = now;
+                       ERROR ("plugin_dispatch_values: Low water mark "
+                                       "reached. Dropping %.0f%% of metrics.",
+                                       100.0 * p);
+               }
+               pthread_mutex_unlock (&last_message_lock);
+       }
+
+       if (p == 1.0)
+               return (1);
+
+       q = cdrand_d ();
+       if (q > p)
+               return (1);
+       else
+               return (0);
+} /* }}} _Bool check_drop_value */
+
 int plugin_dispatch_values (value_list_t const *vl)
 {
        int status;
 
+       if (check_drop_value ())
+               return (0);
+
        status = plugin_write_enqueue (vl);
        if (status != 0)
        {
index 7e0b1e77ef3b523f45eb3a188598a0ee3b8bae2a..e5e11d4317b045fbb8587a3de3cc3f332653e858 100644 (file)
@@ -114,52 +114,26 @@ static int value_list_to_string (char *buffer, int buffer_len,
   return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-    const data_set_t *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+    value_list_t const *vl)
 {
-  int offset = 0;
+  char const suffix[] = ".rrd";
   int status;
+  size_t len;
 
-  assert (0 == strcmp (ds->type, vl->type));
-
-  if (datadir != NULL)
-  {
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s/", datadir);
-    if ((status < 1) || (status >= buffer_len - offset))
-      return (-1);
-    offset += status;
-  }
-
-  status = ssnprintf (buffer + offset, buffer_len - offset,
-      "%s/", vl->host);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
-
-  if (strlen (vl->plugin_instance) > 0)
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s-%s/", vl->plugin, vl->plugin_instance);
-  else
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s/", vl->plugin);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+  status = FORMAT_VL (buffer, buffer_size, vl);
+  if (status != 0)
+    return (status);
 
-  if (strlen (vl->type_instance) > 0)
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s-%s", vl->type, vl->type_instance);
-  else
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s", vl->type);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+  len = strlen (buffer);
+  assert (len < buffer_size);
+  buffer += len;
+  buffer_size -= len;
 
-  strncpy (buffer + offset, ".rrd", buffer_len - offset);
-  buffer[buffer_len - 1] = 0;
+  if (buffer_size <= sizeof (suffix))
+    return (ENOMEM);
 
+  memcpy (buffer, suffix, sizeof (suffix));
   return (0);
 } /* int value_list_to_filename */
 
@@ -434,7 +408,7 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
     return (-1);
   }
 
-  if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+  if (value_list_to_filename (filename, sizeof (filename), vl) != 0)
   {
     ERROR ("rrdcached plugin: value_list_to_filename failed.");
     return (-1);
index 2c80762ecb905454f843620fd86cf3cd449ba8f7..2f28329f0fbef43f17c70b3b7136a6b4f187e2b8 100644 (file)
@@ -198,7 +198,7 @@ static int srrd_update (char *filename, char *template,
 } /* int srrd_update */
 #endif /* !HAVE_THREADSAFE_LIBRRD */
 
-static int value_list_to_string (char *buffer, int buffer_len,
+static int value_list_to_string_multiple (char *buffer, int buffer_len,
                const data_set_t *ds, const value_list_t *vl)
 {
        int offset;
@@ -242,49 +242,67 @@ static int value_list_to_string (char *buffer, int buffer_len,
        } /* for ds->ds_num */
 
        return (0);
-} /* int value_list_to_string */
+} /* int value_list_to_string_multiple */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-               const data_set_t __attribute__((unused)) *ds, const value_list_t *vl)
+static int value_list_to_string (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
 {
-       int offset = 0;
        int status;
+       time_t tt;
+
+       if (ds->ds_num != 1)
+               return (value_list_to_string_multiple (buffer, buffer_len,
+                                       ds, vl));
 
-       if (datadir != NULL)
+       tt = CDTIME_T_TO_TIME_T (vl->time);
+       switch (ds->ds[0].type)
        {
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", datadir);
-               if ((status < 1) || (status >= buffer_len - offset))
-                       return (-1);
-               offset += status;
+               case DS_TYPE_DERIVE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%"PRIi64,
+                               (unsigned) tt, vl->values[0].derive);
+                       break;
+               case DS_TYPE_GAUGE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%lf",
+                               (unsigned) tt, vl->values[0].gauge);
+                       break;
+               case DS_TYPE_COUNTER:
+                       status = ssnprintf (buffer, buffer_len, "%u:%llu",
+                               (unsigned) tt, vl->values[0].counter);
+                       break;
+               case DS_TYPE_ABSOLUTE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%"PRIu64,
+                               (unsigned) tt, vl->values[0].absolute);
+                       break;
+               default:
+                       return (EINVAL);
        }
 
-       status = ssnprintf (buffer + offset, buffer_len - offset,
-                       "%s/", vl->host);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       if ((status < 1) || (status >= buffer_len))
+               return (ENOMEM);
 
-       if (strlen (vl->plugin_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s/", vl->plugin, vl->plugin_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", vl->plugin);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       return (0);
+} /* int value_list_to_string */
 
-       if (strlen (vl->type_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s.rrd", vl->type, vl->type_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s.rrd", vl->type);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+               value_list_t const *vl)
+{
+       char const suffix[] = ".rrd";
+       int status;
+       size_t len;
+
+       status = FORMAT_VL (buffer, buffer_size, vl);
+       if (status != 0)
+               return (status);
+
+       len = strlen (buffer);
+       assert (len < buffer_size);
+       buffer += len;
+       buffer_size -= len;
+
+       if (buffer_size <= sizeof (suffix))
+               return (ENOMEM);
 
+       memcpy (buffer, suffix, sizeof (suffix));
        return (0);
 } /* int value_list_to_filename */
 
@@ -887,7 +905,7 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl,
                return -1;
        }
 
-       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+       if (value_list_to_filename (filename, sizeof (filename), vl) != 0)
                return (-1);
 
        if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
diff --git a/src/sigrok.c b/src/sigrok.c
new file mode 100644 (file)
index 0000000..ba3e406
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * collectd - src/sigrok.c
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <pthread.h>
+
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+
+/* Minimum interval between dispatches coming from this plugin. The RRD
+ * plugin, at least, complains when written to with sub-second intervals.*/
+#define DEFAULT_MIN_DISPATCH_INTERVAL TIME_T_TO_CDTIME_T(0)
+
+static pthread_t sr_thread;
+static int sr_thread_running = FALSE;
+GSList *config_devices;
+static int num_devices;
+static int loglevel = SR_LOG_WARN;
+static struct sr_context *sr_ctx;
+
+struct config_device {
+       char *name;
+       char *driver;
+       char *conn;
+       char *serialcomm;
+       struct sr_dev_inst *sdi;
+       cdtime_t min_dispatch_interval;
+       cdtime_t last_dispatch;
+};
+
+
+static int sigrok_log_callback(void*cb_data __attribute__((unused)),
+               int msg_loglevel, const char *format, va_list args)
+{
+       char s[512];
+
+       if (msg_loglevel <= loglevel) {
+               vsnprintf(s, 512, format, args);
+               plugin_log(LOG_INFO, "sigrok plugin: %s", s);
+       }
+
+       return 0;
+}
+
+static int sigrok_config_device(oconfig_item_t *ci)
+{
+       struct config_device *cfdev;
+       int i;
+
+       if (!(cfdev = malloc(sizeof(struct config_device)))) {
+               ERROR("sigrok plugin: malloc() failed.");
+               return -1;
+       }
+       memset(cfdev, 0, sizeof(*cfdev));
+       if (cf_util_get_string(ci, &cfdev->name)) {
+               free(cfdev);
+               WARNING("sigrok plugin: Invalid device name.");
+               return -1;
+       }
+       cfdev->min_dispatch_interval = DEFAULT_MIN_DISPATCH_INTERVAL;
+
+       for (i = 0; i < ci->children_num; i++) {
+               oconfig_item_t *item = ci->children + i;
+               if (!strcasecmp(item->key, "driver"))
+                       cf_util_get_string(item, &cfdev->driver);
+               else if (!strcasecmp(item->key, "conn"))
+                       cf_util_get_string(item, &cfdev->conn);
+               else if (!strcasecmp(item->key, "serialcomm"))
+                       cf_util_get_string(item, &cfdev->serialcomm);
+               else if (!strcasecmp(item->key, "minimuminterval"))
+                       cf_util_get_cdtime(item, &cfdev->min_dispatch_interval);
+               else
+                       WARNING("sigrok plugin: Invalid keyword \"%s\".",
+                                       item->key);
+       }
+
+       config_devices = g_slist_append(config_devices, cfdev);
+
+       return 0;
+}
+
+static int sigrok_config(oconfig_item_t *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++) {
+               oconfig_item_t *item = ci->children + i;
+               if (strcasecmp("LogLevel", item->key) == 0) {
+                       int status;
+                       int tmp = -1;
+
+                       status = cf_util_get_int (item, &tmp);
+                       if (status != 0)
+                               continue;
+                       else if ((tmp < 0) || (tmp > 5)) {
+                               ERROR ("sigrok plugin: The \"LogLevel\" "
+                                               "configuration option expects "
+                                               "an integer between 0 and 5 "
+                                               "(inclusive); you provided %i.",
+                                               tmp);
+                               continue;
+                       }
+                       loglevel = tmp;
+               } else if (!strcasecmp(item->key, "Device"))
+                       sigrok_config_device(item);
+               else
+                       WARNING("sigrok plugin: Invalid keyword \"%s\".",
+                                       item->key);
+       }
+
+       return 0;
+}
+
+static char *sigrok_value_type(const struct sr_datafeed_analog *analog)
+{
+       char *s;
+
+       if (analog->mq == SR_MQ_VOLTAGE)
+               s = "voltage";
+       else if (analog->mq == SR_MQ_CURRENT)
+               s = "current";
+       else if (analog->mq == SR_MQ_FREQUENCY)
+               s = "frequency";
+       else if (analog->mq == SR_MQ_POWER)
+               s = "power";
+       else if (analog->mq == SR_MQ_TEMPERATURE)
+               s = "temperature";
+       else if (analog->mq == SR_MQ_RELATIVE_HUMIDITY)
+               s = "humidity";
+       else if (analog->mq == SR_MQ_SOUND_PRESSURE_LEVEL)
+               s = "spl";
+       else
+               s = "gauge";
+
+       return s;
+}
+
+static void sigrok_feed_callback(const struct sr_dev_inst *sdi,
+               const struct sr_datafeed_packet *packet, void *cb_data)
+{
+       const struct sr_datafeed_analog *analog;
+       struct config_device *cfdev;
+       GSList *l;
+       value_t value;
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* Find this device's configuration. */
+       cfdev = NULL;
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               if (cfdev->sdi == sdi) {
+                       /* Found it. */
+                       break;
+               }
+               cfdev = NULL;
+       }
+
+       if (!cfdev) {
+               ERROR("sigrok plugin: Received data from driver \"%s\" but "
+                               "can't find a configuration / device matching "
+                               "it.", sdi->driver->name);
+               return;
+       }
+
+       if (packet->type == SR_DF_END) {
+               /* TODO: try to restart acquisition after a delay? */
+               WARNING("sigrok plugin: acquisition for \"%s\" ended.",
+                               cfdev->name);
+               return;
+       }
+
+       if (packet->type != SR_DF_ANALOG)
+               return;
+
+       if ((cfdev->min_dispatch_interval != 0)
+                       && ((cdtime() - cfdev->last_dispatch)
+                               < cfdev->min_dispatch_interval))
+               return;
+
+       /* Ignore all but the first sample on the first probe. */
+       analog = packet->payload;
+       value.gauge = analog->data[0];
+       vl.values = &value;
+       vl.values_len = 1;
+       sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+       sstrncpy(vl.plugin, "sigrok", sizeof(vl.plugin));
+       ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance),
+                       "%s", cfdev->name);
+       sstrncpy(vl.type, sigrok_value_type(analog), sizeof(vl.type));
+
+       plugin_dispatch_values(&vl);
+       cfdev->last_dispatch = cdtime();
+}
+
+static void sigrok_free_drvopts(struct sr_config *src)
+{
+       g_variant_unref(src->data);
+       g_free(src);
+}
+
+static int sigrok_init_driver(struct config_device *cfdev,
+               struct sr_dev_driver *drv)
+{
+       struct sr_config *src;
+       GSList *devlist, *drvopts;
+       char hwident[512];
+
+       if (sr_driver_init(sr_ctx, drv) != SR_OK)
+               /* Error was logged by libsigrok. */
+               return -1;
+
+       drvopts = NULL;
+       if (cfdev->conn) {
+               if (!(src = malloc(sizeof(struct sr_config))))
+                       return -1;
+               src->key = SR_CONF_CONN;
+               src->data = g_variant_new_string(cfdev->conn);
+               drvopts = g_slist_append(drvopts, src);
+       }
+       if (cfdev->serialcomm) {
+               if (!(src = malloc(sizeof(struct sr_config))))
+                       return -1;
+               src->key = SR_CONF_SERIALCOMM;
+               src->data = g_variant_new_string(cfdev->serialcomm);
+               drvopts = g_slist_append(drvopts, src);
+       }
+       devlist = sr_driver_scan(drv, drvopts);
+       g_slist_free_full(drvopts, (GDestroyNotify)sigrok_free_drvopts);
+       if (!devlist) {
+               /* Not an error, but the user should know about it. */
+               WARNING("sigrok plugin: No device found for \"%s\".",
+                               cfdev->name);
+               return 0;
+       }
+
+       if (g_slist_length(devlist) > 1) {
+               INFO("sigrok plugin: %d sigrok devices for device entry "
+                               "\"%s\": must be 1.",
+                               g_slist_length(devlist), cfdev->name);
+               return -1;
+       }
+       cfdev->sdi = devlist->data;
+       g_slist_free(devlist);
+       ssnprintf(hwident, sizeof(hwident), "%s %s %s",
+                       cfdev->sdi->vendor ? cfdev->sdi->vendor : "",
+                       cfdev->sdi->model ? cfdev->sdi->model : "",
+                       cfdev->sdi->version ? cfdev->sdi->version : "");
+       INFO("sigrok plugin: Device \"%s\" is a %s", cfdev->name, hwident);
+
+       if (sr_dev_open(cfdev->sdi) != SR_OK)
+               return -1;
+
+       if (sr_session_dev_add(cfdev->sdi) != SR_OK)
+               return -1;
+
+       return 1;
+}
+
+static void *sigrok_read_thread(void *arg __attribute__((unused)))
+{
+       struct sr_dev_driver *drv, **drvlist;
+       GSList *l;
+       struct config_device *cfdev;
+       int ret, i;
+
+       sr_log_callback_set(sigrok_log_callback, NULL);
+       sr_log_loglevel_set(loglevel);
+
+       if ((ret = sr_init(&sr_ctx)) != SR_OK) {
+               ERROR("sigrok plugin: Failed to initialize libsigrok: %s.",
+                               sr_strerror(ret));
+               return NULL;
+       }
+
+       if (!sr_session_new())
+               return NULL;
+
+       num_devices = 0;
+       drvlist = sr_driver_list();
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               drv = NULL;
+               for (i = 0; drvlist[i]; i++) {
+                       if (!strcmp(drvlist[i]->name, cfdev->driver)) {
+                               drv = drvlist[i];
+                               break;
+                       }
+               }
+               if (!drv) {
+                       ERROR("sigrok plugin: Unknown driver \"%s\".",
+                                       cfdev->driver);
+                       return NULL;
+               }
+
+               if ((ret = sigrok_init_driver(cfdev, drv)) < 0)
+                       /* Error was already logged. */
+                       return NULL;
+
+               num_devices += ret;
+       }
+
+       if (num_devices > 0) {
+               /* Do this only when we're sure there's hardware to talk to. */
+               if (sr_session_datafeed_callback_add(sigrok_feed_callback, NULL)
+                               != SR_OK)
+                       return NULL;
+
+               /* Start acquisition on all devices. */
+               if (sr_session_start() != SR_OK)
+                       return NULL;
+
+               /* Main loop, runs forever. */
+               sr_session_run();
+
+               sr_session_stop();
+               sr_session_dev_remove_all();
+       }
+
+       sr_session_destroy();
+
+       sr_exit(sr_ctx);
+
+       pthread_exit(NULL);
+       sr_thread_running = FALSE;
+
+       return NULL;
+}
+
+static int sigrok_init(void)
+{
+       int status;
+
+       if (sr_thread_running) {
+               ERROR("sigrok plugin: Thread already running.");
+               return -1;
+       }
+
+       if ((status = plugin_thread_create(&sr_thread, NULL, sigrok_read_thread,
+                       NULL)) != 0) {
+               ERROR("sigrok plugin: Failed to create thread: %s.",
+                               strerror(status));
+               return -1;
+       }
+       sr_thread_running = TRUE;
+
+       return 0;
+}
+
+static int sigrok_shutdown(void)
+{
+       struct config_device *cfdev;
+       GSList *l;
+
+       if (sr_thread_running) {
+               pthread_cancel(sr_thread);
+               pthread_join(sr_thread, NULL);
+       }
+
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               free(cfdev->name);
+               free(cfdev->driver);
+               free(cfdev->conn);
+               free(cfdev->serialcomm);
+               free(cfdev);
+       }
+       g_slist_free(config_devices);
+
+       return 0;
+}
+
+void module_register(void)
+{
+       plugin_register_complex_config("sigrok", sigrok_config);
+       plugin_register_init("sigrok", sigrok_init);
+       plugin_register_shutdown("sigrok", sigrok_shutdown);
+}
index 4f849320a3f3f6bdc2bdfb3730bf60fdc08016c5..ad81c89214a926d8e194bad96d2dd207bf6fa707 100644 (file)
@@ -1373,13 +1373,14 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
         csnmp_table_values_t *vt;
         oid_t vb_name;
         oid_t suffix;
+        int ret;
 
         csnmp_oid_init (&vb_name, vb->name, vb->name_length);
 
         /* Calculate the current suffix. This is later used to check that the
          * suffix is increasing. This also checks if we left the subtree */
-        status = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
-        if (status != 0)
+        ret = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
+        if (ret != 0)
         {
           DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
               "Value probably left its subtree.",
index 25cbcd4c1b5188df787e843c60aecb895aad5688..bcb157255fcd1a30a8048d7977271cb1dc46e027 100644 (file)
@@ -52,22 +52,6 @@ typedef struct ctail_config_match_s ctail_config_match_t;
 cu_tail_match_t **tail_match_list = NULL;
 size_t tail_match_list_num = 0;
 
-static int ctail_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 ("tail 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 ctail_config_add_string */
-
 static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
     oconfig_item_t *ci)
 {
@@ -158,16 +142,15 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
     oconfig_item_t *option = ci->children + i;
 
     if (strcasecmp ("Regex", option->key) == 0)
-      status = ctail_config_add_string ("Regex", &cm.regex, option);
+      status = cf_util_get_string (option, &cm.regex);
     else if (strcasecmp ("ExcludeRegex", option->key) == 0)
-      status = ctail_config_add_string ("ExcludeRegex", &cm.excluderegex,
-                                       option);
+      status = cf_util_get_string (option, &cm.excluderegex);
     else if (strcasecmp ("DSType", option->key) == 0)
       status = ctail_config_add_match_dstype (&cm, option);
     else if (strcasecmp ("Type", option->key) == 0)
-      status = ctail_config_add_string ("Type", &cm.type, option);
+      status = cf_util_get_string (option, &cm.type);
     else if (strcasecmp ("Instance", option->key) == 0)
-      status = ctail_config_add_string ("Instance", &cm.type_instance, option);
+      status = cf_util_get_string (option, &cm.type_instance);
     else
     {
       WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
@@ -259,7 +242,7 @@ static int ctail_config_add_file (oconfig_item_t *ci)
       status = 0;
     }
     else if (strcasecmp ("Instance", option->key) == 0)
-      status = ctail_config_add_string ("Instance", &plugin_instance, option);
+      status = cf_util_get_string (option, &plugin_instance);
     else
     {
       WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
index d4cfd6ea922ef2dbc984e6ca137f28774aa6ed68..7df4d616a977cb5ca53028c6808b08c1c580ba80 100644 (file)
@@ -943,12 +943,12 @@ static int ut_missing (const value_list_t *vl,
   char identifier[6 * DATA_MAX_NAME_LEN];
   notification_t n;
 
-  /* dispatch notifications for "interesting" values only */
   if (threshold_tree == NULL)
     return (0);
 
   th = threshold_search (vl);
-  if (th == NULL)
+  /* dispatch notifications for "interesting" values only */
+  if ((th == NULL) || ((th->flags & UT_FLAG_INTERESTING) == 0))
     return (0);
 
   missing_time = cdtime () - vl->time;
index 950f6b0d842dd087b9db3e7b012d280da6ec417b..723b477d72e45e1c4eac75ef719a4aa7d06ec641 100644 (file)
@@ -89,7 +89,7 @@ ipt_packets           value:DERIVE:0:U
 irq                    value:DERIVE:0:U
 latency                        value:GAUGE:0:65535
 links                  value:GAUGE:0:U
-load                   shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
+load                   shortterm:GAUGE:0:5000, midterm:GAUGE:0:5000, longterm:GAUGE:0:5000
 md_disks               value:GAUGE:0:U
 memcached_command      value:DERIVE:0:U
 memcached_connections  value:GAUGE:0:U
@@ -114,6 +114,8 @@ node_tx_rate                value:GAUGE:0:127
 objects                        value:GAUGE:0:U
 operations             value:DERIVE:0:U
 percent                        value:GAUGE:0:100.1
+percent_bytes          value:GAUGE:0:100.1
+percent_inodes         value:GAUGE:0:100.1
 pf_counters            value:DERIVE:0:U
 pf_limits              value:DERIVE:0:U
 pf_source              value:DERIVE:0:U
@@ -158,10 +160,11 @@ signal_quality            value:GAUGE:0:U
 snr                    value:GAUGE:0:U
 spam_check             value:GAUGE:0:U
 spam_score             value:GAUGE:U:U
+spl                    value:GAUGE:U:U
 swap_io                        value:DERIVE:0:U
 swap                   value:GAUGE:0:1099511627776
 tcp_connections                value:GAUGE:0:4294967295
-temperature            value:GAUGE:-273.15:U
+temperature            value:GAUGE:U:U
 threads                        value:GAUGE:0:U
 time_dispersion                value:GAUGE:-1000000:1000000
 timeleft               value:GAUGE:0:U
index 67f988195af55580ea6ab47a6d578afded6c546d..111742ba0dc5c5ae9c3a866dc9920fd4cb566bd1 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 types.db - Data-set specifications for the system statistics collection daemon
index 1f2403c3aa26412acd36046c790db290b0610ff0..83f789be9cd3acdf7f5535cb2e41ed035e3cbc2b 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef COLLECTD_UTILS_MOUNT_H
 #define COLLECTD_UTILS_MOUNT_H 1
 
+#include <stdio.h>
 #if HAVE_FS_INFO_H
 # include <fs_info.h>
 #endif
index f7bca416880648a0e2a7843c8877a466a3149c96..e2ced0e4e20e128184f56330634aa854ae9c21b1 100644 (file)
@@ -700,7 +700,9 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                else
                {
                        WARNING ("Varnish plugin: Ignoring unknown "
-                                       "configuration option: \"%s\"",
+                                       "configuration option: \"%s\". Did "
+                                       "you forget to add an <Instance /> "
+                                       "block around the configuration?",
                                        child->key);
                }
        }
index 949a8425178f700b952787f15bc0426f4f6d7299..6124d33edf5a07ae26f922b1cc7a151a959b6c28 100644 (file)
@@ -35,6 +35,8 @@
   *   <Carbon>
   *     Host "localhost"
   *     Port "2003"
+  *     Protocol "udp"
+  *     LogSendErrors true
   *     Prefix "collectd"
   *   </Carbon>
   * </Plugin>
 # define WG_DEFAULT_SERVICE "2003"
 #endif
 
+#ifndef WG_DEFAULT_PROTOCOL
+# define WG_DEFAULT_PROTOCOL "udp"
+#endif
+
+#ifndef WG_DEFAULT_LOG_SEND_ERRORS
+# define WG_DEFAULT_LOG_SEND_ERRORS 1
+#endif
+
 #ifndef WG_DEFAULT_ESCAPE
 # define WG_DEFAULT_ESCAPE '_'
 #endif
@@ -84,6 +94,8 @@ struct wg_callback
 
     char    *node;
     char    *service;
+    char    *protocol;
+    _Bool   log_send_errors;
     char    *prefix;
     char    *postfix;
     char     escape_char;
@@ -116,10 +128,11 @@ static int wg_send_buffer (struct wg_callback *cb)
     ssize_t status = 0;
 
     status = swrite (cb->sock_fd, cb->send_buf, strlen (cb->send_buf));
-    if (status < 0)
+    if (cb->log_send_errors && status < 0)
     {
         char errbuf[1024];
-        ERROR ("write_graphite plugin: send failed with status %zi (%s)",
+        ERROR ("write_graphite plugin: send to %s:%s (%s) failed with status %zi (%s)",
+                cb->node, cb->service, cb->protocol,
                 status, sstrerror (errno, errbuf, sizeof (errbuf)));
 
 
@@ -173,6 +186,7 @@ static int wg_callback_init (struct wg_callback *cb)
 
     const char *node = cb->node ? cb->node : WG_DEFAULT_NODE;
     const char *service = cb->service ? cb->service : WG_DEFAULT_SERVICE;
+    const char *protocol = cb->protocol ? cb->protocol : WG_DEFAULT_PROTOCOL;
 
     if (cb->sock_fd > 0)
         return (0);
@@ -182,15 +196,19 @@ static int wg_callback_init (struct wg_callback *cb)
     ai_hints.ai_flags |= AI_ADDRCONFIG;
 #endif
     ai_hints.ai_family = AF_UNSPEC;
-    ai_hints.ai_socktype = SOCK_STREAM;
+
+    if (0 == strcasecmp ("tcp", protocol))
+        ai_hints.ai_socktype = SOCK_STREAM;
+    else
+        ai_hints.ai_socktype = SOCK_DGRAM;
 
     ai_list = NULL;
 
     status = getaddrinfo (node, service, &ai_hints, &ai_list);
     if (status != 0)
     {
-        ERROR ("write_graphite plugin: getaddrinfo (%s, %s) failed: %s",
-                node, service, gai_strerror (status));
+        ERROR ("write_graphite plugin: getaddrinfo (%s, %s, %s) failed: %s",
+                node, service, protocol, gai_strerror (status));
         return (-1);
     }
 
@@ -219,17 +237,16 @@ static int wg_callback_init (struct wg_callback *cb)
     {
         char errbuf[1024];
         c_complain (LOG_ERR, &cb->init_complaint,
-                "write_graphite plugin: Connecting to %s:%s failed. "
-                "The last error was: %s", node, service,
+                "write_graphite plugin: Connecting to %s:%s via %s failed. "
+                "The last error was: %s", node, service, protocol,
                 sstrerror (errno, errbuf, sizeof (errbuf)));
-        close (cb->sock_fd);
         return (-1);
     }
     else
     {
         c_release (LOG_INFO, &cb->init_complaint,
-                "write_graphite plugin: Successfully connected to %s:%s.",
-                node, service);
+                "write_graphite plugin: Successfully connected to %s:%s via %s.",
+                node, service, protocol);
     }
 
     wg_reset_buffer (cb);
@@ -250,11 +267,15 @@ static void wg_callback_free (void *data)
 
     wg_flush_nolock (/* timeout = */ 0, cb);
 
-    close(cb->sock_fd);
-    cb->sock_fd = -1;
+    if (cb->sock_fd >= 0)
+    {
+        close (cb->sock_fd);
+        cb->sock_fd = -1;
+    }
 
     sfree(cb->name);
     sfree(cb->node);
+    sfree(cb->protocol);
     sfree(cb->service);
     sfree(cb->prefix);
     sfree(cb->postfix);
@@ -335,9 +356,10 @@ static int wg_send_message (char const *message, struct wg_callback *cb)
     cb->send_buf_fill += message_len;
     cb->send_buf_free -= message_len;
 
-    DEBUG ("write_graphite plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"",
+    DEBUG ("write_graphite plugin: [%s]:%s (%s) buf %zu/%zu (%.1f %%) \"%s\"",
             cb->node,
             cb->service,
+            cb->protocol,
             cb->send_buf_fill, sizeof (cb->send_buf),
             100.0 * ((double) cb->send_buf_fill) / ((double) sizeof (cb->send_buf)),
             message);
@@ -367,12 +389,9 @@ static int wg_write_messages (const data_set_t *ds, const value_list_t *vl,
         return (status);
 
     /* Send the message to graphite */
-    wg_send_message (buffer, cb);
-    if (status != 0)
-    {
-        /* An error message has already been printed. */
+    status = wg_send_message (buffer, cb);
+    if (status != 0) /* error message has been printed already. */
         return (status);
-    }
 
     return (0);
 } /* int wg_write_messages */
@@ -430,6 +449,7 @@ static int wg_config_node (oconfig_item_t *ci)
     user_data_t user_data;
     char callback_name[DATA_MAX_NAME_LEN];
     int i;
+    int status = 0;
 
     cb = malloc (sizeof (*cb));
     if (cb == NULL)
@@ -442,6 +462,8 @@ static int wg_config_node (oconfig_item_t *ci)
     cb->name = NULL;
     cb->node = NULL;
     cb->service = NULL;
+    cb->protocol = NULL;
+    cb->log_send_errors = WG_DEFAULT_LOG_SEND_ERRORS;
     cb->prefix = NULL;
     cb->postfix = NULL;
     cb->escape_char = WG_DEFAULT_ESCAPE;
@@ -450,7 +472,7 @@ static int wg_config_node (oconfig_item_t *ci)
     /* FIXME: Legacy configuration syntax. */
     if (strcasecmp ("Carbon", ci->key) != 0)
     {
-        int status = cf_util_get_string (ci, &cb->name);
+        status = cf_util_get_string (ci, &cb->name);
         if (status != 0)
         {
             wg_callback_free (cb);
@@ -469,6 +491,20 @@ static int wg_config_node (oconfig_item_t *ci)
             cf_util_get_string (child, &cb->node);
         else if (strcasecmp ("Port", child->key) == 0)
             cf_util_get_service (child, &cb->service);
+        else if (strcasecmp ("Protocol", child->key) == 0)
+        {
+            cf_util_get_string (child, &cb->protocol);
+
+            if (strcasecmp ("UDP", cb->protocol) != 0 &&
+                strcasecmp ("TCP", cb->protocol) != 0)
+            {
+                ERROR ("write_graphite plugin: Unknown protocol (%s)",
+                        cb->protocol);
+                status = -1;
+            }
+        }
+        else if (strcasecmp ("LogSendErrors", child->key) == 0)
+            cf_util_get_boolean (child, &cb->log_send_errors);
         else if (strcasecmp ("Prefix", child->key) == 0)
             cf_util_get_string (child, &cb->prefix);
         else if (strcasecmp ("Postfix", child->key) == 0)
@@ -488,14 +524,25 @@ static int wg_config_node (oconfig_item_t *ci)
         {
             ERROR ("write_graphite plugin: Invalid configuration "
                         "option: %s.", child->key);
+            status = -1;
         }
+
+        if (status != 0)
+            break;
+    }
+
+    if (status != 0)
+    {
+        wg_callback_free (cb);
+        return (status);
     }
 
     /* FIXME: Legacy configuration syntax. */
     if (cb->name == NULL)
-        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s",
+        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s/%s",
                 cb->node != NULL ? cb->node : WG_DEFAULT_NODE,
-                cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE);
+                cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE,
+                cb->protocol != NULL ? cb->protocol : WG_DEFAULT_PROTOCOL);
     else
         ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s",
                 cb->name);
index 3e2ddd75b0d5caec3bbc40b5121b1de20ab819b4..3345d0444a94653a4e3be42a9d9051bfd264e790 100644 (file)
@@ -60,6 +60,8 @@ static size_t   riemann_tags_num;
 
 static void riemann_event_protobuf_free (Event *event) /* {{{ */
 {
+       size_t i;
+
        if (event == NULL)
                return;
 
@@ -72,6 +74,15 @@ static void riemann_event_protobuf_free (Event *event) /* {{{ */
        event->tags = NULL;
        event->n_tags = 0;
 
+       for (i = 0; i < event->n_attributes; i++)
+       {
+               sfree (event->attributes[i]->key);
+               sfree (event->attributes[i]->value);
+               sfree (event->attributes[i]);
+       }
+       sfree (event->attributes);
+       event->n_attributes = 0;
+
        sfree (event);
 } /* }}} void riemann_event_protobuf_free */