root/src/modules/snmp.c

Revision 7ae62b9ec2b5f252357a0d80446c0ad5ad6dd7fa, 40.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

support snmp type coercion... BAD VENDORS... BAD.

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