Use ignorelist + malloc fix + conf clarification
[collectd.git] / src / connectivity.c
index cb0ed98..e58ffde 100644 (file)
@@ -30,6 +30,7 @@
 #include "common.h"
 #include "plugin.h"
 #include "utils_complain.h"
+#include "utils_ignorelist.h"
 
 #include <asm/types.h>
 #include <errno.h>
@@ -93,7 +94,7 @@
 /*
  * Private data types
  */
-struct interfacelist_s {
+struct interface_list_s {
   char *interface;
 
   uint32_t status;
@@ -101,14 +102,17 @@ struct interfacelist_s {
   uint32_t sent;
   long long unsigned int timestamp;
 
-  struct interfacelist_s *next;
+  struct interface_list_s *next;
 };
-typedef struct interfacelist_s interfacelist_t;
+typedef struct interface_list_s interface_list_t;
 
 /*
  * Private variables
  */
-static interfacelist_t *interfacelist_head = NULL;
+static ignorelist_t *ignorelist = NULL;
+
+static interface_list_t *interface_list_head = NULL;
+static int monitor_all_interfaces = 0;
 
 static int connectivity_thread_loop = 0;
 static int connectivity_thread_error = 0;
@@ -129,6 +133,7 @@ static int gen_message_payload(int state, int old_state, const char *interface,
                                long long unsigned int timestamp, char **buf) {
   const unsigned char *buf2;
   yajl_gen g;
+  char json_str[DATA_MAX_NAME_LEN];
 
 #if !defined(HAVE_YAJL_V2)
   yajl_gen_config conf = {};
@@ -169,17 +174,13 @@ static int gen_message_payload(int state, int old_state, const char *interface,
 
   event_id = event_id + 1;
   int event_id_len = sizeof(char) * sizeof(int) * 4 + 1;
-  char *event_id_str = malloc(event_id_len);
-  snprintf(event_id_str, event_id_len, "%d", event_id);
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, event_id_len, "%d", event_id);
 
-  if (yajl_gen_number(g, event_id_str, strlen(event_id_str)) !=
-      yajl_gen_status_ok) {
-    sfree(event_id_str);
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
     goto err;
   }
 
-  sfree(event_id_str);
-
   // eventName
   if (yajl_gen_string(g, (u_char *)CONNECTIVITY_EVENT_NAME_FIELD,
                       strlen(CONNECTIVITY_EVENT_NAME_FIELD)) !=
@@ -191,20 +192,16 @@ static int gen_message_payload(int state, int old_state, const char *interface,
   event_name_len = event_name_len + (state == 0 ? 4 : 2); // "down" or "up"
   event_name_len =
       event_name_len + 12; // "interface", 2 spaces and null-terminator
-  char *event_name_str = malloc(event_name_len);
-  memset(event_name_str, '\0', event_name_len);
-  snprintf(event_name_str, event_name_len, "interface %s %s", interface,
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, event_name_len, "interface %s %s", interface,
            (state == 0 ? CONNECTIVITY_EVENT_NAME_DOWN_VALUE
                        : CONNECTIVITY_EVENT_NAME_UP_VALUE));
 
-  if (yajl_gen_string(g, (u_char *)event_name_str, strlen(event_name_str)) !=
+  if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
       yajl_gen_status_ok) {
-    sfree(event_name_str);
     goto err;
   }
 
-  sfree(event_name_str);
-
   // lastEpochMicrosec
   if (yajl_gen_string(g, (u_char *)CONNECTIVITY_LAST_EPOCH_MICROSEC_FIELD,
                       strlen(CONNECTIVITY_LAST_EPOCH_MICROSEC_FIELD)) !=
@@ -213,18 +210,14 @@ static int gen_message_payload(int state, int old_state, const char *interface,
 
   int last_epoch_microsec_len =
       sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
-  char *last_epoch_microsec_str = malloc(last_epoch_microsec_len);
-  snprintf(last_epoch_microsec_str, last_epoch_microsec_len, "%llu",
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, last_epoch_microsec_len, "%llu",
            (long long unsigned int)CDTIME_T_TO_US(cdtime()));
 
-  if (yajl_gen_number(g, last_epoch_microsec_str,
-                      strlen(last_epoch_microsec_str)) != yajl_gen_status_ok) {
-    sfree(last_epoch_microsec_str);
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
     goto err;
   }
 
-  sfree(last_epoch_microsec_str);
-
   // priority
   if (yajl_gen_string(g, (u_char *)CONNECTIVITY_PRIORITY_FIELD,
                       strlen(CONNECTIVITY_PRIORITY_FIELD)) !=
@@ -276,18 +269,14 @@ static int gen_message_payload(int state, int old_state, const char *interface,
 
   int start_epoch_microsec_len =
       sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
-  char *start_epoch_microsec_str = malloc(start_epoch_microsec_len);
-  snprintf(start_epoch_microsec_str, start_epoch_microsec_len, "%llu",
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, start_epoch_microsec_len, "%llu",
            (long long unsigned int)timestamp);
 
-  if (yajl_gen_number(g, start_epoch_microsec_str,
-                      strlen(start_epoch_microsec_str)) != yajl_gen_status_ok) {
-    sfree(start_epoch_microsec_str);
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
     goto err;
   }
 
-  sfree(start_epoch_microsec_str);
-
   // version
   if (yajl_gen_string(g, (u_char *)CONNECTIVITY_VERSION_FIELD,
                       strlen(CONNECTIVITY_VERSION_FIELD)) != yajl_gen_status_ok)
@@ -376,6 +365,13 @@ static int gen_message_payload(int state, int old_state, const char *interface,
 
   *buf = malloc(strlen((char *)buf2) + 1);
 
+  if (*buf == NULL) {
+    char errbuf[1024];
+    ERROR("connectivity plugin: malloc failed during gen_message_payload: %s",
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+    goto err;
+  }
+
   sstrncpy(*buf, (char *)buf2, strlen((char *)buf2) + 1);
 
   yajl_gen_free(g);
@@ -388,6 +384,41 @@ err:
   return -1;
 }
 
+static interface_list_t *add_interface(const char *interface, int status,
+                                       int prev_status) {
+  interface_list_t *il;
+  char *interface2;
+
+  il = malloc(sizeof(*il));
+  if (il == NULL) {
+    char errbuf[1024];
+    ERROR("connectivity plugin: malloc failed during add_interface: %s",
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+    return NULL;
+  }
+
+  interface2 = strdup(interface);
+  if (interface2 == NULL) {
+    char errbuf[1024];
+    sfree(il);
+    ERROR("connectivity plugin: strdup failed during add_interface: %s",
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+    return NULL;
+  }
+
+  il->interface = interface2;
+  il->status = status;
+  il->prev_status = prev_status;
+  il->timestamp = (long long unsigned int)CDTIME_T_TO_US(cdtime());
+  il->sent = 0;
+  il->next = interface_list_head;
+  interface_list_head = il;
+
+  DEBUG("connectivity plugin: added interface %s", interface2);
+
+  return il;
+}
+
 static int connectivity_link_state(struct nlmsghdr *msg) {
   int retval = 0;
   struct ifinfomsg *ifi = mnl_nlmsg_get_payload(msg);
@@ -396,7 +427,7 @@ static int connectivity_link_state(struct nlmsghdr *msg) {
 
   pthread_mutex_lock(&connectivity_lock);
 
-  interfacelist_t *il;
+  interface_list_t *il = NULL;
 
   /* Scan attribute list for device name. */
   mnl_attr_for_each(attr, msg, sizeof(*ifi)) {
@@ -413,34 +444,48 @@ static int connectivity_link_state(struct nlmsghdr *msg) {
 
     dev = mnl_attr_get_str(attr);
 
-    for (il = interfacelist_head; il != NULL; il = il->next)
+    // Check the list of interfaces we should monitor, if we've chosen
+    // a subset.  If we don't care about this one, abort.
+    if (ignorelist_match(ignorelist, dev) != 0) {
+      DEBUG("connectivity plugin: Ignoring link state change for unmonitored "
+            "interface: %s",
+            dev);
+      break;
+    }
+
+    for (il = interface_list_head; il != NULL; il = il->next)
       if (strcmp(dev, il->interface) == 0)
         break;
 
+    uint32_t prev_status;
+
     if (il == NULL) {
-      DEBUG("connectivity plugin: Ignoring link state change for unmonitored "
-            "interface: %s",
-            dev);
-    } else {
-      uint32_t prev_status;
-
-      prev_status = il->status;
-      il->status =
-          ((ifi->ifi_flags & IFF_RUNNING) ? LINK_STATE_UP : LINK_STATE_DOWN);
-      il->timestamp = (long long unsigned int)CDTIME_T_TO_US(cdtime());
-
-      // If the new status is different than the previous status,
-      // store the previous status and set sent to zero
-      if (il->status != prev_status) {
-        il->prev_status = prev_status;
-        il->sent = 0;
+      // We haven't encountered this interface yet, so add it to the linked list
+      il = add_interface(dev, LINK_STATE_UNKNOWN, LINK_STATE_UNKNOWN);
+
+      if (il == NULL) {
+        ERROR("connectivity plugin: unable to add interface %s during "
+              "connectivity_link_state",
+              dev);
+        return MNL_CB_ERROR;
       }
+    }
 
-      DEBUG("connectivity plugin (%llu): Interface %s status is now %s",
-            il->timestamp, dev,
-            ((ifi->ifi_flags & IFF_RUNNING) ? "UP" : "DOWN"));
+    prev_status = il->status;
+    il->status =
+        ((ifi->ifi_flags & IFF_RUNNING) ? LINK_STATE_UP : LINK_STATE_DOWN);
+    il->timestamp = (long long unsigned int)CDTIME_T_TO_US(cdtime());
+
+    // If the new status is different than the previous status,
+    // store the previous status and set sent to zero
+    if (il->status != prev_status) {
+      il->prev_status = prev_status;
+      il->sent = 0;
     }
 
+    DEBUG("connectivity plugin (%llu): Interface %s status is now %s",
+          il->timestamp, dev, ((ifi->ifi_flags & IFF_RUNNING) ? "UP" : "DOWN"));
+
     // no need to loop again, we found the interface name attr
     // (otherwise the first if-statement in the loop would
     // have moved us on with 'continue')
@@ -660,9 +705,10 @@ static int stop_thread(int shutdown) /* {{{ */
 
 static int connectivity_init(void) /* {{{ */
 {
-  if (interfacelist_head == NULL) {
-    NOTICE("connectivity plugin: No interfaces have been configured.");
-    return (-1);
+  if (interface_list_head == NULL) {
+    NOTICE("connectivity plugin: No interfaces have been selected, so all will "
+           "be monitored");
+    monitor_all_interfaces = 1;
   }
 
   return (start_thread());
@@ -670,35 +716,11 @@ static int connectivity_init(void) /* {{{ */
 
 static int connectivity_config(const char *key, const char *value) /* {{{ */
 {
-  if (strcasecmp(key, "Interface") == 0) {
-    interfacelist_t *il;
-    char *interface;
-
-    il = malloc(sizeof(*il));
-    if (il == NULL) {
-      char errbuf[1024];
-      ERROR("connectivity plugin: malloc failed during connectivity_config: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-      return (1);
-    }
-
-    interface = strdup(value);
-    if (interface == NULL) {
-      char errbuf[1024];
-      sfree(il);
-      ERROR("connectivity plugin: strdup failed connectivity_config: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-      return (1);
-    }
-
-    il->interface = interface;
-    il->status = LINK_STATE_UNKNOWN;
-    il->prev_status = LINK_STATE_UNKNOWN;
-    il->timestamp = (long long unsigned int)CDTIME_T_TO_US(cdtime());
-    il->sent = 0;
-    il->next = interfacelist_head;
-    interfacelist_head = il;
+  if (ignorelist == NULL)
+    ignorelist = ignorelist_create(/* invert = */ 1);
 
+  if (strcasecmp(key, "Interface") == 0) {
+    ignorelist_add(ignorelist, value);
   } else {
     return (-1);
   }
@@ -716,10 +738,7 @@ static void connectivity_dispatch_notification(
   if (value == LINK_STATE_UP)
     n.severity = NOTIF_OKAY;
 
-  char hostname[1024];
-  gethostname(hostname, sizeof(hostname));
-
-  sstrncpy(n.host, hostname, sizeof(n.host));
+  sstrncpy(n.host, hostname_g, sizeof(n.host));
   sstrncpy(n.plugin_instance, interface, sizeof(n.plugin_instance));
   sstrncpy(n.type, "gauge", sizeof(n.type));
   sstrncpy(n.type_instance, "interface_status", sizeof(n.type_instance));
@@ -763,7 +782,8 @@ static int connectivity_read(void) /* {{{ */
 
     stop_thread(0);
 
-    for (interfacelist_t *il = interfacelist_head; il != NULL; il = il->next) {
+    for (interface_list_t *il = interface_list_head; il != NULL;
+         il = il->next) {
       il->status = LINK_STATE_UNKNOWN;
       il->prev_status = LINK_STATE_UNKNOWN;
       il->sent = 0;
@@ -774,7 +794,7 @@ static int connectivity_read(void) /* {{{ */
     return (-1);
   } /* if (connectivity_thread_error != 0) */
 
-  for (interfacelist_t *il = interfacelist_head; il != NULL;
+  for (interface_list_t *il = interface_list_head; il != NULL;
        il = il->next) /* {{{ */
   {
     uint32_t status;
@@ -794,22 +814,22 @@ static int connectivity_read(void) /* {{{ */
     }
 
     pthread_mutex_unlock(&connectivity_lock);
-  } /* }}} for (il = interfacelist_head; il != NULL; il = il->next) */
+  } /* }}} for (il = interface_list_head; il != NULL; il = il->next) */
 
   return (0);
 } /* }}} int connectivity_read */
 
 static int connectivity_shutdown(void) /* {{{ */
 {
-  interfacelist_t *il;
+  interface_list_t *il;
 
   DEBUG("connectivity plugin: Shutting down thread.");
   if (stop_thread(1) < 0)
     return (-1);
 
-  il = interfacelist_head;
+  il = interface_list_head;
   while (il != NULL) {
-    interfacelist_t *il_next;
+    interface_list_t *il_next;
 
     il_next = il->next;
 
@@ -819,6 +839,8 @@ static int connectivity_shutdown(void) /* {{{ */
     il = il_next;
   }
 
+  ignorelist_free(ignorelist);
+
   return (0);
 } /* }}} int connectivity_shutdown */