#include "utils_avltree.h"
#include "utils_cache.h"
#include "utils_llist.h"
+#include <regex.h>
#include <net-snmp/net-snmp-config.h>
#define PLUGIN_NAME "snmp_agent"
#define TYPE_STRING -1
-#define MAX_INDEX_TYPES 5
+#define GROUP_UNUSED -1
+#define MAX_KEY_SOURCES 5
+#define MAX_INDEX_KEYS 5
+#define MAX_MATCHES 5
-/* Identifies index key origin */
-enum index_key_e {
+/* Identifies index key source */
+enum index_key_src_e {
INDEX_HOST = 0,
INDEX_PLUGIN,
INDEX_PLUGIN_INSTANCE,
INDEX_TYPE,
INDEX_TYPE_INSTANCE
};
-typedef enum index_key_e index_key_t;
+typedef enum index_key_src_e index_key_src_t;
+
+struct index_key_s {
+ index_key_src_t source;
+ u_char type;
+ char *regex; /* Pattern used to parse index key source string */
+ int group; /* If pattern gives more than one group we can specify which one
+ we want to take */
+ regex_t regex_info;
+};
+typedef struct index_key_s index_key_t;
struct oid_s {
oid oid[MAX_OID_LEN];
};
typedef struct oid_s oid_t;
+struct token_s {
+ char *str;
+ netsnmp_variable_list *key; /* Points to succeeding key */
+};
+typedef struct token_s token_t;
+
struct table_definition_s {
char *name;
oid_t index_oid;
llist_t *columns;
c_avl_tree_t *instance_index;
c_avl_tree_t *index_instance;
- index_key_t indexes[MAX_INDEX_TYPES]; /* Stores information about what each
- index key represents */
- int indexes_len;
+ index_key_t index_keys[MAX_INDEX_KEYS]; /* Stores information about what each
+ index key represents */
+ int index_keys_len;
netsnmp_variable_list *index_list_cont; /* Index key container used for
generating as well as parsing
OIDs, not thread-safe */
+ c_avl_tree_t *tokens[MAX_KEY_SOURCES]; /* Input string after regex execution
+ will be split into sepearate
+ tokens */
+
+ _Bool tokens_done; /* Set to 1 when all tokens are generated */
};
typedef struct table_definition_s table_definition_t;
typedef struct snmp_agent_ctx_s snmp_agent_ctx_t;
static snmp_agent_ctx_t *g_agent;
-const char *const index_opts[MAX_INDEX_TYPES] = {
+const char *const index_opts[MAX_KEY_SOURCES] = {
"Hostname", "Plugin", "PluginInstance", "Type", "TypeInstance"};
#define CHECK_DD_TYPE(_dd, _p, _pi, _t, _ti) \
u_char asn_type, double scale, double shift,
const void *value, size_t len, int type);
static int snmp_agent_unregister_oid_index(oid_t *oid, int index);
+static int num_compare(const int *a, const int *b);
static u_char snmp_agent_get_asn_type(oid *oid, size_t oid_len) {
struct tree *node = get_tree(oid, oid_len, g_agent->tp);
char oid_str[DATA_MAX_NAME_LEN];
for (llentry_t *de = llist_head(list); de != NULL; de = de->next) {
data_definition_t *dd = de->value;
+ table_definition_t const *td = dd->table;
if (dd->table != NULL)
DEBUG(PLUGIN_NAME ": Column:");
DEBUG(PLUGIN_NAME ": Plugin: %s", dd->plugin);
if (dd->plugin_instance)
DEBUG(PLUGIN_NAME ": PluginInstance: %s", dd->plugin_instance);
- if (dd->is_index_key)
- DEBUG(PLUGIN_NAME ": Index: %s", index_opts[dd->index_key_pos]);
+ if (dd->is_index_key) {
+ index_key_t const *index_key = &td->index_keys[dd->index_key_pos];
+
+ DEBUG(PLUGIN_NAME ": IndexKey:");
+ DEBUG(PLUGIN_NAME ": Source: %s", index_opts[index_key->source]);
+ DEBUG(PLUGIN_NAME ": Type: %s",
+ (index_key->type == ASN_INTEGER) ? "Integer" : "String");
+ if (index_key->regex)
+ DEBUG(PLUGIN_NAME ": Regex: %s", index_key->regex);
+ if (index_key->group != GROUP_UNUSED)
+ DEBUG(PLUGIN_NAME ": Group: %d", index_key->group);
+ }
if (dd->type)
DEBUG(PLUGIN_NAME ": Type: %s", dd->type);
if (dd->type_instance)
for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
table_definition_t *td = te->value;
- if (!td->indexes_len) {
+ if (!td->index_keys_len) {
ERROR(PLUGIN_NAME ": Index keys not defined for '%s'", td->name);
return -EINVAL;
}
return 0;
}
-static int snmp_agent_fill_index_list(table_definition_t const *td,
+static int snmp_agent_parse_index_key(const char *input, char *regex,
+ regex_t *regex_info, int gi,
+ regmatch_t *m) {
+ regmatch_t matches[MAX_MATCHES];
+
+ int ret = regexec(regex_info, input, MAX_MATCHES, matches, 0);
+ if (!ret) {
+ if (gi > regex_info->re_nsub) {
+ ERROR(PLUGIN_NAME ": Group index %d not found. Check regex config", gi);
+ return -1;
+ }
+ *m = matches[gi];
+ } else if (ret == REG_NOMATCH) {
+ ERROR(PLUGIN_NAME ": No match found");
+ return -1;
+ } else {
+ char msgbuf[100];
+
+ regerror(ret, regex_info, msgbuf, sizeof(msgbuf));
+ ERROR(PLUGIN_NAME ": Regex match failed: %s", msgbuf);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int snmp_agent_create_token(char const *input, int t_off, int n,
+ c_avl_tree_t *tree,
+ netsnmp_variable_list *index_key) {
+ token_t *token = malloc(sizeof(*token));
+ int *offset = malloc(sizeof(*offset));
+ int ret = 0;
+
+ assert(tree != NULL);
+
+ if (token == NULL || offset == NULL)
+ goto error;
+
+ token->key = index_key;
+ token->str = strndup(input + t_off, n);
+
+ if (token->str == NULL)
+ goto error;
+
+ *offset = t_off;
+ ret = c_avl_insert(tree, (void *)offset, (void *)token);
+
+ if (ret != 0)
+ goto error;
+
+ return 0;
+
+error:
+
+ ERROR(PLUGIN_NAME ": Could not allocate memory to create token");
+ sfree(token->str);
+ sfree(token);
+ sfree(offset);
+ return -1;
+}
+
+static int snmp_agent_delete_token(int t_off, c_avl_tree_t *tree) {
+ token_t *token = NULL;
+ int *offset = NULL;
+
+ int ret = c_avl_remove(tree, &t_off, (void **)&offset, (void **)&token);
+
+ if (ret != 0) {
+ ERROR(PLUGIN_NAME ": Could not delete token");
+ return -1;
+ }
+
+ sfree(token->str);
+ sfree(token);
+ sfree(offset);
+ return 0;
+}
+
+static int snmp_agent_get_token(c_avl_tree_t *tree, int mpos) {
+
+ int *pos;
+ char *token;
+ int prev_pos = 0;
+
+ c_avl_iterator_t *it = c_avl_get_iterator(tree);
+ while (c_avl_iterator_next(it, (void **)&pos, (void **)&token) == 0) {
+ if (*pos >= mpos)
+ break;
+ else
+ prev_pos = *pos;
+ }
+
+ c_avl_iterator_destroy(it);
+ return prev_pos;
+}
+
+static int snmp_agent_tokenize(const char *input, c_avl_tree_t *tokens,
+ const regmatch_t *m,
+ netsnmp_variable_list *key) {
+ assert(tokens != NULL);
+
+ int ret = 0;
+ int len = strlen(input);
+
+ /* Creating first token that is going to be split later */
+ if (c_avl_size(tokens) == 0) {
+ ret = snmp_agent_create_token(input, 0, len, tokens, NULL);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Divide token that contains current match into two */
+ int t_pos = snmp_agent_get_token(tokens, m->rm_so);
+ ret = snmp_agent_delete_token(t_pos, tokens);
+
+ if (ret != 0)
+ return -1;
+
+ ret = snmp_agent_create_token(input, t_pos, m->rm_so - t_pos, tokens, key);
+
+ if (ret != 0)
+ return -1;
+
+ if (len - m->rm_eo > 1) {
+ ret = snmp_agent_create_token(input, m->rm_eo, len - m->rm_eo + 1, tokens,
+ NULL);
+ if (ret != 0) {
+ snmp_agent_delete_token(t_pos, tokens);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int snmp_agent_fill_index_list(table_definition_t *td,
value_list_t const *vl) {
int ret;
+ int i;
netsnmp_variable_list *key = td->index_list_cont;
+ char const *ptr;
- for (int i = 0; i < td->indexes_len; i++) {
+ for (i = 0; i < td->index_keys_len; i++) {
/* var should never be NULL */
assert(key != NULL);
+ ptr = NULL;
+ ret = 0;
+ const index_key_src_t source = td->index_keys[i].source;
+ c_avl_tree_t *const tokens = td->tokens[source];
/* Generating list filled with all data necessary to generate an OID */
- switch (td->indexes[i]) {
+ switch (source) {
case INDEX_HOST:
- ret = snmp_set_var_value(key, vl->host, strlen(vl->host));
+ ptr = vl->host;
break;
case INDEX_PLUGIN:
- ret = snmp_set_var_value(key, vl->plugin, strlen(vl->plugin));
+ ptr = vl->plugin;
break;
case INDEX_PLUGIN_INSTANCE:
- ret = snmp_set_var_value(key, vl->plugin_instance,
- strlen(vl->plugin_instance));
+ ptr = vl->plugin_instance;
break;
case INDEX_TYPE:
- ret = snmp_set_var_value(key, vl->type, strlen(vl->type));
+ ptr = vl->type;
break;
case INDEX_TYPE_INSTANCE:
- ret =
- snmp_set_var_value(key, vl->type_instance, strlen(vl->type_instance));
+ ptr = vl->type_instance;
break;
default:
- ERROR(PLUGIN_NAME ": Unknown index key type provided");
+ ERROR(PLUGIN_NAME ": Unknown index key source provided");
return -EINVAL;
}
if (ret != 0)
return -EINVAL;
+
+ /* Parsing input string if necessary */
+ if (td->index_keys[i].regex) {
+ regmatch_t m = {-1, -1};
+
+ /* Parsing input string */
+ ret = snmp_agent_parse_index_key(ptr, td->index_keys[i].regex,
+ &td->index_keys[i].regex_info,
+ td->index_keys[i].group, &m);
+ if (ret != 0) {
+ ERROR(PLUGIN_NAME ": Error executing regex");
+ return ret;
+ }
+
+ /* Tokenizing input string if not done yet */
+ if (td->tokens_done == 0)
+ ret = snmp_agent_tokenize(ptr, tokens, &m, key);
+
+ if (ret != 0)
+ return -1;
+
+ if (td->index_keys[i].type == ASN_INTEGER) {
+ int val = strtol(ptr + m.rm_so, NULL, 0);
+ ret = snmp_set_var_value(key, &val, sizeof(val));
+ } else
+ ret = snmp_set_var_value(key, ptr + m.rm_so, m.rm_eo - m.rm_so);
+ } else
+ ret = snmp_set_var_value(key, ptr, strlen(ptr));
key = key->next_variable;
}
+
+ /* Tokens for all source strings are generated */
+ for (i = 0; i < MAX_KEY_SOURCES; i++)
+ td->tokens_done = 1;
+
return 0;
}
static int snmp_agent_prep_index_list(table_definition_t const *td,
netsnmp_variable_list **index_list) {
/* Generating list having only the structure (with no values) letting us
- * know how to parse an OID */
- for (int i = 0; i < td->indexes_len; i++) {
- switch (td->indexes[i]) {
+ * know how to parse an OID*/
+ for (int i = 0; i < td->index_keys_len; i++) {
+ switch (td->index_keys[i].source) {
case INDEX_HOST:
case INDEX_PLUGIN:
case INDEX_PLUGIN_INSTANCE:
case INDEX_TYPE:
case INDEX_TYPE_INSTANCE:
- snmp_varlist_add_variable(index_list, NULL, 0, ASN_OCTET_STR, NULL, 0);
+ snmp_varlist_add_variable(index_list, NULL, 0, td->index_keys[i].type,
+ NULL, 0);
break;
default:
- ERROR(PLUGIN_NAME ": Unknown index key type provided");
+ ERROR(PLUGIN_NAME ": Unknown index key source provided");
return -EINVAL;
}
}
return 0;
}
-static int snmp_agent_generate_index(table_definition_t const *td,
+static int snmp_agent_generate_index(table_definition_t *td,
value_list_t const *vl, oid_t *index_oid) {
- /* According to given information by indexes list
+ /* According to given information by index_keys list
* index OID is going to be built
*/
int ret = snmp_agent_fill_index_list(td, vl);
c_avl_destroy((*td)->instance_index);
(*td)->instance_index = NULL;
}
-
snmp_free_varbind((*td)->index_list_cont);
+
+ int i;
+ token_t *tok = NULL;
+
+ for (i = 0; i < (*td)->index_keys_len; i++) {
+ sfree((*td)->index_keys[i].regex);
+ regfree(&(*td)->index_keys[i].regex_info);
+ }
+ for (i = 0; i < MAX_KEY_SOURCES; i++)
+ if ((*td)->tokens[i] != NULL) {
+ while (c_avl_pick((*td)->tokens[i], &key, (void **)&tok) == 0) {
+ sfree(key);
+ sfree(tok->str);
+ sfree(tok);
+ }
+ c_avl_destroy((*td)->tokens[i]);
+ (*td)->tokens[i] = NULL;
+ }
sfree((*td)->name);
sfree(*td);
return;
}
-static int snmp_agent_parse_oid_indexes(const table_definition_t *td,
- oid_t *index_oid) {
+static int snmp_agent_parse_oid_index_keys(const table_definition_t *td,
+ oid_t *index_oid) {
int ret = parse_oid_indexes(index_oid->oid, index_oid->oid_len,
td->index_list_cont);
if (ret != SNMPERR_SUCCESS)
return ret;
}
+static int snmp_agent_build_name(char **name, c_avl_tree_t *tokens) {
+
+ int *pos;
+ token_t *tok;
+ char str[DATA_MAX_NAME_LEN];
+ char out[DATA_MAX_NAME_LEN] = {0};
+ c_avl_iterator_t *it = c_avl_get_iterator(tokens);
+
+ if (it == NULL) {
+ ERROR(PLUGIN_NAME ": Error getting tokens list iterator");
+ return -1;
+ }
+
+ while (c_avl_iterator_next(it, (void **)&pos, (void **)&tok) == 0) {
+ strncat(out, tok->str, strlen(tok->str));
+ if (tok->key != NULL) {
+ if (tok->key->type == ASN_INTEGER) {
+ snprintf(str, sizeof(str), "%ld", *tok->key->val.integer);
+ strncat(out, str, strlen(str));
+ } else { /* OCTET_STR */
+ strncat(out, (char *)tok->key->val.string,
+ strlen((char *)tok->key->val.string));
+ }
+ }
+ }
+ *name = strdup(out);
+ c_avl_iterator_destroy(it);
+
+ if (*name == NULL) {
+ ERROR(PLUGIN_NAME ": Could not allocate memory");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int snmp_agent_format_name(char *name, int name_len,
data_definition_t *dd, oid_t *index_oid) {
+ int ret = 0;
+
if (index_oid == NULL) {
/* It's a scalar */
format_name(name, name_len, hostname_g, dd->plugin, dd->plugin_instance,
} else {
/* Need to parse string index OID */
const table_definition_t *td = dd->table;
- int ret = snmp_agent_parse_oid_indexes(td, index_oid);
+ ret = snmp_agent_parse_oid_index_keys(td, index_oid);
if (ret != 0)
return ret;
int i = 0;
netsnmp_variable_list *key = td->index_list_cont;
- char *host = hostname_g;
- char *plugin = dd->plugin;
- char *plugin_instance = dd->plugin_instance;
- char *type = dd->type;
- char *type_instance = dd->type_instance;
+ char str[DATA_MAX_NAME_LEN];
+ char *fields[MAX_KEY_SOURCES] = {hostname_g, dd->plugin,
+ dd->plugin_instance, dd->type,
+ dd->type_instance};
+
+ /* Looking for simple keys only */
while (key != NULL) {
- switch (td->indexes[i]) {
- case INDEX_HOST:
- host = (char *)key->val.string;
- break;
- case INDEX_PLUGIN:
- plugin = (char *)key->val.string;
- break;
- case INDEX_PLUGIN_INSTANCE:
- plugin_instance = (char *)key->val.string;
- break;
- case INDEX_TYPE:
- type = (char *)key->val.string;
- break;
- case INDEX_TYPE_INSTANCE:
- type_instance = (char *)key->val.string;
- break;
- default:
- ERROR(PLUGIN_NAME ": Unkown index type!");
- return -EINVAL;
+ if (!td->index_keys[i].regex) {
+ index_key_src_t source = td->index_keys[i].source;
+
+ if (source < INDEX_HOST || source > INDEX_TYPE_INSTANCE) {
+ ERROR(PLUGIN_NAME ": Unkown index key source!");
+ return -EINVAL;
+ }
+
+ if (td->index_keys[i].type == ASN_INTEGER) {
+ snprintf(str, sizeof(str), "%ld", *key->val.integer);
+ fields[source] = str;
+ } else /* OCTET_STR */
+ fields[source] = (char *)key->val.string;
}
key = key->next_variable;
i++;
}
- format_name(name, name_len, host, plugin, plugin_instance, type,
- type_instance);
+ /* Keys with regexes */
+ for (i = 0; i < MAX_KEY_SOURCES; i++) {
+ if (td->tokens[i] == NULL)
+ continue;
+ ret = snmp_agent_build_name(&fields[i], td->tokens[i]);
+ if (ret != 0)
+ return ret;
+ }
+ format_name(name, name_len, fields[INDEX_HOST], fields[INDEX_PLUGIN],
+ fields[INDEX_PLUGIN_INSTANCE], fields[INDEX_TYPE],
+ fields[INDEX_TYPE_INSTANCE]);
+ for (i = 0; i < MAX_KEY_SOURCES; i++) {
+ if (td->tokens[i])
+ sfree(fields[i]);
+ }
}
return 0;
if (dd->is_index_key) {
const table_definition_t *td = dd->table;
- ret = snmp_agent_parse_oid_indexes(td, index_oid);
+ int ret = snmp_agent_parse_oid_index_keys(td, index_oid);
if (ret != 0)
return ret;
for (int pos = 0; pos < dd->index_key_pos; pos++)
key = key->next_variable;
- requests->requestvb->type = ASN_OCTET_STR;
- snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
- (const u_char *)key->val.string,
- strlen((const char *)key->val.string));
+ requests->requestvb->type = td->index_keys[dd->index_key_pos].type;
+
+ if (requests->requestvb->type == ASN_INTEGER)
+ snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+ key->val.integer, sizeof(*key->val.integer));
+ else /* OCTET_STR */
+ snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+ (const u_char *)key->val.string,
+ strlen((const char *)key->val.string));
pthread_mutex_unlock(&g_agent->lock);
return 0;
}
-/* Parsing table column representing index key */
-static int snmp_agent_config_index(table_definition_t *td,
- data_definition_t *dd, oconfig_item_t *ci) {
+/* Getting index key source that will represent table row */
+static int snmp_agent_config_index_key_source(table_definition_t *td,
+ data_definition_t *dd,
+ oconfig_item_t *ci) {
char *val = NULL;
int ret = cf_util_get_string(ci, &val);
_Bool match = 0;
- for (int i = 0; i < MAX_INDEX_TYPES; i++) {
+ for (int i = 0; i < MAX_KEY_SOURCES; i++) {
if (strcasecmp(index_opts[i], (const char *)val) == 0) {
- td->indexes[td->indexes_len] = i;
+ td->index_keys[td->index_keys_len].source = i;
+ td->index_keys[td->index_keys_len].group = GROUP_UNUSED;
+ td->index_keys[td->index_keys_len].regex = NULL;
match = 1;
break;
}
}
sfree(val);
- dd->index_key_pos = td->indexes_len++;
+ dd->index_key_pos = td->index_keys_len++;
dd->is_index_key = 1;
return 0;
}
+/* Getting format string used to parse values from index key source */
+static int snmp_agent_config_index_key_regex(table_definition_t *td,
+ data_definition_t *dd,
+ oconfig_item_t *ci) {
+ index_key_t *index_key = &td->index_keys[dd->index_key_pos];
+
+ int ret = cf_util_get_string(ci, &index_key->regex);
+ if (ret != 0)
+ return -1;
+
+ ret = regcomp(&index_key->regex_info, index_key->regex, REG_EXTENDED);
+ if (ret) {
+ ERROR(PLUGIN_NAME ": Could not compile regex for %s", dd->name);
+ return -1;
+ }
+
+ index_key_src_t source = index_key->source;
+ if (td->tokens[source] == NULL && index_key->regex != NULL) {
+ td->tokens[source] =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ if (td->tokens[source] == NULL) {
+ ERROR(PLUGIN_NAME ": Could not allocate memory for AVL tree");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int snmp_agent_config_index_key(table_definition_t *td,
+ data_definition_t *dd,
+ oconfig_item_t *ci) {
+ int ret = 0;
+
+ for (int i = 0; (i < ci->children_num && ret == 0); i++) {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp("Source", option->key) == 0)
+ ret = snmp_agent_config_index_key_source(td, dd, option);
+ else if (strcasecmp("Regex", option->key) == 0)
+ ret = snmp_agent_config_index_key_regex(td, dd, option);
+ else if (strcasecmp("Group", option->key) == 0)
+ ret = cf_util_get_int(option, &td->index_keys[dd->index_key_pos].group);
+ }
+
+ return ret;
+}
+
/* This function parses configuration of both scalar and table column
* because they have nearly the same structure */
static int snmp_agent_config_table_column(table_definition_t *td,
oconfig_item_t *ci) {
data_definition_t *dd;
int ret = 0;
+ oconfig_item_t *option_tmp = NULL;
assert(ci != NULL);
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *option = ci->children + i;
- /* Instance option is reserved for table entry only */
- if (strcasecmp("Index", option->key) == 0 && td != NULL)
- ret = snmp_agent_config_index(td, dd, option);
- else if (strcasecmp("Plugin", option->key) == 0)
+ /* First 3 options are reserved for table entry only */
+ if (td != NULL && strcasecmp("IndexKey", option->key) == 0) {
+ dd->is_index_key = 1;
+ option_tmp = option;
+ } else if (strcasecmp("Plugin", option->key) == 0)
ret = cf_util_get_string(option, &dd->plugin);
else if (strcasecmp("PluginInstance", option->key) == 0)
ret = cf_util_get_string(option, &dd->plugin_instance);
}
}
+ if (dd->is_index_key) {
+ ret = snmp_agent_config_index_key(td, dd, option_tmp);
+ td->index_keys[dd->index_key_pos].type =
+ snmp_agent_get_asn_type(dd->oids[0].oid, dd->oids[0].oid_len);
+
+ if (ret != 0) {
+ snmp_agent_free_data(&dd);
+ return -1;
+ }
+ }
+
llentry_t *entry = llentry_create(dd->name, dd);
if (entry == NULL) {
snmp_agent_free_data(&dd);
return -ENOMEM;
}
+ for (int i = 0; i < MAX_KEY_SOURCES; i++)
+ td->tokens[i] = NULL;
+ td->tokens_done = 0;
+
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *option = ci->children + i;
}
static int snmp_agent_config(oconfig_item_t *ci) {
-
int ret = snmp_agent_preinit();
if (ret != 0) {
--- /dev/null
+/**
+ * collectd - src/snmp_agent_test.c
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Marcin Mozejko <marcinx.mozejko@intel.com>
+ **/
+
+#include "snmp_agent.c"
+#include "testing.h"
+
+DEF_TEST(oid_to_string) {
+ oid_t o = {.oid = {1, 2, 3, 4, 5, 6, 7, 8, 9}, .oid_len = 9};
+ char oid_str[DATA_MAX_NAME_LEN];
+
+ int ret = snmp_agent_oid_to_string(oid_str, DATA_MAX_NAME_LEN, &o);
+ EXPECT_EQ_INT(o.oid_len * 2 - 1, ret);
+ EXPECT_EQ_STR("1.2.3.4.5.6.7.8.9", oid_str);
+
+ return 0;
+}
+
+/* Testing formatting metric name for simple scalar */
+DEF_TEST(format_name_scalar) {
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ dd->plugin = strdup("test_plugin");
+ dd->plugin_instance = strdup("test_plugin_inst");
+ dd->type = strdup("test_type");
+ dd->type_instance = strdup("test_type_inst");
+
+ char name[DATA_MAX_NAME_LEN];
+ int ret = snmp_agent_format_name(name, sizeof(name), dd, NULL);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(
+ "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+ name);
+
+ sfree(dd->plugin);
+ sfree(dd->plugin_instance);
+ sfree(dd->type);
+ sfree(dd->type_instance);
+ sfree(dd);
+
+ return 0;
+}
+
+DEF_TEST(format_name_simple_index) {
+ netsnmp_variable_list *index_list_tmp = NULL;
+ oid_t index_oid;
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+ table_definition_t *td = calloc(1, sizeof(*td));
+
+ td->index_list_cont = NULL;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[1].type = ASN_OCTET_STR;
+ dd->table = td;
+ dd->plugin = strdup("test_plugin");
+ dd->type = strdup("test_type");
+
+ char *plugin_inst = strdup("test_plugin_inst");
+ char *type_inst = strdup("test_type_inst");
+
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+ plugin_inst, strlen(plugin_inst));
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR, type_inst,
+ strlen(type_inst));
+
+ build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+ NULL, 0, index_list_tmp);
+
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+
+ char name[DATA_MAX_NAME_LEN];
+
+ int ret = snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(
+ "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+ name);
+
+ snmp_free_varbind(index_list_tmp);
+ snmp_free_varbind(td->index_list_cont);
+ sfree(dd->plugin);
+ sfree(dd->type);
+ sfree(dd);
+ sfree(td);
+ sfree(plugin_inst);
+ sfree(type_inst);
+
+ return 0;
+}
+
+DEF_TEST(format_name_regex_index) {
+ netsnmp_variable_list *index_list_tmp = NULL;
+ oid_t index_oid;
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+ table_definition_t *td = calloc(1, sizeof(*td));
+
+ td->index_keys_len = 3;
+ td->index_list_cont = NULL;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[1].type = ASN_INTEGER;
+ td->index_keys[1].regex = strdup("^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$");
+ td->index_keys[1].group = 1;
+ td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[2].type = ASN_INTEGER;
+ td->index_keys[2].regex = strdup("^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$");
+ td->index_keys[2].group = 1;
+
+ dd->table = td;
+ dd->plugin = strdup("test_plugin");
+ dd->type = strdup("test_type");
+
+ char *plugin_inst = strdup("test_plugin_inst");
+ char *type_inst = strdup("vcpu_1-cpu_10");
+ int vcpu = 1;
+ int cpu = 10;
+
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+ plugin_inst, strlen(plugin_inst));
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER, &vcpu, 1);
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER, &cpu, 1);
+
+ build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+ NULL, 0, index_list_tmp);
+
+ token_t *token;
+ int *offset;
+
+ td->tokens[INDEX_TYPE_INSTANCE] =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+
+ token = malloc(sizeof(*token));
+ offset = malloc(sizeof(*offset));
+ token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+ ASN_INTEGER, NULL, 0);
+ token->str = strdup("vcpu_");
+ *offset = 0;
+ int ret = c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+ (void *)token);
+
+ token = malloc(sizeof(*token));
+ offset = malloc(sizeof(*offset));
+ token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+ ASN_INTEGER, NULL, 0);
+ token->str = strdup("-cpu_");
+ *offset = 6;
+ ret += c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+ (void *)token);
+ char name[DATA_MAX_NAME_LEN];
+
+ ret += snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(
+ "example.com/test_plugin-test_plugin_inst/test_type-vcpu_1-cpu_10", name);
+ while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+ (void **)&token) == 0) {
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+ }
+ c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+ snmp_free_varbind(index_list_tmp);
+ snmp_free_varbind(td->index_list_cont);
+ for (int i = 0; i < td->index_keys_len; i++)
+ sfree(td->index_keys[i].regex);
+ sfree(dd->plugin);
+ sfree(dd->type);
+ sfree(dd);
+ sfree(td);
+ sfree(plugin_inst);
+ sfree(type_inst);
+
+ return 0;
+}
+
+DEF_TEST(prep_index_list) {
+ table_definition_t *td = calloc(1, sizeof(*td));
+
+ assert(td != NULL);
+ td->index_keys_len = 5;
+ td->index_keys[0].source = INDEX_HOST;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_PLUGIN;
+ td->index_keys[1].type = ASN_OCTET_STR;
+ td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[2].type = ASN_INTEGER;
+ td->index_keys[3].source = INDEX_TYPE;
+ td->index_keys[3].type = ASN_INTEGER;
+ td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[4].type = ASN_OCTET_STR;
+ td->index_list_cont = NULL;
+
+ int ret = snmp_agent_prep_index_list(td, &td->index_list_cont);
+ EXPECT_EQ_INT(0, ret);
+
+ netsnmp_variable_list *key = td->index_list_cont;
+
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_INTEGER, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_INTEGER, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+ key = key->next_variable;
+ OK(key == NULL);
+
+ snmp_free_varbind(td->index_list_cont);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(fill_index_list_simple) {
+ table_definition_t *td = calloc(1, sizeof(*td));
+ assert(td != NULL);
+
+ /* Preparing value list */
+ value_list_t *vl = calloc(1, sizeof(*vl));
+ assert(vl != NULL);
+ strncpy(vl->host, "test_hostname", DATA_MAX_NAME_LEN);
+ strncpy(vl->plugin, "test_plugin", DATA_MAX_NAME_LEN);
+ strncpy(vl->plugin_instance, "test_plugin_inst", DATA_MAX_NAME_LEN);
+ strncpy(vl->type, "test_type", DATA_MAX_NAME_LEN);
+ strncpy(vl->type_instance, "test_type_inst", DATA_MAX_NAME_LEN);
+
+ td->index_keys_len = 5;
+ td->index_keys[0].source = INDEX_HOST;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_PLUGIN;
+ td->index_keys[1].type = ASN_OCTET_STR;
+ td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[2].type = ASN_OCTET_STR;
+ td->index_keys[3].source = INDEX_TYPE;
+ td->index_keys[3].type = ASN_OCTET_STR;
+ td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[4].type = ASN_OCTET_STR;
+
+ td->index_list_cont = NULL;
+ for (int i = 0; i < td->index_keys_len; i++)
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR,
+ NULL, 0);
+
+ int ret = snmp_agent_fill_index_list(td, vl);
+ EXPECT_EQ_INT(0, ret);
+
+ netsnmp_variable_list *key = td->index_list_cont;
+
+ ret = 0;
+
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->host, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->plugin, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->type, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->type_instance, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key == NULL);
+
+ snmp_free_varbind(td->index_list_cont);
+ sfree(vl);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(fill_index_list_regex) {
+ table_definition_t *td = calloc(1, sizeof(*td));
+ int ret = 0;
+
+ assert(td != NULL);
+
+ /* Preparing value list */
+ value_list_t *vl = calloc(1, sizeof(*vl));
+ strncpy(vl->plugin_instance, "test_plugin_inst", DATA_MAX_NAME_LEN);
+ strncpy(vl->type_instance, "1test2test3", DATA_MAX_NAME_LEN);
+
+ td->index_keys_len = 4;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[1].type = ASN_INTEGER;
+ td->index_keys[1].regex = strdup("^([0-9])test[0-9]test[0-9]$");
+ td->index_keys[1].group = 1;
+ td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[2].type = ASN_INTEGER;
+ td->index_keys[2].regex = strdup("^[0-9]test([0-9])test[0-9]$");
+ td->index_keys[2].group = 1;
+ td->index_keys[3].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[3].type = ASN_INTEGER;
+ td->index_keys[3].regex = strdup("^[0-9]test[0-9]test([0-9])$");
+ td->index_keys[3].group = 1;
+
+ td->index_list_cont = NULL;
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+ for (int i = 1; i < td->index_keys_len; i++) {
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_INTEGER, NULL,
+ 0);
+ ret = regcomp(&td->index_keys[i].regex_info, td->index_keys[i].regex,
+ REG_EXTENDED);
+ EXPECT_EQ_INT(0, ret);
+ }
+ td->tokens[INDEX_TYPE_INSTANCE] =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ assert(td->tokens[INDEX_TYPE_INSTANCE] != NULL);
+
+ ret = snmp_agent_fill_index_list(td, vl);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, td->tokens_done);
+
+ netsnmp_variable_list *key = td->index_list_cont;
+
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(1, *key->val.integer);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(2, *key->val.integer);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(3, *key->val.integer);
+ key = key->next_variable;
+ OK(key == NULL);
+
+ token_t *token;
+ int *offset;
+
+ while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+ (void **)&token) == 0) {
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+ }
+
+ c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+ snmp_free_varbind(td->index_list_cont);
+ sfree(vl);
+
+ for (int i = 0; i < td->index_keys_len; i++) {
+ sfree(td->index_keys[i].regex);
+ regfree(&td->index_keys[i].regex_info);
+ }
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(config_index_key_source) {
+ oconfig_item_t *ci = calloc(1, sizeof(*ci));
+ table_definition_t *td = calloc(1, sizeof(*td));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ assert(ci != NULL);
+ assert(td != NULL);
+ assert(dd != NULL);
+
+ ci->values = calloc(1, sizeof(*ci->values));
+ assert(ci->values != NULL);
+ ci->values_num = 1;
+ ci->values->value.string = strdup("PluginInstance");
+ ci->values->type = OCONFIG_TYPE_STRING;
+
+ int ret = snmp_agent_config_index_key_source(td, dd, ci);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, td->index_keys_len);
+ EXPECT_EQ_INT(0, dd->index_key_pos);
+ EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+ EXPECT_EQ_INT(GROUP_UNUSED, td->index_keys[0].group);
+ OK(td->index_keys[0].regex == NULL);
+
+ sfree(ci->values->value.string);
+ sfree(ci->values);
+ sfree(ci);
+ sfree(td);
+ sfree(dd);
+
+ return 0;
+}
+
+DEF_TEST(config_index_key_regex) {
+ oconfig_item_t *ci = calloc(1, sizeof(*ci));
+ table_definition_t *td = calloc(1, sizeof(*td));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ assert(ci != NULL);
+ assert(td != NULL);
+ assert(dd != NULL);
+
+ dd->index_key_pos = 0;
+ td->index_keys_len = 1;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].group = 1;
+ ci->values = calloc(1, sizeof(*ci->values));
+ assert(ci->values != NULL);
+ ci->values_num = 1;
+ ci->values->value.string = strdup("^([0-9])test[0-9]test[0-9]$");
+ ci->values->type = OCONFIG_TYPE_STRING;
+
+ int ret = snmp_agent_config_index_key_regex(td, dd, ci);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(td->index_keys[0].regex, "^([0-9])test[0-9]test[0-9]$");
+ OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+ c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+ sfree(ci->values->value.string);
+ sfree(ci->values);
+ sfree(ci);
+ sfree(td->index_keys[0].regex);
+ regfree(&td->index_keys[0].regex_info);
+ sfree(td);
+ sfree(dd);
+
+ return 0;
+}
+
+DEF_TEST(config_index_key) {
+ oconfig_item_t *ci = calloc(1, sizeof(*ci));
+ table_definition_t *td = calloc(1, sizeof(*td));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ assert(ci != NULL);
+ assert(td != NULL);
+ assert(dd != NULL);
+
+ ci->children_num = 3;
+ ci->children = calloc(1, sizeof(*ci->children) * ci->children_num);
+
+ ci->children[0].key = strdup("Source");
+ ci->children[0].parent = ci;
+ ci->children[0].values_num = 1;
+ ci->children[0].values = calloc(1, sizeof(*ci->children[0].values));
+ assert(ci->children[0].values != NULL);
+ ci->children[0].values->value.string = strdup("PluginInstance");
+ ci->children[0].values->type = OCONFIG_TYPE_STRING;
+
+ ci->children[1].key = strdup("Regex");
+ ci->children[1].parent = ci;
+ ci->children[1].values_num = 1;
+ ci->children[1].values = calloc(1, sizeof(*ci->children[0].values));
+ assert(ci->children[1].values != NULL);
+ ci->children[1].values->value.string = strdup("^([0-9])test[0-9]test[0-9]$");
+ ci->children[1].values->type = OCONFIG_TYPE_STRING;
+
+ ci->children[2].key = strdup("Group");
+ ci->children[2].parent = ci;
+ ci->children[2].values_num = 1;
+ ci->children[2].values = calloc(1, sizeof(*ci->children[0].values));
+ assert(ci->children[2].values != NULL);
+ ci->children[2].values->value.number = 1;
+ ci->children[2].values->type = OCONFIG_TYPE_NUMBER;
+
+ int ret = snmp_agent_config_index_key(td, dd, ci);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, td->index_keys_len);
+ EXPECT_EQ_INT(0, dd->index_key_pos);
+ EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+ EXPECT_EQ_INT(1, td->index_keys[0].group);
+ EXPECT_EQ_STR("^([0-9])test[0-9]test[0-9]$", td->index_keys[0].regex);
+ OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+ sfree(ci->children[0].values->value.string);
+ sfree(ci->children[0].values);
+ sfree(ci->children[0].key);
+
+ sfree(ci->children[1].values->value.string);
+ sfree(ci->children[1].values);
+ sfree(ci->children[1].key);
+
+ sfree(ci->children[2].values);
+ sfree(ci->children[2].key);
+
+ sfree(ci->children);
+ sfree(ci);
+
+ c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+ sfree(dd);
+ sfree(td->index_keys[0].regex);
+ regfree(&td->index_keys[0].regex_info);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(parse_index_key) {
+ char *regex = strdup("test-([0-9])-([0-9])");
+ char *input = strdup("snmp-test-5-6");
+ regex_t regex_info;
+ regmatch_t match;
+
+ assert(regex != NULL);
+ assert(input != NULL);
+
+ int ret = regcomp(®ex_info, regex, REG_EXTENDED);
+ EXPECT_EQ_INT(0, ret);
+
+ ret = snmp_agent_parse_index_key(input, regex, ®ex_info, 0, &match);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(5, match.rm_so);
+ EXPECT_EQ_INT(13, match.rm_eo);
+
+ ret = snmp_agent_parse_index_key(input, regex, ®ex_info, 1, &match);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(10, match.rm_so);
+ EXPECT_EQ_INT(11, match.rm_eo);
+
+ ret = snmp_agent_parse_index_key(input, regex, ®ex_info, 2, &match);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(12, match.rm_so);
+ EXPECT_EQ_INT(13, match.rm_eo);
+
+ sfree(regex);
+ sfree(input);
+ regfree(®ex_info);
+
+ return 0;
+}
+
+DEF_TEST(create_token) {
+ c_avl_tree_t *tokens =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ char *input = strdup("testA1-testB2");
+
+ assert(tokens != NULL);
+ assert(input != NULL);
+
+ int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(2, c_avl_size(tokens));
+
+ token_t *token;
+ int *offset;
+
+ ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(6, *offset);
+ EXPECT_EQ_STR("-testB", token->str);
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+
+ ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(0, *offset);
+ EXPECT_EQ_STR("testA", token->str);
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+
+ ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+ OK(ret != 0);
+
+ c_avl_destroy(tokens);
+ sfree(input);
+
+ return 0;
+}
+
+DEF_TEST(delete_token) {
+ c_avl_tree_t *tokens =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ char *input = strdup("testA1-testB2-testC3");
+
+ assert(tokens != NULL);
+ assert(input != NULL);
+
+ int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ ret = snmp_agent_create_token(input, 13, 6, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(3, c_avl_size(tokens));
+ ret = snmp_agent_delete_token(6, tokens);
+ EXPECT_EQ_INT(0, ret);
+
+ token_t *token;
+ int *offset;
+
+ ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(0, *offset);
+ EXPECT_EQ_STR("testA", token->str);
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+
+ ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(13, *offset);
+ EXPECT_EQ_STR("-testC", token->str);
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+
+ ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+ OK(ret != 0);
+
+ c_avl_destroy(tokens);
+ sfree(input);
+
+ return 0;
+}
+
+DEF_TEST(get_token) {
+ c_avl_tree_t *tokens =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ char *input = strdup("testA1-testB2-testC3");
+
+ assert(tokens != NULL);
+ assert(input != NULL);
+
+ int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ ret = snmp_agent_create_token(input, 13, 6, tokens, NULL);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(3, c_avl_size(tokens));
+ ret = snmp_agent_get_token(tokens, 12);
+ EXPECT_EQ_INT(6, ret);
+
+ token_t *token;
+ int *offset;
+
+ while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0) {
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+ }
+
+ c_avl_destroy(tokens);
+ sfree(input);
+
+ return 0;
+}
+
+DEF_TEST(tokenize) {
+ regmatch_t m[3] = {{5, 6}, /* "1" */
+ {12, 13}, /* "2" */
+ {19, 20}}; /* "3" */
+ c_avl_tree_t *tokens =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ char *input = strdup("testA1-testB2-testC3");
+ token_t *token;
+ int *offset;
+ c_avl_iterator_t *it;
+ int ret;
+
+ assert(tokens != NULL);
+ assert(input != NULL);
+
+ /* First pass */
+ ret = snmp_agent_tokenize(input, tokens, &m[0], NULL);
+ EXPECT_EQ_INT(0, ret);
+ it = c_avl_get_iterator(tokens);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("testA", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("-testB2-testC3", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ OK(ret != 0);
+ c_avl_iterator_destroy(it);
+
+ /* Second pass */
+ ret = snmp_agent_tokenize(input, tokens, &m[1], NULL);
+ EXPECT_EQ_INT(0, ret);
+ it = c_avl_get_iterator(tokens);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("testA", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("-testB", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("-testC3", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ OK(ret != 0);
+ c_avl_iterator_destroy(it);
+
+ /* Third pass */
+ ret = snmp_agent_tokenize(input, tokens, &m[2], NULL);
+ EXPECT_EQ_INT(0, ret);
+ it = c_avl_get_iterator(tokens);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("testA", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("-testB", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("-testC", token->str);
+ ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+ OK(ret != 0);
+ c_avl_iterator_destroy(it);
+
+ while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0) {
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+ }
+
+ c_avl_destroy(tokens);
+ sfree(input);
+
+ return 0;
+}
+
+DEF_TEST(build_name) {
+ table_definition_t *td = calloc(1, sizeof(*td));
+ c_avl_tree_t *tokens =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+
+ assert(tokens != NULL);
+ assert(td != NULL);
+
+ int n[3] = {1, 2, 3};
+ char *t[3] = {"testA", "-testB", "-testC"};
+ int off[3] = {0, 6, 13};
+ token_t *token;
+ int *offset;
+ int ret = 0;
+ char *name = NULL;
+
+ td->index_list_cont = NULL;
+ for (int i = 0; i < 3; i++) {
+ token = malloc(sizeof(*token));
+ token->str = t[i];
+ token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+ ASN_INTEGER, &n[i], sizeof(n[i]));
+ assert(token->key != NULL);
+ offset = &off[i];
+ ret = c_avl_insert(tokens, (void *)offset, (void *)token);
+ assert(ret == 0);
+ }
+
+ ret = snmp_agent_build_name(&name, tokens);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("testA1-testB2-testC3", name);
+
+ while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0)
+ sfree(token);
+
+ c_avl_destroy(tokens);
+ snmp_free_varbind(td->index_list_cont);
+ sfree(td);
+ sfree(name);
+ return 0;
+}
+
+int main(void) {
+ /* snmp_agent_oid_to_string */
+ RUN_TEST(oid_to_string);
+
+ /* snmp_agent_prep_index_list */
+ RUN_TEST(prep_index_list);
+
+ /* snmp_agent_fill_index_list */
+ RUN_TEST(fill_index_list_simple);
+ RUN_TEST(fill_index_list_regex);
+
+ /* snmp_agent_format_name */
+ RUN_TEST(format_name_scalar);
+ RUN_TEST(format_name_simple_index);
+ RUN_TEST(format_name_regex_index);
+
+ /* snmp_agent_config_index_key_source */
+ RUN_TEST(config_index_key_source);
+
+ /* snmp_agent_config_index_key_regex */
+ RUN_TEST(config_index_key_regex);
+
+ /* snmp_agent_config_index_key */
+ RUN_TEST(config_index_key);
+
+ /*snmp_agent_parse_index_key */
+ RUN_TEST(parse_index_key);
+
+ /* snmp_agent_create_token */
+ RUN_TEST(create_token);
+
+ /* snmp_agent_delete_token */
+ RUN_TEST(delete_token);
+
+ /* snmp_agent_get_token */
+ RUN_TEST(get_token);
+
+ /* snmp_agent_tokenize */
+ RUN_TEST(tokenize);
+
+ /* snmp_agent_build_name */
+ RUN_TEST(build_name);
+
+ END_TEST;
+}