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