Merged `branches/log-mode' to `trunk'
authorocto <octo>
Fri, 14 Apr 2006 10:53:44 +0000 (10:53 +0000)
committerocto <octo>
Fri, 14 Apr 2006 10:53:44 +0000 (10:53 +0000)
1  2 
AUTHORS
src/collectd.c
src/collectd.conf.pod
src/collectd.h
src/collectd.pod
src/common.c
src/common.h
src/configfile.c
src/network.c
src/plugin.c

diff --combined AUTHORS
+++ b/AUTHORS
@@@ -22,9 -22,6 +22,9 @@@ tape module by
  users module by:
    Sebastian Harl <sh at tokkee.org>
  
 +vserver module by:
 +  Sebastian Harl <sh at tokkee.org>
 +
  PID-file patch by:
    Tommie Gannert <d00-tga at d.kth.se>
  
@@@ -35,6 -32,9 +35,9 @@@ many autotools related fixes, libltdl c
  contributed:
    Niki W. Waibel <niki.waibel at newlogic.com>
  
+ The `log' mode has been written by:
+   Christophe Kalt <collectd at klb.taranis.org>
  collectd is available at:
    <http://verplant.org/collectd/>
  
diff --combined src/collectd.c
@@@ -29,6 -29,8 +29,6 @@@
  #include "plugin.h"
  #include "configfile.h"
  
 -#include "ping.h"
 -
  static int loop = 0;
  
  #if HAVE_LIBKSTAT
@@@ -39,10 -41,7 +39,7 @@@ kstat_ctl_t *kc
   * exported variables
   */
  time_t curtime;
- #if HAVE_LIBRRD
- int operating_mode;
- #endif
+ int    operating_mode;
  
  static void sigIntHandler (int signal)
  {
@@@ -136,8 -135,6 +133,8 @@@ static void exit_usage (char *name
  #if COLLECT_DEBUG
                        "  Log-File          "LOGFILE"\n"
  #endif
 +                      "  Step              "COLLECTD_STEP" seconds\n"
 +                      "  Heartbeat         "COLLECTD_HEARTBEAT" seconds\n"
                        "\n"PACKAGE" "VERSION", http://verplant.org/collectd/\n"
                        "by Florian octo Forster <octo@verplant.org>\n"
                        "for contributions see `AUTHORS'\n");
  
  static int start_client (void)
  {
 -      int sleepingtime;
 +      int step;
 +
 +      struct timeval tv_now;
 +      struct timeval tv_next;
 +      struct timespec ts_wait;
 +
 +      step = atoi (COLLECTD_STEP);
 +      if (step <= 0)
 +              step = 10;
  
  #if HAVE_LIBKSTAT
        kc = NULL;
  
        while (loop == 0)
        {
 -              curtime = time (NULL);
 +              if (gettimeofday (&tv_next, NULL) < 0)
 +              {
 +                      syslog (LOG_ERR, "gettimeofday failed: %s", strerror (errno));
 +                      return (-1);
 +              }
 +              tv_next.tv_sec += step;
 +
  #if HAVE_LIBKSTAT
                update_kstat ();
  #endif
 +              /* `curtime' is used by many (all?) plugins as the
 +               * data-sample-time passed to RRDTool */
 +              curtime = time (NULL);
 +
 +              /* Issue all plugins */
                plugin_read_all ();
  
 -              sleepingtime = 10;
 -              while (sleepingtime != 0)
 +              if (gettimeofday (&tv_now, NULL) < 0)
                {
 -                      if (loop != 0)
 +                      syslog (LOG_ERR, "gettimeofday failed: %s", strerror (errno));
 +                      return (-1);
 +              }
 +
 +              if (timeval_sub_timespec (&tv_next, &tv_now, &ts_wait) != 0)
 +              {
 +                      syslog (LOG_WARNING, "No sleeping because `timeval_sub_timespec' returned non-zero!");
 +                      continue;
 +              }
 +
 +              while (nanosleep (&ts_wait, &ts_wait) == -1)
 +              {
 +                      if (errno != EINTR)
 +                      {
 +                              syslog (LOG_ERR, "nanosleep failed: %s", strerror (errno));
                                break;
 -                      sleepingtime = sleep (sleepingtime);
 +                      }
                }
        }
  
@@@ -435,7 -400,7 +432,7 @@@ int main (int argc, char **argv
  #if HAVE_LIBRRD
        if (operating_mode == MODE_SERVER)
                start_server ();
-       else /* if (operating_mode == MODE_CLIENT || operating_mode == MODE_LOCAL) */
+       else /* if (operating_mode == MODE_CLIENT || operating_mode == MODE_LOCAL || operating_mode == MODE_LOG) */
  #endif
                start_client ();
  
diff --combined src/collectd.conf.pod
@@@ -36,7 -36,7 +36,7 @@@ ignored
  
  =over 4
  
- =item B<Mode> (B<Local>|B<Client>|B<Server>)
+ =item B<Mode> (B<Local>|B<Client>|B<Server>|B<Log>)
  
  Sets the operating mode. See the section B<MODES> in L<collectd(1)> for a
  description. This option determines which other options are allowed. Defaults
@@@ -104,26 -104,6 +104,26 @@@ operating systems
  Some Plugins may register own options. These options must be inclosed in a
  C<Plugin>-Section. Which options exist depends on the plugin used:
  
 +=head2 Plugin C<apache>
 +
 +=over 4
 +
 +=item B<URL> I<http://host/mod_status?auto>
 +
 +Sets the URL of the C<mod_status> output. This needs to be the output generated
 +by C<ExtendedStatus on> and it needs to be the machine readable output
 +generated by appending the C<?auto> argument.
 +
 +=item B<User> I<Username>
 +
 +Optional user name needed for authentication.
 +
 +=item B<Password> I<Password>
 +
 +Optional password needed for authentication.
 +
 +=back
 +
  =head2 Plugin C<hddtemp>
  
  =over 4
@@@ -170,10 -150,6 +170,10 @@@ option for what this plugin does
  Host to ping periodically. This option may be repeated several times to ping
  multiple hosts.
  
 +=item B<TTL> I<0-255>
 +
 +Sets the Time-To-Live of generated ICMP packets.
 +
  =back
  
  =head1 SEE ALSO
diff --combined src/collectd.h
  #define MODE_SERVER 0x01
  #define MODE_CLIENT 0x02
  #define MODE_LOCAL  0x04
+ #define MODE_LOG    0x08
  
 +#ifndef COLLECTD_STEP
 +#  define COLLECTD_STEP "10"
 +#endif
 +
 +#ifndef COLLECTD_HEARTBEAT
 +#  define COLLECTD_HEARTBEAT "25"
 +#endif
 +
 +#ifndef COLLECTD_ROWS
 +#  define COLLECTD_ROWS "1200"
 +#endif
 +
 +#ifndef COLLECTD_XFF
 +#  define COLLECTD_XFF 0.1
 +#endif
 +
  extern time_t curtime;
  
  #ifdef HAVE_LIBRRD
diff --combined src/collectd.pod
@@@ -82,10 -82,6 +82,10 @@@ Network traffic (I<traffic>
  
  Number of users logged into the system (I<users>)
  
 +=item
 +
 +System ressources used by VServers (I<vserver>)
 +
  =back
  
  =head1 OPTIONS
@@@ -119,44 -115,30 +119,47 @@@ The simplest mode is the so called B<lo
  written in RRD files that reside in I<DataDir>. This is the default mode when
  collectd is linked against C<librrd>.
  
- The other two modes, B<client mode> and B<server mode>, are used to send data
over a network and receive it again.
+ The other modes, B<client mode> and B<server mode>, are used to send data over
+ a network and receive it again.
  
- In B<client mode> the daemon collects the data locally and sends it's results
+ In B<client mode> the daemon collects the data locally and sends its results
  to one or more network addresses. No RRD files are written in this case. This
  is the only mode available if collectd is not linked against C<librrd>.
  
  If started in B<server mode> the daemon will listen on one or more interfaces
  and write the data it receives to RRD files. No data is collected locally.
  
+ In the last mode, B<log mode>, data is collected locally and written in
+ text files that reside in I<DataDir>.
  Please refer to L<collectd.conf(5)> for the configuration options and default
  values.
  
  =head1 SPECIAL PLUGINS
  
 +=head2 apache
 +
 +This module connects to an Apache webserver and expects the output produced by
 +B<mod_status.c>. If requires B<libcurl> to set up the HTTP connection and issue
 +the request(s). The following is a sample config for the Apache webserver. The
 +use of C<ExtendedStatus on> is mandatory.
 +
 +  ExtendedStatus on
 +  <IfModule mod_status.c>
 +    <Location /mod_status>
 +      SetHandler server-status
 +    </Location>
 +  </IfModule>
 +
 +This plugin requires further configuration. Please read L<collectd.conf(5)>.
 +
  =head2 cpufreq
  
  This module reads F</sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq> (for
  the first CPU installed) to get the current CPU frequency. If this file does
  not exist make sure B<cpufreqd> (L<http://cpufreqd.sourceforge.net/>) or a
 -similar tool is installed.
 +similar tool is installed and an "cpu governor" (that's kernel module) is
 +loaded.
  
  =head2 mysql
  
@@@ -193,45 -175,28 +196,45 @@@ which may interfere with other statisti
  The B<hddtemp> homepage can be found at
  L<http://www.guzu.net/linux/hddtemp.php>.
  
 -=head1 RRD FILES
 +=head2 vserver
  
 -The RRD files are created automatically with the following RRAs:
 +B<VServer> support is only available for Linux. It cannot yet be found in a 
 +vanilla kernel, though. To make use of this plugin you need a kernel that has 
 +B<VServer> support built in, i.e. you need to apply the patches and compile 
 +your own kernel, which will then provide the /proc/virtual filesystem that is
 +required by this plugin.
  
 -  RRA:AVERAGE:0.2:6:1500
 -  RRA:AVERAGE:0.1:180:1680
 -  RRA:AVERAGE:0.1:2160:1520
 -  RRA:MIN:0.2:6:1500
 -  RRA:MIN:0.1:180:1680
 -  RRA:MIN:0.1:2160:1520
 -  RRA:MAX:0.2:6:1500
 -  RRA:MAX:0.1:180:1680
 -  RRA:MAX:0.1:2160:1520
 +The B<VServer> homepage can be found at L<http://linux-vserver.org/>.
  
 -Since collectd uses a 10 second I<step> the RRAs contain the following
 -timespans:
 +=head1 RRD FILES
  
 -  Resolution | Data points |  Timespan
 -  -----------+-------------+----------
 -  60 seconds |        1500 |  25 hours
 -  30 minutes |        1680 |  35 days
 -   6 hours   |        1520 | 380 days
 +The RRD files are created automatically. The size of the RRAs depend on the
 +compile time settings of I<step> and I<width>. With the default values (I<step>
 += B<10>, I<width> = B<1200>) the following RRAs are created:
 +
 +  RRA:AVERAGE:0.1:1:8640
 +  RRA:AVERAGE:0.1:50:1210
 +  RRA:AVERAGE:0.1:223:1202
 +  RRA:AVERAGE:0.1:2635:1201
 +  RRA:MIN:0.1:1:8640
 +  RRA:MIN:0.1:50:1210
 +  RRA:MIN:0.1:223:1202
 +  RRA:MIN:0.1:2635:1201
 +  RRA:MAX:0.1:1:8640
 +  RRA:MAX:0.1:50:1210
 +  RRA:MAX:0.1:223:1202
 +  RRA:MAX:0.1:2635:1201
 +
 +By default collectd uses a 10 second I<step>. Thus the RRAs contain the
 +following timespans. If you've changed the I<step> at compile time you will
 +have calculate resolution and timespan yourself.
 +
 +  PDP per CDP |  Resolution  | Data points | Timespan
 +  ------------+--------------+-------------+---------
 +            1 | 10.0 seconds !        8640 ! 1 day
 +           50 |  8.3 minutes |        1210 | 1 week
 +          223 | 37.2 minutes |        1202 | 1 month
 +         2635 |  7.3 hours   |        1201 | 1 year
  
  The DS'es depend on the module creating the RRD files:
  
  
    DS:users:GAUGE:25:0:65535
  
 +=item VServer load (F<vserver-I<E<lt>xidE<gt>>/load.rrd>)
 +
 +  DS:shortterm:GAUGE:25:0:100
 +  DS:midterm:GAUGE:25:0:100
 +  DS:longterm:GAUGE:25:0:100
 +
 +=item VServer threads (F<vserver-I<E<lt>xidE<gt>>/threads.rrd>)
 +
 +  DS:total:GAUGE:25:0:65535
 +  DS:running:GAUGE:25:0:65535
 +  DS:uninterruptible:GAUGE:25:0:65535
 +  DS:onhold:GAUGE:25:0:65535
 +
 +=item VServer network traffic (F<vserver-I<E<lt>xidE<gt>>/traffic-I<E<lt>nameE<gt>>.rrd>)
 +
 +  DS:incoming:COUNTER:25:0:9223372036854775807
 +  DS:outgoing:COUNTER:25:0:9223372036854775807
 +  DS:failed:COUNTER:25:0:9223372036854775807
 +
 +=item VServer processes (F<vserver-I<E<lt>xidE<gt>>/vs_processes.rrd>)
 +
 +  DS:total:GAUGE:25:0:65535
 +
 +=item VServer memory usage (F<vserver-I<E<lt>xidE<gt>>/vs_memory.rrd>)
 +
 +  DS:vm:GAUGE:25:0:9223372036854775807
 +  DS:vml:GAUGE:25:0:9223372036854775807
 +  DS:rss:GAUGE:25:0:9223372036854775807
 +  DS:anon:GAUGE:25:0:9223372036854775807
 +
  =back
  
  =head1 SEE ALSO
diff --combined src/common.c
  #include "common.h"
  #include "utils_debug.h"
  
 +#ifdef HAVE_MATH_H
 +#  include <math.h>
 +#endif
 +
+ extern int operating_mode;
  #ifdef HAVE_LIBKSTAT
  extern kstat_ctl_t *kc;
  #endif
  
  #ifdef HAVE_LIBRRD
 +#if 0
  static char *rra_def[] =
  {
 +              "RRA:AVERAGE:0.0:1:1500",
                "RRA:AVERAGE:0.2:6:1500",
                "RRA:AVERAGE:0.1:180:1680",
                "RRA:AVERAGE:0.1:2160:1520",
 +              "RRA:MIN:0.0:1:1500",
                "RRA:MIN:0.2:6:1500",
                "RRA:MIN:0.1:180:1680",
                "RRA:MIN:0.1:2160:1520",
 +              "RRA:MAX:0.0:1:1500",
                "RRA:MAX:0.2:6:1500",
                "RRA:MAX:0.1:180:1680",
                "RRA:MAX:0.1:2160:1520",
                NULL
  };
 -static int rra_num = 9;
 +static int rra_num = 12;
 +#endif
 +
 +static int rra_timespans[] =
 +{
 +      3600,
 +      86400,
 +      604800,
 +      2678400,
 +      31622400,
 +      0
 +};
 +static int rra_timespans_num = 5;
 +
 +static char *rra_types[] =
 +{
 +      "AVERAGE",
 +      "MIN",
 +      "MAX",
 +      NULL
 +};
 +static int rra_types_num = 3;
  #endif /* HAVE_LIBRRD */
  
  void sstrncpy (char *d, const char *s, int len)
@@@ -182,6 -155,27 +184,27 @@@ int strjoin (char *dst, size_t dst_len
        return (strlen (dst));
  }
  
+ int strsubstitute (char *str, char c_from, char c_to)
+ {
+       int ret;
+       if (str == NULL)
+               return (-1);
+       ret = 0;
+       while (*str != '\0')
+       {
+               if (*str == c_from)
+               {
+                       *str = c_to;
+                       ret++;
+               }
+               str++;
+       }
+       return (ret);
+ }
  int escape_slashes (char *buf, int buf_len)
  {
        int i;
        return (0);
  }
  
- #ifdef HAVE_LIBRRD
- int check_create_dir (const char *file_orig)
 +int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
 +{
 +      if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL))
 +              return (-2);
 +
 +      if ((tv0->tv_sec < tv1->tv_sec)
 +                      || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec)))
 +              return (-1);
 +
 +      ret->tv_sec  = tv0->tv_sec - tv1->tv_sec;
 +      ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec));
 +
 +      if (ret->tv_nsec < 0)
 +      {
 +              assert (ret->tv_sec > 0);
 +
 +              ret->tv_nsec += 1000000000;
 +              ret->tv_sec  -= 1;
 +      }
 +
 +      return (0);
 +}
 +
