Merge branch 'collectd-4.9'
authorFlorian Forster <octo@huhu.verplant.org>
Wed, 7 Apr 2010 09:55:24 +0000 (11:55 +0200)
committerFlorian Forster <octo@huhu.verplant.org>
Wed, 7 Apr 2010 09:55:24 +0000 (11:55 +0200)
1  2 
README
configure.in
src/Makefile.am
src/collectd.conf.in
src/network.c
src/snmp.c

diff --combined README
--- 1/README
--- 2/README
+++ b/README
@@@ -57,10 -57,6 +57,10 @@@ Feature
        Retrieves JSON data via cURL and parses it according to user
        configuration.
  
 +    - curl_xml
 +      Retrieves XML data via cURL and parses it according to user
 +      configuration.
 +
      - dbi
        Executes SQL statements on various databases and interprets the returned
        data.
        Memory utilization: Memory occupied by running processes, page cache,
        buffer cache and free.
  
 +    - modbus
 +      Reads values from Modbus/TCP enabled devices. Supports reading values
 +      from multiple "slaves" so gateway devices can be used.
 +
      - multimeter
        Information provided by serial multimeters, such as the `Metex
        M-4650CR'.
      - zfs_arc
        Statistics for ZFS' “Adaptive Replacement Cache” (ARC).
  
-   * Output can be written or send to various destinations by the following
+   * Output can be written or sent to various destinations by the following
      plugins:
  
      - csv
      network plugins, makes sure your resources are used efficiently. Also,
      since collectd is programmed multithreaded it benefits from hyperthreading
      and multicore processors and makes sure that the daemon isn't idle if only
-     one plugins waits for an IO-operation to complete.
+     one plugin waits for an IO-operation to complete.
  
    * Once set up, hardly any maintenance is necessary. Setup is kept as easy
      as possible and the default values should be okay for most users.
@@@ -545,15 -537,11 +545,15 @@@ Prerequisite
      Used by the `memcachec' plugin to connect to a memcache daemon.
      <http://tangent.org/552/libmemcached.html>
  
 +  * libmodbus (optional)
 +    Used by the `modbus' plugin to communicate with Modbus/TCP devices.
 +    <https://launchpad.net/libmodbus>
 +
    * libmysqlclient (optional)
      Unsurprisingly used by the `mysql' plugin.
      <http://dev.mysql.com/>
  
-   * libnatapp (optional)
+   * libnetapp (optional)
      Required for the “netapp” plugin.
      This library is part of the “Manage ONTAP SDK” published by NetApp.
  
diff --combined configure.in
@@@ -477,7 -477,7 +477,7 @@@ AC_HEADER_TIM
  # Checks for library functions.
  #
  AC_PROG_GCC_TRADITIONAL
 -AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf)
 +AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf if_indextoname)
  
  AC_FUNC_STRERROR_R
  
