Merge branch 'master' into ff/plugins-ng
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 13 Jan 2007 15:47:15 +0000 (16:47 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 13 Jan 2007 15:47:15 +0000 (16:47 +0100)
Conflicts:

src/collectd.c
src/network.c

1  2 
configure.in
src/Makefile.am
src/collectd.c
src/collectd.h
src/network.c

diff --combined configure.in
@@@ -1,5 -1,5 +1,5 @@@
  dnl Process this file with autoconf to produce a configure script.
- AC_INIT(collectd, 3.10.3)
+ AC_INIT(collectd, 3.11.0)
  AC_CONFIG_SRCDIR(src/collectd.c)
  AC_CONFIG_HEADERS(src/config.h)
  AM_INIT_AUTOMAKE(dist-bzip2)
@@@ -44,7 -44,7 +44,7 @@@ AC_CHECK_HEADERS(assert.h
  AC_CHECK_HEADERS(sys/types.h)
  AC_CHECK_HEADERS(sys/socket.h)
  AC_CHECK_HEADERS(sys/select.h)
 -AC_CHECK_HEADERS(sys/poll.h)
 +AC_CHECK_HEADERS(poll.h)
  AC_CHECK_HEADERS(netdb.h)
  AC_CHECK_HEADERS(arpa/inet.h)
  AC_CHECK_HEADERS(sys/resource.h)
@@@ -990,10 -990,10 +990,11 @@@ AC_COLLECTD([email],     [disable], [mo
  AC_COLLECTD([quota],     [enable],  [module], [quota statistics (experimental)])
  AC_COLLECTD([hddtemp],   [disable], [module], [hdd temperature statistics])
  AC_COLLECTD([load],      [disable], [module], [system load statistics])
+ AC_COLLECTD([mbmon],     [disable], [module], [motherboard monitor statistics])
  AC_COLLECTD([memory],    [disable], [module], [memory statistics])
  AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics])
  AC_COLLECTD([mysql],     [disable], [module], [mysql statistics])
 +AC_COLLECTD([network],   [disable], [module], [network functionality])
  AC_COLLECTD([nfs],       [disable], [module], [nfs statistics])
  AC_COLLECTD([ntpd],      [disable], [module], [nfs statistics])
  AC_COLLECTD([ping],      [disable], [module], [ping statistics])
@@@ -1043,10 -1043,10 +1044,11 @@@ Configuration
      email . . . . . . . $enable_email
      hddtemp . . . . . . $enable_hddtemp
      load  . . . . . . . $enable_load
+     mbmon . . . . . . . $enable_mbmon
      memory  . . . . . . $enable_memory
      multimeter  . . . . $enable_multimeter
      mysql . . . . . . . $enable_mysql
 +    network . . . . . . $enable_network
      nfs . . . . . . . . $enable_nfs
      ntpd  . . . . . . . $enable_ntpd
      ping  . . . . . . . $enable_ping
diff --combined src/Makefile.am
@@@ -12,9 -12,9 +12,9 @@@ sbin_PROGRAMS = collect
  collectd_SOURCES = collectd.c collectd.h \
                   utils_debug.c utils_debug.h \
                   utils_mount.c utils_mount.h \
 +                 utils_llist.c utils_llist.h \
                   utils_ignorelist.c utils_ignorelist.h \
                   common.c common.h \
 -                 network.c network.h \
                   plugin.c plugin.h \
                   configfile.c configfile.h
  collectd_CPPFLAGS = $(LTDLINCL)
@@@ -30,6 -30,9 +30,6 @@@ endi
  
  # Link to these libraries..
  collectd_LDFLAGS = -export-dynamic
 -if BUILD_WITH_RRDTOOL
 -collectd_LDFLAGS += -lm -lrrd
 -endif
  if BUILD_WITH_LIBRT
  collectd_LDFLAGS += -lrt
  endif
@@@ -155,7 -158,7 +155,7 @@@ endi
  
  if BUILD_MODULE_DNS
  pkglib_LTLIBRARIES += dns.la
- dns_la_SOURCES = dns.c utils_dns.c
+ dns_la_SOURCES = dns.c utils_dns.c utils_dns.h
  dns_la_LDFLAGS = -module -avoid-version
  if BUILD_WITH_LIBPCAP
  dns_la_LDFLAGS += -lpcap
@@@ -217,6 -220,17 +217,17 @@@ load_la_LDFLAGS += -lstatgra
  endif
  endif
  
+ if BUILD_MODULE_MBMON
+ pkglib_LTLIBRARIES += mbmon.la
+ mbmon_la_SOURCES = mbmon.c
+ mbmon_la_LDFLAGS = -module -avoid-version
+ if BUILD_WITH_LIBSOCKET
+ mbmon_la_LDFLAGS += -lsocket
+ endif
+ collectd_LDADD += "-dlopen" mbmon.la
+ collectd_DEPENDENCIES += mbmon.la
+ endif
  if BUILD_MODULE_MEMORY
  pkglib_LTLIBRARIES += memory.la
  memory_la_SOURCES = memory.c
@@@ -259,17 -273,6 +270,17 @@@ collectd_LDADD += "-dlopen" mysql.l
  collectd_DEPENDENCIES += mysql.la
  endif
  
 +if BUILD_MODULE_NETWORK
 +pkglib_LTLIBRARIES += network.la
 +network_la_SOURCES = network.c
 +network_la_LDFLAGS = -module -avoid-version
 +if BUILD_WITH_LIBSOCKET
 +network_la_LDFLAGS += -lsocket
 +endif
 +collectd_LDADD += "-dlopen" network.la
 +collectd_DEPENDENCIES += network.la
 +endif
 +
  if BUILD_MODULE_NFS
  pkglib_LTLIBRARIES += nfs.la
  nfs_la_SOURCES = nfs.c
@@@ -313,14 -316,6 +324,14 @@@ collectd_LDADD += "-dlopen" processes.l
  collectd_DEPENDENCIES += processes.la
  endif
  
 +if BUILD_WITH_RRDTOOL
 +pkglib_LTLIBRARIES += rrdtool.la
 +rrdtool_la_SOURCES = rrdtool.c
 +rrdtool_la_LDFLAGS = -module -avoid-version -lrrd
 +collectd_LDADD += "-dlopen" rrdtool.la
 +collectd_DEPENDENCIES += rrdtool.la
 +endif
 +
  if BUILD_MODULE_SENSORS
  pkglib_LTLIBRARIES += sensors.la
  sensors_la_SOURCES = sensors.c
diff --combined src/collectd.c
@@@ -4,7 -4,8 +4,7 @@@
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; either version 2 of the License, or (at your
 - * option) any later version.
 + * Free Software Foundation; only version 2 of the License is applicable.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
@@@ -222,18 -223,27 +222,29 @@@ static int start_client (void
  #if HAVE_LIBRRD
  static int start_server (void)
  {
-       /* FIXME use stack here! */
 +      /* FIXME */
 +#if 0
        char *host;
        char *type;
        char *instance;
        char *values;
  
-       while (loop == 0)
+       int  error_counter = 0;
+       int  status;
+       while ((loop == 0) && (error_counter < 3))
        {
-               if (network_receive (&host, &type, &instance, &values) == 0)
-                       plugin_write (host, type, instance, values);
+               status = network_receive (&host, &type, &instance, &values);
+               if (status != 0)
+               {
+                       if (status < 0)
+                               error_counter++;
+                       continue;
+               }
+               error_counter = 0;
+               plugin_write (host, type, instance, values);
  
                if (host     != NULL) free (host);     host     = NULL;
                if (type     != NULL) free (type);     type     = NULL;
                if (values   != NULL) free (values);   values   = NULL;
        }
        
 +#endif
        return (0);
  } /* static int start_server (void) */
  #endif /* HAVE_LIBRRD */
diff --combined src/collectd.h
@@@ -4,7 -4,8 +4,7 @@@
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; either version 2 of the License, or (at your
 - * option) any later version.
 + * Free Software Foundation; only version 2 of the License is applicable.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
  # include <sys/param.h>
  #endif
  
- #if HAVE_SYSLOG
- # define syslog(...) syslog(__VA_ARGS__)
- # if HAVE_OPENLOG
- #  define openlog(...) openlog(__VA_ARGS__)
- # else
- #  define openlog(...) /**/
- # endif
- # if HAVE_CLOSELOG
- #  define closelog(...) closelog(__VA_ARGS__)
- # else
- #  define closelog(...) /**/
- # endif
- #else
+ #if !HAVE_SYSLOG
  # define syslog(...) /**/
  # define openlog(...) /**/
  # define closelog(...) /**/
  #define MODE_LOG    0x08
  
  #ifndef COLLECTD_GRP_NAME
- #     define COLLECTD_GRP_NAME "collectd"
+ # define COLLECTD_GRP_NAME "collectd"
  #endif
  
  #ifndef COLLECTD_STEP
diff --combined src/network.c
@@@ -4,7 -4,8 +4,7 @@@
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; either version 2 of the License, or (at your
 - * option) any later version.
 + * Free Software Foundation; only version 2 of the License is applicable.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   *   Florian octo Forster <octo at verplant.org>
   **/
  
 -#include <stdlib.h>
 -#include <stdio.h>
 -#include <string.h>
 -#include <sys/types.h>
 -#include <sys/socket.h>
 -#include <netdb.h>
 -#include <netinet/in.h>
 -#include <arpa/inet.h>
 -#include <syslog.h>
 -#include <errno.h>
 -
 -#include "network.h"
 +#include "collectd.h"
 +#include "plugin.h"
  #include "common.h"
  #include "configfile.h"
  #include "utils_debug.h"
  
 +#include "network.h"
 +
 +#if HAVE_PTHREAD_H
 +# include <pthread.h>
 +#endif
 +#if HAVE_SYS_SOCKET_H
 +# include <sys/socket.h>
 +#endif
 +#if HAVE_NETDB_H
 +# include <netdb.h>
 +#endif
 +#if HAVE_NETINET_IN_H
 +# include <netinet/in.h>
 +#endif
 +#if HAVE_ARPA_INET_H
 +# include <arpa/inet.h>
 +#endif
 +#if HAVE_POLL_H
 +# include <poll.h>
 +#endif
 +
  /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
  /* #define BUFF_SIZE 1452 */
  
  
  #define BUFF_SIZE 4096
  
 -extern int operating_mode;
 -
 +/*
 + * Private data types
 + */
  typedef struct sockent
  {
        int                      fd;
 -      int                      mode;
        struct sockaddr_storage *addr;
        socklen_t                addrlen;
        struct sockent          *next;
  } sockent_t;
  
 -static sockent_t *socklist_head = NULL;
 +/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 + *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 + * +-------+-----------------------+-------------------------------+
 + * ! Ver.  !                       ! Length                        !
 + * +-------+-----------------------+-------------------------------+
 + */
 +struct part_header_s
 +{
 +      uint16_t type;
 +      uint16_t length;
 +};
 +typedef struct part_header_s part_header_t;
 +
 +/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 + *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 + * +-------------------------------+-------------------------------+
 + * ! Type                          ! Length                        !
 + * +-------------------------------+-------------------------------+
 + * : (Length - 4) Bytes                                            :
 + * +---------------------------------------------------------------+
 + */
 +struct part_string_s
 +{
 +      part_header_t *head;
 +      char *value;
 +};
 +typedef struct part_string_s part_string_t;
 +
 +/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
 + *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 + * +-------------------------------+-------------------------------+
 + * ! Type                          ! Length                        !
 + * +-------------------------------+---------------+---------------+
 + * ! Num of values                 ! Type0         ! Type1         !
 + * +-------------------------------+---------------+---------------+
 + * ! Value0                                                        !
 + * !                                                               !
 + * +---------------------------------------------------------------+
 + * ! Value1                                                        !
 + * !                                                               !
 + * +---------------------------------------------------------------+
 + */
 +struct part_values_s
 +{
 +      part_header_t *head;
 +      uint16_t *num_values;
 +      uint8_t  *values_types;
 +      value_t  *values;
 +};
 +typedef struct part_values_s part_values_t;
 +
 +/*
 + * Private variables
 + */
 +static const char *config_keys[] =
 +{
 +      "Listen",
 +      "Server",
 +      "TimeToLive",
 +      NULL
 +};
 +static int config_keys_num = 3;
 +
 +static int network_config_ttl = 0;
 +
 +static sockent_t *sending_sockets = NULL;
 +
 +static struct pollfd *listen_sockets = NULL;
 +static int listen_sockets_num = 0;
 +static pthread_t listen_thread = 0;
 +static int listen_loop = 0;
 +
 +/*
 + * Private functions
 + */
 +static int write_part_values (char **ret_buffer, int *ret_buffer_len,
 +              const data_set_t *ds, const value_list_t *vl)
 +{
 +      part_values_t pv;
 +      int i;
  
 -static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
 +      i = 6 + (9 * vl->values_len);
 +      if (*ret_buffer_len < i)
 +              return (-1);
 +      *ret_buffer_len -= i;
 +
 +      pv.head = (part_header_t *) *ret_buffer;
 +      pv.num_values = (uint16_t *) (pv.head + 1);
 +      pv.values_types = (uint8_t *) (pv.num_values + 1);
 +      pv.values = (value_t *) (pv.values_types + vl->values_len);
 +      *ret_buffer = (void *) (pv.values + vl->values_len);
 +
 +      pv.head->type = htons (TYPE_VALUES);
 +      pv.head->length = htons (6 + (9 * vl->values_len));
 +      *pv.num_values = htons ((uint16_t) vl->values_len);
 +      
 +      for (i = 0; i < vl->values_len; i++)
 +      {
 +              if (ds->ds[i].type == DS_TYPE_COUNTER)
 +              {
 +                      pv.values_types[i] = DS_TYPE_COUNTER;
 +                      pv.values[i].counter = htonll (vl->values[i].counter);
 +              }
 +              else
 +              {
 +                      pv.values_types[i] = DS_TYPE_GAUGE;
 +                      pv.values[i].gauge = vl->values[i].gauge;
 +              }
 +      } /* for (values) */
 +
 +      return (0);
 +} /* int write_part_values */
 +
 +static int write_part_string (char **ret_buffer, int *ret_buffer_len,
 +              int type, const char *str, int str_len)
 +{
 +      part_string_t ps;
 +      int len;
 +
 +      if (str_len < 1)
 +              return (-1);
 +
 +      len = 4 + str_len + 1;
 +      if (*ret_buffer_len < len)
 +              return (-1);
 +      *ret_buffer_len -= len;
 +
 +      ps.head = (part_header_t *) *ret_buffer;
 +      ps.value = (char *) (ps.head + 1);
 +
 +      ps.head->type = htons ((uint16_t) type);
 +      ps.head->length = htons ((uint16_t) str_len + 4);
 +      memcpy (ps.value, str, str_len);
 +      ps.value[str_len] = '\0';
 +      *ret_buffer = (void *) (ps.value + str_len);
 +
 +      return (0);
 +} /* int write_part_string */
 +
 +static int parse_part_values (void **ret_buffer, int *ret_buffer_len,
 +              value_t **ret_values, int *ret_num_values)
  {
 -      char *ttl_str;
 -      int   ttl_int;
 +      char *buffer = *ret_buffer;
 +      int   buffer_len = *ret_buffer_len;
 +      part_values_t *pvalues;
 +      int   i;
 +
 +      if (buffer_len < (15))
 +      {
 +              DBG ("packet is too short");
 +              return (-1);
 +      }
 +
 +      pvalues = (part_values_t *) malloc (sizeof (part_values_t));
 +      if (pvalues == NULL)
 +              return (-1);
 +
 +      pvalues->head = (part_header_t *) buffer;
 +      assert (pvalues->head->type == htons (TYPE_VALUES));
  
 -      ttl_str = cf_get_option ("TimeToLive", NULL);
 -      if (ttl_str == NULL)
 +      pvalues->num_values = (uint16_t *) (buffer + 4);
 +      if (ntohs (*pvalues->num_values)
 +                      != ((ntohs (pvalues->head->length) - 6) / 9))
 +      {
 +              DBG ("`length' and `num of values' don't match");
 +              free (pvalues);
                return (-1);
 +      }
 +
 +      pvalues->values_types = (uint8_t *) (buffer + 6);
 +      pvalues->values = (value_t *) (buffer + 6 + *pvalues->num_values);
 +
 +      for (i = 0; i < *pvalues->num_values; i++)
 +              if (pvalues->values_types[i] == DS_TYPE_COUNTER)
 +                      pvalues->values[i].counter = ntohll (pvalues->values[i].counter);
 +
 +      *ret_buffer     = (void *) buffer;
 +      *ret_buffer_len = buffer_len - pvalues->head->length;
 +      *ret_num_values = *pvalues->num_values;
 +      *ret_values     = pvalues->values;
 +
 +      free (pvalues);
 +
 +      return (0);
 +} /* int parse_part_values */
 +
 +static int parse_part_string (void **ret_buffer, int *ret_buffer_len,
 +              char *output, int output_len)
 +{
 +      char *buffer = *ret_buffer;
 +      int   buffer_len = *ret_buffer_len;
 +      part_string_t part_string;
 +
 +      part_string.head = (part_header_t *) buffer;
 +      if (buffer_len < part_string.head->length)
 +      {
 +              DBG ("packet is too short");
 +              return (-1);
 +      }
 +      assert ((part_string.head->type == htons (TYPE_HOST))
 +                      || (part_string.head->type == htons (TYPE_PLUGIN))
 +                      || (part_string.head->type == htons (TYPE_PLUGIN_INSTANCE))
 +                      || (part_string.head->type == htons (TYPE_TYPE))
 +                      || (part_string.head->type == htons (TYPE_TYPE_INSTANCE)));
 +
 +      part_string.value = buffer + 4;
 +      if (part_string.value[part_string.head->length - 5] != '\0')
 +      {
 +              DBG ("String does not end with a nullbyte");
 +              return (-1);
 +      }
  
 -      ttl_int = atoi (ttl_str);
 -      if ((ttl_int < 1) || (ttl_int > 255))
 +      if (output_len < (part_string.head->length - 4))
        {
 -              syslog (LOG_WARNING, "A TTL value of %i is invalid.", ttl_int);
 +              DBG ("output buffer is too small");
                return (-1);
        }
 +      strcpy (output, part_string.value);
  
 -      DBG ("ttl = %i", ttl_int);
 +      *ret_buffer = (void *) (buffer + part_string.head->length);
 +      *ret_buffer_len = buffer_len - part_string.head->length;
 +
 +      return (0);
 +} /* int parse_part_string */
 +
 +static int parse_packet (void *buffer, int buffer_len)
 +{
 +      part_header_t *header;
 +      int status;
 +
 +      value_list_t vl;
 +      char type[DATA_MAX_NAME_LEN];
 +
 +      memset (&vl, '\0', sizeof (vl));
 +      memset (&type, '\0', sizeof (type));
 +
 +      while (buffer_len > sizeof (part_header_t))
 +      {
 +              header = (part_header_t *) buffer;
 +
 +              if (header->length > buffer_len)
 +                      break;
 +
 +              if (header->type == TYPE_VALUES)
 +              {
 +                      status = parse_part_values (&buffer, &buffer_len,
 +                                      &vl.values, &vl.values_len);
 +
 +                      if ((status == 0)
 +                                      && (strlen (vl.host) > 0)
 +                                      && (strlen (vl.plugin) > 0)
 +                                      && (strlen (type) > 0))
 +                              plugin_dispatch_values (type, &vl);
 +              }
 +              else if (header->type == TYPE_HOST)
 +              {
 +                      status = parse_part_string (&buffer, &buffer_len,
 +                                      vl.host, sizeof (vl.host));
 +              }
 +              else if (header->type == TYPE_PLUGIN)
 +              {
 +                      status = parse_part_string (&buffer, &buffer_len,
 +                                      vl.plugin, sizeof (vl.plugin));
 +              }
 +              else if (header->type == TYPE_PLUGIN_INSTANCE)
 +              {
 +                      status = parse_part_string (&buffer, &buffer_len,
 +                                      vl.plugin_instance, sizeof (vl.plugin_instance));
 +              }
 +              else if (header->type == TYPE_TYPE)
 +              {
 +                      status = parse_part_string (&buffer, &buffer_len,
 +                                      type, sizeof (type));
 +              }
 +              else if (header->type == TYPE_TYPE_INSTANCE)
 +              {
 +                      status = parse_part_string (&buffer, &buffer_len,
 +                                      vl.type_instance, sizeof (vl.type_instance));
 +              }
 +              else
 +              {
 +                      DBG ("Unknown part type: 0x%0hx", header->type);
 +                      buffer = ((char *) buffer) + header->length;
 +              }
 +      } /* while (buffer_len > sizeof (part_header_t)) */
 +
 +      return (0);
 +} /* int parse_packet */
 +
 +static void free_sockent (sockent_t *se)
 +{
 +      sockent_t *next;
 +      while (se != NULL)
 +      {
 +              next = se->next;
 +              free (se->addr);
 +              free (se);
 +              se = next;
 +      }
 +} /* void free_sockent */
 +
 +/*
 + * int network_set_ttl
 + *
 + * Set the `IP_MULTICAST_TTL', `IP_TTL', `IPV6_MULTICAST_HOPS' or
 + * `IPV6_UNICAST_HOPS', depending on which option is applicable.
 + *
 + * The `struct addrinfo' is used to destinguish between unicast and multicast
 + * sockets.
 + */
 +static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
 +{
 +      if ((network_config_ttl < 1) || (network_config_ttl > 255))
 +              return (-1);
 +
 +      DBG ("ttl = %i", network_config_ttl);
  
        if (ai->ai_family == AF_INET)
        {
                        optname = IP_TTL;
  
                if (setsockopt (se->fd, IPPROTO_IP, optname,
 -                                      &ttl_int, sizeof (ttl_int)) == -1)
 +                                      &network_config_ttl,
 +                                      sizeof (network_config_ttl)) == -1)
                {
                        syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
                        return (-1);
                        optname = IPV6_UNICAST_HOPS;
  
                if (setsockopt (se->fd, IPPROTO_IPV6, optname,
 -                                      &ttl_int, sizeof (ttl_int)) == -1)
 +                                      &network_config_ttl,
 +                                      sizeof (network_config_ttl)) == -1)
                {
                        syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
                        return (-1);
        }
  
        return (0);
 -}
 +} /* int network_set_ttl */
  
  static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
  {
        }
  
        return (0);
 -}
 +} /* int network_bind_socket */
  
 -int network_create_socket (const char *node, const char *service)
 +static sockent_t *network_create_socket (const char *node,
 +              const char *service,
 +              int listen)
  {
 -      sockent_t *socklist_tail;
 -
        struct addrinfo  ai_hints;
        struct addrinfo *ai_list, *ai_ptr;
        int              ai_return;
  
 -      int num_added = 0;
 +      sockent_t *se_head = NULL;
 +      sockent_t *se_tail = NULL;
  
        DBG ("node = %s, service = %s", node, service);
  
 -      if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
 -      {
 -              syslog (LOG_WARNING, "network_create_socket: There is no point opening a socket when you are in mode `%s'.",
 -                              operating_mode == MODE_LOCAL ? "Local" : "Log");
 -              return (-1);
 -      }
 -
 -      socklist_tail = socklist_head;
 -      while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
 -              socklist_tail = socklist_tail->next;
 -
        memset (&ai_hints, '\0', sizeof (ai_hints));
        ai_hints.ai_flags    = 0;
  #ifdef AI_PASSIVE
  #ifdef AI_ADDRCONFIG
        ai_hints.ai_flags |= AI_ADDRCONFIG;
  #endif
 -      ai_hints.ai_family   = PF_UNSPEC;
 +      ai_hints.ai_family   = AF_UNSPEC;
        ai_hints.ai_socktype = SOCK_DGRAM;
        ai_hints.ai_protocol = IPPROTO_UDP;
  
 -      if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
 +      ai_return = getaddrinfo (node, service, &ai_hints, &ai_list);
 +      if (ai_return != 0)
        {
                syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
 -                              node == NULL ? "(null)" : node,
 -                              service == NULL ? "(null)" : service,
 -                              ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
 -              return (-1);
 +                              (node == NULL) ? "(null)" : node,
 +                              (service == NULL) ? "(null)" : service,
 +                              (ai_return == EAI_SYSTEM)
 +                              ? strerror (errno)
 +                              : gai_strerror (ai_return));
 +              return (NULL);
        }
  
        for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
                memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
                se->addrlen = ai_ptr->ai_addrlen;
  
 -              se->mode = operating_mode;
 -              se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
 +              se->fd   = socket (ai_ptr->ai_family,
 +                              ai_ptr->ai_socktype,
 +                              ai_ptr->ai_protocol);
                se->next = NULL;
  
                if (se->fd == -1)
                        continue;
                }
  
 -              if (operating_mode == MODE_SERVER)
 +              if (listen != 0)
                {
                        if (network_bind_socket (se, ai_ptr) != 0)
                        {
+                               close (se->fd);
                                free (se->addr);
                                free (se);
                                continue;
                        }
                }
 -              else if (operating_mode == MODE_CLIENT)
 +              else /* listen == 0 */
                {
                        network_set_ttl (se, ai_ptr);
                }
  
 -              if (socklist_tail == NULL)
 +              if (se_tail == NULL)
                {
 -                      socklist_head = se;
 -                      socklist_tail = se;
 +                      se_head = se;
 +                      se_tail = se;
                }
                else
                {
 -                      socklist_tail->next = se;
 -                      socklist_tail = se;
 +                      se_tail->next = se;
 +                      se_tail = se;
                }
  
 -              num_added++;
 -
                /* We don't open more than one write-socket per node/service pair.. */
 -              if (operating_mode == MODE_CLIENT)
 +              if (listen == 0)
                        break;
        }
  
        freeaddrinfo (ai_list);
  
 -      return (num_added);
 -}
 +      return (se_head);
 +} /* sockent_t *network_create_socket */
  
 -static int network_connect_default (void)
 +static sockent_t *network_create_default_socket (int listen)
  {
 -      int ret;
 +      sockent_t *se_ptr  = NULL;
 +      sockent_t *se_head = NULL;
 +      sockent_t *se_tail = NULL;
  
 -      if (socklist_head != NULL)
 -              return (0);
 +      se_ptr = network_create_socket (NET_DEFAULT_V6_ADDR,
 +                      NET_DEFAULT_PORT, listen);
  
 -      DBG ("socklist_head is NULL");
 +      /* Don't send to the same machine in IPv6 and IPv4 if both are available. */
 +      if ((listen == 0) && (se_ptr != NULL))
 +              return (se_ptr);
  
 -      ret = 0;
 +      if (se_ptr != NULL)
 +      {
 +              se_head = se_ptr;
 +              se_tail = se_ptr;
 +              while (se_tail->next != NULL)
 +                      se_tail = se_tail->next;
 +      }
  
 -      if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
 -              ret++;
 +      se_ptr = network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT, listen);
  
 -      /* Don't use IPv4 and IPv6 in parallel by default.. */
 -      if ((operating_mode == MODE_CLIENT) && (ret != 0))
 -              return (ret);
 +      if (se_tail == NULL)
 +              return (se_ptr);
  
 -      if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
 -              ret++;
 +      se_tail->next = se_ptr;
 +      return (se_head);
 +} /* sockent_t *network_create_default_socket */
  
 -      if (ret == 0)
 -              ret = -1;
 +static int network_add_listen_socket (const char *node, const char *service)
 +{
 +      sockent_t *se;
 +      sockent_t *se_ptr;
 +      int se_num = 0;
  
 -      return (ret);
 -}
 +      if (service == NULL)
 +              service = NET_DEFAULT_PORT;
  
 -static int network_get_listen_socket (void)
 -{
 -      int fd;
 -      int max_fd;
 -      int status;
 +      if (node == NULL)
 +              se = network_create_default_socket (1 /* listen == true */);
 +      else
 +              se = network_create_socket (node, service, 1 /* listen == true */);
  
 -      fd_set readfds;
 -      sockent_t *se;
 +      if (se == NULL)
 +              return (-1);
  
 -      if (socklist_head == NULL)
 -              network_connect_default ();
 +      for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next)
 +              se_num++;
  
 -      FD_ZERO (&readfds);
 -      max_fd = -1;
 -      for (se = socklist_head; se != NULL; se = se->next)
 +      listen_sockets = (struct pollfd *) realloc (listen_sockets,
 +                      (listen_sockets_num + se_num)
 +                      * sizeof (struct pollfd));
 +
 +      for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next)
        {
 -              if (se->mode != operating_mode)
 -                      continue;
 +              listen_sockets[listen_sockets_num].fd = se_ptr->fd;
 +              listen_sockets[listen_sockets_num].events = POLLIN | POLLPRI;
 +              listen_sockets[listen_sockets_num].revents = 0;
 +              listen_sockets_num++;
 +      } /* for (se) */
  
 -              FD_SET (se->fd, &readfds);
 -              if (se->fd >= max_fd)
 -                      max_fd = se->fd + 1;
 -      }
 +      free_sockent (se);
 +      return (0);
 +} /* int network_add_listen_socket */
  
 -      if (max_fd == -1)
 -      {
 -              syslog (LOG_WARNING, "No listen sockets found!");
 -              return (-1);
 -      }
 +static int network_add_sending_socket (const char *node, const char *service)
 +{
 +      sockent_t *se;
 +      sockent_t *se_ptr;
  
 -      status = select (max_fd, &readfds, NULL, NULL, NULL);
 +      if (service == NULL)
 +              service = NET_DEFAULT_PORT;
  
 -      if (status == -1)
 -      {
 -              if (errno != EINTR)
 -                      syslog (LOG_ERR, "select: %s", strerror (errno));
 +      if (node == NULL)
 +              se = network_create_default_socket (0 /* listen == false */);
 +      else
 +              se = network_create_socket (node, service, 0 /* listen == false */);
 +
 +      if (se == NULL)
                return (-1);
 -      }
  
 -      fd = -1;
 -      for (se = socklist_head; se != NULL; se = se->next)
 +      if (sending_sockets == NULL)
        {
 -              if (se->mode != operating_mode)
 -                      continue;
 -
 -              if (FD_ISSET (se->fd, &readfds))
 -              {
 -                      fd = se->fd;
 -                      break;
 -              }
 +              sending_sockets = se;
 +              return (0);
        }
  
 -      if (fd == -1)
 -              syslog (LOG_WARNING, "No socket ready..?");
 +      for (se_ptr = sending_sockets; se_ptr->next != NULL; se_ptr = se_ptr->next)
 +              /* seek end */;
  
 -      DBG ("fd = %i", fd);
 -      return (fd);
 -}
 +      se_ptr->next = se;
 +      return (0);
 +} /* int network_get_listen_socket */
  
 -int network_receive (char **host, char **type, char **inst, char **value)
 +int network_receive (void)
  {
 -      int fd;
        char buffer[BUFF_SIZE];
 +      int  buffer_len;
  
 -      struct sockaddr_storage addr;
 -      socklen_t               addrlen;
 +      int i;
        int status;
  
 -      char *fields[4];
 -
 -      assert (operating_mode == MODE_SERVER);
 -
 -      *host  = NULL;
 -      *type  = NULL;
 -      *inst  = NULL;
 -      *value = NULL;
 -
 -      if ((fd = network_get_listen_socket ()) < 0)
 -              return (-1);
 +      if (listen_sockets_num == 0)
 +              network_add_listen_socket (NULL, NULL);
  
 -      addrlen = sizeof (addr);
 -      if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
 +      if (listen_sockets_num == 0)
        {
 -              syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
 +              syslog (LOG_ERR, "network: Failed to open a listening socket.");
                return (-1);
        }
  
 -      if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
 +      while (listen_loop == 0)
        {
 -              syslog (LOG_EMERG, "malloc: %s", strerror (errno));
 -              return (-1);
 -      }
 -
 -      status = getnameinfo ((struct sockaddr *) &addr, addrlen,
 -                      *host, BUFF_SIZE, NULL, 0, 0);
 -      if (status != 0)
 -      {
 -              free (*host); *host = NULL;
 -              syslog (LOG_ERR, "getnameinfo: %s",
 -                              status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
 -              return (-1);
 -      }
 +              status = poll (listen_sockets, listen_sockets_num, -1);
  
 -      if (strsplit (buffer, fields, 4) != 3)
 -      {
 -              syslog (LOG_WARNING, "Invalid message from `%s'", *host);
 -              free (*host); *host = NULL;
 -              return (1);
 -      }
 -
 -      if ((*type = strdup (fields[0])) == NULL)
 -      {
 -              syslog (LOG_EMERG, "strdup: %s", strerror (errno));
 -              free (*host); *host = NULL;
 -              return (-1);
 -      }
 +              if (status <= 0)
 +              {
 +                      if (errno == EINTR)
 +                              continue;
 +                      syslog (LOG_ERR, "poll failed: %s",
 +                                      strerror (errno));
 +                      return (-1);
 +              }
  
 -      if ((*inst = strdup (fields[1])) == NULL)
 -      {
 -              syslog (LOG_EMERG, "strdup: %s", strerror (errno));
 -              free (*host); *host = NULL;
 -              free (*type); *type = NULL;
 -              return (-1);
 -      }
 +              for (i = 0; (i < listen_sockets_num) && (status > 0); i++)
 +              {
 +                      if ((listen_sockets[i].revents & (POLLIN | POLLPRI)) == 0)
 +                              continue;
 +                      status--;
  
 -      if ((*value = strdup (fields[2])) == NULL)
 -      {
 -              syslog (LOG_EMERG, "strdup: %s", strerror (errno));
 -              free (*host); *host = NULL;
 -              free (*type); *type = NULL;
 -              free (*inst); *inst = NULL;
 -              return (-1);
 -      }
 +                      buffer_len = recv (listen_sockets[i].fd,
 +                                      buffer, sizeof (buffer),
 +                                      0 /* no flags */);
 +                      if (buffer_len < 0)
 +                      {
 +                              syslog (LOG_ERR, "recv failed: %s", strerror (errno));
 +                              return (-1);
 +                      }
  
 -      DBG ("host = %s, type = %s, inst = %s, value = %s",
 -                      *host, *type, *inst, *value);
 +                      parse_packet (buffer, buffer_len);
 +              } /* for (listen_sockets) */
 +      } /* while (listen_loop == 0) */
  
        return (0);
  }
  
 +static void *receive_thread (void *arg)
 +{
 +      return ((void *) network_receive ());
 +} /* void *receive_thread */
 +
 +#if 0
  int network_send (char *type, char *inst, char *value)
  {
        char buf[BUFF_SIZE];
        buflen++;
  
        if (socklist_head == NULL)
 -              network_connect_default ();
 +              network_create_default_socket (0 /* listen == false */);
  
        ret = 0;
        for (se = socklist_head; se != NULL; se = se->next)
        {
 -              if (se->mode != operating_mode)
 -                      continue;
 -
                while (1)
                {
                        status = sendto (se->fd, buf, buflen, 0,
                syslog (LOG_WARNING, "Message wasn't sent to anybody..");
  
        return (ret);
 +} /* int network_send */
 +#endif
 +
 +static int network_write (const data_set_t *ds, const value_list_t *vl)
 +{
 +      char  buf[BUFF_SIZE];
 +      char *buf_ptr;
 +      int   buf_len;
 +
 +      sockent_t *se;
 +
 +      DBG ("host = %s; plugin = %s; plugin_instance = %s; type = %s; type_instance = %s;",
 +                      vl->host, vl->plugin, vl->plugin_instance, ds->type, vl->type_instance);
 +
 +      buf_len = sizeof (buf);
 +      buf_ptr = buf;
 +      if (write_part_string (&buf_ptr, &buf_len, TYPE_HOST,
 +                              vl->host, strlen (vl->host)) != 0)
 +              return (-1);
 +      if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN,
 +                              vl->plugin, strlen (vl->plugin)) != 0)
 +              return (-1);
 +      if (strlen (vl->plugin_instance) > 0)
 +              if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN_INSTANCE,
 +                                      vl->plugin_instance,
 +                                      strlen (vl->plugin_instance)) != 0)
 +                      return (-1);
 +      if (write_part_string (&buf_ptr, &buf_len, TYPE_TYPE,
 +                              ds->type, strlen (ds->type)) != 0)
 +              return (-1);
 +      if (strlen (vl->type_instance) > 0)
 +              if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN_INSTANCE,
 +                                      vl->type_instance,
 +                                      strlen (vl->type_instance)) != 0)
 +                      return (-1);
 +      
 +      write_part_values (&buf_ptr, &buf_len, ds, vl);
 +
 +      buf_len = sizeof (buf) - buf_len;
 +
 +      for (se = sending_sockets; se != NULL; se = se->next)
 +      {
 +              int status;
 +
 +              while (42)
 +              {
 +                      status = sendto (se->fd, buf, buf_len, 0 /* no flags */,
 +                                      (struct sockaddr *) se->addr, se->addrlen);
 +                      if (status < 0)
 +                      {
 +                              if (errno == EINTR)
 +                                      continue;
 +                              syslog (LOG_ERR, "network: sendto failed: %s",
 +                                              strerror (errno));
 +                              break;
 +                      }
 +
 +                      break;
 +              } /* while (42) */
 +      } /* for (sending_sockets) */
 +
 +      return 0;
 +}
 +
 +static int network_config (const char *key, const char *val)
 +{
 +      char *node;
 +      char *service;
 +
 +      char *fields[3];
 +      int   fields_num;
 +
 +      if ((strcasecmp ("Listen", key) == 0)
 +                      || (strcasecmp ("Server", key) == 0))
 +      {
 +              char *val_cpy = strdup (val);
 +              if (val_cpy == NULL)
 +                      return (1);
 +
 +              service = NET_DEFAULT_PORT;
 +              fields_num = strsplit (val_cpy, fields, 3);
 +              if ((fields_num != 1)
 +                              && (fields_num != 2))
 +                      return (1);
 +              else if (fields_num == 2)
 +                      service = fields[1];
 +              node = fields[0];
 +
 +              if (strcasecmp ("Listen", key) == 0)
 +                      network_add_listen_socket (node, service);
 +              else
 +                      network_add_sending_socket (node, service);
 +      }
 +      else if (strcasecmp ("TimeToLive", key) == 0)
 +      {
 +              int tmp = atoi (val);
 +              if ((tmp > 0) && (tmp < 256))
 +                      network_config_ttl = tmp;
 +              else
 +                      return (1);
 +      }
 +      else
 +      {
 +              return (-1);
 +      }
 +      return (0);
 +}
 +
 +static int network_shutdown (void)
 +{
 +      DBG ("Shutting down.");
 +
 +      listen_loop++;
 +
 +      pthread_kill (listen_thread, SIGTERM);
 +      pthread_join (listen_thread, NULL /* no return value */);
 +
 +      listen_thread = 0;
 +
 +      return (0);
 +}
 +
 +static int network_init (void)
 +{
 +      plugin_register_shutdown ("network", network_shutdown);
 +
 +      /* setup socket(s) and so on */
 +      if (sending_sockets != NULL)
 +              plugin_register_write ("network", network_write);
 +
 +      if ((listen_sockets_num != 0) && (listen_thread == 0))
 +      {
 +              int status;
 +
 +              status = pthread_create (&listen_thread, NULL /* no attributes */,
 +                              receive_thread, NULL /* no argument */);
 +
 +              if (status != 0)
 +                      syslog (LOG_ERR, "network: pthread_create failed: %s",
 +                                      strerror (errno));
 +      }
 +      return (0);
 +} /* int network_init */
 +
 +void module_register (void)
 +{
 +      plugin_register_config ("network", network_config,
 +                      config_keys, config_keys_num);
 +      plugin_register_init   ("network", network_init);
  }