net-snmp扩展有多种方式,在此只介绍两种——动态库扩展,静态库扩展。

在做net-snmp开发之前,首先确定net-snmp相关的软件是否安装。

rpm -qa | grep snmp
net-snmp-5.3.1-19.el5
net-snmp-perl-5.3.1-19.el5
net-snmp-libs-5.3.1-19.el5
net-snmp-utils-5.3.1-19.el5
net-snmp-devel-5.3.1-19.el5

动态库扩展

使用动态库扩展net-snmp有4个步骤,分别是:

1:编写MIB库。

2:根据MIB库生成源代码。可以使用mib2c工具。

3:编译。

4:修改配置文件。

编写MIB库

  假设我们现在需要使用snmp来获取某个服务器上的硬盘使用情况——df命令显示的数据。首先我们需要一个字段来表示HostName,然后我们需要一个表来存放磁盘的信息DiskInfoTable。

 DISK-SNMP-MIB DEFINITIONS ::= BEGIN
IMPORTS
MODULE-IDENTITY, enterprises, OBJECT-TYPE, Integer32,
NOTIFICATION-TYPE FROM SNMPv2-SMI
SnmpAdminString FROM SNMP-FRAMEWORK-MIB
netSnmp FROM NET-SNMP-MIB
RowStatus, StorageType ,DisplayString FROM SNMPv2-TC
InetAddressType, InetAddress FROM INET-ADDRESS-MIB
;
DiskCheck MODULE-IDENTITY
LAST-UPDATED "201602170000Z"
ORGANIZATION "www.cnblogs.com/ngnetboy"
CONTACT-INFO
"postal: none
email: ngnetboy@163.com
"
DESCRIPTION
"check the disk"
REVISION "201602170000Z"
DESCRIPTION "First draft"
::= { enterprises 888888 } root OBJECT IDENTIFIER ::= {DiskCheck 1} HostName OBJECT IDENTIFIER ::= {root 1}
DiskInfoTable OBJECT IDENTIFIER ::= {root 2} HostName OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS current
DESCRIPTION
"PC is name"
::= {root 1}
DiskInfoTable OBJECT-TYPE
SYNTAX SEQUENCE OF DiskInfoEntry
ACCESS not-accessible
STATUS current
DESCRIPTION "The disk's info include size used avail capacity and mounted on"
::= {root 2} diskInfoEntry OBJECT-TYPE
SYNTAX DiskInfoEntry
ACCESS not-accessible
STATUS current
DESCRIPTION "A row describing a given working group"
INDEX { Filesystem }
::= {DiskInfoTable 1} DiskInfoEntry ::= SEQUENCE {
Filesystem DisplayString
size DisplayString
used DisplayString
avail DisplayString
capacity DisplayString
mountedOn DisplayString
} Filesystem OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS current
DESCRIPTION "name of the disks"
::= {diskInfoEntry 1} size OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS current
DESCRIPTION "size of the disks"
::= {diskInfoEntry 2} used OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS current
DESCRIPTION "used of the disks"
::= {diskInfoEntry 3} avail OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS current
DESCRIPTION "avail of the disks"
::= {diskInfoEntry 4} capacity OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS current
DESCRIPTION "capacity of the disks"
::= {diskInfoEntry 5} mountedOn OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS current
DESCRIPTION "mounted_on of the disks"
::= {diskInfoEntry 6}
END

DISK-SNMP-MIB

  使用snmptranslate -Tp -IR DISK-SNMP-MIB::DiskCheck 命令查看mib库

 [root@localhost net-snmp]# snmptranslate -Tp -IR DISK-SNMP-MIB::DiskCheck
+--DiskCheck(888888)
|
+--root(1)
|
+-- -R-- String HostName(1)
| Textual Convention: DisplayString
| Size: 0..255
|
+--DiskInfoTable(2)
|
+--diskInfoEntry(1)
| Index: Filesystem
|
+-- -R-- String Filesystem(1)
| Textual Convention: DisplayString
| Size: 0..255
+-- -R-- String size(2)
| Textual Convention: DisplayString
| Size: 0..255
+-- -R-- String used(3)
| Textual Convention: DisplayString
| Size: 0..255
+-- -R-- String avail(4)
| Textual Convention: DisplayString
| Size: 0..255
+-- -R-- String capacity(5)
| Textual Convention: DisplayString
| Size: 0..255
+-- -R-- String mountedOn(6)
Textual Convention: DisplayString
Size: 0..255