+ static int check_create_dir (const char *file_orig)
  {
        struct stat statbuf;
  
        return (0);
  }
  
- int rrd_create_file (char *filename, char **ds_def, int ds_num)
 +/* * * * *
 + * Magic *
 + * * * * */
 +int rra_get (char ***ret)
 +{
 +      static char **rra_def = NULL;
 +      static int rra_num = 0;
 +
 +      int rra_max = rra_timespans_num * rra_types_num;
 +
 +      int step;
 +      int rows;
 +      int span;
 +
 +      int cdp_num;
 +      int cdp_len;
 +      int i, j;
 +
 +      char buffer[64];
 +
 +      if ((rra_num != 0) && (rra_def != NULL))
 +      {
 +              *ret = rra_def;
 +              return (rra_num);
 +      }
 +
 +      if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
 +              return (-1);
 +      memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
 +
 +      step = atoi (COLLECTD_STEP);
 +      rows = atoi (COLLECTD_ROWS);
 +
 +      if ((step <= 0) || (rows <= 0))
 +      {
 +              *ret = NULL;
 +              return (-1);
 +      }
 +
 +      cdp_len = 0;
 +      for (i = 0; i < rra_timespans_num; i++)
 +      {
 +              span = rra_timespans[i];
 +
 +              if ((span / step) < rows)
 +                      continue;
 +
 +              if (cdp_len == 0)
 +                      cdp_len = 1;
 +              else
 +                      cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
 +
 +              cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
 +
 +              for (j = 0; j < rra_types_num; j++)
 +              {
 +                      if (rra_num >= rra_max)
 +                              break;
 +
 +                      if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
 +                                              rra_types[j], COLLECTD_XFF,
 +                                              cdp_len, cdp_num) >= sizeof (buffer))
 +                      {
 +                              syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
 +                              continue;
 +                      }
 +
 +                      rra_def[rra_num++] = sstrdup (buffer);
 +              }
 +      }
 +
 +#if COLLECT_DEBUG
 +      DBG ("rra_num = %i", rra_num);
 +      for (i = 0; i < rra_num; i++)
 +              DBG ("  %s", rra_def[i]);
 +#endif
 +
 +      *ret = rra_def;
 +      return (rra_num);
 +}
 +
