write_redis: remove unused variable from wr_write()
[collectd.git] / src / snmp.c
1 /**
2  * collectd - src/snmp.c
3  * Copyright (C) 2007-2012  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at collectd.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "utils_complain.h"
26
27 #include <pthread.h>
28
29 #include <net-snmp/net-snmp-config.h>
30 #include <net-snmp/net-snmp-includes.h>
31
32 /*
33  * Private data structes
34  */
35 struct oid_s
36 {
37   oid oid[MAX_OID_LEN];
38   size_t oid_len;
39 };
40 typedef struct oid_s oid_t;
41
42 union instance_u
43 {
44   char  string[DATA_MAX_NAME_LEN];
45   oid_t oid;
46 };
47 typedef union instance_u instance_t;
48
49 struct data_definition_s
50 {
51   char *name; /* used to reference this from the `Collect' option */
52   char *type; /* used to find the data_set */
53   int is_table;
54   instance_t instance;
55   char *instance_prefix;
56   oid_t *values;
57   int values_len;
58   double scale;
59   double shift;
60   struct data_definition_s *next;
61 };
62 typedef struct data_definition_s data_definition_t;
63
64 struct host_definition_s
65 {
66   char *name;
67   char *address;
68   char *community;
69   int version;
70   void *sess_handle;
71   c_complain_t complaint;
72   cdtime_t interval;
73   data_definition_t **data_list;
74   int data_list_len;
75 };
76 typedef struct host_definition_s host_definition_t;
77
78 /* These two types are used to cache values in `csnmp_read_table' to handle
79  * gaps in tables. */
80 struct csnmp_list_instances_s
81 {
82   oid_t suffix;
83   char instance[DATA_MAX_NAME_LEN];
84   struct csnmp_list_instances_s *next;
85 };
86 typedef struct csnmp_list_instances_s csnmp_list_instances_t;
87
88 struct csnmp_table_values_s
89 {
90   oid_t suffix;
91   value_t value;
92   struct csnmp_table_values_s *next;
93 };
94 typedef struct csnmp_table_values_s csnmp_table_values_t;
95
96 /*
97  * Private variables
98  */
99 static data_definition_t *data_head = NULL;
100
101 /*
102  * Prototypes
103  */
104 static int csnmp_read_host (user_data_t *ud);
105
106 /*
107  * Private functions
108  */
109 static void csnmp_oid_init (oid_t *dst, oid const *src, size_t n)
110 {
111   assert (n <= STATIC_ARRAY_SIZE (dst->oid));
112   memcpy (dst->oid, src, sizeof (*src) * n);
113   dst->oid_len = n;
114 }
115
116 static int csnmp_oid_compare (oid_t const *left, oid_t const *right)
117 {
118   return (snmp_oid_compare (left->oid, left->oid_len,
119         right->oid, right->oid_len));
120 }
121
122 static int csnmp_oid_suffix (oid_t *dst, oid_t const *src,
123     oid_t const *root)
124 {
125   /* Make sure "src" is in "root"s subtree. */
126   if (src->oid_len <= root->oid_len)
127     return (EINVAL);
128   if (snmp_oid_ncompare (root->oid, root->oid_len,
129         src->oid, src->oid_len,
130         /* n = */ root->oid_len) != 0)
131     return (EINVAL);
132
133   memset (dst, 0, sizeof (*dst));
134   dst->oid_len = src->oid_len - root->oid_len;
135   memcpy (dst->oid, &src->oid[root->oid_len],
136       dst->oid_len * sizeof (dst->oid[0]));
137   return (0);
138 }
139
140 static int csnmp_oid_to_string (char *buffer, size_t buffer_size,
141     oid_t const *o)
142 {
143   char oid_str[MAX_OID_LEN][16];
144   char *oid_str_ptr[MAX_OID_LEN];
145   size_t i;
146
147   for (i = 0; i < o->oid_len; i++)
148   {
149     ssnprintf (oid_str[i], sizeof (oid_str[i]), "%lu", (unsigned long) o->oid[i]);
150     oid_str_ptr[i] = oid_str[i];
151   }
152
153   return (strjoin (buffer, buffer_size,
154         oid_str_ptr, o->oid_len, /* separator = */ "."));
155 }
156
157 static void csnmp_host_close_session (host_definition_t *host) /* {{{ */
158 {
159   if (host->sess_handle == NULL)
160     return;
161
162   snmp_sess_close (host->sess_handle);
163   host->sess_handle = NULL;
164 } /* }}} void csnmp_host_close_session */
165
166 static void csnmp_host_definition_destroy (void *arg) /* {{{ */
167 {
168   host_definition_t *hd;
169
170   hd = arg;
171
172   if (hd == NULL)
173     return;
174
175   if (hd->name != NULL)
176   {
177     DEBUG ("snmp plugin: Destroying host definition for host `%s'.",
178         hd->name);
179   }
180
181   csnmp_host_close_session (hd);
182
183   sfree (hd->name);
184   sfree (hd->address);
185   sfree (hd->community);
186   sfree (hd->data_list);
187
188   sfree (hd);
189 } /* }}} void csnmp_host_definition_destroy */
190
191 /* Many functions to handle the configuration. {{{ */
192 /* First there are many functions which do configuration stuff. It's a big
193  * bloated and messy, I'm afraid. */
194
195 /*
196  * Callgraph for the config stuff:
197  *  csnmp_config
198  *  +-> call_snmp_init_once
199  *  +-> csnmp_config_add_data
200  *  !   +-> csnmp_config_add_data_type
201  *  !   +-> csnmp_config_add_data_table
202  *  !   +-> csnmp_config_add_data_instance
203  *  !   +-> csnmp_config_add_data_instance_prefix
204  *  !   +-> csnmp_config_add_data_values
205  *  +-> csnmp_config_add_host
206  *      +-> csnmp_config_add_host_address
207  *      +-> csnmp_config_add_host_community
208  *      +-> csnmp_config_add_host_version
209  *      +-> csnmp_config_add_host_collect
210  */
211 static void call_snmp_init_once (void)
212 {
213   static int have_init = 0;
214
215   if (have_init == 0)
216     init_snmp (PACKAGE_NAME);
217   have_init = 1;
218 } /* void call_snmp_init_once */
219
220 static int csnmp_config_add_data_type (data_definition_t *dd, oconfig_item_t *ci)
221 {
222   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
223   {
224     WARNING ("snmp plugin: `Type' needs exactly one string argument.");
225     return (-1);
226   }
227
228   sfree (dd->type);
229   dd->type = strdup (ci->values[0].value.string);
230   if (dd->type == NULL)
231     return (-1);
232
233   return (0);
234 } /* int csnmp_config_add_data_type */
235
236 static int csnmp_config_add_data_table (data_definition_t *dd, oconfig_item_t *ci)
237 {
238   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
239   {
240     WARNING ("snmp plugin: `Table' needs exactly one boolean argument.");
241     return (-1);
242   }
243
244   dd->is_table = ci->values[0].value.boolean ? 1 : 0;
245
246   return (0);
247 } /* int csnmp_config_add_data_table */
248
249 static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t *ci)
250 {
251   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
252   {
253     WARNING ("snmp plugin: `Instance' needs exactly one string argument.");
254     return (-1);
255   }
256
257   if (dd->is_table)
258   {
259     /* Instance is an OID */
260     dd->instance.oid.oid_len = MAX_OID_LEN;
261
262     if (!read_objid (ci->values[0].value.string,
263           dd->instance.oid.oid, &dd->instance.oid.oid_len))
264     {
265       ERROR ("snmp plugin: read_objid (%s) failed.",
266           ci->values[0].value.string);
267       return (-1);
268     }
269   }
270   else
271   {
272     /* Instance is a simple string */
273     sstrncpy (dd->instance.string, ci->values[0].value.string,
274         sizeof (dd->instance.string));
275   }
276
277   return (0);
278 } /* int csnmp_config_add_data_instance */
279
280 static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
281     oconfig_item_t *ci)
282 {
283   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
284   {
285     WARNING ("snmp plugin: `InstancePrefix' needs exactly one string argument.");
286     return (-1);
287   }
288
289   if (!dd->is_table)
290   {
291     WARNING ("snmp plugin: data %s: InstancePrefix is ignored when `Table' "
292         "is set to `false'.", dd->name);
293     return (-1);
294   }
295
296   sfree (dd->instance_prefix);
297   dd->instance_prefix = strdup (ci->values[0].value.string);
298   if (dd->instance_prefix == NULL)
299     return (-1);
300
301   return (0);
302 } /* int csnmp_config_add_data_instance_prefix */
303
304 static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *ci)
305 {
306   int i;
307
308   if (ci->values_num < 1)
309   {
310     WARNING ("snmp plugin: `Values' needs at least one argument.");
311     return (-1);
312   }
313
314   for (i = 0; i < ci->values_num; i++)
315     if (ci->values[i].type != OCONFIG_TYPE_STRING)
316     {
317       WARNING ("snmp plugin: `Values' needs only string argument.");
318       return (-1);
319     }
320
321   sfree (dd->values);
322   dd->values_len = 0;
323   dd->values = (oid_t *) malloc (sizeof (oid_t) * ci->values_num);
324   if (dd->values == NULL)
325     return (-1);
326   dd->values_len = ci->values_num;
327
328   for (i = 0; i < ci->values_num; i++)
329   {
330     dd->values[i].oid_len = MAX_OID_LEN;
331
332     if (NULL == snmp_parse_oid (ci->values[i].value.string,
333           dd->values[i].oid, &dd->values[i].oid_len))
334     {
335       ERROR ("snmp plugin: snmp_parse_oid (%s) failed.",
336           ci->values[i].value.string);
337       free (dd->values);
338       dd->values = NULL;
339       dd->values_len = 0;
340       return (-1);
341     }
342   }
343
344   return (0);
345 } /* int csnmp_config_add_data_instance */
346
347 static int csnmp_config_add_data_shift (data_definition_t *dd, oconfig_item_t *ci)
348 {
349   if ((ci->values_num != 1)
350       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
351   {
352     WARNING ("snmp plugin: The `Shift' config option needs exactly one number argument.");
353     return (-1);
354   }
355
356   dd->shift = ci->values[0].value.number;
357
358   return (0);
359 } /* int csnmp_config_add_data_shift */
360
361 static int csnmp_config_add_data_scale (data_definition_t *dd, oconfig_item_t *ci)
362 {
363   if ((ci->values_num != 1)
364       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
365   {
366     WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument.");
367     return (-1);
368   }
369
370   dd->scale = ci->values[0].value.number;
371
372   return (0);
373 } /* int csnmp_config_add_data_scale */
374
375 static int csnmp_config_add_data (oconfig_item_t *ci)
376 {
377   data_definition_t *dd;
378   int status = 0;
379   int i;
380
381   if ((ci->values_num != 1)
382       || (ci->values[0].type != OCONFIG_TYPE_STRING))
383   {
384     WARNING ("snmp plugin: The `Data' config option needs exactly one string argument.");
385     return (-1);
386   }
387
388   dd = (data_definition_t *) malloc (sizeof (data_definition_t));
389   if (dd == NULL)
390     return (-1);
391   memset (dd, '\0', sizeof (data_definition_t));
392
393   dd->name = strdup (ci->values[0].value.string);
394   if (dd->name == NULL)
395   {
396     free (dd);
397     return (-1);
398   }
399   dd->scale = 1.0;
400   dd->shift = 0.0;
401
402   for (i = 0; i < ci->children_num; i++)
403   {
404     oconfig_item_t *option = ci->children + i;
405
406     if (strcasecmp ("Type", option->key) == 0)
407       status = csnmp_config_add_data_type (dd, option);
408     else if (strcasecmp ("Table", option->key) == 0)
409       status = csnmp_config_add_data_table (dd, option);
410     else if (strcasecmp ("Instance", option->key) == 0)
411       status = csnmp_config_add_data_instance (dd, option);
412     else if (strcasecmp ("InstancePrefix", option->key) == 0)
413       status = csnmp_config_add_data_instance_prefix (dd, option);
414     else if (strcasecmp ("Values", option->key) == 0)
415       status = csnmp_config_add_data_values (dd, option);
416     else if (strcasecmp ("Shift", option->key) == 0)
417       status = csnmp_config_add_data_shift (dd, option);
418     else if (strcasecmp ("Scale", option->key) == 0)
419       status = csnmp_config_add_data_scale (dd, option);
420     else
421     {
422       WARNING ("snmp plugin: Option `%s' not allowed here.", option->key);
423       status = -1;
424     }
425
426     if (status != 0)
427       break;
428   } /* for (ci->children) */
429
430   while (status == 0)
431   {
432     if (dd->type == NULL)
433     {
434       WARNING ("snmp plugin: `Type' not given for data `%s'", dd->name);
435       status = -1;
436       break;
437     }
438     if (dd->values == NULL)
439     {
440       WARNING ("snmp plugin: No `Value' given for data `%s'", dd->name);
441       status = -1;
442       break;
443     }
444
445     break;
446   } /* while (status == 0) */
447
448   if (status != 0)
449   {
450     sfree (dd->name);
451     sfree (dd->instance_prefix);
452     sfree (dd->values);
453     sfree (dd);
454     return (-1);
455   }
456
457   DEBUG ("snmp plugin: dd = { name = %s, type = %s, is_table = %s, values_len = %i }",
458       dd->name, dd->type, (dd->is_table != 0) ? "true" : "false", dd->values_len);
459
460   if (data_head == NULL)
461     data_head = dd;
462   else
463   {
464     data_definition_t *last;
465     last = data_head;
466     while (last->next != NULL)
467       last = last->next;
468     last->next = dd;
469   }
470
471   return (0);
472 } /* int csnmp_config_add_data */
473
474 static int csnmp_config_add_host_address (host_definition_t *hd, oconfig_item_t *ci)
475 {
476   if ((ci->values_num != 1)
477       || (ci->values[0].type != OCONFIG_TYPE_STRING))
478   {
479     WARNING ("snmp plugin: The `Address' config option needs exactly one string argument.");
480     return (-1);
481   }
482
483   if (hd->address == NULL)
484     free (hd->address);
485
486   hd->address = strdup (ci->values[0].value.string);
487   if (hd->address == NULL)
488     return (-1);
489
490   DEBUG ("snmp plugin: host = %s; host->address = %s;",
491       hd->name, hd->address);
492
493   return (0);
494 } /* int csnmp_config_add_host_address */
495
496 static int csnmp_config_add_host_community (host_definition_t *hd, oconfig_item_t *ci)
497 {
498   if ((ci->values_num != 1)
499       || (ci->values[0].type != OCONFIG_TYPE_STRING))
500   {
501     WARNING ("snmp plugin: The `Community' config option needs exactly one string argument.");
502     return (-1);
503   }
504
505   if (hd->community == NULL)
506     free (hd->community);
507
508   hd->community = strdup (ci->values[0].value.string);
509   if (hd->community == NULL)
510     return (-1);
511
512   DEBUG ("snmp plugin: host = %s; host->community = %s;",
513       hd->name, hd->community);
514
515   return (0);
516 } /* int csnmp_config_add_host_community */
517
518 static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t *ci)
519 {
520   int version;
521
522   if ((ci->values_num != 1)
523       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
524   {
525     WARNING ("snmp plugin: The `Version' config option needs exactly one number argument.");
526     return (-1);
527   }
528
529   version = (int) ci->values[0].value.number;
530   if ((version != 1) && (version != 2))
531   {
532     WARNING ("snmp plugin: `Version' must either be `1' or `2'.");
533     return (-1);
534   }
535
536   hd->version = version;
537
538   return (0);
539 } /* int csnmp_config_add_host_address */
540
541 static int csnmp_config_add_host_collect (host_definition_t *host,
542     oconfig_item_t *ci)
543 {
544   data_definition_t *data;
545   data_definition_t **data_list;
546   int data_list_len;
547   int i;
548
549   if (ci->values_num < 1)
550   {
551     WARNING ("snmp plugin: `Collect' needs at least one argument.");
552     return (-1);
553   }
554
555   for (i = 0; i < ci->values_num; i++)
556     if (ci->values[i].type != OCONFIG_TYPE_STRING)
557     {
558       WARNING ("snmp plugin: All arguments to `Collect' must be strings.");
559       return (-1);
560     }
561
562   data_list_len = host->data_list_len + ci->values_num;
563   data_list = (data_definition_t **) realloc (host->data_list,
564       sizeof (data_definition_t *) * data_list_len);
565   if (data_list == NULL)
566     return (-1);
567   host->data_list = data_list;
568
569   for (i = 0; i < ci->values_num; i++)
570   {
571     for (data = data_head; data != NULL; data = data->next)
572       if (strcasecmp (ci->values[i].value.string, data->name) == 0)
573         break;
574
575     if (data == NULL)
576     {
577       WARNING ("snmp plugin: No such data configured: `%s'",
578           ci->values[i].value.string);
579       continue;
580     }
581
582     DEBUG ("snmp plugin: Collect: host = %s, data[%i] = %s;",
583         host->name, host->data_list_len, data->name);
584
585     host->data_list[host->data_list_len] = data;
586     host->data_list_len++;
587   } /* for (values_num) */
588
589   return (0);
590 } /* int csnmp_config_add_host_collect */
591
592 static int csnmp_config_add_host (oconfig_item_t *ci)
593 {
594   host_definition_t *hd;
595   int status = 0;
596   int i;
597
598   /* Registration stuff. */
599   char cb_name[DATA_MAX_NAME_LEN];
600   user_data_t cb_data;
601   struct timespec cb_interval;
602
603   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
604   {
605     WARNING ("snmp plugin: `Host' needs exactly one string argument.");
606     return (-1);
607   }
608
609   hd = (host_definition_t *) malloc (sizeof (host_definition_t));
610   if (hd == NULL)
611     return (-1);
612   memset (hd, '\0', sizeof (host_definition_t));
613   hd->version = 2;
614   C_COMPLAIN_INIT (&hd->complaint);
615
616   hd->name = strdup (ci->values[0].value.string);
617   if (hd->name == NULL)
618   {
619     free (hd);
620     return (-1);
621   }
622
623   hd->sess_handle = NULL;
624   hd->interval = 0;
625
626   for (i = 0; i < ci->children_num; i++)
627   {
628     oconfig_item_t *option = ci->children + i;
629     status = 0;
630
631     if (strcasecmp ("Address", option->key) == 0)
632       status = csnmp_config_add_host_address (hd, option);
633     else if (strcasecmp ("Community", option->key) == 0)
634       status = csnmp_config_add_host_community (hd, option);
635     else if (strcasecmp ("Version", option->key) == 0)
636       status = csnmp_config_add_host_version (hd, option);
637     else if (strcasecmp ("Collect", option->key) == 0)
638       csnmp_config_add_host_collect (hd, option);
639     else if (strcasecmp ("Interval", option->key) == 0)
640       cf_util_get_cdtime (option, &hd->interval);
641     else
642     {
643       WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key);
644       status = -1;
645     }
646
647     if (status != 0)
648       break;
649   } /* for (ci->children) */
650
651   while (status == 0)
652   {
653     if (hd->address == NULL)
654     {
655       WARNING ("snmp plugin: `Address' not given for host `%s'", hd->name);
656       status = -1;
657       break;
658     }
659     if (hd->community == NULL)
660     {
661       WARNING ("snmp plugin: `Community' not given for host `%s'", hd->name);
662       status = -1;
663       break;
664     }
665
666     break;
667   } /* while (status == 0) */
668
669   if (status != 0)
670   {
671     csnmp_host_definition_destroy (hd);
672     return (-1);
673   }
674
675   DEBUG ("snmp plugin: hd = { name = %s, address = %s, community = %s, version = %i }",
676       hd->name, hd->address, hd->community, hd->version);
677
678   ssnprintf (cb_name, sizeof (cb_name), "snmp-%s", hd->name);
679
680   memset (&cb_data, 0, sizeof (cb_data));
681   cb_data.data = hd;
682   cb_data.free_func = csnmp_host_definition_destroy;
683
684   CDTIME_T_TO_TIMESPEC (hd->interval, &cb_interval);
685
686   status = plugin_register_complex_read (/* group = */ NULL, cb_name,
687       csnmp_read_host, /* interval = */ &cb_interval,
688       /* user_data = */ &cb_data);
689   if (status != 0)
690   {
691     ERROR ("snmp plugin: Registering complex read function failed.");
692     csnmp_host_definition_destroy (hd);
693     return (-1);
694   }
695
696   return (0);
697 } /* int csnmp_config_add_host */
698
699 static int csnmp_config (oconfig_item_t *ci)
700 {
701   int i;
702
703   call_snmp_init_once ();
704
705   for (i = 0; i < ci->children_num; i++)
706   {
707     oconfig_item_t *child = ci->children + i;
708     if (strcasecmp ("Data", child->key) == 0)
709       csnmp_config_add_data (child);
710     else if (strcasecmp ("Host", child->key) == 0)
711       csnmp_config_add_host (child);
712     else
713     {
714       WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
715     }
716   } /* for (ci->children) */
717
718   return (0);
719 } /* int csnmp_config */
720
721 /* }}} End of the config stuff. Now the interesting part begins */
722
723 static void csnmp_host_open_session (host_definition_t *host)
724 {
725   struct snmp_session sess;
726
727   if (host->sess_handle != NULL)
728     csnmp_host_close_session (host);
729
730   snmp_sess_init (&sess);
731   sess.peername = host->address;
732   sess.community = (u_char *) host->community;
733   sess.community_len = strlen (host->community);
734   sess.version = (host->version == 1) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
735
736   /* snmp_sess_open will copy the `struct snmp_session *'. */
737   host->sess_handle = snmp_sess_open (&sess);
738
739   if (host->sess_handle == NULL)
740   {
741     char *errstr = NULL;
742
743     snmp_error (&sess, NULL, NULL, &errstr);
744
745     ERROR ("snmp plugin: host %s: snmp_sess_open failed: %s",
746         host->name, (errstr == NULL) ? "Unknown problem" : errstr);
747     sfree (errstr);
748   }
749 } /* void csnmp_host_open_session */
750
751 /* TODO: Check if negative values wrap around. Problem: negative temperatures. */
752 static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
753     double scale, double shift,
754     const char *host_name, const char *data_name)
755 {
756   value_t ret;
757   uint64_t tmp_unsigned = 0;
758   int64_t tmp_signed = 0;
759   _Bool defined = 1;
760   /* Set to true when the original SNMP type appears to have been signed. */
761   _Bool prefer_signed = 0;
762
763   if ((vl->type == ASN_INTEGER)
764       || (vl->type == ASN_UINTEGER)
765       || (vl->type == ASN_COUNTER)
766 #ifdef ASN_TIMETICKS
767       || (vl->type == ASN_TIMETICKS)
768 #endif
769       || (vl->type == ASN_GAUGE))
770   {
771     tmp_unsigned = (uint32_t) *vl->val.integer;
772     tmp_signed = (int32_t) *vl->val.integer;
773
774     if ((vl->type == ASN_INTEGER)
775         || (vl->type == ASN_GAUGE))
776       prefer_signed = 1;
777
778     DEBUG ("snmp plugin: Parsed int32 value is %"PRIu64".", tmp_unsigned);
779   }
780   else if (vl->type == ASN_COUNTER64)
781   {
782     tmp_unsigned = (uint32_t) vl->val.counter64->high;
783     tmp_unsigned = tmp_unsigned << 32;
784     tmp_unsigned += (uint32_t) vl->val.counter64->low;
785     tmp_signed = (int64_t) tmp_unsigned;
786     DEBUG ("snmp plugin: Parsed int64 value is %"PRIu64".", tmp_unsigned);
787   }
788   else if (vl->type == ASN_OCTET_STR)
789   {
790     /* We'll handle this later.. */
791   }
792   else
793   {
794     char oid_buffer[1024];
795
796     memset (oid_buffer, 0, sizeof (oid_buffer));
797     snprint_objid (oid_buffer, sizeof (oid_buffer) - 1,
798         vl->name, vl->name_length);
799
800 #ifdef ASN_NULL
801     if (vl->type == ASN_NULL)
802       INFO ("snmp plugin: OID \"%s\" is undefined (type ASN_NULL)",
803           oid_buffer);
804     else
805 #endif
806       WARNING ("snmp plugin: I don't know the ASN type #%i "
807                "(OID: \"%s\", data block \"%s\", host block \"%s\")",
808           (int) vl->type, oid_buffer,
809           (data_name != NULL) ? data_name : "UNKNOWN",
810           (host_name != NULL) ? host_name : "UNKNOWN");
811
812     defined = 0;
813   }
814
815   if (vl->type == ASN_OCTET_STR)
816   {
817     int status = -1;
818
819     if (vl->val.string != NULL)
820     {
821       char string[64];
822       size_t string_length;
823
824       string_length = sizeof (string) - 1;
825       if (vl->val_len < string_length)
826         string_length = vl->val_len;
827
828       /* The strings we get from the Net-SNMP library may not be null
829        * terminated. That is why we're using `memcpy' here and not `strcpy'.
830        * `string_length' is set to `vl->val_len' which holds the length of the
831        * string.  -octo */
832       memcpy (string, vl->val.string, string_length);
833       string[string_length] = 0;
834
835       status = parse_value (string, &ret, type);
836       if (status != 0)
837       {
838         ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
839             DS_TYPE_TO_STRING (type), string);
840       }
841     }
842
843     if (status != 0)
844     {
845       switch (type)
846       {
847         case DS_TYPE_COUNTER:
848         case DS_TYPE_DERIVE:
849         case DS_TYPE_ABSOLUTE:
850           memset (&ret, 0, sizeof (ret));
851           break;
852
853         case DS_TYPE_GAUGE:
854           ret.gauge = NAN;
855           break;
856
857         default:
858           ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown "
859               "data source type: %i.", type);
860           ret.gauge = NAN;
861       }
862     }
863   } /* if (vl->type == ASN_OCTET_STR) */
864   else if (type == DS_TYPE_COUNTER)
865   {
866     ret.counter = tmp_unsigned;
867   }
868   else if (type == DS_TYPE_GAUGE)
869   {
870     if (!defined)
871       ret.gauge = NAN;
872     else if (prefer_signed)
873       ret.gauge = (scale * tmp_signed) + shift;
874     else
875       ret.gauge = (scale * tmp_unsigned) + shift;
876   }
877   else if (type == DS_TYPE_DERIVE)
878   {
879     if (prefer_signed)
880       ret.derive = (derive_t) tmp_signed;
881     else
882       ret.derive = (derive_t) tmp_unsigned;
883   }
884   else if (type == DS_TYPE_ABSOLUTE)
885   {
886     ret.absolute = (absolute_t) tmp_unsigned;
887   }
888   else
889   {
890     ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source "
891         "type: %i.", type);
892     ret.gauge = NAN;
893   }
894
895   return (ret);
896 } /* value_t csnmp_value_list_to_value */
897
898 static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */
899     const struct variable_list *vb, size_t dst_size)
900 {
901   char *buffer_ptr;
902   size_t buffer_free;
903   size_t i;
904
905   buffer_ptr = dst;
906   buffer_free = dst_size;
907
908   for (i = 0; i < vb->val_len; i++)
909   {
910     int status;
911
912     status = snprintf (buffer_ptr, buffer_free,
913         (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]);
914
915     if (status >= buffer_free)
916     {
917       buffer_ptr += (buffer_free - 1);
918       *buffer_ptr = 0;
919       return (dst_size + (buffer_free - status));
920     }
921     else /* if (status < buffer_free) */
922     {
923       buffer_ptr += status;
924       buffer_free -= status;
925     }
926   }
927
928   return ((int) (dst_size - buffer_free));
929 } /* }}} int csnmp_strvbcopy_hexstring */
930
931 static int csnmp_strvbcopy (char *dst, /* {{{ */
932     const struct variable_list *vb, size_t dst_size)
933 {
934   char *src;
935   size_t num_chars;
936   size_t i;
937
938   if (vb->type == ASN_OCTET_STR)
939     src = (char *) vb->val.string;
940   else if (vb->type == ASN_BIT_STR)
941     src = (char *) vb->val.bitstring;
942   else
943   {
944     dst[0] = 0;
945     return (EINVAL);
946   }
947
948   num_chars = dst_size - 1;
949   if (num_chars > vb->val_len)
950     num_chars = vb->val_len;
951
952   for (i = 0; i < num_chars; i++)
953   {
954     /* Check for control characters. */
955     if ((unsigned char)src[i] < 32)
956       return (csnmp_strvbcopy_hexstring (dst, vb, dst_size));
957     dst[i] = src[i];
958   }
959   dst[num_chars] = 0;
960
961   return ((int) vb->val_len);
962 } /* }}} int csnmp_strvbcopy */
963
964 static int csnmp_instance_list_add (csnmp_list_instances_t **head,
965     csnmp_list_instances_t **tail,
966     const struct snmp_pdu *res,
967     const host_definition_t *hd, const data_definition_t *dd)
968 {
969   csnmp_list_instances_t *il;
970   struct variable_list *vb;
971   oid_t vb_name;
972   int status;
973
974   /* Set vb on the last variable */
975   for (vb = res->variables;
976       (vb != NULL) && (vb->next_variable != NULL);
977       vb = vb->next_variable)
978     /* do nothing */;
979   if (vb == NULL)
980     return (-1);
981
982   csnmp_oid_init (&vb_name, vb->name, vb->name_length);
983
984   il = malloc (sizeof (*il));
985   if (il == NULL)
986   {
987     ERROR ("snmp plugin: malloc failed.");
988     return (-1);
989   }
990   memset (il, 0, sizeof (*il));
991   il->next = NULL;
992
993   status = csnmp_oid_suffix (&il->suffix, &vb_name, &dd->instance.oid);
994   if (status != 0)
995   {
996     sfree (il);
997     return (status);
998   }
999
1000   /* Get instance name */
1001   if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
1002   {
1003     char *ptr;
1004
1005     csnmp_strvbcopy (il->instance, vb, sizeof (il->instance));
1006
1007     for (ptr = il->instance; *ptr != '\0'; ptr++)
1008     {
1009       if ((*ptr > 0) && (*ptr < 32))
1010         *ptr = ' ';
1011       else if (*ptr == '/')
1012         *ptr = '_';
1013     }
1014     DEBUG ("snmp plugin: il->instance = `%s';", il->instance);
1015   }
1016   else
1017   {
1018     value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER,
1019         /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name);
1020     ssnprintf (il->instance, sizeof (il->instance),
1021         "%llu", val.counter);
1022   }
1023
1024   /* TODO: Debugging output */
1025
1026   if (*head == NULL)
1027     *head = il;
1028   else
1029     (*tail)->next = il;
1030   *tail = il;
1031
1032   return (0);
1033 } /* int csnmp_instance_list_add */
1034
1035 static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *data,
1036     csnmp_list_instances_t *instance_list,
1037     csnmp_table_values_t **value_table)
1038 {
1039   const data_set_t *ds;
1040   value_list_t vl = VALUE_LIST_INIT;
1041
1042   csnmp_list_instances_t *instance_list_ptr;
1043   csnmp_table_values_t **value_table_ptr;
1044
1045   int i;
1046   _Bool have_more;
1047   oid_t current_suffix;
1048
1049   ds = plugin_get_ds (data->type);
1050   if (!ds)
1051   {
1052     ERROR ("snmp plugin: DataSet `%s' not defined.", data->type);
1053     return (-1);
1054   }
1055   assert (ds->ds_num == data->values_len);
1056   assert (data->values_len > 0);
1057
1058   instance_list_ptr = instance_list;
1059
1060   value_table_ptr = calloc ((size_t) data->values_len, sizeof (*value_table_ptr));
1061   if (value_table_ptr == NULL)
1062     return (-1);
1063   for (i = 0; i < data->values_len; i++)
1064     value_table_ptr[i] = value_table[i];
1065
1066   vl.values_len = data->values_len;
1067   vl.values = malloc (sizeof (*vl.values) * vl.values_len);
1068   if (vl.values == NULL)
1069   {
1070     ERROR ("snmp plugin: malloc failed.");
1071     sfree (value_table_ptr);
1072     return (-1);
1073   }
1074
1075   sstrncpy (vl.host, host->name, sizeof (vl.host));
1076   sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin));
1077
1078   vl.interval = host->interval;
1079
1080   have_more = 1;
1081   memset (&current_suffix, 0, sizeof (current_suffix));
1082   while (have_more)
1083   {
1084     _Bool suffix_skipped = 0;
1085
1086     /* Determine next suffix to handle. */
1087     if (instance_list != NULL)
1088     {
1089       if (instance_list_ptr == NULL)
1090       {
1091         have_more = 0;
1092         continue;
1093       }
1094
1095       memcpy (&current_suffix, &instance_list_ptr->suffix, sizeof (current_suffix));
1096     }
1097     else /* no instance configured */
1098     {
1099       csnmp_table_values_t *ptr = value_table_ptr[0];
1100       if (ptr == NULL)
1101       {
1102         have_more = 0;
1103         continue;
1104       }
1105
1106       memcpy (&current_suffix, &ptr->suffix, sizeof (current_suffix));
1107     }
1108
1109     /* Update all the value_table_ptr to point at the entry with the same
1110      * trailing partial OID */
1111     for (i = 0; i < data->values_len; i++)
1112     {
1113       while ((value_table_ptr[i] != NULL)
1114           && (csnmp_oid_compare (&value_table_ptr[i]->suffix, &current_suffix) < 0))
1115         value_table_ptr[i] = value_table_ptr[i]->next;
1116
1117       if (value_table_ptr[i] == NULL)
1118       {
1119         have_more = 0;
1120         break;
1121       }
1122       else if (csnmp_oid_compare (&value_table_ptr[i]->suffix, &current_suffix) > 0)
1123       {
1124         /* This suffix is missing in the subtree. Indicate this with the
1125          * "suffix_skipped" flag and try the next instance / suffix. */
1126         suffix_skipped = 1;
1127         break;
1128       }
1129     } /* for (i = 0; i < columns; i++) */
1130
1131     if (!have_more)
1132       break;
1133
1134     /* Matching the values failed. Start from the beginning again. */
1135     if (suffix_skipped)
1136     {
1137       if (instance_list != NULL)
1138         instance_list_ptr = instance_list_ptr->next;
1139       else
1140         value_table_ptr[0] = value_table_ptr[0]->next;
1141
1142       continue;
1143     }
1144
1145     /* if we reach this line, all value_table_ptr[i] are non-NULL and are set
1146      * to the same subid. instance_list_ptr is either NULL or points to the
1147      * same subid, too. */
1148 #if COLLECT_DEBUG
1149     for (i = 1; i < data->values_len; i++)
1150     {
1151       assert (value_table_ptr[i] != NULL);
1152       assert (csnmp_oid_compare (&value_table_ptr[i-1]->suffix,
1153             &value_table_ptr[i]->suffix) == 0);
1154     }
1155     assert ((instance_list_ptr == NULL)
1156         || (csnmp_oid_compare (&instance_list_ptr->suffix,
1157             &value_table_ptr[0]->suffix) == 0));
1158 #endif
1159
1160     sstrncpy (vl.type, data->type, sizeof (vl.type));
1161
1162     {
1163       char temp[DATA_MAX_NAME_LEN];
1164
1165       if (instance_list_ptr == NULL)
1166         csnmp_oid_to_string (temp, sizeof (temp), &current_suffix);
1167       else
1168         sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
1169
1170       if (data->instance_prefix == NULL)
1171         sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
1172       else
1173         ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
1174             data->instance_prefix, temp);
1175     }
1176
1177     for (i = 0; i < data->values_len; i++)
1178       vl.values[i] = value_table_ptr[i]->value;
1179
1180     /* If we get here `vl.type_instance' and all `vl.values' have been set */
1181     plugin_dispatch_values (&vl);
1182
1183     if (instance_list != NULL)
1184       instance_list_ptr = instance_list_ptr->next;
1185     else
1186       value_table_ptr[0] = value_table_ptr[0]->next;
1187   } /* while (have_more) */
1188
1189   sfree (vl.values);
1190   sfree (value_table_ptr);
1191
1192   return (0);
1193 } /* int csnmp_dispatch_table */
1194
1195 static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
1196 {
1197   struct snmp_pdu *req;
1198   struct snmp_pdu *res;
1199   struct variable_list *vb;
1200
1201   const data_set_t *ds;
1202
1203   uint32_t oid_list_len = (uint32_t) (data->values_len + 1);
1204   /* Holds the last OID returned by the device. We use this in the GETNEXT
1205    * request to proceed. */
1206   oid_t oid_list[oid_list_len];
1207   /* Set to false when an OID has left its subtree so we don't re-request it
1208    * again. */
1209   _Bool oid_list_todo[oid_list_len];
1210
1211   int status;
1212   int i;
1213   uint32_t j;
1214
1215   /* `value_list_head' and `value_list_tail' implement a linked list for each
1216    * value. `instance_list_head' and `instance_list_tail' implement a linked list of
1217    * instance names. This is used to jump gaps in the table. */
1218   csnmp_list_instances_t *instance_list_head;
1219   csnmp_list_instances_t *instance_list_tail;
1220   csnmp_table_values_t **value_list_head;
1221   csnmp_table_values_t **value_list_tail;
1222
1223   DEBUG ("snmp plugin: csnmp_read_table (host = %s, data = %s)",
1224       host->name, data->name);
1225
1226   if (host->sess_handle == NULL)
1227   {
1228     DEBUG ("snmp plugin: csnmp_read_table: host->sess_handle == NULL");
1229     return (-1);
1230   }
1231
1232   ds = plugin_get_ds (data->type);
1233   if (!ds)
1234   {
1235     ERROR ("snmp plugin: DataSet `%s' not defined.", data->type);
1236     return (-1);
1237   }
1238
1239   if (ds->ds_num != data->values_len)
1240   {
1241     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
1242         data->type, ds->ds_num, data->values_len);
1243     return (-1);
1244   }
1245   assert (data->values_len > 0);
1246
1247   /* We need a copy of all the OIDs, because GETNEXT will destroy them. */
1248   memcpy (oid_list, data->values, data->values_len * sizeof (oid_t));
1249   if (data->instance.oid.oid_len > 0)
1250     memcpy (oid_list + data->values_len, &data->instance.oid, sizeof (oid_t));
1251   else /* no InstanceFrom option specified. */
1252     oid_list_len--;
1253
1254   for (j = 0; j < oid_list_len; j++)
1255     oid_list_todo[j] = 1;
1256
1257   /* We're going to construct n linked lists, one for each "value".
1258    * value_list_head will contain pointers to the heads of these linked lists,
1259    * value_list_tail will contain pointers to the tail of the lists. */
1260   value_list_head = calloc (data->values_len, sizeof (*value_list_head));
1261   value_list_tail = calloc (data->values_len, sizeof (*value_list_tail));
1262   if ((value_list_head == NULL) || (value_list_tail == NULL))
1263   {
1264     ERROR ("snmp plugin: csnmp_read_table: calloc failed.");
1265     sfree (value_list_head);
1266     sfree (value_list_tail);
1267     return (-1);
1268   }
1269
1270   instance_list_head = NULL;
1271   instance_list_tail = NULL;
1272
1273   status = 0;
1274   while (status == 0)
1275   {
1276     int oid_list_todo_num;
1277
1278     req = snmp_pdu_create (SNMP_MSG_GETNEXT);
1279     if (req == NULL)
1280     {
1281       ERROR ("snmp plugin: snmp_pdu_create failed.");
1282       status = -1;
1283       break;
1284     }
1285
1286     oid_list_todo_num = 0;
1287     for (j = 0; j < oid_list_len; j++)
1288     {
1289       /* Do not rerequest already finished OIDs */
1290       if (!oid_list_todo[j])
1291         continue;
1292       oid_list_todo_num++;
1293       snmp_add_null_var (req, oid_list[j].oid, oid_list[j].oid_len);
1294     }
1295
1296     if (oid_list_todo_num == 0)
1297     {
1298       /* The request is still empty - so we are finished */
1299       DEBUG ("snmp plugin: all variables have left their subtree");
1300       status = 0;
1301       break;
1302     }
1303
1304     res = NULL;
1305     status = snmp_sess_synch_response (host->sess_handle, req, &res);
1306     if ((status != STAT_SUCCESS) || (res == NULL))
1307     {
1308       char *errstr = NULL;
1309
1310       snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
1311
1312       c_complain (LOG_ERR, &host->complaint,
1313           "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
1314           host->name, (errstr == NULL) ? "Unknown problem" : errstr);
1315
1316       if (res != NULL)
1317         snmp_free_pdu (res);
1318       res = NULL;
1319
1320       /* snmp_synch_response already freed our PDU */
1321       req = NULL;
1322       sfree (errstr);
1323       csnmp_host_close_session (host);
1324
1325       status = -1;
1326       break;
1327     }
1328
1329     status = 0;
1330     assert (res != NULL);
1331     c_release (LOG_INFO, &host->complaint,
1332         "snmp plugin: host %s: snmp_sess_synch_response successful.",
1333         host->name);
1334
1335     vb = res->variables;
1336     if (vb == NULL)
1337     {
1338       status = -1;
1339       break;
1340     }
1341
1342     for (vb = res->variables, i = 0; (vb != NULL); vb = vb->next_variable, i++)
1343     {
1344       /* Calculate value index from todo list */
1345       while ((i < oid_list_len) && !oid_list_todo[i])
1346         i++;
1347
1348       /* An instance is configured and the res variable we process is the
1349        * instance value (last index) */
1350       if ((data->instance.oid.oid_len > 0) && (i == data->values_len))
1351       {
1352         if ((vb->type == SNMP_ENDOFMIBVIEW)
1353             || (snmp_oid_ncompare (data->instance.oid.oid,
1354                 data->instance.oid.oid_len,
1355                 vb->name, vb->name_length,
1356                 data->instance.oid.oid_len) != 0))
1357         {
1358           DEBUG ("snmp plugin: host = %s; data = %s; Instance left its subtree.",
1359               host->name, data->name);
1360           oid_list_todo[i] = 0;
1361           continue;
1362         }
1363
1364         /* Allocate a new `csnmp_list_instances_t', insert the instance name and
1365          * add it to the list */
1366         if (csnmp_instance_list_add (&instance_list_head, &instance_list_tail,
1367               res, host, data) != 0)
1368         {
1369           ERROR ("snmp plugin: csnmp_instance_list_add failed.");
1370           status = -1;
1371           break;
1372         }
1373       }
1374       else /* The variable we are processing is a normal value */
1375       {
1376         csnmp_table_values_t *vt;
1377         oid_t vb_name;
1378         oid_t suffix;
1379         int ret;
1380
1381         csnmp_oid_init (&vb_name, vb->name, vb->name_length);
1382
1383         /* Calculate the current suffix. This is later used to check that the
1384          * suffix is increasing. This also checks if we left the subtree */
1385         ret = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
1386         if (ret != 0)
1387         {
1388           DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
1389               "Value probably left its subtree.",
1390               host->name, data->name, i);
1391           oid_list_todo[i] = 0;
1392           continue;
1393         }
1394
1395         /* Make sure the OIDs returned by the agent are increasing. Otherwise our
1396          * table matching algorithm will get confused. */
1397         if ((value_list_tail[i] != NULL)
1398             && (csnmp_oid_compare (&suffix, &value_list_tail[i]->suffix) <= 0))
1399         {
1400           DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
1401               "Suffix is not increasing.",
1402               host->name, data->name, i);
1403           oid_list_todo[i] = 0;
1404           continue;
1405         }
1406
1407         vt = malloc (sizeof (*vt));
1408         if (vt == NULL)
1409         {
1410           ERROR ("snmp plugin: malloc failed.");
1411           status = -1;
1412           break;
1413         }
1414         memset (vt, 0, sizeof (*vt));
1415
1416         vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
1417             data->scale, data->shift, host->name, data->name);
1418         memcpy (&vt->suffix, &suffix, sizeof (vt->suffix));
1419         vt->next = NULL;
1420
1421         if (value_list_tail[i] == NULL)
1422           value_list_head[i] = vt;
1423         else
1424           value_list_tail[i]->next = vt;
1425         value_list_tail[i] = vt;
1426       }
1427
1428       /* Copy OID to oid_list[i] */
1429       memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length);
1430       oid_list[i].oid_len = vb->name_length;
1431
1432     } /* for (vb = res->variables ...) */
1433
1434     if (res != NULL)
1435       snmp_free_pdu (res);
1436     res = NULL;
1437   } /* while (status == 0) */
1438
1439   if (res != NULL)
1440     snmp_free_pdu (res);
1441   res = NULL;
1442
1443   if (req != NULL)
1444     snmp_free_pdu (req);
1445   req = NULL;
1446
1447   if (status == 0)
1448     csnmp_dispatch_table (host, data, instance_list_head, value_list_head);
1449
1450   /* Free all allocated variables here */
1451   while (instance_list_head != NULL)
1452   {
1453     csnmp_list_instances_t *next = instance_list_head->next;
1454     sfree (instance_list_head);
1455     instance_list_head = next;
1456   }
1457
1458   for (i = 0; i < data->values_len; i++)
1459   {
1460     while (value_list_head[i] != NULL)
1461     {
1462       csnmp_table_values_t *next = value_list_head[i]->next;
1463       sfree (value_list_head[i]);
1464       value_list_head[i] = next;
1465     }
1466   }
1467
1468   sfree (value_list_head);
1469   sfree (value_list_tail);
1470
1471   return (0);
1472 } /* int csnmp_read_table */
1473
1474 static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
1475 {
1476   struct snmp_pdu *req;
1477   struct snmp_pdu *res;
1478   struct variable_list *vb;
1479
1480   const data_set_t *ds;
1481   value_list_t vl = VALUE_LIST_INIT;
1482
1483   int status;
1484   int i;
1485
1486   DEBUG ("snmp plugin: csnmp_read_value (host = %s, data = %s)",
1487       host->name, data->name);
1488
1489   if (host->sess_handle == NULL)
1490   {
1491     DEBUG ("snmp plugin: csnmp_read_value: host->sess_handle == NULL");
1492     return (-1);
1493   }
1494
1495   ds = plugin_get_ds (data->type);
1496   if (!ds)
1497   {
1498     ERROR ("snmp plugin: DataSet `%s' not defined.", data->type);
1499     return (-1);
1500   }
1501
1502   if (ds->ds_num != data->values_len)
1503   {
1504     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
1505         data->type, ds->ds_num, data->values_len);
1506     return (-1);
1507   }
1508
1509   vl.values_len = ds->ds_num;
1510   vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len);
1511   if (vl.values == NULL)
1512     return (-1);
1513   for (i = 0; i < vl.values_len; i++)
1514   {
1515     if (ds->ds[i].type == DS_TYPE_COUNTER)
1516       vl.values[i].counter = 0;
1517     else
1518       vl.values[i].gauge = NAN;
1519   }
1520
1521   sstrncpy (vl.host, host->name, sizeof (vl.host));
1522   sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin));
1523   sstrncpy (vl.type, data->type, sizeof (vl.type));
1524   sstrncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance));
1525
1526   vl.interval = host->interval;
1527
1528   req = snmp_pdu_create (SNMP_MSG_GET);
1529   if (req == NULL)
1530   {
1531     ERROR ("snmp plugin: snmp_pdu_create failed.");
1532     sfree (vl.values);
1533     return (-1);
1534   }
1535
1536   for (i = 0; i < data->values_len; i++)
1537     snmp_add_null_var (req, data->values[i].oid, data->values[i].oid_len);
1538
1539   res = NULL;
1540   status = snmp_sess_synch_response (host->sess_handle, req, &res);
1541
1542   if ((status != STAT_SUCCESS) || (res == NULL))
1543   {
1544     char *errstr = NULL;
1545
1546     snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
1547     ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
1548         host->name, (errstr == NULL) ? "Unknown problem" : errstr);
1549
1550     if (res != NULL)
1551       snmp_free_pdu (res);
1552     res = NULL;
1553
1554     sfree (errstr);
1555     csnmp_host_close_session (host);
1556
1557     return (-1);
1558   }
1559
1560
1561   for (vb = res->variables; vb != NULL; vb = vb->next_variable)
1562   {
1563 #if COLLECT_DEBUG
1564     char buffer[1024];
1565     snprint_variable (buffer, sizeof (buffer),
1566         vb->name, vb->name_length, vb);
1567     DEBUG ("snmp plugin: Got this variable: %s", buffer);
1568 #endif /* COLLECT_DEBUG */
1569
1570     for (i = 0; i < data->values_len; i++)
1571       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
1572             vb->name, vb->name_length) == 0)
1573         vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
1574             data->scale, data->shift, host->name, data->name);
1575   } /* for (res->variables) */
1576
1577   if (res != NULL)
1578     snmp_free_pdu (res);
1579   res = NULL;
1580
1581   DEBUG ("snmp plugin: -> plugin_dispatch_values (&vl);");
1582   plugin_dispatch_values (&vl);
1583   sfree (vl.values);
1584
1585   return (0);
1586 } /* int csnmp_read_value */
1587
1588 static int csnmp_read_host (user_data_t *ud)
1589 {
1590   host_definition_t *host;
1591   cdtime_t time_start;
1592   cdtime_t time_end;
1593   int status;
1594   int success;
1595   int i;
1596
1597   host = ud->data;
1598
1599   if (host->interval == 0)
1600     host->interval = plugin_get_interval ();
1601
1602   time_start = cdtime ();
1603
1604   if (host->sess_handle == NULL)
1605     csnmp_host_open_session (host);
1606
1607   if (host->sess_handle == NULL)
1608     return (-1);
1609
1610   success = 0;
1611   for (i = 0; i < host->data_list_len; i++)
1612   {
1613     data_definition_t *data = host->data_list[i];
1614
1615     if (data->is_table)
1616       status = csnmp_read_table (host, data);
1617     else
1618       status = csnmp_read_value (host, data);
1619
1620     if (status == 0)
1621       success++;
1622   }
1623
1624   time_end = cdtime ();
1625   if ((time_end - time_start) > host->interval)
1626   {
1627     WARNING ("snmp plugin: Host `%s' should be queried every %.3f "
1628         "seconds, but reading all values takes %.3f seconds.",
1629         host->name,
1630         CDTIME_T_TO_DOUBLE (host->interval),
1631         CDTIME_T_TO_DOUBLE (time_end - time_start));
1632   }
1633
1634   if (success == 0)
1635     return (-1);
1636
1637   return (0);
1638 } /* int csnmp_read_host */
1639
1640 static int csnmp_init (void)
1641 {
1642   call_snmp_init_once ();
1643
1644   return (0);
1645 } /* int csnmp_init */
1646
1647 static int csnmp_shutdown (void)
1648 {
1649   data_definition_t *data_this;
1650   data_definition_t *data_next;
1651
1652   /* When we get here, the read threads have been stopped and all the
1653    * `host_definition_t' will be freed. */
1654   DEBUG ("snmp plugin: Destroying all data definitions.");
1655
1656   data_this = data_head;
1657   data_head = NULL;
1658   while (data_this != NULL)
1659   {
1660     data_next = data_this->next;
1661
1662     sfree (data_this->name);
1663     sfree (data_this->type);
1664     sfree (data_this->values);
1665     sfree (data_this);
1666
1667     data_this = data_next;
1668   }
1669
1670   return (0);
1671 } /* int csnmp_shutdown */
1672
1673 void module_register (void)
1674 {
1675   plugin_register_complex_config ("snmp", csnmp_config);
1676   plugin_register_init ("snmp", csnmp_init);
1677   plugin_register_shutdown ("snmp", csnmp_shutdown);
1678 } /* void module_register */
1679
1680 /*
1681  * vim: shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker
1682  */