root/src/modules/snmp.c

Revision fcfbf00497741abf7ff7e6b42008fd4c4cfffef2, 56.6 kB (checked in by Phil Maddox <philip.maddox@circonus.com>, 5 months ago)

Add Initialization Of target_sessions In SNMP Check

Missed initializing the target_sessions hash table when calloced... add
the initialization.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  * Copyright (c) 2010-2015, Circonus, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
17  *       of its contributors may be used to endorse or promote products
18  *       derived from this software without specific prior written
19  *       permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <mtev_defines.h>
35
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39 #include <errno.h>
40 #include <math.h>
41 #include <ctype.h>
42 #include <arpa/inet.h>
43
44 #include <net-snmp/net-snmp-config.h>
45 #include <net-snmp/net-snmp-includes.h>
46
47 #include <mtev_hash.h>
48
49 #include "noit_module.h"
50 #include "noit_check.h"
51 #include "noit_check_tools.h"
52 #include "noit_mtev_bridge.h"
53
54 static mtev_log_stream_t nlerr = NULL;
55 static mtev_log_stream_t nldeb = NULL;
56 static int __snmp_initialize_once = 0;
57 static void ensure_usm_user(const char *username, u_char *engineID, size_t engineIDLen);
58
59 #define SNMPV2_TRAPS_PREFIX     SNMP_OID_SNMPMODULES,1,1,5
60 oid trap_prefix[]    = { SNMPV2_TRAPS_PREFIX };
61 oid cold_start_oid[] = { SNMPV2_TRAPS_PREFIX, 1 };  /* SNMPv2-MIB */
62 oid warm_start_oid[] = { SNMPV2_TRAPS_PREFIX, 2 };  /* SNMPv2-MIB */
63 oid link_down_oid[]  = { SNMPV2_TRAPS_PREFIX, 3 };  /* IF-MIB */
64 oid link_up_oid[]    = { SNMPV2_TRAPS_PREFIX, 4 };  /* IF-MIB */
65 oid auth_fail_oid[]  = { SNMPV2_TRAPS_PREFIX, 5 };  /* SNMPv2-MIB */
66 oid egp_xxx_oid[]    = { SNMPV2_TRAPS_PREFIX, 99 }; /* ??? */
67
68 #define SNMPV2_TRAP_OBJS_PREFIX SNMP_OID_SNMPMODULES,1,1,4
69 oid snmptrap_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 1, 0 };
70 size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid);
71 oid snmptrapenterprise_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 3, 0 };
72 size_t snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid);
73 oid sysuptime_oid[] = { SNMP_OID_MIB2, 1, 3, 0 };
74 size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid);
75
76 #define SNMPV2_COMM_OBJS_PREFIX SNMP_OID_SNMPMODULES,18,1
77 oid agentaddr_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 3, 0 };
78 size_t agentaddr_oid_len = OID_LENGTH(agentaddr_oid);
79 oid community_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 4, 0 };
80 size_t community_oid_len = OID_LENGTH(community_oid);
81
82 #define RECONNOITER_PREFIX     SNMP_OID_ENTERPRISES,32863,1
83 oid reconnoiter_oid[] = { RECONNOITER_PREFIX };
84 size_t reconnoiter_oid_len = OID_LENGTH(reconnoiter_oid);
85 oid reconnoiter_check_prefix_oid[] = { RECONNOITER_PREFIX,1,1 };
86 size_t reconnoiter_check_prefix_oid_len =
87   OID_LENGTH(reconnoiter_check_prefix_oid);
88 size_t reconnoiter_check_oid_len = OID_LENGTH(reconnoiter_check_prefix_oid) + 8;
89 oid reconnoiter_metric_prefix_oid[] = { RECONNOITER_PREFIX,1,2 };
90 size_t reconnoiter_metric_prefix_oid_len =
91   OID_LENGTH(reconnoiter_metric_prefix_oid);
92
93 oid reconnoiter_check_status_oid[] = { RECONNOITER_PREFIX,1,3};
94 size_t reconnoiter_check_status_oid_len =
95   OID_LENGTH(reconnoiter_check_status_oid);
96 oid reconnoiter_check_state_oid[] = { RECONNOITER_PREFIX,1,3,1};
97 size_t reconnoiter_check_state_oid_len =
98   OID_LENGTH(reconnoiter_check_state_oid);
99 oid reconnoiter_check_state_unknown_oid[] = { RECONNOITER_PREFIX,1,3,1,0};
100 oid reconnoiter_check_state_good_oid[] = { RECONNOITER_PREFIX,1,3,1,1};
101 oid reconnoiter_check_state_bad_oid[] = { RECONNOITER_PREFIX,1,3,1,2};
102 size_t reconnoiter_check_state_val_len =
103   OID_LENGTH(reconnoiter_check_state_unknown_oid);
104 /* Boolean */
105 oid reconnoiter_check_available_oid[] = { RECONNOITER_PREFIX,1,3,2};
106 size_t reconnoiter_check_available_oid_len =
107   OID_LENGTH(reconnoiter_check_available_oid);
108 oid reconnoiter_check_available_unknown_oid[] = { RECONNOITER_PREFIX,1,3,2,0};
109 oid reconnoiter_check_available_yes_oid[] = { RECONNOITER_PREFIX,1,3,2,1};
110 oid reconnoiter_check_available_no_oid[] = { RECONNOITER_PREFIX,1,3,2,2};
111 size_t reconnoiter_check_available_val_len =
112   OID_LENGTH(reconnoiter_check_available_unknown_oid);
113 /* timeticks? gauge/unsigned? */
114 oid reconnoiter_check_duration_oid[] = { RECONNOITER_PREFIX,1,3,3};
115 size_t reconnoiter_check_duration_oid_len =
116   OID_LENGTH(reconnoiter_check_duration_oid);
117 /* string */
118 oid reconnoiter_check_status_msg_oid[] = { RECONNOITER_PREFIX,1,3,4};
119 size_t reconnoiter_check_status_msg_oid_len =
120   OID_LENGTH(reconnoiter_check_status_msg_oid);
121
122 typedef struct _mod_config {
123   mtev_hash_table *options;
124   mtev_hash_table target_sessions;
125 } snmp_mod_config_t;
126
127 struct target_session {
128   struct synch_state state;
129   struct session_list *slp;
130   noit_module_t *self;
131   char *key;
132   char *target;
133   eventer_t timeoutevent;
134   int version;
135   int fd;
136   int in_table;
137   int refcnt;
138   struct timeval last_open;
139 };
140
141 #define sess_handle slp->session
142
143 struct snmp_check_closure {
144   noit_module_t *self;
145   noit_check_t *check;
146 };
147
148 struct v3_probe_magic {
149   eventer_t timeoutevent;
150   netsnmp_callback cb;
151   noit_check_t *check;
152   struct target_session *ts; /* for timeout */
153   struct snmp_pdu *pdu;
154 };
155
156 struct check_info {
157   int timedout;
158   struct {
159      int reqid;
160      char *confname;
161      char *oidname;
162      oid oid[MAX_OID_LEN];
163      size_t oidlen;
164      metric_type_t type_override;
165      mtev_boolean type_should_override;
166      int seen;
167   } *oids;
168   int noids;
169   int noids_seen;
170   int nresults;
171   eventer_t timeoutevent;
172   noit_module_t *self;
173   noit_check_t *check;
174   struct target_session *ts;
175   int version;
176 };
177
178 /* We hold struct check_info's in there key's by their reqid.
179  *   If they timeout, we remove them.
180  *
181  *   When SNMP queries complete, we look them up, if we find them
182  *   then we know we can remove the timeout and  complete the check.
183  *   If we don't find them, the timeout fired and removed the check.
184  */
185 mtev_hash_table active_checks;
186 static void add_check(struct check_info *c) {
187   int i;
188   for(i=0; i<c->noids; i++)
189     mtev_hash_store(&active_checks, (char *)&c->oids[i].reqid, sizeof(c->oids[i].reqid), c);
190 }
191 static struct check_info *get_check(int reqid) {
192   void *vc;
193   if(mtev_hash_retrieve(&active_checks, (char *)&reqid, sizeof(reqid), &vc))
194     return (struct check_info *)vc;
195   return NULL;
196 }
197 static void remove_check_req(struct check_info *c, int reqid) {
198   (void)c;
199   mtev_hash_delete(&active_checks, (char *)&reqid, sizeof(reqid),
200                    NULL, NULL);
201 }
202 static void remove_check(struct check_info *c) {
203   int i, lastreq = -1;
204   for(i=0; i<c->noids; i++) {
205     if(c->oids[i].reqid != lastreq) {
206       mtev_hash_delete(&active_checks, (char *)&c->oids[i].reqid, sizeof(c->oids[i].reqid),
207                        NULL, NULL);
208       lastreq = c->oids[i].reqid;
209     }
210   }
211 }
212
213 struct target_session *
214 _get_target_session(noit_module_t *self, char *target, int version) {
215   char key[128];
216   void *vts;
217   struct target_session *ts;
218   snmp_mod_config_t *conf;
219   conf = noit_module_get_userdata(self);
220   snprintf(key, sizeof(key), "%s:v%d", target, version);
221   if(!mtev_hash_retrieve(&conf->target_sessions,
222                          key, strlen(key), &vts)) {
223     ts = calloc(1, sizeof(*ts));
224     ts->self = self;
225     ts->version = version;
226     ts->fd = -1;
227     ts->refcnt = 0;
228     ts->target = strdup(target);
229     ts->key = strdup(key);
230     ts->in_table = 1;
231     mtev_hash_store(&conf->target_sessions,
232                     ts->key, strlen(ts->key), ts);
233     vts = ts;
234   }
235   return (struct target_session *)vts;
236 }
237
238 static int noit_snmp_accumulate_results(noit_check_t *check, struct snmp_pdu *pdu) {
239   struct check_info *info = check->closure;
240   struct variable_list *vars;
241
242   if(pdu)
243     for(vars = pdu->variables; vars; vars = vars->next_variable)
244       info->nresults++;
245
246   /* manipulate the information ourselves */
247   for(vars = pdu->variables; vars; vars = vars->next_variable) {
248     char *sp;
249     int nresults = 0;
250     int oid_idx;
251     double float_conv;
252     u_int64_t u64;
253     int64_t i64;
254     char *endptr;
255     char varbuff[256];
256
257     snprint_variable(varbuff, sizeof(varbuff),
258                      vars->name, vars->name_length, vars);
259
260     /* find the oid to which this is the response */
261     oid_idx = nresults; /* our check->stats.inprogress idx is the most likely */
262     if(info->oids[oid_idx].oidlen != vars->name_length ||
263        memcmp(info->oids[oid_idx].oid, vars->name,
264               vars->name_length * sizeof(oid))) {
265       /* Not the most obvious guess */
266       for(oid_idx = info->noids - 1; oid_idx >= 0; oid_idx--) {
267         if(info->oids[oid_idx].oidlen == vars->name_length &&
268            !memcmp(info->oids[oid_idx].oid, vars->name,
269                   vars->name_length * sizeof(oid))) break;
270       }
271     }
272     if(oid_idx < 0) {
273       mtevL(nlerr, "Unexpected oid results to %s`%s`%s: %s\n",
274             check->target, check->module, check->name, varbuff);
275       nresults++;
276       info->nresults++;
277       continue;
278     }
279     if(info->oids[oid_idx].seen == 0) {
280       info->oids[oid_idx].seen = 1;
281       info->noids_seen++;
282     }
283
284 #define SETM(a,b) noit_stats_set_metric(check, info->oids[oid_idx].confname, a, b)
285     if(info->oids[oid_idx].type_should_override) {
286       sp = strchr(varbuff, ' ');
287       if(sp) sp++;
288       noit_stats_set_metric_coerce(check, info->oids[oid_idx].confname,
289                                    info->oids[oid_idx].type_override,
290                                    sp);
291     }
292     else {
293       switch(vars->type) {
294         case ASN_OCTET_STR:
295           sp = malloc(1 + vars->val_len);
296           memcpy(sp, vars->val.string, vars->val_len);
297           sp[vars->val_len] = '\0';
298           SETM(METRIC_STRING, sp);
299           free(sp);
300           break;
301         case ASN_INTEGER:
302         case ASN_GAUGE:
303           SETM(METRIC_INT32, vars->val.integer);
304           break;
305         case ASN_TIMETICKS:
306         case ASN_COUNTER:
307           SETM(METRIC_UINT32, vars->val.integer);
308           break;
309 #ifdef ASN_OPAQUE_I64
310         case ASN_OPAQUE_I64:
311 #endif
312         case ASN_INTEGER64:
313           printI64(varbuff, vars->val.counter64);
314           i64 = strtoll(varbuff, &endptr, 10);
315           SETM(METRIC_INT64, (varbuff == endptr) ? NULL : &i64);
316           break;
317 #ifdef ASN_OPAQUE_U64
318         case ASN_OPAQUE_U64:
319 #endif
320 #ifdef ASN_OPAQUE_COUNTER64
321         case ASN_OPAQUE_COUNTER64:
322 #endif
323         case ASN_COUNTER64:
324           printU64(varbuff, vars->val.counter64);
325           u64 = strtoull(varbuff, &endptr, 10);
326           SETM(METRIC_UINT64, (varbuff == endptr) ? NULL : &u64);
327           break;
328 #ifdef ASN_OPAQUE_FLOAT
329         case ASN_OPAQUE_FLOAT:
330 #endif
331         case ASN_FLOAT:
332           if(vars->val.floatVal) float_conv = *(vars->val.floatVal);
333           SETM(METRIC_DOUBLE, vars->val.floatVal ? &float_conv : NULL);
334           break;
335 #ifdef ASN_OPAQUE_DOUBLE
336         case ASN_OPAQUE_DOUBLE:
337 #endif
338         case ASN_DOUBLE:
339           SETM(METRIC_DOUBLE, vars->val.doubleVal);
340           break;
341         case ASN_NULL:
342           mtevL(nldeb, "snmp[null]: %s\n", varbuff);
343         case SNMP_NOSUCHOBJECT:
344         case SNMP_NOSUCHINSTANCE:
345           SETM(METRIC_STRING, NULL);
346           break;
347         default:
348           /* Advance passed the first space and use that unless there
349            * is no space or we have no more string left.
350            */
351           sp = strchr(varbuff, ' ');
352           if(sp) sp++;
353           SETM(METRIC_STRING, (sp && *sp) ? sp : NULL);
354           mtevL(nlerr, "snmp: unknown type[%d] %s\n", vars->type, varbuff);
355       }
356     }
357     nresults++;
358     info->nresults++;
359   }
360   return (info->noids_seen == info->noids) ? 1 : 0;
361 }
362
363 /* Handling of results */
364 static void noit_snmp_log_results(noit_module_t *self, noit_check_t *check, const char *err) {
365   struct check_info *info = check->closure;
366   struct timeval duration, now;
367   char buff[128];
368
369   gettimeofday(&now, NULL);
370   sub_timeval(now, check->last_fire_time, &duration);
371   noit_stats_set_whence(check, &now);
372   noit_stats_set_duration(check, duration.tv_sec * 1000 + duration.tv_usec / 1000);
373   noit_stats_set_available(check, (info->nresults > 0) ? NP_AVAILABLE : NP_UNAVAILABLE);
374   noit_stats_set_state(check, (info->noids_seen == info->noids) ? NP_GOOD : NP_BAD);
375   if(err) snprintf(buff, sizeof(buff), "%s", err);
376   else snprintf(buff, sizeof(buff), "%d/%d gets", info->noids_seen, info->noids);
377   noit_stats_set_status(check, buff);
378
379   noit_check_set_stats(check);
380   return;
381 }
382
383 static int noit_snmp_session_cleanse(struct target_session *ts,
384                                      int needs_free) {
385   if(ts->refcnt == 0 && ts->slp) {
386     eventer_t e = eventer_remove_fd(ts->fd);
387     if(needs_free) eventer_free(e);
388     ts->fd = -1;
389     if(ts->timeoutevent) {
390       eventer_remove(ts->timeoutevent);
391       ts->timeoutevent = NULL;
392     }
393     snmp_sess_close(ts->slp);
394     ts->slp = NULL;
395     if(!ts->in_table) {
396       free(ts);
397     }
398     return 1;
399   }
400   return 0;
401 }
402
403 static int noit_snmp_session_timeout(eventer_t e, int mask, void *closure,
404                                      struct timeval *now) {
405   struct target_session *ts = closure;
406   if(ts->slp) snmp_sess_timeout(ts->slp);
407   noit_snmp_session_cleanse(ts, 1);
408   if(ts->timeoutevent == e)
409     ts->timeoutevent = NULL; /* this will be freed on return */
410   return 0;
411 }
412
413 static int noit_snmp_check_timeout(eventer_t e, int mask, void *closure,
414                                    struct timeval *now) {
415   struct check_info *info = closure;
416   info->timeoutevent = NULL;
417   info->timedout = 1;
418   if(info->ts) {
419     info->ts->refcnt--;
420     noit_snmp_session_cleanse(info->ts, 1);
421     info->ts = NULL;
422   }
423   remove_check(info);
424   /* Log our findings */
425   noit_snmp_log_results(info->self, info->check, NULL);
426   info->check->flags &= ~NP_RUNNING;
427   return 0;
428 }
429
430 static void _set_ts_timeout(struct target_session *ts, struct timeval *t) {
431   struct timeval now;
432   eventer_t e = NULL;
433   if(ts->timeoutevent) {
434     e = eventer_remove(ts->timeoutevent);
435     ts->timeoutevent = NULL;
436   }
437   if(!t) return;
438
439   gettimeofday(&now, NULL);
440   if(!e) e = eventer_alloc();
441   e->callback = noit_snmp_session_timeout;
442   e->closure = ts;
443   e->mask = EVENTER_TIMER;
444   add_timeval(now, *t, &e->whence);
445   ts->timeoutevent = e;
446   eventer_add(e);
447 }
448
449 static int noit_snmp_handler(eventer_t e, int mask, void *closure,
450                              struct timeval *now) {
451   int block = 0;
452   struct timeval timeout = { 0, 0 };
453   struct target_session *ts = closure;
454
455   snmp_sess_read_C1(ts->slp, e->fd);
456   if(noit_snmp_session_cleanse(ts, 0))
457     return 0;
458   snmp_sess_select_info2_flags(ts->slp, NULL, NULL,
459                                &timeout, &block, NETSNMP_SELECT_NOFLAGS);
460   _set_ts_timeout(ts, block ? &timeout : NULL);
461
462   return EVENTER_READ | EVENTER_EXCEPTION;
463 }
464
465 /* This 'convert_v1pdu_to_v2' was cribbed directly from netsnmp */
466 static netsnmp_pdu *
467 convert_v1pdu_to_v2( netsnmp_pdu* template_v1pdu ) {
468   netsnmp_pdu *template_v2pdu;
469   netsnmp_variable_list *var;
470   oid enterprise[MAX_OID_LEN];
471   size_t enterprise_len;
472
473   /*
474    * Make a copy of the v1 Trap PDU
475    *   before starting to convert this
476    *   into a v2 Trap PDU.
477    */
478   template_v2pdu = snmp_clone_pdu( template_v1pdu);
479   if(!template_v2pdu) {
480     snmp_log(LOG_WARNING,
481              "send_trap: failed to copy v2 template PDU\n");
482     return NULL;
483   }
484   template_v2pdu->command = SNMP_MSG_TRAP2;
485
486   /*
487    * Insert an snmpTrapOID varbind before the original v1 varbind list
488    *   either using one of the standard defined trap OIDs,
489    *   or constructing this from the PDU enterprise & specific trap fields
490    */
491   if(template_v1pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
492     if(template_v1pdu->enterprise_length + 2 > MAX_OID_LEN) {
493       mtevL(nlerr, "send_trap: enterprise_length too large\n");
494       snmp_free_pdu(template_v2pdu);
495       return NULL;
496     }
497     memcpy(enterprise, template_v1pdu->enterprise,
498            template_v1pdu->enterprise_length*sizeof(oid));
499     enterprise_len = template_v1pdu->enterprise_length;
500     enterprise[enterprise_len++] = 0;
501     enterprise[enterprise_len++] = template_v1pdu->specific_type;
502   } else {
503     memcpy(enterprise, cold_start_oid, sizeof(cold_start_oid));
504     enterprise[9]  = template_v1pdu->trap_type+1;
505     enterprise_len = sizeof(cold_start_oid)/sizeof(oid);
506   }
507
508   var = NULL;
509   if(!snmp_varlist_add_variable(&var,
510                                 snmptrap_oid, snmptrap_oid_len,
511                                 ASN_OBJECT_ID,
512                                 (u_char*)enterprise,
513                                 enterprise_len*sizeof(oid))) {
514     mtevL(nlerr, "send_trap: failed to insert copied snmpTrapOID varbind\n");
515     snmp_free_pdu(template_v2pdu);
516     return NULL;
517   }
518   var->next_variable        = template_v2pdu->variables;
519   template_v2pdu->variables = var;
520
521   /*
522    * Insert a sysUptime varbind at the head of the v2 varbind list
523    */
524   var = NULL;
525   if(!snmp_varlist_add_variable(&var,
526                                 sysuptime_oid, sysuptime_oid_len,
527                                 ASN_TIMETICKS,
528                                 (u_char*)&(template_v1pdu->time),
529                                 sizeof(template_v1pdu->time))) {
530     mtevL(nlerr, "send_trap: failed to insert copied sysUptime varbind\n");
531     snmp_free_pdu(template_v2pdu);
532     return NULL;
533   }
534   var->next_variable = template_v2pdu->variables;
535   template_v2pdu->variables = var;
536
537   /*
538    * Append the other three conversion varbinds,
539    *  (snmpTrapAgentAddr, snmpTrapCommunity & snmpTrapEnterprise)
540    *  if they're not already present.
541    *  But don't bomb out completely if there are problems.
542    */
543   var = find_varbind_in_list(template_v2pdu->variables,
544                              agentaddr_oid, agentaddr_oid_len);
545   if(!var && (template_v1pdu->agent_addr[0]
546               || template_v1pdu->agent_addr[1]
547               || template_v1pdu->agent_addr[2]
548               || template_v1pdu->agent_addr[3])) {
549     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
550                                   agentaddr_oid, agentaddr_oid_len,
551                                   ASN_IPADDRESS,
552                                   (u_char*)&(template_v1pdu->agent_addr),
553                                   sizeof(template_v1pdu->agent_addr)))
554       mtevL(nlerr, "send_trap: failed to append snmpTrapAddr varbind\n");
555   }
556   var = find_varbind_in_list(template_v2pdu->variables,
557                              community_oid, community_oid_len);
558   if(!var && template_v1pdu->community) {
559     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
560                                   community_oid, community_oid_len,
561                                   ASN_OCTET_STR,
562                                   template_v1pdu->community,
563                                   template_v1pdu->community_len))
564       mtevL(nlerr, "send_trap: failed to append snmpTrapCommunity varbind\n");
565   }
566   var = find_varbind_in_list(template_v2pdu->variables,
567                              snmptrapenterprise_oid,
568                              snmptrapenterprise_oid_len);
569   if(!var &&
570      template_v1pdu->trap_type != SNMP_TRAP_ENTERPRISESPECIFIC) {
571     if(!snmp_varlist_add_variable(&(template_v2pdu->variables),
572                                   snmptrapenterprise_oid,
573                                   snmptrapenterprise_oid_len,
574                                   ASN_OBJECT_ID,
575                                   (u_char*)template_v1pdu->enterprise,
576                                   template_v1pdu->enterprise_length*sizeof(oid)))
577       mtevL(nlerr, "send_trap: failed to append snmpEnterprise varbind\n");
578   }
579   return template_v2pdu;
580 }
581
582 static int noit_snmp_oid_to_checkid(oid *o, int l, uuid_t checkid, char *out) {
583   int i;
584   char _uuid_str[UUID_STR_LEN+1], *cp, *uuid_str;
585
586   uuid_str = out ? out : _uuid_str;
587   if(l != reconnoiter_check_oid_len) {
588     mtevL(nlerr, "unsupported (length) trap recieved\n");
589     return -1;
590   }
591   if(netsnmp_oid_equals(o,
592                         reconnoiter_check_prefix_oid_len,
593                         reconnoiter_check_prefix_oid,
594                         reconnoiter_check_prefix_oid_len) != 0) {
595     mtevL(nlerr, "unsupported (wrong namespace) trap recieved\n");
596     return -1;
597   }
598   /* encode this as a uuid */
599   cp = uuid_str;
600   for(i=0;
601       i < reconnoiter_check_oid_len - reconnoiter_check_prefix_oid_len;
602       i++) {
603     oid v = o[i + reconnoiter_check_prefix_oid_len];
604     if(v > 0xffff) {
605       mtevL(nlerr, "trap target oid [%ld] out of range\n", (long int)v);
606       return -1;
607     }
608     snprintf(cp, 5, "%04x", (unsigned short)(v & 0xffff));
609     cp += 4;
610     /* hyphens after index 1,2,3,4 */
611     if(i > 0 && i < 5) *cp++ = '-';
612   }
613   if(uuid_parse(uuid_str, checkid) != 0) {
614     mtevL(nlerr, "unexpected error decoding trap uuid '%s'\n", uuid_str);
615     return -1;
616   }
617   return 0;
618 }
619
620 #define isoid(a,b,c,d) (netsnmp_oid_equals(a,b,c,d) == 0)
621 #define isoidprefix(a,b,c,d) (netsnmp_oid_equals(a,MIN(b,d),c,d) == 0)
622 #define setstatus(st,soid,sv) \
623   if(isoid(o,l,soid,reconnoiter_check_state_val_len)) noit_stats_set_##st(check, sv)
624
625 static int
626 noit_snmp_trapvars_to_stats(noit_check_t *check, netsnmp_variable_list *var) {
627   if(isoidprefix(var->name, var->name_length, reconnoiter_check_status_oid,
628                  reconnoiter_check_status_oid_len)) {
629     if(var->type == ASN_OBJECT_ID) {
630       if(isoid(var->name, var->name_length,
631                reconnoiter_check_state_oid, reconnoiter_check_state_oid_len)) {
632         oid *o = var->val.objid;
633         size_t l = var->val_len / sizeof(*o);
634         setstatus(state, reconnoiter_check_state_unknown_oid, NP_UNKNOWN);
635         else setstatus(state, reconnoiter_check_state_good_oid, NP_GOOD);
636         else setstatus(state, reconnoiter_check_state_bad_oid, NP_BAD);
637         else return -1;
638       }
639       else if(isoid(var->name, var->name_length,
640                     reconnoiter_check_available_oid,
641                     reconnoiter_check_available_oid_len)) {
642         oid *o = var->val.objid;
643         size_t l = var->val_len / sizeof(*o);
644         setstatus(available, reconnoiter_check_available_unknown_oid, NP_UNKNOWN);
645         else setstatus(available, reconnoiter_check_available_yes_oid, NP_AVAILABLE);
646         else setstatus(available, reconnoiter_check_available_no_oid, NP_UNAVAILABLE);
647         else return -1;
648       }
649       else {
650         /* We don't unerstand any other OBJECT_ID types */
651         return -1;
652       }
653     }
654     else if(var->type == ASN_UNSIGNED) {
655       /* This is only for the duration (in ms) */
656       if(isoid(var->name, var->name_length,
657                reconnoiter_check_duration_oid,
658                reconnoiter_check_duration_oid_len)) {
659         noit_stats_set_duration(check, *(var->val.integer));
660       }
661       else
662         return -1;
663     }
664     else if(var->type == ASN_OCTET_STR) {
665       /* This is only for the status message */
666       if(isoid(var->name, var->name_length,
667                reconnoiter_check_status_msg_oid,
668                reconnoiter_check_status_msg_oid_len)) {
669         char buff[256];
670         strlcpy(buff, (char *)var->val.string, MIN(sizeof(buff), var->val_len+1));
671         noit_stats_set_status(check, buff);
672       }
673       else
674         return -1;
675     }
676     else {
677       /* I don't understand any other type of status message */
678       return -1;
679     }
680   }
681   else if(isoidprefix(var->name, var->name_length,
682                       reconnoiter_metric_prefix_oid,
683                       reconnoiter_metric_prefix_oid_len)) {
684     /* decode the metric and store the value */
685     int i, len;
686     u_int64_t u64;
687     double doubleVal;
688     char metric_name[128], buff[128], *cp;
689     if(var->name_length <= reconnoiter_metric_prefix_oid_len) return -1;
690     len = var->name[reconnoiter_metric_prefix_oid_len];
691     if(var->name_length != (reconnoiter_metric_prefix_oid_len + 1 + len) ||
692        len > sizeof(metric_name) - 1) {
693       mtevL(nlerr, "snmp trap, malformed metric name\n");
694       return -1;
695     }
696     for(i=0;i<len;i++) {
697       ((unsigned char *)metric_name)[i] =
698         (unsigned char)var->name[reconnoiter_metric_prefix_oid_len + 1 + i];
699       if(!isprint(metric_name[i])) {
700         mtevL(nlerr, "metric_name contains unprintable characters\n");
701         return -1;
702       }
703     }
704     metric_name[i] = '\0';
705     switch(var->type) {
706       case ASN_INTEGER:
707       case ASN_UINTEGER:
708       case ASN_TIMETICKS:
709       case ASN_INTEGER64:
710         noit_stats_set_metric(check, metric_name,
711                               METRIC_INT64, var->val.integer);
712         break;
713       case ASN_COUNTER64:
714         u64 = ((u_int64_t)var->val.counter64->high) << 32;
715         u64 |= var->val.counter64->low;
716         noit_stats_set_metric(check, metric_name,
717                               METRIC_UINT64, &u64);
718         break;
719       case ASN_OPAQUE_FLOAT:
720         doubleVal = (double)*var->val.floatVal;
721         noit_stats_set_metric(check, metric_name,
722                               METRIC_DOUBLE, &doubleVal);
723         break;
724       case ASN_OPAQUE_DOUBLE:
725         noit_stats_set_metric(check, metric_name,
726                               METRIC_DOUBLE, var->val.doubleVal);
727         break;
728       case ASN_OCTET_STR:
729         snprint_value(buff, sizeof(buff), var->name, var->name_length, var);
730         /* Advance passed the first space and use that unless there
731          * is no space or we have no more string left.
732          */
733         cp = strchr(buff, ' ');
734         if(cp) {
735           char *ecp;
736           cp++;
737           if(*cp == '"') {
738             ecp = cp + strlen(cp) - 1;
739             if(*ecp == '"') {
740               cp++; *ecp = '\0';
741             }
742           }
743         }
744         noit_stats_set_metric(check, metric_name,
745                               METRIC_STRING, (cp && *cp) ? cp : NULL);
746         break;
747       default:
748         mtevL(nlerr, "snmp trap unsupport data type %d\n", var->type);
749     }
750     mtevL(nldeb, "metric_name -> '%s'\n", metric_name);
751   }
752   else {
753     /* No idea what this is */
754     return -1;
755   }
756   return 0;
757 }
758 static int noit_snmp_trapd_response(int operation, struct snmp_session *sp,
759                                     int reqid, struct snmp_pdu *pdu,
760                                     void *magic) {
761   /* the noit pieces */
762   struct timeval now;
763   noit_check_t *check;
764   struct target_session *ts = magic;
765   snmp_mod_config_t *conf;
766   const char *community = NULL;
767   int success = 0;
768
769   /* parsing destination */
770   char uuid_str[UUID_STR_LEN + 1];
771   uuid_t checkid;
772
773   /* snmp oid parsing helper vars */
774   netsnmp_pdu *newpdu = pdu;
775   netsnmp_variable_list *var;
776
777   conf = noit_module_get_userdata(ts->self);
778
779   if(pdu->version == SNMP_VERSION_1)
780     newpdu = convert_v1pdu_to_v2(pdu);
781   if(!newpdu || newpdu->version != SNMP_VERSION_2c) goto cleanup;
782
783   for(var = newpdu->variables; var != NULL; var = var->next_variable) {
784     if(netsnmp_oid_equals(var->name, var->name_length,
785                           snmptrap_oid, snmptrap_oid_len) == 0)
786       break;
787   }
788
789   if (!var || var->type != ASN_OBJECT_ID) {
790     mtevL(nlerr, "unsupport trap (not a trap?) received\n");
791     goto cleanup;
792   }
793
794   /* var is the oid on which we are trapping.
795    * It should be in the reconnoiter check prefix.
796    */
797   if(noit_snmp_oid_to_checkid(var->val.objid, var->val_len/sizeof(oid),
798                               checkid, uuid_str)) {
799     goto cleanup;
800   }
801   mtevL(nldeb, "recieved trap for %s\n", uuid_str);
802   check = noit_poller_lookup(checkid);
803   if(!check) {
804     mtevL(nlerr, "trap received for non-existent check '%s'\n", uuid_str);
805     goto cleanup;
806   }
807   if(!mtev_hash_retr_str(check->config, "community", strlen("community"),
808                          &community) &&
809      !mtev_hash_retr_str(conf->options, "community", strlen("community"),
810                          &community)) {
811     mtevL(nlerr, "No community defined for check, dropping trap\n");
812     goto cleanup;
813   }
814
815   if(strlen(community) != newpdu->community_len ||
816      memcmp(community, newpdu->community, newpdu->community_len)) {
817     mtevL(nlerr, "trap attempt with wrong community string\n");
818     goto cleanup;
819   }
820
821   /* We have a check. The trap is authorized. Now, extract everything. */
822   gettimeofday(&now, NULL);
823   noit_stats_set_whence(check, &now);
824   noit_stats_set_available(check, NP_AVAILABLE);
825
826   /* Rate limit */
827   if(((now.tv_sec * 1000 +
828        now.tv_usec / 1000) -
829       (check->last_fire_time.tv_sec * 1000 +
830        check->last_fire_time.tv_usec / 1000)) < check->period) goto cleanup;
831
832   /* update the last fire time... */
833   gettimeofday(&check->last_fire_time, NULL);
834
835   for(; var != NULL; var = var->next_variable)
836     if(noit_snmp_trapvars_to_stats(check, var) == 0) success++;
837   if(success) {
838     char buff[24];
839     snprintf(buff, sizeof(buff), "%d datum", success);
840     noit_stats_set_state(check, NP_GOOD);
841     noit_stats_set_status(check, buff);
842   }
843   else {
844     noit_stats_set_state(check, NP_BAD);
845     noit_stats_set_status(check, "no data");
846   }
847   noit_check_set_stats(check);
848
849  cleanup:
850   if(newpdu != pdu)
851     snmp_free_pdu(newpdu);
852   return 0;
853 }
854 static int noit_snmp_asynch_response(int operation, struct snmp_session *sp,
855                                      int reqid, struct snmp_pdu *pdu,
856                                      void *magic) {
857   struct check_info *info;
858   /* We don't deal with refcnt hitting zero here.  We could only be hit from
859    * the snmp read/timeout stuff.  Handle it there.
860    */
861
862   info = get_check(reqid);
863   if(!info) return 1;
864   remove_check_req(info, reqid);
865
866
867   if(noit_snmp_accumulate_results(info->check, pdu)) {
868     mtevL(nldeb, "snmp %s pdu completed check requirements\n", info->check->name);
869     if(info->timeoutevent) {
870       eventer_remove(info->timeoutevent);
871       eventer_free(info->timeoutevent);
872       info->timeoutevent = NULL;
873     }
874     if(info->ts) {
875       info->ts->refcnt--;
876       info->ts = NULL;
877     }
878     noit_snmp_log_results(info->self, info->check, NULL);
879     info->check->flags &= ~NP_RUNNING;
880   }
881   return 1;
882 }
883
884 static void noit_snmp_sess_open(struct target_session *ts,
885                                 noit_check_t *check) {
886   size_t bsize, offset;
887   u_char _buf[512];
888   u_char *buf = _buf;
889   const char *cval;
890   struct snmp_session sess;
891   netsnmp_transport *transport;
892   struct check_info *info = check->closure;
893   memset(&sess, 0, sizeof(sess));
894   snmp_sess_init(&sess);
895   sess.version = info->version;
896   sess.peername = ts->target;
897   u_char contextEngineID_buf[256];
898   u_char securityEngineID_buf[256];
899
900 /*
901 final String walk_base             = config.remove("walk");
902 */
903
904 #define CONF_GET(name, tgt) \
905   mtev_hash_retr_str(check->config, name, strlen(name), tgt)
906 #define SESS_SET_STRING(value, len, key, default) do {    \
907   const char *_cval = NULL;                               \
908   if(!CONF_GET(key, &_cval)) {                            \
909     _cval = default;                                      \
910   }                                                       \
911   value = ( __typeof__ (value) )(_cval);                  \
912   len = strlen(_cval);                                    \
913 } while(0)
914
915   SESS_SET_STRING(sess.community, sess.community_len, "community", "public");
916   /* TCP/UDP -> session_flags |= SNMP_FLAGS_STREAM_SOCKET */
917   /* securityEngineID? */
918   /* contextEngineID? */
919   SESS_SET_STRING(sess.contextName, sess.contextNameLen, "context_name", "");
920   if(CONF_GET("context_engine", &cval)) {
921     bsize = sizeof(buf);
922     offset = 0;
923     if (netsnmp_hex_to_binary(&buf,&bsize,&offset,0,cval,".")) {
924             sess.contextEngineID = contextEngineID_buf;
925             memcpy(sess.contextEngineID, buf, bsize);
926             sess.contextEngineIDLen = bsize;
927     }
928         }
929   SESS_SET_STRING(sess.securityName, sess.securityNameLen, "security_name", "");
930   if(sess.securityName) sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
931   if(CONF_GET("security_engine", &cval)) {
932     bsize = sizeof(buf);
933     offset = 0;
934     if (netsnmp_hex_to_binary(&buf,&bsize,&offset,0,cval,".")) {
935             sess.securityEngineID = securityEngineID_buf;
936             memcpy(sess.securityEngineID, buf, bsize);
937             sess.securityEngineIDLen = bsize;
938     }
939         }
940   if(CONF_GET("security_level", &cval)) {
941     if ((!strcasecmp(cval, "nanp")) || (!strcasecmp(cval, "noAuthNoPriv")))
942       sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
943     else if ((!strcasecmp(cval, "anp")) || (!strcasecmp(cval, "authNoPriv")))
944       sess.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
945     else if ((!strcasecmp(cval, "ap")) || (!strcasecmp(cval, "authPriv")))
946       sess.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
947   }
948   if(CONF_GET("auth_protocol", &cval)) {
949     if (!strcasecmp(cval,"MD5")) {
950       sess.securityAuthProto = usmHMACMD5AuthProtocol;
951       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
952     }
953     else if (!strcasecmp(cval,"SHA")) {
954       sess.securityAuthProto = usmHMACSHA1AuthProtocol;
955       sess.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
956     }
957   }
958   if(CONF_GET("auth_passphrase", &cval)) {
959     if(sess.securityAuthProto == NULL) {
960       sess.securityAuthProto = usmHMACMD5AuthProtocol;
961       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
962     }
963     sess.securityAuthKeyLen = USM_AUTH_KU_LEN;
964     if(generate_Ku(sess.securityAuthProto, sess.securityAuthProtoLen,
965                    (u_char *)cval, strlen(cval),
966                    sess.securityAuthKey, &sess.securityAuthKeyLen) != SNMPERR_SUCCESS) {
967       /* What do we do? */
968       mtevL(nlerr, "auth_passphrase failed to gen master key\n");
969     }
970   }
971   if(CONF_GET("privacy_protocol", &cval)) {
972     if (!strcasecmp(cval,"DES")) {
973       sess.securityPrivProto = usmDESPrivProtocol;
974       sess.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
975     }
976     else if ((!strcasecmp(cval,"AES"))||(!strcasecmp(cval,"AES128"))) {
977       sess.securityPrivProto = usmAESPrivProtocol;
978       sess.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN;
979     }
980   }
981   if(CONF_GET("privacy_passphrase", &cval)) {
982     if(sess.securityAuthProto == NULL) {
983       sess.securityAuthProto = usmHMACMD5AuthProtocol;
984       sess.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
985     }
986     if(sess.securityPrivProto == NULL) {
987       sess.securityPrivProto = usmDESPrivProtocol;
988       sess.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
989     }
990     sess.securityPrivKeyLen = USM_PRIV_KU_LEN;
991     if(generate_Ku(sess.securityAuthProto, sess.securityAuthProtoLen,
992                    (u_char *)cval, strlen(cval),
993                    sess.securityPrivKey, &sess.securityPrivKeyLen) != SNMPERR_SUCCESS) {
994       /* What do we do? */
995       mtevL(nlerr, "priv_passphrase failed to gen master key\n");
996     }
997   }
998   sess.callback = noit_snmp_asynch_response;
999   sess.callback_magic = ts;
1000   sess.flags |= SNMP_FLAGS_DONT_PROBE;
1001   ts->slp = snmp_sess_open_C1(&sess, &transport);
1002   ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1003   ts->fd = transport->sock;
1004   gettimeofday(&ts->last_open, NULL);
1005 }
1006
1007 static int noit_snmp_fill_oidinfo(noit_check_t *check) {
1008   int i, klen;
1009   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
1010   const char *name, *value;
1011   struct check_info *info = check->closure;
1012   mtev_hash_table check_attrs_hash;
1013
1014   /* Toss the old set and bail if we have zero */
1015   if(info->oids) {
1016     for(i=0; i<info->noids;i++) {
1017       if(info->oids[i].confname) free(info->oids[i].confname);
1018       if(info->oids[i].oidname) free(info->oids[i].oidname);
1019     }
1020     free(info->oids);
1021   }
1022   info->noids = 0;
1023   info->nresults = 0;
1024   info->noids_seen = 0;
1025   info->oids = NULL;
1026
1027   /* Figure our how many. */
1028   while(mtev_hash_next_str(check->config, &iter, &name, &klen, &value)) {
1029     if(!strncasecmp(name, "oid_", 4)) {
1030       info->noids++;
1031     }
1032   }
1033
1034   if(info->noids == 0) return 0;
1035
1036   /* Create a hash of important check attributes */
1037   noit_check_make_attrs(check, &check_attrs_hash);
1038
1039   /* Fill out the new set of required oids */
1040   info->oids = calloc(info->noids, sizeof(*info->oids));
1041   memset(&iter, 0, sizeof(iter));
1042   i = 0;
1043   while(mtev_hash_next_str(check->config, &iter, &name, &klen, &value)) {
1044     if(!strncasecmp(name, "oid_", 4)) {
1045       const char *type_override;
1046       char oidbuff[2048], typestr[256];
1047       name += 4;
1048       info->oids[i].confname = strdup(name);
1049       noit_check_interpolate(oidbuff, sizeof(oidbuff), value,
1050                              &check_attrs_hash, check->config);
1051       info->oids[i].oidname = strdup(oidbuff);
1052       info->oids[i].oidlen = MAX_OID_LEN;
1053       if(oidbuff[0] == '.') {
1054         if(!read_objid(oidbuff, info->oids[i].oid, &info->oids[i].oidlen)) {
1055           mtevL(nlerr, "Failed to translate oid: %s\n", oidbuff);
1056           info->noids--;
1057           continue;
1058         }
1059       }
1060       else {
1061         if(!get_node(oidbuff, info->oids[i].oid, &info->oids[i].oidlen)) {
1062           mtevL(nlerr, "Failed to translate oid: %s\n", oidbuff);
1063           info->noids--;
1064           continue;
1065         }
1066       }
1067       snprintf(typestr, sizeof(typestr), "type_%s", name);
1068       if(mtev_hash_retr_str(check->config, typestr, strlen(typestr),
1069                             &type_override)) {
1070         int type_enum_fake = *type_override;
1071
1072         if(!strcasecmp(type_override, "guess"))
1073           type_enum_fake = METRIC_GUESS;
1074         else if(!strcasecmp(type_override, "int32"))
1075           type_enum_fake = METRIC_INT32;
1076         else if(!strcasecmp(type_override, "uint32"))
1077           type_enum_fake = METRIC_UINT32;
1078         else if(!strcasecmp(type_override, "int64"))
1079           type_enum_fake = METRIC_INT64;
1080         else if(!strcasecmp(type_override, "uint64"))
1081           type_enum_fake = METRIC_UINT64;
1082         else if(!strcasecmp(type_override, "double"))
1083           type_enum_fake = METRIC_DOUBLE;
1084         else if(!strcasecmp(type_override, "string"))
1085           type_enum_fake = METRIC_STRING;
1086
1087         switch(type_enum_fake) {
1088           case METRIC_GUESS:
1089           case METRIC_INT32: case METRIC_UINT32:
1090           case METRIC_INT64: case METRIC_UINT64:
1091           case METRIC_DOUBLE: case METRIC_STRING:
1092             info->oids[i].type_override = *type_override;
1093             info->oids[i].type_should_override = mtev_true;
1094           default: break;
1095         }
1096       }
1097       i++;
1098     }
1099   }
1100   mtevAssert(info->noids == i);
1101   mtev_hash_destroy(&check_attrs_hash, NULL, NULL);
1102   return info->noids;
1103 }
1104 static int noit_snmp_fill_req(struct snmp_pdu *req, noit_check_t *check, int idx) {
1105   int i;
1106   struct check_info *info = check->closure;
1107
1108   if(idx == -1) {
1109     for(i=0; i<info->noids; i++)
1110       snmp_add_null_var(req, info->oids[i].oid, info->oids[i].oidlen);
1111     return info->noids;
1112   }
1113
1114   mtevAssert(idx >= 0 && idx <info->noids);
1115   snmp_add_null_var(req, info->oids[idx].oid, info->oids[idx].oidlen);
1116   return 1;
1117 }
1118
1119 static void
1120 ensure_usm_user(const char *username, u_char *engineID, size_t engineIDLen) {
1121   struct usmUser *user;
1122   user = usm_get_user(NULL, 0, (char *)username);
1123   if (user == NULL) {
1124     user = (struct usmUser *) calloc(1, sizeof(struct usmUser));
1125     user->name = strdup(username);
1126     user->secName = strdup(username);
1127     user->authProtocolLen = sizeof(usmNoAuthProtocol) / sizeof(oid);
1128     user->authProtocol =
1129         snmp_duplicate_objid(usmNoAuthProtocol, user->authProtocolLen);
1130     user->privProtocolLen = sizeof(usmNoPrivProtocol) / sizeof(oid);
1131     user->privProtocol =
1132         snmp_duplicate_objid(usmNoPrivProtocol, user->privProtocolLen);
1133     if(engineIDLen) {
1134       user->engineID = malloc(engineIDLen);
1135       memcpy(user->engineID, engineID, engineIDLen);
1136       user->engineIDLen = engineIDLen;
1137     }
1138     usm_add_user(user);
1139     mtevL(nldeb, "usm adding user: %s\n", username);
1140   }
1141 }
1142
1143 /* Shenanigans to work around snmp v3 probes blocking */
1144 static int
1145 snmpv3_build_probe_pdu(netsnmp_pdu **pdu) {
1146
1147     /*
1148      * create the pdu
1149      */
1150     if (!pdu)
1151         return -1;
1152     *pdu = snmp_pdu_create(SNMP_MSG_GET);
1153     if (!(*pdu))
1154         return -1;
1155     (*pdu)->version = SNMP_VERSION_3;
1156     (*pdu)->securityName = strdup("");
1157     (*pdu)->securityNameLen = strlen((*pdu)->securityName);
1158     (*pdu)->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
1159     (*pdu)->securityModel = SNMP_SEC_MODEL_USM;
1160
1161     /*
1162      * create the empty user
1163      */
1164     ensure_usm_user((*pdu)->securityName, NULL, 0);
1165     return 0;
1166 }
1167
1168 static int
1169 noit_snmpv3_probe_timeout(eventer_t e, int mask, void *closure,
1170                           struct timeval *now) {
1171   struct v3_probe_magic *magic = closure;
1172   struct target_session *ts = magic->ts;
1173   if(ts && ts->slp) {
1174     ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1175   }
1176   if(ts) ts->refcnt--;
1177   magic->timeoutevent = NULL;
1178   return 0;
1179 }
1180
1181 static void
1182 copy_auth_to_pdu(struct snmp_session *sp, struct snmp_pdu *pdu) {
1183 #define pdu_copystring(name) do { \
1184   if(pdu->name) free(pdu->name); \
1185   pdu->name = NULL; \
1186   if(sp->name) pdu->name = strdup(sp->name); \
1187   pdu->name##Len = strlen(pdu->name); \
1188 } while(0)
1189 #define pdu_copyoid(oid) do { \
1190   if(pdu->oid) free(pdu->oid); \
1191   pdu->oid = NULL; \
1192   pdu->oid = snmp_duplicate_objid(sp->oid, sp->oid##Len); \
1193   pdu->oid##Len = sp->oid##Len; \
1194 } while(0)
1195
1196   pdu_copystring(securityName);
1197   pdu->securityModel = sp->securityModel;
1198   pdu->securityLevel = sp->securityLevel;
1199 }
1200
1201 static int
1202 probe_engine_step1_cb(int operation,
1203                       struct snmp_session *sp,
1204                       int reqid,
1205                       struct snmp_pdu *pdu,
1206                       void *arg) {
1207   struct v3_probe_magic *magic = arg;
1208   struct check_info *info = magic->check->closure;
1209   struct target_session *ts = info->ts;
1210   int ret = 1;
1211
1212   if(magic->timeoutevent) {
1213     eventer_remove(magic->timeoutevent);
1214     eventer_free(magic->timeoutevent);
1215     magic->timeoutevent = NULL;
1216   }
1217   /* Did we receive the appropriate Report message? */
1218   if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE &&
1219       pdu && pdu->command == SNMP_MSG_REPORT) {
1220     int reqid, i;
1221     if(pdu->securityEngineIDLen) {
1222       if(sp->securityEngineID) free(sp->securityEngineID);
1223       sp->securityEngineID = malloc(sizeof(*sp->securityEngineID)*pdu->securityEngineIDLen);
1224       memcpy(sp->securityEngineID, pdu->securityEngineID, sizeof(*sp->securityEngineID)*pdu->securityEngineIDLen);
1225       sp->securityEngineIDLen = pdu->securityEngineIDLen;
1226     }
1227     if(pdu->contextEngineIDLen) {
1228       if(sp->contextEngineID) free(sp->contextEngineID);
1229       sp->contextEngineID = malloc(sizeof(*sp->contextEngineID)*pdu->contextEngineIDLen);
1230       memcpy(sp->contextEngineID, pdu->contextEngineID, sizeof(*sp->contextEngineID)*pdu->contextEngineIDLen);
1231       sp->contextEngineIDLen = pdu->contextEngineIDLen;
1232     }
1233     i = usm_create_user_from_session(sp);
1234     mtevL(nldeb, "usm_create_user_from_session(...) -> %d\n", i);
1235     copy_auth_to_pdu(sp, magic->pdu);
1236     reqid = snmp_sess_send(ts->slp, magic->pdu);
1237     if(reqid == 0) {
1238       int liberr, snmperr;
1239       char *errmsg;
1240       snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1241       mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1242       snmp_free_pdu(magic->pdu);
1243       magic->pdu = NULL;
1244       goto probe_failed;
1245     }
1246     for(i=0; i<info->noids; i++) info->oids[i].reqid = reqid;
1247     mtevL(nldeb, "Probe followup sent snmp get[all/%d] -> reqid:%d\n", info->noids, reqid);
1248     add_check(info);
1249     ts->refcnt--;
1250     goto out;
1251   }
1252
1253  probe_failed:
1254   if(magic->pdu) snmp_free_pdu(magic->pdu);
1255   sp->flags &= ~SNMP_FLAGS_DONT_PROBE;
1256   ret = magic->cb(NETSNMP_CALLBACK_OP_SEND_FAILED,
1257                   sp, reqid, pdu, info);
1258
1259  out:
1260   return ret;
1261 }
1262
1263 static int noit_snmp_send(noit_module_t *self, noit_check_t *check,
1264                           noit_check_t *cause) {
1265   struct timeval when, to;
1266   struct snmp_pdu *req = NULL;
1267   struct target_session *ts;
1268   struct check_info *info = check->closure;
1269   int port = 161;
1270   mtev_boolean separate_queries = mtev_false;
1271   const char *portstr, *versstr, *sepstr;
1272   const char *err = "unknown err";
1273   char target_port[64];
1274
1275   info->version = SNMP_VERSION_2c;
1276   info->self = self;
1277   info->check = check;
1278   info->timedout = 0;
1279
1280   BAIL_ON_RUNNING_CHECK(check);
1281   check->flags |= NP_RUNNING;
1282
1283   gettimeofday(&check->last_fire_time, NULL);
1284   if(mtev_hash_retr_str(check->config, "separate_queries",
1285                         strlen("separate_queries"), &sepstr)) {
1286     if(!strcasecmp(sepstr, "on") || !strcasecmp(sepstr, "true"))
1287       separate_queries = mtev_true;
1288   }
1289   if(mtev_hash_retr_str(check->config, "port", strlen("port"),
1290                         &portstr)) {
1291     port = atoi(portstr);
1292   }
1293   if(mtev_hash_retr_str(check->config, "version", strlen("version"),
1294                         &versstr)) {
1295     /* We don't care about 2c or others... as they all default to 2c */
1296     if(!strcmp(versstr, "1")) info->version = SNMP_VERSION_1;
1297     if(!strcmp(versstr, "3")) info->version = SNMP_VERSION_3;
1298   }
1299   snprintf(target_port, sizeof(target_port), "%s:%d", check->target_ip, port);
1300   ts = _get_target_session(self, target_port, info->version);
1301   gettimeofday(&check->last_fire_time, NULL);
1302   if(!ts->refcnt) {
1303     eventer_t newe;
1304     noit_snmp_sess_open(ts, check);
1305     newe = eventer_alloc();
1306     newe->fd = ts->fd;
1307     newe->callback = noit_snmp_handler;
1308     newe->closure = ts;
1309     newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1310     eventer_add(newe);
1311   }
1312   if(!ts->slp) goto bail;
1313   ts->refcnt++; /* Increment here, decrement when this check completes */
1314
1315   noit_snmp_fill_oidinfo(check);
1316   /* Do we need probing? */
1317   if (info->version == SNMP_VERSION_3 &&
1318       ts->sess_handle->securityEngineIDLen == 0 &&
1319       (0 == (ts->sess_handle->flags & SNMP_FLAGS_DONT_PROBE))) {
1320     /* Allocate some "magic" structure to remember PDU, callback and argument*/
1321     struct v3_probe_magic *magic = calloc(1, sizeof(struct v3_probe_magic));
1322     netsnmp_pdu *probe = NULL;
1323
1324     mtevL(nldeb, "Probing for v3\n");
1325     if (!magic) goto bail;
1326     magic->cb = noit_snmp_asynch_response;
1327     magic->check = check;
1328     magic->ts = ts;
1329  
1330     if (snmpv3_build_probe_pdu(&probe) != 0) {
1331       free(magic);
1332       goto bail;
1333     }
1334
1335     /* Send it. */
1336     ts->sess_handle->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
1337     ts->refcnt++;
1338
1339     magic->pdu = snmp_pdu_create(SNMP_MSG_GET);
1340     noit_snmp_fill_req(magic->pdu, check, -1);
1341     magic->pdu->version = info->version;
1342
1343     if (!snmp_sess_async_send(ts->slp, probe, probe_engine_step1_cb, magic)) {
1344       ts->refcnt--;
1345       snmp_free_pdu(probe);
1346       free(magic);
1347       ts->sess_handle->flags &= ~SNMP_FLAGS_DONT_PROBE;
1348       goto bail;
1349     }
1350
1351     magic->timeoutevent = eventer_alloc();
1352     magic->timeoutevent->callback = noit_snmpv3_probe_timeout;
1353     magic->timeoutevent->closure = magic;
1354     magic->timeoutevent->mask = EVENTER_TIMER;
1355  
1356     gettimeofday(&when, NULL);
1357     to.tv_sec = 5;
1358     to.tv_usec = 0;
1359     add_timeval(when, to, &magic->timeoutevent->whence);
1360     eventer_add(magic->timeoutevent);
1361   }
1362   else {
1363     /* Separate queries is not supported on v3... it makes no sense */
1364     if(separate_queries && info->version != SNMP_VERSION_3) {
1365       int reqid, i;
1366       mtevL(nldeb, "Regular old get...\n");
1367       for(i=0;i<info->noids;i++) {
1368         req = snmp_pdu_create(SNMP_MSG_GET);
1369         if(!req) continue;
1370         noit_snmp_fill_req(req, check, i);
1371         req->version = info->version;
1372         reqid = snmp_sess_send(ts->slp, req);
1373         if(reqid == 0) {
1374           int liberr, snmperr;
1375           char *errmsg;
1376           snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1377           mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1378           snmp_free_pdu(req);
1379           continue;
1380         }
1381         info->oids[i].reqid = reqid;
1382         mtevL(nldeb, "Sent snmp get[%d/%d] -> reqid:%d\n", i, info->noids, reqid);
1383       }
1384     }
1385     else {
1386       int reqid, i;
1387       mtevL(nldeb, "Regular old get...\n");
1388       req = snmp_pdu_create(SNMP_MSG_GET);
1389       if(!req) goto bail;
1390       noit_snmp_fill_req(req, check, -1);
1391       if(info->version == SNMP_VERSION_3 && ts->sess_handle->securityName) {
1392         i = usm_create_user_from_session(ts->sess_handle);
1393         mtevL(nldeb, "usm_create_user_from_session(...) -> %d\n", i);
1394       }
1395       req->version = info->version;
1396       reqid = snmp_sess_send(ts->slp, req);
1397       if(reqid == 0) {
1398         int liberr, snmperr;
1399         char *errmsg;
1400         snmp_sess_error(ts->slp, &liberr, &snmperr, &errmsg);
1401         mtevL(nlerr, "Error sending snmp get request: %s\n", errmsg);
1402         err = errmsg;
1403         if(reqid == 0) goto bail;
1404       }
1405       for(i=0; i<info->noids; i++) info->oids[i].reqid = reqid;
1406       mtevL(nldeb, "Sent snmp get[all/%d] -> reqid:%d\n", info->noids, reqid);
1407     }
1408   }
1409   info->ts = ts;
1410   info->timeoutevent = eventer_alloc();
1411   info->timeoutevent->callback = noit_snmp_check_timeout;
1412   info->timeoutevent->closure = info;
1413   info->timeoutevent->mask = EVENTER_TIMER;
1414
1415   gettimeofday(&when, NULL);
1416   to.tv_sec = check->timeout / 1000;
1417   to.tv_usec = (check->timeout % 1000) * 1000;
1418   add_timeval(when, to, &info->timeoutevent->whence);
1419   eventer_add(info->timeoutevent);
1420   add_check(info);
1421   return 0;
1422
1423  bail:
1424   ts->refcnt--;
1425   noit_snmp_log_results(self, check, err);
1426   noit_snmp_session_cleanse(ts, 1);
1427   if(req) snmp_free_pdu(req);
1428   check->flags &= ~NP_RUNNING;
1429   return 0;
1430 }
1431
1432 static int noit_snmp_initiate_check(noit_module_t *self, noit_check_t *check,
1433                                     int once, noit_check_t *cause) {
1434   if(!check->closure) check->closure = calloc(1, sizeof(struct check_info));
1435   INITIATE_CHECK(noit_snmp_send, self, check, cause);
1436   return 0;
1437 }
1438
1439 static int noit_snmptrap_initiate_check(noit_module_t *self,
1440                                         noit_check_t *check,
1441                                         int once, noit_check_t *cause) {
1442   /* We don't do anything for snmptrap checks.  Not intuitive... but they
1443    * never "run."  We accept input out-of-band via snmp traps.
1444    */
1445   check->flags |= NP_PASSIVE_COLLECTION;
1446   return 0;
1447 }
1448
1449 static int noit_snmp_config(noit_module_t *self, mtev_hash_table *options) {
1450   snmp_mod_config_t *conf;
1451   conf = noit_module_get_userdata(self);
1452   if(conf) {
1453     if(conf->options) {
1454       mtev_hash_destroy(conf->options, free, free);
1455       free(conf->options);
1456     }
1457   }
1458   else {
1459     conf = calloc(1, sizeof(*conf));
1460     mtev_hash_init(&conf->target_sessions);
1461   }
1462   conf->options = options;
1463   noit_module_set_userdata(self, conf);
1464   return 1;
1465 }
1466 static int noit_snmp_onload(mtev_image_t *self) {
1467   if(!nlerr) nlerr = mtev_log_stream_find("error/snmp");
1468   if(!nldeb) nldeb = mtev_log_stream_find("debug/snmp");
1469   if(!nlerr) nlerr = noit_stderr;
1470   if(!nldeb) nldeb = noit_debug;
1471   eventer_name_callback("noit_snmp/check_timeout", noit_snmp_check_timeout);
1472   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1473   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1474   return 0;
1475 }
1476
1477 static int noit_snmptrap_onload(mtev_image_t *self) {
1478   if(!nlerr) nlerr = mtev_log_stream_find("error/snmp");
1479   if(!nldeb) nldeb = mtev_log_stream_find("debug/snmp");
1480   if(!nlerr) nlerr = noit_stderr;
1481   if(!nldeb) nldeb = noit_debug;
1482   eventer_name_callback("noit_snmp/session_timeout", noit_snmp_session_timeout);
1483   eventer_name_callback("noit_snmp/handler", noit_snmp_handler);
1484   return 0;
1485 }
1486
1487 static void
1488 nc_printf_snmpts_brief(mtev_console_closure_t ncct,
1489                        struct target_session *ts) {
1490   char fd[32];
1491   struct timeval now, diff;
1492   const char *snmpvers = "v(unknown)";
1493   gettimeofday(&now, NULL);
1494   sub_timeval(now, ts->last_open, &diff);
1495   if(ts->fd < 0)
1496     snprintf(fd, sizeof(fd), "%s", "(closed)");
1497   else
1498     snprintf(fd, sizeof(fd), "%d", ts->fd);
1499   switch(ts->version) {
1500     case SNMP_VERSION_1: snmpvers = "v1"; break;
1501     case SNMP_VERSION_2c: snmpvers = "v2c"; break;
1502     case SNMP_VERSION_3: snmpvers = "v3"; break;
1503   }
1504   nc_printf(ncct, "[%s %s]\n\topened: %0.3fs ago\n\tFD: %s\n\trefcnt: %d\n",
1505             ts->target, snmpvers, diff.tv_sec + (float)diff.tv_usec/1000000,
1506             fd, ts->refcnt);
1507 }
1508
1509 static int
1510 noit_console_show_snmp(mtev_console_closure_t ncct,
1511                        int argc, char **argv,
1512                        mtev_console_state_t *dstate,
1513                        void *closure) {
1514   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
1515   uuid_t key_id;
1516   int klen;
1517   void *vts;
1518   snmp_mod_config_t *conf = closure;
1519
1520   while(mtev_hash_next(&conf->target_sessions, &iter,
1521                        (const char **)key_id, &klen,
1522                        &vts)) {
1523     struct target_session *ts = vts;
1524     nc_printf_snmpts_brief(ncct, ts);
1525   }
1526   return 0;
1527 }
1528
1529 static void
1530 register_console_snmp_commands(snmp_mod_config_t *conf) {
1531   mtev_console_state_t *tl;
1532   cmd_info_t *showcmd;
1533
1534   tl = mtev_console_state_initial();
1535   showcmd = mtev_console_state_get_cmd(tl, "show");
1536   mtevAssert(showcmd && showcmd->dstate);
1537   mtev_console_state_add_cmd(showcmd->dstate,
1538     NCSCMD("snmp", noit_console_show_snmp, NULL, NULL, conf));
1539 }
1540
1541 static __thread char linebuf[1024] = "\0";
1542 static int
1543 _private_snmp_log(int majorID, int minorID, void *serverarg, void *clientarg) {
1544   struct snmp_log_message *slm;
1545   size_t len;
1546   slm = serverarg;
1547   len = strlcat(linebuf, slm->msg, sizeof(linebuf));
1548   if(len > sizeof(linebuf)-1) {
1549     linebuf[sizeof(linebuf)-2] = '\n';
1550     linebuf[sizeof(linebuf)-1] = '\0';
1551   }
1552   else if(linebuf[len-1] == '\n') {
1553   }
1554   else {
1555     return 1;
1556   }
1557   mtevL(((slm->priority < LOG_NOTICE) ? nlerr : nldeb), "[pri:%d] %s",
1558         slm->priority, linebuf);
1559   linebuf[0] = '\0';
1560   return 1;
1561 }
1562
1563 static int noit_snmp_init(noit_module_t *self) {
1564   const char *opt;
1565   snmp_mod_config_t *conf;
1566
1567   mtev_hash_init(&active_checks);
1568
1569   conf = noit_module_get_userdata(self);
1570   if(mtev_hash_retr_str(conf->options, "debugging", strlen("debugging"), &opt)) {
1571     snmp_set_do_debugging(atoi(opt));
1572   }
1573
1574   if(!__snmp_initialize_once) {
1575     register_mib_handlers();
1576     read_premib_configs();
1577     read_configs();
1578     init_snmp("noitd");
1579     snmp_disable_stderrlog();
1580     snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING,
1581                            _private_snmp_log, conf);
1582     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_EMERG);
1583     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_ALERT);
1584     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_CRIT);
1585     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_ERR);
1586     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_WARNING);
1587     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_NOTICE);
1588     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_INFO);
1589     netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_DEBUG);
1590     __snmp_initialize_once = 1;
1591   }
1592   if(strcmp(self->hdr.name, "snmp") == 0) {
1593     register_console_snmp_commands(conf);
1594   }
1595
1596   if(strcmp(self->hdr.name, "snmptrap") == 0) {
1597     eventer_t newe;
1598     int i, block = 0, fds = 0;
1599     fd_set fdset;
1600     struct timeval timeout = { 0, 0 };
1601     struct target_session *ts;
1602     netsnmp_transport *transport;
1603     netsnmp_session sess, *session = &sess;
1604
1605     if(!mtev_hash_retrieve(conf->options,
1606                            "snmptrapd_port", strlen("snmptrapd_port"),
1607                            (void **)&opt))
1608       opt = "162";
1609
1610     transport = netsnmp_transport_open_server("snmptrap", opt);
1611     if(!transport) {
1612       mtevL(nlerr, "cannot open netsnmp transport for trap daemon\n");
1613       return -1;
1614     }
1615     ts = _get_target_session(self, "snmptrapd", SNMP_DEFAULT_VERSION);
1616     snmp_sess_init(session);
1617     session->peername = SNMP_DEFAULT_PEERNAME;
1618     session->version = SNMP_DEFAULT_VERSION;
1619     session->community_len = SNMP_DEFAULT_COMMUNITY_LEN;
1620     session->retries = SNMP_DEFAULT_RETRIES;
1621     session->timeout = SNMP_DEFAULT_TIMEOUT;
1622     session->callback = noit_snmp_trapd_response;
1623     session->callback_magic = (void *) ts;
1624     session->authenticator = NULL;
1625     session->isAuthoritative = SNMP_SESS_UNKNOWNAUTH;
1626     ts->slp = snmp_sess_add(session, transport, NULL, NULL);
1627
1628     FD_ZERO(&fdset);
1629     snmp_sess_select_info(ts->slp, &fds, &fdset, &timeout, &block);
1630     mtevAssert(fds > 0);
1631     for(i=0; i<fds; i++) {
1632       if(FD_ISSET(i, &fdset)) {
1633         ts->refcnt++;
1634         ts->fd = i;
1635         newe = eventer_alloc();
1636         newe->fd = ts->fd;
1637         newe->callback = noit_snmp_handler;
1638         newe->closure = ts;
1639         newe->mask = EVENTER_READ | EVENTER_EXCEPTION;
1640         eventer_add(newe);
1641       }
1642     }
1643   }
1644   return 0;
1645 }
1646
1647 #include "snmp.xmlh"
1648 noit_module_t snmp = {
1649   {
1650     .magic = NOIT_MODULE_MAGIC,
1651     .version = NOIT_MODULE_ABI_VERSION,
1652     .name = "snmp",
1653     .description = "SNMP collection",
1654     .xml_description = snmp_xml_description,
1655     .onload = noit_snmp_onload
1656   },
1657   noit_snmp_config,
1658   noit_snmp_init,
1659   noit_snmp_initiate_check,
1660   NULL, /* noit_snmp_cleanup */
1661   .thread_unsafe = 1
1662 };
1663
1664 #include "snmptrap.xmlh"
1665 noit_module_t snmptrap = {
1666   {
1667     .magic = NOIT_MODULE_MAGIC,
1668     .version = NOIT_MODULE_ABI_VERSION,
1669     .name = "snmptrap",
1670     .description = "SNMP trap collection",
1671     .xml_description = snmptrap_xml_description,
1672     .onload = noit_snmptrap_onload
1673   },
1674   noit_snmp_config,
1675   noit_snmp_init,
1676   noit_snmptrap_initiate_check,
1677   NULL, /* noit_snmp_cleanup */
1678   .thread_unsafe = 1
1679 };
Note: See TracBrowser for help on using the browser.