snmp_agent: Implement SNMP Agent plugin.
[collectd.git] / src / snmp_agent.c
1 /**
2  * collectd - src/snmp_agent.c
3  *
4  * Copyright(c) 2016 Intel Corporation. All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of
7  * this software and associated documentation files (the "Software"), to deal in
8  * the Software without restriction, including without limitation the rights to
9  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is furnished to do
11  * so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * Authors:
25  *   Roman Korynkevych <romanx.korynkevych@intel.com>
26  *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
27  **/
28
29 #include "collectd.h"
30 #include "common.h"
31 #include "utils_avltree.h"
32 #include "utils_llist.h"
33 #include "utils_cache.h"
34
35 #include <net-snmp/net-snmp-config.h>
36 #include <net-snmp/net-snmp-includes.h>
37 #include <net-snmp/agent/net-snmp-agent-includes.h>
38
39 #define PLUGIN_NAME "snmp_agent"
40 #define ERR_BUF_SIZE 1024
41 #define TYPE_STRING -1
42
43 struct oid_s {
44   oid oid[MAX_OID_LEN];
45   size_t oid_len;
46   u_char type;
47 };
48 typedef struct oid_s oid_t;
49
50 struct table_definition_s {
51   char *name;
52   oid_t index_oid;
53   oid_t size_oid;
54   llist_t *columns;
55   c_avl_tree_t *instance_index;
56   c_avl_tree_t *index_instance;
57 };
58 typedef struct table_definition_s table_definition_t;
59
60 struct data_definition_s {
61   char *name;
62   char *plugin;
63   char *plugin_instance;
64   char *type;
65   char *type_instance;
66   table_definition_t *table;
67   _Bool is_instance;
68   oid_t *oids;
69   size_t oids_len;
70   double scale;
71   double shift;
72 };
73 typedef struct data_definition_s data_definition_t;
74
75 struct snmp_agent_ctx_s {
76   pthread_t thread;
77   pthread_mutex_t lock;
78   pthread_mutex_t agentx_lock;
79   struct tree *tp;
80
81   llist_t *tables;
82   llist_t *scalars;
83 };
84 typedef struct snmp_agent_ctx_s snmp_agent_ctx_t;
85
86 snmp_agent_ctx_t *g_agent = NULL;
87
88 #define CHECK_DD_TYPE(_dd, _p, _pi, _t, _ti)                                   \
89   (_dd->plugin ? !strcmp(_dd->plugin, _p) : 0) &&                              \
90       (_dd->plugin_instance ? !strcmp(_dd->plugin_instance, _pi) : 1) &&       \
91       (_dd->type ? !strcmp(_dd->type, _t) : 1) &&                              \
92       (_dd->type_instance ? !strcmp(_dd->type_instance, _ti) : 1)
93
94 static void *snmp_agent_thread_run(void *arg);
95 static int snmp_agent_register_oid(oid_t *oid, Netsnmp_Node_Handler *handler);
96 static int snmp_agent_set_vardata(void *dst_buf, size_t *dst_buf_len,
97                                   u_char asn_type, double scale, double shift,
98                                   const void *value, size_t len, int type);
99 static int snmp_agent_unregister_oid_index(oid_t *oid, int index);
100
101 static u_char snmp_agent_get_asn_type(oid *oid, size_t oid_len) {
102   struct tree *node = get_tree(oid, oid_len, g_agent->tp);
103
104   return (node != NULL ? mib_to_asn_type(node->type) : 0);
105 }
106
107 static char *snmp_agent_get_oid_name(oid *oid, size_t oid_len) {
108   struct tree *node = get_tree(oid, oid_len, g_agent->tp);
109
110   return (node != NULL ? node->label : NULL);
111 }
112
113 static int snmp_agent_oid_to_string(char *buf, size_t buf_size,
114                                     oid_t const *o) {
115   char oid_str[MAX_OID_LEN][16];
116   char *oid_str_ptr[MAX_OID_LEN];
117
118   for (size_t i = 0; i < o->oid_len; i++) {
119     ssnprintf(oid_str[i], sizeof(oid_str[i]), "%lu", (unsigned long)o->oid[i]);
120     oid_str_ptr[i] = oid_str[i];
121   }
122
123   return (strjoin(buf, buf_size, oid_str_ptr, o->oid_len, "."));
124 }
125
126 static void snmp_agent_dump_data(void) {
127 #if COLLECT_DEBUG
128   char temp[DATA_MAX_NAME_LEN];
129
130   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
131     table_definition_t *td = (table_definition_t *)te->value;
132
133     DEBUG(PLUGIN_NAME ": Table:");
134     DEBUG(PLUGIN_NAME ":   Name: %s", td->name);
135     if (td->index_oid.oid_len != 0) {
136       snmp_agent_oid_to_string(temp, sizeof(temp), &td->index_oid);
137       DEBUG(PLUGIN_NAME ":   IndexOID: %s", temp);
138     }
139     if (td->size_oid.oid_len != 0) {
140       snmp_agent_oid_to_string(temp, sizeof(temp), &td->size_oid);
141       DEBUG(PLUGIN_NAME ":   SizeOID: %s", temp);
142     }
143
144     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
145       data_definition_t *dd = (data_definition_t *)de->value;
146
147       DEBUG(PLUGIN_NAME ":   Column:");
148       DEBUG(PLUGIN_NAME ":     Name: %s", dd->name);
149       if (dd->plugin)
150         DEBUG(PLUGIN_NAME ":     Plugin: %s", dd->plugin);
151       if (dd->plugin_instance)
152         DEBUG(PLUGIN_NAME ":     PluginInstance: %s", dd->plugin_instance);
153       if (dd->is_instance)
154         DEBUG(PLUGIN_NAME ":     Instance: true");
155       if (dd->type)
156         DEBUG(PLUGIN_NAME ":     Type: %s", dd->type);
157       if (dd->type_instance)
158         DEBUG(PLUGIN_NAME ":     TypeInstance: %s", dd->type_instance);
159       for (int i = 0; i < dd->oids_len; i++) {
160         snmp_agent_oid_to_string(temp, sizeof(temp), &dd->oids[i]);
161         DEBUG(PLUGIN_NAME ":     OID[%d]: %s", i, temp);
162       }
163       DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
164       DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
165     }
166   }
167
168   for (llentry_t *e = llist_head(g_agent->scalars); e != NULL; e = e->next) {
169     data_definition_t *dd = (data_definition_t *)e->value;
170
171     DEBUG(PLUGIN_NAME ": Scalar:");
172     DEBUG(PLUGIN_NAME ":   Name: %s", dd->name);
173     if (dd->plugin)
174       DEBUG(PLUGIN_NAME ":   Plugin: %s", dd->plugin);
175     if (dd->plugin_instance)
176       DEBUG(PLUGIN_NAME ":   PluginInstance: %s", dd->plugin_instance);
177     if (dd->is_instance)
178       DEBUG(PLUGIN_NAME ":   Instance: true");
179     if (dd->type)
180       DEBUG(PLUGIN_NAME ":   Type: %s", dd->type);
181     if (dd->type_instance)
182       DEBUG(PLUGIN_NAME ":   TypeInstance: %s", dd->type_instance);
183     for (int i = 0; i < dd->oids_len; i++) {
184       snmp_agent_oid_to_string(temp, sizeof(temp), &dd->oids[i]);
185       DEBUG(PLUGIN_NAME ":   OID[%d]: %s", i, temp);
186     }
187     DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
188     DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
189   }
190 #endif /* COLLECT_DEBUG */
191 }
192
193 static int snmp_agent_validate_data(void) {
194
195   snmp_agent_dump_data();
196
197   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
198     table_definition_t *td = (table_definition_t *)te->value;
199
200     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
201       data_definition_t *dd = (data_definition_t *)de->value;
202
203       if (!dd->plugin) {
204         ERROR(PLUGIN_NAME ": Plugin not defined for '%s'.'%s'", td->name,
205               dd->name);
206         return (-EINVAL);
207       }
208
209       if (dd->plugin_instance) {
210         ERROR(PLUGIN_NAME ": PluginInstance should not be defined for table "
211                           "data type '%s'.'%s'",
212               td->name, dd->name);
213         return (-EINVAL);
214       }
215
216       if (dd->oids_len == 0) {
217         ERROR(PLUGIN_NAME ": No OIDs defined for '%s'.'%s'", td->name,
218               dd->name);
219         return (-EINVAL);
220       }
221
222       if (dd->is_instance) {
223
224         if (dd->type || dd->type_instance) {
225           ERROR(PLUGIN_NAME ": Type and TypeInstance are not valid for "
226                             "instance data '%s'.'%s'",
227                 td->name, dd->name);
228           return (-EINVAL);
229         }
230
231         if (dd->oids_len > 1) {
232           ERROR(
233               PLUGIN_NAME
234               ": Only one OID should be specified for instance data '%s'.'%s'",
235               td->name, dd->name);
236           return (-EINVAL);
237         }
238       } else {
239
240         if (!dd->type) {
241           ERROR(PLUGIN_NAME ": Type not defined for data '%s'.'%s'", td->name,
242                 dd->name);
243           return (-EINVAL);
244         }
245       }
246     }
247   }
248
249   for (llentry_t *e = llist_head(g_agent->scalars); e != NULL; e = e->next) {
250     data_definition_t *dd = (data_definition_t *)e->value;
251
252     if (!dd->plugin) {
253       ERROR(PLUGIN_NAME ": Plugin not defined for '%s'", dd->name);
254       return (-EINVAL);
255     }
256
257     if (dd->oids_len == 0) {
258       ERROR(PLUGIN_NAME ": No OIDs defined for '%s'", dd->name);
259       return (-EINVAL);
260     }
261
262     if (dd->is_instance) {
263       ERROR(PLUGIN_NAME
264             ": Instance flag can't be specified for scalar data '%s'",
265             dd->name);
266       return (-EINVAL);
267     }
268
269     if (!dd->type) {
270       ERROR(PLUGIN_NAME ": Type not defined for data '%s'", dd->name);
271       return (-EINVAL);
272     }
273   }
274
275   return (0);
276 }
277
278 static int snmp_agent_table_row_remove(table_definition_t *td,
279                                        const char *instance) {
280   int *index;
281   char *ins;
282
283   if ((c_avl_get(td->instance_index, instance, (void**) &index) != 0) ||
284       (c_avl_get(td->index_instance, index, (void**) &ins) != 0))
285     return (0);
286
287   pthread_mutex_lock(&g_agent->agentx_lock);
288
289   for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
290     data_definition_t *dd = (data_definition_t *)de->value;
291
292     for (int i = 0; i < dd->oids_len; i++)
293       snmp_agent_unregister_oid_index(&dd->oids[i], *index);
294   }
295
296   snmp_agent_unregister_oid_index(&td->index_oid, *index);
297
298   pthread_mutex_unlock(&g_agent->agentx_lock);
299
300   DEBUG(PLUGIN_NAME ": Removed row for '%s' table [%d, %s].", td->name, *index,
301         ins);
302
303   notification_t n = {NOTIF_WARNING, cdtime(), "", "", PLUGIN_NAME, "", "", "",
304                       NULL};
305   sstrncpy(n.host, hostname_g, sizeof(n.host));
306   sstrncpy(n.plugin_instance, ins, sizeof(n.plugin_instance));
307   ssnprintf(n.message, sizeof(n.message),
308             "Removed data row from table %s instance %s index %d", td->name,
309             ins, *index);
310   plugin_dispatch_notification(&n);
311
312   c_avl_remove(td->index_instance, index, NULL, (void**) &ins);
313   c_avl_remove(td->instance_index, instance, NULL, (void**) &index);
314   sfree(index);
315   sfree(ins);
316
317   return (0);
318 }
319
320 static int snmp_agent_clear_missing(const value_list_t *vl,
321                                     __attribute__((unused)) user_data_t *ud) {
322   if (vl == NULL)
323     return (-EINVAL);
324
325   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
326     table_definition_t *td = (table_definition_t *)te->value;
327
328     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
329       data_definition_t *dd = (data_definition_t *)de->value;
330
331       if (!dd->is_instance) {
332         if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
333                           vl->type_instance)) {
334           snmp_agent_table_row_remove(td, vl->plugin_instance);
335           return (0);
336         }
337       }
338     }
339   }
340
341   return (0);
342 }
343
344 static void snmp_agent_free_data(data_definition_t **dd) {
345
346   if (dd == NULL || *dd == NULL)
347     return;
348
349   /* unregister scalar type OID */
350   if ((*dd)->table == NULL) {
351     for (int i = 0; i < (*dd)->oids_len; i++)
352       unregister_mib((*dd)->oids[i].oid, (*dd)->oids[i].oid_len);
353   } else {
354     /* unregister all table OIDs */
355     int *index;
356     char *value;
357
358     c_avl_iterator_t *iter = c_avl_get_iterator((*dd)->table->index_instance);
359     while (c_avl_iterator_next(iter, (void *)&index, (void *)&value) == 0) {
360       for (int i = 0; i < (*dd)->oids_len; i++)
361         snmp_agent_unregister_oid_index(&(*dd)->oids[i], *index);
362     }
363     c_avl_iterator_destroy(iter);
364   }
365
366   sfree((*dd)->name);
367   sfree((*dd)->plugin);
368   sfree((*dd)->plugin_instance);
369   sfree((*dd)->type);
370   sfree((*dd)->type_instance);
371   sfree((*dd)->oids);
372
373   sfree(*dd);
374
375   return;
376 }
377
378 static void snmp_agent_free_table(table_definition_t **td) {
379
380   if (td == NULL || *td == NULL)
381     return;
382
383   if ((*td)->size_oid.oid_len)
384     unregister_mib((*td)->size_oid.oid, (*td)->size_oid.oid_len);
385
386   if ((*td)->index_oid.oid_len) {
387     int *index;
388     char *value;
389
390     c_avl_iterator_t *iter = c_avl_get_iterator((*td)->index_instance);
391     while (c_avl_iterator_next(iter, (void *)&index, (void *)&value) == 0)
392       snmp_agent_unregister_oid_index(&(*td)->index_oid, *index);
393
394     c_avl_iterator_destroy(iter);
395   }
396
397   for (llentry_t *de = llist_head((*td)->columns); de != NULL; de = de->next) {
398     data_definition_t *dd = (data_definition_t *)de->value;
399     snmp_agent_free_data(&dd);
400   }
401
402   llist_destroy((*td)->columns);
403
404   void *key = NULL;
405   void *value = NULL;
406
407   c_avl_destroy((*td)->index_instance);
408   (*td)->index_instance = NULL;
409
410   while (c_avl_pick((*td)->instance_index, &key, &value) == 0) {
411     sfree(key);
412     sfree(value);
413   }
414   c_avl_destroy((*td)->instance_index);
415   (*td)->instance_index = NULL;
416
417   sfree((*td)->name);
418   sfree(*td);
419
420   return;
421 }
422
423 static int
424 snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
425                              struct netsnmp_handler_registration_s *reginfo,
426                              struct netsnmp_agent_request_info_s *reqinfo,
427                              struct netsnmp_request_info_s *requests) {
428
429   if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
430     DEBUG(PLUGIN_NAME ": Not supported request mode (%d).", reqinfo->mode);
431     return SNMP_ERR_NOERROR;
432   }
433
434   oid_t oid;
435   char temp[DATA_MAX_NAME_LEN];
436
437   pthread_mutex_lock(&g_agent->lock);
438
439   memcpy(oid.oid, requests->requestvb->name,
440          sizeof(oid.oid[0]) * requests->requestvb->name_length);
441   oid.oid_len = requests->requestvb->name_length;
442
443   snmp_agent_oid_to_string(temp, sizeof(temp), &oid);
444
445   DEBUG(PLUGIN_NAME ": Get request received for table OID '%s'.", temp);
446
447   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
448     table_definition_t *td = (table_definition_t *)te->value;
449
450     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
451       data_definition_t *dd = (data_definition_t *)de->value;
452
453       for (int i = 0; i < dd->oids_len; i++) {
454
455         int ret = snmp_oid_ncompare(oid.oid, oid.oid_len,
456                                 dd->oids[i].oid, dd->oids[i].oid_len,
457                                 MIN(oid.oid_len, dd->oids[i].oid_len));
458         if (ret != 0) continue;
459
460         if (td->index_oid.oid_len) {
461           int index = oid.oid[oid.oid_len - 1];
462           char *instance;
463
464           ret = c_avl_get(td->index_instance, &index, (void **)&instance);
465           if (ret != 0) {
466             DEBUG(PLUGIN_NAME ": Nonexisting index '%d' requested.", index);
467             pthread_mutex_unlock(&g_agent->lock);
468             return SNMP_NOSUCHINSTANCE;
469           }
470
471           if (dd->is_instance) {
472
473             requests->requestvb->type = ASN_OCTET_STR;
474             snmp_set_var_typed_value(requests->requestvb,
475                                      requests->requestvb->type, instance,
476                                      strlen((instance)));
477
478             pthread_mutex_unlock(&g_agent->lock);
479
480             return SNMP_ERR_NOERROR;
481           }
482
483           format_name(temp, sizeof(temp), hostname_g, dd->plugin, instance,
484                       dd->type, dd->type_instance);
485           DEBUG(PLUGIN_NAME ": Identifier '%s'", temp);
486
487           value_t *values;
488           size_t values_num;
489           const data_set_t *ds;
490
491           ds = plugin_get_ds(dd->type);
492           if (ds == NULL) {
493             DEBUG(PLUGIN_NAME ": Data set not found for '%s' type.", dd->type);
494             pthread_mutex_unlock(&g_agent->lock);
495             return SNMP_NOSUCHINSTANCE;
496           }
497
498           ret = uc_get_value_by_name(temp, &values, &values_num);
499
500           if (ret != 0) {
501             ERROR(PLUGIN_NAME ": Failed to get value for '%s'.", temp);
502             pthread_mutex_unlock(&g_agent->lock);
503             return SNMP_NOSUCHINSTANCE;
504           }
505
506           assert(ds->ds_num == values_num);
507           assert(i < values_num);
508
509           char data[DATA_MAX_NAME_LEN];
510           size_t data_len = sizeof(data);
511           ret = snmp_agent_set_vardata(data, &data_len, dd->oids[i].type,
512                                        dd->scale, dd->shift, &values[i],
513                                        sizeof(values[i]), ds->ds[i].type);
514
515           sfree(values);
516           values_num = 0;
517
518           if (ret != 0) {
519             ERROR(PLUGIN_NAME ": Failed to convert '%s' value to snmp data.",
520                   temp);
521             pthread_mutex_unlock(&g_agent->lock);
522             return SNMP_NOSUCHINSTANCE;
523           }
524
525           requests->requestvb->type = dd->oids[i].type;
526           snmp_set_var_typed_value(requests->requestvb,
527                                    requests->requestvb->type, data, data_len);
528
529           pthread_mutex_unlock(&g_agent->lock);
530
531           return SNMP_ERR_NOERROR;
532
533         } else {
534           DEBUG(PLUGIN_NAME ": %s:%d NOT IMPLEMENTED.", __FUNCTION__, __LINE__);
535         }
536       }
537     }
538   }
539
540   pthread_mutex_unlock(&g_agent->lock);
541
542   return SNMP_NOSUCHINSTANCE;
543 }
544
545 static int
546 snmp_agent_table_index_oid_handler(struct netsnmp_mib_handler_s *handler,
547                               struct netsnmp_handler_registration_s *reginfo,
548                               struct netsnmp_agent_request_info_s *reqinfo,
549                               struct netsnmp_request_info_s *requests) {
550
551   if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
552     DEBUG(PLUGIN_NAME ": Not supported request mode (%d).", reqinfo->mode);
553     return SNMP_ERR_NOERROR;
554   }
555
556   oid_t oid;
557
558   pthread_mutex_lock(&g_agent->lock);
559
560   memcpy(oid.oid, requests->requestvb->name,
561          sizeof(oid.oid[0]) * requests->requestvb->name_length);
562   oid.oid_len = requests->requestvb->name_length;
563
564   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
565     table_definition_t *td = (table_definition_t *)te->value;
566
567     if (td->index_oid.oid_len &&
568           (snmp_oid_ncompare(oid.oid, oid.oid_len, td->index_oid.oid,
569                              td->index_oid.oid_len,
570                              MIN(oid.oid_len, td->index_oid.oid_len)) == 0)) {
571
572       DEBUG(PLUGIN_NAME ": Handle '%s' table index OID.", td->name);
573
574       int index = oid.oid[oid.oid_len - 1];
575       char *instance;
576
577       int ret = c_avl_get(td->index_instance, &index, (void **)&instance);
578       if (ret != 0) {
579         /* nonexisting index requested */
580         pthread_mutex_unlock(&g_agent->lock);
581         return SNMP_NOSUCHINSTANCE;
582       }
583
584       requests->requestvb->type = ASN_INTEGER;
585       snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
586                                &index, sizeof(index));
587
588       pthread_mutex_unlock(&g_agent->lock);
589
590       return SNMP_ERR_NOERROR;
591     }
592   }
593
594   pthread_mutex_unlock(&g_agent->lock);
595
596   return SNMP_NOSUCHINSTANCE;
597 }
598
599 static int
600 snmp_agent_table_size_oid_handler(struct netsnmp_mib_handler_s *handler,
601                               struct netsnmp_handler_registration_s *reginfo,
602                               struct netsnmp_agent_request_info_s *reqinfo,
603                               struct netsnmp_request_info_s *requests) {
604
605   if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
606     DEBUG(PLUGIN_NAME ": Not supported request mode (%d).", reqinfo->mode);
607     return SNMP_ERR_NOERROR;
608   }
609
610   oid_t oid;
611
612   pthread_mutex_lock(&g_agent->lock);
613
614   memcpy(oid.oid, requests->requestvb->name,
615          sizeof(oid.oid[0]) * requests->requestvb->name_length);
616   oid.oid_len = requests->requestvb->name_length;
617
618   DEBUG(PLUGIN_NAME ": Get request received for table size OID.");
619
620   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
621     table_definition_t *td = (table_definition_t *)te->value;
622
623     if (td->size_oid.oid_len &&
624         (snmp_oid_ncompare(oid.oid, oid.oid_len, td->size_oid.oid,
625                            td->size_oid.oid_len,
626                            MIN(oid.oid_len, td->size_oid.oid_len)) == 0)) {
627       DEBUG(PLUGIN_NAME ": Handle '%s' table size OID.", td->name);
628
629       long size = c_avl_size(td->index_instance);
630
631       requests->requestvb->type = td->size_oid.type;
632       snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
633                                &size, sizeof(size));
634
635       pthread_mutex_unlock(&g_agent->lock);
636
637       return SNMP_ERR_NOERROR;
638     }
639   }
640
641   pthread_mutex_unlock(&g_agent->lock);
642
643   return SNMP_NOSUCHINSTANCE;
644 }
645
646
647 static int
648 snmp_agent_scalar_oid_handler(struct netsnmp_mib_handler_s *handler,
649                               struct netsnmp_handler_registration_s *reginfo,
650                               struct netsnmp_agent_request_info_s *reqinfo,
651                               struct netsnmp_request_info_s *requests) {
652
653   if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
654     DEBUG(PLUGIN_NAME ": Not supported request mode (%d).", reqinfo->mode);
655     return SNMP_ERR_NOERROR;
656   }
657
658   int ret;
659   oid_t oid;
660   char temp[DATA_MAX_NAME_LEN];
661
662   pthread_mutex_lock(&g_agent->lock);
663
664   memcpy(oid.oid, requests->requestvb->name,
665          sizeof(oid.oid[0]) * requests->requestvb->name_length);
666   oid.oid_len = requests->requestvb->name_length;
667
668   snmp_agent_oid_to_string(temp, sizeof(temp), &oid);
669
670   DEBUG(PLUGIN_NAME ": Get request received for scalar OID '%s'.", temp);
671
672   for (llentry_t *de = llist_head(g_agent->scalars); de != NULL; de = de->next) {
673     data_definition_t *dd = (data_definition_t *)de->value;
674
675     for (int i = 0; i < dd->oids_len; i++) {
676
677       ret = snmp_oid_compare(oid.oid, oid.oid_len,
678                              dd->oids[i].oid, dd->oids[i].oid_len);
679       if (ret != 0) continue;
680
681       format_name(temp, sizeof(temp), hostname_g, dd->plugin,
682                   dd->plugin_instance, dd->type, dd->type_instance);
683       DEBUG(PLUGIN_NAME ": Identifier '%s'", temp);
684
685       value_t *values;
686       size_t values_num;
687       const data_set_t *ds;
688
689       ds = plugin_get_ds(dd->type);
690       if (ds == NULL) {
691         DEBUG(PLUGIN_NAME ": Data set not found for '%s' type", dd->type);
692         pthread_mutex_unlock(&g_agent->lock);
693         return SNMP_NOSUCHINSTANCE;
694       }
695
696       ret = uc_get_value_by_name(temp, &values, &values_num);
697
698       if (ret != 0) {
699         ERROR(PLUGIN_NAME ": Failed to get value for '%s'.", temp);
700         pthread_mutex_unlock(&g_agent->lock);
701         return SNMP_NOSUCHINSTANCE;
702       }
703
704       assert(ds->ds_num == values_num);
705       assert(i < values_num);
706
707       char data[DATA_MAX_NAME_LEN];
708       size_t data_len = sizeof(data);
709       ret = snmp_agent_set_vardata(data, &data_len, dd->oids[i].type,
710                                    dd->scale, dd->shift, &values[i],
711                                    sizeof(values[i]), ds->ds[i].type);
712
713       sfree(values);
714       values_num = 0;
715
716       if (ret != 0) {
717         ERROR(PLUGIN_NAME ": Failed to convert '%s' value to snmp data.", temp);
718         pthread_mutex_unlock(&g_agent->lock);
719         return SNMP_NOSUCHINSTANCE;
720       }
721
722       requests->requestvb->type = dd->oids[i].type;
723       snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
724                                data, data_len);
725
726       pthread_mutex_unlock(&g_agent->lock);
727
728       return SNMP_ERR_NOERROR;
729     }
730   }
731
732   pthread_mutex_unlock(&g_agent->lock);
733
734   return SNMP_NOSUCHINSTANCE;
735 }
736
737 static int snmp_agent_register_table_oids(void) {
738
739   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
740     table_definition_t *td = (table_definition_t *)te->value;
741
742     if (td->size_oid.oid_len != 0) {
743       td->size_oid.type =
744           snmp_agent_get_asn_type(td->size_oid.oid, td->size_oid.oid_len);
745       td->size_oid.oid_len++;
746       int ret = snmp_agent_register_oid(&td->size_oid,
747                                         snmp_agent_table_size_oid_handler);
748       if (ret != 0)
749         return ret;
750     }
751
752     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
753       data_definition_t *dd = (data_definition_t *)de->value;
754
755       for (int i = 0; i < dd->oids_len; i++) {
756         dd->oids[i].type =
757             snmp_agent_get_asn_type(dd->oids[i].oid, dd->oids[i].oid_len);
758       }
759     }
760   }
761
762   return (0);
763 }
764
765 static int snmp_agent_register_scalar_oids(void) {
766
767   for (llentry_t *e = llist_head(g_agent->scalars); e != NULL; e = e->next) {
768     data_definition_t *dd = (data_definition_t *)e->value;
769
770     for (int i = 0; i < dd->oids_len; i++) {
771
772       dd->oids[i].type =
773           snmp_agent_get_asn_type(dd->oids[i].oid, dd->oids[i].oid_len);
774
775       int ret =
776           snmp_agent_register_oid(&dd->oids[i], snmp_agent_scalar_oid_handler);
777       if (ret != 0)
778         return ret;
779     }
780   }
781
782   return (0);
783 }
784
785 static int snmp_agent_config_data_oids(data_definition_t *dd,
786                                        oconfig_item_t *ci) {
787   if (ci->values_num < 1) {
788     WARNING(PLUGIN_NAME ": `OIDs' needs at least one argument.");
789     return (-EINVAL);
790   }
791
792   for (int i = 0; i < ci->values_num; i++)
793     if (ci->values[i].type != OCONFIG_TYPE_STRING) {
794       WARNING(PLUGIN_NAME ": `OIDs' needs only string argument.");
795       return (-EINVAL);
796     }
797
798   if (dd->oids != NULL)
799     sfree(dd->oids);
800   dd->oids_len = 0;
801   dd->oids = calloc(ci->values_num, sizeof(*dd->oids));
802   if (dd->oids == NULL)
803     return (-ENOMEM);
804   dd->oids_len = (size_t)ci->values_num;
805
806   for (int i = 0; i < ci->values_num; i++) {
807     dd->oids[i].oid_len = MAX_OID_LEN;
808
809     if (NULL == snmp_parse_oid(ci->values[i].value.string, dd->oids[i].oid,
810                                &dd->oids[i].oid_len)) {
811       ERROR(PLUGIN_NAME ": snmp_parse_oid (%s) failed.",
812             ci->values[i].value.string);
813       sfree(dd->oids);
814       dd->oids_len = 0;
815       return (-1);
816     }
817   }
818
819   return (0);
820 }
821
822 static int snmp_agent_config_table_size_oid(table_definition_t *td,
823                                             oconfig_item_t *ci) {
824   if (ci->values_num < 1) {
825     WARNING(PLUGIN_NAME ": `TableSizeOID' is empty.");
826     return (-EINVAL);
827   }
828
829   if (ci->values[0].type != OCONFIG_TYPE_STRING) {
830     WARNING(PLUGIN_NAME ": `TableSizeOID' needs only string argument.");
831     return (-EINVAL);
832   }
833
834   td->size_oid.oid_len = MAX_OID_LEN;
835
836   if (NULL == snmp_parse_oid(ci->values[0].value.string, td->size_oid.oid,
837                              &td->size_oid.oid_len)) {
838     ERROR(PLUGIN_NAME ": Failed to parse table size OID (%s)",
839           ci->values[0].value.string);
840     td->size_oid.oid_len = 0;
841     return (-EINVAL);
842   }
843
844   return (0);
845 }
846
847 static int snmp_agent_config_table_index_oid(table_definition_t *td,
848                                              oconfig_item_t *ci) {
849
850   if (ci->values_num < 1) {
851     WARNING(PLUGIN_NAME ": `IndexOID' is empty.");
852     return (-EINVAL);
853   }
854
855   if (ci->values[0].type != OCONFIG_TYPE_STRING) {
856     WARNING(PLUGIN_NAME ": `IndexOID' needs only string argument.");
857     return (-EINVAL);
858   }
859
860   td->index_oid.oid_len = MAX_OID_LEN;
861
862   if (NULL == snmp_parse_oid(ci->values[0].value.string, td->index_oid.oid,
863                              &td->index_oid.oid_len)) {
864     ERROR(PLUGIN_NAME ": Failed to parse table index OID (%s)",
865           ci->values[0].value.string);
866     td->index_oid.oid_len = 0;
867     return (-EINVAL);
868   }
869
870   return (0);
871 }
872
873 static int snmp_agent_config_table_data(table_definition_t *td,
874                                         oconfig_item_t *ci) {
875   data_definition_t *dd;
876   int ret = 0;
877
878   assert(ci != NULL);
879
880   dd = calloc(1, sizeof(*dd));
881   if (dd == NULL) {
882     ERROR(PLUGIN_NAME ": Failed to allocate memory for table data definition");
883     return (-ENOMEM);
884   }
885
886   ret = cf_util_get_string(ci, &dd->name);
887   if (ret != 0) {
888     sfree(dd);
889     return (-1);
890   }
891
892   dd->scale = 1.0;
893   dd->shift = 0.0;
894
895   dd->table = td;
896
897   for (int i = 0; i < ci->children_num; i++) {
898     oconfig_item_t *option = ci->children + i;
899
900     if (strcasecmp("Instance", option->key) == 0)
901       ret = cf_util_get_boolean(option, &dd->is_instance);
902     else if (strcasecmp("Plugin", option->key) == 0)
903       ret = cf_util_get_string(option, &dd->plugin);
904     else if (strcasecmp("PluginInstance", option->key) == 0)
905       ret = cf_util_get_string(option, &dd->plugin_instance);
906     else if (strcasecmp("Type", option->key) == 0)
907       ret = cf_util_get_string(option, &dd->type);
908     else if (strcasecmp("TypeInstance", option->key) == 0)
909       ret = cf_util_get_string(option, &dd->type_instance);
910     else if (strcasecmp("Shift", option->key) == 0)
911       ret = cf_util_get_double(option, &dd->shift);
912     else if (strcasecmp("Scale", option->key) == 0)
913       ret = cf_util_get_double(option, &dd->scale);
914     else if (strcasecmp("OIDs", option->key) == 0)
915       ret = snmp_agent_config_data_oids(dd, option);
916     else {
917       WARNING(PLUGIN_NAME ": Option `%s' not allowed here.", option->key);
918       ret = -1;
919     }
920
921     if (ret != 0) {
922       snmp_agent_free_data(&dd);
923       return (-1);
924     }
925   }
926
927   llentry_t *entry = llentry_create(dd->name, dd);
928   if (entry == NULL) {
929     snmp_agent_free_data(&dd);
930     return (-ENOMEM);
931   }
932
933   llist_append(td->columns, entry);
934
935   return (0);
936 }
937
938 static int snmp_agent_config_data(oconfig_item_t *ci) {
939   data_definition_t *dd;
940   int ret = 0;
941
942   assert(ci != NULL);
943
944   dd = calloc(1, sizeof(*dd));
945   if (dd == NULL) {
946     ERROR(PLUGIN_NAME ": Failed to allocate memory for data definition");
947     return (-ENOMEM);
948   }
949
950   ret = cf_util_get_string(ci, &dd->name);
951   if (ret != 0) {
952     free(dd);
953     return (-1);
954   }
955
956   dd->scale = 1.0;
957   dd->shift = 0.0;
958
959   for (int i = 0; i < ci->children_num; i++) {
960     oconfig_item_t *option = ci->children + i;
961
962     if (strcasecmp("Instance", option->key) == 0)
963       ret = cf_util_get_boolean(option, &dd->is_instance);
964     else if (strcasecmp("Plugin", option->key) == 0)
965       ret = cf_util_get_string(option, &dd->plugin);
966     else if (strcasecmp("PluginInstance", option->key) == 0)
967       ret = cf_util_get_string(option, &dd->plugin_instance);
968     else if (strcasecmp("Type", option->key) == 0)
969       ret = cf_util_get_string(option, &dd->type);
970     else if (strcasecmp("TypeInstance", option->key) == 0)
971       ret = cf_util_get_string(option, &dd->type_instance);
972     else if (strcasecmp("Shift", option->key) == 0)
973       ret = cf_util_get_double(option, &dd->shift);
974     else if (strcasecmp("Scale", option->key) == 0)
975       ret = cf_util_get_double(option, &dd->scale);
976     else if (strcasecmp("OIDs", option->key) == 0)
977       ret = snmp_agent_config_data_oids(dd, option);
978     else {
979       WARNING(PLUGIN_NAME ": Option `%s' not allowed here.", option->key);
980       ret = -1;
981     }
982
983     if (ret != 0) {
984       snmp_agent_free_data(&dd);
985       return (-1);
986     }
987   }
988
989   llentry_t *entry = llentry_create(dd->name, dd);
990   if (entry == NULL) {
991     snmp_agent_free_data(&dd);
992     return (-ENOMEM);
993   }
994
995   llist_append(g_agent->scalars, entry);
996
997   return (0);
998 }
999
1000 static int num_compare(const int *a, const int *b) {
1001   assert((a != NULL) && (b != NULL));
1002   if (*a < *b)
1003     return (-1);
1004   else if (*a > *b)
1005     return (1);
1006   else
1007     return (0);
1008 }
1009
1010 static int snmp_agent_config_table(oconfig_item_t *ci) {
1011   table_definition_t *td;
1012   int ret = 0;
1013
1014   assert(ci != NULL);
1015
1016   td = calloc(1, sizeof(*td));
1017   if (td == NULL) {
1018     ERROR(PLUGIN_NAME ": Failed to allocate memory for table definition.");
1019     return (-ENOMEM);
1020   }
1021
1022   ret = cf_util_get_string(ci, &td->name);
1023   if (ret != 0) {
1024     sfree(td);
1025     return (-1);
1026   }
1027
1028   td->columns = llist_create();
1029   if (td->columns == NULL) {
1030     ERROR(PLUGIN_NAME ": Failed to allocate memory for columns list.");
1031     snmp_agent_free_table(&td);
1032     return (-ENOMEM);
1033   }
1034
1035   for (int i = 0; i < ci->children_num; i++) {
1036     oconfig_item_t *option = ci->children + i;
1037
1038     if (strcasecmp("IndexOID", option->key) == 0)
1039       ret = snmp_agent_config_table_index_oid(td, option);
1040     else if (strcasecmp("SizeOID", option->key) == 0)
1041       ret = snmp_agent_config_table_size_oid(td, option);
1042     else if (strcasecmp("Data", option->key) == 0)
1043       ret = snmp_agent_config_table_data(td, option);
1044     else {
1045       WARNING(PLUGIN_NAME ": Option `%s' not allowed here.", option->key);
1046       ret = -1;
1047     }
1048
1049     if (ret != 0) {
1050       snmp_agent_free_table(&td);
1051       return (-ENOMEM);
1052     }
1053   }
1054
1055   llentry_t *entry = llentry_create(td->name, td);
1056   if (entry == NULL) {
1057     snmp_agent_free_table(&td);
1058     return (-ENOMEM);
1059   }
1060
1061   td->instance_index =
1062       c_avl_create((int (*)(const void *, const void *))strcmp);
1063   if (td->instance_index == NULL) {
1064     snmp_agent_free_table(&td);
1065     return (-ENOMEM);
1066   }
1067
1068   td->index_instance =
1069       c_avl_create((int (*)(const void *, const void *))num_compare);
1070   if (td->index_instance == NULL) {
1071     snmp_agent_free_table(&td);
1072     return (-ENOMEM);
1073   }
1074
1075   llist_append(g_agent->tables, entry);
1076
1077   return (0);
1078 }
1079
1080 static int snmp_agent_get_value_from_ds_type(const value_t *val, int type,
1081                                              double scale, double shift,
1082                                              long *value) {
1083   switch (type) {
1084   case DS_TYPE_COUNTER:
1085     *value = (long)((val->counter * scale) + shift);
1086     break;
1087   case DS_TYPE_ABSOLUTE:
1088     *value = (long)((val->absolute * scale) + shift);
1089     break;
1090   case DS_TYPE_DERIVE:
1091     *value = (long)((val->derive * scale) + shift);
1092     break;
1093   case DS_TYPE_GAUGE:
1094     *value = (long)((val->gauge * scale) + shift);
1095     break;
1096   case TYPE_STRING:
1097     break;
1098   default:
1099     ERROR(PLUGIN_NAME ": Unknown data source type: %i.", type);
1100     return (-EINVAL);
1101   }
1102
1103   return (0);
1104 }
1105
1106 static int snmp_agent_set_vardata(void *data, size_t *data_len, u_char asn_type,
1107                                   double scale, double shift, const void *value,
1108                                   size_t len, int type) {
1109
1110   int ret;
1111   netsnmp_vardata var;
1112   const value_t *val;
1113   long new_value = 0;
1114
1115   val = value;
1116   var.string = (u_char *)data;
1117
1118   ret = snmp_agent_get_value_from_ds_type(val, type, scale, shift, &new_value);
1119   if (ret != 0)
1120     return ret;
1121
1122   switch (asn_type) {
1123   case ASN_INTEGER:
1124   case ASN_UINTEGER:
1125   case ASN_COUNTER:
1126   case ASN_TIMETICKS:
1127   case ASN_GAUGE:
1128     if (*data_len < sizeof(*var.integer))
1129       return (-EINVAL);
1130     *var.integer = new_value;
1131     *data_len = sizeof(*var.integer);
1132     break;
1133   case ASN_COUNTER64:
1134     if (*data_len < sizeof(*var.counter64))
1135       return (-EINVAL);
1136     var.counter64->high = (u_long)((int64_t)new_value >> 32);
1137     var.counter64->low = (u_long)((int64_t)new_value & 0xFFFFFFFF);
1138     *data_len = sizeof(*var.counter64);
1139     break;
1140   case ASN_OCTET_STR:
1141     if (type == DS_TYPE_GAUGE) {
1142       char buf[DATA_MAX_NAME_LEN];
1143       snprintf(buf, sizeof(buf), "%.2f", val->gauge);
1144       if (*data_len < strlen(buf))
1145         return (-EINVAL);
1146       *data_len = strlen(buf);
1147       memcpy(var.string, buf, *data_len);
1148     } else {
1149       ERROR(PLUGIN_NAME ": Failed to convert %d ds type to %d asn type.", type,
1150             asn_type);
1151       return (-EINVAL);
1152     }
1153     break;
1154   default:
1155     ERROR(PLUGIN_NAME ": Failed to convert %d ds type to %d asn type.", type,
1156           asn_type);
1157     return (-EINVAL);
1158   }
1159
1160   return (0);
1161 }
1162
1163 static int snmp_agent_register_oid_index(oid_t *oid, int index,
1164                                          Netsnmp_Node_Handler *handler) {
1165   oid_t new_oid;
1166   memcpy(&new_oid, oid, sizeof(*oid));
1167   new_oid.oid[new_oid.oid_len++] = index;
1168   return snmp_agent_register_oid(&new_oid, handler);
1169 }
1170
1171 static int snmp_agent_unregister_oid_index(oid_t *oid, int index) {
1172   oid_t new_oid;
1173   memcpy(&new_oid, oid, sizeof(*oid));
1174   new_oid.oid[new_oid.oid_len++] = index;
1175   return unregister_mib(new_oid.oid, new_oid.oid_len);
1176 }
1177
1178 static int snmp_agent_update_index(table_definition_t *td,
1179                                    const char *instance) {
1180
1181   if (c_avl_get(td->instance_index, instance, NULL) == 0)
1182     return (0);
1183
1184   int ret;
1185   int *index;
1186   char *ins;
1187
1188   ins = strdup(instance);
1189   if (ins == NULL)
1190     return (-ENOMEM);
1191
1192   index = calloc(1, sizeof(*index));
1193   if (index == NULL) {
1194     sfree(ins);
1195     return (-ENOMEM);
1196   }
1197
1198   *index = c_avl_size(td->instance_index) + 1;
1199
1200   ret = c_avl_insert(td->instance_index, ins, index);
1201   if (ret != 0) {
1202     sfree(ins);
1203     sfree(index);
1204     return ret;
1205   }
1206
1207   ret = c_avl_insert(td->index_instance, index, ins);
1208   if (ret < 0) {
1209     DEBUG(PLUGIN_NAME ": Failed to update index_instance for '%s' table.",
1210           td->name);
1211     return ret;
1212   }
1213
1214   DEBUG(PLUGIN_NAME ": Updated index for '%s' table [%d, %s].", td->name,
1215         *index, ins);
1216
1217   /* need to generate index for the table */
1218   if (td->index_oid.oid_len) {
1219
1220     ret = snmp_agent_register_oid_index(&td->index_oid, *index,
1221                                         snmp_agent_table_index_oid_handler);
1222     if (ret != 0)
1223       return ret;
1224
1225     /* register new oids for all columns */
1226     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
1227       data_definition_t *dd = (data_definition_t *)de->value;
1228
1229       for (int i = 0; i < dd->oids_len; i++) {
1230         ret = snmp_agent_register_oid_index(&dd->oids[i], *index,
1231                                             snmp_agent_table_oid_handler);
1232         if (ret != 0)
1233           return ret;
1234       }
1235     }
1236   }
1237
1238   notification_t n = {NOTIF_OKAY, cdtime(), "", "", PLUGIN_NAME, "", "", "",
1239                       NULL};
1240   sstrncpy(n.host, hostname_g, sizeof(n.host));
1241   sstrncpy(n.plugin_instance, ins, sizeof(n.plugin_instance));
1242   ssnprintf(n.message, sizeof(n.message),
1243             "Data row added to table %s instance %s index %d", td->name, ins,
1244             *index);
1245   plugin_dispatch_notification(&n);
1246
1247   return (0);
1248 }
1249
1250 static int snmp_agent_write(value_list_t const *vl) {
1251
1252   if (vl == NULL)
1253     return (-EINVAL);
1254
1255   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
1256     table_definition_t *td = (table_definition_t *)te->value;
1257
1258     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
1259       data_definition_t *dd = (data_definition_t *)de->value;
1260
1261       if (!dd->is_instance) {
1262         if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
1263                           vl->type_instance)) {
1264           snmp_agent_update_index(td, vl->plugin_instance);
1265           return (0);
1266         }
1267       }
1268     }
1269   }
1270
1271   return (0);
1272 }
1273
1274 static int snmp_agent_collect(const data_set_t *ds, const value_list_t *vl,
1275                               user_data_t __attribute__((unused)) * user_data) {
1276
1277   pthread_mutex_lock(&g_agent->lock);
1278
1279   snmp_agent_write(vl);
1280
1281   pthread_mutex_unlock(&g_agent->lock);
1282
1283   return (0);
1284 }
1285
1286 static int snmp_agent_preinit(void) {
1287   if (g_agent != NULL) {
1288     /* already initialized if config callback was called before init callback */
1289     return (0);
1290   }
1291
1292   g_agent = calloc(1, sizeof(*g_agent));
1293   if (g_agent == NULL) {
1294     ERROR(PLUGIN_NAME ": Failed to allocate memory for snmp agent context.");
1295     return (-ENOMEM);
1296   }
1297
1298   g_agent->tables = llist_create();
1299   g_agent->scalars = llist_create();
1300
1301   int err;
1302   /* make us a agentx client. */
1303   err = netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
1304                                1);
1305   if (err != 0) {
1306     ERROR(PLUGIN_NAME ": Failed to set agent role (%d)", err);
1307     return (-1);
1308   }
1309
1310   /*
1311    *  For SNMP debug purposes uses snmp_set_do_debugging(1);
1312    */
1313
1314   /* initialize the agent library */
1315   err = init_agent(PLUGIN_NAME);
1316   if (err != 0) {
1317     ERROR(PLUGIN_NAME ": Failed to initialize the agent library (%d)", err);
1318     return (-1);
1319   }
1320
1321   init_snmp(PLUGIN_NAME);
1322
1323   g_agent->tp = read_all_mibs();
1324
1325   return (0);
1326 }
1327
1328 static int snmp_agent_init(void) {
1329   int ret;
1330
1331   ret = snmp_agent_preinit();
1332   if (ret != 0)
1333     return ret;
1334
1335   ret = snmp_agent_register_scalar_oids();
1336   if (ret != 0)
1337     return ret;
1338
1339   ret = snmp_agent_register_table_oids();
1340   if (ret != 0)
1341     return ret;
1342
1343   /* create a second thread to listen for requests from AgentX*/
1344   ret = pthread_create(&g_agent->thread, NULL, &snmp_agent_thread_run, NULL);
1345   if (ret != 0) {
1346     ERROR(PLUGIN_NAME ": Failed to create a separate thread, err %u", ret);
1347     return ret;
1348   }
1349
1350   ret = pthread_mutex_init(&g_agent->lock, NULL);
1351   if (ret != 0) {
1352     ERROR(PLUGIN_NAME ": Failed to initialize mutex, err %u", ret);
1353     return ret;
1354   }
1355
1356   ret = pthread_mutex_init(&g_agent->agentx_lock, NULL);
1357   if (ret != 0) {
1358     ERROR(PLUGIN_NAME ": Failed to initialize AgentX mutex, err %u", ret);
1359     return ret;
1360   }
1361
1362   return (0);
1363 }
1364
1365 static void *snmp_agent_thread_run(void __attribute__((unused)) * arg) {
1366   INFO(PLUGIN_NAME ": Thread is up and running");
1367
1368   for (;;) {
1369     pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
1370
1371     pthread_mutex_lock(&g_agent->agentx_lock);
1372     agent_check_and_process(0); /* 0 == don't block */
1373     pthread_mutex_unlock(&g_agent->agentx_lock);
1374
1375     pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1376     usleep(10);
1377   }
1378
1379   pthread_exit(0);
1380 }
1381
1382 static int snmp_agent_register_oid(oid_t *oid, Netsnmp_Node_Handler *handler) {
1383   netsnmp_handler_registration *reg;
1384   char temp[DATA_MAX_NAME_LEN];
1385   char *oid_name = snmp_agent_get_oid_name(oid->oid, oid->oid_len - 1);
1386
1387   snmp_agent_oid_to_string(temp, sizeof(temp), oid);
1388
1389   if (oid_name == NULL) {
1390     WARNING(PLUGIN_NAME
1391             ": Skipped registration: OID (%s) is not found in main tree", temp);
1392     return (0);
1393   }
1394
1395   reg = netsnmp_create_handler_registration(oid_name, handler, oid->oid,
1396                                             oid->oid_len, HANDLER_CAN_RONLY);
1397   if (reg == NULL) {
1398     ERROR(PLUGIN_NAME ": Failed to create handler registration for OID (%s)",
1399           temp);
1400     return (-1);
1401   }
1402
1403   pthread_mutex_lock(&g_agent->agentx_lock);
1404
1405   if (netsnmp_register_instance(reg) != MIB_REGISTERED_OK) {
1406     ERROR(PLUGIN_NAME ": Failed to register handler for OID (%s)", temp);
1407     pthread_mutex_unlock(&g_agent->agentx_lock);
1408     return (-1);
1409   }
1410
1411   pthread_mutex_unlock(&g_agent->agentx_lock);
1412
1413   DEBUG(PLUGIN_NAME ": Registered handler for OID (%s)", temp);
1414
1415   return (0);
1416 }
1417
1418 static int snmp_agent_free_config(void) {
1419
1420   if (g_agent == NULL)
1421     return (-EINVAL);
1422
1423   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next)
1424     snmp_agent_free_table((table_definition_t **)&te->value);
1425   llist_destroy(g_agent->tables);
1426
1427   for (llentry_t *de = llist_head(g_agent->scalars); de != NULL; de = de->next)
1428     snmp_agent_free_data((data_definition_t **)&de->value);
1429   llist_destroy(g_agent->scalars);
1430
1431   return (0);
1432 }
1433
1434 static int snmp_agent_shutdown(void) {
1435   int ret = 0;
1436
1437   DEBUG(PLUGIN_NAME ": snmp_agent_shutdown.");
1438
1439   if (g_agent == NULL) {
1440     ERROR(PLUGIN_NAME ": snmp_agent_shutdown: plugin not initialized.");
1441     return (-EINVAL);
1442   }
1443
1444   if (pthread_cancel(g_agent->thread) != 0)
1445     ERROR(PLUGIN_NAME ": snmp_agent_shutdown: failed to cancel the thread.");
1446
1447   if (pthread_join(g_agent->thread, NULL) != 0)
1448     ERROR(PLUGIN_NAME ": snmp_agent_shutdown: failed to join the thread.");
1449
1450   snmp_agent_free_config();
1451
1452   snmp_shutdown(PLUGIN_NAME);
1453
1454   pthread_mutex_destroy(&g_agent->lock);
1455   pthread_mutex_destroy(&g_agent->agentx_lock);
1456
1457   sfree(g_agent);
1458
1459   return ret;
1460 }
1461
1462 static int snmp_agent_config(oconfig_item_t *ci) {
1463
1464   int ret = snmp_agent_preinit();
1465
1466   if (ret != 0) {
1467     sfree(g_agent);
1468     return (-EINVAL);
1469   }
1470
1471   for (int i = 0; i < ci->children_num; i++) {
1472     oconfig_item_t *child = ci->children + i;
1473     if (strcasecmp("Data", child->key) == 0) {
1474       ret = snmp_agent_config_data(child);
1475     } else if (strcasecmp("Table", child->key) == 0) {
1476       ret = snmp_agent_config_table(child);
1477     } else {
1478       ERROR(PLUGIN_NAME ": Unknown configuration option `%s'.", child->key);
1479       ret = (-EINVAL);
1480     }
1481
1482     if (ret != 0) {
1483       ERROR(PLUGIN_NAME ": Failed to parse configuration.");
1484       snmp_agent_free_config();
1485       snmp_shutdown(PLUGIN_NAME);
1486       sfree(g_agent);
1487       return (-EINVAL);
1488     }
1489   }
1490
1491   ret = snmp_agent_validate_data();
1492   if (ret != 0) {
1493     ERROR(PLUGIN_NAME ": Invalid configuration provided.");
1494     snmp_agent_free_config();
1495     snmp_shutdown(PLUGIN_NAME);
1496     sfree(g_agent);
1497     return (-EINVAL);
1498   }
1499
1500   return (0);
1501 }
1502
1503 void module_register(void) {
1504   plugin_register_init(PLUGIN_NAME, snmp_agent_init);
1505   plugin_register_complex_config(PLUGIN_NAME, snmp_agent_config);
1506   plugin_register_write(PLUGIN_NAME, snmp_agent_collect, NULL);
1507   plugin_register_missing(PLUGIN_NAME, snmp_agent_clear_missing, NULL);
1508   plugin_register_shutdown(PLUGIN_NAME, snmp_agent_shutdown);
1509 }