用NET-SNMP軟件包開發簡單客戶端代理

用NET-SNMP軟件包開發簡單客戶端代理

寫在前面的話:

對於net-snmp我也是一個初學者,開始學習時也碰到了很多低級的問題。在很多論壇上(事實上比較少^_^, 建議大家直接去sourcefoge社區看關於net-snmpmail-list),都沒有比較初級入門的文章,本着開源學習的精神,把自己的一點收穫,共享給大家。通過參考一些前輩的文章和幫助文檔,本文實現了一個簡單的mib,並編寫了文檔。本文主要面向初級學習者(我也是個菜鳥),歡迎大家留言討論。

 

作者:solomoon

完成時間:2005-9-11

Email[email protected]

Webhttp://bibu.blogchina.com

 

1     SNMP協議簡介. 3

1.1          網絡管理協議結構... 3

1.2          管理信息庫... 4

1.3          SNMP的版本... 4

2     SNMP開發軟件包. 5

2.1          NET-SNMP簡介和安裝... 5

2.2          NET-SNMP代理的配置... 5

2.3          NET-SNMP工具的使用... 6

3     擴展開發——代理. 7

3.1          NET-SNMP中的scalar對象和table對象... 7

3.2          NET-SNMP擴展代理的兩種方式... 7

3.3          自定義MIB. 8

3.4          自定義MIB——簡單變量的實現... 9

3.5          自定義MIB——表對象的實現... 11

3.5.1       mib.iterator.conf模版的實現... 11

3.5.2       mib.iterator_access.conf模版的實現... 16

3.6          代碼的合併... 16

3.7          配置和運行... 16

4     開發中的問題與解決. 17

5     總結. 17

6     附錄. 18

6.1          主函數foxmail_new.c. 18

6.2          簡單變量實現代碼... 20

6.2.1       display_time.c. 20

6.2.2       display_time.h. 22

6.3          表的實現... 22

6.3.1       ExampleTable.c. 22

6.3.2       ExampleTable.h. 31

6.3.3       ExampleTable_access.c. 31

6.3.4       ExampleTable_access.h. 40

6.3.5       ExampleTable_checkfns.c. 40

6.3.6       ExampleTable_checkfns.h. 41

6.3.7       ExampleTable_checkfns_local.c. 42

6.3.8       ExampleTable_checkfns_local.h. 43

6.3.9       ExampleTable_columns.h. 43

6.3.10     ExampleTable_enums.h. 44

6.4 自定義mib文件MyMib.txt 44


NET-SNMP軟件包開發簡單客戶端代理

1           SNMP協議簡介

作爲一個完備的系統,必須有一套反饋機制來調整系統的運行。簡單網絡管理協議產生的目的,就是爲了使鬆散的網絡更加有效地運行。它廣泛的應用於監測網絡的狀態、網絡設備的運行情況、各種電腦設備以及一些輔助的外圍設備,使得網絡管理員通過對節點的查詢和設置,發現並定位故障,進而採取相應措施維護網絡。網絡管理的研究已經發展了許多年,對於日益紛繁的需求,簡捷性和擴展性仍是研究的主題。本文檔的目的是關於客戶端代理的開發,不是對協議發展的探討。本文中協議相關資料可以參考RFC文檔:      

RFC1155Structure and Identification of Management Information for TCP/IP-based

Internets         

RFC1157 SNMP                  

RFC1212 Concise MIB Definitions

RFC1215 A Convention for Defining Traps

RFC1905 Protocol Operations for SNMPv2        

RFC2011 SNMPv2 Management Information Base for the Internet Protocol using SMIv2

RFC2578 Structure of Management Information

RFC2579 Textual Conventions

RFC2580 Conformance Statements

1.1         網絡管理協議結構

SNMP的網絡管理模型包括以下關鍵元素:管理端、代理端、管理信息庫、網絡管理協議。它基於tcp/ip協議,屬於應用層協議,通過udp協議通信。管理端與代理端的通信原語包括:Get,Getnext,Set,Trap。對應這些命令相應的SNMP結構框架實現如圖1所示

 

圖1.    SNMP實現結構圖

從上圖我們可以看到協議,消息傳遞方式等。另外,在udp數據包中,發送信息是按ASN.1自解釋方式編碼的。但對於許多小型被監管設備,可能會運行不同協議,或者運行完整代理花費很大,於是產生了代管設備,主代理和子代理的概念。在小型設備上運行子代理,把數據發給主代理來完成snmp協議的通信。

1.2          管理信息庫

SNMPMIB(管理信息結構)爲基礎來描述被監管資源,由此建立的數據集和稱之爲MIB

庫。它是一種樹型結構的數據庫,被監管的對象都處於葉子節點上。每個被監管對象都由一個唯一的對象標識符來識別。對象信息的存儲結構由MIB定義的簡單變量和表來構造,它一般包含描述名(對象標識符)、數據類型、讀寫規則、功能描述、狀態。MIB的定義可以查詢RFC1155,它定義了四種基本數據類型:INTEGEROCTET STRINGOBJECT IDENTIFIERNULL。由這四種基本類型通過SEQUENCE構造列和表,以及新類型如:NetworkAddressIpAddressCounterGaugeTimeTicksOpaque等,以及宏定義。當然,根據需要還可以構造自己的數據類型。

1.3         SNMP的版本

目前SNMP有三個版本snmpV1snmpV2snmpV3。針對原始的V1版,93版的v2加入了安全機制,但用戶對其並不感興趣,在96版的v2中又刪除了安全機制,99年開始醞釀的v3版開始提出一個snmp的統一架構,採用User-based安全模型和View-based訪問控制模型提供SNMP網絡管理的安全性。安全機制是SNMPv3的最具特色的內容。

2           SNMP開發軟件包

目前,開發SNMP的軟件包有許多可以選擇如SNMP++AGENT++NET-SNMP等。這裏我們選用的是NET-SNMP。首先它是一個開源軟件,其次基於C語言開發,便於移植。ucd-snmp源自於卡耐基.梅隆大學的SNMP軟件包CMU snmp 2.1.2.1, 由加州大學Davis分校(University of California at Davis)開發與維護, 所以命名爲ucd-snmp200011ucd-snmp項目轉到由SourceForge(www.sourceforge.net)管理, 並更名爲net-snmp

2.1         NET-SNMP簡介和安裝

net-snmp早先是在Unix平臺下開發的。現可以移植到:

* HP-UX (10.20 to 9.01 and 11.0)

       * Ultrix (4.5 to 4.2)

       * Solaris SPARC/ULTRA (2.8 to 2.3), Intel (2.9) and SunOS (4.1.4 to 4.1.2)

       * OSF (4.0, 3.2)

       * NetBSD (1.5alpha to 1.0)

       * FreeBSD (4.1 to 2.2)

       * BSDi (4.0.1 to 2.1)

       * Linux (kernels 2.4 to 1.3)

       * AIX (4.1.5, 3.2.5)

       * OpenBSD (2.8, 2.6)

       * Irix (6.5 to 5.1)

       * OS X (10.1.1 and 10.1.2)

       * Dynix/PTX 4.4

       * QNX 6.2.1A

* Windows

等多個平臺。Net-snmp是一個代理端軟件,但也提供管理端的查詢工具。安裝有兩種方式:一是直接安裝的二進制包,二是需要編譯的源代碼。我們在windows平臺上安裝的二進制包,在虛擬Unix平臺CygWin上編譯安裝的源代碼。在CygWin中,按照常規的configure, make ,make install三個步驟就可成功編譯安裝源代碼。在windows上的二進制包的安裝就非常簡單了,只需按提示就可完成。源代碼和二進制包可從www.net-snmp.org網站下載,本文中所用的是net-snmp5.2.1.2的版本。之所以要先安裝一個可運行的net-snmp系統,是因爲我們開發程序運行環境的配置文件,是按照默認安裝路徑內部設定搜索的;另外,還可以利用其提供的配置工具來生成配置文件,利用提供的查詢工具來測試程序。

2.2         NET-SNMP代理的配置

運行net-snmp之前先要進行環境設置,否則無法查詢到結果。環境配置文件由snmpconf命令交互生成。運行snmpconf後,提示有三個配置文件:snmpd.confsnmptraps.confsnmp.conf。其中,snmpd.conf用來配置代理和管理端通信時的參數,只需設置兩個參數就可正常運行程序了,一是community name,有隻讀rocommunity和讀寫rwcommunity之分,相當於訪問賬號,這裏設rocommunitypublic;另一個是訪問端口,設爲snmp協議默認的161端口。 Snmp.conf是與mib庫設置相關的配置文件。Snmptraps.conf用來設置代理陷阱,本文沒有討論陷阱。配置文件可以放在三個地方,一是盤符根目錄下,二是~/usr/etc/snmp目錄下,三是~/usr/snmp/persist,按標準路徑最好是第二種方式。

