Merge branch 'collectd-5.5' into collectd-5.6
authorMarc Fournier <marc.fournier@camptocamp.com>
Mon, 10 Oct 2016 18:58:19 +0000 (20:58 +0200)
committerMarc Fournier <marc.fournier@camptocamp.com>
Mon, 10 Oct 2016 18:58:19 +0000 (20:58 +0200)
1  2 
src/collectd.conf.pod
src/daemon/common.c
src/tail_csv.c
src/write_redis.c

diff --combined src/collectd.conf.pod
@@@ -38,7 -38,7 +38,7 @@@ i.e. a C<E<lt>PluginE<nbsp>...E<gt>> bl
  The syntax of this config file is similar to the config file of the famous
  I<Apache> webserver. Each line contains either an option (a key and a list of
  one or more values) or a section-start or -end. Empty lines and everything
 -after a non-quoted hash-symbol (C<#>) is ignored. I<Keys> are unquoted
 +after a non-quoted hash-symbol (C<#>) are ignored. I<Keys> are unquoted
  strings, consisting only of alphanumeric characters and the underscore (C<_>)
  character. Keys are handled case insensitive by I<collectd> itself and all
  plugins included with it. I<Values> can either be an I<unquoted string>, a
@@@ -70,7 -70,7 +70,7 @@@ C<E<lt>B<Plugin> ...E<gt>> block
  
  =item B<BaseDir> I<Directory>
  
 -Sets the base directory. This is the directory beneath all RRD-files are
 +Sets the base directory. This is the directory beneath which all RRD-files are
  created. Possibly more subdirectories are created. This is also the working
  directory for the daemon.
  
@@@ -83,7 -83,7 +83,7 @@@ I<collectd> will be mostly useless
  Only the first B<LoadPlugin> statement or block for a given plugin name has any
  effect. This is useful when you want to split up the configuration into smaller
  files and want each file to be "self contained", i.e. it contains a B<Plugin>
 -block I<and> then appropriate B<LoadPlugin> statement. The downside is that if
 +block I<and> the appropriate B<LoadPlugin> statement. The downside is that if
  you have multiple conflicting B<LoadPlugin> blocks, e.g. when they specify
  different intervals, only one of them (the first one encountered) will take
  effect and all others will be silently ignored.
@@@ -127,17 -127,8 +127,17 @@@ the average user from ever having to de
  =item B<Interval> I<Seconds>
  
  Sets a plugin-specific interval for collecting metrics. This overrides the
 -global B<Interval> setting. If a plugin provides own support for specifying an
 -interval, that setting will take precedence.
 +global B<Interval> setting. If a plugin provides its own support for specifying
 +an interval, that setting will take precedence.
 +
 +=item B<FlushInterval> I<Seconds>
 +
 +Specifies the interval, in seconds, to call the flush callback if it's
 +defined in this plugin. By default, this is disabled.
 +
 +=item B<FlushTimeout> I<Seconds>
 +
 +Specifies the value of the timeout argument of the flush callback.
  
  =back
  
@@@ -212,7 -203,7 +212,7 @@@ matching C<*.conf> in any subdirectory 
  
  =back
  
 -If more than one files are included by a single B<Include> option, the files
 +If more than one file is 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
  order in which the files are loaded.
@@@ -259,7 -250,7 +259,7 @@@ magic! (Assuming you're using the I<RRD
  
  =item B<MaxReadInterval> I<Seconds>
  
 -Read plugin doubles interval between queries after each failed attempt
 +A read plugin doubles the interval between queries after each failed attempt
  to get data.
  
  This options limits the maximum value of the interval. The default value is
@@@ -532,9 -523,7 +532,9 @@@ are disabled by default
  The I<AMQP plugin> can be used to communicate with other instances of
  I<collectd> or third party applications using an AMQP message broker. Values
  are sent to or received from the broker, which handles routing, queueing and
 -possibly filtering or messages.
 +possibly filtering out messages.
 +
 +B<Synopsis:>
  
   <Plugin "amqp">
     # Send values to an AMQP broker
@@@ -832,24 -821,12 +832,24 @@@ B<apcupsd> can handle it
  
  TCP-Port to connect to. Defaults to B<3551>.
  
 -=item B<ReportSeconds> B<true|false>
 +=item B<ReportSeconds> B<true>|B<false>
  
  If set to B<true>, the time reported in the C<timeleft> metric will be
  converted to seconds. This is the recommended setting. If set to B<false>, the
  default for backwards compatibility, the time will be reported in minutes.
  
 +=item B<PersistentConnection> B<true>|B<false>
 +
 +The plugin is designed to keep the connection to I<apcupsd> open between reads.
 +If plugin poll interval is greater than 15 seconds (hardcoded socket close
 +timeout in I<apcupsd> NIS), then this option is B<false> by default.
 +
 +You can instruct the plugin to close the connection after each read by setting
 +this option to B<false> or force keeping the connection by setting it to B<true>.
 +
 +If I<apcupsd> appears to close the connection due to inactivity quite quickly,
 +the plugin will try to detect this problem and switch to an open-read-close mode.
 +
  =back
  
  =head2 Plugin C<aquaero>
@@@ -960,7 -937,7 +960,7 @@@ support the SM Bus command subset)
  
  The reduction or normalization to mean sea level pressure requires (depending
  on selected method/approximation) also altitude and reference to temperature
- sensor(s).  When multiple temperature sensors are configured the minumum of
+ sensor(s).  When multiple temperature sensors are configured the minimum of
  their values is always used (expecting that the warmer ones are affected by
  e.g. direct sun light at that moment).
  
@@@ -1072,7 -1049,7 +1072,7 @@@ The altitude (in meters) of the locatio
  
  Temperature sensor(s) which should be used as a reference when normalizing the
  pressure using C<Normalization> method 2.
- When specified more sensors a minumum is found and used each time.  The
+ When specified more sensors a minimum is found and used each time.  The
  temperature reading directly from this pressure sensor/plugin is typically not
  suitable as the pressure sensor will be probably inside while we want outside
  temperature.  The collectd reference name is something like
@@@ -1367,32 -1344,6 +1367,32 @@@ at all, B<all> cgroups are selected
  
  =back
  
 +=head2 Plugin C<chrony>
 +
 +The C<chrony> plugin collects ntp data from a B<chronyd> server, such as clock
 +skew and per-peer stratum.
 +
 +For talking to B<chronyd>, it mimics what the B<chronyc> control program does
 +on the wire.
 +
 +Available configuration options for the C<chrony> plugin:
 +
 +=over 4
 +
 +=item B<Host> I<Hostname>
 +
 +Hostname of the host running B<chronyd>. Defaults to B<localhost>.
 +
 +=item B<Port> I<Port>
 +
 +UDP-Port to connect to. Defaults to B<323>.
 +
 +=item B<Timeout> I<Timeout>
 +
 +Connection timeout in seconds. Defaults to B<2>.
 +
 +=back
 +
  =head2 Plugin C<conntrack>
  
  This plugin collects IP conntrack statistics.
@@@ -1453,11 -1404,6 +1453,11 @@@ are set to B<true>. In this case, by de
  Jiffies. By setting this option to B<true>, you can request percentage values
  in the un-aggregated (per-CPU, per-state) mode as well.
  
 +=item B<ReportNumCpu> B<false>|B<true>
 +
 +When set to B<true>, reports the number of available CPUs.
 +Defaults to B<false>.
 +
  =back
  
  =head2 Plugin C<cpufreq>
@@@ -1468,16 -1414,6 +1468,16 @@@ installed) to get the current CPU frequ
  sure B<cpufreqd> (L<http://cpufreqd.sourceforge.net/>) or a similar tool is
  installed and an "cpu governor" (that's a kernel module) is loaded.
  
 +=head2 Plugin C<cpusleep>
 +
 +This plugin doesn't have any options. It reads CLOCK_BOOTTIME and
 +CLOCK_MONOTONIC and reports the difference between these clocks. Since
 +BOOTTIME clock increments while device is suspended and MONOTONIC
 +clock does not, the derivative of the difference between these clocks
 +gives the relative amount of time the device has spent in suspend
 +state. The recorded value is in milliseconds of sleep per seconds of
 +wall clock.
 +
  =head2 Plugin C<csv>
  
  =over 4
@@@ -1498,92 -1434,6 +1498,92 @@@ number
  
  =back
  
 +=head2 cURL Statistics
 +
 +All cURL-based plugins support collection of generic, request-based
 +statistics. These are disabled by default and can be enabled selectively for
 +each page or URL queried from the curl, curl_json, or curl_xml plugins. See
 +the documentation of those plugins for specific information. This section
 +describes the available metrics that can be configured for each plugin. All
 +options are disabled by default.
 +
 +See L<http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html> for more details.
 +
 +=over 4
 +
 +=item B<TotalTime> B<true|false>
 +
 +Total time of the transfer, including name resolving, TCP connect, etc.
 +
 +=item B<NamelookupTime> B<true|false>
 +
 +Time it took from the start until name resolving was completed.
 +
 +=item B<ConnectTime> B<true|false>
 +
 +Time it took from the start until the connect to the remote host (or proxy)
 +was completed.
 +
 +=item B<AppconnectTime> B<true|false>
 +
 +Time it took from the start until the SSL/SSH connect/handshake to the remote
 +host was completed.
 +
 +=item B<PretransferTime> B<true|false>
 +
 +Time it took from the start until just before the transfer begins.
 +
 +=item B<StarttransferTime> B<true|false>
 +
 +Time it took from the start until the first byte was received.
 +
 +=item B<RedirectTime> B<true|false>
 +
 +Time it took for all redirection steps include name lookup, connect,
 +pre-transfer and transfer before final transaction was started.
 +
 +=item B<RedirectCount> B<true|false>
 +
 +The total number of redirections that were actually followed.
 +
 +=item B<SizeUpload> B<true|false>
 +
 +The total amount of bytes that were uploaded.
 +
 +=item B<SizeDownload> B<true|false>
 +
 +The total amount of bytes that were downloaded.
 +
 +=item B<SpeedDownload> B<true|false>
 +
 +The average download speed that curl measured for the complete download.
 +
 +=item B<SpeedUpload> B<true|false>
 +
 +The average upload speed that curl measured for the complete upload.
 +
 +=item B<HeaderSize> B<true|false>
 +
 +The total size of all the headers received.
 +
 +=item B<RequestSize> B<true|false>
 +
 +The total size of the issued requests.
 +
 +=item B<ContentLengthDownload> B<true|false>
 +
 +The content-length of the download.
 +
 +=item B<ContentLengthUpload> B<true|false>
 +
 +The specified size of the upload.
 +
 +=item B<NumConnects> B<true|false>
 +
 +The number of new connections that were created to achieve the transfer.
 +
 +=back
 +
  =head2 Plugin C<curl>
  
  The curl plugin uses the B<libcurl> (L<http://curl.haxx.se/>) to read web pages
@@@ -1684,21 -1534,11 +1684,21 @@@ Beware that requests will get aborted i
  B<Timeout> accordingly if you expect B<MeasureResponseTime> to report such slow
  requests.
  
 +This option is similar to enabling the B<TotalTime> statistic but it's
 +measured by collectd instead of cURL.
 +
  =item B<MeasureResponseCode> B<true>|B<false>
  
  Measure response code for the request. If this setting is enabled, B<Match>
  blocks (see below) are optional. Disabled by default.
  
 +=item B<E<lt>StatisticsE<gt>>
 +
 +One B<Statistics> block can be used to specify cURL statistics to be collected
 +for each request to the remote web site. See the section "cURL Statistics"
 +above for details. If this setting is enabled, B<Match> blocks (see below) are
 +optional.
 +
  =item B<E<lt>MatchE<gt>>
  
  One or more B<Match> blocks that define how to match information in the data
@@@ -1781,11 -1621,6 +1781,11 @@@ The following options are valid within 
  
  =over 4
  
 +=item B<Host> I<Name>
 +
 +Use I<Name> as the host name when submitting values. Defaults to the global
 +host name setting.
 +
  =item B<Instance> I<Instance>
  
  Sets the plugin instance to I<Instance>.
@@@ -1816,12 -1651,6 +1816,12 @@@ URL. By default the global B<Interval> 
  These options behave exactly equivalent to the appropriate options of the
  I<cURL> plugin. Please see there for a detailed description.
  
 +=item B<E<lt>StatisticsE<gt>>
 +
 +One B<Statistics> block can be used to specify cURL statistics to be collected
 +for each request to the remote URL. See the section "cURL Statistics" above
 +for details.
 +
  =back
  
  The following options are valid within B<Key> blocks:
@@@ -1924,12 -1753,6 +1924,12 @@@ Examples
  These options behave exactly equivalent to the appropriate options of the
  I<cURL plugin>. Please see there for a detailed description.
  
 +=item B<E<lt>StatisticsE<gt>>
 +
 +One B<Statistics> block can be used to specify cURL statistics to be collected
 +for each request to the remote URL. See the section "cURL Statistics" above
 +for details.
 +
  =item E<lt>B<XPath> I<XPath-expression>E<gt>
  
  Within each B<URL> block, there must be one or more B<XPath> blocks. Each
@@@ -2002,7 -1825,6 +2002,7 @@@ than those of other plugins. It usuall
      </Query>
      <Database "product_information">
        Driver "mysql"
 +      Interval 120
        DriverOption "host" "localhost"
        DriverOption "username" "collectd"
        DriverOption "password" "aZo6daiw"
@@@ -2182,11 -2004,6 +2182,11 @@@ the daemon. Other than that, that name 
  
  =over 4
  
 +=item B<Interval> I<Interval>
 +
 +Sets the interval (in seconds) in which the values will be collected from this
 +database. By default the global B<Interval> setting will be used.
 +
  =item B<Driver> I<Driver>
  
  Specifies the driver to use to connect to the database. In many cases those
@@@ -2222,7 -2039,7 +2222,7 @@@ is passed to them, so invalid settings 
  plugin's fault, it will report errors if it gets them from the libraryE<nbsp>/
  the driver. If a driver complains about an option, the plugin will dump a
  complete list of all options understood by that driver to the log. There is no
- way to programatically find out if an option expects a string or a numeric
+ way to programmatically find out if an option expects a string or a numeric
  argument, so you will have to refer to the appropriate DBD's documentation to
  find this out. Sorry.
  
@@@ -2427,7 -2244,7 +2427,7 @@@ I<TypeInstance> will be used
  
  =item B<MappedOnly> B<true>|B<false>
  
 -When set to B<true>, only metrics that can be mapped to to a I<type> will be
 +When set to B<true>, only metrics that can be mapped to a I<type> will be
  collected, all other metrics will be ignored. Defaults to B<false>.
  
  =back
@@@ -2657,145 -2474,6 +2657,145 @@@ source, this is optional. Otherwise th
  
  =back
  
 +=head2 Plugin C<gps>
 +
 +The C<gps plugin> connects to gpsd on the host machine.
 +The host, port, timeout and pause are configurable.
 +
 +This is useful if you run an NTP server using a GPS for source and you want to
 +monitor it.
 +
 +Mind your GPS must send $--GSA for having the data reported!
 +
 +The following elements are collected:
 +
 +=over 4
 +
 +=item B<satellites>
 +
 +Number of satellites used for fix (type instance "used") and in view (type
 +instance "visible"). 0 means no GPS satellites are visible.
 +
 +=item B<dilution_of_precision>
 +
 +Vertical and horizontal dilution (type instance "horizontal" or "vertical").
 +It should be between 0 and 3.
 +Look at the documentation of your GPS to know more.
 +
 +=back
 +
 +Synopsis:
 +
 + LoadPlugin gps
 + <Plugin "gps">
 +   # Connect to localhost on gpsd regular port:
 +   Host "127.0.0.1"
 +   Port "2947"
 +   # 15 ms timeout
 +   Timeout 0.015
 +   # PauseConnect of 5 sec. between connection attempts.
 +   PauseConnect 5
 + </Plugin>
 +
 +Available configuration options:
 +
 +=over 4
 +
 +=item B<Host> I<Host>
 +
 +The host on which gpsd daemon runs. Defaults to B<localhost>.
 +
 +=item B<Port> I<Port>
 +
 +Port to connect to gpsd on the host machine. Defaults to B<2947>.
 +
 +=item B<Timeout> I<Seconds>
 +
 +Timeout in seconds (default 0.015 sec).
 +
 +The GPS data stream is fetch by the plugin form the daemon.
 +It waits for data to be available, if none arrives it times out
 +and loop for another reading.
 +Mind to put a low value gpsd expects value in the micro-seconds area
 +(recommended is 500 us) since the waiting function is blocking.
 +Value must be between 500 us and 5 sec., if outside that range the
 +default value is applied.
 +
 +This only applies from gpsd release-2.95.
 +
 +=item B<PauseConnect> I<Seconds>
 +
 +Pause to apply between attempts of connection to gpsd in seconds (default 5 sec).
 +
 +=back
 +
 +=head2 Plugin C<grpc>
 +
 +The I<grpc> plugin provides an RPC interface to submit values to or query
 +values from collectd based on the open source gRPC framework. It exposes an
 +end-point for dispatching values to the daemon.
 +
 +The B<gRPC> homepage can be found at L<https://grpc.io/>.
 +
 +=over 4
 +
 +=item B<Server> I<Host> I<Port>
 +
 +The B<Server> statement sets the address of a server to which to send metrics
 +via the C<DispatchValues> function.
 +
 +The argument I<Host> may be a hostname, an IPv4 address, or an IPv6 address.
 +
 +Optionally, B<Server> may be specified as a configuration block which supports
 +the following options:
 +
 +=over 4
 +
 +=item B<EnableSSL> B<false>|B<true>
 +
 +Whether to require SSL for outgoing connections. Default: false.
 +
 +=item B<SSLCACertificateFile> I<Filename>
 +
 +=item B<SSLCertificateFile> I<Filename>
 +
 +=item B<SSLCertificateKeyFile> I<Filename>
 +
 +Filenames specifying SSL certificate and key material to be used with SSL
 +connections.
 +
 +=back
 +
 +=item B<Listen> I<Host> I<Port>
 +
 +The B<Listen> statement sets the network address to bind to. When multiple
 +statements are specified, the daemon will bind to all of them. If none are
 +specified, it defaults to B<0.0.0.0:50051>.
 +
 +The argument I<Host> may be a hostname, an IPv4 address, or an IPv6 address.
 +
 +Optionally, B<Listen> may be specified as a configuration block which
 +supports the following options:
 +
 +=over 4
 +
 +=item B<EnableSSL> I<true>|I<false>
 +
 +Whether to enable SSL for incoming connections. Default: false.
 +
 +=item B<SSLCACertificateFile> I<Filename>
 +
 +=item B<SSLCertificateFile> I<Filename>
 +
 +=item B<SSLCertificateKeyFile> I<Filename>
 +
 +Filenames specifying SSL certificate and key material to be used with SSL
 +connections.
 +
 +=back
 +
 +=back
 +
  =head2 Plugin C<hddtemp>
  
  To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
@@@ -2855,29 -2533,6 +2855,29 @@@ This will ignore the loopback interface
  with I<veth> and all interfaces with names starting with I<tun> followed by
  at least one digit.
  
 +=item B<ReportInactive> I<true>|I<false>
 +
 +When set to I<false>, only interfaces with non-zero traffic will be
 +reported. Note that the check is done by looking into whether a
 +package was sent at any time from boot and the corresponding counter
 +is non-zero. So, if the interface has been sending data in the past
 +since boot, but not during the reported time-interval, it will still
 +be reported.
 +
 +The default value is I<true> and results in collection of the data
 +from all interfaces that are selected by B<Interface> and
 +B<IgnoreSelected> options.
 +
 +=item B<UniqueName> I<true>|I<false>
 +
 +Interface name is not unique on Solaris (KSTAT), interface name is unique
 +only within a module/instance. Following tuple is considered unique:
 +   (ks_module, ks_instance, ks_name)
 +If this option is set to true, interface name contains above three fields
 +separated by an underscore. For more info on KSTAT, visit
 +L<http://docs.oracle.com/cd/E23824_01/html/821-1468/kstat-3kstat.html#REFMAN3Ekstat-3kstat>
 +
 +This option is only available on Solaris.
  
  =back
  
@@@ -3121,12 -2776,6 +3121,12 @@@ Defaults to false
  
  =back
  
 +=head2 Plugin C<lua>
 +
 +This plugin embeds a Lua interpreter into collectd and provides an interface
 +to collectd's plugin system. See L<collectd-lua(5)> for its documentation.
 +
 +
  =head2 Plugin C<mbmon>
  
  The C<mbmon plugin> uses mbmon to retrieve temperature, voltage, etc.
@@@ -3605,146 -3254,6 +3605,146 @@@ B<Collect> option is mandatory
  
  =back
  
 +=head2 Plugin C<mqtt>
 +
 +The I<MQTT plugin> can send metrics to MQTT (B<Publish> blocks) and receive
 +values from MQTT (B<Subscribe> blocks).
 +
 +B<Synopsis:>
 +
 + <Plugin mqtt>
 +   <Publish "name">
 +     Host "mqtt.example.com"
 +     Prefix "collectd"
 +   </Publish>
 +   <Subscribe "name">
 +     Host "mqtt.example.com"
 +     Topic "collectd/#"
 +   </Subscribe>
 + </Plugin>
 +
 +The plugin's configuration is in B<Publish> and/or B<Subscribe> blocks,
 +configuring the sending and receiving direction respectively. The plugin will
 +register a write callback named C<mqtt/I<name>> where I<name> is the string
 +argument given to the B<Publish> block. Both types of blocks share many but not
 +all of the following options. If an option is valid in only one of the blocks,
 +it will be mentioned explicitly.
 +
 +B<Options:>
 +
 +=over 4
 +
 +=item B<Host> I<Hostname>
 +
 +Hostname of the MQTT broker to connect to.
 +
 +=item B<Port> I<Service>
 +
 +Port number or service name of the MQTT broker to connect to.
 +
 +=item B<User> I<UserName>
 +
 +Username used when authenticating to the MQTT broker.
 +
 +=item B<Password> I<Password>
 +
 +Password used when authenticating to the MQTT broker.
 +
 +=item B<ClientId> I<ClientId>
 +
 +MQTT client ID to use. Defaults to the hostname used by I<collectd>.
 +
 +=item B<QoS> [B<0>-B<2>]
 +
 +Sets the I<Quality of Service>, with the values C<0>, C<1> and C<2> meaning:
 +
 +=over 4
 +
 +=item B<0>
 +
 +At most once
 +
 +=item B<1>
 +
 +At least once
 +
 +=item B<2>
 +
 +Exactly once
 +
 +=back
 +
 +In B<Publish> blocks, this option determines the QoS flag set on outgoing
 +messages and defaults to B<0>. In B<Subscribe> blocks, determines the maximum
 +QoS setting the client is going to accept and defaults to B<2>. If the QoS flag
 +on a message is larger than the maximum accepted QoS of a subscriber, the
 +message's QoS will be downgraded.
 +
 +=item B<Prefix> I<Prefix> (Publish only)
 +
 +This plugin will use one topic per I<value list> which will looks like a path.
 +I<Prefix> is used as the first path element and defaults to B<collectd>.
 +
 +An example topic name would be:
 +
 + collectd/cpu-0/cpu-user
 +
 +=item B<Retain> B<false>|B<true> (Publish only)
 +
 +Controls whether the MQTT broker will retain (keep a copy of) the last message
 +sent to each topic and deliver it to new subscribers. Defaults to B<false>.
 +
 +=item B<StoreRates> B<true>|B<false> (Publish only)
 +
 +Controls whether C<DERIVE> and C<COUNTER> metrics are converted to a I<rate>
 +before sending. Defaults to B<true>.
 +
 +=item B<CleanSession> B<true>|B<false> (Subscribe only)
 +
 +Controls whether the MQTT "cleans" the session up after the subscriber
 +disconnects or if it maintains the subscriber's subscriptions and all messages
 +that arrive while the subscriber is disconnected. Defaults to B<true>.
 +
 +=item B<Topic> I<TopicName> (Subscribe only)
 +
 +Configures the topic(s) to subscribe to. You can use the single level C<+> and
 +multi level C<#> wildcards. Defaults to B<collectd/#>, i.e. all topics beneath
 +the B<collectd> branch.
 +
 +=item B<CACert> I<file>
 +
 +Path to the PEM-encoded CA certificate file. Setting this option enables TLS
 +communication with the MQTT broker, and as such, B<Port> should be the TLS-enabled
 +port of the MQTT broker.
 +A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
 +
 +=item B<CertificateFile> I<file>
 +
 +Path to the PEM-encoded certificate file to use as client certificate when
 +connecting to the MQTT broker.
 +A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
 +
 +=item B<CertificateKeyFile> I<file>
 +
 +Path to the unencrypted PEM-encoded key file corresponding to B<CertificateFile>.
 +A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
 +
 +=item B<TLSProtocol> I<protocol>
 +
 +If configured, this specifies the string protocol version (e.g. C<tlsv1>,
 +C<tlsv1.2>) to use for the TLS connection to the broker. If not set a default
 +version is used which depends on the version of OpenSSL the Mosquitto library
 +was linked against.
 +
 +=item B<CipherSuite> I<ciphersuite>
 +
 +A string describing the ciphers available for use. See L<ciphers(1)> and the
 +C<openssl ciphers> utility for more information. If unset, the default ciphers
 +will be used.
 +
 +
 +=back
 +
  =head2 Plugin C<mysql>
  
  The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to
@@@ -3778,11 -3287,6 +3778,11 @@@ Synopsis
        Port "3306"
        MasterStats true
        ConnectTimeout 10
 +      SSLKey "/path/to/key.pem"
 +      SSLCert "/path/to/cert.pem"
 +      SSLCA "/path/to/ca.pem"
 +      SSLCAPath "/path/to/cas/"
 +      SSLCipher "DHE-RSA-AES256-SHA"
      </Database>
  
      <Database bar>
        SlaveStats true
        SlaveNotifications true
      </Database>
 +
 +   <Database galera>
 +      Alias "galera"
 +      Host "localhost"
 +      Socket "/var/run/mysql/mysqld.sock"
 +      WsrepStats true
 +   </Database>
    </Plugin>
  
  A B<Database> block defines one connection to a MySQL database. It accepts a
  single argument which specifies the name of the database. None of the other
  options are required. MySQL will use default values as documented in the
 -section "mysql_real_connect()" in the B<MySQL reference manual>.
 +"mysql_real_connect()" and "mysql_ssl_set()" sections in the
 +B<MySQL reference manual>.
  
  =over 4
  
@@@ -3870,36 -3366,10 +3870,36 @@@ privileges. See the B<User> documentati
  If enabled, the plugin sends a notification if the replication slave I/O and /
  or SQL threads are not running. Defaults to B<false>.
  
 +=item B<WsrepStats> I<true|false>
 + 
 + Enable the collection of wsrep plugin statistics, used in Master-Master
 + replication setups like in MySQL Galera/Percona XtraDB Cluster.
 + User needs only privileges to execute 'SHOW GLOBAL STATUS'
 + 
  =item B<ConnectTimeout> I<Seconds>
  
  Sets the connect timeout for the MySQL client.
  
 +=item B<SSLKey> I<Path>
 +
 +If provided, the X509 key in PEM format.
 +
 +=item B<SSLCert> I<Path>
 +
 +If provided, the X509 cert in PEM format.
 +
 +=item B<SSLCA> I<Path>
 +
 +If provided, the CA file in PEM format (check OpenSSL docs).
 +
 +=item B<SSLCAPath> I<Path>
 +
 +If provided, the CA directory (check OpenSSL docs).
 +
 +=item B<SSLCipher> I<String>
 +
 +If provided, the SSL cipher to use.
 +
  =back
  
  =head2 Plugin C<netapp>
@@@ -4839,21 -4309,6 +4839,21 @@@ Default: C<Collectd notify: %s@%s
  
  =back
  
 +=head2 Plugin C<notify_nagios>
 +
 +The I<notify_nagios> plugin writes notifications to Nagios' I<command file> as
 +a I<passive service check result>.
 +
 +Available configuration options:
 +
 +=over 4
 +
 +=item B<CommandFile> I<Path>
 +
 +Sets the I<command file> to write to. Defaults to F</usr/local/nagios/var/rw/nagios.cmd>.
 +
 +=back
 +
  =head2 Plugin C<ntpd>
  
  The C<ntpd> plugin collects per-peer ntp data such as time offset and time
@@@ -5091,16 -4546,6 +5091,16 @@@ The following options are accepted with
  Sets the URL to use to connect to the I<OpenLDAP> server. This option is
  I<mandatory>.
  
 +=item B<BindDN> I<BindDN>
 +
 +Name in the form of an LDAP distinguished name intended to be used for
 +authentication. Defaults to empty string to establish an anonymous authorization.
 +
 +=item B<Password> I<Password>
 +
 +Password for simple bind authentication. If this option is not set,
 +unauthenticated bind operation is used.
 +
  =item B<StartTLS> B<true|false>
  
  Defines whether TLS must be used when connecting to the I<OpenLDAP> server.
@@@ -5122,9 -4567,8 +5122,9 @@@ client configuration mechanisms. See ld
  
  =item B<Timeout> I<Seconds>
  
 -Sets the timeout value for ldap operations. Defaults to B<-1> which results in
 -an infinite timeout.
 +Sets the timeout value for ldap operations, in seconds. By default, the
 +configured B<Interval> is used to set the timeout. Use B<-1> to disable
 +(infinite timeout).
  
  =item B<Version> I<Version>
  
@@@ -5373,13 -4817,6 +5373,13 @@@ Default: B<0.9
  
  Sets the Time-To-Live of generated ICMP packets.
  
 +=item B<Size> I<size>
 +
 +Sets the size of the data payload in ICMP packet to specified I<size> (it
 +will be filled with regular ASCII pattern). If not set, default 56 byte
 +long string is used so that the packet size of an ICMPv4 packet is exactly
 +64 bytes, similar to the behaviour of normal ping(1) command.
 +
  =item B<SourceAddress> I<host>
  
  Sets the source address to use. I<host> may either be a numerical network
@@@ -5487,12 -4924,13 +5487,12 @@@ The B<Query> block defines one databas
  database definition. It accepts a single mandatory argument which specifies
  the name of the query. The names of all queries have to be unique (see the
  B<MinVersion> and B<MaxVersion> options below for an exception to this
 -rule). The following configuration options are available to define the query:
 +rule).
  
 -In each B<Query> block, there is one or more B<Result> blocks. B<Result>
 -blocks define how to handle the values returned from the query. They define
 -which column holds which value and how to dispatch that value to the daemon.
 -Multiple B<Result> blocks may be used to extract multiple values from a single
 -query.
 +In each B<Query> block, there is one or more B<Result> blocks. Multiple
 +B<Result> blocks may be used to extract multiple values from a single query.
 +
 +The following configuration options are available to define the query:
  
  =over 4
  
@@@ -5510,7 -4948,7 +5510,7 @@@ allowed as long as a single non-empty c
  
  The returned lines will be handled separately one after another.
  
 -=item B<Param> I<hostname>|I<database>|I<username>|I<interval>
 +=item B<Param> I<hostname>|I<database>|I<instance>|I<username>|I<interval>
  
  Specify the parameters which should be passed to the SQL query. The parameters
  are referred to in the SQL query as B<$1>, B<$2>, etc. in the same order as
@@@ -5547,34 -4985,6 +5547,34 @@@ specific or global B<Interval> options)
  Please note that parameters are only supported by PostgreSQL's protocol
  version 3 and above which was introduced in version 7.4 of PostgreSQL.
  
 +=item B<PluginInstanceFrom> I<column>
 +
 +Specify how to create the "PluginInstance" for reporting this query results.
 +Only one column is supported. You may concatenate fields and string values in
 +the query statement to get the required results.
 +
 +=item B<MinVersion> I<version>
 +
 +=item B<MaxVersion> I<version>
 +
 +Specify the minimum or maximum version of PostgreSQL that this query should be
 +used with. Some statistics might only be available with certain versions of
 +PostgreSQL. This allows you to specify multiple queries with the same name but
 +which apply to different versions, thus allowing you to use the same
 +configuration in a heterogeneous environment.
 +
 +The I<version> has to be specified as the concatenation of the major, minor
 +and patch-level versions, each represented as two-decimal-digit numbers. For
 +example, version 8.2.3 will become 80203.
 +
 +=back
 +
 +The B<Result> block defines how to handle the values returned from the query.
 +It defines which column holds which value and how to dispatch that value to
 +the daemon.
 +
 +=over 4
 +
  =item B<Type> I<type>
  
  The I<type> name to be used when dispatching the values. The type describes
@@@ -5582,7 -4992,7 +5582,7 @@@ how to handle the data and where to sto
  details on types and their configuration. The number and type of values (as
  selected by the B<ValuesFrom> option) has to match the type of the given name.
  
 -This option is required inside a B<Result> block.
 +This option is mandatory.
  
  =item B<InstancePrefix> I<prefix>
  
@@@ -5618,6 -5028,20 +5618,6 @@@ This option is required inside a B<Resu
  times. If multiple B<ValuesFrom> options are specified, the columns are read
  in the given order.
  
 -=item B<MinVersion> I<version>
 -
 -=item B<MaxVersion> I<version>
 -
 -Specify the minimum or maximum version of PostgreSQL that this query should be
 -used with. Some statistics might only be available with certain versions of
 -PostgreSQL. This allows you to specify multiple queries with the same name but
 -which apply to different versions, thus allowing you to use the same
 -configuration in a heterogeneous environment.
 -
 -The I<version> has to be specified as the concatenation of the major, minor
 -and patch-level versions, each represented as two-decimal-digit numbers. For
 -example, version 8.2.3 will become 80203.
 -
  =back
  
  The following predefined queries are available (the definitions can be found
@@@ -5785,10 -5209,8 +5785,10 @@@ transaction fails or if the database se
  
  Specify the plugin instance name that should be used instead of the database
  name (which is the default, if this option has not been specified). This
- allows to query multiple databases of the same name on the same host (e.g.
+ allows one to query multiple databases of the same name on the same host (e.g.
  when running multiple database server versions in parallel).
 +The plugin instance name can also be set from the query result using
 +the B<PluginInstanceFrom> option in B<Query> block.
  
  =item B<Host> I<hostname>
  
@@@ -5848,7 -5270,7 +5848,7 @@@ Use SSL only
  
  Specify the plugin instance name that should be used instead of the database
  name (which is the default, if this option has not been specified). This
- allows to query multiple databases of the same name on the same host (e.g.
+ allows one to query multiple databases of the same name on the same host (e.g.
  when running multiple database server versions in parallel).
  
  =item B<KRBSrvName> I<kerberos_service_name>
@@@ -6048,17 -5470,13 +6048,17 @@@ below this limit
  
  =item B<ProcessMatch> I<name> I<regex>
  
- Similar to the B<Process> option this allows to select more detailed
+ Similar to the B<Process> option this allows one to select more detailed
  statistics of processes matching the specified I<regex> (see L<regex(7)> for
  details). The statistics of all matching processes are summed up and
  dispatched to the daemon using the specified I<name> as an identifier. This
- allows to "group" several processes together. I<name> must not contain
+ allows one to "group" several processes together. I<name> must not contain
  slashes.
  
 +=item B<CollectContextSwitch> I<Boolean>
 +
 +Collect context switch of the process.
 +
  =back
  
  =head2 Plugin C<protocols>
@@@ -6267,8 -5685,8 +6267,8 @@@ C<collectd> anymore, it does not need t
  restarted. This results in much shorter (if any) gaps in graphs, especially
  under heavy load. Also, the C<rrdtool> command line utility is aware of the
  daemon so that it can flush values to disk automatically when needed. This
- allows to integrate automated flushing of values into graphing solutions much
- more easily.
+ allows one to integrate automated flushing of values into graphing solutions
+ much more easily.
  
  There are disadvantages, though: The daemon may reside on a different host, so
  it may not be possible for C<collectd> to create the appropriate RRD files
@@@ -6525,12 -5943,6 +6525,12 @@@ few ones. This option enables you to d
  I<true> the effect of B<Sensor> is inverted: All selected sensors are ignored
  and all other sensors are collected.
  
 +=item B<UseLabels> I<true>|I<false>
 +
 +Configures how sensor readings are reported. When set to I<true>, sensor
 +readings are reported using their descriptive label (e.g. "VCore"). When set to
 +I<false> (the default) the sensor name is used ("in0").
 +
  =back
  
  =head2 Plugin C<sigrok>
@@@ -6633,22 -6045,6 +6633,22 @@@ collected. If at least one B<Disk> opti
  set to B<false>, B<only> matching disks will be collected. If B<IgnoreSelected>
  is set to B<true>, all disks are collected B<except> the ones matched.
  
 +=item B<IgnoreSleepMode> B<true>|B<false>
 +
 +Normally, the C<smart> plugin will ignore disks that are reported to be asleep.
 +This option disables the sleep mode check and allows the plugin to collect data
 +from these disks anyway. This is useful in cases where libatasmart mistakenly
 +reports disks as asleep because it has not been updated to incorporate support
 +for newer idle states in the ATA spec.
 +
 +=item B<UseSerial> B<true>|B<false>
 +
 +A disk's kernel name (e.g., sda) can change from one boot to the next. If this
 +option is enabled, the C<smart> plugin will use the disk's serial number (e.g.,
 +HGST_HUH728080ALE600_2EJ8VH8X) instead of the kernel name as the key for
 +storing data. This ensures that the data for a given disk will be kept together
 +even if the kernel name changes.
 +
  =back
  
  =head2 Plugin C<snmp>
@@@ -6695,12 -6091,6 +6695,12 @@@ rate of counters and size of sets will 
  are unchanged. If set to B<True>, the such metrics are not dispatched and
  removed from the internal cache.
  
 +=item B<CounterSum> B<false>|B<true>
 +
 +When enabled, creates a C<count> metric which reports the change since the last
 +read. This option primarily exists for compatibility with the I<statsd>
 +implementation by Etsy.
 +
  =item B<TimerPercentile> I<Percent>
  
  Calculate and dispatch the configured percentile, i.e. compute the latency, so
@@@ -7214,7 -6604,7 +7214,7 @@@ port in numeric form
  =item B<AllPortsSummary> I<true>|I<false>
  
  If this option is set to I<true> a summary of statistics from all connections
 -are collectd. This option defaults to I<false>.
 +are collected. This option defaults to I<false>.
  
  =back
  
@@@ -7309,13 -6699,13 +7309,13 @@@ fails or if you want to disable this fe
  =item B<DigitalTemperatureSensor> I<true>|I<false>
  
  Boolean enabling the collection of the temperature of each core.
 -This option should only be used if the automated detectionfails or 
 +This option should only be used if the automated detectionfails or
  if you want to disable this feature.
  
  =item B<DigitalTemperatureSensor> I<true>|I<false>
  
  Boolean enabling the collection of the temperature of each package.
 -This option should only be used if the automated detectionfails or 
 +This option should only be used if the automated detectionfails or
  if you want to disable this feature.
  
  =item B<TCCActivationTemp> I<Temperature>
@@@ -7655,17 -7045,17 +7655,17 @@@ setting B<name>
  B<address> means use the interface's mac address. This is useful since the
  interface path might change between reboots of a guest or across migrations.
  
 -=item B<PluginInstanceFormat> B<name|uuid>
 +=item B<PluginInstanceFormat> B<name|uuid|none>
  
  When the virt plugin logs data, it sets the plugin_instance of the collected
 -data according to this setting. The default is to use the guest name as provided
 -by the hypervisor, which is equal to setting B<name>.
 +data according to this setting. The default is to not set the plugin_instance.
  
 +B<name> means use the guest's name as provided by the hypervisor.
  B<uuid> means use the guest's UUID.
  
 -You can also specify combinations of these fields. For example B<name uuid>
 -means to concatenate the guest name and UUID (with a literal colon character
 -between, thus I<"foo:1234-1234-1234-1234">).
 +You can also specify combinations of the B<name> and B<uuid> fields.
 +For example B<name uuid> means to concatenate the guest name and UUID
 +(with a literal colon character between, thus I<"foo:1234-1234-1234-1234">).
  
  =back
  
@@@ -7740,14 -7130,6 +7740,14 @@@ Service name or port number to connect 
  
  Protocol to use when connecting to I<Graphite>. Defaults to C<tcp>.
  
 +=item B<ReconnectInterval> I<Seconds>
 +
 +When set to non-zero, forces the connection to the Graphite backend to be
 +closed and re-opend periodically. This behavior is desirable in environments
 +where the connection to the Graphite backend is done through load balancers,
 +for example. When set to zero, the default, the connetion is kept open for as
 +long as possible.
 +
  =item B<LogSendErrors> B<false>|B<true>
  
  If set to B<true> (the default), logs errors when sending data to I<Graphite>.
@@@ -7974,35 -7356,20 +7974,35 @@@ authentication
  
  Password required to load the private key in B<ClientKey>.
  
 +=item B<Header> I<Header>
 +
 +A HTTP header to add to the request.  Multiple headers are added if this option is specified more than once.  Example:
 +
 +  Header "X-Custom-Header: custom_value"
 +
  =item B<SSLVersion> B<SSLv2>|B<SSLv3>|B<TLSv1>|B<TLSv1_0>|B<TLSv1_1>|B<TLSv1_2>
  
  Define which SSL protocol version must be used. By default C<libcurl> will
  attempt to figure out the remote SSL protocol version. See
  L<curl_easy_setopt(3)> for more details.
  
 -=item B<Format> B<Command>|B<JSON>
 +=item B<Format> B<Command>|B<JSON>|B<KAIROSDB>
  
  Format of the output to generate. If set to B<Command>, will create output that
  is understood by the I<Exec> and I<UnixSock> plugins. When set to B<JSON>, will
 -create output in the I<JavaScript Object Notation> (JSON).
 +create output in the I<JavaScript Object Notation> (JSON). When set to KAIROSDB
 +, will create output in the KairosDB format.
  
  Defaults to B<Command>.
  
 +=item B<Metrics> B<true>|B<false>
 +
 +Controls whether I<metrics> are POSTed to this location. Defaults to B<true>.
 +
 +=item B<Notifications> B<false>|B<true>
 +
 +Controls whether I<notifications> are POSTed to this location. Defaults to B<false>.
 +
  =item B<StoreRates> B<true|false>
  
  If set to B<true>, convert counter values to rates. If set to B<false> (the
@@@ -8031,10 -7398,6 +8031,10 @@@ complete. When this limit is reached, t
  all the data in the current send buffer will probably be lost. Defaults to 0,
  which means the connection never times out.
  
 +=item B<LogHttpError> B<false>|B<true>
 +
 +Enables printing of HTTP error code to log. Turned off by default.
 +
  The C<write_http> plugin regularly submits the collected values to the HTTP
  server. How frequently this happens depends on how much data you are collecting
  and the size of B<BufferSize>. The optimal value to set B<Timeout> to is
@@@ -8076,7 -7439,7 +8076,7 @@@ forwarded to the kafka producer librar
  
  =item B<Key> I<String>
  
- Use the specified string as a partioning key for the topic. Kafka breaks
+ Use the specified string as a partitioning key for the topic. Kafka breaks
  topic into partitions and guarantees that for a given topology, the same
  consumer will be used for a specific key. The special (case insensitive)
  string B<Random> can be used to specify that an arbitrary partition should
@@@ -8161,20 -7524,14 +8161,20 @@@ Synopsis
          Host "localhost"
          Port "6379"
          Timeout 1000
 +        Prefix "collectd/"
 +        Database 1
 +        MaxSetSize -1
 +        StoreRates true
      </Node>
    </Plugin>
  
  Values are submitted to I<Sorted Sets>, using the metric name as the key, and
  the timestamp as the score. Retrieving a date range can then be done using the
  C<ZRANGEBYSCORE> I<Redis> command. Additionally, all the identifiers of these
 -I<Sorted Sets> are kept in a I<Set> called C<collectd/values> and can be
 -retrieved using the C<SMEMBERS> I<Redis> command. See
 +I<Sorted Sets> are kept in a I<Set> called C<collectd/values> (or
 +C<${prefix}/values> if the B<Prefix> option was specified) and can be retrieved
 +using the C<SMEMBERS> I<Redis> command. You can specify the database to use
 +with the B<Database> parameter (default is C<0>). See
  L<http://redis.io/commands#sorted_set> and L<http://redis.io/commands#set> for
  details.
  
@@@ -8209,28 -7566,6 +8209,28 @@@ that numerical port numbers must be giv
  
  The B<Timeout> option sets the socket connection timeout, in milliseconds.
  
 +=item B<Prefix> I<Prefix>
 +
 +Prefix used when constructing the name of the I<Sorted Sets> and the I<Set>
 +containing all metrics. Defaults to C<collectd/>, so metrics will have names
 +like C<collectd/cpu-0/cpu-user>. When setting this to something different, it
 +is recommended but not required to include a trailing slash in I<Prefix>.
 +
 +=item B<Database> I<Index>
 +
 +This index selects the redis database to use for writing operations. Defaults
 +to C<0>.
 +
 +=item B<MaxSetSize> I<Items>
 +
 +The B<MaxSetSize> option limits the number of items that the I<Sorted Sets> can
 +hold. Negative values for I<Items> sets no limit, which is the default behavior.
 +
 +=item B<StoreRates> B<true>|B<false>
 +
 +If set to B<true> (the default), convert counter values to rates. If set to
 +B<false> counter values are stored as is, i.e. as an increasing integer number.
 +
  =back
  
  =head2 Plugin C<write_riemann>
@@@ -8275,26 -7610,11 +8275,26 @@@ Hostname or address to connect to. Defa
  
  Service name or port number to connect to. Defaults to C<5555>.
  
 -=item B<Protocol> B<UDP>|B<TCP>
 +=item B<Protocol> B<UDP>|B<TCP>|B<TLS>
  
  Specify the protocol to use when communicating with I<Riemann>. Defaults to
  B<TCP>.
  
 +=item B<TLSCertFile> I<Path>
 +
 +When using the B<TLS> protocol, path to a PEM certificate to present
 +to remote host.
 +
 +=item B<TLSCAFile> I<Path>
 +
 +When using the B<TLS> protocol, path to a PEM CA certificate to
 +use to validate the remote hosts's identity.
 +
 +=item B<TLSKeyFile> I<Path>
 +
 +When using the B<TLS> protocol, path to a PEM private key associated
 +with the certificate defined by B<TLSCertFile>.
 +
  =item B<Batch> B<true>|B<false>
  
  If set to B<true> and B<Protocol> is set to B<TCP>,
@@@ -8314,11 -7634,6 +8314,11 @@@ Defaults to tru
  
  Maximum payload size for a riemann packet. Defaults to 8192
  
 +=item B<BatchFlushTimeout> I<seconds>
 +
 +Maximum amount of seconds to wait in between to batch flushes.
 +No timeout by default.
 +
  =item B<StoreRates> B<true>|B<false>
  
  If set to B<true> (the default), convert counter values to rates. If set to
@@@ -8485,14 -7800,6 +8485,14 @@@ attribute for each metric being sent ou
  
  =back
  
 +=head2 Plugin C<xencpu>
 +
 +This plugin collects metrics of hardware CPU load for machine running Xen
 +hypervisor. Load is calculated from 'idle time' value, provided by Xen.
 +Result is reported using the C<percent> type, for each CPU (core).
 +
 +This plugin doesn't have any options (yet).
 +
  =head2 Plugin C<zookeeper>
  
  The I<zookeeper plugin> will collect statistics from a I<Zookeeper> server
@@@ -9390,8 -8697,6 +9390,8 @@@ Available options
  
  =item B<TypeInstance> I<String>
  
 +=item B<MetaData> I<String> I<String>
 +
  Set the appropriate field to the given string. The strings for plugin instance
  and type instance may be empty, the strings for host and plugin may not be
  empty. It's currently not possible to set the type of a value this way.
diff --combined src/daemon/common.c
  #endif
  
  #include "collectd.h"
 +
  #include "common.h"
  #include "plugin.h"
  #include "utils_cache.h"
  
 -#if HAVE_PTHREAD_H
 -# include <pthread.h>
 -#endif
 -
  #ifdef HAVE_MATH_H
  # include <math.h>
  #endif
  
  /* for getaddrinfo */
  #include <sys/types.h>
 -#include <sys/socket.h>
  #include <netdb.h>
  
  #include <poll.h>
  # include <netinet/in.h>
  #endif
  
 +#if HAVE_NETINET_TCP_H
 +# include <netinet/tcp.h>
 +#endif
 +
  /* for ntohl and htonl */
  #if HAVE_ARPA_INET_H
  # include <arpa/inet.h>
  #endif
  
 +#ifdef HAVE_SYS_CAPABILITY_H
 +# include <sys/capability.h>
 +#endif
 +
  #ifdef HAVE_LIBKSTAT
  extern kstat_ctl_t *kc;
  #endif
  
+ /* AIX doesn't have MSG_DONTWAIT */
+ #ifndef MSG_DONTWAIT
+ #  define MSG_DONTWAIT MSG_NONBLOCK
+ #endif
  #if !HAVE_GETPWNAM_R
  static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
  #endif
@@@ -120,9 -121,10 +125,9 @@@ char *ssnprintf_alloc (char const *form
                return (strdup (static_buffer));
  
        /* Allocate a buffer large enough to hold the string. */
 -      alloc_buffer = malloc (alloc_buffer_size);
 +      alloc_buffer = calloc (1, alloc_buffer_size);
        if (alloc_buffer == NULL)
                return (NULL);
 -      memset (alloc_buffer, 0, alloc_buffer_size);
  
        /* Print again into this new buffer. */
        va_start (ap, format);
@@@ -148,7 -150,7 +153,7 @@@ char *sstrdup (const char *s
        /* Do not use `strdup' here, because it's not specified in POSIX. It's
         * ``only'' an XSI extension. */
        sz = strlen (s) + 1;
 -      r = (char *) malloc (sizeof (char) * sz);
 +      r = malloc (sz);
        if (r == NULL)
        {
                ERROR ("sstrdup: Out of memory.");
@@@ -262,8 -264,8 +267,8 @@@ ssize_t sread (int fd, void *buf, size_
  
                assert ((0 > status) || (nleft >= (size_t)status));
  
 -              nleft = nleft - status;
 -              ptr   = ptr   + status;
 +              nleft = nleft - ((size_t) status);
 +              ptr   = ptr   + ((size_t) status);
        }
  
        return (0);
@@@ -280,9 -282,6 +285,9 @@@ ssize_t swrite (int fd, const void *buf
        ptr   = (const char *) buf;
        nleft = count;
        
 +      if (fd < 0)
 +              return (-1);
 +
        /* checking for closed peer connection */
        pfd.fd = fd;
        pfd.events = POLLIN | POLLHUP;
                if (status < 0)
                        return (status);
  
 -              nleft = nleft - status;
 -              ptr   = ptr   + status;
 +              nleft = nleft - ((size_t) status);
 +              ptr   = ptr   + ((size_t) status);
        }
  
        return (0);
@@@ -341,8 -340,9 +346,8 @@@ int strjoin (char *buffer, size_t buffe
        size_t avail;
        char *ptr;
        size_t sep_len;
 -      size_t i;
  
 -      if ((buffer_size < 1) || (fields_num <= 0))
 +      if ((buffer_size < 1) || (fields_num == 0))
                return (-1);
  
        memset (buffer, 0, buffer_size);
        if (sep != NULL)
                sep_len = strlen (sep);
  
 -      for (i = 0; i < fields_num; i++)
 +      for (size_t i = 0; i < fields_num; i++)
        {
                size_t field_len;
  
        }
  
        assert (buffer[buffer_size - 1] == 0);
 -      return (strlen (buffer));
 +      return ((int) strlen (buffer));
  }
  
 -int strsubstitute (char *str, char c_from, char c_to)
 -{
 -      int ret;
 -
 -      if (str == NULL)
 -              return (-1);
 -
 -      ret = 0;
 -      while (*str != '\0')
 -      {
 -              if (*str == c_from)
 -              {
 -                      *str = c_to;
 -                      ret++;
 -              }
 -              str++;
 -      }
 -
 -      return (ret);
 -} /* int strsubstitute */
 -
  int escape_string (char *buffer, size_t buffer_size)
  {
    char *temp;
 -  size_t i;
    size_t j;
  
    /* Check if we need to escape at all first */
    if (buffer_size < 3)
      return (EINVAL);
  
 -  temp = (char *) malloc (buffer_size);
 +  temp = calloc (1, buffer_size);
    if (temp == NULL)
      return (ENOMEM);
 -  memset (temp, 0, buffer_size);
  
    temp[0] = '"';
    j = 1;
  
 -  for (i = 0; i < buffer_size; i++)
 +  for (size_t i = 0; i < buffer_size; i++)
    {
      if (buffer[i] == 0)
      {
  
  int strunescape (char *buf, size_t buf_len)
  {
 -      size_t i;
 -
 -      for (i = 0; (i < buf_len) && (buf[i] != '\0'); ++i)
 +      for (size_t i = 0; (i < buf_len) && (buf[i] != '\0'); ++i)
        {
                if (buf[i] != '\\')
                        continue;
@@@ -487,6 -512,7 +492,6 @@@ size_t strstripnewline (char *buffer
  
  int escape_slashes (char *buffer, size_t buffer_size)
  {
 -      int i;
        size_t buffer_len;
  
        buffer_len = strlen (buffer);
                buffer_len--;
        }
  
 -      for (i = 0; i < buffer_len; i++)
 +      for (size_t i = 0; i < buffer_len; i++)
        {
                if (buffer[i] == '/')
                        buffer[i] = '_';
  
  void replace_special (char *buffer, size_t buffer_size)
  {
 -      size_t i;
 -
 -      for (i = 0; i < buffer_size; i++)
 +      for (size_t i = 0; i < buffer_size; i++)
        {
                if (buffer[i] == 0)
                        return;
@@@ -594,6 -622,7 +599,6 @@@ int check_create_dir (const char *file_
        int   last_is_file = 1;
        int   path_is_absolute = 0;
        size_t len;
 -      int   i;
  
        /*
         * Sanity checks first
        /*
         * For each component, do..
         */
 -      for (i = 0; i < (fields_num - last_is_file); i++)
 +      for (int i = 0; i < (fields_num - last_is_file); i++)
        {
                /*
                 * Do not create directories that start with a dot. This
                 * Join the components together again
                 */
                dir[0] = '/';
 -              if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
 -                                      fields, i + 1, "/") < 0)
 +              if (strjoin (dir + path_is_absolute, (size_t) (dir_len - path_is_absolute),
 +                                      fields, (size_t) (i + 1), "/") < 0)
                {
                        ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
                        return (-1);
@@@ -938,6 -967,7 +943,6 @@@ int format_values (char *ret, size_t re
  {
          size_t offset = 0;
          int status;
 -        int i;
          gauge_t *rates = NULL;
  
          assert (0 == strcmp (ds->type, vl->type));
  
          BUFFER_ADD ("%.3f", CDTIME_T_TO_DOUBLE (vl->time));
  
 -        for (i = 0; i < ds->ds_num; i++)
 +        for (size_t i = 0; i < ds->ds_num; i++)
          {
                  if (ds->ds[i].type == DS_TYPE_GAUGE)
                          BUFFER_ADD (":"GAUGE_FORMAT, vl->values[i].gauge);
@@@ -1140,7 -1170,7 +1145,7 @@@ int parse_value (const char *value_orig
  
  int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
  {
 -      int i;
 +      size_t i;
        char *dummy;
        char *ptr;
        char *saveptr;
        if ((buffer == NULL) || (vl == NULL) || (ds == NULL))
                return EINVAL;
  
 -      i = -1;
 +      i = 0;
        dummy = buffer;
        saveptr = NULL;
 +      vl->time = 0;
        while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
        {
                dummy = NULL;
                if (i >= vl->values_len)
                {
                        /* Make sure i is invalid. */
 -                      i = vl->values_len + 1;
 +                      i = 0;
                        break;
                }
  
 -              if (i == -1)
 +              if (vl->time == 0)
                {
                        if (strcmp ("N", ptr) == 0)
                                vl->time = cdtime ();
  
                                vl->time = DOUBLE_TO_CDTIME_T (tmp);
                        }
 +
 +                      continue;
                }
 -              else
 -              {
 -                      if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
 -                              vl->values[i].gauge = NAN;
 -                      else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type))
 -                              return -1;
 -              }
 +
 +              if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
 +                      vl->values[i].gauge = NAN;
 +              else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type))
 +                      return -1;
  
                i++;
        } /* while (strtok_r) */
  
 -      if ((ptr != NULL) || (i != vl->values_len))
 +      if ((ptr != NULL) || (i == 0))
                return (-1);
        return (0);
  } /* int parse_values */
@@@ -1356,9 -1385,10 +1361,9 @@@ counter_t counter_diff (counter_t old_v
        if (old_value > new_value)
        {
                if (old_value <= 4294967295U)
 -                      diff = (4294967295U - old_value) + new_value;
 +                      diff = (4294967295U - old_value) + new_value + 1;
                else
 -                      diff = (18446744073709551615ULL - old_value)
 -                              + new_value;
 +                      diff = (18446744073709551615ULL - old_value) + new_value + 1;
        }
        else
        {
@@@ -1463,10 -1493,11 +1468,10 @@@ int rate_to_value (value_t *ret_value, 
        return (0);
  } /* }}} value_t rate_to_value */
  
 -int value_to_rate (value_t *ret_rate, derive_t value, /* {{{ */
 -              value_to_rate_state_t *state,
 -              int ds_type, cdtime_t t)
 +int value_to_rate (gauge_t *ret_rate, /* {{{ */
 +              value_t value, int ds_type, cdtime_t t, value_to_rate_state_t *state)
  {
 -      double interval;
 +      gauge_t interval;
  
        /* Another invalid state: The time is not increasing. */
        if (t <= state->last_time)
        interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
  
        /* Previous value is invalid. */
 -      if (state->last_time == 0) /* {{{ */
 +      if (state->last_time == 0)
        {
 -              if (ds_type == DS_TYPE_DERIVE)
 -              {
 -                      state->last_value.derive = value;
 -              }
 -              else if (ds_type == DS_TYPE_COUNTER)
 -              {
 -                      state->last_value.counter = (counter_t) value;
 -              }
 -              else if (ds_type == DS_TYPE_ABSOLUTE)
 -              {
 -                      state->last_value.absolute = (absolute_t) value;
 -              }
 -              else
 -              {
 -                      assert (23 == 42);
 -              }
 -
 +              state->last_value = value;
                state->last_time = t;
                return (EAGAIN);
 -      } /* }}} */
 +      }
  
 -      if (ds_type == DS_TYPE_DERIVE)
 -      {
 -              ret_rate->gauge = (value - state->last_value.derive) / interval;
 -              state->last_value.derive = value;
 +      switch (ds_type) {
 +      case DS_TYPE_DERIVE: {
 +              derive_t diff = value.derive - state->last_value.derive;
 +              *ret_rate = ((gauge_t) diff) / ((gauge_t) interval);
 +              break;
        }
 -      else if (ds_type == DS_TYPE_COUNTER)
 -      {
 -              ret_rate->gauge = (((counter_t)value) - state->last_value.counter) / interval;
 -              state->last_value.counter = (counter_t) value;
 +      case DS_TYPE_GAUGE: {
 +              *ret_rate = value.gauge;
 +              break;
        }
 -      else if (ds_type == DS_TYPE_ABSOLUTE)
 -      {
 -              ret_rate->gauge = (((absolute_t)value) - state->last_value.absolute) / interval;
 -              state->last_value.absolute = (absolute_t) value;
 +      case DS_TYPE_COUNTER: {
 +              counter_t diff = counter_diff (state->last_value.counter, value.counter);
 +              *ret_rate = ((gauge_t) diff) / ((gauge_t) interval);
 +              break;
        }
 -      else
 -      {
 -              assert (23 == 42);
 +      case DS_TYPE_ABSOLUTE: {
 +              absolute_t diff = value.absolute;
 +              *ret_rate = ((gauge_t) diff) / ((gauge_t) interval);
 +              break;
 +      }
 +      default:
 +              return EINVAL;
        }
  
 -        state->last_time = t;
 +      state->last_value = value;
 +      state->last_time = t;
        return (0);
  } /* }}} value_t rate_to_value */
  
  int service_name_to_port_number (const char *service_name)
  {
        struct addrinfo *ai_list;
 -      struct addrinfo *ai_ptr;
 -      struct addrinfo ai_hints;
        int status;
        int service_number;
  
        if (service_name == NULL)
                return (-1);
  
 -      ai_list = NULL;
 -      memset (&ai_hints, 0, sizeof (ai_hints));
 -      ai_hints.ai_family = AF_UNSPEC;
 +      struct addrinfo ai_hints = {
 +              .ai_family = AF_UNSPEC
 +      };
  
        status = getaddrinfo (/* node = */ NULL, service_name,
                        &ai_hints, &ai_list);
        }
  
        service_number = -1;
 -      for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
 +      for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
        {
                if (ai_ptr->ai_family == AF_INET)
                {
        return (-1);
  } /* int service_name_to_port_number */
  
 +void set_sock_opts (int sockfd) /* {{{ */
 +{
 +      int status;
 +      int socktype;
 +
 +      socklen_t socklen = sizeof (socklen_t);
 +      int so_keepalive = 1;
 +
 +      status = getsockopt (sockfd, SOL_SOCKET, SO_TYPE, &socktype, &socklen);
 +      if (status != 0)
 +      {
 +              WARNING ("set_sock_opts: failed to determine socket type");
 +              return;
 +      }
 +
 +      if (socktype == SOCK_STREAM)
 +      {
 +              status = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
 +                              &so_keepalive, sizeof (so_keepalive));
 +              if (status != 0)
 +                      WARNING ("set_sock_opts: failed to set socket keepalive flag");
 +
 +#ifdef TCP_KEEPIDLE
 +              int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1);
 +              status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
 +                              &tcp_keepidle, sizeof (tcp_keepidle));
 +              if (status != 0)
 +                      WARNING ("set_sock_opts: failed to set socket tcp keepalive time");
 +#endif
 +
 +#ifdef TCP_KEEPINTVL
 +              int tcp_keepintvl = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1);
 +              status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
 +                              &tcp_keepintvl, sizeof (tcp_keepintvl));
 +              if (status != 0)
 +                      WARNING ("set_sock_opts: failed to set socket tcp keepalive interval");
 +#endif
 +      }
 +} /* }}} void set_sock_opts */
 +
  int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
  {
        derive_t tmp;
@@@ -1668,56 -1672,9 +1673,56 @@@ int strarray_add (char ***ret_array, si
  
  void strarray_free (char **array, size_t array_len) /* {{{ */
  {
 -      size_t i;
 -
 -      for (i = 0; i < array_len; i++)
 +      for (size_t i = 0; i < array_len; i++)
                sfree (array[i]);
        sfree (array);
  } /* }}} void strarray_free */
 +
 +#ifdef HAVE_SYS_CAPABILITY_H
 +int check_capability (int capability) /* {{{ */
 +{
 +#ifdef _LINUX_CAPABILITY_VERSION_3
 +      cap_user_header_t cap_header = calloc(1, sizeof (*cap_header));
 +      if (cap_header == NULL)
 +      {
 +              ERROR("check_capability: calloc failed");
 +              return (-1);
 +      }
 +
 +      cap_user_data_t cap_data = calloc(1, sizeof (*cap_data));
 +      if (cap_data == NULL)
 +      {
 +              ERROR("check_capability: calloc failed");
 +              sfree(cap_header);
 +              return (-1);
 +      }
 +
 +      cap_header->pid = getpid();
 +      cap_header->version = _LINUX_CAPABILITY_VERSION;
 +      if (capget(cap_header, cap_data) < 0)
 +      {
 +              ERROR("check_capability: capget failed");
 +              sfree(cap_header);
 +              sfree(cap_data);
 +              return (-1);
 +      }
 +
 +      if ((cap_data->effective & (1 << capability)) == 0)
 +      {
 +              sfree(cap_header);
 +              sfree(cap_data);
 +              return (-1);
 +      }
 +      else
 +      {
 +              sfree(cap_header);
 +              sfree(cap_data);
 +              return (0);
 +      }
 +#else
 +      WARNING ("check_capability: unsupported capability implementation. "
 +          "Some plugin(s) may require elevated privileges to work properly.");
 +      return (0);
 +#endif /* _LINUX_CAPABILITY_VERSION_3 */
 +} /* }}} int check_capability */
 +#endif /* HAVE_SYS_CAPABILITY_H */
diff --combined src/tail_csv.c
@@@ -22,7 -22,6 +22,7 @@@
   **/
  
  #include "collectd.h"
 +
  #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
  #include "common.h" /* auxiliary functions */
  #include "utils_tail.h"
  #include <string.h>
  
  struct metric_definition_s {
 -    char *name;
 -    char *type;
 -    char *instance;
 -    int data_source_type;
 -    int value_from;
 +    char   *name;
 +    char   *type;
 +    char   *instance;
 +    int     data_source_type;
 +    ssize_t value_from;
      struct metric_definition_s *next;
  };
  typedef struct metric_definition_s metric_definition_t;
  
  struct instance_definition_s {
 -    char *instance;
 -    char *path;
 -    cu_tail_t *tail;
 +    char                 *instance;
 +    char                 *path;
 +    cu_tail_t            *tail;
      metric_definition_t **metric_list;
 -    size_t metric_list_len;
 -    cdtime_t interval;
 -    int time_from;
 +    size_t                metric_list_len;
 +    cdtime_t              interval;
 +    ssize_t               time_from;
      struct instance_definition_s *next;
  };
  typedef struct instance_definition_s instance_definition_t;
@@@ -86,7 -85,7 +86,7 @@@ static int tcsv_submit (instance_defini
  static cdtime_t parse_time (char const *tbuf)
  {
      double t;
 -    char *endptr = 0;
 +    char *endptr = NULL;
  
      errno = 0;
      t = strtod (tbuf, &endptr);
@@@ -101,37 -100,37 +101,37 @@@ static int tcsv_read_metric (instance_d
          char **fields, size_t fields_num)
  {
      value_t v;
 -    cdtime_t t;
 +    cdtime_t t = 0;
      int status;
  
      if (md->data_source_type == -1)
          return (EINVAL);
  
 -    if (md->value_from >= fields_num)
 -        return (EINVAL);
 -
 -    if (id->time_from >= 0 && (id->time_from >= fields_num))
 +    assert (md->value_from >= 0);
 +    if (((size_t) md->value_from) >= fields_num)
          return (EINVAL);
  
 -    t = 0;
 -    if (id->time_from >= 0)
 -        t = parse_time (fields[id->time_from]);
 -
      status = parse_value (fields[md->value_from], &v, md->data_source_type);
      if (status != 0)
          return (status);
  
 +    if (id->time_from >= 0) {
 +        if (((size_t) id->time_from) >= fields_num)
 +            return (EINVAL);
 +        t = parse_time (fields[id->time_from]);
 +    }
 +
      return (tcsv_submit (id, md, v, t));
  }
  
 -static _Bool tcsv_check_index (int index, size_t fields_num, char const *name)
 +static _Bool tcsv_check_index (ssize_t index, size_t fields_num, char const *name)
  {
      if (index < 0)
          return 1;
      else if (((size_t) index) < fields_num)
          return 1;
  
 -    ERROR ("tail_csv plugin: Metric \"%s\": Request for index %i when "
 +    ERROR ("tail_csv plugin: Metric \"%s\": Request for index %zd when "
              "only %zu fields are available.",
              name, index, fields_num);
      return (0);
@@@ -268,31 -267,36 +268,31 @@@ static void tcsv_metric_definition_dest
      tcsv_metric_definition_destroy (next);
  }
  
 -static int tcsv_config_get_index(oconfig_item_t *ci, int *ret_index) {
 -    int index;
 -
 +static int tcsv_config_get_index(oconfig_item_t *ci, ssize_t *ret_index) {
      if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)){
          WARNING("tail_csv plugin: The \"%s\" config option needs exactly one "
                  "integer argument.", ci->key);
          return (-1);
      }
  
 -    index = (int) ci->values[0].value.number;
 -    if (index < 0) {
 +    if (ci->values[0].value.number < 0) {
          WARNING("tail_csv plugin: The \"%s\" config option must be positive "
                  "(or zero).", ci->key);
          return (-1);
      }
  
 -    *ret_index = index;
 +    *ret_index = (ssize_t) ci->values[0].value.number;
      return (0);
  }
  
  /* Parse metric  */
  static int tcsv_config_add_metric(oconfig_item_t *ci){
      metric_definition_t *md;
 -    int status = 0;
 -    int i;
 +    int status;
  
 -    md = (metric_definition_t *)malloc(sizeof(*md));
 +    md = calloc(1, sizeof(*md));
      if (md == NULL)
          return (-1);
 -    memset(md, 0, sizeof(*md));
      md->name = NULL;
      md->type = NULL;
      md->instance = NULL;
          return (-1);
      }
  
 -    for (i = 0; i < ci->children_num; ++i){
 +    for (int i = 0; i < ci->children_num; ++i){
          oconfig_item_t *option = ci->children + i;
  
          if (strcasecmp("Type", option->key) == 0)
@@@ -376,6 -380,7 +376,6 @@@ static int tcsv_config_add_instance_col
      metric_definition_t *metric;
      metric_definition_t **metric_list;
      size_t metric_list_size;
 -    int i;
  
      if (ci->values_num < 1) {
          WARNING("tail_csv plugin: The `Collect' config option needs at least one argument.");
          return (-1);
      id->metric_list = metric_list;
  
 -    for (i = 0; i < ci->values_num; i++) {
 +    for (int i = 0; i < ci->values_num; i++) {
          char *metric_name;
  
          if (ci->values[i].type != OCONFIG_TYPE_STRING) {
@@@ -418,13 -423,17 +418,13 @@@ static int tcsv_config_add_file(oconfig
  {
      instance_definition_t* id;
      int status = 0;
 -    int i;
  
      /* Registration variables */
      char cb_name[DATA_MAX_NAME_LEN];
 -    user_data_t cb_data;
 -    struct timespec cb_interval;
  
 -    id = malloc(sizeof(*id));
 +    id = calloc(1, sizeof(*id));
      if (id == NULL)
          return (-1);
 -    memset(id, 0, sizeof(*id));
      id->instance = NULL;
      id->path = NULL;
      id->metric_list = NULL;
      /* Use default interval. */
      id->interval = plugin_get_interval();
  
 -    for (i = 0; i < ci->children_num; ++i){
 +    for (int i = 0; i < ci->children_num; ++i){
          oconfig_item_t *option = ci->children + i;
          status = 0;
  
      }
  
      ssnprintf (cb_name, sizeof (cb_name), "tail_csv/%s", id->path);
 -    memset(&cb_data, 0, sizeof(cb_data));
 -    cb_data.data = id;
 -    cb_data.free_func = tcsv_instance_definition_destroy;
 -    CDTIME_T_TO_TIMESPEC(id->interval, &cb_interval);
 -    status = plugin_register_complex_read(NULL, cb_name, tcsv_read, &cb_interval, &cb_data);
 +
 +    user_data_t ud = {
 +        .data = id,
 +        .free_func = tcsv_instance_definition_destroy
 +    };
 +
 +    status = plugin_register_complex_read(NULL, cb_name, tcsv_read, id->interval, &ud);
  
      if (status != 0){
          ERROR("tail_csv plugin: Registering complex read function failed.");
  
  /* Parse blocks */
  static int tcsv_config(oconfig_item_t *ci){
 -    int i;
 -    for (i = 0; i < ci->children_num; ++i){
 +    for (int i = 0; i < ci->children_num; ++i){
          oconfig_item_t *child = ci->children + i;
          if (strcasecmp("Metric", child->key) == 0)
              tcsv_config_add_metric(child);
@@@ -536,8 -544,8 +536,8 @@@ static int tcsv_init(void) { /* {{{ *
          }
          else if (ds->ds_num != 1)
          {
 -            ERROR ("tail_csv plugin: The type \"%s\" has %i data sources. "
 +            ERROR ("tail_csv plugin: The type \"%s\" has %zu data sources. "
-                     "Only types with a single data soure are supported.",
+                     "Only types with a single data source are supported.",
                      ds->type, ds->ds_num);
              continue;
          }
diff --combined src/write_redis.c
@@@ -1,6 -1,6 +1,6 @@@
  /**
   * collectd - src/write_redis.c
 - * Copyright (C) 2010       Florian Forster
 + * Copyright (C) 2010-2015  Florian Forster
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   **/
  
  #include "collectd.h"
 +
  #include "plugin.h"
  #include "common.h"
 -#include "configfile.h"
  
 -#include <pthread.h>
  #include <sys/time.h>
  #include <hiredis/hiredis.h>
  
 +#ifndef REDIS_DEFAULT_PREFIX
 +# define REDIS_DEFAULT_PREFIX "collectd/"
 +#endif
 +
  struct wr_node_s
  {
    char name[DATA_MAX_NAME_LEN];
    char *host;
    int port;
    struct timeval timeout;
 +  char *prefix;
 +  int database;
 +  int max_set_size;
 +  _Bool store_rates;
  
    redisContext *conn;
    pthread_mutex_t lock;
@@@ -63,7 -56,7 +63,7 @@@ static int wr_write (const data_set_t *
    wr_node_t *node = ud->data;
    char ident[512];
    char key[512];
 -  char value[512];
 +  char value[512] = { 0 };
    char time[24];
    size_t value_size;
    char *value_ptr;
    status = FORMAT_VL (ident, sizeof (ident), vl);
    if (status != 0)
      return (status);
 -  ssnprintf (key, sizeof (key), "collectd/%s", ident);
 +  ssnprintf (key, sizeof (key), "%s%s",
 +      (node->prefix != NULL) ? node->prefix : REDIS_DEFAULT_PREFIX,
 +      ident);
    ssnprintf (time, sizeof (time), "%.9f", CDTIME_T_TO_DOUBLE(vl->time));
  
 -  memset (value, 0, sizeof (value));
    value_size = sizeof (value);
    value_ptr = &value[0];
 -  status = format_values (value_ptr, value_size, ds, vl, /* store rates = */ 0);
 +  status = format_values (value_ptr, value_size, ds, vl, node->store_rates);
    if (status != 0)
      return (status);
  
@@@ -91,7 -83,7 +91,7 @@@
      node->conn = redisConnectWithTimeout ((char *)node->host, node->port, node->timeout);
      if (node->conn == NULL)
      {
-       ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed: Unkown reason",
+       ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed: Unknown reason",
            (node->host != NULL) ? node->host : "localhost",
            (node->port != 0) ? node->port : 6379);
        pthread_mutex_unlock (&node->lock);
        pthread_mutex_unlock (&node->lock);
        return (-1);
      }
 +
 +    rr = redisCommand(node->conn, "SELECT %d", node->database);
 +    if (rr == NULL)
 +      WARNING("SELECT command error. database:%d message:%s", node->database, node->conn->errstr);
 +    else
 +      freeReplyObject (rr);
    }
  
    rr = redisCommand (node->conn, "ZADD %s %s %s", key, time, value);
    else
      freeReplyObject (rr);
  
 +  if (node->max_set_size >= 0)
 +  {
 +    rr = redisCommand (node->conn, "ZREMRANGEBYRANK %s %d %d", key, 0, (-1 * node->max_set_size) - 1);
 +    if (rr == NULL)
 +      WARNING("ZREMRANGEBYRANK command error. key:%s message:%s", key, node->conn->errstr);
 +    else
 +      freeReplyObject (rr);
 +  }
 +
    /* TODO(octo): This is more overhead than necessary. Use the cache and
     * metadata to determine if it is a new metric and call SADD only once for
     * each metric. */
 -  rr = redisCommand (node->conn, "SADD collectd/values %s", ident);
 +  rr = redisCommand (node->conn, "SADD %svalues %s",
 +      (node->prefix != NULL) ? node->prefix : REDIS_DEFAULT_PREFIX,
 +      ident);
    if (rr==NULL)
      WARNING("SADD command error. ident:%s message:%s", ident, node->conn->errstr);
    else
@@@ -167,19 -142,17 +167,19 @@@ static int wr_config_node (oconfig_item
    wr_node_t *node;
    int timeout;
    int status;
 -  int i;
  
 -  node = malloc (sizeof (*node));
 +  node = calloc (1, sizeof (*node));
    if (node == NULL)
      return (ENOMEM);
 -  memset (node, 0, sizeof (*node));
    node->host = NULL;
    node->port = 0;
    node->timeout.tv_sec = 0;
    node->timeout.tv_usec = 1000;
    node->conn = NULL;
 +  node->prefix = NULL;
 +  node->database = 0;
 +  node->max_set_size = -1;
 +  node->store_rates = 1;
    pthread_mutex_init (&node->lock, /* attr = */ NULL);
  
    status = cf_util_get_string_buffer (ci, node->name, sizeof (node->name));
      return (status);
    }
  
 -  for (i = 0; i < ci->children_num; i++)
 +  for (int i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *child = ci->children + i;
  
        status = cf_util_get_int (child, &timeout);
        if (status == 0) node->timeout.tv_usec = timeout;
      }
 +    else if (strcasecmp ("Prefix", child->key) == 0) {
 +      status = cf_util_get_string (child, &node->prefix);
 +    }
 +    else if (strcasecmp ("Database", child->key) == 0) {
 +      status = cf_util_get_int (child, &node->database);
 +    }
 +    else if (strcasecmp ("MaxSetSize", child->key) == 0) {
 +      status = cf_util_get_int (child, &node->max_set_size);
 +    }
 +    else if (strcasecmp ("StoreRates", child->key) == 0) {
 +      status = cf_util_get_boolean (child, &node->store_rates);
 +    }
      else
        WARNING ("write_redis plugin: Ignoring unknown config option \"%s\".",
            child->key);
    if (status == 0)
    {
      char cb_name[DATA_MAX_NAME_LEN];
 -    user_data_t ud;
  
      ssnprintf (cb_name, sizeof (cb_name), "write_redis/%s", node->name);
  
 -    ud.data = node;
 -    ud.free_func = wr_config_free;
 +    user_data_t ud = {
 +      .data = node,
 +      .free_func = wr_config_free
 +    };
  
      status = plugin_register_write (cb_name, wr_write, &ud);
    }
  
  static int wr_config (oconfig_item_t *ci) /* {{{ */
  {
 -  int i;
 -
 -  for (i = 0; i < ci->children_num; i++)
 +  for (int i = 0; i < ci->children_num; i++)
    {
      oconfig_item_t *child = ci->children + i;