MIB tree

  需要注意的是,我们是在1.3.6.1.4.1下的enterprises节点下扩展,因此需要在IMPORTS中添加enterprises。如果需要其他类型,也需要在IMPORTS中添加,比如DisplayString, Integer32 ...

根据MIB库生成源代码

  一种比较懒的方式就是使用mib2c工具,根据不同的模板生成代码。本文中使用两个模板——mib2c.scalar.conf,mib2c.iterate.conf。前者是专门生成一个简单的变量,后者是生成一个table。

  以上的MIB中HostName是一个简单变量,DiskInfoTable是一个表。因此可以使用以下命令:

mib2c -c mib2c.scalar.conf HostName
mib2c -c mib2c.iterate.conf DiskInfoTable

  以上命令会分别生成一个.c 和 .h 的文件。只需要修改生成的.c .h文件即可。生成简单变量的代码比较简单,只需要修改一个地方即可,下面是一个diff的截图:

  只需要在XXX的地方添加即可。有注释,不再赘述。

  相比而言生成表的代码会复杂一些。需要改动的地方也相当的多。表:顾名思义就是可以存储多个相同结构的结构体数组,这个结构体数组并不是一个顺序的存储方式,而是一个链式的。在刚开始通过DiskInfoTable_createEntry创建链表节点,然后通过遍历链表的方式,通过索引为每一组数据进行赋值。

  首先在DiskInfoTable_get_first_data_point函数的开始,创建链表节点,并为节点赋值。我们定义一个函数为 void read_data(void); 此函数主要用来创建节点,赋值节点。做完这些,主要的功能代码就完成了。接下来就是修复编译的一些bug。由于这些代码都是使用脚本生成的,在很多地方都不规范,我们需要手动改一些东西:

1:在initialize_table_DiskInfoTable函数中,

  table_info->min_column = XXX;
  table_info->max_column = YYY;

  XXX和YYY表示输出的最小/大列。在相对应的.h文件中有定义的宏,选取最小的和最大的赋值即可。

2:修改struct DiskInfoTable_entry{}; 由于我们在mib库中定义了Index——Filesystem,在生成结构体的时候你会发现有两个Filesystem字段,删除一个即可,同样你会发现所有的string类型的字段都变成了char型,再此需要把char型数据改为char数组。

3:在DiskInfoTable_createEntry函数中,会有一个对Index赋值的过程,代码中使用的是 a=b 的形式,由于我们的Filesystem是string类型的,此时需要改为 strncpy/strcpy等字符串赋值的函数。

4:DiskInfoTable_get_first_data_point函数中少了一个赋值,加上即可:

*my_data_context = DiskInfoTable_head;

5:DiskInfoTable_get_next_data_point函数中少了一个返回值,加上即可:

return put_index_data; 

6:DiskInfoTable_handler函数中需要修改的地方有两类,第一类是使用snmp_set_var_typed_value设置节点value的时候,需要把字段改成u_char * 类型。第二类是在每个switch分支后,判断table_entry是否为空,如果为空则调用netsnmp_set_request_error返回出错信息。防止用户在访问snmp时导致snmp无法相应的问题。