另外,snmpconfmib2c工具都是基於perl腳本的,在windows下需要安裝perl才能運行。按照幫助文檔的提示,下載ActivePerl安裝。並按照幫助文檔中perl的安裝要求,下載在win32環境下所需的其他組件,配置並測試perl模塊,使snmpconfmib2c能正常運行。

2.3         NET-SNMP工具的使用

當環境設置好後,運行snmpd.exe,即snmp代理進程,就可以使用管理工具查詢其中的信息了。Net-snmp提供的查詢工具有很多,這裏只介紹常用的幾個,而且大部分查詢命令的格式都大同小異。這裏以.iso.org.dod.internet.mgmt.mib-2.system爲例,其Oid爲:.1.3.6.1.2.1.1。結構如下:

   ………system                  .1.3.6.1.2.1.1

            |——sysDescr        .1.3.6.1.2.1.1.1

            |——sysObjectID     .1.3.6.1.2.1.1.2

            ……

1)  snmpget.exe——snmpget [OPTIONS] AGENT OID [OID]...用來查詢葉子節點

實例:snmpget –v2c –c public localhost .1.3.6.1.2.1.1.5.0

-v2c     使用的是2csnmp版本,可選1|2c|3

  -c publiccommunity 名爲public

  localhost: 代理的地址,這裏因爲代理運行在本機上,所以可用localhost

  .1.3…….0:這裏查詢的是.iso.org.dod.internet.mgmt.mib-2.system.sysName,其Oid.1.3.6.1.2.1.1.5,使用這個命令使葉子節點要在後面加.0

2 snmpgetnext.exe——snmpgetnext [OPTIONS] AGENT OID [OID]...通過父節點查詢葉子節點

實例:snmpgetnext –v2c –c public localhost .1.3.6.1.2.1.1

這個命令假設不知道葉子節點,但知道父節點,則可遍歷到第一個葉子節點。此例結果等同於上一個例子。Oid也可輸入.1.3.6.1.2,因爲它是按字典順序遍歷的。

3 snmptable.exe——snmptable [OPTIONS] AGENT TABLE-OID 用來查詢表對象

實例:snmptable –v2c –c public localhost .1.3.6.1.2.1.4.20

這個命令查詢表對象,本例中查詢的是.iso.org.dod.internet.mgmt.mib-2.ip.ipAddrTable

4snmpset.exe——snmpset [OPTIONS] AGENT OID TYPE VALUE [OID TYPE VALUE]...修改數據

   實例:snmpset –v2c –c public localhost .1.3.6.1.2.1.4.21.1.3.x i 99

        x:在這裏是索引值,表示表項中某一列的第幾個數據,根據要求設定   

        i: 這裏是列數據類型,包括i: INTEGER, u: unsigned INTEGER, t: TIMETICKS,

a: IPADDRESS o: OBJID, s: STRING, x: HEX STRING,

d: DECIMAL STRING, b: BITS U: unsigned int64,

I: signed int64, F: float, D: double

5 mib2c 用來把mib庫文件編譯成.c.h模版。具體使用在下面章節的應用中介紹

3           擴展開發——代理

當系統加入了新設備,或設備配置發生了變化等,需要實現新的mib模塊,這時就需要擴展代理端了。在利用net-snmp做擴展之前的準備工作是,一下載源代碼net-snmp5.2.1.2,二編譯出庫文件:netsnmpagent.libnetsnmphelpers.libnetsnmpmibs.libnetsnmp.lib。這四個庫的工程文件都位於~/net-snmp-5.2.1.2/win32/下對應的文件夾中。編譯庫文件時注意把netsnmp.lib放到最後編譯,它可能會參考一下前面的編譯文件。對於其他進一步的應用和開發請參考幫助文檔編譯和使用其他的工具;本文檔中實現的代理程序,在源代碼包中只需要引用這四個庫文件。

開發工具VC6.0的環境設置也需要注意,參考幫助文檔readme.win32,設置好include目錄,library目錄。在project/configure的選項link的相關欄目中添加上四個庫文件,還要加上wsock32.lib庫。另外,在程序編譯過程中會碰到與VC的默認庫相沖突的地方,按提示在上述添加庫的地方加上/NODEFAULTLIB:XXX.LIB來除去默認庫。

代理的開發過程基本上遵循:mib模塊à轉換成C文件à編譯進代理中。

3.1         NET-SNMP中的scalar對象和table對象

mib模塊一般都由變量和表組成。因此Net-snmpSMI中的對象分爲兩大類:scalartableScalar就包含我們常用的整型,字符串,時間等等數據類型。table就是scalar的一種集合,有一個和多個列組成,類似於數據庫中的表。它必須具有索引項,用來按一定順序檢索表項。

Mib2工具通過模版把mib文件解析成.c.h文件,這些文件僅僅是半成品,還需要手工在相應地方添加相應代碼。Mib2c有很多模版,可以根據相應需要來調用不同的模版。但mib2c目前不支持同時解析scalartable 對象,對於具有這兩種對象的mib模塊,需要分別生成代碼文件,然後再合併成整體。

3.2         NET-SNMP擴展代理的兩種方式

net-snmp擴展代理,實現方式可歸結爲兩種:一是靜態庫方式,通過修改配置頭文件,在相應地方包含新引入的mib模塊的.c.h文件,然後重新編譯庫文件和代理程序;二是編譯動態共享庫,只需把新引入的mib模塊的.c.h文件編譯成動態庫,通過設置能夠讓代理程序載入。

對於第二種方式,一需要編譯成.so動態共享庫,二需要原代理程序是否包含dlmodload命令,三還要看系統是否支持。一般情況下僅支持Unix平臺。我們在CygWin下試驗過Unix平臺編譯。在VC下的編譯環境,基本上都是參考其makefile,configure等文件。

因爲是在windows平臺下開發,本文檔採用的是第一種方式。這種方式允許我們在原有mib庫上添加新的mib模塊,也可以只針對需要的mib模塊編譯單獨的程序。

源代碼包中的代理程序工程文件位於~/ net-snmp-5.2.1.2/win32/snmpd下,因爲它可移植到多個平臺,主程序代碼中有許多平臺開關,和各種選項,非常龐大。爲了簡化開發和調試,我們使用了一個幫助文檔中介紹的簡化的代理端程序框架。這個框架非常精簡,在性能上可能比原版差很多,但用來測試和開發mib庫就比較高效了。程序的運行機制:程序啓動,載入初始化mib模塊,然後進入一個等待呼叫的無限循環,代碼片段如下:

… …

init_MyMib();

… …

while(keep_running)

{

                  /* if you use select(), see snmp_select_info() in snmp_api(3) */

                  /*     --- OR ---  */

                  agent_check_and_process(1); /* 0 == don't block */

        }

我們所要做的就是實現init_MyMib()函數,即自定義的mib模塊。關於無限循環中的阻塞與否,根據mib模塊而定。如果你的應用程序需要以非阻塞方式處理SNMP數據流,就使用一步接口(例如GUI、線程、forking等)。否則,只需要使用同步接口就可以了。在阻塞模式下,程序會佔用大量cpu資源。具體實現,參見附錄5.1。下面從mib庫開始。

3.3         自定義MIB

這裏我們定義了一個私人節點的MIB,位於節點.iso.org.dod.internet. private.enterprises.foxmail下,數字Oid爲:.1.3.6.1.4.1.310。樹型結構如下:

                     foxmail                                    .1.3.6.1.4.1.310

               |----SecondCounter                        .1.3.6.1.4.1.310.1

               |----WeekTime                            .1.3.6.1.4.1.310.2

               +----ExampleTable                        .1.3.6.1.4.1.310.3

                         +----ExampleEntry              .1.3.6.1.4.1.310.3.1

                                  |----UserIndex         .1.3.6.1.4.1.310.3.1.1

                                  |----UserStatus         .1.3.6.1.4.1.310.3.1.2

                                  |----CheckTime        .1.3.6.1.4.1.310.3.1.3

                                  |----MonSet          .1.3.6.1.4.1.310.3.1.4

在這個MIB中,定義了兩個簡單變量SecondCounter:整型,WeekTime:時間類型;還定義了一個表對象ExampleTable,其中UserIndex爲索引:整型,UserStatus:字符串,CheckTime:時間類型,以上三個都是隻讀,MonSet爲讀寫變量:整型。參見附錄5.4

