root/src/modules/snmp.c

Revision 1384106e477cb723a67b48449d14e14649261b27, 14.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

not required, and produced a segfault

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <math.h>
13
14 #include "noit_module.h"
15 #include "noit_check.h"
16 #include "noit_check_tools.h"
17 #include "utils/noit_log.h"
18 #include "utils/noit_hash.h"
19
20 #include <net-snmp/net-snmp-config.h>
21 #include <net-snmp/net-snmp-includes.h>
22
23 static noit_log_stream_t nlerr = NULL;
24 static noit_log_stream_t nldeb = NULL;
25 static noit_hash_table target_sessions = NOIT_HASH_EMPTY;
26
27 struct target_session {
28   void *sess_handle;
29   eventer_t timeoutevent;
30   int fd;
31   int in_table;
32   int refcnt;
33 };
34
35 struct snmp_check_closure {
36   noit_module_t *self;
37   noit_check_t *check;
38 };
39
40 struct check_info {
41   int reqid;
42   int timedout;
43   struct {
44      char *confname;
45      char *oidname;
46      oid oid[MAX_OID_LEN];
47      size_t oidlen;
48   } *oids;
49   int noids;
50   eventer_t timeoutevent;
51   noit_module_t *self;
52   noit_check_t *check;
53 };
54
55 /* We hold struct check_info's in there key's by their reqid.
56  *   If they timeout, we remove them.
57  *
58  *   When SNMP queries complete, we look them up, if we find them
59  *   then we know we can remove the timeout and  complete the check.
60  *   If we don't find them, the timeout fired and removed the check.
61  */
62 noit_hash_table active_checks = NOIT_HASH_EMPTY;
63 static void add_check(struct check_info *c) {
64   noit_hash_store(&active_checks, (char *)&c->reqid, sizeof(c->reqid), c);
65 }
66 static struct check_info *get_check(int reqid) {
67   struct check_info *c;
68   if(noit_hash_retrieve(&active_checks, (char *)&reqid, sizeof(reqid),
69                         (void **)&c))
70     return c;
71   return NULL;
72 }
73 static void remove_check(struct check_info *c) {
74   noit_hash_delete(&active_checks, (char *)&c->reqid, sizeof(c->reqid),
75                    NULL, NULL);
76 }
77
78 static int noit_snmp_init(noit_module_t *self) {
79   register_mib_handlers();
80   read_premib_configs();
81   read_configs();
82   netsnmp_init_mib();
83   init_snmp("noitd");
84   return 0;
85 }
86
87 /* Handling of results */
88 static void noit_snmp_log_results(noit_module_t *self, noit_check_t *check,
89                                   struct snmp_pdu *pdu) {
90   struct check_info *info = check->closure;
91   struct variable_list *vars;
92   struct timeval duration;
93   char buff[128];
94   stats_t current;
95   int nresults = 0;
96
97   noit_check_stats_clear(&current);
98
99   if(pdu)
100     for(vars = pdu->variables; vars; vars = vars->next_variable)
101       nresults++;
102
103   gettimeofday(&current.whence, NULL);
104   sub_timeval(current.whence, check->last_fire_time, &duration);
105   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
106   current.available = pdu ? NP_AVAILABLE : NP_UNAVAILABLE;
107   current.state = (nresults == info->noids) ? NP_GOOD : NP_BAD;
108   snprintf(buff, sizeof(buff), "%d/%d gets", nresults, info->noids);
109   current.status = buff;
110
111   /* We have no results over which to iterate. */
112   if(!pdu) {
113     noit_check_set_stats(self, check, &current);
114     return;
115   }
116
117   /* manipulate the information ourselves */
118   nresults = 0;
119   for(vars = pdu->variables; vars; vars = vars->next_variable) {
120     char *sp;
121     int oid_idx;
122     double float_conv;
123     u_int64_t u64;
124     int64_t i64;
125     char *endptr;
126     char varbuff[256];
127
128     /* find the oid to which this is the response */
129     oid_idx = nresults; /* our current idx is the most likely */
130     if(info->oids[oid_idx].oidlen != vars->name_length ||
131        memcmp(info->oids[oid_idx].oid, vars->name,
132               vars->name_length * sizeof(oid))) {
133       /* Not the most obvious guess */
134       for(oid_idx = info->noids - 1; oid_idx >= 0; oid_idx--) {
135         if(info->oids[oid_idx].oidlen == vars->name_length &&
136            memcmp(info->oids[oid_idx].oid, vars->name,
137                   vars->name_length * sizeof(oid))) break;
138       }
139     }
140     if(oid_idx < 0) {
141       snprint_variable(varbuff, sizeof(varbuff),
142                        vars->name, vars->name_length, vars);
143       noitL(nlerr, "Unexpected oid results to %s`%s`%s: %s\n",
144             check->target, check->module, check->name, varbuff);
145       nresults++;
146       continue;
147     }
148    
149 #define SETM(a,b) noit_stats_set_metric(&current, \
150                                         info->oids[oid_idx].confname, a, b)
151     switch(vars->type) {
152       case ASN_OCTET_STR:
153         sp = malloc(1 + vars->val_len);
154         memcpy(sp, vars->val.string, vars->val_len);
155         sp[vars->val_len] = '\0';
156         SETM(METRIC_STRING, sp);
157         free(sp);
158         break;
159       case ASN_INTEGER:
160       case ASN_GAUGE:
161         SETM(METRIC_INT32, vars->val.integer);
162         break;
163       case ASN_TIMETICKS:
164       case ASN_COUNTER:
165         SETM(METRIC_UINT32, vars->val.integer);
166         break;
167       case ASN_INTEGER64:
168         printI64(varbuff, vars->val.counter64);
169         i64 = strtoll(varbuff, &endptr, 10);
170         SETM(METRIC_INT64, (varbuff == endptr) ? NULL : &i64);
171         break;
172       case ASN_COUNTER64:
173         printU64(varbuff, vars->val.counter64);
174         u64 = strtoull(varbuff, &endptr, 10);
175         SETM(METRIC_UINT64, (varbuff == endptr) ? NULL : &u64);
176         break;
177       case ASN_FLOAT:
178         if(vars->val.floatVal) float_conv = *(vars->val.floatVal);
179         SETM(METRIC_DOUBLE, vars->val.floatVal ? &float_conv : NULL);
180         break;
181       case ASN_DOUBLE:
182         SETM(METRIC_DOUBLE, vars->val.doubleVal);
183         break;
184       default:
185         snprint_variable(varbuff, sizeof(varbuff), vars->name, vars->name_length, vars);
186         printf("%s!\n", varbuff);
187         /* Advance passed the first space and use that unless there
188          * is no space or we have no more string left.
189          */
190         sp = strchr(varbuff, ' ');
191         if(sp) sp++;
192         SETM(METRIC_STRING, (sp && *sp) ? sp : NULL);
193     }
194     nresults++;
195   }
196   noit_check_set_stats(self, check, &current);
197 }
198
199 struct target_session *
200 _get_target_session(char *target) {
201   struct target_session *ts;
202   if(!noit_hash_retrieve(&target_sessions,
203                          target, strlen(target), (void **)&ts)) {
204     ts = calloc(1, sizeof(*ts));
205     ts->fd = -1;
206     ts->refcnt = 0;
207     ts->in_table = 1;
208     noit_hash_store(&target_sessions,
209                     strdup(target), strlen(target), ts);
210   }
211   return ts;
212 }
213
214 static int noit_snmp_session_cleanse(struct target_session *ts) {
215   if(ts->refcnt == 0 && ts->sess_handle) {
216     eventer_remove_fd(ts->fd);
217     if(ts->timeoutevent) eventer_remove(ts->timeoutevent);
218     ts->timeoutevent = NULL;
219     snmp_sess_close(ts->sess_handle);
220     ts->sess_handle = NULL;
221     if(!ts->in_table) {
222       free(ts);
223     }
224     return 1;
225   }
226   return 0;
227 }
228
229 static int noit_snmp_session_timeout(eventer_t e, int mask, void *closure,
230                                      struct timeval *now) {
231   struct target_session *ts = closure;
232   snmp_sess_timeout(ts->sess_handle);
233   noit_snmp_session_cleanse(ts);
234   return 0;
235 }
236
237 static int noit_snmp_check_timeout(eventer_t e, int mask, void *closure,
238                                    struct timeval *now) {
239   struct check_info *info = closure;
240   info->timedout = 1;
241   remove_check(info);
242   /* Log our findings */
243   noit_snmp_log_results(info->self, info->check, NULL);
244   info->check->flags &= ~NP_RUNNING;
245   return 0;
246 }
247
248 static void _set_ts_timeout(struct target_session *ts, struct timeval *t) {
249   struct timeval now;
250   eventer_t e = NULL;
251   if(ts->timeoutevent) e = eventer_remove(ts->timeoutevent);
252   ts->timeoutevent = NULL;
253   if(!t) return;
254
255   gettimeofday(&now, NULL);
256   if(!e) e = eventer_alloc();
257   e->callback = noit_snmp_session_timeout;
258   e->closure = ts;
259   e->mask = EVENTER_TIMER;
260   add_timeval(now, *t, &e->whence);
261   ts->timeoutevent = e;
262   eventer_add(e);
263 }
264
265 static int noit_snmp_handler(eventer_t e, int mask, void *closure,
266                              struct timeval *now) {
267   fd_set fdset;
268   int fds, block = 0;
269   struct timeval timeout;
270   struct target_session *ts = closure;
271   FD_ZERO(&fdset);
272   FD_SET(e->fd, &fdset);
273   fds = e->fd + 1;
274   snmp_sess_read(ts->sess_handle, &fdset);
275   if(noit_snmp_session_cleanse(ts))
276     return 0;
277   snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
278   _set_ts_timeout(ts, block ? &timeout : NULL);
279   return EVENTER_READ | EVENTER_EXCEPTION;
280 }
281 static int noit_snmp_asynch_response(int operation, struct snmp_session *sp,
282                                      int reqid, struct snmp_pdu *pdu,
283                                      void *magic) {
284   struct check_info *info;
285   struct target_session *ts = magic;
286
287   /* We don't deal with refcnt hitting zero here.  We could only be hit from
288    * the snmp read/timeout stuff.  Handle it there.
289    */
290   ts->refcnt--;
291
292   info = get_check(reqid);
293   if(!info) return 1;
294   remove_check(info);
295   if(info->timeoutevent) {
296     eventer_remove(info->timeoutevent);
297     eventer_free(info->timeoutevent);
298     info->timeoutevent = NULL;
299   }
300
301   /* Log our findings */
302   noit_snmp_log_results(info->self, info->check, pdu);
303   info->check->flags &= ~NP_RUNNING;
304   return 1;
305 }
306
307 static void noit_snmp_sess_open(struct target_session *ts,
308                                 noit_check_t *check) {
309   const char *community;
310   struct snmp_session sess;
311   snmp_sess_init(&sess);
312   sess.version = SNMP_VERSION_2c;
313   sess.peername = check->target;
314   if(!noit_hash_retrieve(check->config, "community", strlen("community"),
315                          (void **)&community)) {
316     community = "public";
317   }
318   sess.community = (unsigned char *)community;
319   sess.community_len = strlen(community);
320   sess.callback = noit_snmp_asynch_response;
321   sess.callback_magic = ts;
322   ts->sess_handle = snmp_sess_open(&sess);
323 }
324
325 static int noit_snmp_fill_req(struct snmp_pdu *req, noit_check_t *check) {
326   int i, klen;
327   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
328   const char *name, *value;
329   struct check_info *info = check->closure;
330   noit_hash_table check_attrs_hash = NOIT_HASH_EMPTY;
331
332   /* Toss the old set and bail if we have zero */
333   if(info->oids) {
334     for(i=0; i<info->noids;i++) {
335       if(info->oids[i].confname) free(info->oids[i].confname);
336       if(info->oids[i].oidname) free(info->oids[i].oidname);
337     }
338     free(info->oids);
339   }
340   info->noids = 0;
341   info->oids = NULL;
342
343   /* Figure our how many. */
344   while(noit_hash_next(check->config, &iter, &name, &klen, (void **)&value)) {
345     if(!strncasecmp(name, "oid_", 4)) {
346       info->noids++;
347     }
348   }
349
350   if(info->noids == 0) return 0;
351
352   /* Create a hash of important check attributes */
353   noit_check_make_attrs(check, &check_attrs_hash);
354 #define CA_STORE(a,b) noit_hash_store(&check_attrs_hash, a, strlen(a), b)
355   CA_STORE("target", check->target);
356   CA_STORE("name", check->name);
357   CA_STORE("module", check->module);
358
359   /* Fill out the new set of required oids */
360   info->oids = calloc(info->noids, sizeof(*info->oids));
361   memset(&iter, 0, sizeof(iter));
362   i = 0;
363   while(noit_hash_next(check->config, &iter, &name, &klen, (void **)&value)) {
364     if(!strncasecmp(name, "oid_", 4)) {
365       char oidbuff[128];
366       name += 4;
367       info->oids[i].confname = strdup(name);
368       noit_check_interpolate(oidbuff, sizeof(oidbuff), value,
369                              &check_attrs_hash, check->config);
370       info->oids[i].oidname = strdup(oidbuff);
371       info->oids[i].oidlen = MAX_OID_LEN;
372       get_node(oidbuff, info->oids[i].oid, &info->oids[i].oidlen);
373       snmp_add_null_var(req, info->oids[i].oid, info->oids[i].oidlen);
374       i++;
375     }
376   }
377   assert(info->noids == i);
378   noit_hash_destroy(&check_attrs_hash, NULL, NULL);
379   return info->noids;
380 }
381 static int noit_snmp_send(noit_module_t *self, noit_check_t *check) {
382   struct snmp_pdu *req;
383   struct target_session *ts;
384   struct check_info *info = check->closure;
385
386   info->self = self;
387   info->check = check;
388   info->timedout = 0;
389
390   check->flags |= NP_RUNNING;
391   ts = _get_target_session(check->target);
392   gettimeofday(&check->last_fire_time, NULL);
393   if(!ts->refcnt) {
394     eventer_t newe;
395     int fds, block;
396     struct timeval timeout;
397     fd_set fdset;
398     noit_snmp_sess_open(ts, check);
399     block = 0;
400     fds = 0;
401     FD_ZERO(&fdset);
402     snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
403     assert(fds > 0);
404     ts->fd = fds-1;
405     newe = eventer_alloc();
406     newe->fd = ts->fd;
407     newe->callback = noit_snmp_handler;
408     newe->closure = ts;
409     newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
410     eventer_add(newe);
411   }
412   if(!ts->sess_handle) {
413     /* Error */
414     /* No need to do anything, this will be handled in the else below */
415   }
416   ts->refcnt++; /* Increment here, decrement when this check completes */
417
418   req = snmp_pdu_create(SNMP_MSG_GET);
419   noit_snmp_fill_req(req, check);
420   /* Setup out snmp requests */
421   if(ts->sess_handle &&
422      (info->reqid = snmp_sess_send(ts->sess_handle, req)) != 0) {
423     struct timeval when, to;
424     info->timeoutevent = eventer_alloc();
425     info->timeoutevent->callback = noit_snmp_check_timeout;
426     info->timeoutevent->closure = info;
427     info->timeoutevent->mask = EVENTER_TIMER;
428
429     gettimeofday(&when, NULL);
430     to.tv_sec = check->timeout / 1000;
431     to.tv_usec = (check->timeout % 1000) * 1000;
432     add_timeval(when, to, &info->timeoutevent->whence);
433     eventer_add(info->timeoutevent);
434     add_check(info);
435   }
436   else {
437     ts->refcnt--;
438      noit_snmp_session_cleanse(ts);
439     /* Error */
440     snmp_free_pdu(req);
441     /* Log our findings */
442     noit_snmp_log_results(self, check, NULL);
443     check->flags &= ~NP_RUNNING;
444   }
445   return 0;
446 }
447
448 static int noit_snmp_initiate_check(noit_module_t *self, noit_check_t *check,
449                                     int once, noit_check_t *cause) {
450   if(!check->closure) check->closure = calloc(1, sizeof(struct check_info));
451   INITIATE_CHECK(noit_snmp_send, self, check);
452   return 0;
453 }
454
455 static int noit_snmp_config(noit_module_t *self, noit_hash_table *config) {
456   return 0;
457 }
458 static int noit_snmp_onload(noit_module_t *self) {
459   nlerr = noit_log_stream_find("error/snmp");
460   nldeb = noit_log_stream_find("debug/snmp");
461   if(!nlerr) nlerr = noit_stderr;
462   if(!nldeb) nldeb = noit_debug;
463   eventer_name_callback("noit_snmp/check_timeout", noit_snmp_check_timeout);
464   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
465   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
466   return 0;
467 }
468 noit_module_t snmp = {
469   NOIT_MODULE_MAGIC,
470   NOIT_MODULE_ABI_VERSION,
471   "snmp",
472   "SNMP collection",
473   noit_snmp_onload,
474   noit_snmp_config,
475   noit_snmp_init,
476   noit_snmp_initiate_check,
477   NULL /* noit_snmp_cleanup */
478 };
479
Note: See TracBrowser for help on using the browser.