以下是修改后的全部代码:

 /*
* Note: this file originally auto-generated by mib2c using
* : mib2c.iterate.conf,v 5.17 2005/05/09 08:13:45 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 "DiskInfoTable.h"
typedef struct _disk_info {
char filesystem[];
char size[];
char used[];
char avail[];
char capacity[];
char mountedOn[];
}disk_info_t; disk_info_t disk_info[MAX_DISK] = {
{"/dev/mapper", "19G", "2.6G", "15G", "15%", "/"},
{"/dev/sda1", "99M", "12M", "83M", "13%", "/boot"},
{"tmpfs", "252M", "", "252M", "0%", "/dev/shm"},
{"/dev/scd1", "2.8G", "2.8G", "", "100%", "/media/"},
}; /** Initializes the DiskInfoTable module */
void
init_DiskInfoTable(void)
{
/*
* here we initialize all the tables we're planning on supporting
*/
initialize_table_DiskInfoTable();
} /** Initialize the DiskInfoTable table by defining its contents and how it's structured */
void
initialize_table_DiskInfoTable(void)
{
static oid DiskInfoTable_oid[] =
{ , , , , , , , , };
size_t DiskInfoTable_oid_len = OID_LENGTH(DiskInfoTable_oid);
netsnmp_handler_registration *reg;
netsnmp_iterator_info *iinfo;
netsnmp_table_registration_info *table_info; reg =
netsnmp_create_handler_registration("DiskInfoTable",
DiskInfoTable_handler,
DiskInfoTable_oid,
DiskInfoTable_oid_len,
HANDLER_CAN_RONLY); table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
netsnmp_table_helper_add_indexes(table_info, ASN_OCTET_STR, /* index: Filesystem */
);
table_info->min_column = COLUMN_FILESYSTEM;
table_info->max_column = COLUMN_MOUNTEDON; iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info);
iinfo->get_first_data_point = DiskInfoTable_get_first_data_point;
iinfo->get_next_data_point = DiskInfoTable_get_next_data_point;
iinfo->table_reginfo = table_info; netsnmp_register_table_iterator(reg, iinfo); /*
* Initialise the contents of the table here
*/
} /*
* Typical data structure for a row entry
*/
struct DiskInfoTable_entry {
/*
* Column values
*/
char Filesystem[];
char size[];
char used[];
char avail[];
char capacity[];
char mountedOn[];
/*
* Illustrate using a simple linked list
*/
int valid;
struct DiskInfoTable_entry *next;
}; struct DiskInfoTable_entry *DiskInfoTable_head; /*
* create a new row in the (unsorted) table
*/
struct DiskInfoTable_entry *
DiskInfoTable_createEntry(char *Filesystem)
{
struct DiskInfoTable_entry *entry; entry = SNMP_MALLOC_TYPEDEF(struct DiskInfoTable_entry);
if (!entry)
return NULL; //entry->Filesystem = Filesystem;
strncpy(entry->Filesystem, Filesystem, strlen(Filesystem));
entry->next = DiskInfoTable_head;
DiskInfoTable_head = entry;
return entry;
}
/*
* remove a row from the table
*/
void
DiskInfoTable_removeEntry(struct DiskInfoTable_entry *entry)
{
struct DiskInfoTable_entry *ptr, *prev; if (!entry)
return; /* Nothing to remove */ for (ptr = DiskInfoTable_head, prev = NULL;
ptr != NULL; prev = ptr, ptr = ptr->next) {
if (ptr == entry)
break;
}
if (!ptr)
return; /* Can't find it */ if (prev == NULL)
DiskInfoTable_head = ptr->next;
else
prev->next = ptr->next; SNMP_FREE(entry); /* XXX - release any other internal resources */
} static void fill_diskinfotable_entry(struct DiskInfoTable_entry *entry) {
int i; for (i = ; i < MAX_DISK; i++) {
if (!strcmp(disk_info[i].filesystem, entry->Filesystem)) {
strncpy(entry->size, disk_info[i].size, strlen(disk_info[i].size));
//strcpy(entry->size, "444G");
strncpy(entry->used, disk_info[i].used, strlen(disk_info[i].used));
strncpy(entry->avail, disk_info[i].avail, strlen(disk_info[i].avail));
strncpy(entry->capacity, disk_info[i].capacity, strlen(disk_info[i].capacity));
strncpy(entry->mountedOn, disk_info[i].mountedOn, strlen(disk_info[i].mountedOn));
}
} } static void read_data(void) {
int i = ;
struct DiskInfoTable_entry *entry = NULL; if(DiskInfoTable_head == NULL){
for (i = ; i < MAX_DISK; i++) {
DiskInfoTable_createEntry(disk_info[i].filesystem);
}
} entry = DiskInfoTable_head; while (entry) {
fill_diskinfotable_entry(entry);
entry = entry->next;
} } /*
* Example iterator hook routines - using 'get_next' to do most of the work
*/
netsnmp_variable_list *
DiskInfoTable_get_first_data_point(void **my_loop_context,
void **my_data_context,
netsnmp_variable_list * put_index_data,
netsnmp_iterator_info *mydata)
{
read_data();
*my_loop_context = DiskInfoTable_head;
*my_data_context = DiskInfoTable_head;
return DiskInfoTable_get_next_data_point(my_loop_context,
my_data_context,
put_index_data, mydata);
} netsnmp_variable_list *
DiskInfoTable_get_next_data_point(void **my_loop_context,
void **my_data_context,
netsnmp_variable_list * put_index_data,
netsnmp_iterator_info *mydata)
{
struct DiskInfoTable_entry *entry =
(struct DiskInfoTable_entry *) *my_loop_context;
netsnmp_variable_list *idx = put_index_data; if (entry) {
snmp_set_var_value(idx, (u_char *)entry->Filesystem,
sizeof(entry->Filesystem));
idx = idx->next_variable;
*my_data_context = (void *) entry;
*my_loop_context = (void *) entry->next;
} else {
return NULL;
}
return put_index_data;
} /** handles requests for the DiskInfoTable table */
int
DiskInfoTable_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;
struct DiskInfoTable_entry *table_entry; switch (reqinfo->mode) {
/*
* Read-support (also covers GetNext requests)
*/
case MODE_GET:
for (request = requests; request; request = request->next) {
table_entry = (struct DiskInfoTable_entry *)
netsnmp_extract_iterator_context(request);
table_info = netsnmp_extract_table_info(request); switch (table_info->colnum) {
case COLUMN_FILESYSTEM:
if (table_entry == NULL) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
(u_char *)table_entry->Filesystem,
sizeof(table_entry->Filesystem));
break;
case COLUMN_SIZE:
if (table_entry == NULL) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
(u_char *)table_entry->size,
sizeof(table_entry->size));
break;
case COLUMN_USED:
if (table_entry == NULL) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
(u_char *)table_entry->used,
sizeof(table_entry->used));
break;
case COLUMN_AVAIL:
if (table_entry == NULL) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
(u_char *)table_entry->avail,
sizeof(table_entry->avail));
break;
case COLUMN_CAPACITY:
if (table_entry == NULL) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
(u_char *)table_entry->capacity,
sizeof(table_entry->capacity));
break;
case COLUMN_MOUNTEDON:
if (table_entry == NULL) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
(u_char *)table_entry->mountedOn,
sizeof(table_entry->mountedOn));
break;
default:
continue;
//netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
}
}
break; }
return SNMP_ERR_NOERROR;
}

