Merge branch 'sh/collectd-4.5' into sh/collectd-4.6
[collectd.git] / src / snmp.c
index 8673df1..352075f 100644 (file)
@@ -22,6 +22,7 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
+#include "utils_complain.h"
 
 #include <pthread.h>
 
@@ -67,6 +68,7 @@ struct host_definition_s
   char *community;
   int version;
   void *sess_handle;
+  c_complain_t complaint;
   uint32_t interval;
   time_t next_update;
   data_definition_t **data_list;
@@ -116,6 +118,7 @@ static pthread_cond_t  host_cond = PTHREAD_COND_INITIALIZER;
 /*
  * Private functions
  */
+/* Many functions to handle the configuration. {{{ */
 /* First there are many functions which do configuration stuff. It's a big
  * bloated and messy, I'm afraid. */
 
@@ -198,7 +201,8 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
   else
   {
     /* Instance is a simple string */
-    strncpy (dd->instance.string, ci->values[0].value.string, DATA_MAX_NAME_LEN - 1);
+    sstrncpy (dd->instance.string, ci->values[0].value.string,
+       sizeof (dd->instance.string));
   }
 
   return (0);
@@ -526,9 +530,9 @@ static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t
     return (-1);
   }
 
-  hd->interval = (int) ci->values[0].value.number;
-  if (hd->interval < 0)
-    hd->interval = 0;
+  hd->interval = ci->values[0].value.number >= 0
+    ? (uint32_t) ci->values[0].value.number
+    : 0;
 
   return (0);
 } /* int csnmp_config_add_host_interval */
@@ -550,6 +554,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
     return (-1);
   memset (hd, '\0', sizeof (host_definition_t));
   hd->version = 2;
+  C_COMPLAIN_INIT (&hd->complaint);
 
   hd->name = strdup (ci->values[0].value.string);
   if (hd->name == NULL)
@@ -652,28 +657,14 @@ static int csnmp_config (oconfig_item_t *ci)
   return (0);
 } /* int csnmp_config */
 
-/* End of the config stuff. Now the interesting part begins */
+/* }}} End of the config stuff. Now the interesting part begins */
 
 static void csnmp_host_close_session (host_definition_t *host)
 {
-  int status;
-
   if (host->sess_handle == NULL)
     return;
 
-  status = snmp_sess_close (host->sess_handle);
-
-  if (status != 0)
-  {
-    char *errstr = NULL;
-
-    snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
-
-    ERROR ("snmp plugin: host %s: snmp_sess_close failed: %s",
-       host->name, (errstr == NULL) ? "Unknown problem" : errstr);
-    sfree (errstr);
-  }
-
+  snmp_sess_close (host->sess_handle);
   host->sess_handle = NULL;
 } /* void csnmp_host_close_session */
 
@@ -705,6 +696,7 @@ static void csnmp_host_open_session (host_definition_t *host)
   }
 } /* void csnmp_host_open_session */
 
+/* TODO: Check if negative values wrap around. Problem: negative temperatures. */
 static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
     double scale, double shift)
 {
@@ -721,14 +713,18 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
       || (vl->type == ASN_GAUGE))
   {
     temp = (uint32_t) *vl->val.integer;
-    DEBUG ("snmp plugin: Parsed int32 value is %llu.", temp);
+    DEBUG ("snmp plugin: Parsed int32 value is %"PRIu64".", temp);
   }
   else if (vl->type == ASN_COUNTER64)
   {
     temp = (uint32_t) vl->val.counter64->high;
     temp = temp << 32;
     temp += (uint32_t) vl->val.counter64->low;
-    DEBUG ("snmp plugin: Parsed int64 value is %llu.", temp);
+    DEBUG ("snmp plugin: Parsed int64 value is %"PRIu64".", temp);
+  }
+  else if (vl->type == ASN_OCTET_STR)
+  {
+    /* We'll handle this later.. */
   }
   else
   {
@@ -736,7 +732,51 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
     defined = 0;
   }
 