3.4         自定義MIB——簡單變量的實現

把自定義的MIB命名爲MyMib.txt,參照上面章節,放到net-snmp二進制安裝路徑~/usr/share/snmp/mibs下。配置snmp.conf文件,加入新的庫MyMib.txt,語法爲MIBS=+MyMib,這樣才能讓系統搜索到。然後在CMD提示符下輸入:mib2c SecondCounter選擇生成net-snmp版的代碼。就會生成SecondCounter.cSecondCounter.h文件,同樣可以產生WeekTime.cWeekTime.h文件。

由模版生成的文件,不論是簡單變量還是表對象,其整體結構都是固定模式。在模版頭文件中對節點進行宏定義,函數聲明。在模版實現文件中,分爲兩大塊:一是初始化函數,主要用來對變量註冊;二是響應函數,用來響應管理端的查詢命令,響應函數的返回值,就是我們要手工實現的。我們所要做的工作就是把數據以一種合理的方式導入到其中。

模版是針對單個變量來處理的:

1)  初始化:

init_SecondCounter(void)

{

    static oid SecondCounter_oid[] = { 1,3,6,1,4,1,310,1 };

   DEBUGMSGTL(("SecondCounter", "Initializing/n"));

   netsnmp_register_scalar(netsnmp_create_handler_registration("SecondCounter", handle_SecondCounter, SecondCounter_oid, OID_LENGTH(SecondCounter_oid),HANDLER_CAN_RWRITE

                               ));

}

如果有多個變量就要註冊多次。

2)  處理響應函數

   handle_SecondCounter(netsnmp_mib_handler *handler,

                          netsnmp_handler_registration *reginfo,

                          netsnmp_agent_request_info   *reqinfo,

                          netsnmp_request_info         *requests)

其實現主要爲:

  switch(reqinfo->mode) {

        case MODE_GET:

            snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,

                             (u_char *) /* XXX: a pointer to the scalar's data */,

/* XXX: the length of the data in bytes */);

如果有多個變量,就要簡單重複switch語句,因爲switch只提供了通信原語的選擇,變量的選擇依賴各自的處理句柄函數。

顯然這兩套代碼如果合併起來就顯得冗餘了,因爲很多地方可以共用一套代碼。在這裏參考源代碼包~/net-snmp-5.2.1.2/agent/mibgroup/examples下的example.cexample.h文件合併我們的MIB

主要實現方式是:

1)通過一種變量數組,把MIB中的變量列舉出來,代碼片斷如下:

struct variable2 foxmail_variables[] =

{    

       {FoxmailINT,ASN_INTEGER,RONLY,var_foxmail,1,{1}},

       {FoxmailTIMETICKS, ASN_TIMETICKS, RONLY, var_foxmail, 1, {2}}

     /*… … …加入其他的變量… … … */

};

結構體variable2的定義如下:

struct variable2 {

    u_char          magic;      /* passed to function as a hint */

    u_char          type;       /* type of variable */

    u_short         acl;        /* access control list for variable */

    FindVarMethod  *findVar;    /* function that finds variable */

    u_char          namelen;    /* length of name below */

    oid             name[2];    /* object identifier of variable */

};

通過上面代碼,可以看出,數組對每個變量都一一說明了變量類型、節點位置、讀寫狀態、實現函數等。

 

2 然後再對變量數組進行註冊初始化:

REGISTER_MIB("foxmail", foxmail_variables, variable2, foxmail_variables_oid);

 

3)從數組可知實現函數爲var_foxmail();其原型爲:

u_char * var_foxmail(struct variable *vp,

                           oid * name,

                            size_t * length,

                           int exact, size_t * var_len, WriteMethod ** write_method)

它通過一個switch語句來選擇響應變量,代碼片斷如下:

       switch (vp->magic) {

    case FoxmailINT:      /*這裏爲數組中的索引值*/

        long_ret=XXX;     /*XXX爲返回值,這裏可以用指針來引入內部或外部數據*/

/* *write_method=write_foxmailINT  如果是可以set的變量,需調用寫函數*/

        return (u_char *) & long_ret;

    case FoxmailTIMETICKS:

    … … …

}

總之,這種方法比較緊湊有效,完整代碼參見附錄5.2

3.5         自定義MIB——表對象的實現

爲了更加有效的描述對象,引入表來把一系列相關的信息進行綜合。因此表就由具有一個或多個變量的列組成。從實現方式和數據結構來看,表是一個結構體變量數組;從系統管理來看,就是數據庫中的表單。從管理數據庫的需求出發,就要求實現表單的插入、刪除、恢復等基本功能,更高級的功能還應實現多個表的依賴關係、觸發機制等。在底層實現上這些都要通過操縱結構體數組來完成。

Mib2c提供了表的生成模版。其目的之一是可以降低對SNMP知識的要求,二是分離請求中對數據的定位和響應中對數據的處理,使得實現上集中在對數據的處理上。因此它產生的模版主要由三個部分組成:一是數據結構,對請求數據的構造;二是數據檢索,對請求數據的定位;三是操縱數據,對請求數據進行GETSET

不同的需求有不同的實現方式,mib2c根據表中導入數據的來源提供了兩大類模版。

其分類按照運行mib2c時的交互信息:一類爲數據相對代理處於外部,適合於監視和操縱外部數據的MIB,如從操作系統或其他系統的接口提取信息;並且表中的行的創建銷燬,通常都獨立於SNMP代理。第二類是數據爲代理所有,代理對數據的操縱直接反映到數據源。

本文檔只針對第一種類型實現表,且稱之爲外部方式。在mib2c進入外部方式,仍有三個模版,分別爲:mib2c.iterate.confmib2c.iterate_access.confmib2c.mfd.conf;前兩種方式基本一樣,使用了helpers中一種叫iteratorAPI,特點是可以處理無序數據,但是效率較低,適合於小型數據存儲,對內存、響應時間要求不是很嚴格的應用;後一種就很複雜了,它提供了更加細緻的代碼框架,通過多層交互,可以區分實時、半實時、永久型的數據,可以選擇數據的存儲方式,可以做列或表之間的依賴關係,數據結構的自動綁定或手工編輯,以及其他複雜的選項。我們只實現前兩種較爲簡單的。

3.5.1       mib.iterator.conf模版的實現

首先,這個模版生成了Example.cExample.h文件。在Example.c文件中包含下列函數和結構體,形參省略:

struct  ExampleTable_entry

void  init_ExampleTable;

void  initialize_table_ExampleTable;

Netsnmp_Node_Handler  ExampleTable_handler;

Netsnmp_First_Data_Point   ExampleTable_get_first_data_point;

Netsnmp_Next_Data_Point   ExampleTable_get_next_data_point;

struct ExampleTable_entry  *ExampleTable_createEntry

void  ExampleTable_removeEntry

實現過程我們按數據結構,數據檢索,數據操縱來說明函數的調用和處理過程。

1)  結構體:

struct ExampleTable_entry {

    /* Index values */                     /*這個結構體把exampleTable中的*/

    long UserIndex;                      /*變量都列舉,並指明瞭索引變量 */

                                       /*並有一個生成鏈表的next指針  */

    /* Column values */

  /*  long UserIndex;*/                    /*去掉重複                     */

    char UserStatus;

    u_long CheckTime;

    long MonSet;

    long old_MonSet;

 

    /* Illustrate using a simple linked list */

/*    int   valid;*/                        /*沒有用到的變量             */

    struct ExampleTable_entry *next;

};

通過這個結構體,我們把從外部讀入的數據做成一個鏈表,並把鏈表頭地址傳遞給

ExampleTable_get_first_data_point;

ExampleTable_get_next_data_point;

用這兩個函數遍歷鏈表。

2)  數據的檢索:

數據的檢索由ExampleTable_handler函數來完成,其原型爲:

int  ExampleTable_handler(

    netsnmp_mib_handler               *handler,

    netsnmp_handler_registration      *reginfo,

    netsnmp_agent_request_info        *reqinfo,

        netsnmp_request_info              *requests)

檢索過程通過一個循環包含的兩個語句:

for (request=requests; request; request=request->next) {

   table_entry = (struct ExampleTable_entry *)              /*檢索匹配表中的行*/

              netsnmp_extract_iterator_context(request);

table_info = netsnmp_extract_table_info(request);         /*檢索匹配表中的列*/

這樣相當於通過行號和列號確定了數據。而上面的兩個函數需要調用

ExampleTable_get_first_data_point;

ExampleTable_get_next_data_point;

來遍歷鏈表。

3)  數據的操縱:

