root/src/modules/snmp.c

Revision 4ed37cf09ac9817ced9312616da97a3a1e90c6b3, 42.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 months ago)

cleanup of modules, verbose structure setting

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <math.h>
40 #include <ctype.h>
41
42 #include <net-snmp/net-snmp-config.h>
43 #include <net-snmp/net-snmp-includes.h>
44
45 #include "noit_module.h"
46 #include "noit_check.h"
47 #include "noit_check_tools.h"
48 #include "utils/noit_log.h"
49 #include "utils/noit_hash.h"
50
51 static noit_log_stream_t nlerr = NULL;
52 static noit_log_stream_t nldeb = NULL;
53 static int __snmp_initialize_once = 0;
54
55 #define SNMPV2_TRAPS_PREFIX     SNMP_OID_SNMPMODULES,1,1,5
56 oid trap_prefix[]    = { SNMPV2_TRAPS_PREFIX };
57 oid cold_start_oid[] = { SNMPV2_TRAPS_PREFIX, 1 };  /* SNMPv2-MIB */
58 oid warm_start_oid[] = { SNMPV2_TRAPS_PREFIX, 2 };  /* SNMPv2-MIB */
59 oid link_down_oid[]  = { SNMPV2_TRAPS_PREFIX, 3 };  /* IF-MIB */
60 oid link_up_oid[]    = { SNMPV2_TRAPS_PREFIX, 4 };  /* IF-MIB */
61 oid auth_fail_oid[]  = { SNMPV2_TRAPS_PREFIX, 5 };  /* SNMPv2-MIB */
62 oid egp_xxx_oid[]    = { SNMPV2_TRAPS_PREFIX, 99 }; /* ??? */
63
64 #define SNMPV2_TRAP_OBJS_PREFIX SNMP_OID_SNMPMODULES,1,1,4
65 oid snmptrap_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 1, 0 };
66 size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid);
67 oid snmptrapenterprise_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 3, 0 };
68 size_t snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid);
69 oid sysuptime_oid[] = { SNMP_OID_MIB2, 1, 3, 0 };
70 size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid);
71
72 #define SNMPV2_COMM_OBJS_PREFIX SNMP_OID_SNMPMODULES,18,1
73 oid agentaddr_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 3, 0 };
74 size_t agentaddr_oid_len = OID_LENGTH(agentaddr_oid);
75 oid community_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 4, 0 };
76 size_t community_oid_len = OID_LENGTH(community_oid);
77
78 #define RECONNOITER_PREFIX     SNMP_OID_ENTERPRISES,32863,1
79 oid reconnoiter_oid[] = { RECONNOITER_PREFIX };
80 size_t reconnoiter_oid_len = OID_LENGTH(reconnoiter_oid);
81 oid reconnoiter_check_prefix_oid[] = { RECONNOITER_PREFIX,1,1 };
82 size_t reconnoiter_check_prefix_oid_len =
83   OID_LENGTH(reconnoiter_check_prefix_oid);
84 size_t reconnoiter_check_oid_len = OID_LENGTH(reconnoiter_check_prefix_oid) + 8;
85 oid reconnoiter_metric_prefix_oid[] = { RECONNOITER_PREFIX,1,2 };
86 size_t reconnoiter_metric_prefix_oid_len =
87   OID_LENGTH(reconnoiter_metric_prefix_oid);
88
89 oid reconnoiter_check_status_oid[] = { RECONNOITER_PREFIX,1,3};
90 size_t reconnoiter_check_status_oid_len =
91   OID_LENGTH(reconnoiter_check_status_oid);
92 oid reconnoiter_check_state_oid[] = { RECONNOITER_PREFIX,1,3,1};
93 size_t reconnoiter_check_state_oid_len =
94   OID_LENGTH(reconnoiter_check_state_oid);
95 oid reconnoiter_check_state_unknown_oid[] = { RECONNOITER_PREFIX,1,3,1,0};
96 oid reconnoiter_check_state_good_oid[] = { RECONNOITER_PREFIX,1,3,1,1};
97 oid reconnoiter_check_state_bad_oid[] = { RECONNOITER_PREFIX,1,3,1,2};
98 size_t reconnoiter_check_state_val_len =
99   OID_LENGTH(reconnoiter_check_state_unknown_oid);
100 /* Boolean */
101 oid reconnoiter_check_available_oid[] = { RECONNOITER_PREFIX,1,3,2};
102 size_t reconnoiter_check_available_oid_len =
103   OID_LENGTH(reconnoiter_check_available_oid);
104 oid reconnoiter_check_available_unknown_oid[] = { RECONNOITER_PREFIX,1,3,2,0};
105 oid reconnoiter_check_available_yes_oid[] = { RECONNOITER_PREFIX,1,3,2,1};
106 oid reconnoiter_check_available_no_oid[] = { RECONNOITER_PREFIX,1,3,2,2};
107 size_t reconnoiter_check_available_val_len =
108   OID_LENGTH(reconnoiter_check_available_unknown_oid);
109 /* timeticks? gauge/unsigned? */
110 oid reconnoiter_check_duration_oid[] = { RECONNOITER_PREFIX,1,3,3};
111 size_t reconnoiter_check_duration_oid_len =
112   OID_LENGTH(reconnoiter_check_duration_oid);
113 /* string */
114 oid reconnoiter_check_status_msg_oid[] = { RECONNOITER_PREFIX,1,3,4};
115 size_t reconnoiter_check_status_msg_oid_len =
116   OID_LENGTH(reconnoiter_check_status_msg_oid);
117
118 typedef struct _mod_config {
119   noit_hash_table *options;
120   noit_hash_table target_sessions;
121 } snmp_mod_config_t;
122
123 struct target_session {
124   void *sess_handle;
125   noit_module_t *self;
126   char *key;
127   char *target;
128   eventer_t timeoutevent;
129   int version;
130   int fd;
131   int in_table;
132   int refcnt;
133   struct timeval last_open;
134 };
135
136 struct snmp_check_closure {
137   noit_module_t *self;
138   noit_check_t *check;
139 };
140
141 struct check_info {
142   int reqid;
143   int timedout;
144   struct {
145      char *confname;
146      char *oidname;
147      oid oid[MAX_OID_LEN];
148      size_t oidlen;
149      metric_type_t type_override;
150      noit_boolean type_should_override;
151   } *oids;
152   int noids;
153   eventer_t timeoutevent;
154   noit_module_t *self;
155   noit_check_t *check;
156   struct target_session *ts;
157   int version;
158 };
159
160 /* We hold struct check_info's in there key's by their reqid.
161  *   If they timeout, we remove them.
162  *
163  *   When SNMP queries complete, we look them up, if we find them
164  *   then we know we can remove the timeout and  complete the check.
165  *   If we don't find them, the timeout fired and removed the check.
166  */
167 noit_hash_table active_checks = NOIT_HASH_EMPTY;
168 static void add_check(struct check_info *c) {
169   noit_hash_store(&active_checks, (char *)&c->reqid, sizeof(c->reqid), c);
170 }
171 static struct check_info *get_check(int reqid) {
172   void *vc;
173   if(noit_hash_retrieve(&active_checks, (char *)&reqid, sizeof(reqid), &vc))
174     return (struct check_info *)vc;
175   return NULL;
176 }
177 static void remove_check(struct check_info *c) {
178   noit_hash_delete(&active_checks, (char *)&c->reqid, sizeof(c->reqid),
179                    NULL, NULL);
180 }
181
182 struct target_session *
183 _get_target_session(noit_module_t *self, char *target, int version) {
184   char key[128];
185   void *vts;
186   struct target_session *ts;
187   snmp_mod_config_t *conf;
188   conf = noit_module_get_userdata(self);
189   snprintf(key, sizeof(key), "%s:v%d", target, version);
190   if(!noit_hash_retrieve(&conf->target_sessions,
191                          key, strlen(key), &vts)) {
192     ts = calloc(1, sizeof(*ts));
193     ts->self = self;
194     ts->version = version;
195     ts->fd = -1;
196     ts->refcnt = 0;
197     ts->target = strdup(target);
198     ts->key = strdup(key);
199     ts->in_table = 1;
200     noit_hash_store(&conf->target_sessions,
201                     ts->key, strlen(ts->key), ts);
202     vts = ts;
203   }
204   return (struct target_session *)vts;
205 }
206
207 /* Handling of results */
208 static void noit_snmp_log_results(noit_module_t *self, noit_check_t *check,
209                                   struct snmp_pdu *pdu) {
210   struct check_info *info = check->closure;
211   struct variable_list *vars;
212   struct timeval duration;
213   char buff[128];
214   int nresults = 0;
215
216   noit_check_stats_clear(check, &check->stats.inprogress);
217
218   if(pdu)
219     for(vars = pdu->variables; vars; vars = vars->next_variable)
220       nresults++;
221
222   gettimeofday(&check->stats.inprogress.whence, NULL);
223   sub_timeval(check->stats.inprogress.whence, check->last_fire_time, &duration);
224   check->stats.inprogress.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
225   check->stats.inprogress.available = pdu ? NP_AVAILABLE : NP_UNAVAILABLE;
226   check->stats.inprogress.state = (nresults == info->noids) ? NP_GOOD : NP_BAD;
227   snprintf(buff, sizeof(buff), "%d/%d gets", nresults, info->noids);
228   check->stats.inprogress.status = buff;
229
230   /* We have no results over which to iterate. */
231   if(!pdu) {
232     noit_check_set_stats(check, &check->stats.inprogress);
233     noit_check_stats_clear(check, &check->stats.inprogress);
234     return;
235   }
236
237   /* manipulate the information ourselves */
238   nresults = 0;
239   for(vars = pdu->variables; vars; vars = vars->next_variable) {
240     char *sp;
241     int oid_idx;
242     double float_conv;
243     u_int64_t u64;
244     int64_t i64;
245     char *endptr;
246     char varbuff[256];
247
248     /* find the oid to which this is the response */
249     oid_idx = nresults; /* our check->stats.inprogress idx is the most likely */
250     if(info->oids[oid_idx].oidlen != vars->name_length ||
251        memcmp(info->oids[oid_idx].oid, vars->name,
252               vars->name_length * sizeof(oid))) {
253       /* Not the most obvious guess */
254       for(oid_idx = info->noids - 1; oid_idx >= 0; oid_idx--) {
255         if(info->oids[oid_idx].oidlen == vars->name_length &&
256            memcmp(info->oids[oid_idx].oid, vars->name,
257                   vars->name_length * sizeof(oid))) break;
258       }
259     }
260     if(oid_idx < 0) {
261       snprint_variable(varbuff, sizeof(varbuff),
262                        vars->name, vars->name_length, vars);
263       noitL(nlerr, "Unexpected oid results to %s`%s`%s: %s\n",
264             check->target, check->module, check->name, varbuff);
265       nresults++;
266       continue;
267     }
268
269 #define SETM(a,b) noit_stats_set_metric(check, &check->stats.inprogress, \
270                                         info->oids[oid_idx].confname, a, b)
271     if(info->oids[oid_idx].type_should_override) {
272       snprint_value(varbuff, sizeof(varbuff), vars->name, vars->name_length, vars);
273       sp = strchr(varbuff, ' ');
274       if(sp) sp++;
275       noit_stats_set_metric_coerce(check, &check->stats.inprogress, info->oids[oid_idx].confname,
276                                    info->oids[oid_idx].type_override,
277                                    sp);
278     }
279     else {
280       switch(vars->type) {
281         case ASN_OCTET_STR:
282           sp = malloc(1 + vars->val_len);
283           memcpy(sp, vars->val.string, vars->val_len);
284           sp[vars->val_len] = '\0';
285           SETM(METRIC_STRING, sp);
286           free(sp);
287           break;
288         case ASN_INTEGER:
289         case ASN_GAUGE:
290           SETM(METRIC_INT32, vars->val.integer);
291           break;
292         case ASN_TIMETICKS:
293         case ASN_COUNTER:
294           SETM(METRIC_UINT32, vars->val.integer);
295           break;
296 #ifdef ASN_OPAQUE_I64
297         case ASN_OPAQUE_I64:
298 #endif
299         case ASN_INTEGER64:
300           printI64(varbuff, vars->val.counter64);
301           i64 = strtoll(varbuff, &endptr, 10);
302           SETM(METRIC_INT64, (varbuff == endptr) ? NULL : &i64);
303           break;
304 #ifdef ASN_OPAQUE_U64
305         case ASN_OPAQUE_U64:
306 #endif
307 #ifdef ASN_OPAQUE_COUNTER64
308         case ASN_OPAQUE_COUNTER64:
309 #endif
310         case ASN_COUNTER64:
311           printU64(varbuff, vars->val.counter64);
312           u64 = strtoull(varbuff, &endptr, 10);
313           SETM(METRIC_UINT64, (varbuff == endptr) ? NULL : &u64);
314           break;
315 #ifdef ASN_OPAQUE_FLOAT
316         case ASN_OPAQUE_FLOAT:
317 #endif
318         case ASN_FLOAT:
319           if(vars->val.floatVal) float_conv = *(vars->val.floatVal);
320           SETM(METRIC_DOUBLE, vars->val.floatVal ? &float_conv : NULL);
321           break;
322 #ifdef ASN_OPAQUE_DOUBLE
323         case ASN_OPAQUE_DOUBLE:
324 #endif
325         case ASN_DOUBLE:
326           SETM(METRIC_DOUBLE, vars->val.doubleVal);
327           break;
328         case SNMP_NOSUCHOBJECT:
329         case SNMP_NOSUCHINSTANCE:
330           SETM(METRIC_STRING, NULL);
331           break;
332         default:
333           snprint_variable(varbuff, sizeof(varbuff), vars->name, vars->name_length, vars);
334           /* Advance passed the first space and use that unless there
335            * is no space or we have no more string left.
336            */
337           sp = strchr(varbuff, ' ');
338           if(sp) sp++;
339           SETM(METRIC_STRING, (sp && *sp) ? sp : NULL);
340           noitL(nlerr, "snmp: unknown type[%d] %s\n", vars->type, varbuff);
341       }
342     }
343     nresults++;
344   }
345   noit_check_set_stats(check, &check->stats.inprogress);
346   noit_check_stats_clear(check, &check->stats.inprogress);
347 }
348
349 static int noit_snmp_session_cleanse(struct target_session *ts,
350                                      int needs_free) {
351   if(ts->refcnt == 0 && ts->sess_handle) {
352     eventer_t e = eventer_remove_fd(ts->fd);
353     if(needs_free) eventer_free(e);
354     ts->fd = -1;
355     if(ts->timeoutevent) {
356       eventer_remove(ts->timeoutevent);
357       ts->timeoutevent = NULL;
358     }
359     snmp_sess_close(ts->sess_handle);
360     ts->sess_handle = NULL;
361     if(!ts->in_table) {
362       free(ts);
363     }
364     return 1;
365   }
366   return 0;
367 }
368
369 static int noit_snmp_session_timeout(eventer_t e, int mask, void *closure,
370                                      struct timeval *now) {
371   struct target_session *ts = closure;
372   snmp_sess_timeout(ts->sess_handle);
373   noit_snmp_session_cleanse(ts, 1);
374   if(ts->timeoutevent == e)
375     ts->timeoutevent = NULL; /* this will be freed on return */
376   return 0;
377 }
378
379 static int noit_snmp_check_timeout(eventer_t e, int mask, void *closure,
380                                    struct timeval *now) {
381   struct check_info *info = closure;
382   info->timedout = 1;
383   if(info->ts) {
384     info->ts->refcnt--;
385     noit_snmp_session_cleanse(info->ts, 1);
386     info->ts = NULL;
387   }
388   remove_check(info);
389   /* Log our findings */
390   noit_snmp_log_results(info->self, info->check, NULL);
391   info->check->flags &= ~NP_RUNNING;
392   return 0;
393 }
394
395 static void _set_ts_timeout(struct target_session *ts, struct timeval *t) {
396   struct timeval now;
397   eventer_t e = NULL;
398   if(ts->timeoutevent) {
399     e = eventer_remove(ts->timeoutevent);
400     ts->timeoutevent = NULL;
401   }
402   if(!t) return;
403
404   gettimeofday(&now, NULL);
405   if(!e) e = eventer_alloc();
406   e->callback = noit_snmp_session_timeout;
407   e->closure = ts;
408   e->mask = EVENTER_TIMER;
409   add_timeval(now, *t, &e->whence);
410   ts->timeoutevent = e;
411   eventer_add(e);
412 }
413
414 static int noit_snmp_handler(eventer_t e, int mask, void *closure,
415                              struct timeval *now) {
416   fd_set fdset;
417   int fds, block = 0;
418   struct timeval timeout = { 0, 0 };
419   struct target_session *ts = closure;
420   FD_ZERO(&fdset);
421   FD_SET(e->fd, &fdset);
422   fds = e->fd + 1;
423   snmp_sess_read(ts->sess_handle, &fdset);
424   if(noit_snmp_session_cleanse(ts, 0))
425     return 0;
426   snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
427   _set_ts_timeout(ts, block ? &timeout : NULL);
428   return EVENTER_READ | EVENTER_EXCEPTION;
429 }
430
431 /* This 'convert_v1pdu_to_v2' was cribbed directly from netsnmp */
432 static netsnmp_pdu *
433 convert_v1pdu_to_v2( netsnmp_pdu* template_v1pdu ) {
434   netsnmp_pdu *template_v2pdu;
435   netsnmp_variable_list *var;
436   oid enterprise[MAX_OID_LEN];
437   size_t enterprise_len;
438
439   /*
440    * Make a copy of the v1 Trap PDU
441    *   before starting to convert this
442    *   into a v2 Trap PDU.
443    */
444   template_v2pdu = snmp_clone_pdu( template_v1pdu);
445   if(!template_v2pdu) {
446     snmp_log(LOG_WARNING,
447              "send_trap: failed to copy v2 template PDU\n");
448     return NULL;
449   }
450   template_v2pdu->command = SNMP_MSG_TRAP2;
451
452   /*
453    * Insert an snmpTrapOID varbind before the original v1 varbind list
454    *   either using one of the standard defined trap OIDs,
455    *   or constructing this from the PDU enterprise & specific trap fields
456    */
457   if(template_v1pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
458     if(template_v1pdu->enterprise_length + 2 > MAX_OID_LEN) {
459       noitL(nlerr, "send_trap: enterprise_length too large\n");
460       snmp_free_pdu(template_v2pdu);
461       return NULL;
462     }
463     memcpy(enterprise, template_v1pdu->enterprise,
464            template_v1pdu->enterprise_length*sizeof(oid));
465     enterprise_len = template_v1pdu->enterprise_length;
466     enterprise[enterprise_len++] = 0;
467     enterprise[enterprise_len++] = template_v1pdu->specific_type;
468   } else {
469     memcpy(enterprise, cold_start_oid, sizeof(cold_start_oid));
470     enterprise[9]  = template_v1pdu->trap_type+1;
471     enterprise_len = sizeof(cold_start_oid)/sizeof(oid);
472   }
473
474   var = NULL;
475   if(!snmp_varlist_add_variable(&var,
476                                 snmptrap_oid, snmptrap_oid_len,
477                                 ASN_OBJECT_ID,
478                                 (u_char*)enterprise,
479                                 enterprise_len*sizeof(oid))) {
480     noitL(nlerr, "send_trap: failed to insert copied snmpTrapOID varbind\n");
481     snmp_free_pdu(template_v2pdu);
482     return NULL;
483   }
484   var->next_variable        = template_v2pdu->variables;
485   template_v2pdu->variables = var;
486
487   /*
488    * Insert a sysUptime varbind at the head of the v2 varbind list
489    */
490   var = NULL;
491   if(!snmp_varlist_add_variable(&var,
492                                 sysuptime_oid, sysuptime_oid_len,
493                                 ASN_TIMETICKS,
494                                 (u_char*)&(template_v1pdu->time),
495                                 sizeof(template_v1pdu->time))) {
496     noitL(nlerr, "send_trap: failed to insert copied sysUptime varbind\n");
497     snmp_free_pdu(template_v2pdu);
498     return NULL;
499   }
500   var->next_variable = template_v2pdu->variables;
501   template_v2pdu->variables = var;
502
503   /*
504    * Append the other three conversion varbinds,
505    *  (snmpTrapAgentAddr, snmpTrapCommunity & snmpTrapEnterprise)
506    *  if they're not already present.
507    *  But don't bomb out completely if there are problems.
508    */
509   var = find_varbind_in_list(template_v2pdu->variables,
510                              agentaddr_oid, agentaddr_oid_len);
511   if(!var && (template_v1pdu->agent_addr[0]
512               || template_v1pdu->agent_addr[1]
513               || template_v1pdu->agent_addr[2]
514               || template_v1pdu->agent_addr[3])) {
515     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
516                                   agentaddr_oid, agentaddr_oid_len,
517                                   ASN_IPADDRESS,
518                                   (u_char*)&(template_v1pdu->agent_addr),
519                                   sizeof(template_v1pdu->agent_addr)))
520       noitL(nlerr, "send_trap: failed to append snmpTrapAddr varbind\n");
521   }
522   var = find_varbind_in_list(template_v2pdu->variables,
523                              community_oid, community_oid_len);
524   if(!var && template_v1pdu->community) {
525     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
526                                   community_oid, community_oid_len,
527                                   ASN_OCTET_STR,
528                                   template_v1pdu->community,
529                                   template_v1pdu->community_len))
530       noitL(nlerr, "send_trap: failed to append snmpTrapCommunity varbind\n");
531   }
532   var = find_varbind_in_list(template_v2pdu->variables,
533                              snmptrapenterprise_oid,
534                              snmptrapenterprise_oid_len);
535   if(!var &&
536      template_v1pdu->trap_type != SNMP_TRAP_ENTERPRISESPECIFIC) {
537     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
538                                   snmptrapenterprise_oid,
539                                   snmptrapenterprise_oid_len,
540                                   ASN_OBJECT_ID,
541                                   (u_char*)template_v1pdu->enterprise,
542                                   template_v1pdu->enterprise_length*sizeof(oid)))
543       noitL(nlerr, "send_trap: failed to append snmpEnterprise varbind\n");
544   }
545   return template_v2pdu;
546 }
547
548 static int noit_snmp_oid_to_checkid(oid *o, int l, uuid_t checkid, char *out) {
549   int i;
550   char _uuid_str[UUID_STR_LEN+1], *cp, *uuid_str;
551
552   uuid_str = out ? out : _uuid_str;
553   if(l != reconnoiter_check_oid_len) {
554     noitL(nlerr, "unsupported (length) trap recieved\n");
555     return -1;
556   }
557   if(netsnmp_oid_equals(o,
558                         reconnoiter_check_prefix_oid_len,
559                         reconnoiter_check_prefix_oid,
560                         reconnoiter_check_prefix_oid_len) != 0) {
561     noitL(nlerr, "unsupported (wrong namespace) trap recieved\n");
562     return -1;
563   }
564   /* encode this as a uuid */
565   cp = uuid_str;
566   for(i=0;
567       i < reconnoiter_check_oid_len - reconnoiter_check_prefix_oid_len;
568       i++) {
569     oid v = o[i + reconnoiter_check_prefix_oid_len];
570     if(v > 0xffff) {
571       noitL(nlerr, "trap target oid [%ld] out of range\n", (long int)v);
572       return -1;
573     }
574     snprintf(cp, 5, "%04x", (unsigned short)(v & 0xffff));
575     cp += 4;
576     /* hyphens after index 1,2,3,4 */
577     if(i > 0 && i < 5) *cp++ = '-';
578   }
579   if(uuid_parse(uuid_str, checkid) != 0) {
580     noitL(nlerr, "unexpected error decoding trap uuid '%s'\n", uuid_str);
581     return -1;
582   }
583   return 0;
584 }
585
586 #define isoid(a,b,c,d) (netsnmp_oid_equals(a,b,c,d) == 0)
587 #define isoidprefix(a,b,c,d) (netsnmp_oid_equals(a,MIN(b,d),c,d) == 0)
588 #define setstatus(st,soid,sv) \
589   if(isoid(o,l,soid,reconnoiter_check_state_val_len)) current->st = sv
590
591 static int
592 noit_snmp_trapvars_to_stats(noit_check_t *check, netsnmp_variable_list *var) {
593   stats_t *current = &check->stats.inprogress;
594   if(isoidprefix(var->name, var->name_length, reconnoiter_check_status_oid,
595                  reconnoiter_check_status_oid_len)) {
596     if(var->type == ASN_OBJECT_ID) {
597       if(isoid(var->name, var->name_length,
598                reconnoiter_check_state_oid, reconnoiter_check_state_oid_len)) {
599         oid *o = var->val.objid;
600         size_t l = var->val_len / sizeof(*o);
601         setstatus(state, reconnoiter_check_state_unknown_oid, NP_UNKNOWN);
602         else setstatus(state, reconnoiter_check_state_good_oid, NP_GOOD);
603         else setstatus(state, reconnoiter_check_state_bad_oid, NP_BAD);
604         else return -1;
605       }
606       else if(isoid(var->name, var->name_length,
607                     reconnoiter_check_available_oid,
608                     reconnoiter_check_available_oid_len)) {
609         oid *o = var->val.objid;
610         size_t l = var->val_len / sizeof(*o);
611         setstatus(available, reconnoiter_check_available_unknown_oid, NP_UNKNOWN);
612         else setstatus(available, reconnoiter_check_available_yes_oid, NP_AVAILABLE);
613         else setstatus(available, reconnoiter_check_available_no_oid, NP_UNAVAILABLE);
614         else return -1;
615       }
616       else {
617         /* We don't unerstand any other OBJECT_ID types */
618         return -1;
619       }
620     }
621     else if(var->type == ASN_UNSIGNED) {
622       /* This is only for the duration (in ms) */
623       if(isoid(var->name, var->name_length,
624                reconnoiter_check_duration_oid,
625                reconnoiter_check_duration_oid_len)) {
626         current->duration = *(var->val.integer);
627       }
628       else
629         return -1;
630     }
631     else if(var->type == ASN_OCTET_STR) {
632       /* This is only for the status message */
633       if(isoid(var->name, var->name_length,
634                reconnoiter_check_status_msg_oid,
635                reconnoiter_check_status_msg_oid_len)) {
636         current->status = malloc(var->val_len + 1);
637         memcpy(current->status, var->val.string, var->val_len);
638         current->status[var->val_len] = '\0';
639       }
640       else
641         return -1;
642     }
643     else {
644       /* I don't understand any other type of status message */
645       return -1;
646     }
647   }
648   else if(isoidprefix(var->name, var->name_length,
649                       reconnoiter_metric_prefix_oid,
650                       reconnoiter_metric_prefix_oid_len)) {
651     /* decode the metric and store the value */
652     int i, len;
653     u_int64_t u64;
654     double doubleVal;
655     char metric_name[128], buff[128], *cp;
656     if(var->name_length <= reconnoiter_metric_prefix_oid_len) return -1;
657     len = var->name[reconnoiter_metric_prefix_oid_len];
658     if(var->name_length != (reconnoiter_metric_prefix_oid_len + 1 + len) ||
659        len > sizeof(metric_name) - 1) {
660       noitL(nlerr, "snmp trap, malformed metric name\n");
661       return -1;
662     }
663     for(i=0;i<len;i++) {
664       ((unsigned char *)metric_name)[i] =
665         (unsigned char)var->name[reconnoiter_metric_prefix_oid_len + 1 + i];
666       if(!isprint(metric_name[i])) {
667         noitL(nlerr, "metric_name contains unprintable characters\n");
668         return -1;
669       }
670     }
671     metric_name[i] = '\0';
672     switch(var->type) {
673       case ASN_INTEGER:
674       case ASN_UINTEGER:
675       case ASN_TIMETICKS:
676       case ASN_INTEGER64:
677         noit_stats_set_metric(check, current, metric_name,
678                               METRIC_INT64, var->val.integer);
679         break;
680       case ASN_COUNTER64:
681         u64 = ((u_int64_t)var->val.counter64->high) << 32;
682         u64 |= var->val.counter64->low;
683         noit_stats_set_metric(check, current, metric_name,
684                               METRIC_UINT64, &u64);
685         break;
686       case ASN_OPAQUE_FLOAT:
687         doubleVal = (double)*var->val.floatVal;
688         noit_stats_set_metric(check, current, metric_name,
689                               METRIC_DOUBLE, &doubleVal);
690         break;
691       case ASN_OPAQUE_DOUBLE:
692         noit_stats_set_metric(check, current, metric_name,
693                               METRIC_DOUBLE, var->val.doubleVal);
694         break;
695       case ASN_OCTET_STR:
696         snprint_value(buff, sizeof(buff), var->name, var->name_length, var);
697         /* Advance passed the first space and use that unless there
698          * is no space or we have no more string left.
699          */
700         cp = strchr(buff, ' ');
701         if(cp) {
702           char *ecp;
703           cp++;
704           if(*cp == '"') {
705             ecp = cp + strlen(cp) - 1;
706             if(*ecp == '"') {
707               cp++; *ecp = '\0';
708             }
709           }
710         }
711         noit_stats_set_metric(check, current, metric_name,
712                               METRIC_STRING, (cp && *cp) ? cp : NULL);
713         break;
714       default:
715         noitL(nlerr, "snmp trap unsupport data type %d\n", var->type);
716     }
717     noitL(nldeb, "metric_name -> '%s'\n", metric_name);
718   }
719   else {
720     /* No idea what this is */
721     return -1;
722   }
723   return 0;
724 }
725 static int noit_snmp_trapd_response(int operation, struct snmp_session *sp,
726                                     int reqid, struct snmp_pdu *pdu,
727                                     void *magic) {
728   /* the noit pieces */
729   noit_check_t *check;
730   struct target_session *ts = magic;
731   snmp_mod_config_t *conf;
732   const char *community = NULL;
733   int success = 0;
734
735   /* parsing destination */
736   char uuid_str[UUID_STR_LEN + 1];
737   uuid_t checkid;
738
739   /* snmp oid parsing helper vars */
740   netsnmp_pdu *newpdu = pdu;
741   netsnmp_variable_list *var;
742
743   conf = noit_module_get_userdata(ts->self);
744
745   if(pdu->version == SNMP_VERSION_1)
746     newpdu = convert_v1pdu_to_v2(pdu);
747   if(!newpdu || newpdu->version != SNMP_VERSION_2c) goto cleanup;
748
749   for(var = newpdu->variables; var != NULL; var = var->next_variable) {
750     if(netsnmp_oid_equals(var->name, var->name_length,
751                           snmptrap_oid, snmptrap_oid_len) == 0)
752       break;
753   }
754
755   if (!var || var->type != ASN_OBJECT_ID) {
756     noitL(nlerr, "unsupport trap (not a trap?) received\n");
757     goto cleanup;
758   }
759
760   /* var is the oid on which we are trapping.
761    * It should be in the reconnoiter check prefix.
762    */
763   if(noit_snmp_oid_to_checkid(var->val.objid, var->val_len/sizeof(oid),
764                               checkid, uuid_str)) {
765     goto cleanup;
766   }
767   noitL(nldeb, "recieved trap for %s\n", uuid_str);
768   check = noit_poller_lookup(checkid);
769   if(!check) {
770     noitL(nlerr, "trap received for non-existent check '%s'\n", uuid_str);
771     goto cleanup;
772   }
773   if(!noit_hash_retr_str(check->config, "community", strlen("community"),
774                          &community) &&
775      !noit_hash_retr_str(conf->options, "community", strlen("community"),
776                          &community)) {
777     noitL(nlerr, "No community defined for check, dropping trap\n");
778     goto cleanup;
779   }
780
781   if(strlen(community) != newpdu->community_len ||
782      memcmp(community, newpdu->community, newpdu->community_len)) {
783     noitL(nlerr, "trap attempt with wrong community string\n");
784     goto cleanup;
785   }
786
787   /* We have a check. The trap is authorized. Now, extract everything. */
788   memset(&check->stats.inprogress, 0, sizeof(check->stats.inprogress));
789   gettimeofday(&check->stats.inprogress.whence, NULL);
790   check->stats.inprogress.available = NP_AVAILABLE;
791
792   /* Rate limit */
793   if(((check->stats.inprogress.whence.tv_sec * 1000 +
794        check->stats.inprogress.whence.tv_usec / 1000) -
795       (check->last_fire_time.tv_sec * 1000 +
796        check->last_fire_time.tv_usec / 1000)) < check->period) goto cleanup;
797
798   /* update the last fire time... */
799   gettimeofday(&check->last_fire_time, NULL);
800
801   for(; var != NULL; var = var->next_variable)
802     if(noit_snmp_trapvars_to_stats(check, var) == 0) success++;
803   if(success) {
804     char buff[24];
805     snprintf(buff, sizeof(buff), "%d datum", success);
806     check->stats.inprogress.state = NP_GOOD;
807     check->stats.inprogress.status = strdup(buff);
808   }
809   else {
810     check->stats.inprogress.state = NP_BAD;
811     check->stats.inprogress.status = strdup("no data");
812   }
813   noit_check_set_stats(check, &check->stats.inprogress);
814
815  cleanup:
816   if(newpdu != pdu)
817     snmp_free_pdu(newpdu);
818   return 0;
819 }
820 static int noit_snmp_asynch_response(int operation, struct snmp_session *sp,
821                                      int reqid, struct snmp_pdu *pdu,
822                                      void *magic) {
823   struct check_info *info;
824   /* We don't deal with refcnt hitting zero here.  We could only be hit from
825    * the snmp read/timeout stuff.  Handle it there.
826    */
827
828   info = get_check(reqid);
829   if(!info) return 1;
830   if(info->ts) {
831     info->ts->refcnt--;
832     info->ts = NULL;
833   }
834   remove_check(info);
835   if(info->timeoutevent) {
836     eventer_remove(info->timeoutevent);
837     eventer_free(info->timeoutevent);
838     info->timeoutevent = NULL;
839   }
840
841   /* Log our findings */
842   noit_snmp_log_results(info->self, info->check, pdu);
843   info->check->flags &= ~NP_RUNNING;
844   return 1;
845 }
846
847 static void noit_snmp_sess_open(struct target_session *ts,
848                                 noit_check_t *check) {
849   const char *community;
850   struct snmp_session sess;
851   struct check_info *info = check->closure;
852   snmp_sess_init(&sess);
853   sess.version = info->version;
854   sess.peername = ts->target;
855   if(!noit_hash_retr_str(check->config, "community", strlen("community"),
856                          &community)) {
857     community = "public";
858   }
859   sess.community = (unsigned char *)community;
860   sess.community_len = strlen(community);
861   sess.callback = noit_snmp_asynch_response;
862   sess.callback_magic = ts;
863   ts->sess_handle = snmp_sess_open(&sess);
864   gettimeofday(&ts->last_open, NULL);
865 }
866
867 static int noit_snmp_fill_req(struct snmp_pdu *req, noit_check_t *check) {
868   int i, klen;
869   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
870   const char *name, *value;
871   struct check_info *info = check->closure;
872   noit_hash_table check_attrs_hash = NOIT_HASH_EMPTY;
873
874   /* Toss the old set and bail if we have zero */
875   if(info->oids) {
876     for(i=0; i<info->noids;i++) {
877       if(info->oids[i].confname) free(info->oids[i].confname);
878       if(info->oids[i].oidname) free(info->oids[i].oidname);
879     }
880     free(info->oids);
881   }
882   info->noids = 0;
883   info->oids = NULL;
884
885   /* Figure our how many. */
886   while(noit_hash_next_str(check->config, &iter, &name, &klen, &value)) {
887     if(!strncasecmp(name, "oid_", 4)) {
888       info->noids++;
889     }
890   }
891
892   if(info->noids == 0) return 0;
893
894   /* Create a hash of important check attributes */
895   noit_check_make_attrs(check, &check_attrs_hash);
896
897   /* Fill out the new set of required oids */
898   info->oids = calloc(info->noids, sizeof(*info->oids));
899   memset(&iter, 0, sizeof(iter));
900   i = 0;
901   while(noit_hash_next_str(check->config, &iter, &name, &klen, &value)) {
902     if(!strncasecmp(name, "oid_", 4)) {
903       const char *type_override;
904       char oidbuff[2048], typestr[256];
905       name += 4;
906       info->oids[i].confname = strdup(name);
907       noit_check_interpolate(oidbuff, sizeof(oidbuff), value,
908                              &check_attrs_hash, check->config);
909       info->oids[i].oidname = strdup(oidbuff);
910       info->oids[i].oidlen = MAX_OID_LEN;
911       if(oidbuff[0] == '.')
912         read_objid(oidbuff, info->oids[i].oid, &info->oids[i].oidlen);
913       else
914         get_node(oidbuff, info->oids[i].oid, &info->oids[i].oidlen);
915       snprintf(typestr, sizeof(typestr), "type_%s", name);
916       if(noit_hash_retr_str(check->config, typestr, strlen(typestr),
917                             &type_override)) {
918         int type_enum_fake = *type_override;
919
920         if(!strcasecmp(type_override, "guess"))
921           type_enum_fake = METRIC_GUESS;
922         else if(!strcasecmp(type_override, "int32"))
923           type_enum_fake = METRIC_INT32;
924         else if(!strcasecmp(type_override, "uint32"))
925           type_enum_fake = METRIC_UINT32;
926         else if(!strcasecmp(type_override, "int64"))
927           type_enum_fake = METRIC_INT64;
928         else if(!strcasecmp(type_override, "uint64"))
929           type_enum_fake = METRIC_UINT64;
930         else if(!strcasecmp(type_override, "double"))
931           type_enum_fake = METRIC_DOUBLE;
932         else if(!strcasecmp(type_override, "string"))
933           type_enum_fake = METRIC_STRING;
934
935         switch(type_enum_fake) {
936           case METRIC_GUESS:
937           case METRIC_INT32: case METRIC_UINT32:
938           case METRIC_INT64: case METRIC_UINT64:
939           case METRIC_DOUBLE: case METRIC_STRING:
940             info->oids[i].type_override = *type_override;
941             info->oids[i].type_should_override = noit_true;
942           default: break;
943         }
944       }
945       snmp_add_null_var(req, info->oids[i].oid, info->oids[i].oidlen);
946       i++;
947     }
948   }
949   assert(info->noids == i);
950   noit_hash_destroy(&check_attrs_hash, NULL, NULL);
951   return info->noids;
952 }
953 static int noit_snmp_send(noit_module_t *self, noit_check_t *check,
954                           noit_check_t *cause) {
955   struct snmp_pdu *req;
956   struct target_session *ts;
957   struct check_info *info = check->closure;
958   int port = 161;
959   const char *portstr, *versstr;
960   char target_port[64];
961
962   info->version = SNMP_VERSION_2c;
963   info->self = self;
964   info->check = check;
965   info->timedout = 0;
966
967   BAIL_ON_RUNNING_CHECK(check);
968   check->flags |= NP_RUNNING;
969
970   if(noit_hash_retr_str(check->config, "port", strlen("port"),
971                         &portstr)) {
972     port = atoi(portstr);
973   }
974   if(noit_hash_retr_str(check->config, "version", strlen("version"),
975                         &versstr)) {
976     /* We don't care about 2c or others... as they all default to 2c */
977     if(!strcmp(versstr, "1")) info->version = SNMP_VERSION_1;
978     if(!strcmp(versstr, "3")) info->version = SNMP_VERSION_3;
979   }
980   snprintf(target_port, sizeof(target_port), "%s:%d", check->target_ip, port);
981   ts = _get_target_session(self, target_port, info->version);
982   gettimeofday(&check->last_fire_time, NULL);
983   if(!ts->refcnt) {
984     eventer_t newe;
985     int fds, block;
986     struct timeval timeout;
987     fd_set fdset;
988     noit_snmp_sess_open(ts, check);
989     block = 0;
990     fds = 0;
991     FD_ZERO(&fdset);
992     snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
993     assert(fds > 0);
994     ts->fd = fds-1;
995     newe = eventer_alloc();
996     newe->fd = ts->fd;
997     newe->callback = noit_snmp_handler;
998     newe->closure = ts;
999     newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1000     eventer_add(newe);
1001   }
1002   if(!ts->sess_handle) {
1003     /* Error */
1004     /* No need to do anything, this will be handled in the else below */
1005   }
1006   ts->refcnt++; /* Increment here, decrement when this check completes */
1007
1008   req = snmp_pdu_create(SNMP_MSG_GET);
1009   if(req) {
1010     noit_snmp_fill_req(req, check);
1011     req->version = info->version;
1012   }
1013   /* Setup out snmp requests */
1014   if(ts->sess_handle && req &&
1015      (info->reqid = snmp_sess_send(ts->sess_handle, req)) != 0) {
1016     struct timeval when, to;
1017     info->ts = ts;
1018     info->timeoutevent = eventer_alloc();
1019     info->timeoutevent->callback = noit_snmp_check_timeout;
1020     info->timeoutevent->closure = info;
1021     info->timeoutevent->mask = EVENTER_TIMER;
1022
1023     noitL(nldeb, "Sending snmp get\n");
1024     gettimeofday(&when, NULL);
1025     to.tv_sec = check->timeout / 1000;
1026     to.tv_usec = (check->timeout % 1000) * 1000;
1027     add_timeval(when, to, &info->timeoutevent->whence);
1028     eventer_add(info->timeoutevent);
1029     add_check(info);
1030   }
1031   else {
1032     ts->refcnt--;
1033     noitL(nlerr, "Error sending snmp get request.\n");
1034     noit_snmp_session_cleanse(ts, 1);
1035     /* Error */
1036     if(req) snmp_free_pdu(req);
1037     /* Log our findings */
1038     noit_snmp_log_results(self, check, NULL);
1039     check->flags &= ~NP_RUNNING;
1040   }
1041   return 0;
1042 }
1043
1044 static int noit_snmp_initiate_check(noit_module_t *self, noit_check_t *check,
1045                                     int once, noit_check_t *cause) {
1046   if(!check->closure) check->closure = calloc(1, sizeof(struct check_info));
1047   INITIATE_CHECK(noit_snmp_send, self, check, cause);
1048   return 0;
1049 }
1050
1051 static int noit_snmptrap_initiate_check(noit_module_t *self,
1052                                         noit_check_t *check,
1053                                         int once, noit_check_t *cause) {
1054   /* We don't do anything for snmptrap checks.  Not intuitive... but they
1055    * never "run."  We accept input out-of-band via snmp traps.
1056    */
1057   check->flags |= NP_PASSIVE_COLLECTION;
1058   return 0;
1059 }
1060
1061 static int noit_snmp_config(noit_module_t *self, noit_hash_table *options) {
1062   snmp_mod_config_t *conf;
1063   conf = noit_module_get_userdata(self);
1064   if(conf) {
1065     if(conf->options) {
1066       noit_hash_destroy(conf->options, free, free);
1067       free(conf->options);
1068     }
1069   }
1070   else
1071     conf = calloc(1, sizeof(*conf));
1072   conf->options = options;
1073   noit_module_set_userdata(self, conf);
1074   return 1;
1075 }
1076 static int noit_snmp_onload(noit_image_t *self) {
1077   if(!nlerr) nlerr = noit_log_stream_find("error/snmp");
1078   if(!nldeb) nldeb = noit_log_stream_find("debug/snmp");
1079   if(!nlerr) nlerr = noit_stderr;
1080   if(!nldeb) nldeb = noit_debug;
1081   eventer_name_callback("noit_snmp/check_timeout", noit_snmp_check_timeout);
1082   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1083   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1084   return 0;
1085 }
1086
1087 static int noit_snmptrap_onload(noit_image_t *self) {
1088   if(!nlerr) nlerr = noit_log_stream_find("error/snmp");
1089   if(!nldeb) nldeb = noit_log_stream_find("debug/snmp");
1090   if(!nlerr) nlerr = noit_stderr;
1091   if(!nldeb) nldeb = noit_debug;
1092   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1093   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1094   return 0;
1095 }
1096
1097 static void
1098 nc_printf_snmpts_brief(noit_console_closure_t ncct,
1099                        struct target_session *ts) {
1100   char fd[32];
1101   struct timeval now, diff;
1102   const char *snmpvers = "v(unknown)";
1103   gettimeofday(&now, NULL);
1104   sub_timeval(now, ts->last_open, &diff);
1105   if(ts->fd < 0)
1106     snprintf(fd, sizeof(fd), "%s", "(closed)");
1107   else
1108     snprintf(fd, sizeof(fd), "%d", ts->fd);
1109   switch(ts->version) {
1110     case SNMP_VERSION_1: snmpvers = "v1"; break;
1111     case SNMP_VERSION_2c: snmpvers = "v2c"; break;
1112     case SNMP_VERSION_3: snmpvers = "v3"; break;
1113   }
1114   nc_printf(ncct, "[%s %s]\n\topened: %0.3fs ago\n\tFD: %s\n\trefcnt: %d\n",
1115             ts->target, snmpvers, diff.tv_sec + (float)diff.tv_usec/1000000,
1116             fd, ts->refcnt);
1117 }
1118
1119 static int
1120 noit_console_show_snmp(noit_console_closure_t ncct,
1121                        int argc, char **argv,
1122                        noit_console_state_t *dstate,
1123                        void *closure) {
1124   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
1125   uuid_t key_id;
1126   int klen;
1127   void *vts;
1128   snmp_mod_config_t *conf = closure;
1129
1130   while(noit_hash_next(&conf->target_sessions, &iter,
1131                        (const char **)key_id, &klen,
1132                        &vts)) {
1133     struct target_session *ts = vts;
1134     nc_printf_snmpts_brief(ncct, ts);
1135   }
1136   return 0;
1137 }
1138
1139 static void
1140 register_console_snmp_commands(snmp_mod_config_t *conf) {
1141   noit_console_state_t *tl;
1142   cmd_info_t *showcmd;
1143
1144   tl = noit_console_state_initial();
1145   showcmd = noit_console_state_get_cmd(tl, "show");
1146   assert(showcmd && showcmd->dstate);
1147   noit_console_state_add_cmd(showcmd->dstate,
1148     NCSCMD("snmp", noit_console_show_snmp, NULL, NULL, conf));
1149 }
1150
1151 static int noit_snmp_init(noit_module_t *self) {
1152   const char *opt;
1153   snmp_mod_config_t *conf;
1154
1155   conf = noit_module_get_userdata(self);
1156
1157   if(!__snmp_initialize_once) {
1158     register_mib_handlers();
1159     read_premib_configs();
1160     read_configs();
1161     init_snmp("noitd");
1162     __snmp_initialize_once = 1;
1163   }
1164   if(strcmp(self->hdr.name, "snmp") == 0) {
1165     register_console_snmp_commands(conf);
1166   }
1167
1168   if(strcmp(self->hdr.name, "snmptrap") == 0) {
1169     eventer_t newe;
1170     int i, block = 0, fds = 0;
1171     fd_set fdset;
1172     struct timeval timeout = { 0, 0 };
1173     struct target_session *ts;
1174     netsnmp_transport *transport;
1175     netsnmp_session sess, *session = &sess;
1176
1177     if(!noit_hash_retrieve(conf->options,
1178                            "snmptrapd_port", strlen("snmptrapd_port"),
1179                            (void **)&opt))
1180       opt = "162";
1181
1182     transport = netsnmp_transport_open_server("snmptrap", opt);
1183     if(!transport) {
1184       noitL(nlerr, "cannot open netsnmp transport for trap daemon\n");
1185       return -1;
1186     }
1187     ts = _get_target_session(self, "snmptrapd", SNMP_DEFAULT_VERSION);
1188     snmp_sess_init(session);
1189     session->peername = SNMP_DEFAULT_PEERNAME;
1190     session->version = SNMP_DEFAULT_VERSION;
1191     session->community_len = SNMP_DEFAULT_COMMUNITY_LEN;
1192     session->retries = SNMP_DEFAULT_RETRIES;
1193     session->timeout = SNMP_DEFAULT_TIMEOUT;
1194     session->callback = noit_snmp_trapd_response;
1195     session->callback_magic = (void *) ts;
1196     session->authenticator = NULL;
1197     session->isAuthoritative = SNMP_SESS_UNKNOWNAUTH;
1198     ts->sess_handle = snmp_sess_add(session, transport, NULL, NULL);
1199
1200     FD_ZERO(&fdset);
1201     snmp_sess_select_info(ts->sess_handle, &fds, &fdset, &timeout, &block);
1202     assert(fds > 0);
1203     for(i=0; i<fds; i++) {
1204       if(FD_ISSET(i, &fdset)) {
1205         ts->refcnt++;
1206         ts->fd = i;
1207         newe = eventer_alloc();
1208         newe->fd = ts->fd;
1209         newe->callback = noit_snmp_handler;
1210         newe->closure = ts;
1211         newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1212         eventer_add(newe);
1213       }
1214     }
1215   }
1216   return 0;
1217 }
1218
1219 #include "snmp.xmlh"
1220 noit_module_t snmp = {
1221   {
1222     .magic = NOIT_MODULE_MAGIC,
1223     .version = NOIT_MODULE_ABI_VERSION,
1224     .name = "snmp",
1225     .description = "SNMP collection",
1226     .xml_description = snmp_xml_description,
1227     .onload = noit_snmp_onload
1228   },
1229   noit_snmp_config,
1230   noit_snmp_init,
1231   noit_snmp_initiate_check,
1232   NULL /* noit_snmp_cleanup */
1233 };
1234
1235 #include "snmptrap.xmlh"
1236 noit_module_t snmptrap = {
1237   {
1238     .magic = NOIT_MODULE_MAGIC,
1239     .version = NOIT_MODULE_ABI_VERSION,
1240     .name = "snmptrap",
1241     .description = "SNMP trap collection",
1242     .xml_description = snmptrap_xml_description,
1243     .onload = noit_snmptrap_onload
1244   },
1245   noit_snmp_config,
1246   noit_snmp_init,
1247   noit_snmptrap_initiate_check,
1248   NULL /* noit_snmp_cleanup */
1249 };
Note: See TracBrowser for help on using the browser.