src/daemon/common.c: avoid leaking cap_header in error condition
[collectd.git] / src / tcpconns.c
index bf07a2a..e74e4bc 100644 (file)
@@ -17,7 +17,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Author:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Michael Stapelberg <michael+git at stapelberg.de>
  **/
 
@@ -58,6 +58,7 @@
  */
 
 #include "collectd.h"
+
 #include "common.h"
 #include "plugin.h"
 
 
 #if KERNEL_LINUX
 # include <asm/types.h>
-/* sys/socket.h is necessary to compile when using netlink on older systems. */
-# include <sys/socket.h>
 # include <linux/netlink.h>
 #if HAVE_LINUX_INET_DIAG_H
 # include <linux/inet_diag.h>
 #endif
-# include <sys/socket.h>
 # include <arpa/inet.h>
 /* #endif KERNEL_LINUX */
 
@@ -90,9 +88,6 @@
 #if HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
-#if HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
 #if HAVE_NET_IF_H
 # include <net/if.h>
 #endif
 /* This is for OpenBSD and NetBSD. */
 #elif HAVE_LIBKVM_NLIST
 # include <sys/queue.h>
-# include <sys/socket.h>
 # include <net/route.h>
 # include <netinet/in.h>
 # include <netinet/in_systm.h>
@@ -219,13 +213,13 @@ static const char *tcp_state[] =
   "CLOSED",
   "LISTEN",
   "SYN_SENT",
-  "SYN_RCVD",
+  "SYN_RECV",
   "ESTABLISHED",
   "CLOSE_WAIT",
-  "FIN_WAIT_1",
+  "FIN_WAIT1",
   "CLOSING",
   "LAST_ACK",
-  "FIN_WAIT_2",
+  "FIN_WAIT2",
   "TIME_WAIT"
 };
 
@@ -272,12 +266,15 @@ static const char *config_keys[] =
 {
   "ListeningPorts",
   "LocalPort",
-  "RemotePort"
+  "RemotePort",
+  "AllPortsSummary"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
 static int port_collect_listening = 0;
+static int port_collect_total = 0;
 static port_entry_t *port_list_head = NULL;
+static uint32_t count_total[TCP_STATE_MAX + 1];
 
 #if KERNEL_LINUX
 #if HAVE_STRUCT_LINUX_INET_DIAG_REQ
@@ -287,7 +284,7 @@ static port_entry_t *port_list_head = NULL;
 static uint32_t sequence_number = 0;
 #endif
 
-enum
+static enum
 {
   SRC_DUNNO,
   SRC_NETLINK,
@@ -295,17 +292,21 @@ enum
 } linux_source = SRC_DUNNO;
 #endif
 
+static void conn_prepare_vl (value_list_t *vl, value_t *values)
+{
+  vl->values = values;
+  vl->values_len = 1;
+  sstrncpy (vl->host, hostname_g, sizeof (vl->host));
+  sstrncpy (vl->plugin, "tcpconns", sizeof (vl->plugin));
+  sstrncpy (vl->type, "tcp_connections", sizeof (vl->type));
+}
+
 static void conn_submit_port_entry (port_entry_t *pe)
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
-  int i;
 
-  vl.values = values;
-  vl.values_len = 1;
-  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-  sstrncpy (vl.plugin, "tcpconns", sizeof (vl.plugin));
-  sstrncpy (vl.type, "tcp_connections", sizeof (vl.type));
+  conn_prepare_vl (&vl, values);
 
   if (((port_collect_listening != 0) && (pe->flags & PORT_IS_LISTENING))
       || (pe->flags & PORT_COLLECT_LOCAL))
@@ -313,7 +314,7 @@ static void conn_submit_port_entry (port_entry_t *pe)
     ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
        "%"PRIu16"-local", pe->port);
 
-    for (i = 1; i <= TCP_STATE_MAX; i++)
+    for (int i = 1; i <= TCP_STATE_MAX; i++)
     {
       vl.values[0].gauge = pe->count_local[i];
 
@@ -328,7 +329,7 @@ static void conn_submit_port_entry (port_entry_t *pe)
     ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
        "%"PRIu16"-remote", pe->port);
 