數據的操縱涉及到對請求命令的響應,其代碼片斷如下:

 

 

switch (reqinfo->mode) {

case MODE_GET:

switch (table_info->colnum) {

       case COLUMN_USERINDEX:

              snmp_set_var_typed_value( request->requestvb, ASN_INTEGER,

                                          table_entry->UserIndex,

                                          sizeof(table_entry->UserIndex));

       … … …             /*對於get命令,通過snmp_set_var_typed_value*/

}                        /*返回變量值                                 */

case MODE_SET_RESERVE1:          /*對於set命令,就涉及這六個步驟,這裏省略了 */

case MODE_SET_RESERVE2:          /*具體實現代碼,我們所做的程序中,沒有涉及表 */

case MODE_SET_FREE:              /*中行的創建和刪除,模版說明中認爲,set 一個 */

case MODE_SET_ACTION:            /*不存在的索引,就可以在表中爲其創建一行     */

case MODE_SET_UNDO:             

case MODE_SET_COMMIT:          

}                                /*代碼已合併到mib.iterator_access.conf模版中*/

關於表的set操作的流程圖,如下圖:

圖2.    SET的執行過程

3.5.2       mib.iterator_access.conf模版的實現

首先,這個模版生成了  ExampleTable.c

ExampleTable.h

ExampleTable_columns.h

ExampleTable_enums.h

ExampleTable_checkfns_local.h

ExampleTable_checkfns_local.c

ExampleTable_checkfns.h

ExampleTable_checkfns.c

ExampleTable_access.h

ExampleTable_access.c

這些文件實際上是對mib.iterator.conf模版功能的細化,提供更加完善方式的控制。只有部分需要修改,根據自定義的mib,只編輯了ExampleTable.cExampleTable_access.c等文件。

它提供了get_XXX()和set_XXX()函數來完成數據的獲取和設置,需要手工實現。數據的組織不再自動生成,可以自己以其他方式實現,但仍要能關聯到遍歷函數上。ExampleTable_checkfns_local.cExampleTable_checkfns.c文件都是對set的檢查操作。在主體結構和運行過程上仍和mib.iterator.conf模版一樣。參見附錄5.3

3.6         代碼的合併

在實現整個mib時,只需把簡單變量的初始化函數和表實現的初始化函數合併。在這裏簡單變量的初始化函數爲:void init_foxmail。把表的初始化函數void initialize_table_ExampleTable放到init_foxmail內部 ,再將init_foxmail作爲主函數的初始化函數,修改相關頭文件的引用,代碼的簡單合併就完成了。最後以主函數建立工程編譯全部文件。

3.7         配置和運行

在主函數中,代理的初始化由三個語句順序完成:

init_agent("example-demon");  /*運行代理名*/

init_foxmail();

init_snmp("example-demon");  /*讀入的配置文件名*/

這裏需要寫一個名爲example-demon.conf的配置文件,配置方式同介紹的snmpd.conf配置一樣。然後放在安裝目錄~/usr/etc/snmp下。關閉防火牆,在命令提示符下運行該程序,然後用snmpget,snmpgetnext,snmptable,snmpset測試程序。

4           開發中的問題與解決

在開始拿到這個軟件包時,並不知道從哪裏下手,以前沒接觸過這麼大的代碼包,只是按照幫助中關於win32平臺下的說明往下做,但在編譯源代碼包時,編譯到代理程序時就進行不下去了,出現了很多全局變量的依賴錯誤,當時的判斷是編譯環境的設置問題。於是轉到CygWin下,利用自帶的makefile來編譯整個代碼,非常成功的編譯並安裝到CygWin中。我們試着在其中編譯了幾個例子以及自己寫的幾個程序,也成功的通過了。在例子中,提供了一個makefile文件,於是就觀察其環境設置。在其gcc的編譯中,用到了

CFLAGS=-I. `net-snmp-config --cflags`

BUILDLIBS=`net-snmp-config --libs`

BUILDAGENTLIBS=`net-snmp-config --agent-libs`

查找配置文件,發現這裏面涉及到四個關鍵庫(見前面環境設置章節),在VC中我都沒有加上去。在VC中添加上去後,變量依賴問題大大減少,發現遺留的錯誤是由socket引起的。加上wsock32.lib後,在VC下就可以通過先前的程序了。

但程序運行時有碰到了問題,查不到結果,或者程序崩潰。最後我們通過檢查環境設置,對比文件,發現問題出在netsnmpagent.lib這個庫上。在原有的基礎上重編譯這個庫,新編譯的庫比原來大了幾倍,也就是說庫的編譯存在一個先後順序關係,可能這個庫要引用到其他庫,可能VC沒那麼智能,需要手工設置。解決這個問題後,後面碰到的基本上是編寫程序本身的錯誤了。

5           總結

net-snmp是一個功能很強的軟件,這個開源項目仍在不斷前進中。我們所做的只使用了其中的一小部分功能,主要是學習了它的一個開發流程、簡單的結構框架,以及工具的使用;另外也學習了snmp協議,瞭解了開發包應爲用戶隔離底層細節,提供應用接口給用戶。Net-snmp的幫助文檔並不適合新手入門,它的幫助都是針對功能寫的,一般通過它的開發網站中的tutorials來編譯幾個例子逐漸瞭解其功能,再回頭查看幫助說明。Net-snmp項目目前放在www.sourceforge.net,可以加入net-snmp的郵件列表尋求幫助。


 

6           附錄

6.1         主函數foxmail_new.c

/*主函數:foxmail_new.c */

#include <net-snmp/net-snmp-config.h>

#include <net-snmp/net-snmp-includes.h>

#include <net-snmp/agent/net-snmp-agent-includes.h>

#include <signal.h>

 

#include "Display_time.h"

#include "ExampleTable.h"

static int keep_running;

 

RETSIGTYPE

stop_server(int a) {

    keep_running = 0;

}

 

int

main (int argc, char **argv) {

  int agentx_subagent=0; /* change this if you want to be a SNMP master agent */

  int background = 0; /* change this if you want to run in the background */

  int syslog = 0; /* change this if you want to use syslog */

 

  /* print log errors to syslog or stderr */

  if (syslog)

    snmp_enable_calllog();

  else

    snmp_enable_stderrlog();

 

  /* we're an agentx subagent? */

  if (agentx_subagent) {

    /* make us a agentx client. */

    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);

  }

 

  /* run in background, if requested */

  if (background && netsnmp_daemonize(1, !syslog))

      exit(1);

 

  /* Initialize tcpip, if necessary */

  SOCK_STARTUP;

 

  /* Initialize the agent library */

  init_agent("example-demon");

 

  /* Initialize our mib code here */

init_foxmail();

 

  /* initialize vacm/usm access control  */

  if (!agentx_subagent) {

    void  init_vacm_vars();

    void  init_usmUser();

  }

 

  /* Example-demon will be used to read example-demon.conf files. */

  init_snmp("example-demon");

 

  /* If we're going to be a snmp master agent, initial the ports */

  if (!agentx_subagent)

    init_master_agent();  /* open the port to listen on (defaults to udp:161) */

 

  /* In case we recevie a request to stop (kill -TERM or kill -INT) */

  keep_running = 1;

  signal(SIGTERM, stop_server);

  signal(SIGINT, stop_server);

  snmp_log(LOG_INFO,"example-demon is up and running./n");

 

  /* your main loop here... */

  while(keep_running) {

    /* if you use select(), see snmp_select_info() in snmp_api(3) */

    /*     --- OR ---  */

    agent_check_and_process(1); /* 0 == don't block */

  }

 

  /* at shutdown time */

  snmp_shutdown("example-demon");

  SOCK_CLEANUP;

 

  return 0;

}

6.2         簡單變量實現代碼

6.2.1       display_time.c

    /*display_time.c*/

#include <net-snmp/net-snmp-config.h>

#include <net-snmp/net-snmp-includes.h>

#include <net-snmp/agent/net-snmp-agent-includes.h>

 

#if HAVE_STDLIB_H

#include <stdlib.h>

#endif

 

#if TIME_WITH_SYS_TIME

# ifdef WIN32

#  include <sys/timeb.h>

# else

#  include <sys/time.h>

# endif

# include <time.h>

#else

# if HAVE_SYS_TIME_H

#  include <sys/time.h>

# else

#  include <time.h>

# endif

#endif

 

#include "util_funcs.h"

 

#include "Display_time.h"

