【网络编程】——ne-snmp开发实例1
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的更多相关文章
- snmp++开发实例一
1.官网下载 snmp开发,首先需要机器已经安装了snmp服务,这方面的资料网上比较完备,安装的时候注意每少一个文件,网上都可以下载到,这样可以自己形成一个包,供以后使用.只要最后snmp的服务开启就 ...
- 第四模块:网络编程进阶&数据库开发 第1章·网络编程进阶
01-进程与程序的概念 02-操作系统介绍 03-操作系统发展历史-第一代计算机 04-操作系统发展历史-批处理系统 05-操作系统发展历史-多道技术 06-操作系统发展历史-分时操作系统 07-总结 ...
- 第四模块:网络编程进阶&数据库开发 练习
练习题 基于queue模块实现线程池 import threading from multiprocessing import Queue class A(threading.Thread): def ...
- 第四模块:网络编程进阶&数据库开发 口述
进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 子进程死了之后 ,父进程关闭的时候要清理掉子进程的僵尸进程(收尸),孤儿进程是指父进程先死掉了的,交给init管理. join() 等待子进 ...
- Java基础篇Socket网络编程中的应用实例
说到java网络通讯章节的内容,刚入门的学员可能会感到比较头疼,应为Socket通信中一定会伴随有IO流的操作,当然对IO流比较熟练的哥们会觉得这是比较好玩的一章,因为一切都在他们的掌握之中,这样操作 ...
- 第四模块:网络编程进阶&数据库开发 第2章·MySQL数据库开发
01-MySQL开篇 02-MySQL简单介绍 03-不同平台下安装MySQL 04-Windows平台MySQL密码设置与破解 05-Linux平台MySQL密码设置与破解 06-Mac平台MySQ ...
- 第四模块:网络编程进阶&数据库开发 考核实战
1.什么是进程?什么是线程? 什么是协程? 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 线程:在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 协程是一种用 ...
- python之网络编程
本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...
- Socket网络编程详解
一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...
随机推荐
- TFT LCD显示原理详解
<什么是液晶> 我们一般认为物体有三态:固态.液态.气态,其实这只是针对水而言,有一些有机化和物 还有介于固态和液态中间的状态 就是液晶态,如下图(一): ...
- cocosCreator 新版本的动作函数API的应用
利用触摸位置判断,点击的是屏幕的左侧还是右侧,控制主角左右移动: 见代码: InputControl:function () { var self=this; //cc.systemEvent sel ...
- SourceTree + Beynod Compare解决Git冲突的方法
https://www.cnblogs.com/yufeng218/p/6523422.html
- GitHub 的公开演讲文化
2013年在某个地方为GitHub 240名员工中的三分之一或一半员工做演讲. 鼓励你的员工在大会上做演讲通常被认为是一件好事.另外对于GitHub,它还是一种好的广告:和我们花钱砸在banner广告 ...
- look-into-oracle-redo
https://fritshoogland.wordpress.com/2018/02/05/a-look-into-oracle-redo-part-2-the-discovery-of-the-k ...
- GeoHash原理和可视化显示
最近在做附近定位功能的产品,geohash是一个非常不错的实现方式.查询资料,发现阿里的这篇文章讲解的很好.但文中并没有给出geohash显示的工具.无奈,也没有查到类似的.只好自己简单显示一下,方便 ...
- DHCP服务原理与搭建(Linux系统+路由器,二选一方案)
大家都知道上网的最基本前提是要在终端上设置IP.子网掩码.网关.DNS等地址信息,在家里或者在办公室很多时候打开电脑后发现就可以上网,并没有手动设置IP.掩码.DNS地址也能上网,这是什么原因呢?其实 ...
- vscode 中使用php-cs-fixer和PHP Formatter 插件规范化PHP代码
什么是PHP-CS-Fixer? 它是php-fig组织定义的PHP代码规范,良好的代码规范可以提高代码可读性,团队沟通维护成本 使用它可以按照指定的规范格式化您的PHP代码,此工具不仅可 ...
- java多线程有哪些实际的应用场景?
多线程使用的主要目的在于: 1.吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的.简单的说,可能就是一个请求一个线程.或多个请求一个线程.如果是单线程,那同时只能处理一个用户的请求. ...
- 启动exe
public void OpenTabTip(){ bool bt = true; Process[] processes = Process.GetProcesses(); for ...