-    for (i = 1; i <= TCP_STATE_MAX; i++)
+    for (int i = 1; i <= TCP_STATE_MAX; i++)
     {
       vl.values[0].gauge = pe->count_remote[i];
 
@@ -339,11 +340,31 @@ static void conn_submit_port_entry (port_entry_t *pe)
   }
 } /* void conn_submit */
 
+static void conn_submit_port_total (void)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  conn_prepare_vl (&vl, values);
+
+  sstrncpy (vl.plugin_instance, "all", sizeof (vl.plugin_instance));
+
+  for (int i = 1; i <= TCP_STATE_MAX; i++)
+  {
+    vl.values[0].gauge = count_total[i];
+
+    sstrncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+  }
+}
+
 static void conn_submit_all (void)
 {
-  port_entry_t *pe;
+  if (port_collect_total)
+    conn_submit_port_total ();
 
-  for (pe = port_list_head; pe != NULL; pe = pe->next)
+  for (port_entry_t *pe = port_list_head; pe != NULL; pe = pe->next)
     conn_submit_port_entry (pe);
 } /* void conn_submit_all */
 
@@ -361,10 +382,9 @@ static port_entry_t *conn_get_port_entry (uint16_t port, int create)
 
   if ((ret == NULL) && (create != 0))
   {
-    ret = (port_entry_t *) malloc (sizeof (port_entry_t));
+    ret = calloc (1, sizeof (*ret));
     if (ret == NULL)
       return (NULL);
-    memset (ret, '\0', sizeof (port_entry_t));
 
     ret->port = port;
     ret->next = port_list_head;
@@ -381,6 +401,8 @@ static void conn_reset_port_entry (void)
   port_entry_t *prev = NULL;
   port_entry_t *pe = port_list_head;
 
+  memset (&count_total, '\0', sizeof(count_total));
+
   while (pe != NULL)
   {
     /* If this entry was created while reading the files (ant not when handling
@@ -429,6 +451,8 @@ static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t
     return (-1);
   }
 
+  count_total[state]++;
+
   /* Listening sockets */
   if ((state == TCP_STATE_LISTEN) && (port_collect_listening != 0))
   {
@@ -458,10 +482,6 @@ static int conn_read_netlink (void)
 {
 #if HAVE_STRUCT_LINUX_INET_DIAG_REQ
   int fd;
-  struct sockaddr_nl nladdr;
-  struct nlreq req;
-  struct msghdr msg;
-  struct iovec iov;
   struct inet_diag_msg *r;
   char buf[8192];
 
@@ -476,34 +496,38 @@ static int conn_read_netlink (void)
     return (-1);
   }
 
-  memset(&nladdr, 0, sizeof(nladdr));
-  nladdr.nl_family = AF_NETLINK;
-
-  memset(&req, 0, sizeof(req));
-  req.nlh.nlmsg_len = sizeof(req);
-  req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
-  /* NLM_F_ROOT: return the complete table instead of a single entry.
-   * NLM_F_MATCH: return all entries matching criteria (not implemented)
-   * NLM_F_REQUEST: must be set on all request messages */
-  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
-  req.nlh.nlmsg_pid = 0;
-  /* The sequence_number is used to track our messages. Since netlink is not
-   * reliable, we don't want to end up with a corrupt or incomplete old
-   * message in case the system is/was out of memory. */
-  req.nlh.nlmsg_seq = ++sequence_number;
-  req.r.idiag_family = AF_INET;
-  req.r.idiag_states = 0xfff;
-  req.r.idiag_ext = 0;
-
-  memset(&iov, 0, sizeof(iov));
-  iov.iov_base = &req;
-  iov.iov_len = sizeof(req);
-
-  memset(&msg, 0, sizeof(msg));
-  msg.msg_name = (void*)&nladdr;
-  msg.msg_namelen = sizeof(nladdr);
-  msg.msg_iov = &iov;
-  msg.msg_iovlen = 1;
+  struct sockaddr_nl nladdr = {
+    .nl_family = AF_NETLINK
+  };
+
+  struct nlreq req = {
+    .nlh.nlmsg_len = sizeof(req),
+    .nlh.nlmsg_type = TCPDIAG_GETSOCK,
+    /* NLM_F_ROOT: return the complete table instead of a single entry.
+     * NLM_F_MATCH: return all entries matching criteria (not implemented)
+     * NLM_F_REQUEST: must be set on all request messages */
+    .nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST,
+    .nlh.nlmsg_pid = 0,
+    /* The sequence_number is used to track our messages. Since netlink is not
+     * reliable, we don't want to end up with a corrupt or incomplete old
+     * message in case the system is/was out of memory. */
+    .nlh.nlmsg_seq = ++sequence_number,
+    .r.idiag_family = AF_INET,
+    .r.idiag_states = 0xfff,
+    .r.idiag_ext = 0
+  };
+
+  struct iovec iov = {
+    .iov_base = &req,
+    .iov_len = sizeof(req)
+  };
+
+  struct msghdr msg = {
+    .msg_name = (void*)&nladdr,
+    .msg_namelen = sizeof(nladdr),
+    .msg_iov = &iov,
+    .msg_iovlen = 1
+  };
 
   if (sendmsg (fd, &msg, 0) < 0)
   {
@@ -705,6 +729,13 @@ static int conn_config (const char *key, const char *value)
       else
        pe->flags |= PORT_COLLECT_REMOTE;
   }
+  else if (strcasecmp (key, "AllPortsSummary") == 0)
+  {
+    if (IS_TRUE (value))
+      port_collect_total = 1;
+    else
+      port_collect_total = 0;
+  }
   else
   {
     return (-1);
@@ -716,7 +747,7 @@ static int conn_config (const char *key, const char *value)
 #if KERNEL_LINUX
 static int conn_init (void)
 {
-  if (port_list_head == NULL)
+  if (port_collect_total == 0 && port_list_head == NULL)
     port_collect_listening = 1;
 
   return (0);
@@ -797,7 +828,7 @@ static int conn_read (void)
     return (-1);
   }
 
-  buffer = (char *) malloc (buffer_len);
+  buffer = malloc (buffer_len);
   if (buffer == NULL)
   {
     ERROR ("tcpconns plugin: malloc failed.");
@@ -910,7 +941,9 @@ static int conn_init (void)
 static int conn_read (void)
 {
   struct inpcbtable table;
+#if !defined(__OpenBSD__) && (defined(__NetBSD_Version__) && __NetBSD_Version__ <= 699002700)
   struct inpcb *head;
+#endif
   struct inpcb *next;
   struct inpcb inpcb;
   struct tcpcb tcpcb;
@@ -923,12 +956,19 @@ static int conn_read (void)
   if (status != 0)
     return (-1);
 
+#if defined(__OpenBSD__) || (defined(__NetBSD_Version__) && __NetBSD_Version__ > 699002700)
+  /* inpt_queue is a TAILQ on OpenBSD */
+  /* Get the first pcb */
+  next = (struct inpcb *)TAILQ_FIRST (&table.inpt_queue);
+  while (next)
+#else
   /* Get the `head' pcb */
   head = (struct inpcb *) &(inpcbtable_ptr->inpt_queue);
   /* Get the first pcb */
   next = (struct inpcb *)CIRCLEQ_FIRST (&table.inpt_queue);
 
   while (next != head)
+#endif
   {
     /* Read the pcb pointed to by `next' into `inpcb' */
     status = kread ((u_long) next, &inpcb, sizeof (inpcb));
@@ -936,7 +976,12 @@ static int conn_read (void)
       return (-1);
 
     /* Advance `next' */
+#if defined(__OpenBSD__) || (defined(__NetBSD_Version__) && __NetBSD_Version__ > 699002700)
+    /* inpt_queue is a TAILQ on OpenBSD */
+    next = (struct inpcb *)TAILQ_NEXT (&inpcb, inp_queue);
+#else
     next = (struct inpcb *)CIRCLEQ_NEXT (&inpcb, inp_queue);
+#endif
 
     /* Ignore sockets, that are not connected. */
 #ifdef __NetBSD__
@@ -968,7 +1013,6 @@ static int conn_read (void)
 static int conn_read (void)
 {
   int size;
-  int i;
   int nconn;
   void *data;
   struct netinfo_header *header;
@@ -1010,7 +1054,7 @@ static int conn_read (void)
   nconn = header->size;
   conn = (struct netinfo_conn *)(data + sizeof(struct netinfo_header));
 
-  for (i=0; i < nconn; conn++, i++)
+  for (int i = 0; i < nconn; conn++, i++)
   {
     conn_handle_ports (conn->srcport, conn->dstport, conn->tcp_state);
   }