Merge remote-tracking branch 'github/pr/1124'
authorFlorian Forster <octo@collectd.org>
Tue, 14 Jul 2015 06:19:38 +0000 (08:19 +0200)
committerFlorian Forster <octo@collectd.org>
Tue, 14 Jul 2015 06:19:38 +0000 (08:19 +0200)
23 files changed:
.gitignore
contrib/collectd2html.pl
src/Makefile.am
src/bind.c
src/collectd.conf.in
src/collectd.conf.pod
src/daemon/Makefile.am
src/daemon/collectd.c
src/daemon/common.c
src/daemon/common_test.c
src/daemon/meta_data_test.c [new file with mode: 0644]
src/daemon/utils_cache_mock.c
src/daemon/utils_time_mock.c
src/libcollectdclient/client.c
src/libcollectdclient/network.c
src/liboconfig/oconfig.c
src/network.c
src/testing.h
src/utils_latency.c
src/utils_latency_test.c [new file with mode: 0644]
src/utils_vl_lookup_test.c
src/varnish.c
src/write_redis.c

index 5202bc2..54eaa97 100644 (file)
@@ -83,6 +83,7 @@ src/tests/.deps/
 src/tests/mock/.deps/
 src/tests/.dirstamp
 src/tests/mock/.dirstamp
+test*.log
 
 # new daemon repo
 src/daemon/.deps/
