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. ubuntu安装mysql 时未提示输入密码

    我在Ubuntu16.04版本中使用终端安装MySQL5.7时,按照度娘的教程,搜索如何安装,大多是如下代码: sudo apt-get install mysql-server sudo apt-g ...

  2. [软件研究]对wdcp v3的一次小研究#1

    0x00 前言 好久没有更新了,已经长草无疑. 之前团队要搞个测验的系统,用来安全培训考核,团队内又没啥人搞开发的,自己又想学一下vue,就用vue+ci 撸了一个. 搞了一个星期基本搞完(开发能力真 ...

  3. Android应用开发-数据存储和界面展现(一)

    常见布局 相对布局(RelativeLayout) 相对布局下控件默认位置都是左上角(左对齐.顶部对齐父元素),控件之间可以重叠 可以相对于父元素上下左右对齐,相对于父元素水平居中.竖直居中.水平竖直 ...

  4. Fibonacci Modified

    题目来源:Fibonacci Modified We define a modified Fibonacci sequence using the following definition: Give ...

  5. Linux 输入子系统驱动程序范例

    <按键驱动程序> #include <stdio.h> #include <fcntl.h> #include <linux/input.h> #inc ...

  6. Angularjs 根据数据结构创建动态菜单无限嵌套循环--指令版

    目标:根据数据生成动态菜单,希望可以根据判断是否有子集无限循环下去. 菜单希望的样子是这样的: 菜单数据是这样的: $scope.expanders = [{ title: 'title1', lin ...

  7. 多臂机测试, AB测试

    bandit  强盗,土匪:恶棍:敲诈者 ['bændɪt] 多臂机 multi-armed bandit MAB  简写. one-arm bandit   tiger ji 是一种自动AB测试的方 ...

  8. 多线程里面this.getName()和currentThread.getName()有什么区别

    public class hello extends Thread { public hello(){ System.out.println("Thread.currentThread(). ...

  9. javascript加密PHP解密---jsencrypt

    今天偶然发现jsencrypt这玩意,之前做"直播室聊天"时 数据传输明文问题没解决; 一直苦苦寻找技术解决方案今天勉强找了个: 原理:javascript加密PHP解密: 完全依 ...

  10. 来自极客头条的 35 个 Java 代码性能优化总结

    前言 代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用, ...