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