#include "ExampleTable.h"

 

struct variable2 foxmail_variables[] =

{    

       {FoxmailINT,ASN_INTEGER,RONLY,var_foxmail,1,{1}},

       {FoxmailTIMETICKS, ASN_TIMETICKS, RONLY, var_foxmail, 1, {2}}

};

 

oid foxmail_variables_oid[] = { 1,3,6,1,4,1,310 };

 

void

init_foxmail(void)

{

    REGISTER_MIB("foxmail", foxmail_variables, variable2,

                foxmail_variables_oid);

    initialize_table_ExampleTable();         

}

 

u_char         *

var_foxmail(struct variable *vp,

            oid * name,

            size_t * length,

            int exact, size_t * var_len, WriteMethod ** write_method)

{

    static long     long_ret;   /* for everything else */

    static time_t timep;

    struct tm *p;

    time(&timep);  

   p=gmtime(&timep);

    DEBUGMSGTL(("foxmail", "var_foxmail entered/n"));

    if (header_generic(vp, name, length, exact, var_len, write_method) ==

        MATCH_FAILED)

        return NULL;

   

    switch (vp->magic) {

   

 

    case FoxmailINT:

        long_ret=timep;  

        return (u_char *) & long_ret;

 

    case FoxmailTIMETICKS:

              time(&timep);

              long_ret=(p->tm_wday*24*3600+(p->tm_hour+8)*3600+p->tm_min*60+p->tm_sec)*100;            

              return (u_char*) & long_ret;

    default:

   

        DEBUGMSGTL(("snmpd", "unknown sub-id %d in examples/var_example/n",

                    vp->magic));

    }

  

    return NULL;

}

6.2.2       display_time.h

/*display_time.h*/

#ifndef DISPLAY_TIME_H

#define DISPLAY_TIME_H

 

config_require(util_funcs)

extern void init_foxmail(void);

extern FindVarMethod var_foxmail;

 

#define FoxmailINT 1

#define FoxmailTIMETICKS 2

 

#endif

6.3         表的實現

6.3.1       ExampleTable.c

/*ExampleTable.c*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : mib2c.iterate_access.conf,v 1.11 2004/08/31 10:23:16 dts12 Exp $

 */

 

#include <net-snmp/net-snmp-config.h>

#include <net-snmp/net-snmp-includes.h>

#include <net-snmp/agent/net-snmp-agent-includes.h>

#include "ExampleTable.h"

#include "ExampleTable_checkfns.h"

#include "ExampleTable_access.h"

 

static netsnmp_oid_stash_node *undoStorage = NULL;

static netsnmp_oid_stash_node *commitStorage = NULL;

 

struct undoInfo {

   void *ptr;

   size_t len;

};

 

struct commitInfo {

   void *data_context;

   int have_committed;

   int new_row;

};

 

void

ExampleTable_free_undoInfo(void *vptr) {

    struct undoInfo *ui = vptr;

    if (!ui)

        return;

    SNMP_FREE(ui->ptr);

    SNMP_FREE(ui);

}

 

/** Initialize the ExampleTable table by defining its contents and how it's structured */

void

initialize_table_ExampleTable(void)

{

    static oid ExampleTable_oid[] = {1,3,6,1,4,1,310,3};

    netsnmp_table_registration_info *table_info;

    netsnmp_handler_registration *my_handler;

    netsnmp_iterator_info *iinfo;

 

    /** create the table registration information structures */

    table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);

    iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info);

 

    my_handler = netsnmp_create_handler_registration("ExampleTable",

                                             ExampleTable_handler,

                                             ExampleTable_oid,

                                             OID_LENGTH(ExampleTable_oid),

                                             HANDLER_CAN_RWRITE

                                             );

           

    if (!my_handler || !table_info || !iinfo) {

        snmp_log(LOG_ERR, "malloc failed in initialize_table_ExampleTable");

        return; /** Serious error. */

    }

 

    /***************************************************

     * Setting up the table's definition

     */

    netsnmp_table_helper_add_indexes(table_info,

                                  ASN_INTEGER, /** index: MachineNumber */

                             0);

 

    /** Define the minimum and maximum accessible columns.  This

        optimizes retrival. */

    table_info->min_column = 1;

    table_info->max_column = 4;

 

    /** iterator access routines */

    iinfo->get_first_data_point = ExampleTable_get_first_data_point;

    iinfo->get_next_data_point = ExampleTable_get_next_data_point;

 

    /** you may wish to set these as well */

#ifdef MAYBE_USE_THESE

    iinfo->make_data_context = ExampleTable_context_convert_function;

    iinfo->free_data_context = ExampleTable_data_free;

 

    /** pick *only* one of these if you use them */

    iinfo->free_loop_context = ExampleTable_loop_free;

    iinfo->free_loop_context_at_end = ExampleTable_loop_free;

#endif

 

    /** tie the two structures together */

    iinfo->table_reginfo = table_info;

 

    /***************************************************

     * registering the table with the master agent

     */

    DEBUGMSGTL(("initialize_table_ExampleTable",

                "Registering table ExampleTable as a table iterator/n"));             

    netsnmp_register_table_iterator(my_handler, iinfo);

}

 

/** Initializes the ExampleTable module */

/*void

init_ExampleTable(void)

{

*/

  /** here we initialize all the tables we're planning on supporting */

/*    initialize_table_ExampleTable();

}*/

 

/** handles requests for the ExampleTable table, if anything else needs to be done */

int

