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