Merge branch 'collectd-4.4'
authorFlorian Forster <octo@huhu.verplant.org>
Wed, 27 Aug 2008 08:13:24 +0000 (10:13 +0200)
committerFlorian Forster <octo@huhu.verplant.org>
Wed, 27 Aug 2008 08:13:24 +0000 (10:13 +0200)
1  2 
bindings/perl/Collectd/Unixsock.pm
src/collectd.conf.in
src/collectd.conf.pod
src/memcached.c
src/perl.c

@@@ -71,7 -71,7 +71,7 @@@ sub _create_socke
        return ($sock);
  } # _create_socket
  
 -=head1 VALUE IDENTIFIER
 +=head1 VALUE IDENTIFIERS
  
  The values in the collectd are identified using an five-tuple (host, plugin,
  plugin-instance, type, type-instance) where only plugin-instance and
@@@ -219,8 -219,8 +219,8 @@@ sub getva
  Submits a value-list to the daemon. If the B<time> argument is omitted
  C<time()> is used. The required argument B<values> is a reference to an array
  of values that is to be submitted. The number of values must match the number
 -of values expected for the given B<type> (see L<VALUE IDENTIFIER>), though this
 -is checked by the daemon, not the Perl module. Also, gauge data-sources
 +of values expected for the given B<type> (see L<VALUE IDENTIFIERS>), though
 +this is checked by the daemon, not the Perl module. Also, gauge data-sources
  (e.E<nbsp>g. system-load) may be C<undef>. Returns true upon success and false
  otherwise.
  
@@@ -236,6 -236,12 +236,12 @@@ sub putva
        my $msg;
        my $identifier;
        my $values;
+       my $interval = "";
+       if (defined $args{'interval'})
+       {
+               $interval = ' interval=' . $args{'interval'};
+       }
  
        $identifier = _create_identifier (\%args) or return;
        if (!$args{'values'})
                $values = join (':', $time, map { defined ($_) ? $_ : 'U' } (@{$args{'values'}}));
        }
  
-       $msg = "PUTVAL $identifier $values\n";
+       $msg = "PUTVAL $identifier$interval $values\n";
        #print "-> $msg";
        send ($fh, $msg, 0) or confess ("send: $!");
        $msg = undef;
@@@ -400,7 -406,7 +406,7 @@@ sub putnoti
        return;
  } # putnotif
  
 -=item I<$obj>-E<gt>B<flush> (B<timeout> =E<gt> I<$timeout>, B<plugins> =E<gt> [...]);
 +=item I<$obj>-E<gt>B<flush> (B<timeout> =E<gt> I<$timeout>, B<plugins> =E<gt> [...], B<identifier>  =E<gt> [...]);
  
  Flush cached data.
  
@@@ -415,14 -421,7 +421,14 @@@ flushed
  
  =item B<plugins>
  
 -If this option is specified, only the selected plugins will be flushed. 
 +If this option is specified, only the selected plugins will be flushed. The
 +argument is a reference to an array of strings.
 +
 +=item B<identifier>
 +
 +If this option is specified, only the given identifier(s) will be flushed. The
 +argument is a reference to an array of identifiers. Identifiers, in this case,
 +are hash references and have the members as outlined in L<VALUE IDENTIFIERS>.
  
  =back
  
@@@ -438,7 -437,7 +444,7 @@@ sub flus
        my $status = 0;
        my $msg    = "FLUSH";
  
 -      if ($args{'timeout'})
 +      if (defined ($args{'timeout'}))
        {
                $msg .= " timeout=" . $args{'timeout'};
        }
                }
        }
  
 +      if ($args{'identifier'})
 +      {
 +              for (@{$args{'identifier'}})
 +              {
 +                      my $identifier = $_;
 +                      my $ident_str;
 +
 +                      if (ref ($identifier) ne 'HASH')
 +                      {
 +                              cluck ("The argument of the `identifier' "
 +                                      . "option must be an array reference "
 +                                      . "of hash references.");
 +                              return;
 +                      }
 +
 +                      $ident_str = _create_identifier ($identifier);
 +                      if (!$ident_str)
 +                      {
 +                              return;
 +                      }
 +                      if ($ident_str =~ m/ /)
 +                      {
 +                              $ident_str =~ s#\\#\\\\#g;
 +                              $ident_str =~ s#"#\\"#g;
 +                              $ident_str = "\"$ident_str\"";
 +                      }
 +
 +                      $msg .= " identifier=$ident_str";
 +              }
 +      }
 +
        $msg .= "\n";
  
        send ($fh, $msg, 0) or confess ("send: $!");
diff --combined src/collectd.conf.in
@@@ -13,6 -13,19 +13,19 @@@ FQDNLookup   tru
  #Interval     10
  #ReadThreads  5
  
+ @BUILD_PLUGIN_LOGFILE_TRUE@LoadPlugin logfile
+ @BUILD_PLUGIN_SYSLOG_TRUE@LoadPlugin syslog
+ #<Plugin logfile>
+ #     LogLevel info
+ #     File STDOUT
+ #     Timestamp true
+ #</Plugin>
+ #<Plugin syslog>
+ #     LogLevel info
+ #</Plugin>
  @BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
  @BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
  @BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
@@@ -35,7 -48,6 +48,6 @@@
  @BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq
  @BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
  @BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
- @BUILD_PLUGIN_LOGFILE_TRUE@LoadPlugin logfile
  @BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
  @BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached
  @BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory
  @BUILD_PLUGIN_NETWORK_TRUE@LoadPlugin network
  @BUILD_PLUGIN_NFS_TRUE@LoadPlugin nfs
  @BUILD_PLUGIN_NGINX_TRUE@LoadPlugin nginx
 +@BUILD_PLUGIN_NOTIFY_DESKTOP_TRUE@LoadPlugin notify_desktop
 +@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email
  @BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd
  @BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
 +@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire
  @BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
  @BUILD_PLUGIN_PING_TRUE@LoadPlugin ping
 +@BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql
  @BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns
  @BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
  @BUILD_PLUGIN_RRDTOOL_TRUE@LoadPlugin rrdtool
  @BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial
  @BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp
  @BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
- @BUILD_PLUGIN_SYSLOG_TRUE@LoadPlugin syslog
  @BUILD_PLUGIN_TAIL_TRUE@LoadPlugin tail
  @BUILD_PLUGIN_TAPE_TRUE@LoadPlugin tape
  @BUILD_PLUGIN_TCPCONNS_TRUE@LoadPlugin tcpconns
  @BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2
 +@BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal
  @BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock
  @BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
  @BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
  #     HostnameFormat name
  #</Plugin>
  
