天天看點

net-snmp代理開發之表格開發入門

Net-SNMP代理開發之表格開發入門

net-snmp開發比較難以了解的是表格的實作方式,本文,筆者嘗試一個人的了解,用簡單的方式解釋net-snmp的開發。并通過示例來說明如何開發表格。

Net-SNMP表格的了解

現實中的表格如下圖所示,有多行,多列,有唯一确定某一行的索引列,可以是固定的一列,也可以是多列共同決定某行。

net-snmp代理開發之表格開發入門

在Net-SNMP中,表格的辨別方法如下圖所示:

net-snmp代理開發之表格開發入門

上圖定義了表的類型,每一個表有個Entry辨別,表示行,通過MIB定了了表頭的結構。其中XXXTable和XXXEntry是MIB的規範,其中ColumnN為列名稱,可以像标量定義。如果按MIB的OID編碼方法,表格的資料可以解釋為下圖:

net-snmp代理開發之表格開發入門

比如需要通路第一行,第一列的資料,需要通過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語言中,我們通過連結清單實作一個表格的模型,如下圖所示。辨別索引可以單獨定義,也可以定義在資料中,資料可以是一個結構體,辨別表格的一行資料。

net-snmp代理開發之表格開發入門
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代理開發之表格開發入門

後記

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;           

繼續閱讀