index fe4e2bd..511b3f9 100644 (file)
@@ -216,7 +216,7 @@ for (my $i = 0; $i < scalar(@rrds); ++$i) {
 END
 
        # graph various ranges
-       foreach my $span qw(1hour 1day 1week 1month){
+       foreach my $span (qw(1hour 1day 1week 1month)){
                system("mkdir -p $IMG_DIR/" . dirname($bn));
                my $img = "$IMG_DIR/${bn}-$span$IMG_SFX";
 
index 8cad6fe..df60d0d 100644 (file)
@@ -27,6 +27,13 @@ noinst_LTLIBRARIES =
 check_PROGRAMS =
 TESTS =
 
+noinst_LTLIBRARIES += liblatency.la
+liblatency_la_SOURCES = utils_latency.c utils_latency.h
+check_PROGRAMS += test_utils_latency
+TESTS += test_utils_latency
+test_utils_latency_SOURCES = utils_latency_test.c testing.h
+test_utils_latency_LDADD = liblatency.la daemon/libcommon.la daemon/libplugin_mock.la -lm
+
 noinst_LTLIBRARIES += liblookup.la
 liblookup_la_SOURCES = utils_vl_lookup.c utils_vl_lookup.h
 liblookup_la_LIBADD = daemon/libavltree.la
@@ -974,10 +981,9 @@ endif
 
 if BUILD_PLUGIN_STATSD
 pkglib_LTLIBRARIES += statsd.la
-statsd_la_SOURCES = statsd.c \
-                    utils_latency.h utils_latency.c
+statsd_la_SOURCES = statsd.c
 statsd_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-statsd_la_LIBADD = -lpthread -lm
+statsd_la_LIBADD = liblatency.la -lpthread -lm
 endif
 
 if BUILD_PLUGIN_SWAP
index 2ad50f1..32b0f16 100644 (file)
@@ -763,7 +763,7 @@ static int bind_xml_stats_handle_zone (int version, xmlDoc *doc, /* {{{ */
   xmlFree (zone_name);
   zone_name = NULL;
 
-  if (j >= views->zones_num)
+  if (j >= view->zones_num)
     return (0);
 
   zone_name = view->zones[j];
index 7877228..1f4ccf8 100644 (file)
@@ -99,9 +99,9 @@
 #@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CEPH_TRUE@LoadPlugin ceph
+#@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
-#@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @LOAD_PLUGIN_CSV@LoadPlugin csv
 #@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
 @BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
 #@BUILD_PLUGIN_IPC_TRUE@@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc
-#@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
 #@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi
+#@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
 #@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs
 #@BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq
 #@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java
 #@BUILD_PLUGIN_MEMCACHEC_TRUE@LoadPlugin memcachec
 #@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached
 @BUILD_PLUGIN_MEMORY_TRUE@@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory
+#@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic
 #@BUILD_PLUGIN_MODBUS_TRUE@LoadPlugin modbus
 #@BUILD_PLUGIN_MQTT_TRUE@LoadPlugin mqtt
 #@BUILD_PLUGIN_MULTIMETER_TRUE@LoadPlugin multimeter
 #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
 #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
 #@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish
-#@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic
 #@BUILD_PLUGIN_VIRT_TRUE@LoadPlugin virt
 #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
index 36634d4..978c1c5 100644 (file)
@@ -7605,6 +7605,7 @@ Synopsis:
         Port "6379"
         Timeout 1000
         Prefix "collectd/"
+        Database 1
     </Node>
   </Plugin>
 
@@ -7613,7 +7614,8 @@ the timestamp as the score. Retrieving a date range can then be done using the
 C<ZRANGEBYSCORE> I<Redis> command. Additionnally, all the identifiers of these
 I<Sorted Sets> are kept in a I<Set> called C<collectd/values> (or
 C<${prefix}/values> if the B<Prefix> option was specified) and can be retrieved
-using the C<SMEMBERS> I<Redis> command. See
+using the C<SMEMBERS> I<Redis> command. You can specify the database to use 
+with the B<Database> parameter (default is C<0>). See
 L<http://redis.io/commands#sorted_set> and L<http://redis.io/commands#set> for
 details.
 
@@ -7655,6 +7657,10 @@ containing all metrics. Defaults to C<collectd/>, so metrics will have names
 like C<collectd/cpu-0/cpu-user>. When setting this to something different, it
 is recommended but not required to include a trailing slash in I<Prefix>.
 
+=item B<Database> I<Index>
+
+This index selects the redis database to use for writing operations. Defaults to C<0>.
+
 =back
 
 =head2 Plugin C<write_riemann>
index 8275eee..acc0aac 100644 (file)
@@ -41,7 +41,7 @@ AUTOMAKE_OPTIONS = subdir-objects
 
 sbin_PROGRAMS = collectd
 
-noinst_LTLIBRARIES = libavltree.la libcommon.la libheap.la libplugin_mock.la
+noinst_LTLIBRARIES = libavltree.la libcommon.la libheap.la libmetadata.la libplugin_mock.la
 
 libavltree_la_SOURCES = utils_avltree.c utils_avltree.h
 
@@ -49,6 +49,8 @@ libcommon_la_SOURCES = common.c common.h
 
 libheap_la_SOURCES = utils_heap.c utils_heap.h
 
+libmetadata_la_SOURCES = meta_data.c meta_data.h
+
 libplugin_mock_la_SOURCES = plugin_mock.c utils_cache_mock.c utils_time_mock.c
 
 collectd_SOURCES = collectd.c collectd.h \
@@ -73,7 +75,7 @@ collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
 collectd_CFLAGS = $(AM_CFLAGS)
 collectd_LDFLAGS = -export-dynamic
 collectd_LDADD = libavltree.la libcommon.la libheap.la -lm $(COMMON_LIBS)
-collectd_DEPENDENCIES =
+collectd_DEPENDENCIES = libavltree.la libcommon.la libheap.la libmetadata.la
 
 # The daemon needs to call sg_init, so we need to link it against libstatgrab,
 # too. -octo
@@ -89,12 +91,15 @@ else
 collectd_LDADD += -loconfig
 endif
 
-check_PROGRAMS = test_common test_utils_avltree test_utils_heap test_utils_subst
-TESTS = test_common test_utils_avltree test_utils_heap test_utils_subst
+check_PROGRAMS = test_common test_meta_data test_utils_avltree test_utils_heap test_utils_subst
+TESTS          = test_common test_meta_data test_utils_avltree test_utils_heap test_utils_subst
 
 test_common_SOURCES = common_test.c ../testing.h
 test_common_LDADD = libcommon.la libplugin_mock.la $(COMMON_LIBS)
 
+test_meta_data_SOURCES = meta_data_test.c ../testing.h
+test_meta_data_LDADD = libmetadata.la libplugin_mock.la $(COMMON_LIBS)
+
 test_utils_avltree_SOURCES = utils_avltree_test.c ../testing.h
 test_utils_avltree_LDADD = libavltree.la $(COMMON_LIBS)
 
index 46e13b3..cffb9a5 100644 (file)
@@ -418,15 +418,18 @@ static int pidfile_remove (void)
 #ifdef KERNEL_LINUX
 int notify_upstart (void)
 {
-    const char  *upstart_job = getenv("UPSTART_JOB");
+    char const *upstart_job = getenv("UPSTART_JOB");
 
     if (upstart_job == NULL)
         return 0;
 
     if (strcmp(upstart_job, "collectd") != 0)
+    {
+        WARNING ("Environment specifies unexpected UPSTART_JOB=\"%s\", expected \"collectd\". Ignoring the variable.", upstart_job);
         return 0;
+    }
 
-    WARNING ("supervised by upstart, will stop to signal readyness");
+    NOTICE("Upstart detected, stopping now to signal readyness.");
     raise(SIGSTOP);
     unsetenv("UPSTART_JOB");
 
@@ -435,49 +438,70 @@ int notify_upstart (void)
 
 int notify_systemd (void)
 {
-    int                  fd = -1;
-    const char          *notifysocket = getenv("NOTIFY_SOCKET");
+    int                  fd;
+    const char          *notifysocket;
     struct sockaddr_un   su;
-    struct iovec         iov;
-    struct msghdr        hdr;
+    size_t               su_size;
+    char                 buffer[] = "READY=1\n";
 
+    notifysocket = getenv ("NOTIFY_SOCKET");
     if (notifysocket == NULL)
         return 0;
 
-    if ((strchr("@/", notifysocket[0])) == NULL ||
-        strlen(notifysocket) < 2)
+    if ((strlen (notifysocket) < 2)
+        || ((notifysocket[0] != '@') && (notifysocket[0] != '/')))
+    {
+        ERROR ("invalid notification socket NOTIFY_SOCKET=\"%s\": path must be absolute", notifysocket);
         return 0;
+    }
+    NOTICE ("Systemd detected, trying to signal readyness.");
+
+    unsetenv ("NOTIFY_SOCKET");
 
-    WARNING ("supervised by systemd, will signal readyness");
-    if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
-        WARNING ("cannot contact systemd socket %s", notifysocket);
+    fd = socket (AF_UNIX, SOCK_DGRAM, /* protocol = */ 0);
+    if (fd < 0) {
+        char errbuf[1024];
+        ERROR ("creating UNIX socket failed: %s",
+                 sstrerror (errno, errbuf, sizeof (errbuf)));
         return 0;
     }
 
-    bzero(&su, sizeof(su));
+    memset (&su, 0, sizeof (su));
     su.sun_family = AF_UNIX;
-    sstrncpy (su.sun_path, notifysocket, sizeof(su.sun_path));
-
-    if (notifysocket[0] == '@')
+    if (notifysocket[0] != '@')
+    {
+        /* regular UNIX socket */
+        sstrncpy (su.sun_path, notifysocket, sizeof (su.sun_path));
+        su_size = sizeof (su);
+    }
+    else
+    {
+#if KERNEL_LINUX
+        /* Linux abstract namespace socket: specify address as "\0foo", i.e.
+         * start with a null byte. Since null bytes have no special meaning in
+         * that case, we have to set su_size correctly to cover only the bytes
+         * that are part of the address. */
+        sstrncpy (su.sun_path, notifysocket, sizeof (su.sun_path));
         su.sun_path[0] = 0;
+        su_size = sizeof (sa_family_t) + strlen (notifysocket);
+        if (su_size > sizeof (su))
+            su_size = sizeof (su);
+#else
+       ERROR ("Systemd socket uses Linux abstract namespace notation (\"%s\"), "
+                       "but I don't appear to be running on Linux.", notifysocket);
+       return 0;
+#endif
+    }
 
-    bzero(&iov, sizeof(iov));
-    iov.iov_base = "READY=1";
-    iov.iov_len = strlen("READY=1");
-
-    bzero(&hdr, sizeof(hdr));
-    hdr.msg_name = &su;
-    hdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) +
-        strlen(notifysocket);
-    hdr.msg_iov = &iov;
-    hdr.msg_iovlen = 1;
-
-    unsetenv("NOTIFY_SOCKET");
-    if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) {
-        WARNING ("cannot send notification to systemd");
+    if (sendto (fd, buffer, strlen (buffer), MSG_NOSIGNAL, (void *) &su, (socklen_t) su_size) < 0)
+    {
+        char errbuf[1024];
+        ERROR ("sendto(\"%s\") failed: %s", notifysocket,
+                 sstrerror (errno, errbuf, sizeof (errbuf)));
         close(fd);
         return 0;
     }
+
     close(fd);
     return 1;
 }
index 92950de..abe9a6e 100644 (file)
@@ -257,8 +257,8 @@ ssize_t sread (int fd, void *buf, size_t count)
 
                assert ((0 > status) || (nleft >= (size_t)status));
 
-               nleft = nleft - status;
-               ptr   = ptr   + status;
+               nleft = nleft - ((size_t) status);
+               ptr   = ptr   + ((size_t) status);
        }
 
        return (0);
@@ -284,8 +284,8 @@ ssize_t swrite (int fd, const void *buf, size_t count)
                if (status < 0)
                        return (status);
 
-               nleft = nleft - status;
-               ptr   = ptr   + status;
+               nleft = nleft - ((size_t) status);
+               ptr   = ptr   + ((size_t) status);
        }
 
        return (0);
@@ -356,7 +356,7 @@ int strjoin (char *buffer, size_t buffer_size,
        }
 
        assert (buffer[buffer_size - 1] == 0);
-       return (strlen (buffer));
+       return ((int) strlen (buffer));
 }
 
 int strsubstitute (char *str, char c_from, char c_to)
@@ -665,8 +665,8 @@ int check_create_dir (const char *file_orig)
                 * Join the components together again
                 */
                dir[0] = '/';
-               if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
-                                       fields, i + 1, "/") < 0)
+               if (strjoin (dir + path_is_absolute, (size_t) (dir_len - path_is_absolute),
+                                       fields, (size_t) (i + 1), "/") < 0)
                {
                        ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
                        return (-1);
index 21602d5..0ee4e7e 100644 (file)
@@ -328,11 +328,11 @@ DEF_TEST(parse_values)
     };
 
     int status = parse_values (cases[i].buffer, &vl, &ds);