- #<Plugin logfile>
- #     LogLevel info
- #     File STDOUT
- #     Timestamp true
- #</Plugin>
  #<Plugin mbmon>
  #     Host "127.0.0.1"
  #     Port "411"
  #     CACert "/etc/ssl/ca.crt"
  #</Plugin>
  
 +#<Plugin notify_desktop>
 +#     OkayTimeout 1000
 +#     WarningTimeout 5000
 +#     FailureTimeout 0
 +#</Plugin>
 +
 +#<Plugin notify_email>
 +#       SMTPServer "localhost"
 +#     SMTPPort 25
 +#     SMTPUser "my-username"
 +#     SMTPPassword "my-password"
 +#     From "collectd@main0server.com"
 +#     # <WARNING/FAILURE/OK> on <hostname>. beware! do not use not more than two %s in this string!!!
 +#     Subject "Aaaaaa!! %s on %s!!!!!"
 +#     Recipient "email1@domain1.net"
 +#     Recipient "email2@domain2.com"
 +#</Plugin>
 +
  #<Plugin ntpd>
  #     Host "localhost"
  #     Port 123
  #     UPS "upsname@hostname:port"
  #</Plugin>
  
 +#<Plugin onewire>
 +#     Device "-s localhost:4304"
 +#     Sensor "F10FCA000800"
 +#     IgnoreSelected false
 +#</Plugin>
 +
  #<Plugin perl>
  #     IncludeDir "/my/include/path"
  #     BaseName "Collectd::Plugin"
  #     TTL 255
  #</Plugin>
  
 +#<Plugin postgresql>
 +#     <Query magic>
 +#             Query "SELECT magic, spells FROM wizard WHERE host = $1;"
 +#             Param hostname
 +#             Column gauge magic
 +#             Column counter spells
 +#     </Query>
 +#
 +#     <Database foo>
 +#             Host "hostname"
 +#             Port 5432
 +#             User "username"
 +#             Password "secret"
 +#
 +#             SSLMode "prefer"
 +#             KRBSrvName "kerberos_service_name"
 +#
 +#             Query magic
 +#     </Database>
 +#
 +#     <Database bar>
 +#             Service "service_name"
 +#     </Database>
 +#</Plugin>
 +
  #<Plugin powerdns>
  #  <Server "server_name">
  #    Collect "latency"
  #   </Host>
  #</Plugin>
  
- #<Plugin syslog>
- #     LogLevel info
- #</Plugin>
  #<Plugin "tail">
  #  <File "/var/log/exim4/mainlog">
  #    Instance "exim"
  #     Server "8767"
  #</Plugin>
  
 +#<Plugin thermal>
 +#     ForceUseProcfs false
 +#     Device "THRM"
 +#     IgnoreSelected false
 +#</Plugin>
 +
  #<Plugin unixsock>
  #     SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
  #     SocketGroup "collectd"
