1. 获取net-snmp开发包
可以用开源上http://www.net-snmp.org/download.html获取自己所需要的各种版本。
2. 分析需求,进行合理配置与编译安装
比如要不要将其编译成动态库,还是静态库;要不要某些库;等等。可以通过在其目录下执行./configure --help即可知道她有配置的选项。比如“—host=xxx”配置她将在什么样的环境下运行,“—build=xxx”配置她将在什么环境下建立的(即本机环境),“—with-endianness=xxx”配置所移殖的CPU是工作在大头模式还是小头模式,“—target=xxx”目标工具(即xxx-gcc, xxx-ar等等工具的xxx),“—disable-debug”不使能调试等等。下面是我自己配置的选项:
#tar xzvf net-snmp-5.3.4.tar.gz
#cd net-snmp-5.2
#./configure --with-perl-modules --prefix=/usr/local/net-snmp --enable-mfd-rewrites --with-default-snmp-version="2" --with-sys-contact="linguang,[email protected]" --with-sys-location="China" --with-logfile="/var/log/snmpd.log" --with-persistent-directory="/var/net-snmp"
(以上几个重要的字眼用红色画出,这些在以后会很重要,否则你又得重新编译了,切记!!其中含义如下:
Perl-modules:这个是mib2c依赖的关键模块。
prefix:net-snmp将要安装的路径
enable-mfd-rewrites:允许用新的 MFD重写可用的 mid模块
with-default-snmp-version:默认的 SNMP版本
with-sys-contact:可以配置该设备的联系人
with-sys-location:该设备的位置
with-logfile:日志文件路径
with-persistent-directory:不变数据存储目录 )
#make;make install
(这里有一个小经验,这时我先声明一点,我当时做这些工作的时候是采用linux内核2.6.34,net-snmp是用5.0版本的。因为我当时编译的时候没那么快成功的,出现很多错误,最重要的是编译完成后执行mib2c这个命令(在/usr/local/net-snmp/bin/下执行,因为这是我还没有添加环境变量,所以只能到源目录下执行)时提示出没有安装perl模块,但是我们确实安装了啊???这时我又重新回到源文件,进入安装包的perl目录下,执行perl Makefile.PL -NET-SNMP-CONFIG="sh ../net-csnmp-config" -NET-SNMP-IN-SOURCE=true,后再执行make;make test,这时就会出错,错误是找不到default_store.so,先不管,然后执行make install,没问题后再重新回到上次说的目录执行mib2c,还提示没有安装!!!!问题应该出现在这里的版本不大兼容上,后来我采用5.3版本,在前面configure上就没出现这个问题,而且执行mib2c时也没有在出错!!!这只是我的经验之谈!!!还有一点是如果不懂在安装包理由一个FAQ文件,里面的东西也挺有用的。。)
3、将该服务自启动以及添加所需的环境变量
#cd /etc/
#vim rc.local
在文件的末尾添加上一行:/usr/local/net-snmp/sbin/snmpd -c /usr/local/net-snmp/share/snmp/snmpd.conf &
#vim profile
找到有export关键词单独的一行前添加下面一行:
PATH=/usr/local/net-snmp/bin:/usr/local/net-snmp/sbin:$PATH
这样在哪就都可以执行相关命令了。
4、修改snmpd.conf文件
本来在/usr/local/net-snmp/share/snmp/目录下没有这个文件的,所以我们将安装包的EXAMPLE.conf复制到这个目录下。
修改该文件,具体修改哪些,读者可以参考别的资料,这样类似的很多我就不废话说这些不是很重要的了。
5、创建自定义的MIBs
方法有好多。可以仿照agent/mibgroup目录下的.C文件来编写,也可以通过相应的工具来生成现稍微修改。比如net-snmp软件包的mib2c工具。如果是通过手写的MIB,这样花费的时间比工具生成的多得多。下面介绍mib2c生mib的简单方法。
一:将自己写好的mib文件(通常以.txt结尾的文件)复制到安装net-snmp的路径下的snmp/mibs目录下。
二:在命令行执行下以命令
#export MIBS=all
就会将安装net-snmp的路径下的snmp/mibs目录的所有mibs文件包含进来了。
三:在命令行执行以下命令,就会在当前目录下生成你所需的MIB的.C和.H文件了。(我们这里举个例子,比如你的MIB文件叫LOOGSON-MIB,然后文件里有一个叶子,是依靠在enterprise下的叫loogson,所以你应该这样编译)
#mib2c mib2c.old-api.conf LOOGSON-MIB::loogson
至于大家想生成什么风格的MIB的.C文件,可读取安装net-snmp的路径下的snmp目录下的*.conf文件。只要大家按照前面的说的去做并且没有提示错误的话,这一步肯定能成功,并且会在你执行的目录下产生四个文件,其中就有.c/.h文件。接下去很重要的就是完善自己的mib文件。
以下的代码是我自己编写的实现从agent获得对象的一条命令执行的结果(如执行date命令,返回的结果应该是打印了linux系统的时间)。并返回打印出来,这个程序还不是很完善,我会继续更新的(更新于2月21日14点27分)。代码见附录。
6、 添加自己所需的MIBs
在配置net-snmp时,软件包就加了一些默认的mibs进来了,她们的.C和.H文件都存放在agent/mibgroup目录下。那么,怎么样来添加大家所需的自定义的MIB文件呢?
一:将自己的MIB文件的.C和.H文件copy到agent/mibgroup/YourDefinedPath目录下。因为所有的MIB文件都放在此目录下,文件生成libnetsnmpmib.a库。
二:将自己所定义的MIB的初始化函数注册到agent/mibgroup/mib_module_inits.h文件中来,因为snmpd启动时会先初始化这样注册函数,具体可以从代码agent/snmpd.c文件的main函数查下去。一般用mib2c生成的mib的注册函数形式都是init_****(void),函数都要调用REGISTER_MIB()函数来注册大家相关的东东。说到此处,如果大家不需要系统那些自带的MIB,可以在此文件把相关的注册函数屏闭,可减少可执行文件的大小。同时大家要将自义的MIB的.H头文件增加到agent/mibgroup/mib_module_includes.h文件里,这只要是系统编译链接的问题。比如你的MIB的.C文件为agent/mibgroup/YourDefinedPath/yourMIB.c,.H文件为agent/mibgroup/YourDefinedPath/yourMIB.h,则在agent/mibgroup/mib_module_includes.h文件里添加一行“#include "mibgroup/ YourDefinedPath/yourMIB.h””即可。
三:MIB文件是加进来了,但还得改下有关Makefile,让添加进来的MIB也编译进去libnetsnmpmib.a。这要改两个文件,一个是agent/Makefile文件,另一个是agent/mibgroup/Makefile文件。先打开agent/Makefile文件,找到“LMIBOBJS =”这一行,仿照已有东东,在后面加上自己的东东“ mibgroup/YourDefinedPath/yourMIB.lo”,注意不要漏了空格。还要找到“MIBOBJS =”这一行,在其后加上自己的东东“mibgroup/YourDefinedPath/yourMIB.o”。最后打开agent/mibgroup/Makefile,找到“OBJS =”这一行的最后面添加你自己的东东“ YourDefinedPath/yourMIB.o”,在“LOBJS =”这一行的最后面添加你的东东“ YourDefinedPath/yourMIB.lo”,并在“SRCS =”此行的最后面添加你的东东“ YourDefinedPath/yourMIB.c”。这样,编译链接的东东也搞完了。
四:好多人都以为做了以上三步并认为代码没错就一定可以编译得过去了,并不然。因为net-snmp的开源软件的Makefile做得好严紧,每次编译时,Makefile都得查下你在命令行配置生成的文件有没有改变,如有改变,则会恢复回你命令行配置的makefile状态,如果这样,大家在第三步做工作不是白搭了?呵呵,那么大家就要找到这个文件,将大家修改的Makefile不让其恢复到配置时的状态。这个文件是config.status,只要将此文件的内容全部清空就没问题了。
这样,执行#make;make install;reboot就可以将大家所加进的MIB也编译进来了。(记住了得重新启动才能生效的!!)
附录:我自己的MIB库文件以及完善的MIB.c文件
首先是:LINUX-VER-MIB.txt
1 LINUX-VER-MIB DEFINITIONS ::= BEGIN
2
3
4 IMPORTS
5 OBJECT-TYPE, NOTIFICATION-TYPE, MODULE-IDENTITY,
6 Integer32, Opaque, enterprises, Counter32
7 FROM SNMPv2-SMI
8
9 TEXTUAL-CONVENTION, DisplayString, TruthValue
10 FROM SNMPv2-TC;
11
12 lxver OBJECT IDENTIFIER ::= { enterprises 9999 }
13
14
15 CommandType OBJECT-TYPE
16 SYNTAX DisplayString
17 MAX-ACCESS read-only
18 STATUS current
19 DESCRIPTION
20 "The command line to be executed."
21 ::= {lxver 1 }
22
23
24 OutputType OBJECT-TYPE
25 SYNTAX DisplayString
26 MAX-ACCESS read-only
27 STATUS current
28 DESCRIPTION
29 "The first line of output of the executed command."
30 ::={ lxver 2 }
31
32 END
接着是我自己完善的mib文件,仅供参考,代码我就懒得注释了,全靠自己的理解以及基础了,不懂的话可以留言:
#include
#include
#include
#include "lxver.h"
#include
#include
#include
#include
#include
#include
#define PATH_MAX 1024
#define MAX_STR 1024
char command[1024]="date";
oid lxver_variables_oid[] = { 1, 3, 6, 1, 4, 1, 9999};
struct variable4 lxver_variables[] = {
#define COMMANDTYPE 1
{COMMANDTYPE, ASN_OCTET_STR, RONLY, var_lxver, 1, {1}},
#define OUTPUTTYPE 2
{OUTPUTTYPE,ASN_OCTET_STR, RONLY, var_lxver, 1, {2}},
};
void
init_lxver(void)
{
DEBUGMSGTL(("lxver", "Initializing\n"));
REGISTER_MIB("lxver", lxver_variables, variable4,lxver_variables_oid);
}
unsigned char *
var_lxver(struct variable *vp,
oid * name,
size_t *length,
int exact, size_t *var_len, WriteMethod ** write_method)
{
structtimeval tv;
intret;
intpipe_fd[2];
intpid;
intr_num;
FILE*fp;
fd_setreadfds;
charbuf_r[MAX_STR];
charpath[PATH_MAX]={0};
if(header_generic(vp, name, length, exact, var_len,write_method)
== MATCH_FAILED)
return NULL;
switch(vp->magic) {
caseCOMMANDTYPE:
DEBUGMSGTL(("lxver","string content is %s,sizeofis %d\n",command,sizeof(command)));
// *write_method = write_CommandType;
*var_len=strlen(command);
return (u_char *)(command);
caseOUTPUTTYPE:
// *write_method = write_OutputType;
memset(buf_r,0,sizeof(buf_r));
if(pipe(pipe_fd)<0)
{
perror("creat pipe failed\n");
}
if((pid=fork())==0)
{
fp=popen(command,"r");
if(fp==NULL)
{
perror("can not get theresult\n");
exit(1);
}
while(fgets(path,PATH_MAX,fp)!=NULL)
{
if(write(pipe_fd[1],path,strlen(path))!=-1)
{ printf("childprocess write %d bytes to the pipe,the string is%s\n",strlen(path),path);
}
}
int status=pclose(fp);
if(status==-1)
{
perror("can not close the fp\n");
exit(1);
}
printf("childpid has not exitedyet!\n");
exit(0);//return 0;
}
else
if(pid>0)
{
printf("\n");
FD_ZERO(&readfds);
FD_SET(pipe_fd[0],&readfds);
tv.tv_sec=1;
tv.tv_usec=0;
ret=select(pipe_fd[0]+1,&readfds,NULL,NULL,&tv);
if(ret==-1)
{
if(EAGAIN==errno);
else
printf("selecterror,error %d\n",errno);
}
else if(0==ret)
{
printf("timeout\n");
}
if(!FD_ISSET(pipe_fd[0],&readfds))
{
printf("fdnot ready\n");
}
r_num=read(pipe_fd[0],buf_r,MAX_STR);
if(r_num>0)
{
printf("%dbytes read from the pipe,the string is%s\n",r_num,buf_r);
}
else
{
printf("readnothing\n");
exit(1);
}
waitpid(pid,NULL,0);
close(pipe_fd[0]);
close(pipe_fd[1]);
*var_len=strlen(buf_r);
return (u_char *)(buf_r);
}
default:
ERROR_MSG("");
}
return NULL;
}
int
write_CommandType(int action,
u_char * var_val,
u_char var_val_type,