Code

Unified string handling.
[collectd.git] / src / snmp.c
index 622d64885e989ef49c719c5e819e71b50a4a09f5..6f11af045a7f7627b1e7feadf40e100ebb9f76aa 100644 (file)
@@ -51,6 +51,7 @@ struct data_definition_s
   char *type; /* used to find the data_set */
   int is_table;
   instance_t instance;
+  char *instance_prefix;
   oid_t *values;
   int values_len;
   double scale;
@@ -66,16 +67,16 @@ struct host_definition_s
   char *community;
   int version;
   void *sess_handle;
-  int16_t skip_num;
-  int16_t skip_left;
+  uint32_t interval;
+  time_t next_update;
   data_definition_t **data_list;
   int data_list_len;
-  enum          /****************************************************/
-  {             /* This host..                                      */
-    STATE_IDLE, /* - just sits there until `skip_left < interval_g' */
-    STATE_WAIT, /* - waits to be queried.                           */
-    STATE_BUSY  /* - is currently being queried.                    */
-  } state;      /****************************************************/
+  enum          /******************************************************/
+  {             /* This host..                                        */
+    STATE_IDLE, /* - just sits there until `next_update < interval_g' */
+    STATE_WAIT, /* - waits to be queried.                             */
+    STATE_BUSY  /* - is currently being queried.                      */
+  } state;      /******************************************************/
   struct host_definition_s *next;
 };
 typedef struct host_definition_s host_definition_t;
@@ -126,6 +127,7 @@ static pthread_cond_t  host_cond = PTHREAD_COND_INITIALIZER;
  *  !   +-> csnmp_config_add_data_type
  *  !   +-> csnmp_config_add_data_table
  *  !   +-> csnmp_config_add_data_instance
+ *  !   +-> csnmp_config_add_data_instance_prefix
  *  !   +-> csnmp_config_add_data_values
  *  +-> csnmp_config_add_host
  *      +-> csnmp_config_add_host_address
@@ -151,9 +153,7 @@ static int csnmp_config_add_data_type (data_definition_t *dd, oconfig_item_t *ci
     return (-1);
   }
 
-  if (dd->type != NULL)
-    free (dd->type);
-
+  sfree (dd->type);
   dd->type = strdup (ci->values[0].value.string);
   if (dd->type == NULL)
     return (-1);
@@ -198,12 +198,37 @@ 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);
 } /* int csnmp_config_add_data_instance */
 
+static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
+    oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("snmp plugin: `InstancePrefix' needs exactly one string argument.");
+    return (-1);
+  }
+
+  if (!dd->is_table)
+  {
+    WARNING ("snmp plugin: data %s: InstancePrefix is ignored when `Table' "
+       "is set to `false'.", dd->name);
+    return (-1);
+  }
+
+  sfree (dd->instance_prefix);
+  dd->instance_prefix = strdup (ci->values[0].value.string);
+  if (dd->instance_prefix == NULL)
+    return (-1);
+
+  return (0);
+} /* int csnmp_config_add_data_instance_prefix */
+
 static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *ci)
 {
   int i;
@@ -221,8 +246,8 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *
       return (-1);
     }
 
-  if (dd->values != NULL)
-    free (dd->values);
+  sfree (dd->values);
+  dd->values_len = 0;
   dd->values = (oid_t *) malloc (sizeof (oid_t) * ci->values_num);
   if (dd->values == NULL)
     return (-1);
@@ -313,6 +338,8 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
       status = csnmp_config_add_data_table (dd, option);
     else if (strcasecmp ("Instance", option->key) == 0)
       status = csnmp_config_add_data_instance (dd, option);
+    else if (strcasecmp ("InstancePrefix", option->key) == 0)
+      status = csnmp_config_add_data_instance_prefix (dd, option);
     else if (strcasecmp ("Values", option->key) == 0)
       status = csnmp_config_add_data_values (dd, option);
     else if (strcasecmp ("Shift", option->key) == 0)
