net-snmp example分析
http://qgjie456.blog.163.com/
/*display_time.c*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if TIME_WITH_SYS_TIME
# ifdef WIN32
# include <sys/timeb.h>
# else
# include <sys/time.h>
# endif
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
/*
* header_generic() comes from here
*/
#include "util_funcs.h"
包含自己的头文件
#include "Display_time.h"
#define EXAMPLE_STR_LEN 300
#define EXAMPLE_STR_DEFAULT "life the universe and everything"
int example_int = 42;
char example_str[EXAMPLE_STR_LEN];
void example_parse_config_exampleint(const char *token, char *cptr);
void example_parse_config_examplestr(const char *token, char *cptr);
void example_free_config_exampleint(void);
void example_free_config_examplestr(void);
这个数组的类型是struct variableN, 其中N是这个数组中OID号的最长的数,
即:结构体最后一个成员(这个成员是个数组)定义了MIB Tree OID的底层数字。
N定义了MIB Tree OID的底层的层数(也就是这个数组的长度)。
所有有效的N数字都定义在了<agent/var_struct.h>文件中。
struct variableN类型成员的说明:
1):FoxmailINT:这个magic number是在自己的头文件Display_time.h中宏定义,
这个参数被用来传递给CallBack 例程,用来决定那个object被查询。
2):ASN_INTEGER:这个参数说明了object的类型,所有有效的类型在snmp_impl.h文件中列表说明。
3):RONLY:这个参数说明了object是否能够被set。
4):var_foxmail:当有object被查询时,这个CallBack 例程被调用。
一般的情况下,同一个文件中的所有的object使用相同的allBack 例程。
5):1:MIB Tree OID的底层数字的层数。(这个数字决定了struct variableN中的N)
6):{1}:MIB Tree OID的底层数字。
struct variable2 example_variables[] = {
{EXAMPLESTRING, ASN_OCTET_STR, RONLY, var_example, 1, {1}},
{EXAMPLEINTEGER, ASN_INTEGER, RWRITE, var_example, 2, {2, 1}},
{EXAMPLEOBJECTID, ASN_OBJECT_ID, RONLY, var_example, 2, {2, 2}},
{EXAMPLETIMETICKS, ASN_TIMETICKS, RONLY, var_example, 1, {3}},
{EXAMPLEIPADDRESS, ASN_IPADDRESS, RONLY, var_example, 1, {4}},
{EXAMPLECOUNTER, ASN_COUNTER, RONLY, var_example, 1, {5}},
{EXAMPLEGAUGE, ASN_GAUGE, RONLY, var_example, 1, {6}},
{EXAMPLETRIGGERTRAP, ASN_INTEGER, RWRITE, var_example, 1, {7}},
{EXAMPLETRIGGERTRAP2, ASN_INTEGER, RWRITE, var_example, 1, {8}}
};
下面这个数组定义了MIB Tree OID的顶层数字。
oid example_variables_oid[] = { 1, 3, 6, 1, 4, 1, 2021, 254 };
这个例程在Agent程序开始的时候被调用,用来初始化可能被查询的Object。
void init_example(void)
{
注册我们自己的MIB Tree,以便Agent查询的时候能够处理。
参数:
1)descr: 描述这个MIB Tree
2)var: 变量结构体,类型struct variableN。
3)vartype: 类型struct variableN
4)theoid: MIB Tree的顶层数字
REGISTER_MIB("example", example_variables, variable2,
example_variables_oid);
把example_str变量设上默认字符串。example_int已经在上面初始化了。
strncpy(example_str, EXAMPLE_STR_DEFAULT, EXAMPLE_STR_LEN);
* Register config handlers for the two objects that can be set
* via configuration file directive
snmpd_register_config_handler("exampleint",
example_parse_config_exampleint,
example_free_config_exampleint,
"exampleint value");
snmpd_register_config_handler("examplestr",
example_parse_config_examplestr,
example_free_config_examplestr,
"examplestr value");
snmpd_register_config_handler("examplestring",
example_parse_config_examplestr,
example_free_config_examplestr,
"examplestring value");
我们经常需要读取内核中的数据,我们需要在这里进行一些必要的初始化。
以加快我们读取这些内核信息的速度,快速反应查询请求。
/*
* auto_nlist( "example_symbol", 0, 0 );
*/
}
配置文件处理函数
void
example_parse_config_exampleint(const char *token, char *cptr)
{
example_int = atoi(cptr);
}
void
example_parse_config_examplestr(const char *token, char *cptr)
{
必须确保字符串长度小于分配的空间。
if (strlen(cptr) < EXAMPLE_STR_LEN)
strcpy(example_str, cptr);
else {
如果需要的话,截断这个字符串。
strncpy(example_str, cptr, EXAMPLE_STR_LEN - 4);
example_str[EXAMPLE_STR_LEN - 4] = 0;
strcat(example_str, "...");
example_str[EXAMPLE_STR_LEN - 1] = 0;
}
}
当关闭Agent时需要的清除工作。
void
example_free_config_exampleint(void)
{
}
void
example_free_config_examplestr(void)
{
}
当有请求访问这个MIB Tree的object时,就会调用这个处理函数。
参数:
1)vp 被请求访问的object的example_variables的入口地址
2)name 被请求访问的object的OID
3)length OID的长度
4)exact 指示这个request是“exact”(GET/SET)请求,还是“inexact”(GETNEXT/GETBULK)请求
四个参数被用来返回信息:
1)name 被请求访问的object的OID
2)length OID的长度
3)var_len 返回应答的长度
4)write_method 被请求访问的object的SET函数的指针
u_char *
var_example(struct variable *vp,
oid * name,
size_t * length,
int exact, size_t * var_len, WriteMethod ** write_method)
{
从这个函数返回的值必须是一个static data的指针,这样才能够从函数外来访问。
static char string[EXAMPLE_STR_LEN]; /* for EXAMPLESTRING */
static oid oid_ret[8]; /* for EXAMPLEOBJECTID */
static long long_ret; /* for everything else */
在进行应答请求之前,需要检查这个请求object OID是否是一个有效的OID。
header_generic()函数能够用来检查scalar objects。
header_simple_table()函数能够用来检查simple table。
这些函数也当检查正确时,设置默认的返回值。
* The name and length are set suitably for the current object,
* var_len assumes that the result is an integer of some form,
* and write_method assumes that the object cannot be set.
DEBUGMSGTL(("example", "var_example entered\n"));
if (header_generic(vp, name, length, exact, var_len, write_method) ==
MATCH_FAILED)
return NULL;
我们使用struct variableN结构体中的magic number来决定那个object被请求。
switch (vp->magic) {
case EXAMPLESTRING:
sprintf(string, example_str);
在上面时假设返回值是integer,但是并不是,所以需要重新设置var_len。
*var_len = strlen(string);
return (u_char *) string;
case EXAMPLEINTEGER:
这种情况,上面的假设的长度是正确的,但是这个object是可以写的,所以需要设置write_method。
long_ret = example_int;
*write_method = write_exampleint;
return (u_char *) & long_ret;
case EXAMPLEOBJECTID:
oid_ret[0] = 1;
oid_ret[1] = 3;
oid_ret[2] = 6;
oid_ret[3] = 1;
oid_ret[4] = 4;
oid_ret[5] = oid_ret[6] = oid_ret[7] = 42;
这种情况,上面的假设的长度是错误的。
*var_len = 8 * sizeof(oid);
return (u_char *) oid_ret;
case EXAMPLETIMETICKS:
这种情况,上面的假设的长度是正确的,直接返回。
long_ret = 363136200; /* 42 days, 42 minutes and 42.0 seconds */
return (u_char *) & long_ret;
case EXAMPLEIPADDRESS:
long_ret = ntohl(INADDR_LOOPBACK);
return (u_char *) & long_ret;
case EXAMPLECOUNTER:
long_ret = 42;
return (u_char *) & long_ret;
case EXAMPLEGAUGE:
long_ret = 42; /* Do we detect a theme running through these answers? */
return (u_char *) & long_ret;
case EXAMPLETRIGGERTRAP:
这个object是只能够写的“write-only”。
它的作用是只能够产生一个“trap”,当读它的时候只能够返回0。
long_ret = 0;
*write_method = write_exampletrap;
return (u_char *) & long_ret;
case EXAMPLETRIGGERTRAP2:
这个object是只能够写的“write-only”。
它的作用是只能够产生一个SNMP v2版本的“trap”,当读它的时候只能够返回0。
long_ret = 0;
*write_method = write_exampletrap2;
return (u_char *) & long_ret;
default:
这种情况,报告一个错误,并把错误写入log。
DEBUGMSGTL(("snmpd", "unknown sub-id %d in examples/var_example\n",
vp->magic));
}
return NULL;
}
当某个object是可写的时候,需要设置SET处理例程。
int
write_exampleint(int action,
u_char * var_val,
u_char var_val_type,
size_t var_val_len,
u_char * statP, oid * name, size_t name_len)
{
定义一个允许访问的最大数值,它是任意的。
#define MAX_EXAMPLE_INT 100
static long intval;
static long old_intval;
switch (action) {
case RESERVE1:
检查要设置的值是否符合条件。
if (var_val_type != ASN_INTEGER) {
DEBUGMSGTL(("example", "%x not integer type", var_val_type));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > sizeof(long)) {
DEBUGMSGTL(("example", "wrong length %x", var_val_len));
return SNMP_ERR_WRONGLENGTH;
}
intval = *((long *) var_val);
if (intval > MAX_EXAMPLE_INT) {
DEBUGMSGTL(("example", "wrong value %x", intval));
return SNMP_ERR_WRONGVALUE;
}
break;
case RESERVE2:
break;
case FREE:
break;
case ACTION:
按照请求设置数值,但是这个请求可能被撤销,所以需要保存原来的值。
old_intval = example_int;
example_int = intval;
break;
case UNDO:
撤销上一个请求,恢复原来的值。
example_int = old_intval;
break;
case COMMIT:
break;
}
return SNMP_ERR_NOERROR;
}
int
write_exampletrap(int action,
u_char * var_val,
u_char var_val_type,
size_t var_val_len,
u_char * statP, oid * name, size_t name_len)
{
long intval;
DEBUGMSGTL(("example", "write_exampletrap entered: action=%d\n",
action));
switch (action) {
case RESERVE1:
if (var_val_type != ASN_INTEGER) {
DEBUGMSGTL(("example", "%x not integer type", var_val_type));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > sizeof(long)) {
DEBUGMSGTL(("example", "wrong length %x", var_val_len));
return SNMP_ERR_WRONGLENGTH;
}
intval = *((long *) var_val);
if (intval != 1) {
DEBUGMSGTL(("example", "wrong value %x", intval));
return SNMP_ERR_WRONGVALUE;
}
break;
case RESERVE2:
break;
case FREE:
break;
case ACTION:
break;
case UNDO:
break;
case COMMIT:
产生一个“trap”
DEBUGMSGTL(("example", "write_exampletrap sending the trap\n",
action));
send_easy_trap(SNMP_TRAP_ENTERPRISESPECIFIC, 99);
DEBUGMSGTL(("example", "write_exampletrap trap sent\n", action));
break;
}
return SNMP_ERR_NOERROR;
}
int
write_exampletrap2(int action,
u_char * var_val,
u_char var_val_type,
size_t var_val_len,
u_char * statP, oid * name, size_t name_len)
{
long intval;
oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; /* snmpTrapOID.0 */
oid demo_trap[] = { 1, 3, 6, 1, 4, 1, 2021, 13, 990 }; /*demo-trap */
oid example_string_oid[] =
{ 1, 3, 6, 1, 4, 1, 2021, 254, 1, 0 };
static netsnmp_variable_list var_trap;
static netsnmp_variable_list var_obj;
DEBUGMSGTL(("example", "write_exampletrap2 entered: action=%d\n",
action));
switch (action) {
case RESERVE1:
if (var_val_type != ASN_INTEGER) {
DEBUGMSGTL(("example", "%x not integer type", var_val_type));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > sizeof(long)) {
DEBUGMSGTL(("example", "wrong length %x", var_val_len));
return SNMP_ERR_WRONGLENGTH;
}
intval = *((long *) var_val);
if (intval != 1) {
DEBUGMSGTL(("example", "wrong value %x", intval));
return SNMP_ERR_WRONGVALUE;
}
break;
case RESERVE2:
break;
case FREE:
break;
case ACTION:
break;
case UNDO:
break;
case COMMIT:
产生一个 SNMP v2版本的“trap”
var_trap.next_variable = &var_obj; /* next variable */
var_trap.name = objid_snmptrap; /* snmpTrapOID.0 */
var_trap.name_length = sizeof(objid_snmptrap) / sizeof(oid); /* number of sub-ids */
var_trap.type = ASN_OBJECT_ID;
var_trap.val.objid = demo_trap; /* demo-trap objid */
var_trap.val_len = sizeof(demo_trap); /* length in bytes (not number of subids!) */
var_obj.next_variable = NULL; /* No more variables after this one */
var_obj.name = example_string_oid;
var_obj.name_length = sizeof(example_string_oid) / sizeof(oid); /* number of sub-ids */
var_obj.type = ASN_OCTET_STR; /* type of variable */
var_obj.val.string = example_str; /* value */
var_obj.val_len = strlen(example_str);
DEBUGMSGTL(("example", "write_exampletrap2 sending the v2 trap\n",
action));
send_v2trap(&var_trap);
DEBUGMSGTL(("example", "write_exampletrap2 v2 trap sent\n",
action));
break;
}
return SNMP_ERR_NOERROR;
}
REGISTER_MIB()宏定义最终调用的是netsnmp_register_old_api函数。
In fact, this macro is simply a wrapper round the routine register_mib(),
but the details of this can safely be ignored, unless more control over the
registration is required.
里面用到了这个结构体。
typedef struct netsnmp_handler_registration_s {
/** for mrTable listings, and other uses */
char *handlerName;
/** NULL = default context */
char *contextName;
/**
* where are we registered at?
*/
oid *rootoid;
size_t rootoid_len;
/**
* handler details
*/
netsnmp_mib_handler *handler;
int modes;
/**
* more optional stuff
*/
int priority;
int range_subid;
oid range_ubound;
int timeout;
int global_cacheid;
/**
* void ptr for registeree
*/
void * my_reg_void;
} netsnmp_handler_registration;
在调用netsnmp_register_handler(reginfo)函数注册netsnmp_handler_registration 类型的reginfo变量。
netsnmp_register_handler()在agent/agent_handler.c文件中定义。
有调用netsnmp_register_mib()函数
这个函数构建一个netsnmp_subtree 类型的变量subtree进行注册。
typedef struct netsnmp_subtree_s {
oid *name_a; /* objid prefix of registered subtree */
u_char namelen; /* number of subid's in name above */
oid *start_a; /* objid of start of covered range */
u_char start_len; /* number of subid's in start name */
oid *end_a; /* objid of end of covered range */
u_char end_len; /* number of subid's in end name */
struct variable *variables; /* pointer to variables array */
int variables_len; /* number of entries in above array */
int variables_width; /* sizeof each variable entry */
char *label_a; /* calling module's label */
netsnmp_session *session;
u_char flags;
u_char priority;
int timeout;
struct netsnmp_subtree_s *next; /* List of 'sibling' subtrees */
struct netsnmp_subtree_s *prev; /* (doubly-linked list) */
struct netsnmp_subtree_s *children; /* List of 'child' subtrees */
int range_subid;
oid range_ubound;
netsnmp_handler_registration *reginfo; /* new API */
int cacheid;
int global_cacheid;
} netsnmp_subtree;
调用这个函数netsnmp_subtree_load()进行注册。
auto_nlist()的使用,许多的请求需要直接访问内核空间中的数据,
这需要作一些预处理,以便快速反应,能够迅速返回信息。
Alternatively, there may be ioctl calls on suitable devices, specific system
calls, or special files that can be read to provide the necessary
information.
#ifdef {NAME}_SYMBOL
auto_nlist( {NAME}_SYMBOL, 0, 0);
#endif
{NAME}_SYMBOL符号被定义为system-specific配置的一部分,是内核空间中的变量或结构体的名称。
这两个“0”是因为只是简单的利用内核来提供信息。
(see the !HAVE_SYS_TCPIPSTATS_H case of the ICMP group for an example)
/*display_time.c*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if TIME_WITH_SYS_TIME
# ifdef WIN32
# include <sys/timeb.h>
# else
# include <sys/time.h>
# endif
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
/*
* header_generic() comes from here
*/
#include "util_funcs.h"
包含自己的头文件
#include "Display_time.h"
#define EXAMPLE_STR_LEN 300
#define EXAMPLE_STR_DEFAULT "life the universe and everything"
int example_int = 42;
char example_str[EXAMPLE_STR_LEN];
void example_parse_config_exampleint(const char *token, char *cptr);
void example_parse_config_examplestr(const char *token, char *cptr);
void example_free_config_exampleint(void);
void example_free_config_examplestr(void);
这个数组的类型是struct variableN, 其中N是这个数组中OID号的最长的数,
即:结构体最后一个成员(这个成员是个数组)定义了MIB Tree OID的底层数字。
N定义了MIB Tree OID的底层的层数(也就是这个数组的长度)。
所有有效的N数字都定义在了<agent/var_struct.h>文件中。
struct variableN类型成员的说明:
1):FoxmailINT:这个magic number是在自己的头文件Display_time.h中宏定义,
这个参数被用来传递给CallBack 例程,用来决定那个object被查询。
2):ASN_INTEGER:这个参数说明了object的类型,所有有效的类型在snmp_impl.h文件中列表说明。
3):RONLY:这个参数说明了object是否能够被set。
4):var_foxmail:当有object被查询时,这个CallBack 例程被调用。
一般的情况下,同一个文件中的所有的object使用相同的allBack 例程。
5):1:MIB Tree OID的底层数字的层数。(这个数字决定了struct variableN中的N)
6):{1}:MIB Tree OID的底层数字。
struct variable2 example_variables[] = {
{EXAMPLESTRING, ASN_OCTET_STR, RONLY, var_example, 1, {1}},
{EXAMPLEINTEGER, ASN_INTEGER, RWRITE, var_example, 2, {2, 1}},
{EXAMPLEOBJECTID, ASN_OBJECT_ID, RONLY, var_example, 2, {2, 2}},
{EXAMPLETIMETICKS, ASN_TIMETICKS, RONLY, var_example, 1, {3}},
{EXAMPLEIPADDRESS, ASN_IPADDRESS, RONLY, var_example, 1, {4}},
{EXAMPLECOUNTER, ASN_COUNTER, RONLY, var_example, 1, {5}},
{EXAMPLEGAUGE, ASN_GAUGE, RONLY, var_example, 1, {6}},
{EXAMPLETRIGGERTRAP, ASN_INTEGER, RWRITE, var_example, 1, {7}},
{EXAMPLETRIGGERTRAP2, ASN_INTEGER, RWRITE, var_example, 1, {8}}
};
下面这个数组定义了MIB Tree OID的顶层数字。
oid example_variables_oid[] = { 1, 3, 6, 1, 4, 1, 2021, 254 };
这个例程在Agent程序开始的时候被调用,用来初始化可能被查询的Object。
void init_example(void)
{
注册我们自己的MIB Tree,以便Agent查询的时候能够处理。
参数:
1)descr: 描述这个MIB Tree
2)var: 变量结构体,类型struct variableN。
3)vartype: 类型struct variableN
4)theoid: MIB Tree的顶层数字
REGISTER_MIB("example", example_variables, variable2,
example_variables_oid);
把example_str变量设上默认字符串。example_int已经在上面初始化了。
strncpy(example_str, EXAMPLE_STR_DEFAULT, EXAMPLE_STR_LEN);
* Register config handlers for the two objects that can be set
* via configuration file directive
snmpd_register_config_handler("exampleint",
example_parse_config_exampleint,
example_free_config_exampleint,
"exampleint value");
snmpd_register_config_handler("examplestr",
example_parse_config_examplestr,
example_free_config_examplestr,
"examplestr value");
snmpd_register_config_handler("examplestring",
example_parse_config_examplestr,
example_free_config_examplestr,
"examplestring value");
我们经常需要读取内核中的数据,我们需要在这里进行一些必要的初始化。
以加快我们读取这些内核信息的速度,快速反应查询请求。
/*
* auto_nlist( "example_symbol", 0, 0 );
*/
}
配置文件处理函数
void
example_parse_config_exampleint(const char *token, char *cptr)
{
example_int = atoi(cptr);
}
void
example_parse_config_examplestr(const char *token, char *cptr)
{
必须确保字符串长度小于分配的空间。
if (strlen(cptr) < EXAMPLE_STR_LEN)
strcpy(example_str, cptr);
else {
如果需要的话,截断这个字符串。
strncpy(example_str, cptr, EXAMPLE_STR_LEN - 4);
example_str[EXAMPLE_STR_LEN - 4] = 0;
strcat(example_str, "...");
example_str[EXAMPLE_STR_LEN - 1] = 0;
}
}
当关闭Agent时需要的清除工作。
void
example_free_config_exampleint(void)
{
}
void
example_free_config_examplestr(void)
{
}
当有请求访问这个MIB Tree的object时,就会调用这个处理函数。
参数:
1)vp 被请求访问的object的example_variables的入口地址
2)name 被请求访问的object的OID
3)length OID的长度
4)exact 指示这个request是“exact”(GET/SET)请求,还是“inexact”(GETNEXT/GETBULK)请求
四个参数被用来返回信息:
1)name 被请求访问的object的OID
2)length OID的长度
3)var_len 返回应答的长度
4)write_method 被请求访问的object的SET函数的指针
u_char *
var_example(struct variable *vp,
oid * name,
size_t * length,
int exact, size_t * var_len, WriteMethod ** write_method)
{
从这个函数返回的值必须是一个static data的指针,这样才能够从函数外来访问。
static char string[EXAMPLE_STR_LEN]; /* for EXAMPLESTRING */
static oid oid_ret[8]; /* for EXAMPLEOBJECTID */
static long long_ret; /* for everything else */
在进行应答请求之前,需要检查这个请求object OID是否是一个有效的OID。
header_generic()函数能够用来检查scalar objects。
header_simple_table()函数能够用来检查simple table。
这些函数也当检查正确时,设置默认的返回值。
* The name and length are set suitably for the current object,
* var_len assumes that the result is an integer of some form,
* and write_method assumes that the object cannot be set.
DEBUGMSGTL(("example", "var_example entered\n"));
if (header_generic(vp, name, length, exact, var_len, write_method) ==
MATCH_FAILED)
return NULL;
我们使用struct variableN结构体中的magic number来决定那个object被请求。
switch (vp->magic) {
case EXAMPLESTRING:
sprintf(string, example_str);
在上面时假设返回值是integer,但是并不是,所以需要重新设置var_len。
*var_len = strlen(string);
return (u_char *) string;
case EXAMPLEINTEGER:
这种情况,上面的假设的长度是正确的,但是这个object是可以写的,所以需要设置write_method。
long_ret = example_int;
*write_method = write_exampleint;
return (u_char *) & long_ret;
case EXAMPLEOBJECTID:
oid_ret[0] = 1;
oid_ret[1] = 3;
oid_ret[2] = 6;
oid_ret[3] = 1;
oid_ret[4] = 4;
oid_ret[5] = oid_ret[6] = oid_ret[7] = 42;
这种情况,上面的假设的长度是错误的。
*var_len = 8 * sizeof(oid);
return (u_char *) oid_ret;
case EXAMPLETIMETICKS:
这种情况,上面的假设的长度是正确的,直接返回。
long_ret = 363136200; /* 42 days, 42 minutes and 42.0 seconds */
return (u_char *) & long_ret;
case EXAMPLEIPADDRESS:
long_ret = ntohl(INADDR_LOOPBACK);
return (u_char *) & long_ret;
case EXAMPLECOUNTER:
long_ret = 42;
return (u_char *) & long_ret;
case EXAMPLEGAUGE:
long_ret = 42; /* Do we detect a theme running through these answers? */
return (u_char *) & long_ret;
case EXAMPLETRIGGERTRAP:
这个object是只能够写的“write-only”。
它的作用是只能够产生一个“trap”,当读它的时候只能够返回0。
long_ret = 0;
*write_method = write_exampletrap;
return (u_char *) & long_ret;
case EXAMPLETRIGGERTRAP2:
这个object是只能够写的“write-only”。
它的作用是只能够产生一个SNMP v2版本的“trap”,当读它的时候只能够返回0。
long_ret = 0;
*write_method = write_exampletrap2;
return (u_char *) & long_ret;
default:
这种情况,报告一个错误,并把错误写入log。
DEBUGMSGTL(("snmpd", "unknown sub-id %d in examples/var_example\n",
vp->magic));
}
return NULL;
}
当某个object是可写的时候,需要设置SET处理例程。
int
write_exampleint(int action,
u_char * var_val,
u_char var_val_type,
size_t var_val_len,
u_char * statP, oid * name, size_t name_len)
{
定义一个允许访问的最大数值,它是任意的。
#define MAX_EXAMPLE_INT 100
static long intval;
static long old_intval;
switch (action) {
case RESERVE1:
检查要设置的值是否符合条件。
if (var_val_type != ASN_INTEGER) {
DEBUGMSGTL(("example", "%x not integer type", var_val_type));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > sizeof(long)) {
DEBUGMSGTL(("example", "wrong length %x", var_val_len));
return SNMP_ERR_WRONGLENGTH;
}
intval = *((long *) var_val);
if (intval > MAX_EXAMPLE_INT) {
DEBUGMSGTL(("example", "wrong value %x", intval));
return SNMP_ERR_WRONGVALUE;
}
break;
case RESERVE2:
break;
case FREE:
break;
case ACTION:
按照请求设置数值,但是这个请求可能被撤销,所以需要保存原来的值。
old_intval = example_int;
example_int = intval;
break;
case UNDO:
撤销上一个请求,恢复原来的值。
example_int = old_intval;
break;
case COMMIT:
break;
}
return SNMP_ERR_NOERROR;
}
int
write_exampletrap(int action,
u_char * var_val,
u_char var_val_type,
size_t var_val_len,
u_char * statP, oid * name, size_t name_len)
{
long intval;
DEBUGMSGTL(("example", "write_exampletrap entered: action=%d\n",
action));
switch (action) {
case RESERVE1:
if (var_val_type != ASN_INTEGER) {
DEBUGMSGTL(("example", "%x not integer type", var_val_type));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > sizeof(long)) {
DEBUGMSGTL(("example", "wrong length %x", var_val_len));
return SNMP_ERR_WRONGLENGTH;
}
intval = *((long *) var_val);
if (intval != 1) {
DEBUGMSGTL(("example", "wrong value %x", intval));
return SNMP_ERR_WRONGVALUE;
}
break;
case RESERVE2:
break;
case FREE:
break;
case ACTION:
break;
case UNDO:
break;
case COMMIT:
产生一个“trap”
DEBUGMSGTL(("example", "write_exampletrap sending the trap\n",
action));
send_easy_trap(SNMP_TRAP_ENTERPRISESPECIFIC, 99);
DEBUGMSGTL(("example", "write_exampletrap trap sent\n", action));
break;
}
return SNMP_ERR_NOERROR;
}
int
write_exampletrap2(int action,
u_char * var_val,
u_char var_val_type,
size_t var_val_len,
u_char * statP, oid * name, size_t name_len)
{
long intval;
oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; /* snmpTrapOID.0 */
oid demo_trap[] = { 1, 3, 6, 1, 4, 1, 2021, 13, 990 }; /*demo-trap */
oid example_string_oid[] =
{ 1, 3, 6, 1, 4, 1, 2021, 254, 1, 0 };
static netsnmp_variable_list var_trap;
static netsnmp_variable_list var_obj;
DEBUGMSGTL(("example", "write_exampletrap2 entered: action=%d\n",
action));
switch (action) {
case RESERVE1:
if (var_val_type != ASN_INTEGER) {
DEBUGMSGTL(("example", "%x not integer type", var_val_type));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > sizeof(long)) {
DEBUGMSGTL(("example", "wrong length %x", var_val_len));
return SNMP_ERR_WRONGLENGTH;
}
intval = *((long *) var_val);
if (intval != 1) {
DEBUGMSGTL(("example", "wrong value %x", intval));
return SNMP_ERR_WRONGVALUE;
}
break;
case RESERVE2:
break;
case FREE:
break;
case ACTION:
break;
case UNDO:
break;
case COMMIT:
产生一个 SNMP v2版本的“trap”
var_trap.next_variable = &var_obj; /* next variable */
var_trap.name = objid_snmptrap; /* snmpTrapOID.0 */
var_trap.name_length = sizeof(objid_snmptrap) / sizeof(oid); /* number of sub-ids */
var_trap.type = ASN_OBJECT_ID;
var_trap.val.objid = demo_trap; /* demo-trap objid */
var_trap.val_len = sizeof(demo_trap); /* length in bytes (not number of subids!) */
var_obj.next_variable = NULL; /* No more variables after this one */
var_obj.name = example_string_oid;
var_obj.name_length = sizeof(example_string_oid) / sizeof(oid); /* number of sub-ids */
var_obj.type = ASN_OCTET_STR; /* type of variable */
var_obj.val.string = example_str; /* value */
var_obj.val_len = strlen(example_str);
DEBUGMSGTL(("example", "write_exampletrap2 sending the v2 trap\n",
action));
send_v2trap(&var_trap);
DEBUGMSGTL(("example", "write_exampletrap2 v2 trap sent\n",
action));
break;
}
return SNMP_ERR_NOERROR;
}
REGISTER_MIB()宏定义最终调用的是netsnmp_register_old_api函数。
In fact, this macro is simply a wrapper round the routine register_mib(),
but the details of this can safely be ignored, unless more control over the
registration is required.
里面用到了这个结构体。
typedef struct netsnmp_handler_registration_s {
/** for mrTable listings, and other uses */
char *handlerName;
/** NULL = default context */
char *contextName;
/**
* where are we registered at?
*/
oid *rootoid;
size_t rootoid_len;
/**
* handler details
*/
netsnmp_mib_handler *handler;
int modes;
/**
* more optional stuff
*/
int priority;
int range_subid;
oid range_ubound;
int timeout;
int global_cacheid;
/**
* void ptr for registeree
*/
void * my_reg_void;
} netsnmp_handler_registration;
在调用netsnmp_register_handler(reginfo)函数注册netsnmp_handler_registration 类型的reginfo变量。
netsnmp_register_handler()在agent/agent_handler.c文件中定义。
有调用netsnmp_register_mib()函数
这个函数构建一个netsnmp_subtree 类型的变量subtree进行注册。
typedef struct netsnmp_subtree_s {
oid *name_a; /* objid prefix of registered subtree */
u_char namelen; /* number of subid's in name above */
oid *start_a; /* objid of start of covered range */
u_char start_len; /* number of subid's in start name */
oid *end_a; /* objid of end of covered range */
u_char end_len; /* number of subid's in end name */
struct variable *variables; /* pointer to variables array */
int variables_len; /* number of entries in above array */
int variables_width; /* sizeof each variable entry */
char *label_a; /* calling module's label */
netsnmp_session *session;
u_char flags;
u_char priority;
int timeout;
struct netsnmp_subtree_s *next; /* List of 'sibling' subtrees */
struct netsnmp_subtree_s *prev; /* (doubly-linked list) */
struct netsnmp_subtree_s *children; /* List of 'child' subtrees */
int range_subid;
oid range_ubound;
netsnmp_handler_registration *reginfo; /* new API */
int cacheid;
int global_cacheid;
} netsnmp_subtree;
调用这个函数netsnmp_subtree_load()进行注册。
auto_nlist()的使用,许多的请求需要直接访问内核空间中的数据,
这需要作一些预处理,以便快速反应,能够迅速返回信息。
Alternatively, there may be ioctl calls on suitable devices, specific system
calls, or special files that can be read to provide the necessary
information.
#ifdef {NAME}_SYMBOL
auto_nlist( {NAME}_SYMBOL, 0, 0);
#endif
{NAME}_SYMBOL符号被定义为system-specific配置的一部分,是内核空间中的变量或结构体的名称。
这两个“0”是因为只是简单的利用内核来提供信息。
(see the !HAVE_SYS_TCPIPSTATS_H case of the ICMP group for an example)