+ static int log_create_file (char *filename, char **ds_def, int ds_num)
+ {
+       FILE *log;
+       int i;
+       log = fopen (filename, "w");
+       if (log == NULL)
+       {
+               syslog (LOG_WARNING, "Failed to create %s: %s", filename,
+                               strerror(errno));
+               return (-1);
+       }
+       fprintf (log, "epoch");
+       for (i = 0; i < ds_num; i++)
+       {
+               char *name;
+               char *tmp;
+               name = strchr (ds_def[i], ':');
+               if (name == NULL)
+               {
+                       syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
+                                       ds_def[i], filename);
+                       fclose(log);
+                       remove(filename);
+                       return (-1);
+               }
+               name += 1;
+               tmp = strchr (name, ':');
+               if (tmp == NULL)
+               {
+                       syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
+                                       ds_def[i], filename);
+                       fclose(log);
+                       remove(filename);
+                       return (-1);
+               }
+               /* The `%.*s' is needed because there is no null-byte behind
+                * the name. */
+               fprintf(log, ",%.*s", (tmp - name), name);
+       }
+       fprintf(log, "\n");
+       fclose(log);
+       return 0;
+ }
+ static int log_update_file (char *host, char *file, char *values,
+               char **ds_def, int ds_num)
+ {
+       char *tmp;
+       FILE *fp;
+       struct stat statbuf;
+       char full_file[1024];
+       /* Cook the values a bit: Substitute colons with commas */
+       strsubstitute (values, ':', ',');
+       /* host == NULL => local mode */
+       if (host != NULL)
+       {
+               if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
+                       return (-1);
+       }
+       else
+       {
+               if (snprintf (full_file, 1024, "%s", file) >= 1024)
+                       return (-1);
+       }
+       strncpy (full_file, file, 1024);
+       tmp = full_file + strlen (full_file) - 4;
+       assert (tmp > 0);
+       /* Change the filename for logfiles. */
+       if (strncmp (tmp, ".rrd", 4) == 0)
+       {
+               time_t now;
+               struct tm *tm;
+               /* TODO: Find a way to minimize the calls to `localtime', since
+                * they are pretty expensive.. */
+               now = time (NULL);
+               tm = localtime (&now);
+               strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
+               /* `localtime(3)' returns a pointer to static data,
+                * therefore the pointer may not be free'd. */
+       }
+       else
+               DBG ("The filename ends with `%s' which is unexpected.", tmp);
+       if (stat (full_file, &statbuf) == -1)
+       {
+               if (errno == ENOENT)
+               {
+                       if (log_create_file (full_file, ds_def, ds_num))
+                               return (-1);
+               }
+               else
+               {
+                       syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
+                       return (-1);
+               }
+       }
+       else if (!S_ISREG (statbuf.st_mode))
+       {
+               syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
+               return (-1);
+       }
+       fp = fopen (full_file, "a");
+       if (fp == NULL)
+       {
+               syslog (LOG_WARNING, "Failed to append to %s: %s", full_file,
+                               strerror(errno));
+               return (-1);
+       }
+       fprintf(fp, "%s\n", values);
+       fclose(fp);
+       return (0);
+ } /* int log_update_file */
+ #if HAVE_LIBRRD
+ static int rrd_create_file (char *filename, char **ds_def, int ds_num)
  {
        char **argv;
        int argc;
 +      char **rra_def;
 +      int rra_num;
        int i, j;
        int status = 0;
  
        if (check_create_dir (filename))
                return (-1);
  
 +      if ((rra_num = rra_get (&rra_def)) < 1)
 +      {
 +              syslog (LOG_ERR, "rra_create failed: Could not calculate RRAs");
 +              return (-1);
 +      }
 +
        argc = ds_num + rra_num + 4;
  
        if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
        argv[0] = "create";
        argv[1] = filename;
        argv[2] = "-s";
 -      argv[3] = "10";
 +      argv[3] = COLLECTD_STEP;
  
        j = 4;
        for (i = 0; i < ds_num; i++)
        }
  
        free (argv);