@@@ -550,8 -550,16 +550,16 @@@ AC_CHECK_FUNCS(socket, [], AC_CHECK_LIB
  AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes")
  
  nanosleep_needs_rt="no"
- AC_CHECK_FUNCS(nanosleep, [], AC_CHECK_LIB(rt, nanosleep, [nanosleep_needs_rt="yes"], AC_MSG_ERROR(cannot find nanosleep)))
+ nanosleep_needs_posix4="no"
+ AC_CHECK_FUNCS(nanosleep,
+     [],
+     AC_CHECK_LIB(rt, nanosleep,
+         [nanosleep_needs_rt="yes"],
+         AC_CHECK_LIB(posix4, nanosleep,
+             [nanosleep_needs_posix4="yes"],
+             AC_MSG_ERROR(cannot find nanosleep))))
  AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
+ AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$nanosleep_needs_posix4" = "xyes")
  
  AC_CHECK_FUNCS(sysctl, [have_sysctl="yes"], [have_sysctl="no"])
  AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
@@@ -971,13 -979,6 +979,13 @@@ AC_CHECK_MEMBERS([struct net_device_sta
        #include <linux/netdevice.h>
        ])
  
 +AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [],
 +      [],
 +      [
 +      #include <netinet/in.h>
 +      #include <net/if.h>
 +      ])
 +
  AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct kinfo_proc.ki_rusage],
        [
                AC_DEFINE(HAVE_STRUCT_KINFO_PROC_FREEBSD, 1,
  AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes")
  # }}}
  
 +# --with-libmodbus {{{
 +with_libmodbus_config=""
 +with_libmodbus_cflags=""
 +with_libmodbus_libs=""
 +AC_ARG_WITH(libmodbus, [AS_HELP_STRING([--with-libmodbus@<:@=PREFIX@:>@], [Path to the modbus library.])],
 +[
 +      if test "x$withval" = "xno"
 +      then
 +              with_libmodbus="no"
 +      else if test "x$withval" = "xyes"
 +      then
 +              with_libmodbus="use_pkgconfig"
 +      else if test -d "$with_libmodbus/lib"
 +      then
 +              AC_MSG_NOTICE([Not checking for libmodbus: Manually configured])
 +              with_libmodbus_cflags="-I$withval/include"
 +              with_libmodbus_libs="-L$withval/lib -lmodbus"
 +              with_libmodbus="yes"
 +      fi; fi; fi
 +],
 +[with_libmodbus="use_pkgconfig"])
 +
 +# configure using pkg-config
 +if test "x$with_libmodbus" = "xuse_pkgconfig"
 +then
 +      if test "x$PKG_CONFIG" = "x"
 +      then
 +              with_libmodbus="no (Don't have pkg-config)"
 +      fi
 +fi
 +if test "x$with_libmodbus" = "xuse_pkgconfig"
 +then
 +      AC_MSG_NOTICE([Checking for modbus using $PKG_CONFIG])
 +      $PKG_CONFIG --exists 'modbus' 2>/dev/null
 +      if test $? -ne 0
 +      then
 +              with_libmodbus="no (pkg-config doesn't know library)"
 +      fi
 +fi
 +if test "x$with_libmodbus" = "xuse_pkgconfig"
 +then
 +      with_libmodbus_cflags="`$PKG_CONFIG --cflags 'modbus'`"
 +      if test $? -ne 0
 +      then
 +              with_libmodbus="no ($PKG_CONFIG failed)"
 +      fi
 +      with_libmodbus_libs="`$PKG_CONFIG --libs 'modbus'`"
 +      if test $? -ne 0
 +      then
 +              with_libmodbus="no ($PKG_CONFIG failed)"
 +      fi
 +fi
 +if test "x$with_libmodbus" = "xuse_pkgconfig"
 +then
 +      with_libmodbus="yes"
 +fi
 +
 +# with_libmodbus_cflags and with_libmodbus_libs are set up now, let's do
 +# the actual checks.
 +if test "x$with_libmodbus" = "xyes"
 +then
 +      SAVE_CPPFLAGS="$CPPFLAGS"
 +      CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
 +
 +      AC_CHECK_HEADERS(modbus/modbus.h, [], [with_libmodbus="no (modbus/modbus.h not found)"])
 +
 +      CPPFLAGS="$SAVE_CPPFLAGS"
 +fi
 +if test "x$with_libmodbus" = "xyes"
 +then
 +      SAVE_CPPFLAGS="$CPPFLAGS"
 +      SAVE_LDFLAGS="$LDFLAGS"
 +
 +      CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
 +      LDFLAGS="$LDFLAGS $with_libmodbus_libs"
 +
 +      AC_CHECK_LIB(modbus, modbus_init_tcp,
 +                   [with_libmodbus="yes"],
 +                   [with_libmodbus="no (symbol modbus_init_tcp not found)"])
 +
 +      CPPFLAGS="$SAVE_CPPFLAGS"
 +      LDFLAGS="$SAVE_LDFLAGS"
 +fi
 +if test "x$with_libmodbus" = "xyes"
 +then
 +      BUILD_WITH_LIBMODBUS_CFLAGS="$with_libmodbus_cflags"
 +      BUILD_WITH_LIBMODBUS_LIBS="$with_libmodbus_libs"
 +      AC_SUBST(BUILD_WITH_LIBMODBUS_CFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBMODBUS_LIBS)
 +fi
 +# }}}
 +
  # --with-libmysql {{{
  with_mysql_config="mysql_config"
  with_mysql_cflags=""
  if test "x$with_python" = "xyes"
  then
        AC_MSG_CHECKING([for Python CPPFLAGS])
 -      python_include_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_python_inc()" | "$with_python_prog" 2>&1`
 +      python_include_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_python_inc())" | "$with_python_prog" 2>&1`
        python_config_status=$?
  
        if test "$python_config_status" -ne 0 || test "x$python_include_path" = "x"
  if test "x$with_python" = "xyes"
  then
        AC_MSG_CHECKING([for Python LDFLAGS])
 -      python_library_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0)" | "$with_python_prog" 2>&1`
 +      python_library_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0))" | "$with_python_prog" 2>&1`
        python_config_status=$?
  
        if test "$python_config_status" -ne 0 || test "x$python_library_path" = "x"
  if test "x$with_python" = "xyes"
  then
        AC_MSG_CHECKING([for Python LIBS])
 -      python_library_flags=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0)" | "$with_python_prog" 2>&1`
 +      python_library_flags=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0))" | "$with_python_prog" 2>&1`
        python_config_status=$?
  
        if test "$python_config_status" -ne 0 || test "x$python_library_flags" = "x"
