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. for循环的灵活性

      for循环把初始化.测试和更新组合在一起,其基本形式如下所示: for(初始化:测试条件:更新表达式) { //循环体 }   关键字for后面的圆括号中3个表达式,分别用两个分号隔开:   第一 ...

  2. 8.10 正睿暑期集训营 Day7

    目录 2018.8.10 正睿暑期集训营 Day7 总结 A 花园(思路) B 归来(Tarjan 拓扑) C 机场(凸函数 点分治) 考试代码 A B C 2018.8.10 正睿暑期集训营 Day ...

  3. 潭州课堂25班:Ph201805201 WEB 之 JS 第四课 (课堂笔记)

    JS 引入方式 在 HTML 中写入 写在 的标签里 <script> </script>推荐 放在 </body> 结束之前 <!DOCTYPE html& ...

  4. C++学习笔记38:事件机制

    事件基本概念 操作系统或应用程序内部发生某件事,程序的某个组件需要响应该事件,并进行特定处理 面向对象架构中,事件响应函数最可能为成员函数 问题:指向类成员函数的指针不能转换为哑型指针void *,也 ...

  5. Docker修改daemon.json后无法启动的问题

    本文的运行环境为Centos 7.3,Docker与Kubernetes的安装方式见kubeadm安装kubernetes V1.11.1 集群 最近在整理Docker和Kubernetes中的日志与 ...

  6. 细说firewalld和iptables

    在RHEL7里有几种防火墙共存:firewalld.iptables.ebtables,默认是使用firewalld来管理netfilter子系统,不过底层调用的命令仍然是iptables等. fir ...

  7. ln: creating symbolic link XXXXXX : Operation not supported

    ln: creating symbolic link XXXXXX : Operation not supported 转自:https://blog.csdn.net/z444_579/articl ...

  8. Uploadify导致Chrome频繁崩溃Crash

    上传功能是工作中经常会遇到的问题,应该作为开发标配的技能每个人都会. 我选用的是Uploadify 3.1.2进行上传,使用方法参考之前的一篇文章, 今天记录下一个我遇到的很神奇的bug chrome ...

  9. Adobe Photoshop for Mac(图像处理软件)破解版安装

    1.软件简介    Adobe Photoshop(简称 "PS")是 macOS 系统上一款由 Adobe Systems 开发和发行的图像处理软件.Photoshop 主要处理 ...

  10. OpenCV 学习笔记 06 SIFT使用中出现版权问题error: (-213:The function/feature is not implemented)

    1 错误原因 1.1 报错全部信息: cv2.error: OpenCV(4.0.1) D:\Build\OpenCV\opencv_contrib-4.0.1\modules\xfeatures2d ...