-       
        return (status);
  }
  #endif /* HAVE_LIBRRD */
  int rrd_update_file (char *host, char *file, char *values,
                char **ds_def, int ds_num)
  {
- #ifdef HAVE_LIBRRD
+ #if HAVE_LIBRRD
        struct stat statbuf;
        char full_file[1024];
        char *argv[4] = { "update", full_file, values, NULL };
+ #endif /* HAVE_LIBRRD */
  
+       /* I'd rather have a function `common_update_file' to make this
+        * decission, but for that we'd need to touch all plugins.. */
+       if (operating_mode == MODE_LOG)
+               return (log_update_file (host, file, values,
+                                       ds_def, ds_num));
+ #if HAVE_LIBRRD
        /* host == NULL => local mode */
        if (host != NULL)
        {
                syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ());
                return (-1);
        }
- #endif /* HAVE_LIBRRD */
        return (0);
+ /* #endif HAVE_LIBRRD */
+ #else
+       syslog (LOG_ERR, "`rrd_update_file' was called, but collectd isn't linked against librrd!");
+       return (-1);
+ #endif
  }
  
  #ifdef HAVE_LIBKSTAT
diff --combined src/common.h
@@@ -103,11 -103,8 +103,11 @@@ int strjoin (char *dst, size_t dst_len
   */
  int escape_slashes (char *buf, int buf_len);
  
- int rrd_update_file (char *host, char *file, char *values, char **ds_def,
-               int ds_num);
 +/* FIXME: `timeval_sub_timespec' needs a description */
 +int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret);
 +