@@@ -3888,7 -3797,7 +3896,7 @@@ AC_DEFUN
             enable_plugin="yes"
             force="yes"
       else
 -           enable_plugin="no"
 +           enable_plugin="no (disabled on command line)"
       fi; fi
      ],
      [
@@@ -3941,7 -3850,6 +3949,7 @@@ plugin_contextswitch="no
  plugin_cpu="no"
  plugin_cpufreq="no"
  plugin_curl_json="no"
 +plugin_curl_xml="no"
  plugin_df="no"
  plugin_disk="no"
  plugin_entropy="no"
        plugin_curl_json="yes"
  fi
  
 +if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
 +then
 +      plugin_curl_xml="yes"
 +fi
 +
  if test "x$have_processor_info" = "xyes"
  then
        plugin_cpu="yes"
@@@ -4239,7 -4142,6 +4247,7 @@@ AC_PLUGIN([cpu],         [$plugin_cpu]
  AC_PLUGIN([csv],         [yes],                [CSV output plugin])
  AC_PLUGIN([curl],        [$with_libcurl],      [CURL generic web statistics])
  AC_PLUGIN([curl_json],   [$plugin_curl_json],    [CouchDB statistics])
 +AC_PLUGIN([curl_xml],   [$plugin_curl_xml],    [CURL generic xml statistics])
  AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
  AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
  AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
@@@ -4270,7 -4172,6 +4278,7 @@@ AC_PLUGIN([mbmon],       [yes]
  AC_PLUGIN([memcachec],   [$with_libmemcached], [memcachec statistics])
  AC_PLUGIN([memcached],   [yes],                [memcached statistics])
  AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
 +AC_PLUGIN([modbus],      [$with_libmodbus],    [Modbus plugin])
  AC_PLUGIN([multimeter],  [$plugin_multimeter], [Read multimeter values])
  AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
  AC_PLUGIN([netapp],      [$with_libnetapp],    [NetApp plugin])
@@@ -4504,7 -4405,6 +4512,7 @@@ Configuration
      libkstat  . . . . . . $with_kstat
      libkvm  . . . . . . . $with_libkvm
      libmemcached  . . . . $with_libmemcached
 +    libmodbus . . . . . . $with_libmodbus
      libmysql  . . . . . . $with_libmysql
      libnetapp . . . . . . $with_libnetapp
      libnetlink  . . . . . $with_libnetlink
      csv . . . . . . . . . $enable_csv
      curl  . . . . . . . . $enable_curl
      curl_json . . . . . . $enable_curl_json
 +    curl_xml  . . . . . . $enable_curl_xml
      dbi . . . . . . . . . $enable_dbi
      df  . . . . . . . . . $enable_df
      disk  . . . . . . . . $enable_disk
      memcachec . . . . . . $enable_memcachec
      memcached . . . . . . $enable_memcached
      memory  . . . . . . . $enable_memory
 +    modbus  . . . . . . . $enable_modbus
      multimeter  . . . . . $enable_multimeter
      mysql . . . . . . . . $enable_mysql
      netapp  . . . . . . . $enable_netapp
diff --combined src/Makefile.am
@@@ -53,6 -53,9 +53,9 @@@ collectd_DEPENDENCIES 
  if BUILD_WITH_LIBRT
  collectd_LDADD += -lrt
  endif
+ if BUILD_WITH_LIBPOSIX4
+ collectd_LDADD += -lposix4
+ endif
  if BUILD_WITH_LIBSOCKET
  collectd_LDADD += -lsocket
  endif
@@@ -257,17 -260,6 +260,17 @@@ collectd_LDADD += "-dlopen" curl_json.l
  collectd_DEPENDENCIES += curl_json.la
  endif
  
 +if BUILD_PLUGIN_CURL_XML
 +pkglib_LTLIBRARIES += curl_xml.la
 +curl_xml_la_SOURCES = curl_xml.c
 +curl_xml_la_LDFLAGS = -module -avoid-version
 +curl_xml_la_CFLAGS = $(AM_CFLAGS) \
 +              $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 +curl_xml_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
 +collectd_LDADD += "-dlopen" curl_xml.la
 +collectd_DEPENDENCIES += curl_xml.la
 +endif
 +
  if BUILD_PLUGIN_DBI
  pkglib_LTLIBRARIES += dbi.la
  dbi_la_SOURCES = dbi.c \
@@@ -598,16 -590,6 +601,16 @@@ memory_la_LIBADD += -lperfsta
  endif
  endif
  
 +if BUILD_PLUGIN_MODBUS
 +pkglib_LTLIBRARIES += modbus.la
 +modbus_la_SOURCES = modbus.c
 +modbus_la_LDFLAGS = -module -avoid-version
 +modbus_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMODBUS_CFLAGS)
 +modbus_la_LIBADD = $(BUILD_WITH_LIBMODBUS_LIBS)
 +collectd_LDADD += "-dlopen" modbus.la
 +collectd_DEPENDENCIES += modbus.la
 +endif
 +
  if BUILD_PLUGIN_MULTIMETER
  pkglib_LTLIBRARIES += multimeter.la
  multimeter_la_SOURCES = multimeter.c
diff --combined src/collectd.conf.in
@@@ -63,7 -63,6 +63,7 @@@ FQDNLookup   tru
  @LOAD_PLUGIN_CSV@LoadPlugin csv
  #@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl
  #@BUILD_PLUGIN_CURL_JSON_TRUE@LoadPlugin curl_json
 +#@BUILD_PLUGIN_CURL_XML_TRUE@LoadPlugin curl_xml
  #@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi
  #@BUILD_PLUGIN_DF_TRUE@LoadPlugin df
  #@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
  #  </URL>
  #</Plugin>
  
 +#<Plugin "curl_xml">
 +#  <URL "http://localhost/stats.xml">
 +#    Host "my_host"
 +#    Instance "some_instance"
 +#    User "collectd"
 +#    Password "thaiNg0I"
 +#    VerifyPeer true
 +#    VerifyHost true
 +#    CACert "/path/to/ca.crt"
 +#
 +#    <XPath "table[@id=\"magic_level\"]/tr">
 +#      Type "magic_level"
 +#      #InstancePrefix "prefix-"
 +#      InstanceFrom "td[1]"
 +#      ValuesFrom "td[2]/span[@class=\"level\"]"
 +#    </XPath>
 +#  </URL>
 +#</Plugin>
 +
  #<Plugin dbi>
  #     <Query "num_of_customers">
  #             Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
  
  #<Plugin perl>
  #     IncludeDir "/my/include/path"
- #     BaseName "Collectd::Plugin"
+ #     BaseName "Collectd::Plugins"
  #     EnableDebugger ""
  #     LoadPlugin Monitorus
  #     LoadPlugin OpenVZ
  #             Password "dozaiTh4"
  #             CollectInterface true
  #             CollectRegistrationTable true
 +#             CollectCPULoad true
 +#             CollectMemory true
 +#             CollectDF true
 +#             CollectDisk true
  #     </Router>
  #</Plugin>
  
  #    </Match>
  #    <Match>
  #      Regex "\\<R=local_user\\>"
 +#      ExcludeRegex "\\<R=local_user\\>.*mail_spool defer"
  #      DSType "CounterInc"
  #      Type "counter"
  #      Instance "local_user"
diff --combined src/network.c
@@@ -52,9 -52,6 +52,9 @@@
  #if HAVE_POLL_H
  # include <poll.h>
  #endif
 +#if HAVE_NET_IF_H
 +# include <net/if.h>
 +#endif
  
  #if HAVE_LIBGCRYPT
  # include <gcrypt.h>
@@@ -121,7 -118,6 +121,7 @@@ typedef struct socken
  
        char *node;
        char *service;
 +      int interface;
  
        union
        {
@@@ -1144,7 -1140,10 +1144,10 @@@ static int parse_part_encr_aes256 (sock
    cypher = network_get_aes256_cypher (se, pea.iv, sizeof (pea.iv),
        pea.username);
    if (cypher == NULL)
+   {
+     sfree (pea.username);
      return (-1);
+   }
  
    payload_len = part_size - (PART_ENCRYPTION_AES256_SIZE + username_len);
    assert (payload_len > 0);
        /* in = */ NULL, /* in len = */ 0);
    if (err != 0)
    {
+     sfree (pea.username);
      ERROR ("network plugin: gcry_cipher_decrypt returned: %s",
          gcry_strerror (err));
      return (-1);
        buffer + buffer_offset, payload_len);
    if (memcmp (hash, pea.hash, sizeof (hash)) != 0)
    {
+     sfree (pea.username);
      ERROR ("network plugin: Decryption failed: Checksum mismatch.");
      return (-1);
    }
    *ret_buffer =     buffer     + part_size;
    *ret_buffer_len = buffer_len - part_size;
  
+   sfree (pea.username);
    return (0);
  } /* }}} int parse_part_encr_aes256 */
  /* #endif HAVE_LIBGCRYPT */
@@@ -1553,7 -1556,7 +1560,7 @@@ static int network_set_ttl (const socke
  
                if (setsockopt (se->data.client.fd, IPPROTO_IP, optname,
                                        &network_config_ttl,
-                                       sizeof (network_config_ttl)) == -1)
+                                       sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
                        ERROR ("setsockopt: %s",
  
                if (setsockopt (se->data.client.fd, IPPROTO_IPV6, optname,
                                        &network_config_ttl,
-                                       sizeof (network_config_ttl)) == -1)
+                                       sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
                        ERROR ("setsockopt: %s",
        return (0);
  } /* int network_set_ttl */
  
 -static int network_bind_socket (int fd, const struct addrinfo *ai)
 +static int network_set_interface (const sockent_t *se, const struct addrinfo *ai) /* {{{ */
 +{
 +      DEBUG ("network plugin: network_set_interface: interface index = %i;",
 +                      se->interface);
 +
 +        assert (se->type == SOCKENT_TYPE_CLIENT);
 +
 +      if (ai->ai_family == AF_INET)
 +      {
 +              struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
 +
 +              if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
 +              {
 +#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 +                      /* If possible, use the "ip_mreqn" structure which has
 +                       * an "interface index" member. Using the interface
 +                       * index is preferred here, because of its similarity
 +                       * to the way IPv6 handles this. Unfortunately, it
 +                       * appears not to be portable. */
 +                      struct ip_mreqn mreq;
 +
 +                      memset (&mreq, 0, sizeof (mreq));
 +                      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
 +                      mreq.imr_address.s_addr = ntohl (INADDR_ANY);
 +                      mreq.imr_ifindex = se->interface;
 +#else
 +                      struct ip_mreq mreq;
 +
 +                      memset (&mreq, 0, sizeof (mreq));
 +                      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
 +                      mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
 +#endif
 +
 +                      if (setsockopt (se->data.client.fd, IPPROTO_IP, IP_MULTICAST_IF,
 +                                              &mreq, sizeof (mreq)) != 0)
 +                      {
 +                              char errbuf[1024];
 +                              ERROR ("setsockopt: %s",
 +                                              sstrerror (errno, errbuf, sizeof (errbuf)));
 +                              return (-1);
 +                      }
 +
 +                      return (0);
 +              }
 +      }
 +      else if (ai->ai_family == AF_INET6)
 +      {
 +              struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
 +
 +              if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
 +              {
 +                      if (setsockopt (se->data.client.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
 +                                              &se->interface,
 +                                              sizeof (se->interface)) != 0)
 +                      {
 +                              char errbuf[1024];
 +                              ERROR ("setsockopt: %s",
 +                                              sstrerror (errno, errbuf,
 +                                                      sizeof (errbuf)));
 +                              return (-1);
 +                      }
 +
 +                      return (0);
 +              }
 +      }
 +
 +      /* else: Not a multicast interface. */
 +#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
 +      if (se->interface != 0)
 +      {
 +              char interface_name[IFNAMSIZ];
 +
 +              if (if_indextoname (se->interface, interface_name) == NULL)
 +                      return (-1);
 +
 +              DEBUG ("network plugin: Binding socket to interface %s", interface_name);
 +
 +              if (setsockopt (se->data.client.fd, SOL_SOCKET, SO_BINDTODEVICE,
 +                                      interface_name,
 +                                      sizeof(interface_name)) == -1 )
 +              {
 +                      char errbuf[1024];
 +                      ERROR ("setsockopt: %s",
 +                                      sstrerror (errno, errbuf, sizeof (errbuf)));
 +                      return (-1);
 +              }
 +      }
 +/* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
 +
 +#else
 +      WARNING ("network plugin: Cannot set the interface on a unicast "
 +                      "socket because "
 +# if !defined(SO_BINDTODEVICE)
 +                      "the the \"SO_BINDTODEVICE\" socket option "
 +# else
 +                      "the \"if_indextoname\" function "
 +# endif
 +                      "is not available on your system.");
 +#endif
 +
 +      return (0);
 +} /* }}} network_set_interface */
 +
 +static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx)
  {
        int loop = 0;
        int yes  = 1;
                struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
                if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
                {
 +#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 +                      struct ip_mreqn mreq;
 +#else
                        struct ip_mreq mreq;
 +#endif
  
                        DEBUG ("fd = %i; IPv4 multicast address found", fd);
  
                        mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
 -                      mreq.imr_interface.s_addr = htonl (INADDR_ANY);
 +#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 +                      /* Set the interface using the interface index if
 +                       * possible (available). Unfortunately, the struct
 +                       * ip_mreqn is not portable. */
 +                      mreq.imr_address.s_addr = ntohl (INADDR_ANY);
 +                      mreq.imr_ifindex = interface_idx;
 +#else
 +                      mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
 +#endif
  
                        if (setsockopt (fd, IPPROTO_IP, IP_MULTICAST_LOOP,
                                                &loop, sizeof (loop)) == -1)
                                                        sizeof (errbuf)));
                                return (-1);
                        }
 +
 +                      return (0);
                }
        }
        else if (ai->ai_family == AF_INET6)
                         * single interface; programs running on
                         * multihomed hosts may need to join the same
                         * group on more than one interface.*/
 -                      mreq.ipv6mr_interface = 0;
 +                      mreq.ipv6mr_interface = interface_idx;
  
                        if (setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
                                                &loop, sizeof (loop)) == -1)
                                                        sizeof (errbuf)));
                                return (-1);
                        }
 +
 +                      return (0);
 +              }
 +      }
 +
 +#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
 +      /* if a specific interface was set, bind the socket to it. But to avoid
 +       * possible problems with multicast routing, only do that for non-multicast
 +       * addresses */
 +      if (interface_idx != 0)
 +      {
 +              char interface_name[IFNAMSIZ];
 +
 +              if (if_indextoname (interface_idx, interface_name) == NULL)
 +                      return (-1);
 +
 +              DEBUG ("fd = %i; Binding socket to interface %s", fd, interface_name);
 +
 +              if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE,
 +                                      interface_name,
 +                                      sizeof(interface_name)) == -1 )
 +              {
 +                      char errbuf[1024];
 +                      ERROR ("setsockopt: %s",
 +                                      sstrerror (errno, errbuf, sizeof (errbuf)));
 +                      return (-1);
                }
        }
 +#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
  
        return (0);
  } /* int network_bind_socket */