ExampleTable_handler(

    netsnmp_mib_handler               *handler,

    netsnmp_handler_registration      *reginfo,

    netsnmp_agent_request_info        *reqinfo,

    netsnmp_request_info              *requests) {

 

    netsnmp_request_info *request;

    netsnmp_table_request_info *table_info;

    netsnmp_variable_list *var;

    struct commitInfo *ci = NULL;

 

    void *data_context = NULL;

 

    oid *suffix;

    size_t suffix_len;

 

    /** column and row index encoded portion */

    suffix = requests->requestvb->name + reginfo->rootoid_len + 1;

    suffix_len = requests->requestvb->name_length -

        (reginfo->rootoid_len + 1);

   

    for(request = requests; request; request = request->next) {

        var = request->requestvb;

        if (request->processed != 0)

            continue;

 

        switch (reqinfo->mode) {

        case MODE_GET:

            data_context =  netsnmp_extract_iterator_context(request);

            if (data_context == NULL) {

                netsnmp_set_request_error(reqinfo, request,

                                          SNMP_NOSUCHINSTANCE);

                continue;

            }

            break;

 

        case MODE_SET_RESERVE1:

            data_context =  netsnmp_extract_iterator_context(request);

            if (data_context == NULL) {

                netsnmp_set_request_error(reqinfo, request,

                                          SNMP_ERR_NOCREATION);

                continue;

            }

            break;

 

        default: /* == the other SET modes */

            ci = netsnmp_oid_stash_get_data(commitStorage,

                                            suffix+1, suffix_len-1);

            break;

        }

 

        /** extracts the information about the table from the request */

        table_info = netsnmp_extract_table_info(request);

        /** table_info->colnum contains the column number requested */

        /** table_info->indexes contains a linked list of snmp variable

           bindings for the indexes of the table.  Values in the list

           have been set corresponding to the indexes of the

           request */

        if (table_info == NULL) {

            continue;

        }

 

        switch(reqinfo->mode) {

            case MODE_GET:

                switch(table_info->colnum) {

                    case COLUMN_MACHINENUMBER:

                            {

                                long *retval;

                                size_t retval_len = 0;

                                retval = get_MachineNumber(data_context, &retval_len);

                                if (retval)

                                    snmp_set_var_typed_value(var, ASN_INTEGER,

                                                         (const u_char *) retval,

                                                         retval_len);

                            }

                        break;

 

                    case COLUMN_MACHINESTATUS:

                            {

                                char *retval;

                                size_t retval_len = 0;

                                retval = get_MachineStatus(data_context, &retval_len);

                                if (retval)

                                    snmp_set_var_typed_value(var, ASN_OCTET_STR,

                                                         (const u_char *) retval,

                                                         retval_len);

                            }

                        break;

 

                    case COLUMN_CHECKTIME:

                            {

                                u_long *retval;

                                size_t retval_len = 0;

                                retval = get_CheckTime(data_context, &retval_len);

                                if (retval)

                                    snmp_set_var_typed_value(var, ASN_TIMETICKS,

                                                         (const u_char *) retval,

                                                         retval_len);

                            }

                        break;

 

                    case COLUMN_MONSET:

                            {

                                long *retval;

                                size_t retval_len = 0;

                                retval = get_MonSet(data_context, &retval_len);

                                if (retval)

                                    snmp_set_var_typed_value(var, ASN_INTEGER,

                                                         (const u_char *) retval,

                                                         retval_len);

                            }

                        break;

 

                    default:

                /** We shouldn't get here */

                        snmp_log(LOG_ERR, "problem encountered in ExampleTable_handler: unknown column/n");

                }

                break;

 

            case MODE_SET_RESERVE1:

                ci = netsnmp_oid_stash_get_data(commitStorage,

                                                suffix+1, suffix_len-1);

               

                if (!ci) {

                    /** create the commit storage info */

                    ci = SNMP_MALLOC_STRUCT(commitInfo);

                    if (!data_context) {

                       // ci->data_context = ExampleTable_create_data_context(table_info->indexes);

                        ci->new_row = 1;

                    } else {

                        ci->data_context = data_context;

                    }

                    netsnmp_oid_stash_add_data(&commitStorage,

                                               suffix+1, suffix_len-1, ci);

                }

            break;

               

            case MODE_SET_RESERVE2:

                switch(table_info->colnum) {

                          case COLUMN_MONSET:

                            {

                                long *retval;

                                size_t retval_len = 0;

                                struct undoInfo *ui = NULL;

                                int ret;

                               

                    /** first, get the old value */

                                retval = get_MonSet(ci->data_context, &retval_len);

                                if (retval) {

                                    ui = SNMP_MALLOC_STRUCT(undoInfo);

                                    ui->len = retval_len;

                                    memdup((u_char **) &ui->ptr,

                                           (u_char *) retval,

                                           ui->len);

                                }

 

                    /** check the new value, possibly against the

                        older value for a valid state transition */

                                ret = check_MonSet(request->requestvb->type,

                                                   (long *) request->requestvb->val.integer,

                                                   request->requestvb->val_len,

                                                   retval, retval_len);

                                if (ret != 0) {

                                    netsnmp_set_request_error(reqinfo, request,

                                                              ret);

                                    ExampleTable_free_undoInfo(ui);

                                } else if (ui) {

                        /** remember information for undo purposes later */

                                    netsnmp_oid_stash_add_data(&undoStorage,

                                                               suffix,

                                                               suffix_len,

                                                               ui);

                                }

                               

                            }

                            break;

                    default:

                       netsnmp_set_request_error(reqinfo, request,

                                                 SNMP_ERR_NOTWRITABLE);

                       break;

                 }

                break;

 

            case MODE_SET_ACTION:

            /** save a variable copy */

                switch(table_info->colnum) {

                          case COLUMN_MONSET:

                            {

                                int ret;

                                ret = set_MonSet(ci->data_context,

                                             (long *) request->requestvb->val.integer,

                                             request->requestvb->val_len);

                                if (ret) {

                                    netsnmp_set_request_error(reqinfo, request,

                                                              ret);

                                }

                            }

                            break;

                 }

                break;

 

            case MODE_SET_COMMIT:

                if (!ci->have_committed) {

                    /** do this once per row only */

                    ExampleTable_commit_row(&ci->data_context, ci->new_row);

                    ci->have_committed = 1;

                }

                break;

 

            case MODE_SET_UNDO:

             /** save a variable copy */

                switch(table_info->colnum) {

                          case COLUMN_MONSET:

                            {

                                int retval;

                                struct undoInfo *ui;

                                ui = netsnmp_oid_stash_get_data(undoStorage,

                                                                suffix,

                                                                suffix_len);

                                retval = set_MonSet(ci->data_context, ui->ptr,

                                                ui->len);

                                if (retval) {

                                    netsnmp_set_request_error(reqinfo, request,

                                                              SNMP_ERR_UNDOFAILED);

                                }

                            }

                            break;

                }

                break;

               

            case MODE_SET_FREE:

                break;

 

            default:

                snmp_log(LOG_ERR, "problem encountered in ExampleTable_handler: unsupported mode/n");

        }

    }

 

    /** clean up after all requset processing has ended */

    switch(reqinfo->mode) {

    case MODE_SET_UNDO:

    case MODE_SET_FREE:

    case MODE_SET_COMMIT:

        /** clear out the undo cache */

        netsnmp_oid_stash_free(&undoStorage, ExampleTable_free_undoInfo);

        netsnmp_oid_stash_free(&commitStorage, netsnmp_oid_stash_no_free);

    }

 

    return SNMP_ERR_NOERROR;

}

6.3.2       ExampleTable.h

/*ExampleTable.h*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : mib2c.iterate_access.conf,v 1.11 2004/08/31 10:23:16 dts12 Exp $

 */

#ifndef EXAMPLETABLE_H

#define EXAMPLETABLE_H

 

/** other required module components */

config_require(ExampleTable_access)

config_require(ExampleTable_checkfns)

 

/* function declarations */

void init_ExampleTable(void);

void initialize_table_ExampleTable(void);

Netsnmp_Node_Handler ExampleTable_handler;

 

 

/* column number definitions for table ExampleTable */

#include "ExampleTable_columns.h"

 

/* enum definions */

#include "ExampleTable_enums.h"

 

#endif /** EXAMPLETABLE_H */

6.3.3       ExampleTable_access.c

/*ExampleTable_access.c*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : mib2c.access_functions.conf,v 1.9 2004/10/14 12:57:33 dts12 Exp $

 */

 

#include <net-snmp/net-snmp-config.h>

#include <net-snmp/net-snmp-includes.h>

#include <net-snmp/agent/net-snmp-agent-includes.h>

#include "ExampleTable_access.h"

#include "ExampleTable_enums.h"

#include <stdio.h>

 

struct ExampleTable_entry {

    long MachineNumber;

    char MachineStatus[10];

    u_long CheckTime;

    long   MonSet;

    struct ExampleTable_entry *next;

};

 

struct ExampleTable_entry  *ExampleTable_head=NULL;

 

/* create a new row in the (unsorted) table */

struct ExampleTable_entry *

ExampleTable_createEntry(long  MachineNumber,

                                           char *MachineStatus,

                                           u_long CheckTime,

                                           long MonSet

                                                      )

 {

    struct ExampleTable_entry *entry;

 

    entry = SNMP_MALLOC_TYPEDEF(struct ExampleTable_entry);

    if (!entry)

        return NULL;

 

    entry->MachineNumber = MachineNumber;

    strcpy(entry->MachineStatus ,MachineStatus);

    entry->CheckTime     = CheckTime;

       entry->MonSet        =MonSet;

    entry->next          = ExampleTable_head;

    ExampleTable_head    = entry;

   

    return entry;

}

 

/* remove a row from the table */

void

ExampleTable_removeEntry( struct ExampleTable_entry *entry )

{

    struct ExampleTable_entry *ptr, *prev;

 

    if (!entry)

        return;    /* Nothing to remove */

 

    for ( ptr  = ExampleTable_head, prev = NULL;

          ptr != NULL;

          prev = ptr, ptr = ptr->next ) {

        if ( ptr == entry )

            break;

    }

    if ( !ptr )

        return;    /* Can't find it */

 

    if ( prev == NULL )

        ExampleTable_head = ptr->next;

    else

        prev->next = ptr->next;

 

     SNMP_FREE( entry );   /* XXX - release any other internal resources */

}

 

void data_read(void)

{

       FILE *fp;

  int i;

  struct  ExampleTable_entry *temp;

      fp=fopen("data.txt","r");

      while (ExampleTable_head)/*clean link list begin*/

              {

                     temp=ExampleTable_head->next;

                     ExampleTable_removeEntry(ExampleTable_head);

                     ExampleTable_head=temp;

              }/*clean link list end*/

             

       temp=SNMP_MALLOC_TYPEDEF(struct ExampleTable_entry);

       temp->next=NULL;

      

     /*set up a link list begin*/

       if(fp)

       {

  i=fscanf(fp,"%d %s %d %d",&temp->MachineNumber, temp->MachineStatus, &temp->CheckTime,&temp->MonSet);

  /*fscanf return reading var numbers .if EOF return -1.feof() dosen't work well*/

       while(i>0)

  {

              ExampleTable_createEntry(temp->MachineNumber,temp->MachineStatus,temp->CheckTime,temp->MonSet);

              i=fscanf(fp,"%d %s %d %d",&temp->MachineNumber, temp->MachineStatus, &temp->CheckTime,&temp->MonSet);

       }

SNMP_FREE(temp);

fclose(fp);/*set up a link list end*/

       }

       else

       {

              printf("Error:Can't open data.txt!/n");

              /*exit(1);*/

       }

}

 

 

