Merge branch 'collectd-4.4' into collectd-4.5
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 20 Dec 2008 09:17:26 +0000 (10:17 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 20 Dec 2008 09:17:26 +0000 (10:17 +0100)
Conflicts:

src/utils_dns.c

1  2 
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/email.c
src/ipmi.c
src/rrdtool.c
src/unixsock.c
src/utils_dns.c

diff --combined src/collectd.c
@@@ -50,7 -50,7 +50,7 @@@ static int loop = 0
  static void *do_flush (void *arg)
  {
        INFO ("Flushing all data.");
 -      plugin_flush_all (-1);
 +      plugin_flush (NULL, -1, NULL);
        INFO ("Finished flushing all data.");
        pthread_exit (NULL);
        return NULL;
@@@ -90,7 -90,8 +90,7 @@@ static int init_hostname (void
        str = global_option_get ("Hostname");
        if (str != NULL)
        {
 -              strncpy (hostname_g, str, sizeof (hostname_g));
 -              hostname_g[sizeof (hostname_g) - 1] = '\0';
 +              sstrncpy (hostname_g, str, sizeof (hostname_g));
                return (0);
        }
  
                if (ai_ptr->ai_canonname == NULL)
                        continue;
  
 -              strncpy (hostname_g, ai_ptr->ai_canonname, sizeof (hostname_g));
 -              hostname_g[sizeof (hostname_g) - 1] = '\0';
 +              sstrncpy (hostname_g, ai_ptr->ai_canonname, sizeof (hostname_g));
                break;
        }
  
@@@ -244,7 -246,7 +244,7 @@@ static void update_kstat (void
  /* TODO
   * Remove all settings but `-f' and `-C'
   */
- static void exit_usage (void)
+ static void exit_usage (int status)
  {
        printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
                        
                        "\n"PACKAGE" "VERSION", http://collectd.org/\n"
                        "by Florian octo Forster <octo@verplant.org>\n"
                        "for contributions see `AUTHORS'\n");
-       exit (0);
- } /* static void exit_usage (char *name) */
+       exit (status);
+ } /* static void exit_usage (int status) */
  
  static int do_init (void)
  {
@@@ -436,11 -438,16 +436,16 @@@ int main (int argc, char **argv
                                break;
  #endif /* COLLECT_DAEMON */
                        case 'h':
+                               exit_usage (0);
+                               break;
                        default:
-                               exit_usage ();
+                               exit_usage (1);
                } /* switch (c) */
        } /* while (1) */
  
+       if (optind < argc)
+               exit_usage (1);
        /*
         * Read options from the config file, the environment and the command
         * line (in that order, with later options overwriting previous ones in
diff --combined src/collectd.conf.in
@@@ -40,7 -40,6 +40,7 @@@ FQDNLookup   tru
  @BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email
  @BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
  @BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec
 +@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
  @BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
  @BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
  @BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
  @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
@@@ -77,7 -72,6 +77,7 @@@
  @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
  #     NotificationExec "user:group" "/path/to/exec"
  #</Plugin>
  
 +#<Plugin filecount>
 +#     <Directory "/path/to/dir">
 +#             Instance "foodir"
 +#             Name "*.conf"
 +#             MTime "-5m"
 +#             Size "+10k"
 +#     </Directory>
 +#</Plugin>
 +
  @BUILD_PLUGIN_HDDTEMP_TRUE@<Plugin hddtemp>
  #     Host "127.0.0.1"
  #     Port "7634"
  #     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"
  #     EnableDebugger ""
  #     LoadPlugin foo
 +#
 +#     <Plugin foo>
 +#             Foo "Bar"
 +#             Qux "Baz"
 +#     </Plugin>
  #</Plugin>
  
  #<Plugin ping>
  #     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"
  #    <Match>
  #      Regex "\\<R=local_user\\>"
  #      DSType "CounterInc"
- #      Type "email_count"
+ #      Type "counter"
  #      Instance "local_user"
  #    </Match>
  #  </File>
  #     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
@@@ -769,12 -665,12 +769,12 @@@ database when started and keeps the con
  connection is interrupted for whatever reason it will try to re-connect. The
  plugin will complaint loudly in case anything goes wrong.
  
- This plugin issues C<SHOW STATUS> and evaluates C<Bytes_{received,sent}>,
- C<Com_*> and C<Handler_*> which correspond to F<mysql_octets.rrd>,
- F<mysql_commands-*.rrd> and F<mysql_handler-*.rrd>. Also, the values of
- C<Qcache_*> are put in F<mysql_qcache.rrd> and values of C<Threads_*> are put
- in F<mysql_threads.rrd>. Please refer to the B<MySQL reference manual>,
I<5.2.4. Server Status Variables> for an explanation of these values.
+ This plugin issues the MySQL C<SHOW STATUS> command and collects information
+ about MySQL network traffic, executed statements, requests, the query cache
+ and threads by evaluating the C<Bytes_{received,sent}>, C<Com_*>,
+ C<Handler_*>, C<Qcache_*> and C<Threads_*> return values. Please refer to the
+ B<MySQL reference manual>, I<5.1.6. Server Status Variables> for an
+ explanation of these values.
  
  Use the following options to configure the plugin:
  
@@@ -786,7 -682,9 +786,9 @@@ Hostname of the database server. Defaul
  
  =item B<User> I<Username>
  
- Username to use when connecting to the database.
+ Username to use when connecting to the database. The user does not have to be
+ granted any privileges (which is synonym to granting the C<USAGE> privilege).
+ Any existing MySQL user will do.
  
  =item B<Password> I<Password>
  
@@@ -871,7 -769,7 +873,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 -877,6 +981,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 -909,6 +1039,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 -929,6 +1121,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 -1133,7 +1560,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 -1152,6 +1579,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 you 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 -1180,7 +1631,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
@@@ -1665,16 -1216,16 +1667,16 @@@ user using (extended) regular expressio
      <File "/var/log/exim4/mainlog">
        Instance "exim"
        <Match>
-               Regex "S=([1-9][0-9]*)"
-       DSType "CounterAdd"
-       Type "ipt_bytes"
-       Instance "total"
+         Regex "S=([1-9][0-9]*)"
+         DSType "CounterAdd"
+         Type "ipt_bytes"
+         Instance "total"
        </Match>
        <Match>
-               Regex "\\<R=local_user\\>"
-       DSType "CounterInc"
-       Type "counter"
-       Instance "local_user"
+         Regex "\\<R=local_user\\>"
+         DSType "CounterInc"
+         Type "counter"
+         Instance "local_user"
        </Match>
      </File>
    </Plugin>
@@@ -1832,32 -1383,6 +1834,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
@@@ -2005,7 -1530,7 +2007,7 @@@ information
       <Plugin "memory">
         <Type "memory">
           Instance "cached"
-        WarningMin 100000000
+          WarningMin 100000000
         </Type>
       </Plugin>
     </Host>
@@@ -2085,7 -1610,6 +2087,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/common.c
@@@ -61,19 -61,6 +61,19 @@@ char *sstrncpy (char *dest, const char 
        return (dest);
  } /* char *sstrncpy */
  
 +int ssnprintf (char *dest, size_t n, const char *format, ...)
 +{
 +      int ret = 0;
 +      va_list ap;
 +
 +      va_start (ap, format);
 +      ret = vsnprintf (dest, n, format, ap);
 +      dest[n - 1] = '\0';
 +      va_end (ap);
 +
 +      return (ret);
 +} /* int ssnprintf */
 +
  char *sstrdup (const char *s)
  {
        char *r;
@@@ -104,7 -91,7 +104,7 @@@ char *sstrerror (int errnum, char *buf
                pthread_mutex_lock (&strerror_r_lock);
  
                temp = strerror (errnum);
 -              strncpy (buf, temp, buflen);
 +              sstrncpy (buf, temp, buflen);
  
                pthread_mutex_unlock (&strerror_r_lock);
        }
                if (buf[0] == '\0')
                {
                        if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
 -                              strncpy (buf, temp, buflen);
 +                              sstrncpy (buf, temp, buflen);
                        else
 -                              strncpy (buf, "strerror_r did not return "
 +                              sstrncpy (buf, "strerror_r did not return "
                                                "an error message", buflen);
                }
        }
  #else
        if (strerror_r (errnum, buf, buflen) != 0)
        {
 -              snprintf (buf, buflen, "Error #%i; "
 +              ssnprintf (buf, buflen, "Error #%i; "
                                "Additionally, strerror_r failed.",
                                errnum);
        }
  #endif /* STRERROR_R_CHAR_P */
  
 -      buf[buflen - 1] = '\0';
        return (buf);
  } /* char *sstrerror */
  
@@@ -388,7 -376,7 +388,7 @@@ int check_create_dir (const char *file_
  
        if ((len = strlen (file_orig)) < 1)
                return (-1);
 -      else if (len >= 512)
 +      else if (len >= sizeof (file_copy))
                return (-1);
  
        /*
        /*
         * Create a copy for `strtok_r' to destroy
         */
 -      strncpy (file_copy, file_orig, 512);
 -      file_copy[511] = '\0';
 +      sstrncpy (file_copy, file_orig, sizeof (file_copy));
  
        /*
         * Break into components. This will eat up several slashes in a row and
@@@ -488,7 -477,8 +488,7 @@@ int get_kstat (kstat_t **ksp_ptr, char 
        if (kc == NULL)
                return (-1);
  
 -      snprintf (ident, 128, "%s,%i,%s", module, instance, name);
 -      ident[127] = '\0';
 +      ssnprintf (ident, sizeof (ident), "%s,%i,%s", module, instance, name);
  
        if (*ksp_ptr == NULL)
        {
@@@ -537,12 -527,12 +537,12 @@@ long long get_kstat_value (kstat_t *ksp
  #else
        if (ksp == NULL)
        {
-               fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
+               ERROR ("ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
                return (-1LL);
        }
        else if (ksp->ks_type != KSTAT_TYPE_NAMED)
        {
-               fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
+               ERROR ("ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
                return (-1LL);
        }
  #endif
@@@ -673,21 -663,21 +673,21 @@@ int format_name (char *ret, int ret_len
        if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
        {
                if ((type_instance == NULL) || (strlen (type_instance) == 0))
 -                      status = snprintf (ret, ret_len, "%s/%s/%s",
 +                      status = ssnprintf (ret, ret_len, "%s/%s/%s",
                                        hostname, plugin, type);
                else
 -                      status = snprintf (ret, ret_len, "%s/%s/%s-%s",
 +                      status = ssnprintf (ret, ret_len, "%s/%s/%s-%s",
                                        hostname, plugin, type,
                                        type_instance);
        }
        else
        {
                if ((type_instance == NULL) || (strlen (type_instance) == 0))
 -                      status = snprintf (ret, ret_len, "%s/%s-%s/%s",
 +                      status = ssnprintf (ret, ret_len, "%s/%s-%s/%s",
                                        hostname, plugin, plugin_instance,
                                        type);
                else
 -                      status = snprintf (ret, ret_len, "%s/%s-%s/%s-%s",
 +                      status = ssnprintf (ret, ret_len, "%s/%s-%s/%s-%s",
                                        hostname, plugin, plugin_instance,
                                        type, type_instance);
        }
@@@ -848,75 -838,26 +848,75 @@@ int notification_init (notification_t *
        n->severity = severity;
  
        if (message != NULL)
 -              strncpy (n->message, message, sizeof (n->message));
 +              sstrncpy (n->message, message, sizeof (n->message));
        if (host != NULL)
 -              strncpy (n->host, host, sizeof (n->host));
 +              sstrncpy (n->host, host, sizeof (n->host));
        if (plugin != NULL)
 -              strncpy (n->plugin, plugin, sizeof (n->plugin));
 +              sstrncpy (n->plugin, plugin, sizeof (n->plugin));
        if (plugin_instance != NULL)
 -              strncpy (n->plugin_instance, plugin_instance,
 +              sstrncpy (n->plugin_instance, plugin_instance,
                                sizeof (n->plugin_instance));
        if (type != NULL)
 -              strncpy (n->type, type, sizeof (n->type));
 +              sstrncpy (n->type, type, sizeof (n->type));
        if (type_instance != NULL)
 -              strncpy (n->type_instance, type_instance,
 +              sstrncpy (n->type_instance, type_instance,
                                sizeof (n->type_instance));
  
 -      n->message[sizeof (n->message) - 1] = '\0';
 -      n->host[sizeof (n->host) - 1] = '\0';
 -      n->plugin[sizeof (n->plugin) - 1] = '\0';
 -      n->plugin_instance[sizeof (n->plugin_instance) - 1] = '\0';
 -      n->type[sizeof (n->type) - 1] = '\0';
 -      n->type_instance[sizeof (n->type_instance) - 1] = '\0';
 -
        return (0);
  } /* int notification_init */
 +
 +int walk_directory (const char *dir, dirwalk_callback_f callback,
 +              void *user_data)
 +{
 +      struct dirent *ent;
 +      DIR *dh;
 +      int success;
 +      int failure;
 +
 +      success = 0;
 +      failure = 0;
 +
 +      if ((dh = opendir (dir)) == NULL)
 +      {
 +              char errbuf[1024];
 +              ERROR ("walk_directory: Cannot open '%s': %s", dir,
 +                              sstrerror (errno, errbuf, sizeof (errbuf)));
 +              return -1;
 +      }
 +
 +      while ((ent = readdir (dh)) != NULL)
 +      {
 +              int status;
 +
 +              if (ent->d_name[0] == '.')
 +                      continue;
 +
 +              status = (*callback) (dir, ent->d_name, user_data);
 +              if (status != 0)
 +                      failure++;
 +              else
 +                      success++;
 +      }
 +
 +      closedir (dh);
 +
 +      if ((success == 0) && (failure > 0))
 +              return (-1);
 +      return (0);
 +}
 +
 +int read_file_contents (const char *filename, char *buf, int bufsize)
 +{
 +      FILE *fh;
 +      int n;
 +
 +      if ((fh = fopen (filename, "r")) == NULL)
 +              return -1;
 +
 +      n = fread(buf, 1, bufsize, fh);
 +      fclose(fh);
 +
 +      return n;
 +}
 +
 +
diff --combined src/email.c
@@@ -187,12 -187,18 +187,18 @@@ static int email_config (const char *ke
                        fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
                                        "value %li, will use default %i.\n",
                                        tmp, MAX_CONNS);
+                       ERROR ("email plugin: `MaxConns' was set to invalid "
+                                       "value %li, will use default %i.\n",
+                                       tmp, MAX_CONNS);
                        max_conns = MAX_CONNS;
                }
                else if (tmp > MAX_CONNS_LIMIT) {
                        fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
                                        "value %li, will use hardcoded limit %i.\n",
                                        tmp, MAX_CONNS_LIMIT);
+                       ERROR ("email plugin: `MaxConns' was set to invalid "
+                                       "value %li, will use hardcoded limit %i.\n",
+                                       tmp, MAX_CONNS_LIMIT);
                        max_conns = MAX_CONNS_LIMIT;
                }
                else {
@@@ -390,7 -396,9 +396,7 @@@ static void *open_connection (void *arg
        }
  
        addr.sun_family = AF_UNIX;
 -
 -      strncpy (addr.sun_path, path, (size_t)(UNIX_PATH_MAX - 1));
 -      addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
 +      sstrncpy (addr.sun_path, path, (size_t)(UNIX_PATH_MAX - 1));
  
        errno = 0;
        if (-1 == bind (connector_socket, (struct sockaddr *)&addr,
@@@ -653,10 -661,9 +659,10 @@@ static void email_submit (const char *t
        vl.time = time (NULL);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "email", sizeof (vl.plugin));
 -      strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 +      sstrncpy (vl.type, type, sizeof (vl.type));
 +      sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
  
 -      plugin_dispatch_values (type, &vl);
 +      plugin_dispatch_values (&vl);
  } /* void email_submit */
  
  /* Copy list l1 to list l2. l2 may partly exist already, but it is assumed
diff --combined src/ipmi.c
@@@ -1,7 -1,6 +1,7 @@@
  /**
   * collectd - src/ipmi.c
   * Copyright (C) 2008  Florian octo Forster
 + * Copyright (C) 2008  Peter Holik
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the
@@@ -18,7 -17,6 +18,7 @@@
   *
   * Authors:
   *   Florian octo Forster <octo at verplant.org>
 + *   Peter Holik <peter at holik.at>
   **/
  
  #include "collectd.h"
@@@ -43,9 -41,6 +43,9 @@@ typedef struct c_ipmi_sensor_list_s c_i
  struct c_ipmi_sensor_list_s
  {
    ipmi_sensor_id_t sensor_id;
 +  char sensor_name[DATA_MAX_NAME_LEN];
 +  char sensor_type[DATA_MAX_NAME_LEN];
 +  int sensor_not_present;
    c_ipmi_sensor_list_t *next;
  };
  
  static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
  static c_ipmi_sensor_list_t *sensor_list = NULL;
  
 +static int c_ipmi_init_in_progress = 0;
  static int c_ipmi_active = 0;
  static pthread_t thread_id = (pthread_t) 0;
  
  static const char *config_keys[] =
  {
        "Sensor",
 -      "IgnoreSelected"
 +      "IgnoreSelected",
 +      "NotifySensorAdd",
 +      "NotifySensorRemove",
 +      "NotifySensorNotPresent"
  };
  static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
  
  static ignorelist_t *ignorelist = NULL;
  
 +static int c_ipmi_nofiy_add = 0;
 +static int c_ipmi_nofiy_remove = 0;
 +static int c_ipmi_nofiy_notpresent = 0;
 +
  /*
   * Misc private functions
   */
@@@ -119,73 -106,36 +119,73 @@@ static void sensor_read_handler (ipmi_s
    value_t values[1];
    value_list_t vl = VALUE_LIST_INIT;
  
 -  char sensor_name[IPMI_SENSOR_NAME_LEN];
 -  char *sensor_name_ptr;
 -  int sensor_type;
 -  const char *type;
 -
 -  memset (sensor_name, 0, sizeof (sensor_name));
 -  ipmi_sensor_get_name (sensor, sensor_name, sizeof (sensor_name));
 -  sensor_name[sizeof (sensor_name) - 1] = 0;
 -
 -  sensor_name_ptr = strstr (sensor_name, ").");
 -  if (sensor_name_ptr == NULL)
 -    sensor_name_ptr = sensor_name;
 -  else
 -    sensor_name_ptr += 2;
 +  c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
  
    if (err != 0)
    {
 -    INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
 -        "because it failed with status %#x.",
 -        sensor_name_ptr, err);
 -    sensor_list_remove (sensor);
 +    if ((err & 0xff) == IPMI_NOT_PRESENT_CC)
 +    {
 +      if (list_item->sensor_not_present == 0)
 +      {
 +        list_item->sensor_not_present = 1;
 +
 +        INFO ("ipmi plugin: sensor_read_handler: sensor %s "
 +            "not present.", list_item->sensor_name);
 +
 +        if (c_ipmi_nofiy_notpresent)
 +        {
 +          notification_t n = { NOTIF_WARNING, time(NULL), "", "", "ipmi",
 +            "", "", "", NULL };
 +
 +          sstrncpy (n.host, hostname_g, sizeof (n.host));
 +          sstrncpy (n.type_instance, list_item->sensor_name,
 +              sizeof (n.type_instance));
 +          sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
 +          ssnprintf (n.message, sizeof (n.message),
 +              "sensor %s not present", list_item->sensor_name);
 +
 +          plugin_dispatch_notification (&n);
 +        }
 +      }
 +    }
 +    else
 +    {
 +      INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
 +          "because it failed with status %#x.",
 +          list_item->sensor_name, err);
 +      sensor_list_remove (sensor);
 +    }
      return;
    }
 +  else if (list_item->sensor_not_present == 1)
 +  {
 +    list_item->sensor_not_present = 0;
 +
 +    INFO ("ipmi plugin: sensor_read_handler: sensor %s present.",
 +        list_item->sensor_name);
 +
 +    if (c_ipmi_nofiy_notpresent)
 +    {
 +      notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi",
 +        "", "", "", NULL };
 +
 +      sstrncpy (n.host, hostname_g, sizeof (n.host));
 +      sstrncpy (n.type_instance, list_item->sensor_name,
 +          sizeof (n.type_instance));
 +      sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
 +      ssnprintf (n.message, sizeof (n.message),
 +          "sensor %s present", list_item->sensor_name);
 +
 +      plugin_dispatch_notification (&n);
 +    }
 +  }
  
    if (value_present != IPMI_BOTH_VALUES_PRESENT)
    {
      INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
          "because it provides %s. If you need this sensor, "
          "please file a bug report.",
 -        sensor_name_ptr,
 +        list_item->sensor_name,
          (value_present == IPMI_RAW_VALUE_PRESENT)
          ? "only the raw value"
          : "no value");
      return;
    }
  
 -  /* Both `ignorelist' and `plugin_instance' may be NULL. */
 -  if (ignorelist_match (ignorelist, sensor_name_ptr) != 0)
 +  values[0].gauge = value;
 +
 +  vl.values = values;
 +  vl.values_len = 1;
 +  vl.time = time (NULL);
 +
 +  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
 +  sstrncpy (vl.plugin, "ipmi", sizeof (vl.plugin));
 +  sstrncpy (vl.type, list_item->sensor_type, sizeof (vl.type));
 +  sstrncpy (vl.type_instance, list_item->sensor_name, sizeof (vl.type_instance));
 +
 +  plugin_dispatch_values (&vl);
 +} /* void sensor_read_handler */
 +
 +static int sensor_list_add (ipmi_sensor_t *sensor)
 +{
 +  ipmi_sensor_id_t sensor_id;
 +  c_ipmi_sensor_list_t *list_item;
 +  c_ipmi_sensor_list_t *list_prev;
 +
 +  char sensor_name[DATA_MAX_NAME_LEN];
 +  char *sensor_name_ptr;
 +  int sensor_type, len;
 +  const char *type;
 +  ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
 +
 +  sensor_id = ipmi_sensor_convert_to_id (sensor);
 +
 +  memset (sensor_name, 0, sizeof (sensor_name));
 +  ipmi_sensor_get_name (sensor, sensor_name, sizeof (sensor_name));
 +  sensor_name[sizeof (sensor_name) - 1] = 0;
 +
 +  len = DATA_MAX_NAME_LEN - strlen(sensor_name);
 +  strncat(sensor_name, " ", len--);
 +  strncat(sensor_name, ipmi_entity_get_entity_id_string(ent), len);
 +
 +  sensor_name_ptr = strstr (sensor_name, ").");
 +  if (sensor_name_ptr == NULL)
 +    sensor_name_ptr = sensor_name;
 +  else
    {
 -    sensor_list_remove (sensor);
 -    return;
 +    char *sensor_name_ptr_id = strstr (sensor_name, "(");
 +
 +    sensor_name_ptr += 2;
 +    len = DATA_MAX_NAME_LEN - strlen(sensor_name);
 +    strncat(sensor_name, " ", len--);
 +    strncat(sensor_name, sensor_name_ptr_id, 
 +      MIN(sensor_name_ptr - sensor_name_ptr_id - 1, len));
    }
  
 +  /* Both `ignorelist' and `plugin_instance' may be NULL. */
 +  if (ignorelist_match (ignorelist, sensor_name_ptr) != 0)
 +    return (0);
 +
    /* FIXME: Use rate unit or base unit to scale the value */
  
    sensor_type = ipmi_sensor_get_sensor_type (sensor);
      default:
        {
          const char *sensor_type_str;
 -        
 +
          sensor_type_str = ipmi_sensor_get_sensor_type_string (sensor);
 -        INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
 +        INFO ("ipmi plugin: sensor_list_add: Ignore sensor %s, "
              "because I don't know how to handle its type (%#x, %s). "
              "If you need this sensor, please file a bug report.",
              sensor_name_ptr, sensor_type, sensor_type_str);
 -        sensor_list_remove (sensor);
 -        return;
 +        return (-1);
        }
    } /* switch (sensor_type) */
  
 -  values[0].gauge = value;
 -
 -  vl.values = values;
 -  vl.values_len = 1;
 -  vl.time = time (NULL);
 -
 -  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
 -  sstrncpy (vl.plugin, "ipmi", sizeof (vl.plugin));
 -  sstrncpy (vl.type_instance, sensor_name_ptr, sizeof (vl.type_instance));
 -
 -  plugin_dispatch_values (type, &vl);
 -} /* void sensor_read_handler */
 -
 -static int sensor_list_add (ipmi_sensor_t *sensor)
 -{
 -  ipmi_sensor_id_t sensor_id;
 -  c_ipmi_sensor_list_t *list_item;
 -  c_ipmi_sensor_list_t *list_prev;
 -
 -  sensor_id = ipmi_sensor_convert_to_id (sensor);
 -
    pthread_mutex_lock (&sensor_list_lock);
  
    list_prev = NULL;
    else
      sensor_list = list_item;
  
 +  sstrncpy (list_item->sensor_name, sensor_name_ptr,
 +            sizeof (list_item->sensor_name));
 +  sstrncpy (list_item->sensor_type, type, sizeof (list_item->sensor_type));
 +
    pthread_mutex_unlock (&sensor_list_lock);
  
 +  if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0))
 +  {
 +    notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi",
 +                         "", "", "", NULL };
 +
 +    sstrncpy (n.host, hostname_g, sizeof (n.host));
 +    sstrncpy (n.type_instance, list_item->sensor_name,
 +              sizeof (n.type_instance));
 +    sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
 +    ssnprintf (n.message, sizeof (n.message),
 +              "sensor %s added", list_item->sensor_name);
 +
 +    plugin_dispatch_notification (&n);
 +  }
 +
    return (0);
  } /* int sensor_list_add */
  