Table code

除了这些还需要有一个common.c把所有的代码连接到一起。

 #include "HostName.h"
#include "DiskInfoTable.h"
void init_diskcheck(void) {
init_HostName();
init_DiskInfoTable();
}

注:init_动态库名 和生成的动态库名字有联系,如果动态库名字不为 diskcheck.so net-snmp有可能无法识别。

编译

编译这块主要是利用net-snmp给的命令,直接给出Makefile好了。

 CC = gcc 

 DIR = HostName DiskInfoTable
#DIR = HostName vpath %.c ./
vpath %.c $(DIR) INCLUDE = -I./
INCLUDE += $(foreach x, $(DIR), -I./$(x)) CFLAGS = -fPIC -shared -O3
CFLAGS += $(shell net-snmp-config --cflags) LDFLAGS = $(shell net-snmp-config --libs) SRC = common.c
SRC += $(foreach x, $(DIR), $(x).c)
OBJS = $(SRC:.c=.o) TARGET = diskcheck.so all:$(TARGET) $(TARGET):$(OBJS)
$(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS) $(OBJS):%.o:%.c
$(CC) -c $^ -o $@ $(CFLAGS) $(INCLUDE) .PHONY:clean
clean:
rm *.o $(TARGET)

把编译好的diskcheck.so拷贝到 /usr/lib (32位系统) /usr/lib64 (64位系统)中.

修改配置文件

1:/etc/snmp/snmp.conf 中加上 mibs +MIB文件的名字(不带后缀)

2:/etc/snmp/snmpd.conf 中加上

  dlmod 动态库名 动态库绝对路径 —— dlmod diskcheck /usr/lib/diskcheck.so

  view systemview .1

Ps: 在/etc/snmp/snmpd.conf 中添加

view    systemview    included   .1.3.6.1.4.1

有些系统在访问节点的时候有错误,例如在redhat as5.8 中有问题,而CentOS6.4中没问题【有可能是我的配置问题】。需要改成步骤二中的配置。

