Merge branch 'collectd-4.10' into collectd-5.3
[collectd.git] / src / snmp.c
index c4d043b..7d340d1 100644 (file)
@@ -108,7 +108,7 @@ static int csnmp_read_host (user_data_t *ud);
  */
 static void csnmp_oid_init (oid_t *dst, oid const *src, size_t n)
 {
-  assert (n <= STATIC_ARRAY_LEN (dst->oid));
+  assert (n <= STATIC_ARRAY_SIZE (dst->oid));
   memcpy (dst->oid, src, sizeof (*src) * n);
   dst->oid_len = n;
 }
@@ -896,71 +896,6 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   return (ret);
 } /* value_t csnmp_value_list_to_value */
 
-/* Returns true if all OIDs have left their subtree */
-static int csnmp_check_res_left_subtree (const host_definition_t *host,
-    const data_definition_t *data,
-    struct snmp_pdu *res)
-{
-  struct variable_list *vb;
-  int num_checked;
-  int num_left_subtree;
-  int i;
-
-  vb = res->variables;
-  if (vb == NULL)
-    return (-1);
-
-  num_checked = 0;
-  num_left_subtree = 0;
-
-  /* check all the variables and count how many have left their subtree */
-  for (vb = res->variables, i = 0;
-      (vb != NULL) && (i < data->values_len);
-      vb = vb->next_variable, i++)
-  {
-    num_checked++;
-
-    if ((vb->type == SNMP_ENDOFMIBVIEW)
-        || (snmp_oid_ncompare (data->values[i].oid,
-            data->values[i].oid_len,
-            vb->name, vb->name_length,
-            data->values[i].oid_len) != 0))
-      num_left_subtree++;
-  }
-
-  /* check if enough variables have been returned */
-  if (i < data->values_len)
-  {
-    ERROR ("snmp plugin: host %s: Expected %i variables, but got only %i",
-        host->name, data->values_len, i);
-    return (-1);
-  }
-
-  if (data->instance.oid.oid_len > 0)
-  {
-    if (vb == NULL)
-    {
-      ERROR ("snmp plugin: host %s: Expected one more variable for "
-          "the instance..", host->name);
-      return (-1);
-    }
-
-    num_checked++;
-    if (snmp_oid_ncompare (data->instance.oid.oid,
-          data->instance.oid.oid_len,
-          vb->name, vb->name_length,
-          data->instance.oid.oid_len) != 0)
-      num_left_subtree++;
-  }
-
-  DEBUG ("snmp plugin: csnmp_check_res_left_subtree: %i of %i variables have "
-      "left their subtree",
-      num_left_subtree, num_checked);
-  if (num_left_subtree >= num_checked)
-    return (1);
-  return (0);
-} /* int csnmp_check_res_left_subtree */
-
 static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */
     const struct variable_list *vb, size_t dst_size)
 {
@@ -1264,11 +1199,18 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   struct variable_list *vb;
 
   const data_set_t *ds;
-  oid_t *oid_list;
-  uint32_t oid_list_len;
+
+  uint32_t oid_list_len = (uint32_t) (data->values_len + 1);
+  /* Holds the last OID returned by the device. We use this in the GETNEXT
+   * request to proceed. */
+  oid_t oid_list[oid_list_len];
+  /* Set to false when an OID has left its subtree so we don't re-request it
+   * again. */
+  _Bool oid_list_todo[oid_list_len];
 
   int status;
   int i;
+  uint32_t j;
 
   /* `value_list_head' and `value_list_tail' implement a linked list for each
    * value. `instance_list_head' and `instance_list_tail' implement a linked list of
@@ -1302,19 +1244,15 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   }
 
   /* We need a copy of all the OIDs, because GETNEXT will destroy them. */
-  oid_list_len = data->values_len + 1;
-  oid_list = (oid_t *) malloc (sizeof (oid_t) * (oid_list_len));
-  if (oid_list == NULL)
-  {
-    ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
-    return (-1);
-  }
   memcpy (oid_list, data->values, data->values_len * sizeof (oid_t));
   if (data->instance.oid.oid_len > 0)
     memcpy (oid_list + data->values_len, &data->instance.oid, sizeof (oid_t));
-  else
+  else /* no InstanceFrom option specified. */
     oid_list_len--;
 
+  for (j = 0; j < oid_list_len; j++)
+    oid_list_todo[j] = 1;
+
   /* We're going to construct n linked lists, one for each "value".
    * value_list_head will contain pointers to the heads of these linked lists,
    * value_list_tail will contain pointers to the tail of the lists. */
@@ -1323,7 +1261,6 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   if ((value_list_head == NULL) || (value_list_tail == NULL))
   {
     ERROR ("snmp plugin: csnmp_read_table: calloc failed.");
-    sfree (oid_list);
     sfree (value_list_head);
     sfree (value_list_tail);
     return (-1);
@@ -1335,6 +1272,8 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   status = 0;
   while (status == 0)
   {
+    int oid_list_todo_num;
+
     req = snmp_pdu_create (SNMP_MSG_GETNEXT);
     if (req == NULL)
     {
@@ -1343,12 +1282,26 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    for (i = 0; (uint32_t) i < oid_list_len; i++)
-      snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len);
+    oid_list_todo_num = 0;
+    for (j = 0; j < oid_list_len; j++)
+    {
+      /* Do not rerequest already finished OIDs */
+      if (!oid_list_todo[j])
+        continue;
+      oid_list_todo_num++;
+      snmp_add_null_var (req, oid_list[j].oid, oid_list[j].oid_len);
+    }
+
+    if (oid_list_todo_num == 0)
+    {
+      /* The request is still empty - so we are finished */
+      DEBUG ("snmp plugin: all variables have left their subtree");
+      status = 0;
+      break;
+    }
 
     res = NULL;
     status = snmp_sess_synch_response (host->sess_handle, req, &res);
-
     if ((status != STAT_SUCCESS) || (res == NULL))
     {
       char *errstr = NULL;
@@ -1363,12 +1316,15 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
         snmp_free_pdu (res);
       res = NULL;
 
+      /* snmp_synch_response already freed our PDU */
+      req = NULL;
       sfree (errstr);
       csnmp_host_close_session (host);
 
       status = -1;
       break;
     }
+
     status = 0;
     assert (res != NULL);
     c_release (LOG_INFO, &host->complaint,
@@ -1382,103 +1338,97 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    /* 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;
-    }
-
-    /* Copy the OID of the value used as instance to oid_list, if an instance
-     * is configured. */
-    if (data->instance.oid.oid_len > 0)
-    {
-      /* Allocate a new `csnmp_list_instances_t', insert the instance name and
-       * add it to the list */
-      if (csnmp_instance_list_add (&instance_list_head, &instance_list_tail,
-           res, host, data) != 0)
-      {
-        ERROR ("snmp plugin: csnmp_instance_list_add failed.");
-        status = -1;
-        break;
-      }
-
-      /* The instance OID is added to the list of OIDs to GET from the
-       * snmp agent last, so set vb on the last variable returned and copy
-       * that OID. */
-      for (vb = res->variables;
-          (vb != NULL) && (vb->next_variable != NULL);
-          vb = vb->next_variable)
-        /* do nothing */;
-      assert (vb != NULL);
-
-      /* Copy the OID of the instance value to oid_list[data->values_len].
-       * "oid_list" is used for the next GETNEXT request. */
-      memcpy (oid_list[data->values_len].oid, vb->name,
-          sizeof (oid) * vb->name_length);
-      oid_list[data->values_len].oid_len = vb->name_length;
-    }
-
-    /* Iterate over all the (non-instance) values returned by the agent. The
-     * (i < value_len) check will make sure we're not handling the instance OID
-     * twice. */
-    for (vb = res->variables, i = 0;
-        (vb != NULL) && (i < data->values_len);
-        vb = vb->next_variable, i++)
+    for (vb = res->variables, i = 0; (vb != NULL); vb = vb->next_variable, i++)
     {
-      csnmp_table_values_t *vt;
-      oid_t vb_name;
-      oid_t suffix;
-
-      csnmp_oid_init (&vb_name, vb->name, vb->name_length);
+      /* Calculate value index from todo list */
+      while (!oid_list_todo[i] && (i < oid_list_len))
+        i++;
 
-      /* Calculate the current suffix. This is later used to check that the
-       * suffix is increasing. This also checks if we left the subtree */
-      status = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
-      if (status != 0)
+      /* An instance is configured and the res variable we process is the
+       * instance value (last index) */
+      if ((data->instance.oid.oid_len > 0) && (i == data->values_len))
       {
-        DEBUG ("snmp plugin: host = %s; data = %s; Value %i failed. "
-            "It probably left its subtree.",
-            host->name, data->name, i);
-        continue;
+        if ((vb->type == SNMP_ENDOFMIBVIEW)
+            || (snmp_oid_ncompare (data->instance.oid.oid,
+                data->instance.oid.oid_len,
+                vb->name, vb->name_length,
+                data->instance.oid.oid_len) != 0))
+        {
+          DEBUG ("snmp plugin: host = %s; data = %s; Instance left its subtree.",
+              host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_list_instances_t', insert the instance name and
+         * add it to the list */
+        if (csnmp_instance_list_add (&instance_list_head, &instance_list_tail,
+              res, host, data) != 0)
+        {
+          ERROR ("snmp plugin: csnmp_instance_list_add failed.");
+          status = -1;
+          break;
+        }
       }
-
-      /* Make sure the OIDs returned by the agent are increasing. Otherwise our
-       * table matching algorithm will get confused. */
-      if ((value_list_tail[i] != NULL)
-          && (csnmp_oid_compare (&suffix, &value_list_tail[i]->suffix) <= 0))
+      else /* The variable we are processing is a normal value */
       {
-        DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
-            "Suffix is not increasing.",
-            host->name, data->name, i);
-        continue;
-      }
+        csnmp_table_values_t *vt;
+        oid_t vb_name;
+        oid_t suffix;
+        int ret;
+
+        csnmp_oid_init (&vb_name, vb->name, vb->name_length);
+
+        /* Calculate the current suffix. This is later used to check that the
+         * suffix is increasing. This also checks if we left the subtree */
+        ret = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
+        if (ret != 0)
+        {
+          DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
+              "Value probably left its subtree.",
+              host->name, data->name, i);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Make sure the OIDs returned by the agent are increasing. Otherwise our
+         * table matching algorithm will get confused. */
+        if ((value_list_tail[i] != NULL)
+            && (csnmp_oid_compare (&suffix, &value_list_tail[i]->suffix) <= 0))
+        {
+          DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
+              "Suffix is not increasing.",
+              host->name, data->name, i);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        vt = malloc (sizeof (*vt));
+        if (vt == NULL)
+        {
+          ERROR ("snmp plugin: malloc failed.");
+          status = -1;
+          break;
+        }
+        memset (vt, 0, sizeof (*vt));
 
-      vt = malloc (sizeof (*vt));
-      if (vt == NULL)
-      {
-        ERROR ("snmp plugin: malloc failed.");
-        status = -1;
-        break;
+        vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
+            data->scale, data->shift, host->name, data->name);
+        memcpy (&vt->suffix, &suffix, sizeof (vt->suffix));
+        vt->next = NULL;
+
+        if (value_list_tail[i] == NULL)
+          value_list_head[i] = vt;
+        else
+          value_list_tail[i]->next = vt;
+        value_list_tail[i] = vt;
       }
-      memset (vt, 0, sizeof (*vt));
-
-      vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
-          data->scale, data->shift, host->name, data->name);
-      memcpy (&vt->suffix, &suffix, sizeof (vt->suffix));
-      vt->next = NULL;
 
-      if (value_list_tail[i] == NULL)
-        value_list_head[i] = vt;
-      else
-        value_list_tail[i]->next = vt;
-      value_list_tail[i] = vt;
-
-      /* Copy OID to oid_list[i + 1] */
+      /* Copy OID to oid_list[i] */
       memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length);
       oid_list[i].oid_len = vb->name_length;
-    } /* for (i = data->values_len) */
+
+    } /* for (vb = res->variables ...) */
 
     if (res != NULL)
       snmp_free_pdu (res);
@@ -1489,6 +1439,10 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     snmp_free_pdu (res);
   res = NULL;
 
+  if (req != NULL)
+    snmp_free_pdu (req);
+  req = NULL;
+
   if (status == 0)
     csnmp_dispatch_table (host, data, instance_list_head, value_list_head);
 
@@ -1512,7 +1466,6 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
 
   sfree (value_list_head);
   sfree (value_list_tail);
-  sfree (oid_list);
 
   return (0);
 } /* int csnmp_read_table */
@@ -1671,10 +1624,10 @@ static int csnmp_read_host (user_data_t *ud)
   if ((time_end - time_start) > host->interval)
   {
     WARNING ("snmp plugin: Host `%s' should be queried every %.3f "
-       "seconds, but reading all values takes %.3f seconds.",
-       host->name,
-       CDTIME_T_TO_DOUBLE (host->interval),
-       CDTIME_T_TO_DOUBLE (time_end - time_start));
+        "seconds, but reading all values takes %.3f seconds.",
+        host->name,
+        CDTIME_T_TO_DOUBLE (host->interval),
+        CDTIME_T_TO_DOUBLE (time_end - time_start));
   }
 
   if (success == 0)