root/src/modules/snmp.c

Revision 327712134f74d1a3209f8beee3b0f593e721c8f6, 14.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

cleanup solaris bits, refs #32

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