Merge branch 'collectd-4.5'
[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 *endptr;
736
737     endptr = NULL;
738     if (vl->val.string != NULL)
739     {
740       char string[64];
741       size_t string_length;
742
743       string_length = sizeof (string) - 1;
744       if (vl->val_len < string_length)
745         string_length = vl->val_len;
746
747       /* The strings we get from the Net-SNMP library may not be null
748        * terminated. That is why we're using `membpy' here and not `strcpy'.
749        * `string_length' is set to `vl->val_len' which holds the length of the
750        * string.  -octo */
751       memcpy (string, vl->val.string, string_length);
752       string[string_length] = 0;
753
754       if (type == DS_TYPE_COUNTER)
755       {
756         ret.counter = (counter_t) strtoll (string, &endptr, /* base = */ 0);
757         DEBUG ("snmp plugin: csnmp_value_list_to_value: String to counter: %s -> %llu",
758             string, (unsigned long long) ret.counter);
759       }
760       else if (type == DS_TYPE_GAUGE)
761       {
762         ret.gauge = (gauge_t) strtod (string, &endptr);
763         DEBUG ("snmp plugin: csnmp_value_list_to_value: String to gauge: %s -> %g",
764             string, (double) ret.gauge);
765       }
766     }
767
768     /* Check if an error occurred */
769     if ((vl->val.string == NULL) || (endptr == (char *) vl->val.string))
770     {
771       if (type == DS_TYPE_COUNTER)
772         ret.counter = 0;
773       else if (type == DS_TYPE_GAUGE)
774         ret.gauge = NAN;
775     }
776   }
777   else if (type == DS_TYPE_COUNTER)
778   {
779     ret.counter = temp;
780   }
781   else if (type == DS_TYPE_GAUGE)
782   {
783     ret.gauge = NAN;
784     if (defined != 0)
785       ret.gauge = (scale * temp) + shift;
786   }
787
788   return (ret);
789 } /* value_t csnmp_value_list_to_value */
790
791 /* Returns true if all OIDs have left their subtree */
792 static int csnmp_check_res_left_subtree (const host_definition_t *host,
793     const data_definition_t *data,
794     struct snmp_pdu *res)
795 {
796   struct variable_list *vb;
797   int num_checked;
798   int num_left_subtree;
799   int i;
800
801   vb = res->variables;
802   if (vb == NULL)
803     return (-1);
804
805   num_checked = 0;
806   num_left_subtree = 0;
807
808   /* check all the variables and count how many have left their subtree */
809   for (vb = res->variables, i = 0;
810       (vb != NULL) && (i < data->values_len);
811       vb = vb->next_variable, i++)
812   {
813     num_checked++;
814     if (snmp_oid_ncompare (data->values[i].oid,
815           data->values[i].oid_len,
816           vb->name, vb->name_length,
817           data->values[i].oid_len) != 0)
818       num_left_subtree++;
819   }
820
821   /* check if enough variables have been returned */
822   if (i < data->values_len)
823   {
824     ERROR ("snmp plugin: host %s: Expected %i variables, but got only %i",
825         host->name, data->values_len, i);
826     return (-1);
827   }
828
829   if (data->instance.oid.oid_len > 0)
830   {
831     if (vb == NULL)
832     {
833       ERROR ("snmp plugin: host %s: Expected one more variable for "
834           "the instance..", host->name);
835       return (-1);
836     }
837
838     num_checked++;
839     if (snmp_oid_ncompare (data->instance.oid.oid,
840           data->instance.oid.oid_len,
841           vb->name, vb->name_length,
842           data->instance.oid.oid_len) != 0)
843       num_left_subtree++;
844   }
845
846   DEBUG ("snmp plugin: csnmp_check_res_left_subtree: %i of %i variables have "
847       "left their subtree",
848       num_left_subtree, num_checked);
849   if (num_left_subtree >= num_checked)
850     return (1);
851   return (0);
852 } /* int csnmp_check_res_left_subtree */
853
854 static int csnmp_instance_list_add (csnmp_list_instances_t **head,
855     csnmp_list_instances_t **tail,
856     const struct snmp_pdu *res)
857 {
858   csnmp_list_instances_t *il;
859   struct variable_list *vb;
860
861   /* Set vb on the last variable */
862   for (vb = res->variables;
863       (vb != NULL) && (vb->next_variable != NULL);
864       vb = vb->next_variable)
865     /* do nothing */;
866   if (vb == NULL)
867     return (-1);
868
869   il = (csnmp_list_instances_t *) malloc (sizeof (csnmp_list_instances_t));
870   if (il == NULL)
871   {
872     ERROR ("snmp plugin: malloc failed.");
873     return (-1);
874   }
875   il->subid = vb->name[vb->name_length - 1];
876   il->next = NULL;
877
878   /* Get instance name */
879   if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
880   {
881     char *ptr;
882     size_t instance_len;
883
884     memset (il->instance, 0, sizeof (il->instance));
885     instance_len = sizeof (il->instance) - 1;
886     if (instance_len > vb->val_len)
887       instance_len = vb->val_len;
888
889     sstrncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR)
890           ? vb->val.string
891           : vb->val.bitstring),
892         instance_len + 1);
893
894     for (ptr = il->instance; *ptr != '\0'; ptr++)
895     {
896       if ((*ptr > 0) && (*ptr < 32))
897         *ptr = ' ';
898       else if (*ptr == '/')
899         *ptr = '_';
900     }
901     DEBUG ("snmp plugin: il->instance = `%s';", il->instance);
902   }
903   else
904   {
905     value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER, 1.0, 0.0);
906     ssnprintf (il->instance, sizeof (il->instance),
907         "%llu", val.counter);
908   }
909
910   /* TODO: Debugging output */
911
912   if (*head == NULL)
913     *head = il;
914   else
915     (*tail)->next = il;
916   *tail = il;
917
918   return (0);
919 } /* int csnmp_instance_list_add */
920
921 static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *data,
922     csnmp_list_instances_t *instance_list,
923     csnmp_table_values_t **value_table)
924 {
925   const data_set_t *ds;
926   value_list_t vl = VALUE_LIST_INIT;
927
928   csnmp_list_instances_t *instance_list_ptr;
929   csnmp_table_values_t **value_table_ptr;
930
931   int i;
932   oid subid;
933   int have_more;
934
935   ds = plugin_get_ds (data->type);
936   if (!ds)
937   {
938     ERROR ("snmp plugin: DataSet `%s' not defined.", data->type);
939     return (-1);
940   }
941   assert (ds->ds_num == data->values_len);
942
943   instance_list_ptr = instance_list;
944
945   value_table_ptr = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
946       * data->values_len);
947   if (value_table_ptr == NULL)
948     return (-1);
949   for (i = 0; i < data->values_len; i++)
950     value_table_ptr[i] = value_table[i];
951
952   vl.values_len = ds->ds_num;
953   vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len);
954   if (vl.values == NULL)
955   {
956     ERROR ("snmp plugin: malloc failed.");
957     sfree (value_table_ptr);
958     return (-1);
959   }
960
961   sstrncpy (vl.host, host->name, sizeof (vl.host));
962   sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin));
963
964   vl.interval = host->interval;
965   vl.time = time (NULL);
966
967   subid = 0;
968   have_more = 1;
969
970   while (have_more != 0)
971   {
972     if (instance_list != NULL)
973     {
974       while ((instance_list_ptr != NULL)
975           && (instance_list_ptr->subid < subid))
976         instance_list_ptr = instance_list_ptr->next;
977
978       if (instance_list_ptr == NULL)
979       {
980         have_more = 0;
981         continue;
982       }
983       else if (instance_list_ptr->subid > subid)
984       {
985         subid = instance_list_ptr->subid;
986         continue;
987       }
988     } /* if (instance_list != NULL) */
989
990     for (i = 0; i < data->values_len; i++)
991     {
992       while ((value_table_ptr[i] != NULL)
993           && (value_table_ptr[i]->subid < subid))
994         value_table_ptr[i] = value_table_ptr[i]->next;
995
996       if (value_table_ptr[i] == NULL)
997       {
998         have_more = 0;
999         break;
1000       }
1001       else if (value_table_ptr[i]->subid > subid)
1002       {
1003         subid = value_table_ptr[i]->subid;
1004         break;
1005       }
1006     } /* for (i = 0; i < columns; i++) */
1007     /* The subid has been increased - start scanning from the beginning
1008      * again.. */
1009     if (i < data->values_len)
1010       continue;
1011
1012     /* if we reach this line, all value_table_ptr[i] are non-NULL and are set
1013      * to the same subid. instance_list_ptr is either NULL or points to the
1014      * same subid, too. */
1015 #if COLLECT_DEBUG
1016     for (i = 1; i < data->values_len; i++)
1017     {
1018       assert (value_table_ptr[i] != NULL);
1019       assert (value_table_ptr[i-1]->subid == value_table_ptr[i]->subid);
1020     }
1021     assert ((instance_list_ptr == NULL)
1022         || (instance_list_ptr->subid == value_table_ptr[0]->subid));
1023 #endif
1024
1025     sstrncpy (vl.type, data->type, sizeof (vl.type));
1026
1027     {
1028       char temp[DATA_MAX_NAME_LEN];
1029
1030       if (instance_list_ptr == NULL)
1031         ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid);
1032       else
1033         sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
1034
1035       if (data->instance_prefix == NULL)
1036         sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
1037       else
1038         ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
1039             data->instance_prefix, temp);
1040     }
1041
1042     for (i = 0; i < data->values_len; i++)
1043       vl.values[i] = value_table_ptr[i]->value;
1044
1045     /* If we get here `vl.type_instance' and all `vl.values' have been set */
1046     plugin_dispatch_values (&vl);
1047
1048     subid++;
1049   } /* while (have_more != 0) */
1050
1051   sfree (vl.values);
1052   sfree (value_table_ptr);
1053
1054   return (0);
1055 } /* int csnmp_dispatch_table */
1056
1057 static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
1058 {
1059   struct snmp_pdu *req;
1060   struct snmp_pdu *res;
1061   struct variable_list *vb;
1062
1063   const data_set_t *ds;
1064   oid_t *oid_list;
1065   uint32_t oid_list_len;
1066
1067   int status;
1068   int i;
1069
1070   /* `value_table' and `value_table_ptr' implement a linked list for each
1071    * value. `instance_list' and `instance_list_ptr' implement a linked list of
1072    * instance names. This is used to jump gaps in the table. */
1073   csnmp_list_instances_t *instance_list;
1074   csnmp_list_instances_t *instance_list_ptr;
1075   csnmp_table_values_t **value_table;
1076   csnmp_table_values_t **value_table_ptr;
1077
1078   DEBUG ("snmp plugin: csnmp_read_table (host = %s, data = %s)",
1079       host->name, data->name);
1080
1081   if (host->sess_handle == NULL)
1082   {
1083     DEBUG ("snmp plugin: csnmp_read_table: host->sess_handle == NULL");
1084     return (-1);
1085   }
1086
1087   ds = plugin_get_ds (data->type);
1088   if (!ds)
1089   {
1090     ERROR ("snmp plugin: DataSet `%s' not defined.", data->type);
1091     return (-1);
1092   }
1093
1094   if (ds->ds_num != data->values_len)
1095   {
1096     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
1097         data->type, ds->ds_num, data->values_len);
1098     return (-1);
1099   }
1100
1101   /* We need a copy of all the OIDs, because GETNEXT will destroy them. */
1102   oid_list_len = data->values_len + 1;
1103   oid_list = (oid_t *) malloc (sizeof (oid_t) * (oid_list_len));
1104   if (oid_list == NULL)
1105   {
1106     ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
1107     return (-1);
1108   }
1109   memcpy (oid_list, data->values, data->values_len * sizeof (oid_t));
1110   if (data->instance.oid.oid_len > 0)
1111     memcpy (oid_list + data->values_len, &data->instance.oid, sizeof (oid_t));
1112   else
1113     oid_list_len--;
1114
1115   /* Allocate the `value_table' */
1116   value_table = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
1117       * 2 * data->values_len);
1118   if (value_table == NULL)
1119   {
1120     ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
1121     sfree (oid_list);
1122     return (-1);
1123   }
1124   memset (value_table, '\0', sizeof (csnmp_table_values_t *) * 2 * data->values_len);
1125   value_table_ptr = value_table + data->values_len;
1126   
1127   instance_list = NULL;
1128   instance_list_ptr = NULL;
1129
1130   status = 0;
1131   while (status == 0)
1132   {
1133     req = snmp_pdu_create (SNMP_MSG_GETNEXT);
1134     if (req == NULL)
1135     {
1136       ERROR ("snmp plugin: snmp_pdu_create failed.");
1137       status = -1;
1138       break;
1139     }
1140
1141     for (i = 0; i < oid_list_len; i++)
1142       snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len);
1143
1144     res = NULL;
1145     status = snmp_sess_synch_response (host->sess_handle, req, &res);
1146
1147     if ((status != STAT_SUCCESS) || (res == NULL))
1148     {
1149       char *errstr = NULL;
1150
1151       snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
1152
1153       c_complain (LOG_ERR, &host->complaint,
1154           "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
1155           host->name, (errstr == NULL) ? "Unknown problem" : errstr);
1156
1157       if (res != NULL)
1158         snmp_free_pdu (res);
1159       res = NULL;
1160
1161       sfree (errstr);
1162       csnmp_host_close_session (host);
1163
1164       status = -1;
1165       break;
1166     }
1167     status = 0;
1168     assert (res != NULL);
1169     c_release (LOG_INFO, &host->complaint,
1170         "snmp plugin: host %s: snmp_sess_synch_response successful.",
1171         host->name);
1172
1173     vb = res->variables;
1174     if (vb == NULL)
1175     {
1176       status = -1;
1177       break;
1178     }
1179
1180     /* Check if all values (and possibly the instance) have left their
1181      * subtree */
1182     if (csnmp_check_res_left_subtree (host, data, res) != 0)
1183     {
1184       status = 0;
1185       break;
1186     }
1187
1188     /* if an instance-OID is configured.. */
1189     if (data->instance.oid.oid_len > 0)
1190     {
1191       /* Allocate a new `csnmp_list_instances_t', insert the instance name and
1192        * add it to the list */
1193       if (csnmp_instance_list_add (&instance_list, &instance_list_ptr,
1194             res) != 0)
1195       {
1196         ERROR ("snmp plugin: csnmp_instance_list_add failed.");
1197         status = -1;
1198         break;
1199       }
1200
1201       /* Set vb on the last variable */
1202       for (vb = res->variables;
1203           (vb != NULL) && (vb->next_variable != NULL);
1204           vb = vb->next_variable)
1205         /* do nothing */;
1206       assert (vb != NULL);
1207
1208       /* Copy OID to oid_list[data->values_len] */
1209       memcpy (oid_list[data->values_len].oid, vb->name,
1210           sizeof (oid) * vb->name_length);
1211       oid_list[data->values_len].oid_len = vb->name_length;
1212     }
1213
1214     for (vb = res->variables, i = 0;
1215         (vb != NULL) && (i < data->values_len);
1216         vb = vb->next_variable, i++)
1217     {
1218       csnmp_table_values_t *vt;
1219
1220       /* Check if we left the subtree */
1221       if (snmp_oid_ncompare (data->values[i].oid,
1222             data->values[i].oid_len,
1223             vb->name, vb->name_length,
1224             data->values[i].oid_len) != 0)
1225       {
1226         DEBUG ("snmp plugin: host = %s; data = %s; Value %i left its subtree.",
1227             host->name, data->name, i);
1228         continue;
1229       }
1230
1231       if ((value_table_ptr[i] != NULL)
1232           && (vb->name[vb->name_length - 1] <= value_table_ptr[i]->subid))
1233       {
1234         DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
1235             "SUBID is not increasing.",
1236             host->name, data->name, i);
1237         continue;
1238       }
1239
1240       vt = (csnmp_table_values_t *) malloc (sizeof (csnmp_table_values_t));
1241       if (vt == NULL)
1242       {
1243         ERROR ("snmp plugin: malloc failed.");
1244         status = -1;
1245         break;
1246       }
1247
1248       vt->subid = vb->name[vb->name_length - 1];
1249       vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
1250           data->scale, data->shift);
1251       vt->next = NULL;
1252
1253       if (value_table_ptr[i] == NULL)
1254         value_table[i] = vt;
1255       else
1256         value_table_ptr[i]->next = vt;
1257       value_table_ptr[i] = vt;
1258
1259       /* Copy OID to oid_list[i + 1] */
1260       memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length);
1261       oid_list[i].oid_len = vb->name_length;
1262     } /* for (i = data->values_len) */
1263
1264     if (res != NULL)
1265       snmp_free_pdu (res);
1266     res = NULL;
1267   } /* while (status == 0) */
1268
1269   if (res != NULL)
1270     snmp_free_pdu (res);
1271   res = NULL;
1272
1273   if (status == 0)
1274     csnmp_dispatch_table (host, data, instance_list, value_table);
1275
1276   /* Free all allocated variables here */
1277   while (instance_list != NULL)
1278   {
1279     instance_list_ptr = instance_list->next;
1280     sfree (instance_list);
1281     instance_list = instance_list_ptr;
1282   }
1283
1284   for (i = 0; i < data->values_len; i++)
1285   {
1286     csnmp_table_values_t *tmp;
1287     while (value_table[i] != NULL)
1288     {
1289       tmp = value_table[i]->next;
1290       sfree (value_table[i]);
1291       value_table[i] = tmp;
1292     }
1293   }
1294
1295   sfree (value_table);
1296   sfree (oid_list);
1297
1298   return (0);
1299 } /* int csnmp_read_table */
1300
1301 static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
1302 {
1303   struct snmp_pdu *req;
1304   struct snmp_pdu *res;
1305   struct variable_list *vb;
1306
1307   const data_set_t *ds;
1308   value_list_t vl = VALUE_LIST_INIT;
1309
1310   int status;
1311   int i;
1312
1313   DEBUG ("snmp plugin: csnmp_read_value (host = %s, data = %s)",
1314       host->name, data->name);
1315
1316   if (host->sess_handle == NULL)
1317   {
1318     DEBUG ("snmp plugin: csnmp_read_table: host->sess_handle == NULL");
1319     return (-1);
1320   }
1321
1322   ds = plugin_get_ds (data->type);
1323   if (!ds)
1324   {
1325     ERROR ("snmp plugin: DataSet `%s' not defined.", data->type);
1326     return (-1);
1327   }
1328
1329   if (ds->ds_num != data->values_len)
1330   {
1331     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
1332         data->type, ds->ds_num, data->values_len);
1333     return (-1);
1334   }
1335
1336   vl.values_len = ds->ds_num;
1337   vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len);
1338   if (vl.values == NULL)
1339     return (-1);
1340   for (i = 0; i < vl.values_len; i++)
1341   {
1342     if (ds->ds[i].type == DS_TYPE_COUNTER)
1343       vl.values[i].counter = 0;
1344     else
1345       vl.values[i].gauge = NAN;
1346   }
1347
1348   sstrncpy (vl.host, host->name, sizeof (vl.host));
1349   sstrncpy (vl.plugin, "snmp", sizeof (vl.plugin));
1350   sstrncpy (vl.type, data->type, sizeof (vl.type));
1351   sstrncpy (vl.type_instance, data->instance.string, sizeof (vl.type_instance));
1352
1353   vl.interval = host->interval;
1354
1355   req = snmp_pdu_create (SNMP_MSG_GET);
1356   if (req == NULL)
1357   {
1358     ERROR ("snmp plugin: snmp_pdu_create failed.");
1359     sfree (vl.values);
1360     return (-1);
1361   }
1362
1363   for (i = 0; i < data->values_len; i++)
1364     snmp_add_null_var (req, data->values[i].oid, data->values[i].oid_len);
1365
1366   res = NULL;
1367   status = snmp_sess_synch_response (host->sess_handle, req, &res);
1368
1369   if ((status != STAT_SUCCESS) || (res == NULL))
1370   {
1371     char *errstr = NULL;
1372
1373     snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
1374     ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
1375         host->name, (errstr == NULL) ? "Unknown problem" : errstr);
1376
1377     if (res != NULL)
1378       snmp_free_pdu (res);
1379     res = NULL;
1380
1381     sfree (errstr);
1382     csnmp_host_close_session (host);
1383
1384     return (-1);
1385   }
1386
1387   vl.time = time (NULL);
1388
1389   for (vb = res->variables; vb != NULL; vb = vb->next_variable)
1390   {
1391 #if COLLECT_DEBUG
1392     char buffer[1024];
1393     snprint_variable (buffer, sizeof (buffer),
1394         vb->name, vb->name_length, vb);
1395     DEBUG ("snmp plugin: Got this variable: %s", buffer);
1396 #endif /* COLLECT_DEBUG */
1397
1398     for (i = 0; i < data->values_len; i++)
1399       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
1400             vb->name, vb->name_length) == 0)
1401         vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
1402             data->scale, data->shift);
1403   } /* for (res->variables) */
1404
1405   if (res != NULL)
1406     snmp_free_pdu (res);
1407   res = NULL;
1408
1409   DEBUG ("snmp plugin: -> plugin_dispatch_values (&vl);");
1410   plugin_dispatch_values (&vl);
1411   sfree (vl.values);
1412
1413   return (0);
1414 } /* int csnmp_read_value */
1415
1416 static int csnmp_read_host (host_definition_t *host)
1417 {
1418   int i;
1419   time_t time_start;
1420   time_t time_end;
1421
1422   time_start = time (NULL);
1423   DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name,
1424       (unsigned int) time_start);
1425
1426   if (host->sess_handle == NULL)
1427     csnmp_host_open_session (host);
1428
1429   if (host->sess_handle == NULL)
1430     return (-1);
1431
1432   for (i = 0; i < host->data_list_len; i++)
1433   {
1434     data_definition_t *data = host->data_list[i];
1435
1436     if (data->is_table)
1437       csnmp_read_table (host, data);
1438     else
1439       csnmp_read_value (host, data);
1440   }
1441
1442   time_end = time (NULL);
1443   DEBUG ("snmp plugin: csnmp_read_host (%s) finished at %u;", host->name,
1444       (unsigned int) time_end);
1445   if ((time_end - time_start) > host->interval)
1446   {
1447     WARNING ("snmp plugin: Host `%s' should be queried every %i seconds, "
1448         "but reading all values takes %u seconds.",
1449         host->name, host->interval, (unsigned int) (time_end - time_start));
1450   }
1451
1452   return (0);
1453 } /* int csnmp_read_host */
1454
1455 static void *csnmp_read_thread (void *data)
1456 {
1457   host_definition_t *host;
1458
1459   pthread_mutex_lock (&host_lock);
1460   while (do_shutdown == 0)
1461   {
1462     pthread_cond_wait (&host_cond, &host_lock);
1463
1464     for (host = host_head; host != NULL; host = host->next)
1465     {
1466       if (do_shutdown != 0)
1467         break;
1468       if (host->state != STATE_WAIT)
1469         continue;
1470
1471       host->state = STATE_BUSY;
1472       pthread_mutex_unlock (&host_lock);
1473       csnmp_read_host (host);
1474       pthread_mutex_lock (&host_lock);
1475       host->state = STATE_IDLE;
1476     } /* for (host) */
1477   } /* while (do_shutdown == 0) */
1478   pthread_mutex_unlock (&host_lock);
1479
1480   pthread_exit ((void *) 0);
1481   return ((void *) 0);
1482 } /* void *csnmp_read_thread */
1483
1484 static int csnmp_init (void)
1485 {
1486   host_definition_t *host;
1487   int i;
1488
1489   if (host_head == NULL)
1490   {
1491     NOTICE ("snmp plugin: No host has been defined.");
1492     return (-1);
1493   }
1494
1495   call_snmp_init_once ();
1496
1497   threads_num = 0;
1498   for (host = host_head; host != NULL; host = host->next)
1499   {
1500     threads_num++;
1501     /* We need to initialize `interval' here, because `interval_g' isn't
1502      * initialized during `configure'. */
1503     host->next_update = time (NULL);
1504     if (host->interval == 0)
1505     {
1506       host->interval = interval_g;
1507     }
1508     else if (host->interval < interval_g)
1509     {
1510       host->interval = interval_g;
1511       WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.",
1512           host->name, host->interval);
1513     }
1514
1515     csnmp_host_open_session (host);
1516   } /* for (host) */
1517
1518   /* Now start the reading threads */
1519   if (threads_num > 3)
1520   {
1521     threads_num = 3 + ((threads_num - 3) / 10);
1522     if (threads_num > 10)
1523       threads_num = 10;
1524   }
1525
1526   threads = (pthread_t *) malloc (threads_num * sizeof (pthread_t));
1527   if (threads == NULL)
1528   {
1529     ERROR ("snmp plugin: malloc failed.");
1530     return (-1);
1531   }
1532   memset (threads, '\0', threads_num * sizeof (pthread_t));
1533
1534   for (i = 0; i < threads_num; i++)
1535       pthread_create (threads + i, NULL, csnmp_read_thread, (void *) 0);
1536
1537   return (0);
1538 } /* int csnmp_init */
1539
1540 static int csnmp_read (void)
1541 {
1542   host_definition_t *host;
1543   time_t now;
1544
1545   if (host_head == NULL)
1546   {
1547     INFO ("snmp plugin: No hosts configured.");
1548     return (-1);
1549   }
1550
1551   now = time (NULL);
1552
1553   pthread_mutex_lock (&host_lock);
1554   for (host = host_head; host != NULL; host = host->next)
1555   {
1556     if (host->state != STATE_IDLE)
1557       continue;
1558
1559     /* Skip this host if the next or a later iteration will be sufficient. */
1560     if (host->next_update >= (now + interval_g))
1561       continue;
1562
1563     host->state = STATE_WAIT;
1564     host->next_update = now + host->interval;
1565   } /* for (host) */
1566
1567   pthread_cond_broadcast (&host_cond);
1568   pthread_mutex_unlock (&host_lock);
1569
1570   return (0);
1571 } /* int csnmp_read */
1572
1573 static int csnmp_shutdown (void)
1574 {
1575   host_definition_t *host_this;
1576   host_definition_t *host_next;
1577
1578   data_definition_t *data_this;
1579   data_definition_t *data_next;
1580
1581   int i;
1582
1583   pthread_mutex_lock (&host_lock);
1584   do_shutdown = 1;
1585   pthread_cond_broadcast (&host_cond);
1586   pthread_mutex_unlock (&host_lock);
1587
1588   for (i = 0; i < threads_num; i++)
1589     pthread_join (threads[i], NULL);
1590
1591   /* Now that all the threads have exited, let's free all the global variables.
1592    * This isn't really neccessary, I guess, but I think it's good stile to do
1593    * so anyway. */
1594   host_this = host_head;
1595   host_head = NULL;
1596   while (host_this != NULL)
1597   {
1598     host_next = host_this->next;
1599
1600     csnmp_host_close_session (host_this);
1601
1602     sfree (host_this->name);
1603     sfree (host_this->address);
1604     sfree (host_this->community);
1605     sfree (host_this->data_list);
1606     sfree (host_this);
1607
1608     host_this = host_next;
1609   }
1610
1611   data_this = data_head;
1612   data_head = NULL;
1613   while (data_this != NULL)
1614   {
1615     data_next = data_this->next;
1616
1617     sfree (data_this->name);
1618     sfree (data_this->type);
1619     sfree (data_this->values);
1620     sfree (data_this);
1621
1622     data_this = data_next;
1623   }
1624
1625   return (0);
1626 } /* int csnmp_shutdown */
1627
1628 void module_register (void)
1629 {
1630   plugin_register_complex_config ("snmp", csnmp_config);
1631   plugin_register_init ("snmp", csnmp_init);
1632   plugin_register_read ("snmp", csnmp_read);
1633   plugin_register_shutdown ("snmp", csnmp_shutdown);
1634 } /* void module_register */
1635
1636 /*
1637  * vim: shiftwidth=2 softtabstop=2 tabstop=8
1638  */