@@@ -373,21 -279,6 +373,21 @@@ static int sensor_list_remove (ipmi_sen
  
    pthread_mutex_unlock (&sensor_list_lock);
  
 +  if (c_ipmi_nofiy_remove && c_ipmi_active)
 +  {
 +    notification_t n = { NOTIF_WARNING, time(NULL), "", "",
 +                         "ipmi", "", "", "", NULL };
 +
 +    sstrncpy (n.host, hostname_g, sizeof (n.host));
 +    sstrncpy (n.type_instance, list_item->sensor_name,
 +              sizeof (n.type_instance));
 +    sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
 +    ssnprintf (n.message, sizeof (n.message),
 +              "sensor %s removed", list_item->sensor_name);
 +
 +    plugin_dispatch_notification (&n);
 +  }
 +
    free (list_item);
    return (0);
  } /* int sensor_list_remove */
@@@ -403,7 -294,7 +403,7 @@@ static int sensor_list_read_all (void
        list_item = list_item->next)
    {
      ipmi_sensor_id_get_reading (list_item->sensor_id,
 -        sensor_read_handler, /* user data = */ NULL);
 +        sensor_read_handler, /* user data = */ list_item);
    } /* for (list_item) */
  
    pthread_mutex_unlock (&sensor_list_lock);
