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