daemon/common.[ch]: add set_sock_opts() function
authorMarc Fournier <marc.fournier@camptocamp.com>
Sat, 13 Feb 2016 10:51:23 +0000 (11:51 +0100)
committerMarc Fournier <marc.fournier@camptocamp.com>
Tue, 9 Aug 2016 09:05:37 +0000 (11:05 +0200)
Enables the TCP keepalive mechanism on a given socket.

This would typically be used on sockets opened by write plugins
which submit data to a remote server.

Up to now, collectd fails to notice when a remote server dies without
properly closing the network connection, leading to more and more memory
getting allocated as the values pile up in the write queue.

The keepalive values are proportional to the configured `Interval`. The
first probe is emitted `10 x Interval` seconds after the last TCP packet
was seen, and then once per `Interval` until the threshold defined by
the OS is reached.

See: http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/#checkdeadpeers

src/daemon/common.c
src/daemon/common.h

index b60530a..c4dbecb 100644 (file)
 # include <netinet/in.h>
 #endif
 
+#if HAVE_NETINET_TCP_H
+# include <netinet/tcp.h>
+#endif
+
 /* for ntohl and htonl */
 #if HAVE_ARPA_INET_H
 # include <arpa/inet.h>
@@ -1557,6 +1561,46 @@ int service_name_to_port_number (const char *service_name)
        return (-1);
 } /* int service_name_to_port_number */
 
+void set_sock_opts (int sockfd) /* {{{ */
+{
+       int status;
+       int socktype;
+
+       socklen_t socklen = sizeof (socklen_t);
+       int so_keepalive = 1;
+       int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1);
+       int tcp_keepintvl = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1);
+
+       status = getsockopt (sockfd, SOL_SOCKET, SO_TYPE, &socktype, &socklen);
+       if (status != 0)
+       {
+               WARNING ("set_sock_opts: failed to determine socket type");
+               return;
+       }
+
+       if (socktype == SOCK_STREAM)
+       {
+               status = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+                               &so_keepalive, sizeof (so_keepalive));
+               if (status != 0)
+                       WARNING ("set_sock_opts: failed to set socket keepalive flag");
+
+#ifdef TCP_KEEPIDLE
+               status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
+                               &tcp_keepidle, sizeof (tcp_keepidle));
+               if (status != 0)
+                       WARNING ("set_sock_opts: failed to set socket tcp keepalive time");
+#endif
+
+#ifdef TCP_KEEPINTVL
+               status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
+                               &tcp_keepintvl, sizeof (tcp_keepintvl));
+               if (status != 0)
+                       WARNING ("set_sock_opts: failed to set socket tcp keepalive interval");
+#endif
+       }
+} /* }}} void set_sock_opts */
+
 int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
 {
        derive_t tmp;
index 8079661..5ad2b50 100644 (file)
@@ -361,6 +361,9 @@ int value_to_rate (gauge_t *ret_rate, value_t value, int ds_type, cdtime_t t,
  * (in the range [1-65535]). Returns less than zero on error. */
 int service_name_to_port_number (const char *service_name);
 
+/* Sets various, non-default, socket options */
+void set_sock_opts (int sockfd);
+
 /** Parse a string to a derive_t value. Returns zero on success or non-zero on
  * failure. If failure is returned, ret_value is not touched. */
 int strtoderive (const char *string, derive_t *ret_value);