static u_long long_ret;

/** returns the first data point within the ExampleTable table data.

 

    Set the my_loop_context variable to the first data point structure

    of your choice (from which you can find the next one).  This could

    be anything from the first node in a linked list, to an integer

    pointer containing the beginning of an array variable.

 

    Set the my_data_context variable to something to be returned to

    you later that will provide you with the data to return in a given

    row.  This could be the same pointer as what my_loop_context is

    set to, or something different.

 

    The put_index_data variable contains a list of snmp variable

    bindings, one for each index in your table.  Set the values of

    each appropriately according to the data matching the first row

    and return the put_index_data variable at the end of the function.

*/

netsnmp_variable_list *

ExampleTable_get_first_data_point(void **my_loop_context, void **my_data_context,

                          netsnmp_variable_list *put_index_data,

                          netsnmp_iterator_info *mydata)

{

    data_read();

    *my_loop_context = ExampleTable_head;

    *my_data_context = ExampleTable_head;

 

return ExampleTable_get_next_data_point(my_loop_context, my_data_context,

                                    put_index_data,  mydata );

}

 

/** functionally the same as ExampleTable_get_first_data_point, but

   my_loop_context has already been set to a previous value and should

   be updated to the next in the list.  For example, if it was a

   linked list, you might want to cast it to your local data type and

   then return my_loop_context->next.  The my_data_context pointer

   should be set to something you need later and the indexes in

   put_index_data updated again. */

netsnmp_variable_list *

ExampleTable_get_next_data_point(void **my_loop_context, void **my_data_context,

                         netsnmp_variable_list *put_index_data,

                         netsnmp_iterator_info *mydata)

{

       struct ExampleTable_entry *entry = (struct ExampleTable_entry *)*my_loop_context;

    netsnmp_variable_list *idx = put_index_data;

 

    if ( entry )

           {

        snmp_set_var_value( idx, (u_char *)&entry->MachineNumber, sizeof(entry->MachineNumber) );

        idx = idx->next_variable;

        *my_data_context = (void *)entry;

        *my_loop_context = (struct ExampleTable_entry *)entry->next;

           }

           else

                  {

        return NULL;

                  }

    return put_index_data;

      

}

 

/** Create a data_context for non-existent rows that SETs are performed on.

 *  return a void * pointer which will be passed to subsequent get_XXX

 *  and set_XXX functions for data retrival and modification during

 *  this SET request.

 *

 *  The indexes are encoded (in order) into the index_data pointer,

 *  and the column object which triggered the row creation is available

 *  via the column parameter, if it would be helpful to use that information.

 */

/*

void *

ExampleTable_create_data_context(netsnmp_variable_list *index_data) {

  

    return entry; /* XXX: you likely want to return a real pointer */

/*

}

*/

/** If the implemented set_* functions don't operate directly on the

   real-live data (which is actually recommended), then this function

   can be used to take a given my_data_context pointer and "commit" it

   to whereever the modified data needs to be put back to.  For

   example, if this was a routing table you could publish the modified

   routes back into the kernel at this point.

 

   new_or_del will be set to 1 if new, or -1 if it should be deleted

   or 0 if it is just a modification of an existing row.

 

   If you free the data yourself, make sure to *my_data_context = NULL */

int

ExampleTable_commit_row(void **my_data_context, int new_or_del)

{

    /** Add any necessary commit code here */

    /*  */

 

    /* return no errors.  And there shouldn't be any!!!  Ever!!!  You

    should have checked the values long before this. */

    return SNMP_ERR_NOERROR;

}

 

 

/* User-defined data access functions (per column) for data in table ExampleTable */

/*

 * NOTE:

 * - these get_ routines MUST return data that will not be freed (ie,

 *   use static variables or persistent data).  It will be copied, if

 *   needed, immediately after the get_ routine has been called.

 * - these SET routines must copy the incoming data and can not take

 *   ownership of the memory passed in by the val pointer.

 */

/** XXX: return a data pointer to the data for the MachineNumber column and set

         ret_len to its proper size in bytes. */

      long *get_MachineNumber(void *data_context, size_t *ret_len) {

          struct ExampleTable_entry *entry=(struct ExampleTable_entry *)data_context;

          long_ret=entry->MachineNumber;

          *ret_len=sizeof(long_ret);

        return &long_ret; /** XXX: replace this with a pointer to a real value */

      }

/** XXX: return a data pointer to the data for the MachineStatus column and set

         ret_len to its proper size in bytes. */

      char *get_MachineStatus(void *data_context, size_t *ret_len) {

        struct ExampleTable_entry *entry=(struct ExampleTable_entry *)data_context;

         

          *ret_len=strlen(entry->MachineStatus);

        return entry->MachineStatus; /** XXX: replace this with a pointer to a real value */

      }

/** XXX: return a data pointer to the data for the CheckTime column and set

         ret_len to its proper size in bytes. */

      u_long *get_CheckTime(void *data_context, size_t *ret_len) {

        struct ExampleTable_entry *entry=(struct ExampleTable_entry *)data_context;

          long_ret=entry->CheckTime;

          *ret_len=sizeof(long_ret);

        return &long_ret; /** XXX: replace this with a pointer to a real value */

      }

/** XXX: return a data pointer to the data for the MonSet column and set

         ret_len to its proper size in bytes. */

      long *get_MonSet(void *data_context, size_t *ret_len) {

        struct ExampleTable_entry *entry=(struct ExampleTable_entry *)data_context;

          long_ret=entry->MonSet;

          *ret_len=sizeof(long_ret);

        return &long_ret; /** XXX: replace this with a pointer to a real value */

      }

/** XXX: Set the value of the MonSet column and return

         SNMP_ERR_NOERROR on success

         SNMP_ERR_XXX     for SNMP deterministic error codes

         SNMP_ERR_GENERR  on generic failures (a last result response). */

      int set_MonSet(void *data_context, long *val, size_t val_len) {

                FILE *fp;

                struct ExampleTable_entry *temp=ExampleTable_head,*entry=data_context;

                 memcpy(&entry->MonSet, val, val_len);

                 fp=fopen("data.txt","w+");

                  if(!fp)

                     {DEBUGMSGTL(("set_MonSet","Open file failure/n"));           

                     return SNMP_ERR_NOACCESS;

                     }

           else

                 while(temp)

                 {

                        fprintf(fp,"/n%d %s %d %d",temp->MachineNumber,

                                             temp->MachineStatus,

                                                                  temp->CheckTime,

                                                                  temp->MonSet);

                        temp=temp->next;

                 }

          fclose(fp);

        return SNMP_ERR_NOERROR;  /** XXX: change if an error occurs */

      }

   

6.3.4       ExampleTable_access.h

/*ExampleTable_access.h*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : mib2c.access_functions.conf,v 1.9 2004/10/14 12:57:33 dts12 Exp $

 */

#ifndef EXAMPLETABLE_ACCESS_H

#define EXAMPLETABLE_ACCESS_H

 

/** User-defined data access functions for data in table ExampleTable */

/** row level accessors */

Netsnmp_First_Data_Point  ExampleTable_get_first_data_point;

Netsnmp_Next_Data_Point   ExampleTable_get_next_data_point;

int ExampleTable_commit_row(void **my_data_context, int new_or_del);

void * ExampleTable_create_data_context(netsnmp_variable_list *index_data, int column);

 

/** column accessors */

      long *get_MachineNumber(void *data_context, size_t *ret_len);

      char *get_MachineStatus(void *data_context, size_t *ret_len);

      u_long *get_CheckTime(void *data_context, size_t *ret_len);

      long *get_MonSet(void *data_context, size_t *ret_len);

      int set_MonSet(void *data_context, long *val, size_t val_len);

 

#endif /* EXAMPLETABLE_ACCESS_H */

 

6.3.5       ExampleTable_checkfns.c

/*ExampleTable_checkfns.c*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : mib2c.check_values.conf,v 1.8 2004/01/12 00:43:45 rstory Exp $

 */

 

/********************************************************************

 *                       NOTE   NOTE   NOTE

 *   This file is auto-generated and SHOULD NOT BE EDITED by hand.

 *   Modify the ExampleTable_checkfns_local.[ch] files insead so that you

 *   can regenerate this one as mib2c improvements are made.

 ********************************************************************/

 

/* standard headers */

#include <net-snmp/net-snmp-config.h>

#include <net-snmp/net-snmp-includes.h>

#include "ExampleTable_checkfns.h"