@@ -350,6 +377,7 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
   if (status != 0)
   {
     sfree (dd->name);
+    sfree (dd->instance_prefix);
     sfree (dd->values);
     sfree (dd);
     return (-1);
@@ -492,8 +520,6 @@ static int csnmp_config_add_host_collect (host_definition_t *host,
 
 static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t *ci)
 {
-  int interval;
-
   if ((ci->values_num != 1)
       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
   {
@@ -501,10 +527,9 @@ static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t
     return (-1);
   }
 
-  interval = (int) ci->values[0].value.number;
-  hd->skip_num = interval;
-  if (hd->skip_num < 0)
-    hd->skip_num = 0;
+  hd->interval = (int) ci->values[0].value.number;
+  if (hd->interval < 0)
+    hd->interval = 0;
 
   return (0);
 } /* int csnmp_config_add_host_interval */
@@ -535,8 +560,8 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   }
 
   hd->sess_handle = NULL;
-  hd->skip_num = 0;
-  hd->skip_left = 0;
+  hd->interval = 0;
+  hd->next_update = 0;
   hd->state = STATE_IDLE;
 
   for (i = 0; i < ci->children_num; i++)
@@ -632,24 +657,10 @@ static int csnmp_config (oconfig_item_t *ci)
 
 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 */
 
@@ -823,11 +834,10 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     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';
 
     for (ptr = il->instance; *ptr != '\0'; ptr++)
     {
@@ -841,10 +851,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 */
 
@@ -897,11 +906,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';
+  sstrncpy (vl.host, host->name, sizeof (vl.host));
   strcpy (vl.plugin, "snmp");
 
-  vl.interval = host->skip_num;
+  vl.interval = host->interval;
   vl.time = time (NULL);
 
   subid = 0;
@@ -962,19 +970,28 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
        || (instance_list_ptr->subid == value_table_ptr[0]->subid));
 #endif
 
-    if (instance_list_ptr == NULL)
-      snprintf (vl.type_instance, sizeof (vl.type_instance), "%u",
-         (uint32_t) subid);
-    else
-      strncpy (vl.type_instance, instance_list_ptr->instance,
-         sizeof (vl.type_instance));
-    vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+    sstrncpy (vl.type, data->type, sizeof (vl.type));
+
+    {
+      char temp[DATA_MAX_NAME_LEN];
+
+      if (instance_list_ptr == NULL)
+       ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid);
+      else
+       sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
+
+      if (data->instance_prefix == NULL)
+       sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
+      else
+       ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
+           data->instance_prefix, temp);
+    }
 
     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) */
@@ -1072,15 +1089,22 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     for (i = 0; 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",
          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;
@@ -1092,6 +1116,10 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     vb = res->variables;
     if (vb == NULL)
     {
+      if (res != NULL)
+       snmp_free_pdu (res);
+      res = NULL;
+
       status = -1;
       break;
     }
@@ -1099,7 +1127,13 @@ 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)
+    {
+      if (res != NULL)
+       snmp_free_pdu (res);
+      res = NULL;
+
       break;
+    }
 
     /* if an instance-OID is configured.. */
     if (data->instance.oid.oid_len > 0)
@@ -1261,13 +1295,12 @@ 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';
+  sstrncpy (vl.host, host->name, sizeof (vl.host));
   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.type, data->type, sizeof (vl.type));
+  sstrncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance));
 
-  vl.interval = host->skip_num;
+  vl.interval = host->interval;
 
   req = snmp_pdu_create (SNMP_MSG_GET);
   if (req == NULL)
@@ -1279,17 +1312,24 @@ 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);
   }
@@ -1298,10 +1338,12 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 
   for (vb = res->variables; vb != NULL; vb = vb->next_variable)
   {
+#if COLLECT_DEBUG
     char buffer[1024];
     snprint_variable (buffer, sizeof (buffer),
        vb->name, vb->name_length, vb);
     DEBUG ("snmp plugin: Got this variable: %s", buffer);
+#endif /* COLLECT_DEBUG */
 
     for (i = 0; i < data->values_len; i++)
       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
@@ -1310,10 +1352,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);
@@ -1322,8 +1366,12 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 static int csnmp_read_host (host_definition_t *host)
 {
   int i;
+  time_t time_start;
+  time_t time_end;
 
-  DEBUG ("snmp plugin: csnmp_read_host (%s);", host->name);
+  time_start = time (NULL);
+  DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name,
+      (unsigned int) time_start);
 
   if (host->sess_handle == NULL)
     csnmp_host_open_session (host);
@@ -1341,6 +1389,16 @@ static int csnmp_read_host (host_definition_t *host)
       csnmp_read_value (host, data);
   }
 
+  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)
+  {
+    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);
+  }
+
   return (0);
 } /* int csnmp_read_host */
 
@@ -1390,18 +1448,18 @@ static int csnmp_init (void)
   for (host = host_head; host != NULL; host = host->next)
   {
     threads_num++;
-    /* We need to initialize `skip_num' here, because `interval_g' isn't
+    /* We need to initialize `interval' here, because `interval_g' isn't
      * initialized during `configure'. */
-    host->skip_left = interval_g;
-    if (host->skip_num == 0)
+    host->next_update = time (NULL);
+    if (host->interval == 0)
     {
-      host->skip_num = interval_g;
+      host->interval = interval_g;
     }
-    else if (host->skip_num < interval_g)
+    else if (host->interval < interval_g)
     {
-      host->skip_num = interval_g;
+      host->interval = interval_g;
       WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.",
-         host->name, host->skip_num);
+         host->name, host->interval);
     }
 
     csnmp_host_open_session (host);
@@ -1448,13 +1506,12 @@ static int csnmp_read (void)
     if (host->state != STATE_IDLE)
       continue;
 
-    host->skip_left -= interval_g;
-    if (host->skip_left >= interval_g)
+    /* Skip this host if the next or a later iteration will be sufficient. */
+    if (host->next_update >= (now + interval_g))
       continue;
 
     host->state = STATE_WAIT;
-
-    host->skip_left = host->skip_num;
+    host->next_update = now + host->interval;
   } /* for (host) */
 
   pthread_cond_broadcast (&host_cond);