-  if (type == DS_TYPE_COUNTER)
+  if (vl->type == ASN_OCTET_STR)
+  {
+    char *endptr;
+
+    endptr = NULL;
+    if (vl->val.string != NULL)
+    {
+      char string[64];
+      size_t string_length;
+
+      string_length = sizeof (string) - 1;
+      if (vl->val_len < string_length)
+       string_length = vl->val_len;
+
+      /* The strings we get from the Net-SNMP library may not be null
+       * terminated. That is why we're using `membpy' here and not `strcpy'.
+       * `string_length' is set to `vl->val_len' which holds the length of the
+       * string.  -octo */
+      memcpy (string, vl->val.string, string_length);
+      string[string_length] = 0;
+
+      if (type == DS_TYPE_COUNTER)
+      {
+       ret.counter = (counter_t) strtoll (string, &endptr, /* base = */ 0);
+       DEBUG ("snmp plugin: csnmp_value_list_to_value: String to counter: %s -> %llu",
+           string, (unsigned long long) ret.counter);
+      }
+      else if (type == DS_TYPE_GAUGE)
+      {
+       ret.gauge = (gauge_t) strtod (string, &endptr);
+       DEBUG ("snmp plugin: csnmp_value_list_to_value: String to gauge: %s -> %g",
+           string, (double) ret.gauge);
+      }
+    }
+
+    /* Check if an error occurred */
+    if ((vl->val.string == NULL) || (endptr == (char *) vl->val.string))
+    {
+      if (type == DS_TYPE_COUNTER)
+       ret.counter = 0;
+      else if (type == DS_TYPE_GAUGE)
+       ret.gauge = NAN;
+    }
+  }
+  else if (type == DS_TYPE_COUNTER)
   {
     ret.counter = temp;
   }
@@ -793,7 +833,7 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
     if (vb == NULL)
     {
       ERROR ("snmp plugin: host %s: Expected one more variable for "
-         "the instance..");
+         "the instance..", host->name);
       return (-1);
     }
 
@@ -843,15 +883,15 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     char *ptr;
     size_t instance_len;
 
+    memset (il->instance, 0, sizeof (il->instance));
     instance_len = sizeof (il->instance) - 1;
     if (instance_len > vb->val_len)
       instance_len = vb->val_len;
 
-    strncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR)
+    sstrncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR)
          ? vb->val.string
          : vb->val.bitstring),
-       instance_len);
-    il->instance[instance_len] = '\0';
+       instance_len + 1);
 
     for (ptr = il->instance; *ptr != '\0'; ptr++)
     {
@@ -865,10 +905,9 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   else
   {
     value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER, 1.0, 0.0);
-    snprintf (il->instance, sizeof (il->instance),
+    ssnprintf (il->instance, sizeof (il->instance),
        "%llu", val.counter);
   }
-  il->instance[sizeof (il->instance) - 1] = '\0';
 
   /* TODO: Debugging output */
 
@@ -921,12 +960,10 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
     return (-1);
   }
 
-  strncpy (vl.host, host->name, sizeof (vl.host));
-  vl.host[sizeof (vl.host) - 1] = '\0';
-  strcpy (vl.plugin, "snmp");
+  sstrncpy (vl.host, host->name, sizeof (vl.host));
+  sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin));
 
   vl.interval = host->interval;
-  vl.time = time (NULL);
 
   subid = 0;
   have_more = 1;
@@ -986,30 +1023,28 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
        || (instance_list_ptr->subid == value_table_ptr[0]->subid));
 #endif
 
+    sstrncpy (vl.type, data->type, sizeof (vl.type));
+
     {
       char temp[DATA_MAX_NAME_LEN];
 
       if (instance_list_ptr == NULL)
-       snprintf (temp, sizeof (temp), "%u",
-           (uint32_t) subid);
+       ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid);
       else
-       strncpy (temp, instance_list_ptr->instance,
-           sizeof (temp));
-      temp[sizeof (temp) - 1] = '\0';
+       sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
 
       if (data->instance_prefix == NULL)
-       strncpy (vl.type_instance, temp, sizeof (vl.type_instance));
+       sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
       else
-       snprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
+       ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
            data->instance_prefix, temp);
-      vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
     }
 
     for (i = 0; i < data->values_len; i++)
       vl.values[i] = value_table_ptr[i]->value;
 
     /* If we get here `vl.type_instance' and all `vl.values' have been set */
-    plugin_dispatch_values (data->type, &vl);
+    plugin_dispatch_values (&vl);
 
     subid++;
   } /* while (have_more != 0) */
@@ -1104,18 +1139,27 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    for (i = 0; i < oid_list_len; i++)
+    for (i = 0; (uint32_t) i < oid_list_len; i++)
       snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len);
 
+    res = NULL;
     status = snmp_sess_synch_response (host->sess_handle, req, &res);
 
