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。
1 DISK-SNMP-MIB DEFINITIONS ::= BEGIN 2 IMPORTS 3 MODULE-IDENTITY, enterprises, OBJECT-TYPE, Integer32, 4 NOTIFICATION-TYPE FROM SNMPv2-SMI 5 SnmpAdminString FROM SNMP-FRAMEWORK-MIB 6 netSnmp FROM NET-SNMP-MIB 7 RowStatus, StorageType ,DisplayString FROM SNMPv2-TC 8 InetAddressType, InetAddress FROM INET-ADDRESS-MIB 9 ; 10 DiskCheck MODULE-IDENTITY 11 LAST-UPDATED "201602170000Z" 12 ORGANIZATION "www.cnblogs.com/ngnetboy" 13 CONTACT-INFO 14 "postal: none 15 email: [email protected].com 16 " 17 DESCRIPTION 18 "check the disk" 19 REVISION "201602170000Z" 20 DESCRIPTION "First draft" 21 ::= { enterprises 888888 } 22 23 root OBJECT IDENTIFIER ::= {DiskCheck 1} 24 25 HostName OBJECT IDENTIFIER ::= {root 1} 26 DiskInfoTable OBJECT IDENTIFIER ::= {root 2} 27 28 HostName OBJECT-TYPE 29 SYNTAX DisplayString 30 ACCESS read-only 31 STATUS current 32 DESCRIPTION 33 "PC is name" 34 ::= {root 1} 35 DiskInfoTable OBJECT-TYPE 36 SYNTAX SEQUENCE OF DiskInfoEntry 37 ACCESS not-accessible 38 STATUS current 39 DESCRIPTION "The disk‘s info include size used avail capacity and mounted on" 40 ::= {root 2} 41 42 diskInfoEntry OBJECT-TYPE 43 SYNTAX DiskInfoEntry 44 ACCESS not-accessible 45 STATUS current 46 DESCRIPTION "A row describing a given working group" 47 INDEX { Filesystem } 48 ::= {DiskInfoTable 1} 49 50 DiskInfoEntry ::= SEQUENCE { 51 Filesystem DisplayString 52 size DisplayString 53 used DisplayString 54 avail DisplayString 55 capacity DisplayString 56 mountedOn DisplayString 57 } 58 59 Filesystem OBJECT-TYPE 60 SYNTAX DisplayString 61 ACCESS read-only 62 STATUS current 63 DESCRIPTION "name of the disks" 64 ::= {diskInfoEntry 1} 65 66 size OBJECT-TYPE 67 SYNTAX DisplayString 68 ACCESS read-only 69 STATUS current 70 DESCRIPTION "size of the disks" 71 ::= {diskInfoEntry 2} 72 73 used OBJECT-TYPE 74 SYNTAX DisplayString 75 ACCESS read-only 76 STATUS current 77 DESCRIPTION "used of the disks" 78 ::= {diskInfoEntry 3} 79 80 avail OBJECT-TYPE 81 SYNTAX DisplayString 82 ACCESS read-only 83 STATUS current 84 DESCRIPTION "avail of the disks" 85 ::= {diskInfoEntry 4} 86 87 capacity OBJECT-TYPE 88 SYNTAX DisplayString 89 ACCESS read-only 90 STATUS current 91 DESCRIPTION "capacity of the disks" 92 ::= {diskInfoEntry 5} 93 94 mountedOn OBJECT-TYPE 95 SYNTAX DisplayString 96 ACCESS read-only 97 STATUS current 98 DESCRIPTION "mounted_on of the disks" 99 ::= {diskInfoEntry 6} 100 END
DISK-SNMP-MIB
使用snmptranslate -Tp -IR DISK-SNMP-MIB::DiskCheck 命令查看mib库
1 [[email protected] net-snmp]# snmptranslate -Tp -IR DISK-SNMP-MIB::DiskCheck 2 +--DiskCheck(888888) 3 | 4 +--root(1) 5 | 6 +-- -R-- String HostName(1) 7 | Textual Convention: DisplayString 8 | Size: 0..255 9 | 10 +--DiskInfoTable(2) 11 | 12 +--diskInfoEntry(1) 13 | Index: Filesystem 14 | 15 +-- -R-- String Filesystem(1) 16 | Textual Convention: DisplayString 17 | Size: 0..255 18 +-- -R-- String size(2) 19 | Textual Convention: DisplayString 20 | Size: 0..255 21 +-- -R-- String used(3) 22 | Textual Convention: DisplayString 23 | Size: 0..255 24 +-- -R-- String avail(4) 25 | Textual Convention: DisplayString 26 | Size: 0..255 27 +-- -R-- String capacity(5) 28 | Textual Convention: DisplayString 29 | Size: 0..255 30 +-- -R-- String mountedOn(6) 31 Textual Convention: DisplayString 32 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无法相应的问题。
以下是修改后的全部代码:
1 /* 2 * Note: this file originally auto-generated by mib2c using 3 * : mib2c.iterate.conf,v 5.17 2005/05/09 08:13:45 dts12 Exp $ 4 */ 5 6 #include <net-snmp/net-snmp-config.h> 7 #include <net-snmp/net-snmp-includes.h> 8 #include <net-snmp/agent/net-snmp-agent-includes.h> 9 #include "DiskInfoTable.h" 10 typedef struct _disk_info { 11 char filesystem[64]; 12 char size[64]; 13 char used[64]; 14 char avail[64]; 15 char capacity[64]; 16 char mountedOn[64]; 17 }disk_info_t; 18 19 disk_info_t disk_info[MAX_DISK] = { 20 {"/dev/mapper", "19G", "2.6G", "15G", "15%", "/"}, 21 {"/dev/sda1", "99M", "12M", "83M", "13%", "/boot"}, 22 {"tmpfs", "252M", "0", "252M", "0%", "/dev/shm"}, 23 {"/dev/scd1", "2.8G", "2.8G", "0", "100%", "/media/"}, 24 }; 25 26 /** Initializes the DiskInfoTable module */ 27 void 28 init_DiskInfoTable(void) 29 { 30 /* 31 * here we initialize all the tables we‘re planning on supporting 32 */ 33 initialize_table_DiskInfoTable(); 34 } 35 36 /** Initialize the DiskInfoTable table by defining its contents and how it‘s structured */ 37 void 38 initialize_table_DiskInfoTable(void) 39 { 40 static oid DiskInfoTable_oid[] = 41 { 1, 3, 6, 1, 4, 1, 888888, 1, 2 }; 42 size_t DiskInfoTable_oid_len = OID_LENGTH(DiskInfoTable_oid); 43 netsnmp_handler_registration *reg; 44 netsnmp_iterator_info *iinfo; 45 netsnmp_table_registration_info *table_info; 46 47 reg = 48 netsnmp_create_handler_registration("DiskInfoTable", 49 DiskInfoTable_handler, 50 DiskInfoTable_oid, 51 DiskInfoTable_oid_len, 52 HANDLER_CAN_RONLY); 53 54 table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); 55 netsnmp_table_helper_add_indexes(table_info, ASN_OCTET_STR, /* index: Filesystem */ 56 0); 57 table_info->min_column = COLUMN_FILESYSTEM; 58 table_info->max_column = COLUMN_MOUNTEDON; 59 60 iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info); 61 iinfo->get_first_data_point = DiskInfoTable_get_first_data_point; 62 iinfo->get_next_data_point = DiskInfoTable_get_next_data_point; 63 iinfo->table_reginfo = table_info; 64 65 netsnmp_register_table_iterator(reg, iinfo); 66 67 /* 68 * Initialise the contents of the table here 69 */ 70 } 71 72 /* 73 * Typical data structure for a row entry 74 */ 75 struct DiskInfoTable_entry { 76 /* 77 * Column values 78 */ 79 char Filesystem[16]; 80 char size[8]; 81 char used[8]; 82 char avail[8]; 83 char capacity[8]; 84 char mountedOn[16]; 85 /* 86 * Illustrate using a simple linked list 87 */ 88 int valid; 89 struct DiskInfoTable_entry *next; 90 }; 91 92 struct DiskInfoTable_entry *DiskInfoTable_head; 93 94 /* 95 * create a new row in the (unsorted) table 96 */ 97 struct DiskInfoTable_entry * 98 DiskInfoTable_createEntry(char *Filesystem) 99 { 100 struct DiskInfoTable_entry *entry; 101 102 entry = SNMP_MALLOC_TYPEDEF(struct DiskInfoTable_entry); 103 if (!entry) 104 return NULL; 105 106 //entry->Filesystem = Filesystem; 107 strncpy(entry->Filesystem, Filesystem, strlen(Filesystem)); 108 entry->next = DiskInfoTable_head; 109 DiskInfoTable_head = entry; 110 return entry; 111 } 112 /* 113 * remove a row from the table 114 */ 115 void 116 DiskInfoTable_removeEntry(struct DiskInfoTable_entry *entry) 117 { 118 struct DiskInfoTable_entry *ptr, *prev; 119 120 if (!entry) 121 return; /* Nothing to remove */ 122 123 for (ptr = DiskInfoTable_head, prev = NULL; 124 ptr != NULL; prev = ptr, ptr = ptr->next) { 125 if (ptr == entry) 126 break; 127 } 128 if (!ptr) 129 return; /* Can‘t find it */ 130 131 if (prev == NULL) 132 DiskInfoTable_head = ptr->next; 133 else 134 prev->next = ptr->next; 135 136 SNMP_FREE(entry); /* XXX - release any other internal resources */ 137 } 138 139 static void fill_diskinfotable_entry(struct DiskInfoTable_entry *entry) { 140 int i; 141 142 for (i = 0; i < MAX_DISK; i++) { 143 if (!strcmp(disk_info[i].filesystem, entry->Filesystem)) { 144 strncpy(entry->size, disk_info[i].size, strlen(disk_info[i].size)); 145 //strcpy(entry->size, "444G"); 146 strncpy(entry->used, disk_info[i].used, strlen(disk_info[i].used)); 147 strncpy(entry->avail, disk_info[i].avail, strlen(disk_info[i].avail)); 148 strncpy(entry->capacity, disk_info[i].capacity, strlen(disk_info[i].capacity)); 149 strncpy(entry->mountedOn, disk_info[i].mountedOn, strlen(disk_info[i].mountedOn)); 150 } 151 } 152 153 } 154 155 static void read_data(void) { 156 int i = 0; 157 struct DiskInfoTable_entry *entry = NULL; 158 159 if(DiskInfoTable_head == NULL){ 160 for (i = 0; i < MAX_DISK; i++) { 161 DiskInfoTable_createEntry(disk_info[i].filesystem); 162 } 163 } 164 165 entry = DiskInfoTable_head; 166 167 while (entry) { 168 fill_diskinfotable_entry(entry); 169 entry = entry->next; 170 } 171 172 173 } 174 175 /* 176 * Example iterator hook routines - using ‘get_next‘ to do most of the work 177 */ 178 netsnmp_variable_list * 179 DiskInfoTable_get_first_data_point(void **my_loop_context, 180 void **my_data_context, 181 netsnmp_variable_list * put_index_data, 182 netsnmp_iterator_info *mydata) 183 { 184 read_data(); 185 *my_loop_context = DiskInfoTable_head; 186 *my_data_context = DiskInfoTable_head; 187 return DiskInfoTable_get_next_data_point(my_loop_context, 188 my_data_context, 189 put_index_data, mydata); 190 } 191 192 netsnmp_variable_list * 193 DiskInfoTable_get_next_data_point(void **my_loop_context, 194 void **my_data_context, 195 netsnmp_variable_list * put_index_data, 196 netsnmp_iterator_info *mydata) 197 { 198 struct DiskInfoTable_entry *entry = 199 (struct DiskInfoTable_entry *) *my_loop_context; 200 netsnmp_variable_list *idx = put_index_data; 201 202 if (entry) { 203 snmp_set_var_value(idx, (u_char *)entry->Filesystem, 204 sizeof(entry->Filesystem)); 205 idx = idx->next_variable; 206 *my_data_context = (void *) entry; 207 *my_loop_context = (void *) entry->next; 208 } else { 209 return NULL; 210 } 211 return put_index_data; 212 } 213 214 215 /** handles requests for the DiskInfoTable table */ 216 int 217 DiskInfoTable_handler(netsnmp_mib_handler *handler, 218 netsnmp_handler_registration *reginfo, 219 netsnmp_agent_request_info *reqinfo, 220 netsnmp_request_info *requests) 221 { 222 223 netsnmp_request_info *request; 224 netsnmp_table_request_info *table_info; 225 struct DiskInfoTable_entry *table_entry; 226 227 switch (reqinfo->mode) { 228 /* 229 * Read-support (also covers GetNext requests) 230 */ 231 case MODE_GET: 232 for (request = requests; request; request = request->next) { 233 table_entry = (struct DiskInfoTable_entry *) 234 netsnmp_extract_iterator_context(request); 235 table_info = netsnmp_extract_table_info(request); 236 237 switch (table_info->colnum) { 238 case COLUMN_FILESYSTEM: 239 if (table_entry == NULL) { 240 netsnmp_set_request_error(reqinfo, request, 241 SNMP_NOSUCHINSTANCE); 242 continue; 243 } 244 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 245 (u_char *)table_entry->Filesystem, 246 sizeof(table_entry->Filesystem)); 247 break; 248 case COLUMN_SIZE: 249 if (table_entry == NULL) { 250 netsnmp_set_request_error(reqinfo, request, 251 SNMP_NOSUCHINSTANCE); 252 continue; 253 } 254 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 255 (u_char *)table_entry->size, 256 sizeof(table_entry->size)); 257 break; 258 case COLUMN_USED: 259 if (table_entry == NULL) { 260 netsnmp_set_request_error(reqinfo, request, 261 SNMP_NOSUCHINSTANCE); 262 continue; 263 } 264 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 265 (u_char *)table_entry->used, 266 sizeof(table_entry->used)); 267 break; 268 case COLUMN_AVAIL: 269 if (table_entry == NULL) { 270 netsnmp_set_request_error(reqinfo, request, 271 SNMP_NOSUCHINSTANCE); 272 continue; 273 } 274 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 275 (u_char *)table_entry->avail, 276 sizeof(table_entry->avail)); 277 break; 278 case COLUMN_CAPACITY: 279 if (table_entry == NULL) { 280 netsnmp_set_request_error(reqinfo, request, 281 SNMP_NOSUCHINSTANCE); 282 continue; 283 } 284 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 285 (u_char *)table_entry->capacity, 286 sizeof(table_entry->capacity)); 287 break; 288 case COLUMN_MOUNTEDON: 289 if (table_entry == NULL) { 290 netsnmp_set_request_error(reqinfo, request, 291 SNMP_NOSUCHINSTANCE); 292 continue; 293 } 294 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 295 (u_char *)table_entry->mountedOn, 296 sizeof(table_entry->mountedOn)); 297 break; 298 default: 299 continue; 300 //netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); 301 } 302 } 303 break; 304 305 } 306 return SNMP_ERR_NOERROR; 307 }
Table code
除了这些还需要有一个common.c把所有的代码连接到一起。
1 #include "HostName.h" 2 #include "DiskInfoTable.h" 3 void init_diskcheck(void) { 4 init_HostName(); 5 init_DiskInfoTable(); 6 }
注:init_动态库名 和生成的动态库名字有联系,如果动态库名字不为 diskcheck.so net-snmp有可能无法识别。
编译
编译这块主要是利用net-snmp给的命令,直接给出Makefile好了。
1 CC = gcc 2 3 DIR = HostName DiskInfoTable 4 #DIR = HostName 5 6 vpath %.c ./ 7 vpath %.c $(DIR) 8 9 INCLUDE = -I./ 10 INCLUDE += $(foreach x, $(DIR), -I./$(x)) 11 12 CFLAGS = -fPIC -shared -O3 13 CFLAGS += $(shell net-snmp-config --cflags) 14 15 LDFLAGS = $(shell net-snmp-config --libs) 16 17 SRC = common.c 18 SRC += $(foreach x, $(DIR), $(x).c) 19 OBJS = $(SRC:.c=.o) 20 21 TARGET = diskcheck.so 22 23 all:$(TARGET) 24 25 $(TARGET):$(OBJS) 26 $(CC) $^ -o [email protected] $(CFLAGS) $(LDFLAGS) 27 28 $(OBJS):%.o:%.c 29 $(CC) -c $^ -o [email protected] $(CFLAGS) $(INCLUDE) 30 31 .PHONY:clean 32 clean: 33 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中没问题【有可能是我的配置问题】。需要改成步骤二中的配置。