#include "ExampleTable_checkfns_local.h"

#include "ExampleTable_enums.h"

 

/** Decides if an incoming value for the MonSet mib node is legal.

 *  @param type    The incoming data type.

 *  @param val     The value to be checked.

 *  @param val_len The length of data stored in val (in bytes).

 *  @return 0 if the incoming value is legal, an SNMP error code otherwise.

 */

    int

    check_MonSet(int type, long *val, size_t val_len,

             long *old_val, size_t old_val_len) {

 

    int ret;

 

    /** Check to see that we were called legally */

      if (!val)

        return SNMP_ERR_GENERR;

 

    /** Check the incoming type for correctness */

      if (type != ASN_INTEGER)

        return SNMP_ERR_WRONGTYPE;

 

       ret = SNMP_ERR_NOERROR;

 

 

    /** looks ok, call the local version of the same function. */

      return check_MonSet_local(type, val, val_len, old_val, old_val_len);

    }

6.3.6       ExampleTable_checkfns.h

/*ExampleTable_checkfns.h*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : mib2c.iterate.conf,v 5.6 2003/02/20 00:52:07 hardaker Exp $

 */

 

/***********************************************************************

 *   This file is auto-generated and SHOULD NOT BE EDITED by hand.

 *   Modify the ExampleTable_checkfns_local.[ch] files insead.

 *   (so that you can regenerate this one as mib2c improvements are made)

 ***********************************************************************/

#ifndef EXAMPLETABLE_CHECKFNS_H

#define EXAMPLETABLE_CHECKFNS_H

 

/** make sure we load the functions that you can modify */

config_require(ExampleTable_checkfns_local)

 

/* these functions are designed to check incoming values for

columns in the ExampleTable table for legality with respect to

datatype and value.

 */

 

      int check_MonSet(int type, long *val, size_t val_len, long *old_val, size_t old_val_len);

 

#endif /* EXAMPLETABLE_CHECKFNS_H */

 

6.3.7       ExampleTable_checkfns_local.c

/*ExampleTable_checkfns_local.c*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : mib2c.check_values_local.conf,v 5.2 2004/05/04 23:34:56 hardaker Exp $

 */

 

/* standard headers */

#include <net-snmp/net-snmp-config.h>

#include <net-snmp/net-snmp-includes.h>

#include "ExampleTable_checkfns.h"

#include "ExampleTable_enums.h"

 

/** Decides if an incoming value for the MonSet mib node is legal, from a local implementation specific viewpoint.

 *  @param type    The incoming data type.

 *  @param val     The value to be checked.

 *  @param val_len The length of data stored in val (in bytes).

 *  @return 0 if the incoming value is legal, an SNMP error code otherwise.

 */

    int

    check_MonSet_local(int type, long *val, size_t val_len, long *old_val, size_t old_val_len) {

 

    /** XXX: you may want to check aspects of the new value that

       were not covered by the automatic checks by the parent function. */

 

    /** XXX: you make want to check that the requested change from

        the old value to the new value is legal (ie, the transistion

        from one value to another is legal */

     

    /** if everything looks ok, return SNMP_ERR_NOERROR */

      return SNMP_ERR_NOERROR;

    }

6.3.8       ExampleTable_checkfns_local.h

/*ExampleTable_checkfns_local.h*/

/*

 * Note: this file originally auto-generated by mib2c using

 *        : : mib2c.check_values_local.conf,v 5.2 2004/05/04 23:34:56 hardaker Exp $

 *

 */

#ifndef EXAMPLETABLE_CHECKFNS_H

#define EXAMPLETABLE_CHECKFNS_H

 

/* these functions are designed to check incoming values for

columns in the ExampleTable table for legality with respect to

datatype and value according to local conventions.  You should modify

them as appropriate.  They will be called from parent check_value

functions that are auto-generated using mib2c and the parent functions

should NOT be modified.

 */

 

    int check_MonSet_local(int type, long *val, size_t val_len, long *old_val, size_t old_val_len);

 

#endif /* EXAMPLETABLE_CHECKFNS_H */

 

6.3.9       ExampleTable_columns.h

/* ExampleTable_columns.h*/

/*

 * Note: this file originally auto-generated by mib2c using

 *  : mib2c.column_defines.conf,v 5.1 2002/05/08 05:42:47 hardaker Exp $

 */

#ifndef EXAMPLETABLE_COLUMNS_H

#define EXAMPLETABLE_COLUMNS_H

 

/* column number definitions for table ExampleTable */

       #define COLUMN_MACHINENUMBER        1

       #define COLUMN_MACHINESTATUS          2

       #define COLUMN_CHECKTIME           3

       #define COLUMN_MONSET         4

#endif /* EXAMPLETABLE_COLUMNS_H */

6.3.10   ExampleTable_enums.h

/* ExampleTable_enums.h*/

/*

 * Note: this file originally auto-generated by mib2c using

 *  : mib2c.column_enums.conf,v 5.2 2003/02/22 04:09:25 hardaker Exp $

 */

#ifndef EXAMPLETABLE_ENUMS_H

#define EXAMPLETABLE_ENUMS_H

 

#endif /* EXAMPLETABLE_ENUMS_H */

6.4 自定義mib文件MyMib.txt

MyMIB DEFINITIONS::=BEGIN

       IMPORTS     

              enterprises,OBJECT-TYPE,Integer32,TimeTicks

                     FROM SNMPv2-SMI

       TEXTUAL-CONVENTION,  DisplayString FROM SNMPv2-TC;

       foxmail OBJECT IDENTIFIER::={enterprises 310}

       SecondCounter OBJECT-TYPE

              SYNTAX Integer32

              ACCESS read-write

              STATUS mandatory

                DESCRIPTION "This is a one minute counter from year 1970.1.1.0:0 to now"

              ::={foxmail 1}

 

       WeekTime OBJECT-TYPE

              SYNTAX TimeTicks

              ACCESS read-only

              STATUS mandatory

                DESCRIPTION "Recording taday's time and the day sorts in the week"

              ::={foxmail 2}

 

       ExampleTable        OBJECT-TYPE

              SYNTAX SEQUENCE OF ExampleEntry

              MAX-ACCESS  not-accessible

              STATUS current

              DESCRIPTION

            "A list of interface entries.  The number of entries is

            given by the value of ExampleNumber."

                  ::= { foxmail 3 }

 

       ExampleEntry OBJECT-TYPE

                  SYNTAX      ExampleEntry

                  MAX-ACCESS  not-accessible

                  STATUS      current

                  DESCRIPTION

            "An entry containing management information applicable to a

            particular interface."

                  INDEX   { UserIndex }

                  ::= { ExampleTable 1 }

 

       ExampleEntry ::=

                  SEQUENCE {

                             UserIndex           InterfaceIndex,

                             UserStatus           DisplayString,

                             CheckTime               TimeTicks,

                            MonSet                  Integer32

                            }     

 

  InterfaceIndex ::= TEXTUAL-CONVENTION

    DISPLAY-HINT "d"

    STATUS       current

    DESCRIPTION

            "A unique value, greater than zero, for each interface or

            interface sub-layer in the managed system.  It is

            recommended that values are assigned contiguously starting

            from 1.  The value for each interface sub-layer must remain

            constant at least from one re-initialization of the entity's

            network management system to the next re-initialization."

    SYNTAX       Integer32 (1..2147483647)

 

 

       UserIndex OBJECT-TYPE

                  SYNTAX      InterfaceIndex

              MAX-ACCESS  read-only

              STATUS      current

                  DESCRIPTION

            "A unique value, greater than zero, for each interface.  It

            is recommended that values are assigned contiguously

            starting from 1.  The value for each interface sub-layer

            must remain constant at least from one re-initialization of

            the entity's network management system to the next re-

            initialization."

                ::= { ExampleEntry 1 }

 

       UserStatus OBJECT-TYPE

                  SYNTAX      DisplayString

                  MAX-ACCESS  read-only

                  STATUS      current

                  DESCRIPTION

            "machine status ."

                  ::= { ExampleEntry 2 }

 

       CheckTime OBJECT-TYPE

                  SYNTAX      TimeTicks

                  MAX-ACCESS  read-only

                  STATUS      current

                  DESCRIPTION

            "machine status checking time."

                  ::= { ExampleEntry 3 }

 

       MonSet OBJECT-TYPE

              SYNTAX Integer32

              MAX-ACCESS read-write

              STATUS current

              DESCRIPTION

             "An example to use set function."

              ::={ ExampleEntry 4}

END

發佈了4 篇原創文章 · 獲贊 0 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章