Net-SNMP代理开发之表格开发入门
net-snmp开发比较难以理解的是表格的实现方式,本文,笔者尝试一个人的理解,用简单的方式解释net-snmp的开发。并通过示例来说明如何开发表格。
Net-SNMP表格的理解
在Net-SNMP中,表格的标识方法如下图所示:
上图定义了表的类型,每一个表有个Entry标识,表示行,通过MIB定了了表头的结构。其中XXXTable和XXXEntry是MIB的规范,其中ColumnN为列名称,可以像标量定义。如果按MIB的OID编码方法,表格的数据可以解释为下图:
比如需要访问第一行,第一列的数据,需要通过XXXTable.XXXEntry.XXXColumn1.1来访问,绿色的数字表示那一列,红色的数字表示哪一行。比如要访问第二行,第五列,通过XXXTable.XXXEntry.XXXColumn5.2来访问。
Net-SNMP表格数据的迭代器访问方式
通过mib2c命令可以生成表格访问的代码模板,本文讲述mib2c.iterator_access.conf配置文件生成的模板。该模板生成表格访问代码除需要实现get/set函数外,还需要实现表格行的迭代。
在讲述之前,请读者想一下:如果我需要按表格的方式访问时,我应该如何实现代码?首先,我需要知道用户请求的行索引,让后通过索引定位到行,再依据访问的列调用特定的get/set方法。通过索引找到对应的行就需要对表格所有行上索引列进行遍历,定位特定的行。SNMP协议支持遍历(snmpwalk),从第一行开始,遍历到最后一行。所以表格的迭代需要实现一个遍历表格的方法。
net-snmp库需要使用一个迭代(循环)上下文,来保存当前的迭代位置,还有一个内容上下文,保存一行数据的上下文,net-snmp库需要获得索引数据的指针和数据的长度,用于比较。迭代上下文在迭代时作为迭代的参数,内容上下文会作为get/set的参数。
对于iterator.conf或iterator_access.conf的配置文件生成的文件需要实现xxx_get_first_data_point和xxx_get_next_data_point函数。xxx_get_first_data_point和xxx_get_next_data_point两个函数取得表格的第一行,以及下一行。这两个函数前两个参数为用户自定义指针,my_loop_context由xxxx_get_first_data_point创建,XXX_get_next_data_point和xxx_context_convert_function函数中作为参数中传入。
xxx_get_first_data_point返回第一行索引对象指针,并初始化迭代器上下文和数据上下文。
xxx_get_next_data_point返回后续行索引对象指针,并修改迭代器上下文和数据上下文。
一般,在C语言中,我们通过链表实现一个表格的模型,如下图所示。标识索引可以单独定义,也可以定义在数据中,数据可以是一个结构体,标识表格的一行数据。
netsnmp_variable_list *
controllerTable_get_first_data_point(void **my_loop_context, void **my_data_context,
netsnmp_variable_list *put_index_data,
netsnmp_iterator_info *mydata)
{
netsnmp_variable_list *vptr;
lampctrl_row_t* prow = get_first_lampctrl_row();
if (prow == NULL){
*my_loop_context = NULL;
*my_data_context = NULL;
return NULL;
}
*my_loop_context = prow;
*my_data_context = prow->data;
vptr = put_index_data;
snmp_set_var_value(vptr, prow->index, strlen(prow->index));
vptr = vptr->next_variable;
return put_index_data;
}
上述代码通过方法get_first_lampctrl_row()取得表格的第一行对象,让后把行对象写入迭代上下文(my_loop_context),把数据对象写入数据上下文(my_data_context),接着,通过snmp_set_var_value把索引写入put_index_data结构体(一个snmp的变量结构,可以存SNMP定义的所有数据类型),本示例代码使用的字符串的索引,最后返回索引的变量对象指针。
netsnmp_variable_list *
controllerTable_get_next_data_point(void **my_loop_context, void **my_data_context,
netsnmp_variable_list *put_index_data,
netsnmp_iterator_info *mydata)
{
netsnmp_variable_list *vptr;
lampctrl_row_t* pcurrow = *my_loop_context;
lampctrl_row_t* prow = get_next_lampctrl_row(pcurrow);
if (prow == NULL){
*my_loop_context = NULL;
*my_data_context = NULL;
return NULL;
}
*my_loop_context = prow;
*my_data_context = prow->data;
vptr = put_index_data;
snmp_set_var_value(vptr, prow->index, strlen(prow->index));
vptr = vptr->next_variable;
return put_index_data;
}
在xxx_get_next_data_point实现是,迭代器上下文和数据上下文都通过参数传递过来了,迭代器指向了当前行的位置,而数据上下文指向了当前行的数据。该行数需要通过上下文获得下一行的迭代器上下文和数据上下文,并更新上下文参数的数据。最后,把新行的索引写入索引数据。
char *get_controllerId(void *data_context, size_t *ret_len) {
lampctrl_data_t* pdata = (lampctrl_data_t*)data_context;
DEBUGMSGTL(("lampController:get", "controller Id:%s\n", pdata->controllerId));
*ret_len = strlen(pdata->controllerId);
return pdata->controllerId; /** XXX: replace this with a pointer to a real value */
}
get方法有一个数据上下文(data_context),还有一个放回值长度的参数,需要设置为返回数据的长度。通过数据上下文,可以很容易的取得对应列(每一个列都实现了一个get/set方法)。数据返回的是数据的地址,本例为字符串,直接使用了字符串地址,并通过strlen计算了数据的长度。
(高级)迭代器上下文和snmp变量结构体
需要完全理解表格的操作,需要理解结构体:netsnmp_iterator_info_s的定义。
迭代器的定义如下:
/** @struct netsnmp_iterator_info_s
* Holds iterator information containing functions which should be
called by the iterator_handler to loop over your data set and
sort it in a SNMP specific manner.
The netsnmp_iterator_info typedef can be used instead of directly calling this struct if you would prefer.
*/
typedef struct netsnmp_iterator_info_s {
/** Number of handlers that own this data structure. */
int refcnt;
/** Responsible for: returning the first set of "index" data, a
loop-context pointer, and optionally a data context
pointer */
Netsnmp_First_Data_Point *get_first_data_point;
/** Given the previous loop context, this should return the
next loop context, associated index set and optionally a
data context */
Netsnmp_Next_Data_Point *get_next_data_point;
/** If a data context wasn't supplied by the
get_first_data_point or get_next_data_point functions and
the make_data_context pointer is defined, it will be called
to convert a loop context into a data context. */
Netsnmp_Make_Data_Context *make_data_context;
/** A function which should free the loop context. This
function is called at *each* iteration step, which is
not-optimal for speed purposes. The use of
free_loop_context_at_end instead is strongly
encouraged. This can be set to NULL to avoid its usage. */
Netsnmp_Free_Loop_Context *free_loop_context;
/** Frees a data context. This will be called at any time a
data context needs to be freed. This may be at the same
time as a correspondng loop context is freed, or much much
later. Multiple data contexts may be kept in existence at
any time. */
Netsnmp_Free_Data_Context *free_data_context;
/** Frees a loop context at the end of the entire iteration
sequence. Generally, this would free the loop context
allocated by the get_first_data_point function (which would
then be updated by each call to the get_next_data_point
function). It is not called until the get_next_data_point
function returns a NULL */
Netsnmp_Free_Loop_Context *free_loop_context_at_end;
/** This can be used by client handlers to store any
information they need */
void *myvoid;
int flags;
#define NETSNMP_ITERATOR_FLAG_SORTED 0x01
#define NETSNMP_HANDLER_OWNS_IINFO 0x02
/** A pointer to the netsnmp_table_registration_info object
this iterator is registered along with. */
netsnmp_table_registration_info *table_reginfo;
/* Experimental extension - Use At Your Own Risk
(these two fields may change/disappear without warning) */
Netsnmp_First_Data_Point *get_row_indexes;
netsnmp_variable_list *indexes;
} netsnmp_iterator_info;
下面简要说明一下主要参数的作用(笔者是理解力该结构体才理解表格迭代的方法的):
get_first_data_point 函数返回第一行记录的索引变量列表,一个记录行迭代器上下文指针和数据上下文指针(可选)需要设置。
get_next_data_point 函数返回后续行记录的索引,提供了迭代器和数据上下文指针,同时需要修改迭代器和数据上下文指针指向下一记录。make_data_context函数在数据上下文没有在get_first_data_point和get_next_data_point函数中设置数据上下文变量时,这个函数会调用。
free_data_context函数释放数据上下文。
free_loop_context_at_end 释放迭代器上下文。
netsnmp_variable_list定义了SNMP数据变量的结构体,理解它更有助于编写代理的实现:
后记
net-snmp表格的实现,主要在于理解,理解了,设计数据结构,和实现代码就很简单。由于参考资料中使用的方法都有点复杂,所以理解就存在困难。本文使用的数据结构简单,定义一个行数据结构,在定义一个链表结构,就可以实现表格的访问了。
typedef struct _lampctrl_data_t{
char controllerId[MAX_CONTROLLER_ID_LEN];
unsigned int sysUpTime;
//...
unsigned int batChargeMode;
}lampctrl_data_t;
typedef struct _lampctrl_node_t{
lampctrl_data_t data;
struct _lampctrl_node_t* next;
}lampctrl_node_t;