@@@ -1850,7 -1709,6 +1857,7 @@@ static int sockent_init (sockent_t *se
        se->type = SOCKENT_TYPE_CLIENT;
        se->node = NULL;
        se->service = NULL;
 +      se->interface = 0;
        se->next = NULL;
  
        if (type == SOCKENT_TYPE_SERVER)
@@@ -1999,7 -1857,7 +2006,7 @@@ static int sockent_open (sockent_t *se
                                continue;
                        }
  
 -                      status = network_bind_socket (*tmp, ai_ptr);
 +                      status = network_bind_socket (*tmp, ai_ptr, se->interface);
                        if (status != 0)
                        {
                                close (*tmp);
                        se->data.client.addrlen = ai_ptr->ai_addrlen;
  
                        network_set_ttl (se, ai_ptr);
 +                      network_set_interface (se, ai_ptr);
  
                        /* We don't open more than one write-socket per
                         * node/service pair.. */
@@@ -2751,25 -2608,6 +2758,25 @@@ static int network_config_set_ttl (cons
    return (0);
  } /* }}} int network_config_set_ttl */
  
 +static int network_config_set_interface (const oconfig_item_t *ci, /* {{{ */
 +    int *interface)
 +{
 +  if ((ci->values_num != 1)
 +      || (ci->values[0].type != OCONFIG_TYPE_STRING))
 +  {
 +    WARNING ("network plugin: The `Interface' config option needs exactly "
 +        "one string argument.");
 +    return (-1);
 +  }
 +
 +  if (interface == NULL)
 +    return (-1);
 +
 +  *interface = if_nametoindex (ci->values[0].value.string);
 +
 +  return (0);
 +} /* }}} int network_config_set_interface */
 +
  static int network_config_set_buffer_size (const oconfig_item_t *ci) /* {{{ */
  {
    int tmp;
@@@ -2881,10 -2719,6 +2888,10 @@@ static int network_config_add_listen (c
            &se->data.server.security_level);
      else
  #endif /* HAVE_LIBGCRYPT */
 +    if (strcasecmp ("Interface", child->key) == 0)
 +      network_config_set_interface (child,
 +          &se->interface);
 +    else
      {
        WARNING ("network plugin: Option `%s' is not allowed here.",
            child->key);
@@@ -2963,10 -2797,6 +2970,10 @@@ static int network_config_add_server (c
            &se->data.client.security_level);
      else
  #endif /* HAVE_LIBGCRYPT */
 +    if (strcasecmp ("Interface", child->key) == 0)
 +      network_config_set_interface (child,
 +          &se->interface);
 +    else
      {
        WARNING ("network plugin: Option `%s' is not allowed here.",
            child->key);
diff --combined src/snmp.c
@@@ -655,9 -655,8 +655,9 @@@ static int csnmp_config_add_host (oconf
    if (hd->interval != 0)
      cb_interval.tv_sec = (time_t) hd->interval;
  
 -  status = plugin_register_complex_read (cb_name, csnmp_read_host,
 -      /* interval = */ &cb_interval, /* user_data = */ &cb_data);
 +  status = plugin_register_complex_read (/* group = */ NULL, cb_name,
 +      csnmp_read_host, /* interval = */ &cb_interval,
 +      /* user_data = */ &cb_data);
    if (status != 0)
    {
      ERROR ("snmp plugin: Registering complex read function failed.");
@@@ -1128,7 -1127,7 +1128,7 @@@ static int csnmp_dispatch_table (host_d
        char temp[DATA_MAX_NAME_LEN];
  
        if (instance_list_ptr == NULL)
-       ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid);
+       ssnprintf (temp, sizeof (temp), "%"PRIu32, (uint32_t) subid);
        else
        sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));