【网络编程】——ne-snmp开发实例1的更多相关文章

  1. snmp++开发实例一

    1.官网下载 snmp开发,首先需要机器已经安装了snmp服务,这方面的资料网上比较完备,安装的时候注意每少一个文件,网上都可以下载到,这样可以自己形成一个包,供以后使用.只要最后snmp的服务开启就 ...

  2. 第四模块:网络编程进阶&数据库开发 第1章·网络编程进阶

    01-进程与程序的概念 02-操作系统介绍 03-操作系统发展历史-第一代计算机 04-操作系统发展历史-批处理系统 05-操作系统发展历史-多道技术 06-操作系统发展历史-分时操作系统 07-总结 ...

  3. 第四模块:网络编程进阶&数据库开发 练习

    练习题 基于queue模块实现线程池 import threading from multiprocessing import Queue class A(threading.Thread): def ...

  4. 第四模块:网络编程进阶&数据库开发 口述

    进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 子进程死了之后 ,父进程关闭的时候要清理掉子进程的僵尸进程(收尸),孤儿进程是指父进程先死掉了的,交给init管理. join() 等待子进 ...

  5. Java基础篇Socket网络编程中的应用实例

    说到java网络通讯章节的内容,刚入门的学员可能会感到比较头疼,应为Socket通信中一定会伴随有IO流的操作,当然对IO流比较熟练的哥们会觉得这是比较好玩的一章,因为一切都在他们的掌握之中,这样操作 ...

  6. 第四模块:网络编程进阶&数据库开发 第2章·MySQL数据库开发

    01-MySQL开篇 02-MySQL简单介绍 03-不同平台下安装MySQL 04-Windows平台MySQL密码设置与破解 05-Linux平台MySQL密码设置与破解 06-Mac平台MySQ ...

  7. 第四模块:网络编程进阶&数据库开发 考核实战

     1.什么是进程?什么是线程? 什么是协程? 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 线程:在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 协程是一种用 ...

  8. python之网络编程

    本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...

  9. Socket网络编程详解

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...

随机推荐

  1. JAVA编码 —— 字符串关键字内容替换

    前言 工作中,我们可能遇到字符串内容替换的场景.例如:我们需要将一个字符串凡是 “#” 标注的,分别替换为不同的内容,那我们应该怎么做呢? 分析,一个字符串可能含有多个“#”,每个 “#”又对应不同的 ...

  2. Ubuntu crontab 定时 python 详细

    Ubuntu系统,定时执行python脚本. 目的:每分钟执行一次timer_test.py timer_test.py    路径 /home/li/d/pythonwork/test/timer_ ...

  3. Hbuilder ios证书申请

      最近负责app开发上线,一些心的总结 1.需要先用 苹果操作系统的证书管理  生成 .csr (多次使用) 2.进入开发者帐号 - 证书与配置c&p + App store and AD ...

  4. 洛谷.4114.Qtree1(树链剖分)

    题目链接 模板题都错了这么多次.. //边权赋到点上 树剖模板 //注意LCA.链的顶端不能统计到答案! #include <cstdio> #include <cctype> ...

  5. COGS.1901.[模板][国家集训队2011]数颜色(带修改莫队)

    题目链接 COGS BZOJ2120 洛谷P1903 /* Add和Subd函数中的vis不能直接设为=1或=0 比如 l=1,r=0 -> l=3,r=5 时,[1,5]的vis标记全都是1 ...

  6. JavaScript_几种继承方式(2017-07-04)

    原型链继承 核心: 将父类的实例作为子类的原型 //父类 function SuperType() {   this.property = true; } SuperType.prototype.ge ...

  7. Codeforces 994F Compute Power 二分+DP

    题意:给n个任务 每个任务有两个值$a,b$ 现有许多机器 每台最多可以执行两次任务 若存在第二次任务则满足$a_{second}<a_{first}$ 定义代价$val = \frac { \ ...

  8. Windows远程桌面,连接被拒绝,因为没有授权此用户帐户进行远程登录。

    Windows 服务器远程连接的时候,出现错误:“连接被拒绝,因为没有授权此用户帐户进行远程登录.”,导致无法远程登录服务器,如下图所示: 问题分析 该错误一般是由于 Windows 远程桌面相关权限 ...

  9. android常用工具收集

    1.脱壳工具 https://github.com/DrizzleRisk/drizzleDumper

  10. vim 语法着色完全配置

    原文地址:http://blog.sina.com.cn/s/blog_878940b3010156ku.html 在终端输入:sudo vim /etc/vim/vimrc   打开配置文件.编辑命 ...