2 * collectd - src/snmp_agent.c
4 * Copyright(c) 2017 Intel Corporation. All rights reserved.
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
25 * Roman Korynkevych <romanx.korynkevych@intel.com>
26 * Serhiy Pshyk <serhiyx.pshyk@intel.com>
27 * Marcin Mozejko <marcinx.mozejko@intel.com>
33 #include "utils_avltree.h"
34 #include "utils_cache.h"
35 #include "utils_llist.h"
37 #include <net-snmp/net-snmp-config.h>
39 #include <net-snmp/net-snmp-includes.h>
41 #include <net-snmp/agent/net-snmp-agent-includes.h>
43 #define PLUGIN_NAME "snmp_agent"
44 #define TYPE_STRING -1
45 #define MAX_INDEX_TYPES 5
47 /* Identifies index key origin */
51 INDEX_PLUGIN_INSTANCE,
55 typedef enum index_key_e index_key_t;
62 typedef struct oid_s oid_t;
64 struct table_definition_s {
69 c_avl_tree_t *instance_index;
70 c_avl_tree_t *index_instance;
71 index_key_t indexes[MAX_INDEX_TYPES]; /* Stores information about what each
72 index key represents */
74 netsnmp_variable_list *index_list_cont; /* Index key container used for
75 generating as well as parsing
76 OIDs, not thread-safe */
78 typedef struct table_definition_s table_definition_t;
80 struct data_definition_s {
83 char *plugin_instance;
86 const table_definition_t *table;
87 bool is_index_key; /* indicates if table column is an index key */
88 int index_key_pos; /* position in indexes list */
94 typedef struct data_definition_s data_definition_t;
96 struct snmp_agent_ctx_s {
99 pthread_mutex_t agentx_lock;
105 typedef struct snmp_agent_ctx_s snmp_agent_ctx_t;
107 static snmp_agent_ctx_t *g_agent;
108 const char *const index_opts[MAX_INDEX_TYPES] = {
109 "Hostname", "Plugin", "PluginInstance", "Type", "TypeInstance"};
111 #define CHECK_DD_TYPE(_dd, _p, _pi, _t, _ti) \
112 (_dd->plugin ? !strcmp(_dd->plugin, _p) : 0) && \
113 (_dd->plugin_instance ? !strcmp(_dd->plugin_instance, _pi) : 1) && \
114 (_dd->type ? !strcmp(_dd->type, _t) : 0) && \
115 (_dd->type_instance ? !strcmp(_dd->type_instance, _ti) : 1)
117 static int snmp_agent_shutdown(void);
118 static void *snmp_agent_thread_run(void *arg);
119 static int snmp_agent_register_oid(oid_t *oid, Netsnmp_Node_Handler *handler);
120 static int snmp_agent_set_vardata(void *dst_buf, size_t *dst_buf_len,
121 u_char asn_type, double scale, double shift,
122 const void *value, size_t len, int type);
123 static int snmp_agent_unregister_oid_index(oid_t *oid, int index);
125 static u_char snmp_agent_get_asn_type(oid *oid, size_t oid_len) {
126 struct tree *node = get_tree(oid, oid_len, g_agent->tp);
128 return (node != NULL) ? mib_to_asn_type(node->type) : 0;
131 static char *snmp_agent_get_oid_name(oid *oid, size_t oid_len) {
132 struct tree *node = get_tree(oid, oid_len, g_agent->tp);
134 return (node != NULL) ? node->label : NULL;
137 static int snmp_agent_oid_to_string(char *buf, size_t buf_size,
139 char oid_str[MAX_OID_LEN][16];
140 char *oid_str_ptr[MAX_OID_LEN];
142 for (size_t i = 0; i < o->oid_len; i++) {
143 snprintf(oid_str[i], sizeof(oid_str[i]), "%lu", (unsigned long)o->oid[i]);
144 oid_str_ptr[i] = oid_str[i];
147 return strjoin(buf, buf_size, oid_str_ptr, o->oid_len, ".");
150 /* Prints a configuration storing list. It handles both table columns list
153 static void snmp_agent_dump_data(llist_t *list) {
154 char oid_str[DATA_MAX_NAME_LEN];
155 for (llentry_t *de = llist_head(list); de != NULL; de = de->next) {
156 data_definition_t *dd = de->value;
158 if (dd->table != NULL)
159 DEBUG(PLUGIN_NAME ": Column:");
161 DEBUG(PLUGIN_NAME ": Scalar:");
163 DEBUG(PLUGIN_NAME ": Name: %s", dd->name);
165 DEBUG(PLUGIN_NAME ": Plugin: %s", dd->plugin);
166 if (dd->plugin_instance)
167 DEBUG(PLUGIN_NAME ": PluginInstance: %s", dd->plugin_instance);
168 if (dd->is_index_key)
169 DEBUG(PLUGIN_NAME ": Index: %s", index_opts[dd->index_key_pos]);
171 DEBUG(PLUGIN_NAME ": Type: %s", dd->type);
172 if (dd->type_instance)
173 DEBUG(PLUGIN_NAME ": TypeInstance: %s", dd->type_instance);
174 for (size_t i = 0; i < dd->oids_len; i++) {
175 snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &dd->oids[i]);
176 DEBUG(PLUGIN_NAME ": OID[%" PRIsz "]: %s", i, oid_str);
178 DEBUG(PLUGIN_NAME ": Scale: %g", dd->scale);
179 DEBUG(PLUGIN_NAME ": Shift: %g", dd->shift);
183 /* Prints parsed configuration */
184 static void snmp_agent_dump_config(void) {
185 char oid_str[DATA_MAX_NAME_LEN];
187 /* Printing tables */
188 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
189 table_definition_t *td = te->value;
191 DEBUG(PLUGIN_NAME ": Table:");
192 DEBUG(PLUGIN_NAME ": Name: %s", td->name);
193 if (td->index_oid.oid_len != 0) {
194 snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &td->index_oid);
195 DEBUG(PLUGIN_NAME ": IndexOID: %s", oid_str);
197 if (td->size_oid.oid_len != 0) {
198 snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &td->size_oid);
199 DEBUG(PLUGIN_NAME ": SizeOID: %s", oid_str);
202 snmp_agent_dump_data(td->columns);
205 /* Printing scalars */
206 snmp_agent_dump_data(g_agent->scalars);
208 #endif /* COLLECT_DEBUG */
210 static int snmp_agent_validate_config(void) {
213 snmp_agent_dump_config();
216 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
217 table_definition_t *td = te->value;
219 if (!td->indexes_len) {
220 ERROR(PLUGIN_NAME ": Index keys not defined for '%s'", td->name);
224 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
225 data_definition_t *dd = de->value;
228 ERROR(PLUGIN_NAME ": Plugin not defined for '%s'.'%s'", td->name,
233 if (dd->plugin_instance) {
234 ERROR(PLUGIN_NAME ": PluginInstance should not be defined for table "
235 "data type '%s'.'%s'",
240 if (dd->oids_len == 0) {
241 ERROR(PLUGIN_NAME ": No OIDs defined for '%s'.'%s'", td->name,
246 if (dd->is_index_key) {
247 if (dd->type || dd->type_instance) {
248 ERROR(PLUGIN_NAME ": Type and TypeInstance are not valid for "
249 "index data '%s'.'%s'",
254 if (dd->oids_len > 1) {
257 ": Only one OID should be specified for instance data '%s'.'%s'",
264 ERROR(PLUGIN_NAME ": Type not defined for data '%s'.'%s'", td->name,
272 for (llentry_t *e = llist_head(g_agent->scalars); e != NULL; e = e->next) {
273 data_definition_t *dd = e->value;
276 ERROR(PLUGIN_NAME ": Plugin not defined for '%s'", dd->name);
280 if (dd->oids_len == 0) {
281 ERROR(PLUGIN_NAME ": No OIDs defined for '%s'", dd->name);
285 if (dd->is_index_key) {
286 ERROR(PLUGIN_NAME ": Index field can't be specified for scalar data '%s'",
292 ERROR(PLUGIN_NAME ": Type not defined for data '%s'", dd->name);
300 static int snmp_agent_fill_index_list(table_definition_t const *td,
301 value_list_t const *vl) {
303 netsnmp_variable_list *key = td->index_list_cont;
305 for (int i = 0; i < td->indexes_len; i++) {
306 /* var should never be NULL */
308 /* Generating list filled with all data necessary to generate an OID */
309 switch (td->indexes[i]) {
311 ret = snmp_set_var_value(key, vl->host, strlen(vl->host));
314 ret = snmp_set_var_value(key, vl->plugin, strlen(vl->plugin));
316 case INDEX_PLUGIN_INSTANCE:
317 ret = snmp_set_var_value(key, vl->plugin_instance,
318 strlen(vl->plugin_instance));
321 ret = snmp_set_var_value(key, vl->type, strlen(vl->type));
323 case INDEX_TYPE_INSTANCE:
325 snmp_set_var_value(key, vl->type_instance, strlen(vl->type_instance));
328 ERROR(PLUGIN_NAME ": Unknown index key type provided");
333 key = key->next_variable;
338 static int snmp_agent_prep_index_list(table_definition_t const *td,
339 netsnmp_variable_list **index_list) {
340 /* Generating list having only the structure (with no values) letting us
341 * know how to parse an OID */
342 for (int i = 0; i < td->indexes_len; i++) {
343 switch (td->indexes[i]) {
346 case INDEX_PLUGIN_INSTANCE:
348 case INDEX_TYPE_INSTANCE:
349 snmp_varlist_add_variable(index_list, NULL, 0, ASN_OCTET_STR, NULL, 0);
352 ERROR(PLUGIN_NAME ": Unknown index key type provided");
359 static int snmp_agent_generate_index(table_definition_t const *td,
360 value_list_t const *vl, oid_t *index_oid) {
362 /* According to given information by indexes list
363 * index OID is going to be built
365 int ret = snmp_agent_fill_index_list(td, vl);
369 /* Building only index part OID (without table prefix OID) */
370 ret = build_oid_noalloc(index_oid->oid, sizeof(index_oid->oid),
371 &index_oid->oid_len, NULL, 0, td->index_list_cont);
372 if (ret != SNMPERR_SUCCESS) {
373 ERROR(PLUGIN_NAME ": Error building index OID");
380 /* It appends one OID to the end of another */
381 static int snmp_agent_append_oid(oid_t *out, const oid_t *in) {
383 if (out->oid_len + in->oid_len > MAX_OID_LEN) {
384 ERROR(PLUGIN_NAME ": Cannot create OID. Output length is too long!");
387 memcpy(&out->oid[out->oid_len], in->oid, in->oid_len * sizeof(oid));
388 out->oid_len += in->oid_len;
393 static int snmp_agent_register_oid_string(const oid_t *oid,
394 const oid_t *index_oid,
395 Netsnmp_Node_Handler *handler) {
398 memcpy(&new_oid, oid, sizeof(*oid));
399 /* Concatenating two string oids */
400 int ret = snmp_agent_append_oid(&new_oid, index_oid);
404 return snmp_agent_register_oid(&new_oid, handler);
407 static int snmp_agent_unregister_oid_string(oid_t *oid,
408 const oid_t *index_oid) {
410 char oid_str[DATA_MAX_NAME_LEN];
412 memcpy(&new_oid, oid, sizeof(*oid));
413 /* Concatenating two string oids */
414 int ret = snmp_agent_append_oid(&new_oid, index_oid);
418 snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &new_oid);
419 DEBUG(PLUGIN_NAME ": Unregistered handler for OID (%s)", oid_str);
421 return unregister_mib(new_oid.oid, new_oid.oid_len);
424 static int snmp_agent_table_row_remove(table_definition_t *td,
427 oid_t *ind_oid = NULL;
429 if (td->index_oid.oid_len) {
430 if ((c_avl_get(td->instance_index, index_oid, (void **)&index) != 0) ||
431 (c_avl_get(td->index_instance, index, NULL) != 0))
434 if (c_avl_get(td->instance_index, index_oid, NULL) != 0)
438 pthread_mutex_lock(&g_agent->agentx_lock);
440 if (td->index_oid.oid_len)
441 snmp_agent_unregister_oid_index(&td->index_oid, *index);
443 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
444 data_definition_t *dd = de->value;
446 for (size_t i = 0; i < dd->oids_len; i++)
447 if (td->index_oid.oid_len)
448 snmp_agent_unregister_oid_index(&dd->oids[i], *index);
450 snmp_agent_unregister_oid_string(&dd->oids[i], index_oid);
453 pthread_mutex_unlock(&g_agent->agentx_lock);
455 char index_str[DATA_MAX_NAME_LEN];
458 snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid);
460 snprintf(index_str, sizeof(index_str), "%d", *index);
463 .severity = NOTIF_WARNING, .time = cdtime(), .plugin = PLUGIN_NAME};
464 sstrncpy(n.host, hostname_g, sizeof(n.host));
465 snprintf(n.message, sizeof(n.message),
466 "Removed data row from table %s with index %s", td->name, index_str);
467 DEBUG(PLUGIN_NAME ": %s", n.message);
468 plugin_dispatch_notification(&n);
470 if (td->index_oid.oid_len) {
471 c_avl_remove(td->index_instance, index, NULL, (void **)&ind_oid);
472 c_avl_remove(td->instance_index, index_oid, NULL, (void **)&index);
476 c_avl_remove(td->instance_index, index_oid, NULL, NULL);
483 static int snmp_agent_clear_missing(const value_list_t *vl,
484 __attribute__((unused)) user_data_t *ud) {
488 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
489 table_definition_t *td = te->value;
491 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
492 data_definition_t *dd = de->value;
493 oid_t *index_oid = (oid_t *)calloc(1, sizeof(*index_oid));
496 if (!dd->is_index_key) {
497 if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
498 vl->type_instance)) {
499 ret = snmp_agent_generate_index(td, vl, index_oid);
501 ret = snmp_agent_table_row_remove(td, index_oid);
512 static void snmp_agent_free_data(data_definition_t **dd) {
514 if (dd == NULL || *dd == NULL)
517 /* unregister scalar type OID */
518 if ((*dd)->table == NULL) {
519 for (size_t i = 0; i < (*dd)->oids_len; i++)
520 unregister_mib((*dd)->oids[i].oid, (*dd)->oids[i].oid_len);
524 sfree((*dd)->plugin);
525 sfree((*dd)->plugin_instance);
527 sfree((*dd)->type_instance);
535 static void snmp_agent_free_table_columns(table_definition_t *td) {
536 if (td->columns == NULL)
539 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
540 data_definition_t *dd = de->value;
542 if (td->index_oid.oid_len) {
546 c_avl_iterator_t *iter = c_avl_get_iterator(td->index_instance);
547 while (c_avl_iterator_next(iter, (void *)&index, (void *)&index_oid) ==
549 for (size_t i = 0; i < dd->oids_len; i++)
550 snmp_agent_unregister_oid_index(&dd->oids[i], *index);
552 c_avl_iterator_destroy(iter);
556 c_avl_iterator_t *iter = c_avl_get_iterator(dd->table->instance_index);
557 while (c_avl_iterator_next(iter, (void *)&index_oid, NULL) == 0) {
558 for (size_t i = 0; i < dd->oids_len; i++)
559 snmp_agent_unregister_oid_string(&dd->oids[i], index_oid);
561 c_avl_iterator_destroy(iter);
564 snmp_agent_free_data(&dd);
567 llist_destroy(td->columns);
569 } /* void snmp_agent_free_table_columns */
571 static void snmp_agent_free_table(table_definition_t **td) {
573 if (td == NULL || *td == NULL)
576 if ((*td)->size_oid.oid_len)
577 unregister_mib((*td)->size_oid.oid, (*td)->size_oid.oid_len);
579 /* Unregister Index OIDs */
580 if ((*td)->index_oid.oid_len) {
584 c_avl_iterator_t *iter = c_avl_get_iterator((*td)->index_instance);
585 while (c_avl_iterator_next(iter, (void **)&index, (void **)&index_oid) == 0)
586 snmp_agent_unregister_oid_index(&(*td)->index_oid, *index);
588 c_avl_iterator_destroy(iter);
591 /* Unregister all table columns and their registered OIDs */
592 snmp_agent_free_table_columns(*td);
597 /* index_instance and instance_index contain the same pointers */
598 c_avl_destroy((*td)->index_instance);
599 (*td)->index_instance = NULL;
601 if ((*td)->instance_index != NULL) {
602 while (c_avl_pick((*td)->instance_index, &key, &value) == 0) {
607 c_avl_destroy((*td)->instance_index);
608 (*td)->instance_index = NULL;
611 snmp_free_varbind((*td)->index_list_cont);
618 static int snmp_agent_parse_oid_indexes(const table_definition_t *td,
620 int ret = parse_oid_indexes(index_oid->oid, index_oid->oid_len,
621 td->index_list_cont);
622 if (ret != SNMPERR_SUCCESS)
623 ERROR(PLUGIN_NAME ": index OID parse error!");
627 static int snmp_agent_format_name(char *name, int name_len,
628 data_definition_t *dd, oid_t *index_oid) {
630 if (index_oid == NULL) {
632 format_name(name, name_len, hostname_g, dd->plugin, dd->plugin_instance,
633 dd->type, dd->type_instance);
635 /* Need to parse string index OID */
636 const table_definition_t *td = dd->table;
637 int ret = snmp_agent_parse_oid_indexes(td, index_oid);
642 netsnmp_variable_list *key = td->index_list_cont;
643 char *host = hostname_g;
644 char *plugin = dd->plugin;
645 char *plugin_instance = dd->plugin_instance;
646 char *type = dd->type;
647 char *type_instance = dd->type_instance;
648 while (key != NULL) {
649 switch (td->indexes[i]) {
651 host = (char *)key->val.string;
654 plugin = (char *)key->val.string;
656 case INDEX_PLUGIN_INSTANCE:
657 plugin_instance = (char *)key->val.string;
660 type = (char *)key->val.string;
662 case INDEX_TYPE_INSTANCE:
663 type_instance = (char *)key->val.string;
666 ERROR(PLUGIN_NAME ": Unkown index type!");
669 key = key->next_variable;
673 format_name(name, name_len, host, plugin, plugin_instance, type,
680 static int snmp_agent_form_reply(struct netsnmp_request_info_s *requests,
681 data_definition_t *dd, oid_t *index_oid,
685 if (dd->is_index_key) {
686 const table_definition_t *td = dd->table;
687 ret = snmp_agent_parse_oid_indexes(td, index_oid);
692 netsnmp_variable_list *key = td->index_list_cont;
693 /* Searching index key */
694 for (int pos = 0; pos < dd->index_key_pos; pos++)
695 key = key->next_variable;
697 requests->requestvb->type = ASN_OCTET_STR;
698 snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
699 (const u_char *)key->val.string,
700 strlen((const char *)key->val.string));
702 pthread_mutex_unlock(&g_agent->lock);
704 return SNMP_ERR_NOERROR;
707 char name[DATA_MAX_NAME_LEN];
709 ret = snmp_agent_format_name(name, sizeof(name), dd, index_oid);
713 DEBUG(PLUGIN_NAME ": Identifier '%s'", name);
717 const data_set_t *ds = plugin_get_ds(dd->type);
719 ERROR(PLUGIN_NAME ": Data set not found for '%s' type", dd->type);
720 return SNMP_NOSUCHINSTANCE;
723 ret = uc_get_value_by_name(name, &values, &values_num);
726 ERROR(PLUGIN_NAME ": Failed to get value for '%s'", name);
727 return SNMP_NOSUCHINSTANCE;
730 assert(ds->ds_num == values_num);
731 assert(oid_index < (int)values_num);
733 char data[DATA_MAX_NAME_LEN];
734 size_t data_len = sizeof(data);
735 ret = snmp_agent_set_vardata(
736 data, &data_len, dd->oids[oid_index].type, dd->scale, dd->shift,
737 &values[oid_index], sizeof(values[oid_index]), ds->ds[oid_index].type);
742 ERROR(PLUGIN_NAME ": Failed to convert '%s' value to snmp data", name);
743 return SNMP_NOSUCHINSTANCE;
746 requests->requestvb->type = dd->oids[oid_index].type;
747 snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
748 (const u_char *)data, data_len);
750 return SNMP_ERR_NOERROR;
754 snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
755 struct netsnmp_handler_registration_s *reginfo,
756 struct netsnmp_agent_request_info_s *reqinfo,
757 struct netsnmp_request_info_s *requests) {
759 if (reqinfo->mode != MODE_GET) {
760 DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
761 return SNMP_ERR_NOERROR;
764 pthread_mutex_lock(&g_agent->lock);
766 oid_t oid; /* Requested OID */
767 memcpy(oid.oid, requests->requestvb->name,
768 sizeof(oid.oid[0]) * requests->requestvb->name_length);
769 oid.oid_len = requests->requestvb->name_length;
772 char oid_str[DATA_MAX_NAME_LEN];
773 snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &oid);
774 DEBUG(PLUGIN_NAME ": Get request received for table OID '%s'", oid_str);
776 oid_t index_oid; /* Index part of requested OID */
778 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
779 table_definition_t *td = te->value;
781 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
782 data_definition_t *dd = de->value;
784 for (size_t i = 0; i < dd->oids_len; i++) {
785 int ret = snmp_oid_ncompare(oid.oid, oid.oid_len, dd->oids[i].oid,
787 SNMP_MIN(oid.oid_len, dd->oids[i].oid_len));
791 /* Calculating OID length for index part */
792 index_oid.oid_len = oid.oid_len - dd->oids[i].oid_len;
793 /* Fetching index part of the OID */
794 memcpy(index_oid.oid, &oid.oid[dd->oids[i].oid_len],
795 index_oid.oid_len * sizeof(*oid.oid));
797 char index_str[DATA_MAX_NAME_LEN];
798 snmp_agent_oid_to_string(index_str, sizeof(index_str), &index_oid);
800 if (!td->index_oid.oid_len) {
801 ret = c_avl_get(td->instance_index, &index_oid, NULL);
805 assert(index_oid.oid_len == 1);
806 ret = c_avl_get(td->index_instance, (int *)&index_oid.oid[0],
808 memcpy(&index_oid, temp_oid, sizeof(index_oid));
812 INFO(PLUGIN_NAME ": Non-existing index (%s) requested", index_str);
813 pthread_mutex_unlock(&g_agent->lock);
814 return SNMP_NOSUCHINSTANCE;
817 ret = snmp_agent_form_reply(requests, dd, &index_oid, i);
818 pthread_mutex_unlock(&g_agent->lock);
825 pthread_mutex_unlock(&g_agent->lock);
827 return SNMP_NOSUCHINSTANCE;
830 static int snmp_agent_table_index_oid_handler(
831 struct netsnmp_mib_handler_s *handler,
832 struct netsnmp_handler_registration_s *reginfo,
833 struct netsnmp_agent_request_info_s *reqinfo,
834 struct netsnmp_request_info_s *requests) {
836 if (reqinfo->mode != MODE_GET) {
837 DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
838 return SNMP_ERR_NOERROR;
841 pthread_mutex_lock(&g_agent->lock);
844 memcpy(oid.oid, requests->requestvb->name,
845 sizeof(oid.oid[0]) * requests->requestvb->name_length);
846 oid.oid_len = requests->requestvb->name_length;
848 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
849 table_definition_t *td = te->value;
851 if (td->index_oid.oid_len &&
853 oid.oid, oid.oid_len, td->index_oid.oid, td->index_oid.oid_len,
854 SNMP_MIN(oid.oid_len, td->index_oid.oid_len)) == 0)) {
856 DEBUG(PLUGIN_NAME ": Handle '%s' table index OID", td->name);
858 int index = oid.oid[oid.oid_len - 1];
860 int ret = c_avl_get(td->index_instance, &index, NULL);
862 /* nonexisting index requested */
863 pthread_mutex_unlock(&g_agent->lock);
864 return SNMP_NOSUCHINSTANCE;
867 requests->requestvb->type = ASN_INTEGER;
868 snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
869 (const u_char *)&index, sizeof(index));
871 pthread_mutex_unlock(&g_agent->lock);
873 return SNMP_ERR_NOERROR;
877 pthread_mutex_unlock(&g_agent->lock);
879 return SNMP_NOSUCHINSTANCE;
882 static int snmp_agent_table_size_oid_handler(
883 struct netsnmp_mib_handler_s *handler,
884 struct netsnmp_handler_registration_s *reginfo,
885 struct netsnmp_agent_request_info_s *reqinfo,
886 struct netsnmp_request_info_s *requests) {
888 if (reqinfo->mode != MODE_GET) {
889 DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
890 return SNMP_ERR_NOERROR;
893 pthread_mutex_lock(&g_agent->lock);
896 memcpy(oid.oid, requests->requestvb->name,
897 sizeof(oid.oid[0]) * requests->requestvb->name_length);
898 oid.oid_len = requests->requestvb->name_length;
900 DEBUG(PLUGIN_NAME ": Get request received for table size OID");
902 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
903 table_definition_t *td = te->value;
905 if (td->size_oid.oid_len &&
906 (snmp_oid_ncompare(oid.oid, oid.oid_len, td->size_oid.oid,
907 td->size_oid.oid_len,
908 SNMP_MIN(oid.oid_len, td->size_oid.oid_len)) == 0)) {
909 DEBUG(PLUGIN_NAME ": Handle '%s' table size OID", td->name);
912 if (td->index_oid.oid_len)
913 size = c_avl_size(td->index_instance);
915 size = c_avl_size(td->instance_index);
917 requests->requestvb->type = ASN_INTEGER;
918 snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
919 (const u_char *)&size, sizeof(size));
921 pthread_mutex_unlock(&g_agent->lock);
923 return SNMP_ERR_NOERROR;
927 pthread_mutex_unlock(&g_agent->lock);
929 return SNMP_NOSUCHINSTANCE;
933 snmp_agent_scalar_oid_handler(struct netsnmp_mib_handler_s *handler,
934 struct netsnmp_handler_registration_s *reginfo,
935 struct netsnmp_agent_request_info_s *reqinfo,
936 struct netsnmp_request_info_s *requests) {
938 if (reqinfo->mode != MODE_GET) {
939 DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
940 return SNMP_ERR_NOERROR;
943 pthread_mutex_lock(&g_agent->lock);
946 memcpy(oid.oid, requests->requestvb->name,
947 sizeof(oid.oid[0]) * requests->requestvb->name_length);
948 oid.oid_len = requests->requestvb->name_length;
951 char oid_str[DATA_MAX_NAME_LEN];
952 snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &oid);
953 DEBUG(PLUGIN_NAME ": Get request received for scalar OID '%s'", oid_str);
956 for (llentry_t *de = llist_head(g_agent->scalars); de != NULL;
958 data_definition_t *dd = de->value;
960 for (size_t i = 0; i < dd->oids_len; i++) {
962 int ret = snmp_oid_compare(oid.oid, oid.oid_len, dd->oids[i].oid,
963 dd->oids[i].oid_len);
967 ret = snmp_agent_form_reply(requests, dd, NULL, i);
969 pthread_mutex_unlock(&g_agent->lock);
975 pthread_mutex_unlock(&g_agent->lock);
977 return SNMP_NOSUCHINSTANCE;
980 static int snmp_agent_register_table_oids(void) {
982 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
983 table_definition_t *td = te->value;
985 if (td->size_oid.oid_len != 0) {
987 snmp_agent_get_asn_type(td->size_oid.oid, td->size_oid.oid_len);
988 td->size_oid.oid_len++;
989 int ret = snmp_agent_register_oid(&td->size_oid,
990 snmp_agent_table_size_oid_handler);
995 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
996 data_definition_t *dd = de->value;
998 for (size_t i = 0; i < dd->oids_len; i++) {
1000 snmp_agent_get_asn_type(dd->oids[i].oid, dd->oids[i].oid_len);
1008 static int snmp_agent_register_scalar_oids(void) {
1010 for (llentry_t *e = llist_head(g_agent->scalars); e != NULL; e = e->next) {
1011 data_definition_t *dd = e->value;
1013 for (size_t i = 0; i < dd->oids_len; i++) {
1016 snmp_agent_get_asn_type(dd->oids[i].oid, dd->oids[i].oid_len);
1019 snmp_agent_register_oid(&dd->oids[i], snmp_agent_scalar_oid_handler);
1028 static int snmp_agent_config_data_oids(data_definition_t *dd,
1029 oconfig_item_t *ci) {
1030 if (ci->values_num < 1) {
1031 WARNING(PLUGIN_NAME ": `OIDs' needs at least one argument");
1035 for (int i = 0; i < ci->values_num; i++)
1036 if (ci->values[i].type != OCONFIG_TYPE_STRING) {
1037 WARNING(PLUGIN_NAME ": `OIDs' needs only string argument");
1041 if (dd->oids != NULL)
1044 dd->oids = calloc(ci->values_num, sizeof(*dd->oids));
1045 if (dd->oids == NULL)
1047 dd->oids_len = (size_t)ci->values_num;
1049 for (int i = 0; i < ci->values_num; i++) {
1050 dd->oids[i].oid_len = MAX_OID_LEN;
1052 if (NULL == snmp_parse_oid(ci->values[i].value.string, dd->oids[i].oid,
1053 &dd->oids[i].oid_len)) {
1054 ERROR(PLUGIN_NAME ": snmp_parse_oid (%s) failed",
1055 ci->values[i].value.string);
1065 static int snmp_agent_config_table_size_oid(table_definition_t *td,
1066 oconfig_item_t *ci) {
1067 if (ci->values_num < 1) {
1068 WARNING(PLUGIN_NAME ": `TableSizeOID' is empty");
1072 if (ci->values[0].type != OCONFIG_TYPE_STRING) {
1073 WARNING(PLUGIN_NAME ": `TableSizeOID' needs only string argument");
1077 td->size_oid.oid_len = MAX_OID_LEN;
1079 if (NULL == snmp_parse_oid(ci->values[0].value.string, td->size_oid.oid,
1080 &td->size_oid.oid_len)) {
1081 ERROR(PLUGIN_NAME ": Failed to parse table size OID (%s)",
1082 ci->values[0].value.string);
1083 td->size_oid.oid_len = 0;
1090 static int snmp_agent_config_table_index_oid(table_definition_t *td,
1091 oconfig_item_t *ci) {
1093 if (ci->values_num < 1) {
1094 WARNING(PLUGIN_NAME ": `IndexOID' is empty");
1098 if (ci->values[0].type != OCONFIG_TYPE_STRING) {
1099 WARNING(PLUGIN_NAME ": `IndexOID' needs only string argument");
1103 td->index_oid.oid_len = MAX_OID_LEN;
1105 if (NULL == snmp_parse_oid(ci->values[0].value.string, td->index_oid.oid,
1106 &td->index_oid.oid_len)) {
1107 ERROR(PLUGIN_NAME ": Failed to parse table index OID (%s)",
1108 ci->values[0].value.string);
1109 td->index_oid.oid_len = 0;
1116 /* Parsing table column representing index key */
1117 static int snmp_agent_config_index(table_definition_t *td,
1118 data_definition_t *dd, oconfig_item_t *ci) {
1121 int ret = cf_util_get_string(ci, &val);
1127 for (int i = 0; i < MAX_INDEX_TYPES; i++) {
1128 if (strcasecmp(index_opts[i], (const char *)val) == 0) {
1129 td->indexes[td->indexes_len] = i;
1136 ERROR(PLUGIN_NAME ": Failed to parse index key source: '%s'", val);
1142 dd->index_key_pos = td->indexes_len++;
1143 dd->is_index_key = 1;
1148 /* This function parses configuration of both scalar and table column
1149 * because they have nearly the same structure */
1150 static int snmp_agent_config_table_column(table_definition_t *td,
1151 oconfig_item_t *ci) {
1152 data_definition_t *dd;
1157 dd = calloc(1, sizeof(*dd));
1159 ERROR(PLUGIN_NAME ": Failed to allocate memory for table data definition");
1163 ret = cf_util_get_string(ci, &dd->name);
1171 /* NULL if it's a scalar */
1173 dd->is_index_key = 0;
1175 for (int i = 0; i < ci->children_num; i++) {
1176 oconfig_item_t *option = ci->children + i;
1178 /* Instance option is reserved for table entry only */
1179 if (strcasecmp("Index", option->key) == 0 && td != NULL)
1180 ret = snmp_agent_config_index(td, dd, option);
1181 else if (strcasecmp("Plugin", option->key) == 0)
1182 ret = cf_util_get_string(option, &dd->plugin);
1183 else if (strcasecmp("PluginInstance", option->key) == 0)
1184 ret = cf_util_get_string(option, &dd->plugin_instance);
1185 else if (strcasecmp("Type", option->key) == 0)
1186 ret = cf_util_get_string(option, &dd->type);
1187 else if (strcasecmp("TypeInstance", option->key) == 0)
1188 ret = cf_util_get_string(option, &dd->type_instance);
1189 else if (strcasecmp("Shift", option->key) == 0)
1190 ret = cf_util_get_double(option, &dd->shift);
1191 else if (strcasecmp("Scale", option->key) == 0)
1192 ret = cf_util_get_double(option, &dd->scale);
1193 else if (strcasecmp("OIDs", option->key) == 0)
1194 ret = snmp_agent_config_data_oids(dd, option);
1196 WARNING(PLUGIN_NAME ": Option `%s' not allowed here", option->key);
1201 snmp_agent_free_data(&dd);
1206 llentry_t *entry = llentry_create(dd->name, dd);
1207 if (entry == NULL) {
1208 snmp_agent_free_data(&dd);
1212 /* Append to column list in parent table */
1214 llist_append(td->columns, entry);
1219 /* Parses scalar configuration entry */
1220 static int snmp_agent_config_scalar(oconfig_item_t *ci) {
1221 return snmp_agent_config_table_column(NULL, ci);
1224 static int num_compare(const int *a, const int *b) {
1225 assert((a != NULL) && (b != NULL));
1234 static int oid_compare(const oid_t *a, const oid_t *b) {
1235 return snmp_oid_compare(a->oid, a->oid_len, b->oid, b->oid_len);
1238 static int snmp_agent_config_table(oconfig_item_t *ci) {
1239 table_definition_t *td;
1244 td = calloc(1, sizeof(*td));
1246 ERROR(PLUGIN_NAME ": Failed to allocate memory for table definition");
1250 ret = cf_util_get_string(ci, &td->name);
1256 td->columns = llist_create();
1257 if (td->columns == NULL) {
1258 ERROR(PLUGIN_NAME ": Failed to allocate memory for columns list");
1259 snmp_agent_free_table(&td);
1263 for (int i = 0; i < ci->children_num; i++) {
1264 oconfig_item_t *option = ci->children + i;
1266 if (strcasecmp("IndexOID", option->key) == 0)
1267 ret = snmp_agent_config_table_index_oid(td, option);
1268 else if (strcasecmp("SizeOID", option->key) == 0)
1269 ret = snmp_agent_config_table_size_oid(td, option);
1270 else if (strcasecmp("Data", option->key) == 0)
1271 ret = snmp_agent_config_table_column(td, option);
1273 WARNING(PLUGIN_NAME ": Option `%s' not allowed here", option->key);
1278 snmp_agent_free_table(&td);
1283 /* Preparing index list container */
1284 ret = snmp_agent_prep_index_list(td, &td->index_list_cont);
1288 td->instance_index =
1289 c_avl_create((int (*)(const void *, const void *))oid_compare);
1290 if (td->instance_index == NULL) {
1291 snmp_agent_free_table(&td);
1295 td->index_instance =
1296 c_avl_create((int (*)(const void *, const void *))num_compare);
1297 if (td->index_instance == NULL) {
1298 snmp_agent_free_table(&td);
1302 llentry_t *entry = llentry_create(td->name, td);
1303 if (entry == NULL) {
1304 snmp_agent_free_table(&td);
1307 llist_append(g_agent->tables, entry);
1312 static int snmp_agent_get_value_from_ds_type(const value_t *val, int type,
1313 double scale, double shift,
1316 case DS_TYPE_COUNTER:
1317 *value = (long)((val->counter * scale) + shift);
1319 case DS_TYPE_ABSOLUTE:
1320 *value = (long)((val->absolute * scale) + shift);
1322 case DS_TYPE_DERIVE:
1323 *value = (long)((val->derive * scale) + shift);
1326 *value = (long)((val->gauge * scale) + shift);
1331 ERROR(PLUGIN_NAME ": Unknown data source type: %i", type);
1338 static int snmp_agent_set_vardata(void *data, size_t *data_len, u_char asn_type,
1339 double scale, double shift, const void *value,
1340 size_t len, int type) {
1343 netsnmp_vardata var;
1348 var.string = (u_char *)data;
1350 ret = snmp_agent_get_value_from_ds_type(val, type, scale, shift, &new_value);
1360 if (*data_len < sizeof(*var.integer))
1362 *var.integer = new_value;
1363 *data_len = sizeof(*var.integer);
1366 if (*data_len < sizeof(*var.counter64))
1368 var.counter64->high = (u_long)((int64_t)new_value >> 32);
1369 var.counter64->low = (u_long)((int64_t)new_value & 0xFFFFFFFF);
1370 *data_len = sizeof(*var.counter64);
1373 if (type == DS_TYPE_GAUGE) {
1374 char buf[DATA_MAX_NAME_LEN];
1375 snprintf(buf, sizeof(buf), "%.2f", val->gauge);
1376 if (*data_len < strlen(buf))
1378 *data_len = strlen(buf);
1379 memcpy(var.string, buf, *data_len);
1381 ERROR(PLUGIN_NAME ": Failed to convert %d ds type to %d asn type", type,
1387 ERROR(PLUGIN_NAME ": Failed to convert %d ds type to %d asn type", type,
1395 static int snmp_agent_register_oid_index(oid_t *oid, int index,
1396 Netsnmp_Node_Handler *handler) {
1398 memcpy(&new_oid, oid, sizeof(*oid));
1399 new_oid.oid[new_oid.oid_len++] = index;
1400 return snmp_agent_register_oid(&new_oid, handler);
1403 static int snmp_agent_unregister_oid_index(oid_t *oid, int index) {
1405 memcpy(&new_oid, oid, sizeof(*oid));
1406 new_oid.oid[new_oid.oid_len++] = index;
1407 return unregister_mib(new_oid.oid, new_oid.oid_len);
1410 static int snmp_agent_update_index(table_definition_t *td, oid_t *index_oid) {
1412 if (c_avl_get(td->instance_index, index_oid, NULL) == 0)
1418 /* need to generate index for the table */
1419 if (td->index_oid.oid_len) {
1420 index = calloc(1, sizeof(*index));
1421 if (index == NULL) {
1426 *index = c_avl_size(td->instance_index) + 1;
1428 ret = c_avl_insert(td->instance_index, index_oid, index);
1435 ret = c_avl_insert(td->index_instance, index, index_oid);
1437 DEBUG(PLUGIN_NAME ": Failed to update index_instance for '%s' table",
1439 c_avl_remove(td->instance_index, index_oid, NULL, (void **)&index);
1445 ret = snmp_agent_register_oid_index(&td->index_oid, *index,
1446 snmp_agent_table_index_oid_handler);
1450 /* instance as a key is required for any table */
1451 ret = c_avl_insert(td->instance_index, index_oid, NULL);
1458 /* register new oids for all columns */
1459 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
1460 data_definition_t *dd = de->value;
1462 for (size_t i = 0; i < dd->oids_len; i++) {
1463 if (td->index_oid.oid_len)
1464 ret = snmp_agent_register_oid_index(&dd->oids[i], *index,
1465 snmp_agent_table_oid_handler);
1467 ret = snmp_agent_register_oid_string(&dd->oids[i], index_oid,
1468 snmp_agent_table_oid_handler);
1475 char index_str[DATA_MAX_NAME_LEN];
1478 snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid);
1480 snprintf(index_str, sizeof(index_str), "%d", *index);
1482 notification_t n = {
1483 .severity = NOTIF_OKAY, .time = cdtime(), .plugin = PLUGIN_NAME};
1484 sstrncpy(n.host, hostname_g, sizeof(n.host));
1485 snprintf(n.message, sizeof(n.message),
1486 "Data row added to table %s with index %s", td->name, index_str);
1487 DEBUG(PLUGIN_NAME ": %s", n.message);
1489 plugin_dispatch_notification(&n);
1494 static int snmp_agent_write(value_list_t const *vl) {
1498 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
1499 table_definition_t *td = te->value;
1501 for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
1502 data_definition_t *dd = de->value;
1503 oid_t *index_oid = (oid_t *)calloc(1, sizeof(*index_oid));
1506 if (index_oid == NULL)
1509 if (!dd->is_index_key) {
1510 if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
1511 vl->type_instance)) {
1512 ret = snmp_agent_generate_index(td, vl, index_oid);
1514 ret = snmp_agent_update_index(td, index_oid);
1525 static int snmp_agent_collect(const data_set_t *ds, const value_list_t *vl,
1526 user_data_t __attribute__((unused)) * user_data) {
1528 pthread_mutex_lock(&g_agent->lock);
1530 snmp_agent_write(vl);
1532 pthread_mutex_unlock(&g_agent->lock);
1537 static int snmp_agent_preinit(void) {
1539 g_agent = calloc(1, sizeof(*g_agent));
1540 if (g_agent == NULL) {
1541 ERROR(PLUGIN_NAME ": Failed to allocate memory for snmp agent context");
1545 g_agent->tables = llist_create();
1546 g_agent->scalars = llist_create();
1548 if (g_agent->tables == NULL || g_agent->scalars == NULL) {
1549 ERROR(PLUGIN_NAME ": llist_create() failed");
1550 llist_destroy(g_agent->scalars);
1551 llist_destroy(g_agent->tables);
1556 /* make us an agentx client. */
1557 err = netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
1560 ERROR(PLUGIN_NAME ": Failed to set agent role (%d)", err);
1561 llist_destroy(g_agent->scalars);
1562 llist_destroy(g_agent->tables);
1567 * For SNMP debug purposes uses snmp_set_do_debugging(1);
1570 /* initialize the agent library */
1571 err = init_agent(PLUGIN_NAME);
1573 ERROR(PLUGIN_NAME ": Failed to initialize the agent library (%d)", err);
1574 llist_destroy(g_agent->scalars);
1575 llist_destroy(g_agent->tables);
1579 init_snmp(PLUGIN_NAME);
1581 g_agent->tp = read_all_mibs();
1586 static int snmp_agent_init(void) {
1589 if (g_agent == NULL || ((llist_head(g_agent->scalars) == NULL) &&
1590 (llist_head(g_agent->tables) == NULL))) {
1591 ERROR(PLUGIN_NAME ": snmp_agent_init: plugin not configured");
1595 plugin_register_shutdown(PLUGIN_NAME, snmp_agent_shutdown);
1597 ret = snmp_agent_register_scalar_oids();
1601 ret = snmp_agent_register_table_oids();
1605 ret = pthread_mutex_init(&g_agent->lock, NULL);
1607 ERROR(PLUGIN_NAME ": Failed to initialize mutex, err %u", ret);
1611 ret = pthread_mutex_init(&g_agent->agentx_lock, NULL);
1613 ERROR(PLUGIN_NAME ": Failed to initialize AgentX mutex, err %u", ret);
1617 /* create a second thread to listen for requests from AgentX*/
1618 ret = pthread_create(&g_agent->thread, NULL, &snmp_agent_thread_run, NULL);
1620 ERROR(PLUGIN_NAME ": Failed to create a separate thread, err %u", ret);
1624 if (llist_head(g_agent->tables) != NULL) {
1625 plugin_register_write(PLUGIN_NAME, snmp_agent_collect, NULL);
1626 plugin_register_missing(PLUGIN_NAME, snmp_agent_clear_missing, NULL);
1632 static void *snmp_agent_thread_run(void __attribute__((unused)) * arg) {
1633 INFO(PLUGIN_NAME ": Thread is up and running");
1636 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
1638 pthread_mutex_lock(&g_agent->agentx_lock);
1639 agent_check_and_process(0); /* 0 == don't block */
1640 pthread_mutex_unlock(&g_agent->agentx_lock);
1642 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1649 static int snmp_agent_register_oid(oid_t *oid, Netsnmp_Node_Handler *handler) {
1650 netsnmp_handler_registration *reg;
1651 char *oid_name = snmp_agent_get_oid_name(oid->oid, oid->oid_len - 1);
1652 char oid_str[DATA_MAX_NAME_LEN];
1654 snmp_agent_oid_to_string(oid_str, sizeof(oid_str), oid);
1656 if (oid_name == NULL) {
1658 ": Skipped registration: OID (%s) is not found in main tree",
1663 reg = netsnmp_create_handler_registration(oid_name, handler, oid->oid,
1664 oid->oid_len, HANDLER_CAN_RONLY);
1666 ERROR(PLUGIN_NAME ": Failed to create handler registration for OID (%s)",
1671 pthread_mutex_lock(&g_agent->agentx_lock);
1673 if (netsnmp_register_instance(reg) != MIB_REGISTERED_OK) {
1674 ERROR(PLUGIN_NAME ": Failed to register handler for OID (%s)", oid_str);
1675 pthread_mutex_unlock(&g_agent->agentx_lock);
1679 pthread_mutex_unlock(&g_agent->agentx_lock);
1681 DEBUG(PLUGIN_NAME ": Registered handler for OID (%s)", oid_str);
1686 static int snmp_agent_free_config(void) {
1688 if (g_agent == NULL)
1691 for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next)
1692 snmp_agent_free_table((table_definition_t **)&te->value);
1693 llist_destroy(g_agent->tables);
1695 for (llentry_t *de = llist_head(g_agent->scalars); de != NULL; de = de->next)
1696 snmp_agent_free_data((data_definition_t **)&de->value);
1697 llist_destroy(g_agent->scalars);
1702 static int snmp_agent_shutdown(void) {
1705 DEBUG(PLUGIN_NAME ": snmp_agent_shutdown");
1707 if (g_agent == NULL) {
1708 ERROR(PLUGIN_NAME ": snmp_agent_shutdown: plugin not initialized");
1712 if (pthread_cancel(g_agent->thread) != 0)
1713 ERROR(PLUGIN_NAME ": snmp_agent_shutdown: failed to cancel the thread");
1715 if (pthread_join(g_agent->thread, NULL) != 0)
1716 ERROR(PLUGIN_NAME ": snmp_agent_shutdown: failed to join the thread");
1718 snmp_agent_free_config();
1720 snmp_shutdown(PLUGIN_NAME);
1722 pthread_mutex_destroy(&g_agent->lock);
1723 pthread_mutex_destroy(&g_agent->agentx_lock);
1730 static int snmp_agent_config(oconfig_item_t *ci) {
1732 int ret = snmp_agent_preinit();
1739 for (int i = 0; i < ci->children_num; i++) {
1740 oconfig_item_t *child = ci->children + i;
1741 if (strcasecmp("Data", child->key) == 0) {
1742 ret = snmp_agent_config_scalar(child);
1743 } else if (strcasecmp("Table", child->key) == 0) {
1744 ret = snmp_agent_config_table(child);
1746 ERROR(PLUGIN_NAME ": Unknown configuration option `%s'", child->key);
1751 ERROR(PLUGIN_NAME ": Failed to parse configuration");
1752 snmp_agent_free_config();
1753 snmp_shutdown(PLUGIN_NAME);
1759 ret = snmp_agent_validate_config();
1761 ERROR(PLUGIN_NAME ": Invalid configuration provided");
1762 snmp_agent_free_config();
1763 snmp_shutdown(PLUGIN_NAME);
1771 void module_register(void) {
1772 plugin_register_init(PLUGIN_NAME, snmp_agent_init);
1773 plugin_register_complex_config(PLUGIN_NAME, snmp_agent_config);