diff --combined src/collectd.conf.pod
@@@ -30,11 -30,7 +30,11 @@@ section-start or -end. Empty lines and 
  ignored. Values are either string, enclosed in double-quotes,
  (floating-point-)numbers or a boolean expression, i.E<nbsp>e. either B<true> or
  B<false>. String containing of only alphanumeric characters and underscores do
 -not need to be quoted.
 +not need to be quoted. Lines may be wrapped by using `\' as the last character
 +before the newline. This allows long lines to be split into multiple lines.
 +Quoted strings may be wrapped as well. However, those are treated special in
 +that whitespace at the beginning of the following lines will be ignored, which
 +allows for nicely indenting the wrapped lines.
  
  The configuration is read and processed in order, i.E<nbsp>e. from top to
  bottom. So the plugins are loaded in the order listed in this config file. It
@@@ -236,19 -232,6 +236,19 @@@ Optional user name needed for authentic
  
  Optional password needed for authentication.
  
 +=item B<VerifyPeer> B<true|false>
 +
 +Enable or disable peer SSL certificate verification. See
 +L<http://curl.haxx.se/docs/sslcerts.html> for details. Enabled by default.
 +
 +=item B<VerifyHost> B<true|false>
 +
 +Enable or disable peer host name verification. If enabled, the plugin checks
 +if the C<Common Name> or a C<Subject Alternate Name> field of the SSL
 +certificate matches the host name provided by the B<URL> option. If this
 +identity check fails, the connection is aborted. Obviously, only works when
 +connecting to a SSL enabled server. Enabled by default.
 +
  =item B<CACert> I<File>
  
  File that holds one or more SSL certificates. If you want to use HTTPS you will
@@@ -420,79 -403,6 +420,79 @@@ expected from them. This is documented 
  
  =back
  
 +=head2 Plugin C<filecount>
 +
 +The C<filecount> plugin counts the number of files in a certain directory (and
 +its subdirectories) and their combined size. The configuration is very straight
 +forward:
 +
 +  <Plugin "filecount">
 +    <Directory "/var/qmail/queue/mess">
 +      Instance "qmail-message"
 +    </Directory>
 +    <Directory "/var/qmail/queue/todo">
 +      Instance "qmail-todo"
 +    </Directory>
 +    <Directory "/var/lib/php5">
 +      Instance "php5-sessions"
 +      Name "sess_*"
 +    </Directory>
 +  </Plugin>
 +
 +The example above counts the number of files in QMail's queue directories and
 +the number of PHP5 sessions. Jfiy: The "todo" queue holds the messages that
 +QMail has not yet looked at, the "message" queue holds the messages that were
 +classified into "local" and "remote".
 +
 +As you can see, the configuration consists of one or more C<Directory> blocks,
 +each of which specifies a directory in which to count the files. Within those
 +blocks, the following options are recognized:
 +
 +=over 4
 +
 +=item B<Instance> I<Instance>
 +
 +Sets the plugin instance to I<Instance>. That instance name must be unique, but
 +it's your responsibility, the plugin doesn't check for that. If not given, the
 +instance is set to the directory name with all slashes replaced by underscores
 +and all leading underscores removed.
 +
 +=item B<Name> I<Pattern>
 +
 +Only count files that match I<Pattern>, where I<Pattern> is a shell-like
 +wildcard as understood by L<fnmatch(3)>. Only the B<filename> is checked
 +against the pattern, not the entire path. In case this makes it easier for you:
 +This option has been named after the B<-name> parameter to L<find(1)>.
 +
 +=item B<MTime> I<Age>
 +
 +Count only files of a specific age: If I<Age> is greater than zero, only files
 +that haven't been touched in the last I<Age> seconds are counted. If I<Age> is
 +a negative number, this is inversed. For example, if B<-60> is specified, only
 +files that have been modified in the last minute will be counted.
 +
 +The number can also be followed by a "multiplier" to easily specify a larger
 +timespan. When given in this notation, the argument must in quoted, i.E<nbsp>e.
 +must be passed as string. So the B<-60> could also be written as B<"-1m"> (one
 +minute). Valid multipliers are C<s> (second), C<m> (minute), C<h> (hour), C<d>
 +(day), C<w> (week), and C<y> (year). There is no "month" multiplier. You can
 +also specify fractional numbers, e.E<nbsp>g. B<"0.5d"> is identical to
 +B<"12h">.
 +
 +=item B<Size> I<Size>
 +
 +Count only files of a specific size. When I<Size> is a positive number, only
 +files that are at least this big are counted. If I<Size> is a negative number,
 +this is inversed, i.E<nbsp>e. only files smaller than the absolute value of
 +I<Size> are counted.
 +
 +As with the B<MTime> option, a "multiplier" may be added. For a detailed
 +description see above. Valid multipliers here are C<b> (byte), C<k> (kilobyte),
 +C<m> (megabyte), C<g> (gigabyte), C<t> (terabyte), and C<p> (petabyte). Please
 +note that there are 1000 bytes in a kilobyte, not 1024.
 +
 +=back
 +
  =head2 Plugin C<hddtemp>
  
  To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
@@@ -540,7 -450,7 +540,7 @@@ similar interfaces. Thus, you can use t
  interfaces you're interested in. Sometimes, however, it's easier/preferred
  to collect all interfaces I<except> a few ones. This option enables you to
  do that: By setting B<IgnoreSelected> to I<true> the effect of
 -B<Interface> is inversed: All selected interfaces are ignored and all
 +B<Interface> is inverted: All selected interfaces are ignored and all
  other interfaces are collected.
  
  =back
@@@ -561,20 -471,6 +561,20 @@@ This option enables you to do that: By 
  the effect of B<Sensor> is inverted: All selected sensors are ignored and
  all other sensors are collected.
  
 +=item B<NotifySensorAdd> I<true>|I<false>
 +
 +If a sensor appears after initialization time of a minute a notification
 +is sent.
 +
 +=item B<NotifySensorRemove> I<true>|I<false>
 +
 +If a sensor disappears a notification is sent.
 +
 +=item B<NotifySensorNotPresent> I<true>|I<false>
 +
 +If you have for example dual power supply and one of them is (un)plugged then
 +a notification is sent.
 +
  =back
  
  =head2 Plugin C<iptables>
@@@ -612,7 -508,7 +612,7 @@@ irqs. This may not be practical, especi
  can use the B<Irq>-option to pick the interrupt you're interested in.
  Sometimes, however, it's easier/preferred to collect all interrupts I<except> a
  few ones. This option enables you to do that: By setting B<IgnoreSelected> to
 -I<true> the effect of B<Irq> is inversed: All selected interrupts are ignored
 +I<true> the effect of B<Irq> is inverted: All selected interrupts are ignored
  and all other interrupts are collected.
  
  =back
@@@ -871,7 -767,7 +871,7 @@@ Here are some examples to help you unde
  The behaviour is the same as with all other similar plugins: If nothing is
  selected at all, everything is collected. If some things are selected using the
  options described above, only these statistics are collected. If you set
 -B<IgnoreSelected> to B<true>, this behavior is inversed, i.E<nbsp>e. the
 +B<IgnoreSelected> to B<true>, this behavior is inverted, i.E<nbsp>e. the
  specified statistics will not be collected.
  
  =back
@@@ -979,32 -875,6 +979,32 @@@ and are checked by default depends on t
  
  =back
  
 +=head2 Plugin C<notify_desktop>
 +
 +This plugin sends a desktop notification to a notification daemon, as defined
 +in the Desktop Notification Specification. To actually display the
 +notifications, B<notification-daemon> is required and B<collectd> has to be
 +able to access the X server.
 +
 +The Desktop Notification Specification can be found at
 +L<http://www.galago-project.org/specs/notification/>.
 +
 +=over 4
 +
 +=item B<OkayTimeout> I<timeout>
 +
 +=item B<WarningTimeout> I<timeout>
 +
 +=item B<FailureTimeout> I<timeout>
 +
 +Set the I<timeout>, in milliseconds, after which to expire the notification
 +for C<OKAY>, C<WARNING> and C<FAILURE> severities respectively. If zero has
 +been specified, the displayed notification will not be closed at all - the
 +user has to do so herself. These options default to 5000. If a negative number
 +has been specified, the default is used as well.
 +
 +=back
 +
  =head2 Plugin C<ntpd>
  
  =over 4
@@@ -1037,68 -907,6 +1037,68 @@@ L<upsc(8)>
  
  =back
  
 +=head2 Plugin C<onewire>
 +
 +B<EXPERIMENTAL!> See notes below.
 +
 +The C<onewire> plugin uses the B<owcapi> library from the B<owfs> project
 +L<http://owfs.org/> to read sensors connected via the onewire bus.
 +
 +Currently only temperature sensors (sensors with the family code C<10>,
 +e.E<nbsp>g. DS1820, DS18S20, DS1920) can be read. If you have other sensors you
 +would like to have included, please send a sort request to the mailing list.
 +
 +Hubs (the DS2409 chips) are working, but read the note, why this plugin is
 +experimental, below.
 +
 +=over 4
 +
 +=item B<Device> I<Device>
 +
 +Sets the device to read the values from. This can either be a "real" hardware
 +device, such as a serial port or an USB port, or the address of the
 +L<owserver(1)> socket, usually B<localhost:4304>.
 +
 +Though the documentation claims to automatically recognize the given address
 +format, with versionE<nbsp>2.7p4 we had to specify the type explicitly. So
 +with that version, the following configuration worked for us:
 +
 +  <Plugin onewire>
 +    Device "-s localhost:4304"
 +  </Plugin>
 +
 +This directive is B<required> and does not have a default value.
 +
 +=item B<Sensor> I<Sensor>
 +
 +Selects sensors to collect or to ignore, depending on B<IgnoreSelected>, see
 +below. Sensors are specified without the family byte at the beginning, to you'd
 +use C<F10FCA000800>, and B<not> include the leading C<10.> family byte and
 +point.
 +
 +=item B<IgnoreSelected> I<true>|I<false>
 +
 +If no configuration if given, the B<onewire> plugin will collect data from all
 +sensors found. This may not be practical, especially if sensors are added and
 +removed regularly. Sometimes, however, it's easier/preferred to collect only
 +specific sensors or all sensors I<except> a few specified ones. This option
 +enables you to do that: By setting B<IgnoreSelected> to I<true> the effect of
 +B<Sensor> is inverted: All selected interfaces are ignored and all other
 +interfaces are collected.
 +
 +=back
 +
 +B<EXPERIMENTAL!> The C<onewire> plugin is experimental, because it doesn't yet
 +work with big setups. It works with one sensor being attached to one
 +controller, but as soon as you throw in a couple more senors and maybe a hub
 +or two, reading all values will take more than ten seconds (the default
 +interval). We will probably add some separate thread for reading the sensors
 +and some cache or something like that, but it's not done yet. We will try to
 +maintain backwards compatibility in the future, but we can't probmise. So in
 +short: If it works for you: Great! But kaap in mind that the config I<might>
 +change, though this is unlikely. Oh, and if you want to help improving this
 +plugin, just send a short notice to the mailing list. ThanksE<nbsp>:)
 +
  =head2 Plugin C<perl>
  
  This plugin embeds a Perl-interpreter into collectd and provides an interface
@@@ -1119,241 -927,6 +1119,241 @@@ Sets the Time-To-Live of generated ICM
  
  =back
  
 +=head2 Plugin C<postgresql>
 +
 +The C<postgresql> plugin queries statistics from PostgreSQL databases. It
 +keeps a persistent connection to all configured databases and tries to
 +reconnect if the connection has been interrupted. A database is configured by
 +specifying a B<Database> block as described below. The default statistics are
 +collected from PostgreSQL's B<statistics collector> which thus has to be
 +enabled for this plugin to work correctly. This should usually be the case by
 +default. See the section "The Statistics Collector" of the B<PostgreSQL
 +Documentation> for details.
 +
 +By specifying custom database queries using a B<Query> block as described
 +below, you may collect any data that is available from some PostgreSQL
 +database. This way, you are able to access statistics of external daemons
 +which are available in a PostgreSQL database or use future or special
 +statistics provided by PostgreSQL without the need to upgrade your collectd
 +installation.
 +
 +The B<PostgreSQL Documentation> manual can be found at
 +L<http://www.postgresql.org/docs/manuals/>.
 +
 +  <Plugin postgresql>
 +    <Query magic>
 +      Query "SELECT magic, spells FROM wizard WHERE host = $1;"
 +      Param hostname
 +      Column gauge magic
 +      Column counter spells
 +    </Query>
 +
 +    <Database foo>
 +      Host "hostname"
 +      Port "5432"
 +      User "username"
 +      Password "secret"
 +      SSLMode "prefer"
 +      KRBSrvName "kerberos_service_name"
 +      Query magic
 +    </Database>
 +    <Database bar>
 +      Service "service_name"
 +    </Database>
 +  </Plugin>
 +
 +The B<Query> block defines one database query which may later be used by a
 +database definition. It accepts a single mandatory argument which specifies
 +the name of the query. The names of all queries have to be unique. The
 +following configuration options are available to define the query:
 +
 +=over 4
 +
 +=item B<Query> I<sql query>
 +
 +Specify the I<sql query> which the plugin should execute. The string may
 +contain the tokens B<$1>, B<$2>, etc. which are used to reference the first,
 +second, etc. parameter. The value of the parameters is specified by the
 +B<Param> configuration option - see below for details. To include a literal
 +B<$> character followed by a number, surround it with single quotes (B<'>).
 +
 +Any SQL command which may return data (such as C<SELECT> or C<SHOW>) is
 +allowed. Note, however, that only a single command may be used. Semicolons are
 +allowed as long as a single non-empty command has been specified only.
 +
 +=item B<Param> I<hostname>|I<database>|I<username>
 +
 +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
 +they appear in the configuration file. The value of the parameter is
 +determined depending on the value of the B<Param> option as follows:
 +
 +=over 4
 +
 +=item I<hostname>
 +
 +The configured hostname of the database connection. If a UNIX domain socket is
 +used, the parameter expands to "localhost".
 +
 +=item I<database>
 +
 +The name of the database of the current connection.
 +
 +=item I<username>
 +
 +The username used to connect to the database.
 +
 +=back
 +
 +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<Column> I<type> [I<type instance>]
 +
 +Specify the I<type> and optional I<type instance> used to dispatch the value
 +of each result column. Detailed information about types and their
 +configuration can be found in L<types.db(5)>. The number and order of the
 +B<Column> options has to match the columns of the query result.
 +
 +=item B<MinPGVersion> I<version>
 +
 +=item B<MaxPGVersion> 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
 +in the F<postgresql_default.conf> file which, by default, is available at
 +C<I<prefix>/share/collectd/>):
 +
 +=over 4
 +
 +=item B<backends>
 +
 +This query collects the number of backends, i.E<nbsp>e. the number of
 +connected clients.
 +
 +=item B<transactions>
 +
 +This query collects the numbers of committed and rolled-back transactions of
 +the user tables.
 +
 +=item B<queries>
 +
 +This query collects the numbers of various table modifications (i.E<nbsp>e.
 +insertions, updates, deletions) of the user tables.
 +
 +=item B<query_plans>
 +
 +This query collects the numbers of various table scans and returned tuples of
 +the user tables.
 +
 +=item B<table_states>
 +
 +This query collects the numbers of live and dead rows in the user tables.
 +
 +=item B<disk_io>
 +
 +This query collects disk block access counts for user tables.
 +
 +=item B<disk_usage>
 +
 +This query collects the on-disk size of the database in bytes.
 +
 +=back
 +
 +The B<Database> block defines one PostgreSQL database for which to collect
 +statistics. It accepts a single mandatory argument which specifies the
 +database name. None of the other options are required. PostgreSQL will use
 +default values as documented in the section "CONNECTING TO A DATABASE" in the
 +L<psql(1)> manpage. However, be aware that those defaults may be influenced by
 +the user collectd is run as and special environment variables. See the manpage
 +for details.
 +
 +=over 4
 +
 +=item B<Host> I<hostname>
 +
 +Specify the hostname or IP of the PostgreSQL server to connect to. If the
 +value begins with a slash, it is interpreted as the directory name in which to
 +look for the UNIX domain socket.
 +
 +This option is also used to determine the hostname that is associated with a
 +collected data set. If it has been omitted or either begins with with a slash
 +or equals B<localhost> it will be replaced with the global hostname definition
 +of collectd. Any other value will be passed literally to collectd when
 +dispatching values. Also see the global B<Hostname> and B<FQDNLookup> options.
 +
 +=item B<Port> I<port>
 +
 +Specify the TCP port or the local UNIX domain socket file extension of the
 +server.
 +
 +=item B<User> I<username>
 +
 +Specify the username to be used when connecting to the server.
 +
 +=item B<Password> I<password>
 +
 +Specify the password to be used when connecting to the server.
 +
 +=item B<SSLMode> I<disable>|I<allow>|I<prefer>|I<require>
 +
 +Specify whether to use an SSL connection when contacting the server. The
 +following modes are supported:
 +
 +=over 4
 +
 +=item I<disable>
 +
 +Do not use SSL at all.
 +
 +=item I<allow>
 +
 +First, try to connect without using SSL. If that fails, try using SSL.
 +
 +=item I<prefer> (default)
 +
 +First, try to connect using SSL. If that fails, try without using SSL.
 +
 +=item I<require>
 +
 +Use SSL only.
 +
 +=back
 +
 +=item B<KRBSrvName> I<kerberos_service_name>
 +
 +Specify the Kerberos service name to use when authenticating with Kerberos 5
 +or GSSAPI. See the sections "Kerberos authentication" and "GSSAPI" of the
 +B<PostgreSQL Documentation> for details.
 +
 +=item B<Service> I<service_name>
 +
 +Specify the PostgreSQL service name to use for additional parameters. That
 +service has to be defined in F<pg_service.conf> and holds additional
 +connection parameters. See the section "The Connection Service File" in the
 +B<PostgreSQL Documentation> for details.
 +
 +=item B<Query> I<query>
 +
 +Specify a I<query> which should be executed for the database connection. This
 +may be any of the predefined or user-defined queries. If no such option is
 +given, it defaults to "backends", "transactions", "queries", "query_plans",
 +"table_states", "disk_io" and "disk_usage". Else, the specified queries are
 +used only.
 +
 +=back
 +
  =head2 Plugin C<powerdns>
  
  The C<powerdns> plugin queries statistics from an authoritative PowerDNS
@@@ -1558,7 -1131,7 +1558,7 @@@ Set the "XFiles Factor". The default i
  
  =item B<CacheFlush> I<Seconds>
  
 -When the C<rrdtool plugin> uses a cache (by setting B<CacheTimeout>, see below)
 +When the C<rrdtool> plugin uses a cache (by setting B<CacheTimeout>, see below)
  it writes all values for a certain RRD-file if the oldest value is older than
  (or equal to) the number of seconds specified. If some RRD-file is not updated
  anymore for some reason (the computer was shut down, the network is broken,
@@@ -1577,30 -1150,6 +1577,30 @@@ reduces IO-operations and thus lessens 
  The trade off is that the graphs kind of "drag behind" and that more memory is
  used.
  
 +=item B<WritesPerSecond> B<Updates>
 +
 +When collecting many statistics with collectd and the C<rrdtool> plugin, you
 +will run serious performance problems. The B<CacheFlush> setting and the
 +internal update queue assert that collectd continues to work just fine even
 +under heavy load, but the system may become very unresponsive and slow. This is
 +a problem especially if create graphs from the RRD files on the same machine,
 +for example using the C<graph.cgi> script included in the
 +C<contrib/collection3/> directory.
 +
 +This setting is designed for very large setups. Setting this option to a value
 +between 25 and 80 updates per second, depending on your hardware, will leave
 +the server responsive enough to draw graphs even while all the cached values
 +are written to disk. Flushed values, i.E<nbsp>e. values that are forced to disk
 +by the B<FLUSH> command, are B<not> effected by this limit. They are still
 +written as fast as possible, so that web frontends have up to date data when
 +generating graphs.
 +
 +For example: If you have 100,000 RRD files and set B<WritesPerSecond> to 30
 +updates per second, writing all values to disk will take approximately
 +56E<nbsp>minutes. Together with the flushing ability that's integrated into
 +"collection3" you'll end up with a responsive and fast system, up to date
 +graphs and basically a "backup" of your values every hour.
 +
  =back
  
  =head2 Plugin C<sensors>
@@@ -1629,7 -1178,7 +1629,7 @@@ sensors. This may not be practical, esp
  Thus, you can use the B<Sensor>-option to pick the sensors you're interested
  in. Sometimes, however, it's easier/preferred to collect all sensors I<except> a
  few ones. This option enables you to do that: By setting B<IgnoreSelected> to
 -I<true> the effect of B<Sensor> is inversed: All selected sensors are ignored
 +I<true> the effect of B<Sensor> is inverted: All selected sensors are ignored
  and all other sensors are collected.
  
  =back
@@@ -1673,7 -1222,7 +1673,7 @@@ user using (extended) regular expressio
        <Match>
                Regex "\\<R=local_user\\>"
        DSType "CounterInc"
-       Type "email_count"
+       Type "counter"
        Instance "local_user"
        </Match>
      </File>
@@@ -1832,32 -1381,6 +1832,32 @@@ port in numeric form
  
  =back
  
 +=head2 Plugin C<thermal>
 +
 +=over 4
 +
 +=item B<ForceUseProcfs> I<true>|I<false>
 +
 +By default, the C<thermal> plugin tries to read the statistics from the Linux
 +C<sysfs> interface. If that is not available, the plugin falls back to the
 +C<procfs> interface. By setting this option to I<true>, you can force the
 +plugin to use the latter. This option defaults to I<false>.
 +
 +=item B<Device> I<Device>
 +
 +Selects the name of the thermal device that you want to collect or ignore,
 +depending on the value of the B<IgnoreSelected> option. This option may be
 +used multiple times to specify a list of devices.
 +
 +=item B<IgnoreSelected> I<true>|I<false>
 +
 +Invert the selection: If set to true, all devices B<except> the ones that
 +match the device names specified by the B<Device> option are collected. By
 +default only selected devices are collected if a selection is made. If no
 +selection is configured at all, B<all> devices are selected.
 +
 +=back
 +
  =head2 Plugin C<unixsock>
  
  =over 4
@@@ -2082,7 -1605,6 +2082,7 @@@ L<types.db(5)>
  L<hddtemp(8)>,
  L<kstat(3KSTAT)>,
  L<mbmon(1)>,
 +L<psql(1)>,
  L<rrdtool(1)>,
  L<sensors(1)>
  
diff --combined src/memcached.c
@@@ -134,7 -134,7 +134,7 @@@ static int memcached_query_daemon (cha
                p.events = POLLIN | POLLERR | POLLHUP;
                p.revents = 0;
  
-               status = poll (&p, /* nfds = */ 1, /* timeout = */ interval_g);
+               status = poll (&p, /* nfds = */ 1, /* timeout = */ 1000 * interval_g);
                if (status <= 0)
                {
                        if (status == 0)
@@@ -212,10 -212,11 +212,10 @@@ static int memcached_config (const cha
        } else if (strcasecmp (key, "Port") == 0) {
                int port = (int) (atof (value));
                if ((port > 0) && (port <= 65535)) {
 -                      snprintf (memcached_port, sizeof (memcached_port), "%i", port);
 +                      ssnprintf (memcached_port, sizeof (memcached_port), "%i", port);
                } else {
 -                      strncpy (memcached_port, value, sizeof (memcached_port));
 +                      sstrncpy (memcached_port, value, sizeof (memcached_port));
                }
 -              memcached_port[sizeof (memcached_port) - 1] = '\0';
        } else {
                return -1;
        }
@@@ -237,11 -238,13 +237,11 @@@ static void submit_counter (const char 
        vl.time = time (NULL);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
 +      sstrncpy (vl.type, type, sizeof (vl.type));
        if (type_inst != NULL)
 -      {
 -              strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 -              vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
 -      }
 +              sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  
 -      plugin_dispatch_values (type, &vl);
 +      plugin_dispatch_values (&vl);
  } /* void memcached_submit_cmd */
  /* }}} */
  
@@@ -259,11 -262,13 +259,11 @@@ static void submit_counter2 (const cha
        vl.time = time (NULL);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
 +      sstrncpy (vl.type, type, sizeof (vl.type));
        if (type_inst != NULL)
 -      {
 -              strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 -              vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
 -      }
 +              sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  
 -      plugin_dispatch_values (type, &vl);
 +      plugin_dispatch_values (&vl);
  } /* void memcached_submit_cmd */
  /* }}} */
  
@@@ -280,11 -285,13 +280,11 @@@ static void submit_gauge (const char *t
        vl.time = time (NULL);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
 +      sstrncpy (vl.type, type, sizeof (vl.type));
        if (type_inst != NULL)
 -      {
 -              strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 -              vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
 -      }
 +              sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  
 -      plugin_dispatch_values (type, &vl);
 +      plugin_dispatch_values (&vl);
  }
  /* }}} */
  
@@@ -302,11 -309,13 +302,11 @@@ static void submit_gauge2 (const char *
        vl.time = time (NULL);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
 +      sstrncpy (vl.type, type, sizeof (vl.type));
        if (type_inst != NULL)
 -      {
 -              strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 -              vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
 -      }
 +              sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  
 -      plugin_dispatch_values (type, &vl);
 +      plugin_dispatch_values (&vl);
  }
  /* }}} */
  
diff --combined src/perl.c
@@@ -71,7 -71,6 +71,7 @@@
  
  #define PLUGIN_TYPES    7
  
 +#define PLUGIN_CONFIG   254
  #define PLUGIN_DATASET  255
  
  #define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
@@@ -85,7 -84,8 +85,7 @@@ void boot_DynaLoader (PerlInterpreter *
  static XS (Collectd_plugin_register_ds);
  static XS (Collectd_plugin_unregister_ds);
  static XS (Collectd_plugin_dispatch_values);
 -static XS (Collectd_plugin_flush_one);
 -static XS (Collectd_plugin_flush_all);
 +static XS (Collectd__plugin_flush);
  static XS (Collectd_plugin_dispatch_notification);
  static XS (Collectd_plugin_log);
  static XS (Collectd_call_by_name);
@@@ -139,7 -139,8 +139,7 @@@ static struct 
        { "Collectd::plugin_register_data_set",   Collectd_plugin_register_ds },
        { "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
        { "Collectd::plugin_dispatch_values",     Collectd_plugin_dispatch_values },
 -      { "Collectd::plugin_flush_one",           Collectd_plugin_flush_one },
 -      { "Collectd::plugin_flush_all",           Collectd_plugin_flush_all },
 +      { "Collectd::_plugin_flush",              Collectd__plugin_flush },
        { "Collectd::plugin_dispatch_notification",
                Collectd_plugin_dispatch_notification },
        { "Collectd::plugin_log",                 Collectd_plugin_log },
@@@ -159,7 -160,6 +159,7 @@@ struct 
        { "Collectd::TYPE_LOG",        PLUGIN_LOG },
        { "Collectd::TYPE_NOTIF",      PLUGIN_NOTIF },
        { "Collectd::TYPE_FLUSH",      PLUGIN_FLUSH },
 +      { "Collectd::TYPE_CONFIG",     PLUGIN_CONFIG },
        { "Collectd::TYPE_DATASET",    PLUGIN_DATASET },
        { "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
        { "Collectd::DS_TYPE_GAUGE",   DS_TYPE_GAUGE },
@@@ -216,7 -216,8 +216,7 @@@ static int hv2data_source (pTHX_ HV *ha
                return -1;
  
        if (NULL != (tmp = hv_fetch (hash, "name", 4, 0))) {
 -              strncpy (ds->name, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
 -              ds->name[DATA_MAX_NAME_LEN - 1] = '\0';
 +              sstrncpy (ds->name, SvPV_nolen (*tmp), sizeof (ds->name));
        }
        else {
                log_err ("hv2data_source: No DS name given.");
@@@ -378,10 -379,6 +378,10 @@@ static int value_list2hv (pTHX_ value_l
                                newSVpv (vl->plugin_instance, 0), 0))
                        return -1;
  
 +      if ('\0' != vl->type[0])
 +              if (NULL == hv_store (hash, "type", 4, newSVpv (vl->type, 0), 0))
 +                      return -1;
 +
        if ('\0' != vl->type_instance[0])
                if (NULL == hv_store (hash, "type_instance", 13,
                                newSVpv (vl->type_instance, 0), 0))
@@@ -426,81 -423,6 +426,81 @@@ static int notification2hv (pTHX_ notif
        return 0;
  } /* static int notification2hv (notification_t *, HV *) */
  
 +static int oconfig_item2hv (pTHX_ oconfig_item_t *ci, HV *hash)
 +{
 +      int i;
 +
 +      AV *values;
 +      AV *children;
 +
 +      if (NULL == hv_store (hash, "key", 3, newSVpv (ci->key, 0), 0))
 +              return -1;
 +
 +      values = newAV ();
 +      if (0 < ci->values_num)
 +              av_extend (values, ci->values_num);
 +
 +      if (NULL == hv_store (hash, "values", 6, newRV_noinc ((SV *)values), 0)) {
 +              av_clear (values);
 +              av_undef (values);
 +              return -1;
 +      }
 +
 +      for (i = 0; i < ci->values_num; ++i) {
 +              SV *value;
 +
 +              switch (ci->values[i].type) {
 +                      case OCONFIG_TYPE_STRING:
 +                              value = newSVpv (ci->values[i].value.string, 0);
 +                              break;
 +                      case OCONFIG_TYPE_NUMBER:
 +                              value = newSVnv ((NV)ci->values[i].value.number);
 +                              break;
 +                      case OCONFIG_TYPE_BOOLEAN:
 +                              value = ci->values[i].value.boolean ? &PL_sv_yes : &PL_sv_no;
 +                              break;
 +                      default:
 +                              log_err ("oconfig_item2hv: Invalid value type %i.",
 +                                              ci->values[i].type);
 +                              value = &PL_sv_undef;
 +              }
 +
 +              if (NULL == av_store (values, i, value)) {
 +                      sv_free (value);
 +                      return -1;
 +              }
 +      }
 +
 +      /* ignoring 'parent' member which is uninteresting in this case */
 +
 +      children = newAV ();
 +      if (0 < ci->children_num)
 +              av_extend (children, ci->children_num);
 +
 +      if (NULL == hv_store (hash, "children", 8, newRV_noinc ((SV *)children), 0)) {
 +              av_clear (children);
 +              av_undef (children);
 +              return -1;
 +      }
 +
 +      for (i = 0; i < ci->children_num; ++i) {
 +              HV *child = newHV ();
 +
 +              if (0 != oconfig_item2hv (aTHX_ ci->children + i, child)) {
 +                      hv_clear (child);
 +                      hv_undef (child);
 +                      return -1;
 +              }
 +
 +              if (NULL == av_store (children, i, newRV_noinc ((SV *)child))) {
 +                      hv_clear (child);
 +                      hv_undef (child);
 +                      return -1;
 +              }
 +      }
 +      return 0;
 +} /* static int oconfig_item2hv (pTHX_ oconfig_item_t *, HV *) */
 +
  /*
   * Internal functions.
   */
  static char *get_module_name (char *buf, size_t buf_len, const char *module) {
        int status = 0;
        if (base_name[0] == '\0')
 -              status = snprintf (buf, buf_len, "%s", module);
 +              status = ssnprintf (buf, buf_len, "%s", module);
        else
 -              status = snprintf (buf, buf_len, "%s::%s", base_name, module);
 +              status = ssnprintf (buf, buf_len, "%s::%s", base_name, module);
        if ((status < 0) || ((unsigned int)status >= buf_len))
                return (NULL);
 -      buf[buf_len - 1] = '\0';
        return (buf);
  } /* char *get_module_name */
  
@@@ -558,7 -481,8 +558,7 @@@ static int pplugin_register_data_set (p
                                ds[i].name, ds[i].type, ds[i].min, ds[i].max);
        }
  
 -      strncpy (set->type, name, DATA_MAX_NAME_LEN);
 -      set->type[DATA_MAX_NAME_LEN - 1] = '\0';
 +      sstrncpy (set->type, name, sizeof (set->type));
  
        set->ds_num = len + 1;
        set->ds = ds;
@@@ -593,7 -517,7 +593,7 @@@ static int pplugin_unregister_data_set 
   *   type_instance   => $tinstance,
   * }
   */
 -static int pplugin_dispatch_values (pTHX_ char *name, HV *values)
 +static int pplugin_dispatch_values (pTHX_ HV *values)
  {
        value_list_t list = VALUE_LIST_INIT;
        value_t      *val = NULL;
  
        int ret = 0;
  
 -      if ((NULL == name) || (NULL == values))
 +      if (NULL == values)
 +              return -1;
 +
 +      if (NULL == (tmp = hv_fetch (values, "type", 4, 0))) {
 +              log_err ("pplugin_dispatch_values: No type given.");
                return -1;
 +      }
 +
 +      sstrncpy (list.type, SvPV_nolen (*tmp), sizeof (list.type));
  
        if ((NULL == (tmp = hv_fetch (values, "values", 6, 0)))
                        || (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
  
                val = (value_t *)smalloc (len * sizeof (value_t));
  
 -              list.values_len = av2value (aTHX_ name, (AV *)SvRV (*tmp), val, len);
 +              list.values_len = av2value (aTHX_ list.type, (AV *)SvRV (*tmp),
 +                              val, len);
                list.values = val;
  
                if (-1 == list.values_len) {
        }
  
        if (NULL != (tmp = hv_fetch (values, "host", 4, 0))) {
 -              strncpy (list.host, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
 -              list.host[DATA_MAX_NAME_LEN - 1] = '\0';
 +              sstrncpy (list.host, SvPV_nolen (*tmp), sizeof (list.host));
        }
        else {
                sstrncpy (list.host, hostname_g, sizeof (list.host));
        }
  
 -      if (NULL != (tmp = hv_fetch (values, "plugin", 6, 0))) {
 -              strncpy (list.plugin, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
 -              list.plugin[DATA_MAX_NAME_LEN - 1] = '\0';
 -      }
 +      if (NULL != (tmp = hv_fetch (values, "plugin", 6, 0)))
 +              sstrncpy (list.plugin, SvPV_nolen (*tmp), sizeof (list.plugin));
  
 -      if (NULL != (tmp = hv_fetch (values, "plugin_instance", 15, 0))) {
 -              strncpy (list.plugin_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
 -              list.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
 -      }
 +      if (NULL != (tmp = hv_fetch (values, "plugin_instance", 15, 0)))
 +              sstrncpy (list.plugin_instance, SvPV_nolen (*tmp),
 +                              sizeof (list.plugin_instance));
  
 -      if (NULL != (tmp = hv_fetch (values, "type_instance", 13, 0))) {
 -              strncpy (list.type_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
 -              list.type_instance[DATA_MAX_NAME_LEN - 1] = '\0';
 -      }
 +      if (NULL != (tmp = hv_fetch (values, "type_instance", 13, 0)))
 +              sstrncpy (list.type_instance, SvPV_nolen (*tmp),
 +                              sizeof (list.type_instance));
  
 -      ret = plugin_dispatch_values (name, &list);
 +      ret = plugin_dispatch_values (&list);
  
        sfree (val);
        return ret;
@@@ -705,25 -626,31 +705,25 @@@ static int pplugin_dispatch_notificatio
                n.time = time (NULL);
  
        if (NULL != (tmp = hv_fetch (notif, "message", 7, 0)))
 -              strncpy (n.message, SvPV_nolen (*tmp), sizeof (n.message));
 -      n.message[sizeof (n.message) - 1] = '\0';
 +              sstrncpy (n.message, SvPV_nolen (*tmp), sizeof (n.message));
  
        if (NULL != (tmp = hv_fetch (notif, "host", 4, 0)))
 -              strncpy (n.host, SvPV_nolen (*tmp), sizeof (n.host));
 +              sstrncpy (n.host, SvPV_nolen (*tmp), sizeof (n.host));
        else
 -              strncpy (n.host, hostname_g, sizeof (n.host));
 -      n.host[sizeof (n.host) - 1] = '\0';
 +              sstrncpy (n.host, hostname_g, sizeof (n.host));
  
        if (NULL != (tmp = hv_fetch (notif, "plugin", 6, 0)))
 -              strncpy (n.plugin, SvPV_nolen (*tmp), sizeof (n.plugin));
 -      n.plugin[sizeof (n.plugin) - 1] = '\0';
 +              sstrncpy (n.plugin, SvPV_nolen (*tmp), sizeof (n.plugin));
  
        if (NULL != (tmp = hv_fetch (notif, "plugin_instance", 15, 0)))
 -              strncpy (n.plugin_instance, SvPV_nolen (*tmp),
 +              sstrncpy (n.plugin_instance, SvPV_nolen (*tmp),
                                sizeof (n.plugin_instance));
 -      n.plugin_instance[sizeof (n.plugin_instance) - 1] = '\0';
  
        if (NULL != (tmp = hv_fetch (notif, "type", 4, 0)))
 -              strncpy (n.type, SvPV_nolen (*tmp), sizeof (n.type));
 -      n.type[sizeof (n.type) - 1] = '\0';
 +              sstrncpy (n.type, SvPV_nolen (*tmp), sizeof (n.type));
  
        if (NULL != (tmp = hv_fetch (notif, "type_instance", 13, 0)))
 -              strncpy (n.type_instance, SvPV_nolen (*tmp), sizeof (n.type_instance));
 -      n.type_instance[sizeof (n.type_instance) - 1] = '\0';
 +              sstrncpy (n.type_instance, SvPV_nolen (*tmp), sizeof (n.type_instance));
        return plugin_dispatch_notification (&n);
  } /* static int pplugin_dispatch_notification (HV *) */
  
@@@ -772,7 -699,6 +772,7 @@@ static int pplugin_call_all (pTHX_ int 
                 *   time   => $time,
                 *   host   => $hostname,
                 *   plugin => $plugin,
 +               *   type   => $type,
                 *   plugin_instance => $instance,
                 *   type_instance   => $type_instance
                 * };
        else if (PLUGIN_FLUSH == type) {
                /*
                 * $_[0] = $timeout;
 +               * $_[1] = $identifier;
                 */
                XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
 +              XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0)));
        }
  
        PUTBACK;
@@@ -950,43 -874,33 +950,43 @@@ static XS (Collectd_plugin_unregister_d
   */
  static XS (Collectd_plugin_dispatch_values)
  {
 -      SV *values = NULL;
 +      SV *values     = NULL;
 +      int values_idx = 0;
  
        int ret = 0;
  
        dXSARGS;
  
 -      if (2 != items) {
 -              log_err ("Usage: Collectd::plugin_dispatch_values(name, values)");
 +      if (2 == items) {
 +              log_warn ("Collectd::plugin_dispatch_values with two arguments "
 +                              "is deprecated - pass the type through values->{type}.");
 +              values_idx = 1;
 +      }
 +      else if (1 != items) {
 +              log_err ("Usage: Collectd::plugin_dispatch_values(values)");
                XSRETURN_EMPTY;
        }
  
 -      log_debug ("Collectd::plugin_dispatch_values: "
 -                      "name = \"%s\", values=\"%s\"",
 -                      SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
 +      log_debug ("Collectd::plugin_dispatch_values: values=\"%s\"",
 +                      SvPV_nolen (ST (values_idx)));
  
 -      values = ST (1);
 +      values = ST (values_idx);
  
        if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
                log_err ("Collectd::plugin_dispatch_values: Invalid values.");
                XSRETURN_EMPTY;
        }
  
 -      if ((NULL == ST (0)) || (NULL == values))
 +      if (((2 == items) && (NULL == ST (0))) || (NULL == values))
                XSRETURN_EMPTY;
  
 -      ret = pplugin_dispatch_values (aTHX_ SvPV_nolen (ST (0)),
 -                      (HV *)SvRV (values));
 +      if ((2 == items) && (NULL == hv_store ((HV *)SvRV (values), "type", 4,
 +                      newSVsv (ST (0)), 0))) {
 +              log_err ("Collectd::plugin_dispatch_values: Could not store type.");
 +              XSRETURN_EMPTY;
 +      }
 +
 +      ret = pplugin_dispatch_values (aTHX_ (HV *)SvRV (values));
  
        if (0 == ret)
                XSRETURN_YES;
  } /* static XS (Collectd_plugin_dispatch_values) */
  
  /*
 - * Collectd::plugin_flush_one (timeout, name).
 + * Collectd::_plugin_flush (plugin, timeout, identifier).
 + *
 + * plugin:
 + *   name of the plugin to flush
   *
   * timeout:
   *   timeout to use when flushing the data
   *
 - * name:
 - *   name of the plugin to flush
 + * identifier:
 + *   data-set identifier to flush
   */
 -static XS (Collectd_plugin_flush_one)
 +static XS (Collectd__plugin_flush)
  {
 +      char *plugin  = NULL;
 +      int   timeout = -1;
 +      char *id      = NULL;
 +
        dXSARGS;
  
 -      if (2 != items) {
 -              log_err ("Usage: Collectd::plugin_flush_one(timeout, name)");
 +      if (3 != items) {
 +              log_err ("Usage: Collectd::_plugin_flush(plugin, timeout, id)");
                XSRETURN_EMPTY;
        }
  
 -      log_debug ("Collectd::plugin_flush_one: timeout = %i, name = \"%s\"",
 -                      (int)SvIV (ST (0)), SvPV_nolen (ST (1)));
 +      if (SvOK (ST (0)))
 +              plugin = SvPV_nolen (ST (0));
  
 -      if (0 == plugin_flush_one ((int)SvIV (ST (0)), SvPV_nolen (ST (1))))
 -              XSRETURN_YES;
 -      else
 -              XSRETURN_EMPTY;
 -} /* static XS (Collectd_plugin_flush_one) */
 -
 -/*
 - * Collectd::plugin_flush_all (timeout).
 - *
 - * timeout:
 - *   timeout to use when flushing the data
 - */
 -static XS (Collectd_plugin_flush_all)
 -{
 -      dXSARGS;
 +      if (SvOK (ST (1)))
 +              timeout = (int)SvIV (ST (1));
  
 -      if (1 != items) {
 -              log_err ("Usage: Collectd::plugin_flush_all(timeout)");
 -              XSRETURN_EMPTY;
 -      }
 +      if (SvOK (ST (2)))
 +              id = SvPV_nolen (ST (2));
  
 -      log_debug ("Collectd::plugin_flush_all: timeout = %i", (int)SvIV (ST (0)));
 +      log_debug ("Collectd::_plugin_flush: plugin = \"%s\", timeout = %i, "
 +                      "id = \"%s\"", plugin, timeout, id);
  
 -      plugin_flush_all ((int)SvIV (ST (0)));
 -      XSRETURN_YES;
 -} /* static XS (Collectd_plugin_flush_all) */
 +      if (0 == plugin_flush (plugin, timeout, id))
 +              XSRETURN_YES;
 +      else
 +              XSRETURN_EMPTY;
 +} /* static XS (Collectd__plugin_flush) */
  
  /*
   * Collectd::plugin_dispatch_notification (notif).
@@@ -1342,7 -1261,7 +1342,7 @@@ static int perl_notify (const notificat
        return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
  } /* static int perl_notify (const notification_t *) */
  
 -static int perl_flush (const int timeout)
 +static int perl_flush (int timeout, const char *identifier)
  {
        dTHX;
  
  
                aTHX = t->interp;
        }
 -      return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout);
 +      return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout, identifier);
  } /* static int perl_flush (const int) */
  
  static int perl_shutdown (void)
@@@ -1439,7 -1358,8 +1439,7 @@@ static int g_pv_get (pTHX_ SV *var, MAG
  static int g_pv_set (pTHX_ SV *var, MAGIC *mg)
  {
        char *pv = mg->mg_ptr;
 -      strncpy (pv, SvPV_nolen (var), DATA_MAX_NAME_LEN);
 -      pv[DATA_MAX_NAME_LEN - 1] = '\0';
 +      sstrncpy (pv, SvPV_nolen (var), DATA_MAX_NAME_LEN);
        return 0;
  } /* static int g_pv_set (pTHX_ SV *, MAGIC *) */
  
@@@ -1640,7 -1560,8 +1640,7 @@@ static int perl_config_basename (pTHX_ 
        value = ci->values[0].value.string;
  
        log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
 -      strncpy (base_name, value, sizeof (base_name));
 -      base_name[sizeof (base_name) - 1] = '\0';
 +      sstrncpy (base_name, value, sizeof (base_name));
        return 0;
  } /* static int perl_config_basename (oconfig_item_it *) */
  
@@@ -1723,76 -1644,18 +1723,74 @@@ static int perl_config_includedir (pTHX
        return 0;
  } /* static int perl_config_includedir (oconfig_item_it *) */
  
 +/*
 + * <Plugin> block
 + */
 +static int perl_config_plugin (pTHX_ oconfig_item_t *ci)
 +{
 +      int retvals = 0;
 +      int ret     = 0;
 +
 +      char *plugin;
 +      HV   *config;
 +
 +      dSP;
 +
 +      if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
 +              log_err ("LoadPlugin expects a single string argument.");
 +              return 1;
 +      }
 +
 +      plugin = ci->values[0].value.string;
 +      config = newHV ();
 +
 +      if (0 != oconfig_item2hv (aTHX_ ci, config)) {
 +              hv_clear (config);
 +              hv_undef (config);
 +
 +              log_err ("Unable to convert configuration to a Perl hash value.");
 +              config = Nullhv;
 +      }
 +
 +      ENTER;
 +      SAVETMPS;
 +
 +      PUSHMARK (SP);
 +
 +      XPUSHs (sv_2mortal (newSVpv (plugin, 0)));
 +      XPUSHs (sv_2mortal (newRV_noinc ((SV *)config)));
 +
 +      PUTBACK;
 +
 +      retvals = call_pv ("Collectd::_plugin_dispatch_config", G_SCALAR);
 +
 +      SPAGAIN;
 +      if (0 < retvals) {
 +              SV *tmp = POPs;
 +              if (! SvTRUE (tmp))
 +                      ret = -1;
 +      }
 +      else
 +              ret = -1;
 +
 +      PUTBACK;
 +      FREETMPS;
 +      LEAVE;
 +      return ret;
 +} /* static int perl_config_plugin (oconfig_item_it *) */
 +
  static int perl_config (oconfig_item_t *ci)
  {
        int i = 0;
  
-       dTHX;
-       /* dTHX does not get any valid values in case Perl
-        * has not been initialized */
-       if (NULL == perl_threads)
-               aTHX = NULL;
+       dTHXa (NULL);
  
        for (i = 0; i < ci->children_num; ++i) {
                oconfig_item_t *c = ci->children + i;
  
+               if (NULL != perl_threads)
+                       aTHX = PERL_GET_CONTEXT;
                if (0 == strcasecmp (c->key, "LoadPlugin"))
                        perl_config_loadplugin (aTHX_ c);
                else if (0 == strcasecmp (c->key, "BaseName"))
                        perl_config_enabledebugger (aTHX_ c);
                else if (0 == strcasecmp (c->key, "IncludeDir"))
                        perl_config_includedir (aTHX_ c);
 +              else if (0 == strcasecmp (c->key, "Plugin"))
 +                      perl_config_plugin (aTHX_ c);
                else
                        log_warn ("Ignoring unknown config key \"%s\".", c->key);
        }