-    if (status != STAT_SUCCESS)
+    if ((status != STAT_SUCCESS) || (res == NULL))
     {
       char *errstr = NULL;
 
       snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
-      ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
+
+      c_complain (LOG_ERR, &host->complaint,
+         "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
          host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+
+      if (res != NULL)
+       snmp_free_pdu (res);
+      res = NULL;
+
+      sfree (errstr);
       csnmp_host_close_session (host);
 
       status = -1;
@@ -1123,6 +1167,9 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     }
     status = 0;
     assert (res != NULL);
+    c_release (LOG_INFO, &host->complaint,
+       "snmp plugin: host %s: snmp_sess_synch_response successful.",
+       host->name);
 
     vb = res->variables;
     if (vb == NULL)
@@ -1134,7 +1181,10 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     /* Check if all values (and possibly the instance) have left their
      * subtree */
     if (csnmp_check_res_left_subtree (host, data, res) != 0)
+    {
+      status = 0;
       break;
+    }
 
     /* if an instance-OID is configured.. */
     if (data->instance.oid.oid_len > 0)
@@ -1154,11 +1204,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
          (vb != NULL) && (vb->next_variable != NULL);
          vb = vb->next_variable)
        /* do nothing */;
-      if (vb == NULL)
-      {
-       status = -1;
-       break;
-      }
+      assert (vb != NULL);
 
       /* Copy OID to oid_list[data->values_len] */
       memcpy (oid_list[data->values_len].oid, vb->name,
@@ -1221,6 +1267,10 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     res = NULL;
   } /* while (status == 0) */
 
+  if (res != NULL)
+    snmp_free_pdu (res);
+  res = NULL;
+
   if (status == 0)
     csnmp_dispatch_table (host, data, instance_list, value_table);
 
@@ -1296,11 +1346,10 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
       vl.values[i].gauge = NAN;
   }
 
-  strncpy (vl.host, host->name, sizeof (vl.host));
-  vl.host[sizeof (vl.host) - 1] = '\0';
-  strcpy (vl.plugin, "snmp");
-  strncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance));
-  vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+  sstrncpy (vl.host, host->name, sizeof (vl.host));
+  sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin));
+  sstrncpy (vl.type, data->type, sizeof (vl.type));
+  sstrncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance));
 
   vl.interval = host->interval;
 
@@ -1314,22 +1363,28 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 
   for (i = 0; i < data->values_len; i++)
     snmp_add_null_var (req, data->values[i].oid, data->values[i].oid_len);
+
+  res = NULL;
   status = snmp_sess_synch_response (host->sess_handle, req, &res);
 
-  if (status != STAT_SUCCESS)
+  if ((status != STAT_SUCCESS) || (res == NULL))
   {
     char *errstr = NULL;
 
     snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
     ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
        host->name, (errstr == NULL) ? "Unknown problem" : errstr);
-    csnmp_host_close_session (host);
+
+    if (res != NULL)
+      snmp_free_pdu (res);
+    res = NULL;
+
     sfree (errstr);
+    csnmp_host_close_session (host);
 
     return (-1);
   }
 
-  vl.time = time (NULL);
 
   for (vb = res->variables; vb != NULL; vb = vb->next_variable)
   {
@@ -1347,10 +1402,12 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
            data->scale, data->shift);
   } /* for (res->variables) */
 
-  snmp_free_pdu (res);
+  if (res != NULL)
+    snmp_free_pdu (res);
+  res = NULL;
 
-  DEBUG ("snmp plugin: -> plugin_dispatch_values (%s, &vl);", data->type);
-  plugin_dispatch_values (data->type, &vl);
+  DEBUG ("snmp plugin: -> plugin_dispatch_values (&vl);");
+  plugin_dispatch_values (&vl);
   sfree (vl.values);
 
   return (0);
@@ -1385,17 +1442,17 @@ static int csnmp_read_host (host_definition_t *host)
   time_end = time (NULL);
   DEBUG ("snmp plugin: csnmp_read_host (%s) finished at %u;", host->name,
       (unsigned int) time_end);
-  if ((time_end - time_start) > host->interval)
+  if ((uint32_t) (time_end - time_start) > host->interval)
   {
     WARNING ("snmp plugin: Host `%s' should be queried every %i seconds, "
-       "but reading all values takes %i seconds.",
-       host->name, host->interval, time_end - time_start);
+       "but reading all values takes %u seconds.",
+       host->name, host->interval, (unsigned int) (time_end - time_start));
   }
 
   return (0);
 } /* int csnmp_read_host */
 
-static void *csnmp_read_thread (void *data)
+static void *csnmp_read_thread (void __attribute__((unused)) *data)
 {
   host_definition_t *host;
 
@@ -1448,7 +1505,7 @@ static int csnmp_init (void)
     {
       host->interval = interval_g;
     }
-    else if (host->interval < interval_g)
+    else if (host->interval < (uint32_t) interval_g)
     {
       host->interval = interval_g;
       WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.",
@@ -1577,5 +1634,5 @@ void module_register (void)
 } /* void module_register */
 
 /*
- * vim: shiftwidth=2 softtabstop=2 tabstop=8
+ * vim: shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker
  */