@@@ -494,7 -385,7 +494,7 @@@ static void domain_connection_change_ha
  {
    int status;
  
-   printf ("domain_connection_change_handler (domain = %p, err = %i, "
+   DEBUG ("domain_connection_change_handler (domain = %p, err = %i, "
        "conn_num = %u, port_num = %u, still_connected = %i, "
        "user_data = %p);\n",
        (void *) domain, err, conn_num, port_num, still_connected, user_data);
@@@ -561,7 -452,7 +561,7 @@@ static void *thread_main (void *user_da
    status = thread_init (&os_handler);
    if (status != 0)
    {
-     fprintf (stderr, "ipmi plugin: thread_init failed.\n");
+     ERROR ("ipmi plugin: thread_init failed.\n");
      return ((void *) -1);
    }
  
@@@ -591,32 -482,11 +591,32 @@@ static int c_ipmi_config (const char *k
    {
      int invert = 1;
      if ((strcasecmp ("True", value) == 0)
 -      || (strcasecmp ("Yes", value) == 0)
 -      || (strcasecmp ("On", value) == 0))
 +        || (strcasecmp ("Yes", value) == 0)
 +        || (strcasecmp ("On", value) == 0))
        invert = 0;
      ignorelist_set_invert (ignorelist, invert);
    }
 +  else if (strcasecmp ("NotifySensorAdd", key) == 0)
 +  {
 +    if ((strcasecmp ("True", value) == 0)
 +        || (strcasecmp ("Yes", value) == 0)
 +        || (strcasecmp ("On", value) == 0))
 +      c_ipmi_nofiy_add = 1;
 +  }
 +  else if (strcasecmp ("NotifySensorRemove", key) == 0)
 +  {
 +    if ((strcasecmp ("True", value) == 0)
 +        || (strcasecmp ("Yes", value) == 0)
 +        || (strcasecmp ("On", value) == 0))
 +      c_ipmi_nofiy_remove = 1;
 +  }
 +  else if (strcasecmp ("NotifySensorNotPresent", key) == 0)
 +  {
 +    if ((strcasecmp ("True", value) == 0)
 +        || (strcasecmp ("Yes", value) == 0)
 +        || (strcasecmp ("On", value) == 0))
 +      c_ipmi_nofiy_notpresent = 1;
 +  }
    else
    {
      return (-1);
@@@ -629,9 -499,6 +629,9 @@@ static int c_ipmi_init (void
  {
    int status;
  
 +  /* Don't send `ADD' notifications during startup (~ 1 minute) */
 +  c_ipmi_init_in_progress = 1 + (60 / interval_g);
 +
    c_ipmi_active = 1;
  
    status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
@@@ -656,12 -523,7 +656,12 @@@ static int c_ipmi_read (void
    }
  
    sensor_list_read_all ();
 -  
 +
 +  if (c_ipmi_init_in_progress > 0)
 +    c_ipmi_init_in_progress--;
 +  else
 +    c_ipmi_init_in_progress = 0;
 +
    return (0);
  } /* int c_ipmi_read */
  
diff --combined src/rrdtool.c
@@@ -1,6 -1,6 +1,6 @@@
  /**
   * collectd - src/rrdtool.c
 - * Copyright (C) 2006,2007  Florian octo Forster
 + * Copyright (C) 2006-2008  Florian octo Forster
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the
@@@ -42,19 -42,11 +42,19 @@@ struct rrd_cache_
        enum
        {
                FLAG_NONE   = 0x00,
 -              FLAG_QUEUED = 0x01
 +              FLAG_QUEUED = 0x01,
 +              FLAG_FLUSHQ = 0x02
        } flags;
  };
  typedef struct rrd_cache_s rrd_cache_t;
  
 +enum rrd_queue_dir_e
 +{
 +  QUEUE_INSERT_FRONT,
 +  QUEUE_INSERT_BACK
 +};
 +typedef enum rrd_queue_dir_e rrd_queue_dir_t;
 +
  struct rrd_queue_s
  {
        char *filename;
@@@ -95,8 -87,7 +95,8 @@@ static const char *config_keys[] 
        "HeartBeat",
        "RRARows",
        "RRATimespan",
 -      "XFF"
 +      "XFF",
 +      "WritesPerSecond"
  };
  static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
  
@@@ -108,7 -99,6 +108,7 @@@ static int     stepsize  = 0
  static int     heartbeat = 0;
  static int     rrarows   = 1200;
  static double  xff       = 0.1;
 +static double  write_rate = 0.0;
  
  /* XXX: If you need to lock both, cache_lock and queue_lock, at the same time,
   * ALWAYS lock `cache_lock' first! */
@@@ -120,8 -110,6 +120,8 @@@ static pthread_mutex_t cache_lock = PTH
  
  static rrd_queue_t    *queue_head = NULL;
  static rrd_queue_t    *queue_tail = NULL;
 +static rrd_queue_t    *flushq_head = NULL;
 +static rrd_queue_t    *flushq_tail = NULL;
  static pthread_t       queue_thread = 0;
  static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
  static pthread_cond_t  queue_cond = PTHREAD_COND_INITIALIZER;
@@@ -223,7 -211,7 +223,7 @@@ static int rra_get (char ***ret, const 
                        if (rra_num >= rra_max)
                                break;
  
 -                      if (snprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u",
 +                      if (ssnprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u",
                                                rra_types[j], xff,
                                                cdp_len, cdp_num) >= sizeof (buffer))
                        {
@@@ -300,16 -288,22 +300,16 @@@ static int ds_get (char ***ret, const d
                        sstrncpy (min, "U", sizeof (min));
                }
                else
 -              {
 -                      snprintf (min, sizeof (min), "%lf", d->min);
 -                      min[sizeof (min) - 1] = '\0';
 -              }
 +                      ssnprintf (min, sizeof (min), "%lf", d->min);
  
                if (isnan (d->max))
                {
                        sstrncpy (max, "U", sizeof (max));
                }
                else
 -              {
 -                      snprintf (max, sizeof (max), "%lf", d->max);
 -                      max[sizeof (max) - 1] = '\0';
 -              }
 +                      ssnprintf (max, sizeof (max), "%lf", d->max);
  
 -              status = snprintf (buffer, sizeof (buffer),
 +              status = ssnprintf (buffer, sizeof (buffer),
                                "DS:%s:%s:%i:%s:%s",
                                d->name, type,
                                (heartbeat > 0) ? heartbeat : (2 * vl->interval),
  
  #if HAVE_THREADSAFE_LIBRRD
  static int srrd_create (char *filename, unsigned long pdp_step, time_t last_up,
 -              int argc, char **argv)
 +              int argc, const char **argv)
  {
        int status;
  
        return (status);
  } /* int srrd_create */
  
 -static int srrd_update (char *filename, char *template, int argc, char **argv)
 +static int srrd_update (char *filename, char *template,
 +              int argc, const char **argv)
  {
        int status;
  
  
  #else /* !HAVE_THREADSAFE_LIBRRD */
  static int srrd_create (char *filename, unsigned long pdp_step, time_t last_up,
 -              int argc, char **argv)
 +              int argc, const char **argv)
  {
        int status;
  
        if (last_up == 0)
                last_up = time (NULL) - 10;
  
 -      snprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step);
 -      pdp_step_str[sizeof (pdp_step_str) - 1] = '\0';
 -      snprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up);
 -      last_up_str[sizeof (last_up_str) - 1] = '\0';
 +      ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step);
 +      ssnprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up);
  
        new_argv[0] = "create";
        new_argv[1] = filename;
        return (status);
  } /* int srrd_create */
  
 -static int srrd_update (char *filename, char *template, int argc, char **argv)
 +static int srrd_update (char *filename, char *template,
 +              int argc, const char **argv)
  {
        int status;
  
@@@ -519,7 -513,7 +519,7 @@@ static int rrd_create_file (char *filen
        status = srrd_create (filename,
                        (stepsize > 0) ? stepsize : vl->interval,
                        vl->time - 10,
 -                      argc, argv);
 +                      argc, (const char **)argv);
  
        free (argv);
        ds_free (ds_num, ds_def);
@@@ -537,7 -531,7 +537,7 @@@ static int value_list_to_string (char *
  
        memset (buffer, '\0', buffer_len);
  
 -      status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
 +      status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
        if ((status < 1) || (status >= buffer_len))
                return (-1);
        offset = status;
                        return (-1);
  
                if (ds->ds[i].type == DS_TYPE_COUNTER)
 -                      status = snprintf (buffer + offset, buffer_len - offset,
 +                      status = ssnprintf (buffer + offset, buffer_len - offset,
                                        ":%llu", vl->values[i].counter);
                else
 -                      status = snprintf (buffer + offset, buffer_len - offset,
 +                      status = ssnprintf (buffer + offset, buffer_len - offset,
                                        ":%lf", vl->values[i].gauge);
  
                if ((status < 1) || (status >= (buffer_len - offset)))
@@@ -572,35 -566,35 +572,35 @@@ static int value_list_to_filename (cha
  
        if (datadir != NULL)
        {
 -              status = snprintf (buffer + offset, buffer_len - offset,
 +              status = ssnprintf (buffer + offset, buffer_len - offset,
                                "%s/", datadir);
                if ((status < 1) || (status >= buffer_len - offset))
                        return (-1);
                offset += status;
        }
  
 -      status = snprintf (buffer + offset, buffer_len - offset,
 +      status = ssnprintf (buffer + offset, buffer_len - offset,
                        "%s/", vl->host);
        if ((status < 1) || (status >= buffer_len - offset))
                return (-1);
        offset += status;
  
        if (strlen (vl->plugin_instance) > 0)
 -              status = snprintf (buffer + offset, buffer_len - offset,
 +              status = ssnprintf (buffer + offset, buffer_len - offset,
                                "%s-%s/", vl->plugin, vl->plugin_instance);
        else
 -              status = snprintf (buffer + offset, buffer_len - offset,
 +              status = ssnprintf (buffer + offset, buffer_len - offset,
                                "%s/", vl->plugin);
        if ((status < 1) || (status >= buffer_len - offset))
                return (-1);
        offset += status;
  
        if (strlen (vl->type_instance) > 0)
 -              status = snprintf (buffer + offset, buffer_len - offset,
 -                              "%s-%s.rrd", ds->type, vl->type_instance);
 +              status = ssnprintf (buffer + offset, buffer_len - offset,
 +                              "%s-%s.rrd", vl->type, vl->type_instance);
        else
 -              status = snprintf (buffer + offset, buffer_len - offset,
 -                              "%s.rrd", ds->type);
 +              status = ssnprintf (buffer + offset, buffer_len - offset,
 +                              "%s.rrd", vl->type);
        if ((status < 1) || (status >= buffer_len - offset))
                return (-1);
        offset += status;
  
  static void *rrd_queue_thread (void *data)
  {
 +        struct timeval tv_next_update;
 +        struct timeval tv_now;
 +
 +        gettimeofday (&tv_next_update, /* timezone = */ NULL);
 +
        while (42)
        {
                rrd_queue_t *queue_entry;
                int    values_num;
                int    i;
  
 -              /* XXX: If you need to lock both, cache_lock and queue_lock, at
 -               * the same time, ALWAYS lock `cache_lock' first! */
 -
 -              /* wait until an entry is available */
 -              pthread_mutex_lock (&queue_lock);
 -              while ((queue_head == NULL) && (do_shutdown == 0))
 -                      pthread_cond_wait (&queue_cond, &queue_lock);
 -
 -              /* We're in the shutdown phase */
 -              if (queue_head == NULL)
 -              {
 -                      pthread_mutex_unlock (&queue_lock);
 -                      break;
 -              }
 -
 -              /* Dequeue the first entry */
 -              queue_entry = queue_head;
 -              if (queue_head == queue_tail)
 -                      queue_head = queue_tail = NULL;
 -              else
 -                      queue_head = queue_head->next;
 +                pthread_mutex_lock (&queue_lock);
 +                /* Wait for values to arrive */
 +                while (true)
 +                {
 +                  struct timespec ts_wait;
 +                  int status;
 +
 +                  while ((flushq_head == NULL) && (queue_head == NULL)
 +                      && (do_shutdown == 0))
 +                    pthread_cond_wait (&queue_cond, &queue_lock);
 +
 +                  if ((flushq_head == NULL) && (queue_head == NULL))
 +                    break;
 +
 +                  /* Don't delay if there's something to flush */
 +                  if (flushq_head != NULL)
 +                    break;
 +
 +                  /* Don't delay if we're shutting down */
 +                  if (do_shutdown != 0)
 +                    break;
 +
 +                  /* Don't delay if no delay was configured. */
 +                  if (write_rate <= 0.0)
 +                    break;
 +
 +                  gettimeofday (&tv_now, /* timezone = */ NULL);
 +                  status = timeval_sub_timespec (&tv_next_update, &tv_now,
 +                      &ts_wait);
 +                  /* We're good to go */
 +                  if (status != 0)
 +                    break;
 +
 +                  /* We're supposed to wait a bit with this update, so we'll
 +                   * wait for the next addition to the queue or to the end of
 +                   * the wait period - whichever comes first. */
 +                  ts_wait.tv_sec = tv_next_update.tv_sec;
 +                  ts_wait.tv_nsec = 1000 * tv_next_update.tv_usec;
 +
 +                  status = pthread_cond_timedwait (&queue_cond, &queue_lock,
 +                      &ts_wait);
 +                  if (status == ETIMEDOUT)
 +                    break;
 +                } /* while (true) */
 +
 +                /* XXX: If you need to lock both, cache_lock and queue_lock, at
 +                 * the same time, ALWAYS lock `cache_lock' first! */
 +
 +                /* We're in the shutdown phase */
 +                if ((flushq_head == NULL) && (queue_head == NULL))
 +                {
 +                  pthread_mutex_unlock (&queue_lock);
 +                  break;
 +                }
 +
 +                if (flushq_head != NULL)
 +                {
 +                  /* Dequeue the first flush entry */
 +                  queue_entry = flushq_head;
 +                  if (flushq_head == flushq_tail)
 +                    flushq_head = flushq_tail = NULL;
 +                  else
 +                    flushq_head = flushq_head->next;
 +                }
 +                else /* if (queue_head != NULL) */
 +                {
 +                  /* Dequeue the first regular entry */
 +                  queue_entry = queue_head;
 +                  if (queue_head == queue_tail)
 +                    queue_head = queue_tail = NULL;
 +                  else
 +                    queue_head = queue_head->next;
 +                }
  
                /* Unlock the queue again */
                pthread_mutex_unlock (&queue_lock);
  
                pthread_mutex_unlock (&cache_lock);
  
 +              /* Update `tv_next_update' */
 +              if (write_rate > 0.0) 
 +                {
 +                  gettimeofday (&tv_now, /* timezone = */ NULL);
 +                  tv_next_update.tv_sec = tv_now.tv_sec;
 +                  tv_next_update.tv_usec = tv_now.tv_usec
 +                    + ((suseconds_t) (1000000 * write_rate));
 +                  while (tv_next_update.tv_usec > 1000000)
 +                  {
 +                    tv_next_update.tv_sec++;
 +                    tv_next_update.tv_usec -= 1000000;
 +                  }
 +                }
 +
                /* Write the values to the RRD-file */
 -              srrd_update (queue_entry->filename, NULL, values_num, values);
 +              srrd_update (queue_entry->filename, NULL,
 +                              values_num, (const char **)values);
                DEBUG ("rrdtool plugin: queue thread: Wrote %i values to %s",
                                values_num, queue_entry->filename);
  
        return ((void *) 0);
  } /* void *rrd_queue_thread */
  
 -static int rrd_queue_cache_entry (const char *filename)
 +static int rrd_queue_enqueue (const char *filename,
 +    rrd_queue_t **head, rrd_queue_t **tail)
  {
 -      rrd_queue_t *queue_entry;
 +  rrd_queue_t *queue_entry;
  
 -      queue_entry = (rrd_queue_t *) malloc (sizeof (rrd_queue_t));
 -      if (queue_entry == NULL)
 -              return (-1);
 +  queue_entry = (rrd_queue_t *) malloc (sizeof (rrd_queue_t));
 +  if (queue_entry == NULL)
 +    return (-1);
  
 -      queue_entry->filename = strdup (filename);
 -      if (queue_entry->filename == NULL)
 -      {
 -              free (queue_entry);
 -              return (-1);
 -      }
 +  queue_entry->filename = strdup (filename);
 +  if (queue_entry->filename == NULL)
 +  {
 +    free (queue_entry);
 +    return (-1);
 +  }
  
 -      queue_entry->next = NULL;
 +  queue_entry->next = NULL;
  
 -      pthread_mutex_lock (&queue_lock);
 -      if (queue_tail == NULL)
 -              queue_head = queue_entry;
 -      else
 -              queue_tail->next = queue_entry;
 -      queue_tail = queue_entry;
 -      pthread_cond_signal (&queue_cond);
 -      pthread_mutex_unlock (&queue_lock);
 +  pthread_mutex_lock (&queue_lock);
  
 -      DEBUG ("rrdtool plugin: Put `%s' into the update queue", filename);
 +  if (*tail == NULL)
 +    *head = queue_entry;
 +  else
 +    (*tail)->next = queue_entry;
 +  *tail = queue_entry;
  
 -      return (0);
 -} /* int rrd_queue_cache_entry */
 +  pthread_cond_signal (&queue_cond);
 +  pthread_mutex_unlock (&queue_lock);
 +
 +  return (0);
 +} /* int rrd_queue_enqueue */
 +
 +static int rrd_queue_dequeue (const char *filename,
 +    rrd_queue_t **head, rrd_queue_t **tail)
 +{
 +  rrd_queue_t *this;
 +  rrd_queue_t *prev;
 +
 +  pthread_mutex_lock (&queue_lock);
 +
 +  prev = NULL;
 +  this = *head;
 +
 +  while (this != NULL)
 +  {
 +    if (strcmp (this->filename, filename) == 0)
 +      break;
 +    
 +    prev = this;
 +    this = this->next;
 +  }
 +
 +  if (this == NULL)
 +  {
 +    pthread_mutex_unlock (&queue_lock);
 +    return (-1);
 +  }
 +
 +  if (prev == NULL)
 +    *head = this->next;
 +  else
 +    prev->next = this->next;
 +
 +  if (this->next == NULL)
 +    *tail = prev;
 +
 +  pthread_mutex_unlock (&queue_lock);
 +
 +  sfree (this->filename);
 +  sfree (this);
 +
 +  return (0);
 +} /* int rrd_queue_dequeue */
  
  static void rrd_cache_flush (int timeout)
  {
        iter = c_avl_get_iterator (cache);
        while (c_avl_iterator_next (iter, (void *) &key, (void *) &rc) == 0)
        {
 -              if (rc->flags == FLAG_QUEUED)
 +              if (rc->flags != FLAG_NONE)
                        continue;
                else if ((now - rc->first_value) < timeout)
                        continue;
                else if (rc->values_num > 0)
                {
 -                      if (rrd_queue_cache_entry (key) == 0)
 +                      int status;
 +
 +                      status = rrd_queue_enqueue (key, &queue_head,  &queue_tail);
 +                      if (status == 0)
                                rc->flags = FLAG_QUEUED;
                }
                else /* ancient and no values -> waste of memory */
        cache_flush_last = now;
  } /* void rrd_cache_flush */
  
 +static int rrd_cache_flush_identifier (int timeout, const char *identifier)
 +{
 +  rrd_cache_t *rc;
 +  time_t now;
 +  int status;
 +  char key[2048];
 +
 +  if (identifier == NULL)
 +  {
 +    rrd_cache_flush (timeout);
 +    return (0);
 +  }
 +
 +  now = time (NULL);
 +
 +  if (datadir == NULL)
 +    snprintf (key, sizeof (key), "%s.rrd",
 +        identifier);
 +  else
 +    snprintf (key, sizeof (key), "%s/%s.rrd",
 +        datadir, identifier);
 +  key[sizeof (key) - 1] = 0;
 +
 +  status = c_avl_get (cache, key, (void *) &rc);
 +  if (status != 0)
 +  {
 +    WARNING ("rrdtool plugin: rrd_cache_flush_identifier: "
 +        "c_avl_get (%s) failed. Does that file really exist?",
 +        key);
 +    return (status);
 +  }
 +
 +  if (rc->flags == FLAG_FLUSHQ)
 +  {
 +    status = 0;
 +  }
 +  else if (rc->flags == FLAG_QUEUED)
 +  {
 +    rrd_queue_dequeue (key, &queue_head, &queue_tail);
 +    status = rrd_queue_enqueue (key, &flushq_head, &flushq_tail);
 +    if (status == 0)
 +      rc->flags = FLAG_FLUSHQ;
 +  }
 +  else if ((now - rc->first_value) < timeout)
 +  {
 +    status = 0;
 +  }
 +  else if (rc->values_num > 0)
 +  {
 +    status = rrd_queue_enqueue (key, &flushq_head, &flushq_tail);
 +    if (status == 0)
 +      rc->flags = FLAG_FLUSHQ;
 +  }
 +
 +  return (status);
 +} /* int rrd_cache_flush_identifier */
 +
  static int rrd_cache_insert (const char *filename,
                const char *value, time_t value_time)
  {
        }
  
        DEBUG ("rrdtool plugin: rrd_cache_insert: file = %s; "
 -                      "values_num = %i; age = %u;",
 +                      "values_num = %i; age = %lu;",
                        filename, rc->values_num,
 -                      rc->last_value - rc->first_value);
 +                      (unsigned long)(rc->last_value - rc->first_value));
  
        if ((rc->last_value - rc->first_value) >= cache_timeout)
        {
                /* XXX: If you need to lock both, cache_lock and queue_lock, at
                 * the same time, ALWAYS lock `cache_lock' first! */
 -              if (rc->flags != FLAG_QUEUED)
 +              if (rc->flags == FLAG_NONE)
                {
 -                      if (rrd_queue_cache_entry (filename) == 0)
 +                      int status;
 +
 +                      status = rrd_queue_enqueue (filename, &queue_head, &queue_tail);
 +                      if (status == 0)
                                rc->flags = FLAG_QUEUED;
                }
                else
                        ((time (NULL) - cache_flush_last) > cache_flush_timeout))
                rrd_cache_flush (cache_flush_timeout);
  
 -
        pthread_mutex_unlock (&cache_lock);
  
        return (0);
@@@ -1096,11 -913,6 +1096,11 @@@ static int rrd_write (const data_set_t 
        char         values[512];
        int          status;
  
 +      if (0 != strcmp (ds->type, vl->type)) {
 +              ERROR ("rrdtool plugin: DS type does not match value list type");
 +              return -1;
 +      }
 +
        if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
                return (-1);
  
        return (status);
  } /* int rrd_write */
  
 -static int rrd_flush (const int timeout)
 +static int rrd_flush (int timeout, const char *identifier)
  {
        pthread_mutex_lock (&cache_lock);
  
                return (0);
        }
  
 -      rrd_cache_flush (timeout);
 +      rrd_cache_flush_identifier (timeout, identifier);
 +
        pthread_mutex_unlock (&cache_lock);
        return (0);
  } /* int rrd_flush */
@@@ -1159,6 -970,8 +1159,8 @@@ static int rrd_config (const char *key
                {
                        fprintf (stderr, "rrdtool: `CacheTimeout' must "
                                        "be greater than 0.\n");
+                       ERROR ("rrdtool: `CacheTimeout' must "
+                                       "be greater than 0.\n");
                        return (1);
                }
                cache_timeout = tmp;
                {
                        fprintf (stderr, "rrdtool: `CacheFlush' must "
                                        "be greater than 0.\n");
+                       ERROR ("rrdtool: `CacheFlush' must "
+                                       "be greater than 0.\n");
                        return (1);
                }
                cache_flush_timeout = tmp;
                {
                        fprintf (stderr, "rrdtool: `RRARows' must "
                                        "be greater than 0.\n");
+                       ERROR ("rrdtool: `RRARows' must "
+                                       "be greater than 0.\n");
                        return (1);
                }
                rrarows = tmp;
                        if (tmp_alloc == NULL)
                        {
                                fprintf (stderr, "rrdtool: realloc failed.\n");
+                               ERROR ("rrdtool: realloc failed.\n");
                                free (value_copy);
                                return (1);
                        }
                {
                        fprintf (stderr, "rrdtool: `XFF' must "
                                        "be in the range 0 to 1 (exclusive).");
+                       ERROR ("rrdtool: `XFF' must "
+                                       "be in the range 0 to 1 (exclusive).");
                        return (1);
                }
                xff = tmp;
        }
 +      else if (strcasecmp ("WritesPerSecond", key) == 0)
 +      {
 +              double wps = atof (value);
 +
 +              if (wps < 0.0)
 +              {
 +                      fprintf (stderr, "rrdtool: `WritesPerSecond' must be "
 +                                      "greater than or equal to zero.");
 +                      return (1);
 +              }
 +              else if (wps == 0.0)
 +              {
 +                      write_rate = 0.0;
 +              }
 +              else
 +              {
 +                      write_rate = 1.0 / wps;
 +              }
 +      }
        else
        {
                return (-1);
diff --combined src/unixsock.c
@@@ -86,8 -86,8 +86,8 @@@ static int us_open_socket (void
  
        memset (&sa, '\0', sizeof (sa));
        sa.sun_family = AF_UNIX;
 -      strncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
 -                      sizeof (sa.sun_path) - 1);
 +      sstrncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
 +                      sizeof (sa.sun_path));
        /* unlink (sa.sun_path); */
  
        DEBUG ("unixsock plugin: socket path = %s", sa.sun_path);
@@@ -159,12 -159,15 +159,12 @@@ static void *us_handle_client (void *ar
  {
        int fd;
        FILE *fhin, *fhout;
 -      char buffer[1024];
 -      char *fields[128];
 -      int   fields_num;
  
        fd = *((int *) arg);
        free (arg);
        arg = NULL;
  
 -      DEBUG ("Reading from fd #%i", fd);
 +      DEBUG ("unixsock plugin: us_handle_client: Reading from fd #%i", fd);
  
        fhin  = fdopen (fd, "r");
        if (fhin == NULL)
  
        while (42)
        {
 -              int len;
 +              char buffer[1024];
 +              char buffer_copy[1024];
 +              char *fields[128];
 +              int   fields_num;
 +              int   len;
  
                errno = 0;
                if (fgets (buffer, sizeof (buffer), fhin) == NULL)
                if (len == 0)
                        continue;
  
 -              DEBUG ("fgets -> buffer = %s; len = %i;", buffer, len);
 +              sstrncpy (buffer_copy, buffer, sizeof (buffer_copy));
  
 -              fields_num = strsplit (buffer, fields,
 +              fields_num = strsplit (buffer_copy, fields,
                                sizeof (fields) / sizeof (fields[0]));
  
                if (fields_num < 1)
  
                if (strcasecmp (fields[0], "getval") == 0)
                {
 -                      handle_getval (fhout, fields, fields_num);
 +                      handle_getval (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "putval") == 0)
                {
 -                      handle_putval (fhout, fields, fields_num);
 +                      handle_putval (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "listval") == 0)
                {
 -                      handle_listval (fhout, fields, fields_num);
 +                      handle_listval (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "putnotif") == 0)
                {
 -                      handle_putnotif (fhout, fields, fields_num);
 +                      handle_putnotif (fhout, buffer);
                }
                else if (strcasecmp (fields[0], "flush") == 0)
                {
 -                      handle_flush (fhout, fields, fields_num);
 +                      handle_flush (fhout, buffer);
                }
                else
                {
                }
        } /* while (fgets) */
  
 -      DEBUG ("Exiting..");
 +      DEBUG ("unixsock plugin: us_handle_client: Exiting..");
        fclose (fhin);
        fclose (fhout);
  
@@@ -383,8 -382,15 +383,15 @@@ static int us_config (const char *key, 
  
  static int us_init (void)
  {
+       static int have_init = 0;
        int status;
  
+       /* Initialize only once. */
+       if (have_init != 0)
+               return (0);
+       have_init = 1;
        loop = 1;
  
        status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
diff --combined src/utils_dns.c
@@@ -34,7 -34,7 +34,8 @@@
   */
  
  #include "collectd.h"
+ #include "plugin.h"
 +#include "common.h"
  
  #if HAVE_NETINET_IN_SYSTM_H
  # include <netinet/in_systm.h>
@@@ -355,7 -355,6 +356,6 @@@ handle_dns(const char *buf, int len
  
      memcpy(&us, buf + 2, 2);
      us = ntohs(us);
-     fprintf (stderr, "Bytes 0, 1: 0x%04hx\n", us);
      qh.qr = (us >> 15) & 0x01;
      qh.opcode = (us >> 11) & 0x0F;
      qh.aa = (us >> 10) & 0x01;
      if (0 != x)
        return 0;
      if ('\0' == qh.qname[0])
 -      strncpy (qh.qname, ".", sizeof (qh.qname));
 +      sstrncpy (qh.qname, ".", sizeof (qh.qname));
      while ((t = strchr(qh.qname, '\n')))
        *t = ' ';
      while ((t = strchr(qh.qname, '\r')))
@@@ -649,7 -648,7 +649,7 @@@ void handle_pcap(u_char *udata, const s
  {
      int status;
  
-     fprintf (stderr, "handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n",
+     DEBUG ("handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n",
                    (void *) udata, (void *) hdr, (void *) pkt,
                    hdr->caplen);
  
            break;
  
        default:
-           fprintf (stderr, "unsupported data link type %d\n",
+           ERROR ("handle_pcap: unsupported data link type %d\n",
                    pcap_datalink(pcap_obj));
            status = 0;
            break;
@@@ -816,7 -815,8 +816,7 @@@ const char *qtype_str(int t
            case T_ANY:         return ("ANY"); /* ... 255 */
  #endif /* __BIND >= 19950621 */
            default:
 -                  snprintf (buf, 32, "#%i", t);
 -                  buf[31] = '\0';
 +                  ssnprintf (buf, sizeof (buf), "#%i", t);
                    return (buf);
      }; /* switch (t) */
      /* NOTREACHED */
@@@ -843,7 -843,7 +843,7 @@@ const char *opcode_str (int o
        return "Update";
        break;
      default:
 -      snprintf(buf, 30, "Opcode%d", o);
 +      ssnprintf(buf, sizeof (buf), "Opcode%d", o);
        return buf;
      }
      /* NOTREACHED */
@@@ -887,7 -887,8 +887,7 @@@ const char *rcode_str (int rcode
  #endif  /* RFC2136 rcodes */
  #endif /* __BIND >= 19950621 */
                default:
 -                      snprintf (buf, 32, "RCode%i", rcode);
 -                      buf[31] = '\0';
 +                      ssnprintf (buf, sizeof (buf), "RCode%i", rcode);
                        return (buf);
        }
        /* Never reached */