+ int rrd_update_file (char *host, char *file, char *values,
+               char **ds_def, int ds_num);
  
  #ifdef HAVE_LIBKSTAT
  int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name);
diff --combined src/configfile.c
  #define ERR_NEEDS_ARG "Section `%s' needs an argument.\n"
  #define ERR_NEEDS_SECTION "`%s' can only be used within a section.\n"
  
- #ifdef HAVE_LIBRRD
  extern int operating_mode;
- #else
- static int operating_mode = MODE_CLIENT;
- #endif
  
  typedef struct cf_callback
  {
@@@ -67,9 -63,9 +63,9 @@@ typedef struct cf_mode_ite
  static cf_mode_item_t cf_mode_list[] =
  {
        {"TimeToLive",  NULL, MODE_CLIENT                           },
-       {"PIDFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL},
-       {"DataDir",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL},
-       {"LogFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL}
+       {"PIDFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG },
+       {"DataDir",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG },
+       {"LogFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG }
  };
  static int cf_mode_num = 4;
  
@@@ -110,7 -106,7 +106,7 @@@ static int cf_dispatch (char *type, con
  
        if ((cf_cb = cf_search (type)) == NULL)
        {
 -              syslog (LOG_WARNING, "Plugin `%s' did not register a callback.\n", type);
 +              syslog (LOG_WARNING, "Plugin `%s' did not register a callback.", type);
                return (-1);
        }
  
        }
  
        if (i >= cf_cb->keys_num)
 -              syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.\n", type, key);
 +              syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.", type, key);
  
        free (key);
        free (value);
  
 +      DBG ("return (%i)", ret);
 +
        return (ret);
  }
  
@@@ -246,10 -240,14 +242,14 @@@ static int cf_callback_mode (const cha
  
        if (strcasecmp (value, "Client") == 0)
                operating_mode = MODE_CLIENT;
+ #if HAVE_LIBRRD
        else if (strcasecmp (value, "Server") == 0)
                operating_mode = MODE_SERVER;
        else if (strcasecmp (value, "Local") == 0)
                operating_mode = MODE_LOCAL;
+ #endif
+       else if (strcasecmp (value, "Log") == 0)
+               operating_mode = MODE_LOG;
        else
        {
                syslog (LOG_ERR, "Invalid value for config option `Mode': `%s'", value);
diff --combined src/network.c
  
  #define BUFF_SIZE 4096
  
- #ifdef HAVE_LIBRRD
  extern int operating_mode;
- #else
- static int operating_mode = MODE_CLIENT;
- #endif
  
  typedef struct sockent
  {
@@@ -218,7 -214,7 +214,7 @@@ int network_create_socket (const char *
  
        DBG ("node = %s, service = %s", node, service);
  
-       if (operating_mode == MODE_LOCAL)
+       if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
                return (-1);
  
        socklist_tail = socklist_head;
@@@ -516,6 -512,8 +512,6 @@@ int network_send (char *type, char *ins
                if (se->mode != operating_mode)
                        continue;
  
 -              DBG ("fd = %i", se->fd);
 -
                while (1)
                {
                        status = sendto (se->fd, buf, buflen, 0,
diff --combined src/plugin.c
@@@ -26,7 -26,6 +26,7 @@@
  
  #include "plugin.h"
  #include "network.h"
 +#include "utils_debug.h"
  
  typedef struct plugin
  {
@@@ -39,9 -38,7 +39,7 @@@
  
  static plugin_t *first_plugin = NULL;
  
- #ifdef HAVE_LIBRRD
  extern int operating_mode;
- #endif
  
  static char *plugindir = NULL;
  
@@@ -117,19 -114,11 +115,19 @@@ int plugin_load_file (char *file
        lt_dlhandle dlh;
        void (*reg_handle) (void);
  
 +      DBG ("file = %s", file);
 +
        lt_dlinit ();
        lt_dlerror (); /* clear errors */
  
        if ((dlh = lt_dlopen (file)) == NULL)
 +      {
 +              const char *error = lt_dlerror ();
 +
 +              syslog (LOG_ERR, "lt_dlopen failed: %s", error);
 +              DBG ("lt_dlopen failed: %s", error);
                return (1);
 +      }
  
        if ((reg_handle = lt_dlsym (dlh, "module_register")) == NULL)
        {
@@@ -156,8 -145,6 +154,8 @@@ int plugin_load (const char *type
        struct stat    statbuf;
        struct dirent *de;
  
 +      DBG ("type = %s", type);
 +
        dir = plugin_get_dir ();
        ret = 1;
  
@@@ -301,7 -288,7 +299,7 @@@ void plugin_register (char *type
                return;
  
  #ifdef HAVE_LIBRRD
-       if ((operating_mode == MODE_LOCAL) || (operating_mode == MODE_CLIENT))
+       if (operating_mode != MODE_SERVER)
  #endif
                if ((init != NULL) && (read == NULL))
                        syslog (LOG_NOTICE, "Plugin `%s' doesn't provide a read function.", type);
   * Send received data back to the plugin/module which will append DS
   * definitions and pass it on to ``rrd_update_file''.
   */
- #ifdef HAVE_LIBRRD
  void plugin_write (char *host, char *type, char *inst, char *val)
  {
        plugin_t *p;
  
        (*p->write) (host, inst, val);
  }
- #endif /* HAVE_LIBRRD */
  
  /*
   * Receive data from the plugin/module and get it somehow to ``plugin_write'':
   */
  void plugin_submit (char *type, char *inst, char *val)
  {
- #ifdef HAVE_LIBRRD
-       if (operating_mode == MODE_LOCAL)
-               plugin_write (NULL, type, inst, val);
-       else if (operating_mode == MODE_CLIENT)
+         if (operating_mode == MODE_CLIENT)
                network_send (type, inst, val);
-       else /* operating_mode == MODE_SERVER */
-               syslog (LOG_ERR, "WTF is the server doing in ``plugin_submit''?!?\n");
- #else
-       network_send (type, inst, val);
- #endif
+       else
+               plugin_write (NULL, type, inst, val);
  }