-    OK(status == cases[i].status);
+    EXPECT_INTEQ (cases[i].status, status);
     if (status != 0)
       continue;
 
-    OK(cases[i].value == vl.values[0].gauge);
+    DBLEQ (cases[i].value, vl.values[0].gauge);
   }
 
   return (0);
diff --git a/src/daemon/meta_data_test.c b/src/daemon/meta_data_test.c
new file mode 100644 (file)
index 0000000..6d61107
--- /dev/null
@@ -0,0 +1,119 @@
+/**
+ * collectd - src/daemon/meta_data_test.c
+ * Copyright (C) 2015       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ */
+
+#include "testing.h"
+#include "collectd.h"
+#include "common.h" /* for STATIC_ARRAY_SIZE */
+#include "meta_data.h"
+
+DEF_TEST(base)
+{
+  meta_data_t *m;
+
+  char *s;
+  int64_t si;
+  uint64_t ui;
+  double d;
+  _Bool b;
+
+  CHECK_NOT_NULL (m = meta_data_create ());
+
+  /* all of these are absent */
+  OK(meta_data_get_string (m, "string", &s) != 0);
+  OK(meta_data_get_signed_int (m, "signed_int", &si) != 0);
+  OK(meta_data_get_unsigned_int (m, "unsigned_int", &ui) != 0);
+  OK(meta_data_get_double (m, "double", &d) != 0);
+  OK(meta_data_get_boolean (m, "boolean", &b) != 0);
+
+  /* populate structure */
+  CHECK_ZERO (meta_data_add_string (m, "string", "foobar"));
+  OK(meta_data_exists (m, "string"));
+  OK(meta_data_type (m, "string") == MD_TYPE_STRING);
+
+  CHECK_ZERO (meta_data_add_signed_int (m, "signed_int", -1));
+  OK(meta_data_exists (m, "signed_int"));
+  OK(meta_data_type (m, "signed_int") == MD_TYPE_SIGNED_INT);
+
+  CHECK_ZERO (meta_data_add_unsigned_int (m, "unsigned_int", 1));
+  OK(meta_data_exists (m, "unsigned_int"));
+  OK(meta_data_type (m, "unsigned_int") == MD_TYPE_UNSIGNED_INT);
+
+  CHECK_ZERO (meta_data_add_double (m, "double", 47.11));
+  OK(meta_data_exists (m, "double"));
+  OK(meta_data_type (m, "double") == MD_TYPE_DOUBLE);
+
+  CHECK_ZERO (meta_data_add_boolean (m, "boolean", 1));
+  OK(meta_data_exists (m, "boolean"));
+  OK(meta_data_type (m, "boolean") == MD_TYPE_BOOLEAN);
+
+  /* retrieve and check all values */
+  CHECK_ZERO (meta_data_get_string (m, "string", &s));
+  STREQ ("foobar", s);
+  sfree (s);
+
+  CHECK_ZERO (meta_data_get_signed_int (m, "signed_int", &si));
+  EXPECT_INTEQ (-1, (int) si);
+
+  CHECK_ZERO (meta_data_get_unsigned_int (m, "unsigned_int", &ui));
+  EXPECT_INTEQ (1, (int) ui);
+
+  CHECK_ZERO (meta_data_get_double (m, "double", &d));
+  DBLEQ (47.11, d);
+
+  CHECK_ZERO (meta_data_get_boolean (m, "boolean", &b));
+  OK1 (b, "b evaluates to true");
+
+  /* retrieving the wrong type always fails */
+  EXPECT_INTEQ (-2, meta_data_get_boolean (m, "string", &b));
+  EXPECT_INTEQ (-2, meta_data_get_string (m, "signed_int", &s));
+  EXPECT_INTEQ (-2, meta_data_get_string (m, "unsigned_int", &s));
+  EXPECT_INTEQ (-2, meta_data_get_string (m, "double", &s));
+  EXPECT_INTEQ (-2, meta_data_get_string (m, "boolean", &s));
+
+  /* replace existing keys */
+  CHECK_ZERO (meta_data_add_signed_int (m, "string", 666));
+  OK(meta_data_type (m, "string") == MD_TYPE_SIGNED_INT);
+
+  CHECK_ZERO (meta_data_add_signed_int (m, "signed_int", 666));
+  CHECK_ZERO (meta_data_get_signed_int (m, "signed_int", &si));
+  EXPECT_INTEQ (666, (int) si);
+
+  /* deleting keys */
+  CHECK_ZERO (meta_data_delete (m, "signed_int"));
+  EXPECT_INTEQ (-2, meta_data_delete (m, "doesnt exist"));
+
+  meta_data_destroy (m);
+  return 0;
+}
+
+int main (void)
+{
+  RUN_TEST(base);
+
+  END_TEST;
+}
+
+/* vim: set sw=2 sts=2 et : */
index 6c78d64..37f21ed 100644 (file)
@@ -26,7 +26,8 @@
 
 #include "utils_cache.h"
 
-gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
+gauge_t *uc_get_rate (__attribute((unused)) data_set_t const *ds,
+                      __attribute((unused)) value_list_t const *vl)
 {
   return (NULL);
 }
index 5edfe6f..2bde4d3 100644 (file)
@@ -28,6 +28,6 @@
 
 cdtime_t cdtime (void)
 {
-  return (0);
+  return (1542455354518929408);
 }
 
index a97dc50..eaee9c0 100644 (file)
@@ -287,7 +287,7 @@ static int lcc_receive (lcc_connection_t *c, /* {{{ */
    * beginning of the message. */
   ptr = NULL;
   errno = 0;
-  res.status = strtol (buffer, &ptr, 0);
+  res.status = (int) strtol (buffer, &ptr, 0);
   if ((errno != 0) || (ptr == &buffer[0]))
   {
     lcc_set_errno (c, errno);
index c390a1c..0539cd0 100644 (file)
@@ -393,7 +393,7 @@ int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
 
 int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ */
 {
-  int if_index;
+  unsigned int if_index;
   int status;
 
   if ((srv == NULL) || (interface == NULL))
@@ -421,7 +421,7 @@ int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ *
       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 = if_index;
+      mreq.imr_ifindex = (int) if_index;
 #else
       struct ip_mreq mreq;
 
@@ -457,8 +457,8 @@ int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ *
 
   /* else: Not a multicast interface. */
 #if defined(SO_BINDTODEVICE)
-  status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
-      interface, strlen (interface) + 1);
+  status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE, interface,
+      (socklen_t) (strlen (interface) + 1));
   if (status != 0)
     return (-1);
 #endif
index f422f5a..d01c79d 100644 (file)
@@ -53,11 +53,9 @@ oconfig_item_t *oconfig_parse_fh (FILE *fh)
   yyset_in (fh);
 
   if (NULL == c_file) {
-    int status;
-
     status = snprintf (file, sizeof (file), "<fd#%d>", fileno (fh));
 
-    if ((status < 0) || (status >= sizeof (file))) {
+    if ((status < 0) || (((size_t) status) >= sizeof (file))) {
       c_file = "<unknown>";
     }
     else {
@@ -131,8 +129,8 @@ oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig)
   {
     int i;
 
-    ci_copy->values = (oconfig_value_t *) calloc (ci_orig->values_num,
-       sizeof (*ci_copy->values));
+    ci_copy->values = (oconfig_value_t *) calloc ((size_t) ci_orig->values_num,
+        sizeof (*ci_copy->values));
     if (ci_copy->values == NULL)
     {
       fprintf (stderr, "calloc failed.\n");
@@ -147,18 +145,17 @@ oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig)
        ci_copy->values[i].type = ci_orig->values[i].type;
        if (ci_copy->values[i].type == OCONFIG_TYPE_STRING)
        {
-        ci_copy->values[i].value.string
-          = strdup (ci_orig->values[i].value.string);
-        if (ci_copy->values[i].value.string == NULL)
-        {
-          fprintf (stderr, "strdup failed.\n");
-          oconfig_free (ci_copy);
-          return (NULL);
-        }
+         ci_copy->values[i].value.string = strdup (ci_orig->values[i].value.string);
+         if (ci_copy->values[i].value.string == NULL)
+         {
+           fprintf (stderr, "strdup failed.\n");
+           oconfig_free (ci_copy);
+           return (NULL);
+         }
        }
        else /* ci_copy->values[i].type != OCONFIG_TYPE_STRING) */
        {
-        ci_copy->values[i].value = ci_orig->values[i].value;
+         ci_copy->values[i].value = ci_orig->values[i].value;
        }
     }
   } /* }}} if (ci_orig->values_num > 0) */
@@ -167,8 +164,8 @@ oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig)
   {
     int i;
 
-    ci_copy->children = (oconfig_item_t *) calloc (ci_orig->children_num,
-       sizeof (*ci_copy->children));
+    ci_copy->children = (oconfig_item_t *) calloc ((size_t) ci_orig->children_num,
+        sizeof (*ci_copy->children));
     if (ci_copy->children == NULL)
     {
       fprintf (stderr, "calloc failed.\n");
index f14a0ad..0c74e22 100644 (file)
@@ -3105,7 +3105,7 @@ static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */
   status = sockent_server_listen (se);
   if (status != 0)
   {
-    ERROR ("network plugin: network_config_add_server: sockent_server_listen failed.");
+    ERROR ("network plugin: network_config_add_listen: sockent_server_listen failed.");
     sockent_destroy (se);
     return (-1);
   }
index 4056311..84a1242 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/tests/macros.h
- * Copyright (C) 2013       Florian octo Forster
+ * Copyright (C) 2013-2015  Florian octo Forster
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
 static int fail_count__ = 0;
 static int check_count__ = 0;
 
+#ifndef DBL_PRECISION
+# define DBL_PRECISION 1e-12
+#endif
+
 #define DEF_TEST(func) static int test_##func ()
 
 #define RUN_TEST(func) do { \
@@ -42,6 +46,7 @@ static int check_count__ = 0;
 #define OK1(cond, text) do { \
   _Bool result = (cond); \
   printf ("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text); \
+  if (!result) { return -1; } \
 } while (0)
 #define OK(cond) OK1(cond, #cond)
 
@@ -54,13 +59,27 @@ static int check_count__ = 0;
   printf ("ok %i - %s evaluates to \"%s\"\n", ++check_count__, #actual, expect); \
 } while (0)
 
+#define EXPECT_INTEQ(expect, actual) do { \
+  if ((expect) != (actual)) {\
+    printf ("not ok %i - %s incorrect: expected %d, got %d\n", \
+        ++check_count__, #actual, expect, actual); \
+    return (-1); \
+  } \
+  printf ("ok %i - %s evaluates to %d\n", ++check_count__, #actual, expect); \
+} while (0)
+
 #define DBLEQ(expect, actual) do { \
-  if ((isnan (expect) && !isnan (actual)) || ((expect) != (actual))) {\
+  double e = (expect); double a = (actual); \
+  if (isnan (e) && !isnan (a)) { \
     printf ("not ok %i - %s incorrect: expected %.15g, got %.15g\n", \
-        ++check_count__, #actual, expect, actual); \
+        ++check_count__, #actual, e, a); \
+    return (-1); \
+  } else if (!isnan (e) && (((e-a) < -DBL_PRECISION) || ((e-a) > DBL_PRECISION))) { \
+    printf ("not ok %i - %s incorrect: expected %.15g, got %.15g\n", \
+        ++check_count__, #actual, e, a); \
     return (-1); \
   } \
-  printf ("ok %i - %s evaluates to %.15g\n", ++check_count__, #actual, expect); \
+  printf ("ok %i - %s evaluates to %.15g\n", ++check_count__, #actual, e); \
 } while (0)
 
 #define CHECK_NOT_NULL(expr) do { \
index c6a1ff6..d570ab6 100644 (file)
 #include "common.h"
 
 #include <math.h>
+#include <limits.h>
 
 #ifndef HISTOGRAM_NUM_BINS
 # define HISTOGRAM_NUM_BINS 1000
 #endif
 
-static const int HISTOGRAM_DEFAULT_BIN_WIDTH = 1;
+#ifndef HISTOGRAM_DEFAULT_BIN_WIDTH
+/* 1048576 = 2^20 ^= 1/1024 s */
+# define HISTOGRAM_DEFAULT_BIN_WIDTH 1048576
+#endif
 
 struct latency_counter_s
 {
@@ -47,7 +51,7 @@ struct latency_counter_s
   cdtime_t min;
   cdtime_t max;
 
-  int bin_width;
+  cdtime_t bin_width;
   int histogram[HISTOGRAM_NUM_BINS];
 };
 
@@ -67,47 +71,46 @@ struct latency_counter_s
 *
 * So, if the required bin width is 300, then new bin width will be 512 as it is
 * the next nearest power of 2.
-*
 */
-void change_bin_width (latency_counter_t *lc, size_t val) /* {{{ */
+void change_bin_width (latency_counter_t *lc, cdtime_t latency) /* {{{ */
 {
-  int i=0;
   /* This function is called because the new value is above histogram's range.
    * First find the required bin width:
    *           requiredBinWidth = (value + 1) / numBins
    * then get the next nearest power of 2
    *           newBinWidth = 2^(ceil(log2(requiredBinWidth)))
    */
-  double required_bin_width = (double)(val + 1) / HISTOGRAM_NUM_BINS;
-  double required_bin_width_logbase2 = log(required_bin_width) / log(2.0);
-  int new_bin_width = (int)(pow(2.0, ceil( required_bin_width_logbase2)));
-  int old_bin_width = lc->bin_width;
+  double required_bin_width = ((double) (latency + 1)) / ((double) HISTOGRAM_NUM_BINS);
+  double required_bin_width_logbase2 = log (required_bin_width) / log (2.0);
+  cdtime_t new_bin_width = (cdtime_t) (pow (2.0, ceil (required_bin_width_logbase2)) + .5);
+  cdtime_t old_bin_width = lc->bin_width;
+
   lc->bin_width = new_bin_width;
 
-  /*
-   * bin width has been increased, now iterate through all bins and move the
-   * old bin's count to new bin.
-   */
+  /* bin_width has been increased, now iterate through all bins and move the
+   * old bin's count to new bin. */
   if (lc->num > 0) // if the histogram has data then iterate else skip
   {
-      double width_change_ratio = old_bin_width / new_bin_width;
-      for (i=0; i<HISTOGRAM_NUM_BINS; i++)
+      double width_change_ratio = ((double) old_bin_width) / ((double) new_bin_width);
+      size_t i;
+
+      for (i = 0; i < HISTOGRAM_NUM_BINS; i++)
       {
-         int new_bin = (int)(i * width_change_ratio);
+         size_t new_bin = (size_t) (((double) i) * width_change_ratio);
          if (i == new_bin)
              continue;
+         assert (new_bin < i);
+
          lc->histogram[new_bin] += lc->histogram[i];
          lc->histogram[i] = 0;
       }
-      DEBUG("utils_latency: change_bin_width: fixed all bins");
   }
 
-  DEBUG("utils_latency: change_bin_width: val-[%zu], oldBinWidth-[%d], "
-          "newBinWidth-[%d], required_bin_width-[%f], "
-          "required_bin_width_logbase2-[%f]",
-          val, old_bin_width, new_bin_width, required_bin_width,
-          required_bin_width_logbase2);
-
+  DEBUG("utils_latency: change_bin_width: latency = %.3f; "
+      "old_bin_width = %.3f; new_bin_width = %.3f;",
+      CDTIME_T_TO_DOUBLE (latency),
+      CDTIME_T_TO_DOUBLE (old_bin_width),
+      CDTIME_T_TO_DOUBLE (new_bin_width));
 } /* }}} void change_bin_width */
 
 latency_counter_t *latency_counter_create () /* {{{ */
@@ -131,9 +134,9 @@ void latency_counter_destroy (latency_counter_t *lc) /* {{{ */
 
 void latency_counter_add (latency_counter_t *lc, cdtime_t latency) /* {{{ */
 {
-  size_t latency_ms;
+  cdtime_t bin;
 
-  if ((lc == NULL) || (latency == 0))
+  if ((lc == NULL) || (latency == 0) || (latency > ((cdtime_t) LLONG_MAX)))
     return;
 
   lc->sum += latency;
@@ -149,16 +152,14 @@ void latency_counter_add (latency_counter_t *lc, cdtime_t latency) /* {{{ */
   /* A latency of _exactly_ 1.0 ms should be stored in the buffer 0, so
    * subtract one from the cdtime_t value so that exactly 1.0 ms get sorted
    * accordingly. */
-  latency_ms = (size_t) CDTIME_T_TO_MS (latency - 1);
-
-  int bin = (int)(latency_ms / lc->bin_width);
+  bin = (latency - 1) / lc->bin_width;
   if (bin >= HISTOGRAM_NUM_BINS)
   {
-      change_bin_width(lc, latency_ms);
-      bin = (int)(latency_ms / lc->bin_width);
+      change_bin_width (lc, latency);
+      bin = (latency - 1) / lc->bin_width;
       if (bin >= HISTOGRAM_NUM_BINS)
       {
-          ERROR("utils_latency: latency_counter_add: Invalid bin %d", bin);
+          ERROR ("utils_latency: latency_counter_add: Invalid bin %lu", bin);
           return;
       }
   }
@@ -170,7 +171,7 @@ void latency_counter_reset (latency_counter_t *lc) /* {{{ */
   if (lc == NULL)
     return;
 
-  int bin_width = lc->bin_width;
+  cdtime_t bin_width = lc->bin_width;
   memset (lc, 0, sizeof (*lc));
 
   /* preserve bin width */
@@ -217,14 +218,14 @@ cdtime_t latency_counter_get_average (latency_counter_t *lc) /* {{{ */
   return (DOUBLE_TO_CDTIME_T (average));
 } /* }}} cdtime_t latency_counter_get_average */
 
-cdtime_t latency_counter_get_percentile (latency_counter_t *lc,
+cdtime_t latency_counter_get_percentile (latency_counter_t *lc, /* {{{ */
     double percent)
 {
   double percent_upper;
   double percent_lower;
-  double ms_upper;
-  double ms_lower;
-  double ms_interpolated;
+  double p;
+  cdtime_t latency_lower;
+  cdtime_t latency_interpolated;
   int sum;
   size_t i;
 
@@ -254,16 +255,18 @@ cdtime_t latency_counter_get_percentile (latency_counter_t *lc,
   assert (percent_upper >= percent);
   assert (percent_lower < percent);
 
-  ms_upper = (double) ( (i + 1) * lc->bin_width );
-  ms_lower = (double) ( i * lc->bin_width );
   if (i == 0)
-    return (MS_TO_CDTIME_T (ms_upper));
+    return (lc->bin_width);
+
+  latency_lower = ((cdtime_t) i) * lc->bin_width;
+  p = (percent - percent_lower) / (percent_upper - percent_lower);
 
-  ms_interpolated = (((percent_upper - percent) * ms_lower)
-      + ((percent - percent_lower) * ms_upper))
-    / (percent_upper - percent_lower);
+  latency_interpolated = latency_lower
+    + DOUBLE_TO_CDTIME_T (p * CDTIME_T_TO_DOUBLE (lc->bin_width));
 
-  return (MS_TO_CDTIME_T (ms_interpolated));
+  DEBUG ("latency_counter_get_percentile: latency_interpolated = %.3f",
+      CDTIME_T_TO_DOUBLE (latency_interpolated));
+  return (latency_interpolated);
 } /* }}} cdtime_t latency_counter_get_percentile */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_latency_test.c b/src/utils_latency_test.c
new file mode 100644 (file)
index 0000000..9aecf38
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+ * collectd - src/utils_latency_test.c
+ * Copyright (C) 2015       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ */
+
+#define DBL_PRECISION 1e-9
+
+#include "testing.h"
+#include "collectd.h"
+#include "common.h" /* for STATIC_ARRAY_SIZE */
+#include "utils_time.h"
+#include "utils_latency.h"
+
+DEF_TEST(simple)
+{
+  struct {
+    double val;
+    double min;
+    double max;
+    double sum;
+    double avg;
+  } cases[] = {
+  /* val  min  max  sum  avg */
+    {0.5, 0.5, 0.5, 0.5, 0.5},
+    {0.3, 0.3, 0.5, 0.8, 0.4},
+    {0.7, 0.3, 0.7, 1.5, 0.5},
+    {2.5, 0.3, 2.5, 4.0, 1.0},
+    { -1, 0.3, 2.5, 4.0, 1.0},
+  };
+  size_t i;
+  latency_counter_t *l;
+
+  CHECK_NOT_NULL (l = latency_counter_create ());
+
+  for (i = 0; i < STATIC_ARRAY_SIZE (cases); i++) {
+    latency_counter_add (l, DOUBLE_TO_CDTIME_T (cases[i].val));
+
+    DBLEQ (cases[i].min, CDTIME_T_TO_DOUBLE (latency_counter_get_min (l)));
+    DBLEQ (cases[i].max, CDTIME_T_TO_DOUBLE (latency_counter_get_max (l)));
+    DBLEQ (cases[i].sum, CDTIME_T_TO_DOUBLE (latency_counter_get_sum (l)));
+    DBLEQ (cases[i].avg, CDTIME_T_TO_DOUBLE (latency_counter_get_average (l)));
+  }
+
+  latency_counter_destroy (l);
+  return 0;
+}
+
+DEF_TEST(percentile)
+{
+  size_t i;
+  latency_counter_t *l;
+
+  CHECK_NOT_NULL (l = latency_counter_create ());
+
+  for (i = 0; i < 100; i++) {
+    latency_counter_add (l, TIME_T_TO_CDTIME_T (((time_t) i) + 1));
+  }
+
+  DBLEQ (  1.0, CDTIME_T_TO_DOUBLE (latency_counter_get_min (l)));
+  DBLEQ (100.0, CDTIME_T_TO_DOUBLE (latency_counter_get_max (l)));
+  DBLEQ (100.0 * 101.0 / 2.0, CDTIME_T_TO_DOUBLE (latency_counter_get_sum (l)));
+  DBLEQ ( 50.5, CDTIME_T_TO_DOUBLE (latency_counter_get_average (l)));
+
+  DBLEQ (50.0, CDTIME_T_TO_DOUBLE (latency_counter_get_percentile (l, 50.0)));
+  DBLEQ (80.0, CDTIME_T_TO_DOUBLE (latency_counter_get_percentile (l, 80.0)));
+  DBLEQ (95.0, CDTIME_T_TO_DOUBLE (latency_counter_get_percentile (l, 95.0)));
+  DBLEQ (99.0, CDTIME_T_TO_DOUBLE (latency_counter_get_percentile (l, 99.0)));
+
+  CHECK_ZERO (latency_counter_get_percentile (l, -1.0));
+  CHECK_ZERO (latency_counter_get_percentile (l, 101.0));
+
+  latency_counter_destroy (l);
+  return 0;
+}
+
+int main (void)
+{
+  RUN_TEST(simple);
+  RUN_TEST(percentile);
+
+  END_TEST;
+}
+
+/* vim: set sw=2 sts=2 et : */
index 6a2676a..41cc0a4 100644 (file)
@@ -65,7 +65,7 @@ static void *lookup_class_callback (data_set_t const *ds,
   identifier_t *class = user_class;
   identifier_t *obj;
 
-  OK(expect_new_obj);
+  assert (expect_new_obj);
 
   memcpy (&last_class_ident, class, sizeof (last_class_ident));
   
@@ -81,7 +81,7 @@ static void *lookup_class_callback (data_set_t const *ds,
   return ((void *) obj);
 }
 
-static void checked_lookup_add (lookup_t *obj, /* {{{ */
+static int checked_lookup_add (lookup_t *obj, /* {{{ */
     char const *host,
     char const *plugin, char const *plugin_instance,
     char const *type, char const *type_instance,
@@ -101,7 +101,8 @@ static void checked_lookup_add (lookup_t *obj, /* {{{ */
   memmove (user_class, &ident, sizeof (ident));
 
   OK(lookup_add (obj, &ident, group_by, user_class) == 0);
-} /* }}} void test_add */
+  return 0;
+} /* }}} int checked_lookup_add */
 
 static int checked_lookup_search (lookup_t *obj,
     char const *host,
@@ -129,20 +130,11 @@ static int checked_lookup_search (lookup_t *obj,
   return (status);
 }
 
-static lookup_t *checked_lookup_create (void)
-{
-  lookup_t *obj = lookup_create (
-      lookup_class_callback,
-      lookup_obj_callback,
-      (void *) free,
-      (void *) free);
-  OK(obj != NULL);
-  return (obj);
-}
-
 DEF_TEST(group_by_specific_host)
 {
-  lookup_t *obj = checked_lookup_create ();
+  lookup_t *obj;
+  CHECK_NOT_NULL (obj = lookup_create (
+        lookup_class_callback, lookup_obj_callback, (void *) free, (void *) free));
 
   checked_lookup_add (obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST);
   checked_lookup_search (obj, "host0", "test", "", "test", "0",
@@ -160,7 +152,9 @@ DEF_TEST(group_by_specific_host)
 
 DEF_TEST(group_by_any_host)
 {
-  lookup_t *obj = checked_lookup_create ();
+  lookup_t *obj;
+  CHECK_NOT_NULL (obj = lookup_create (
+        lookup_class_callback, lookup_obj_callback, (void *) free, (void *) free));
 
   checked_lookup_add (obj, "/.*/", "/.*/", "/.*/", "test", "/.*/", LU_GROUP_BY_HOST);
   checked_lookup_search (obj, "host0", "plugin0", "", "test", "0",
@@ -186,9 +180,12 @@ DEF_TEST(group_by_any_host)
 
 DEF_TEST(multiple_lookups)
 {
-  lookup_t *obj = checked_lookup_create ();
+  lookup_t *obj;
   int status;
 
+  CHECK_NOT_NULL (obj = lookup_create (
+        lookup_class_callback, lookup_obj_callback, (void *) free, (void *) free));
+
   checked_lookup_add (obj, "/.*/", "plugin0", "", "test", "/.*/", LU_GROUP_BY_HOST);
   checked_lookup_add (obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST);
 
@@ -211,7 +208,9 @@ DEF_TEST(multiple_lookups)
 
 DEF_TEST(regex)
 {
-  lookup_t *obj = checked_lookup_create ();
+  lookup_t *obj;
+  CHECK_NOT_NULL (obj = lookup_create (
+        lookup_class_callback, lookup_obj_callback, (void *) free, (void *) free));
 
   checked_lookup_add (obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/",
       LU_GROUP_BY_TYPE_INSTANCE);
index 0ea8c7f..a94cb80 100644 (file)
@@ -608,6 +608,7 @@ static int varnish_read (user_data_t *ud) /* {{{ */
                status = VSM_n_Arg (vd, conf->instance);
                if (status < 0)
                {
+                       VSM_Delete (vd);
                        ERROR ("varnish plugin: VSM_n_Arg (\"%s\") failed "
                                        "with status %i.",
                                        conf->instance, status);
@@ -621,7 +622,8 @@ static int varnish_read (user_data_t *ud) /* {{{ */
        if (VSM_Open (vd))
 #endif
        {
-               ERROR ("varnish plugin: Unable to load statistics.");
+               VSM_Delete (vd);
+               ERROR ("varnish plugin: Unable to open connection.");
 
                return (-1);
        }
@@ -631,9 +633,17 @@ static int varnish_read (user_data_t *ud) /* {{{ */
 #else /* if HAVE_VARNISH_V4 */
        stats = VSC_Main(vd, NULL);
 #endif
+       if (!stats)
+       {
+               VSM_Delete (vd);
+               ERROR ("varnish plugin: Unable to get statistics.");
+
+               return (-1);
+       }
+
 
        varnish_monitor (conf, stats);
-       VSM_Close (vd);
+       VSM_Delete (vd);
 
        return (0);
 } /* }}} */
index fa58ad2..7ba476c 100644 (file)
@@ -45,6 +45,7 @@ struct wr_node_s
   int port;
   struct timeval timeout;
   char *prefix;
+  int database;
 
   redisContext *conn;
   pthread_mutex_t lock;
@@ -138,6 +139,12 @@ static int wr_write (const data_set_t *ds, /* {{{ */
       pthread_mutex_unlock (&node->lock);
       return (-1);
     }
+   
+    rr = redisCommand(node->conn, "SELECT %d", node->database);
+    if (rr == NULL)
+      WARNING("SELECT command error. database:%d message:%s", node->database, node->conn->errstr);
+    else
+      freeReplyObject (rr);
   }
 
   rr = redisCommand (node->conn, "ZADD %s %s %s", key, time, value);
@@ -196,6 +203,7 @@ static int wr_config_node (oconfig_item_t *ci) /* {{{ */
   node->timeout.tv_usec = 1000;
   node->conn = NULL;
   node->prefix = NULL;
+  node->database = 0;
   pthread_mutex_init (&node->lock, /* attr = */ NULL);
 
   status = cf_util_get_string_buffer (ci, node->name, sizeof (node->name));
@@ -227,6 +235,9 @@ static int wr_config_node (oconfig_item_t *ci) /* {{{ */
     else if (strcasecmp ("Prefix", child->key) == 0) {
       status = cf_util_get_string (child, &node->prefix);
     }
+    else if (strcasecmp ("Database", child->key) == 0) {
+      status = cf_util_get_int (child, &node->database);
+    }
     else
       WARNING ("write_redis plugin: Ignoring unknown config option \"%s\".",
           child->key);