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