1300 lines
37 KiB
C
1300 lines
37 KiB
C
## -*- c -*-
|
|
##
|
|
## For documentation on the code generated by this configuration file,
|
|
## see the file agent/helpers/table_array.c.
|
|
##
|
|
######################################################################
|
|
## Do the .h file
|
|
## @perleval $vars{shortname} =~ s/([A-Z])[a-z]+/$1/g@
|
|
######################################################################
|
|
@foreach $i table@
|
|
@open ${i}.h@
|
|
/*
|
|
* Note: this file originally auto-generated by mib2c
|
|
* using mib2c.array-user.conf
|
|
*
|
|
* Yes, there is lots of code here that you might not use. But it is much
|
|
* easier to remove code than to add it!
|
|
*/
|
|
#ifndef $i.uc_H
|
|
#define $i.uc_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/library/container.h>
|
|
#include <net-snmp/agent/table_array.h>
|
|
|
|
@eval $ext_index = 0@
|
|
@foreach $idx index@
|
|
@if "$idx" ne ""@
|
|
@eval $found = "external"@
|
|
@foreach $c column@
|
|
@if "$idx" eq "$c"@
|
|
@eval $found = "internal"@
|
|
@end@
|
|
@end@
|
|
/** Index $idx is $found */
|
|
@if "$found" eq "external"@
|
|
@eval $ext_index = 1@
|
|
@end@
|
|
@end@
|
|
@end@
|
|
|
|
typedef struct ${i}_context_s {
|
|
netsnmp_index index; /** THIS MUST BE FIRST!!! */
|
|
|
|
/*************************************************************
|
|
* You can store data internally in this structure.
|
|
*
|
|
* TODO: You will probably have to fix a few types here...
|
|
*/
|
|
@if $ext_index != 0@
|
|
/** TODO: add storage for external index(s)! */
|
|
@end@
|
|
@foreach $c column@
|
|
/** $c.syntax = $c.type */
|
|
@eval $have_type = 0@
|
|
@if "$c.type" eq "ASN_OCTET_STR"@
|
|
@eval $have_type = 1@
|
|
@eval $o_len = "65535"@
|
|
@if "$c.syntax" eq "DisplayString"@
|
|
@eval $o_len = "255"@
|
|
@end@
|
|
@if "$c.syntax" eq "SnmpAdminString"@
|
|
@eval $o_len = "255"@
|
|
@end@
|
|
unsigned char $c[$o_len];
|
|
long ${c}_len;
|
|
@end@
|
|
@if "$c.type" eq "ASN_OBJECT_ID"@
|
|
@eval $have_type = 1@
|
|
oid $c[MAX_OID_LEN];
|
|
long ${c}_len;
|
|
@end@
|
|
@if "$c.type" eq "ASN_UNSIGNED"@
|
|
@eval $have_type = 1@
|
|
unsigned long $c;
|
|
@end@
|
|
@if "$c.type" eq "ASN_TIMETICKS"@
|
|
@eval $have_type = 1@
|
|
unsigned long $c;
|
|
@end@
|
|
@if "$c.type" eq "ASN_IPADDRESS"@
|
|
@eval $have_type = 1@
|
|
unsigned long $c;
|
|
@end@
|
|
@if "$c.type" eq "ASN_UINTEGER"@
|
|
@eval $have_type = 1@
|
|
unsigned long $c;
|
|
@end@
|
|
@if "$c.type" eq "ASN_INTEGER"@
|
|
@eval $have_type = 1@
|
|
long $c;
|
|
@end@
|
|
@if "$c.type" eq "ASN_COUNTER"@
|
|
@eval $have_type = 1@
|
|
unsigned long $c;
|
|
@end@
|
|
@if $have_type == 0@
|
|
/** TODO: Is this type correct? */
|
|
long $c;
|
|
@end@
|
|
|
|
@end@
|
|
|
|
/*
|
|
* OR
|
|
*
|
|
* Keep a pointer to your data
|
|
*/
|
|
void * data;
|
|
|
|
/*
|
|
*add anything else you want here
|
|
*/
|
|
|
|
} ${i}_context;
|
|
|
|
/*************************************************************
|
|
* function declarations
|
|
*/
|
|
void init_$i(void);
|
|
void initialize_table_$i(void);
|
|
const ${i}_context * ${i}_get_by_idx(netsnmp_index *);
|
|
const ${i}_context * ${i}_get_by_idx_rs(netsnmp_index *,
|
|
int row_status);
|
|
int ${i}_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *);
|
|
|
|
|
|
/*************************************************************
|
|
* oid declarations
|
|
*/
|
|
extern const oid ${i}_oid[];
|
|
extern const size_t ${i}_oid_len;
|
|
|
|
#define ${i}_TABLE_OID $i.commaoid
|
|
|
|
/*************************************************************
|
|
* column number definitions for table $i
|
|
*/
|
|
@eval $minv = 0xffffffff@
|
|
@eval $maxv = 0@
|
|
@foreach $c column@
|
|
#define COLUMN_$c.uc $c.subid
|
|
@if ! $c.noaccess@
|
|
@eval $minv = min($minv, $c.subid)@
|
|
@eval $maxv = max($maxv, $c.subid)@
|
|
@end@
|
|
@if "$c.syntax" eq "RowStatus"@
|
|
@eval $rs_name = "$c"@
|
|
@end@
|
|
@if "$c.syntax" eq "StorageType"@
|
|
@eval $st_name = "$c"@
|
|
@end@
|
|
@end@
|
|
#define ${i}_COL_MIN $minv
|
|
#define ${i}_COL_MAX $maxv
|
|
|
|
/* comment out the following line if you don't want a custom sort */
|
|
#define ${i}_CUSTOM_SORT
|
|
|
|
@if "$rs_name" ne ""@
|
|
/* uncommend the following line if you allow modifications to an
|
|
* active row */
|
|
/** define ${i}_CAN_MODIFY_ACTIVE_ROW */
|
|
|
|
@end@
|
|
@if $i.settable@
|
|
int ${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr );
|
|
|
|
void ${i}_set_reserve1( netsnmp_request_group * );
|
|
void ${i}_set_reserve2( netsnmp_request_group * );
|
|
void ${i}_set_action( netsnmp_request_group * );
|
|
void ${i}_set_commit( netsnmp_request_group * );
|
|
void ${i}_set_free( netsnmp_request_group * );
|
|
void ${i}_set_undo( netsnmp_request_group * );
|
|
|
|
${i}_context * ${i}_duplicate_row( ${i}_context* );
|
|
netsnmp_index * ${i}_delete_row( ${i}_context* );
|
|
|
|
@if "$rs_name" ne ""@
|
|
int ${i}_can_activate(${i}_context *undo_ctx,
|
|
${i}_context *row_ctx,
|
|
netsnmp_request_group * rg);
|
|
int ${i}_can_deactivate(${i}_context *undo_ctx,
|
|
${i}_context *row_ctx,
|
|
netsnmp_request_group * rg);
|
|
@end@
|
|
int ${i}_can_delete(${i}_context *undo_ctx,
|
|
${i}_context *row_ctx,
|
|
netsnmp_request_group * rg);
|
|
|
|
|
|
@if $i.creatable@
|
|
${i}_context * ${i}_create_row( netsnmp_index* );
|
|
@end@
|
|
@end@
|
|
|
|
#ifdef ${i}_CUSTOM_SORT
|
|
${i}_context * ${i}_get( const char *name, int len );
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /** $i.uc_H */
|
|
@end@
|
|
######################################################################
|
|
## Do the .c file
|
|
######################################################################
|
|
@foreach $i table@
|
|
@open ${i}.c@
|
|
/*
|
|
* Note: this file originally auto-generated by mib2c
|
|
* using mib2c.array-user.conf
|
|
*
|
|
*
|
|
* For help understanding NET-SNMP in general, please check the
|
|
* documentation and FAQ at:
|
|
*
|
|
* http://www.net-snmp.org/
|
|
*
|
|
*
|
|
* For help understanding this code, the agent and how it processes
|
|
* requests, please check the following references.
|
|
*
|
|
* http://www.net-snmp.org/tutorial-5/
|
|
*
|
|
*
|
|
* You can also join the #net-snmp channel on irc.freenode.net
|
|
* and ask for help there.
|
|
*
|
|
*
|
|
* And if all else fails, send a detailed message to the developers
|
|
* describing the problem you are having to:
|
|
*
|
|
* net-snmp-coders@lists.sourceforge.net
|
|
*
|
|
*
|
|
* Yes, there is lots of code here that you might not use. But it is much
|
|
* easier to remove code than to add it!
|
|
*/
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
|
|
|
#include <net-snmp/library/snmp_assert.h>
|
|
|
|
#include "${i}.h"
|
|
|
|
static netsnmp_handler_registration *my_handler = NULL;
|
|
static netsnmp_table_array_callbacks cb;
|
|
|
|
const oid ${i}_oid[] = { ${i}_TABLE_OID };
|
|
const size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
|
|
|
|
|
|
#ifdef ${i}_CUSTOM_SORT
|
|
/************************************************************
|
|
* keep binary tree to find context by name
|
|
*/
|
|
static int ${i}_cmp( const void *lhs, const void *rhs );
|
|
|
|
/************************************************************
|
|
* compare two context pointers here. Return -1 if lhs < rhs,
|
|
* 0 if lhs == rhs, and 1 if lhs > rhs.
|
|
*/
|
|
static int
|
|
${i}_cmp( const void *lhs, const void *rhs )
|
|
{
|
|
${i}_context *context_l =
|
|
(${i}_context *)lhs;
|
|
${i}_context *context_r =
|
|
(${i}_context *)rhs;
|
|
|
|
/*
|
|
* check primary key, then secondary. Add your own code if
|
|
* there are more than 2 keys
|
|
*/
|
|
int rc;
|
|
|
|
/*
|
|
* TODO: implement compare. Remove this ifdef code and
|
|
* add your own code here.
|
|
*/
|
|
#ifdef TABLE_CONTAINER_TODO
|
|
snmp_log(LOG_ERR,
|
|
"${i}_compare not implemented! Container order undefined\n" );
|
|
return 0;
|
|
#endif
|
|
|
|
/*
|
|
* EXAMPLE (assuming you want to sort on a name):
|
|
*
|
|
* rc = strcmp( context_l->xxName, context_r->xxName );
|
|
*
|
|
* if(rc)
|
|
* return rc;
|
|
*
|
|
* TODO: fix secondary keys (or delete if there are none)
|
|
*
|
|
* if(context_l->yy < context_r->yy)
|
|
* return -1;
|
|
*
|
|
* return (context_l->yy == context_r->yy) ? 0 : 1;
|
|
*/
|
|
}
|
|
|
|
/************************************************************
|
|
* search tree
|
|
*/
|
|
/** TODO: set additional indexes as parameters */
|
|
${i}_context *
|
|
${i}_get( const char *name, int len )
|
|
{
|
|
${i}_context tmp;
|
|
|
|
/** we should have a custom container */
|
|
netsnmp_assert(cb.container->next != NULL);
|
|
|
|
/*
|
|
* TODO: implement compare. Remove this ifdef code and
|
|
* add your own code here.
|
|
*/
|
|
#ifdef TABLE_CONTAINER_TODO
|
|
snmp_log(LOG_ERR, "${i}_get not implemented!\n" );
|
|
return NULL;
|
|
#endif
|
|
|
|
/*
|
|
* EXAMPLE:
|
|
*
|
|
* if(len > sizeof(tmp.xxName))
|
|
* return NULL;
|
|
*
|
|
* strncpy( tmp.xxName, name, sizeof(tmp.xxName) );
|
|
* tmp.xxName_len = len;
|
|
*
|
|
* return CONTAINER_FIND(cb.container->next, &tmp);
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
|
|
/************************************************************
|
|
* Initializes the $i module
|
|
*/
|
|
void
|
|
init_$i(void)
|
|
{
|
|
initialize_table_$i();
|
|
|
|
/*
|
|
* TODO: perform any startup stuff here, such as
|
|
* populating the table with initial data.
|
|
*
|
|
* ${i}_context * new_row = create_row(index);
|
|
* CONTAINER_INSERT(cb.container,new_row);
|
|
*/
|
|
}
|
|
|
|
@if $i.settable@
|
|
/************************************************************
|
|
* the *_row_copy routine
|
|
*/
|
|
static int ${i}_row_copy(${i}_context * dst,
|
|
${i}_context * src)
|
|
{
|
|
if(!dst||!src)
|
|
return 1;
|
|
|
|
/*
|
|
* copy index, if provided
|
|
*/
|
|
if(dst->index.oids)
|
|
free(dst->index.oids);
|
|
if(snmp_clone_mem( (void*)&dst->index.oids, src->index.oids,
|
|
src->index.len * sizeof(oid) )) {
|
|
dst->index.oids = NULL;
|
|
return 1;
|
|
}
|
|
dst->index.len = src->index.len;
|
|
|
|
|
|
/*
|
|
* copy components into the context structure
|
|
*/
|
|
@if $ext_index != 0@
|
|
/** TODO: add code for external index(s)! */
|
|
@end@
|
|
@foreach $c column@
|
|
@eval $have_type = 0@
|
|
@if "$c.type" eq "ASN_OCTET_STR"@
|
|
@eval $have_type = 1@
|
|
memcpy( dst->$c, src->$c, src->${c}_len );
|
|
dst->${c}_len = src->${c}_len;
|
|
@end@
|
|
@if "$c.type" eq "ASN_OBJECT_ID"@
|
|
@eval $have_type = 1@
|
|
memcpy( dst->$c, src->$c, src->${c}_len );
|
|
dst->${c}_len = src->${c}_len;
|
|
@end@
|
|
@if $have_type == 0@
|
|
dst->$c = src->$c;
|
|
@end@
|
|
|
|
@end@
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* the *_extract_index routine
|
|
*
|
|
* This routine is called when a set request is received for an index
|
|
* that was not found in the table container. Here, we parse the oid
|
|
* in the the individual index components and copy those indexes to the
|
|
* context. Then we make sure the indexes for the new row are valid.
|
|
*/
|
|
int
|
|
${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr )
|
|
{
|
|
/*
|
|
* temporary local storage for extracting oid index
|
|
*
|
|
* extract index uses varbinds (netsnmp_variable_list) to parse
|
|
* the index OID into the individual components for each index part.
|
|
*/
|
|
@if $ext_index != 0@
|
|
/** TODO: add storage for external index(s)! */
|
|
@end@
|
|
@eval $first_idx = ""@
|
|
@foreach $idx index@
|
|
@if "$first_idx" eq ""@
|
|
@eval $first_idx = $idx@
|
|
@end@
|
|
netsnmp_variable_list var_$idx;
|
|
@end@
|
|
int err;
|
|
|
|
/*
|
|
* copy index, if provided
|
|
*/
|
|
if(hdr) {
|
|
netsnmp_assert(ctx->index.oids == NULL);
|
|
if((hdr->len > MAX_OID_LEN) ||
|
|
snmp_clone_mem( (void*)&ctx->index.oids, hdr->oids,
|
|
hdr->len * sizeof(oid) )) {
|
|
return -1;
|
|
}
|
|
ctx->index.len = hdr->len;
|
|
}
|
|
|
|
/*
|
|
* initialize variable that will hold each component of the index.
|
|
* If there are multiple indexes for the table, the variable_lists
|
|
* need to be linked together, in order.
|
|
*/
|
|
@if $ext_index != 0@
|
|
/** TODO: add code for external index(s)! */
|
|
@end@
|
|
@foreach $idx index@
|
|
memset( &var_$idx, 0x00, sizeof(var_$idx) );
|
|
var_${idx}.type = $idx.type; /* type hint for parse_oid_indexes */
|
|
/** TODO: link this index to the next, or NULL for the last one */
|
|
#ifdef TABLE_CONTAINER_TODO
|
|
snmp_log(LOG_ERR, "${i}_extract_index index list not implemented!\n" );
|
|
return 0;
|
|
#else
|
|
var_${idx}.next_variable = &var_XX;
|
|
#endif
|
|
|
|
@end@
|
|
|
|
/*
|
|
* parse the oid into the individual index components
|
|
*/
|
|
err = parse_oid_indexes( hdr->oids, hdr->len, &var_$first_idx );
|
|
if (err == SNMP_ERR_NOERROR) {
|
|
/*
|
|
* copy index components into the context structure
|
|
*/
|
|
@foreach $idx index@
|
|
@eval $found = "external"@
|
|
@foreach $c column@
|
|
@if "$idx" eq "$c"@
|
|
@eval $found = "internal"@
|
|
@end@
|
|
@end@
|
|
@if "$found" eq "external"@
|
|
/** skipping external index $idx */
|
|
@end@
|
|
@if "$found" eq "internal"@
|
|
@eval $have_type = 0@
|
|
@if "$idx.type" eq "ASN_OCTET_STR"@
|
|
@eval $have_type = 1@
|
|
if(var_${idx}.val_len > sizeof(ctx->$idx))
|
|
err = -1;
|
|
else
|
|
memcpy( ctx->$idx, var_${idx}.val.string, var_${idx}.val_len );
|
|
ctx->${idx}_len = var_${idx}.val_len;
|
|
@end@
|
|
@if "$idx.type" eq "ASN_OBJECT_ID"@
|
|
@eval $have_type = 1@
|
|
memcpy( ctx->$idx, var_${idx}.val.string, var_${idx}.val_len );
|
|
ctx->${idx}_len = var_${idx}.val_len;
|
|
@end@
|
|
@if $have_type == 0@
|
|
ctx->$idx = *var_${idx}.val.integer;
|
|
@end@
|
|
@end@
|
|
|
|
@end@
|
|
|
|
@foreach $c index@
|
|
/*
|
|
* TODO: check index for valid values. For EXAMPLE:
|
|
*
|
|
@eval $have_check = 0@
|
|
@if "$c.type" eq "ASN_IPADDRESS"@
|
|
@eval $have_check = 1@
|
|
* if ( XXX_check_ip( *var_${c}.val.integer ) ) {
|
|
@end@
|
|
@if "$c.type" eq "ASN_OBJECT_ID"@
|
|
@eval $have_check = 1@
|
|
* if ( XXX_check_oid( var_${c}.val.objid, var_${c}.val_len /
|
|
sizeof(oid) ) ) {
|
|
@end@
|
|
@if "$c.type" eq "ASN_OCTET_STR"@
|
|
@eval $have_check = 1@
|
|
* if ( XXX_check_value( var_${c}.val.string, XXX ) ) {
|
|
@end@
|
|
@if $have_check != 1@
|
|
* if ( *var_${c}.val.integer != XXX ) {
|
|
@end@
|
|
* err = -1;
|
|
* }
|
|
*/
|
|
@end@
|
|
}
|
|
|
|
/*
|
|
* parsing may have allocated memory. free it.
|
|
*/
|
|
snmp_reset_var_buffers( &var_$first_idx );
|
|
|
|
return err;
|
|
}
|
|
|
|
@if "$rs_name" ne ""@
|
|
/************************************************************
|
|
* the *_can_activate routine is called
|
|
* when a row is changed to determine if all the values
|
|
* set are consistent with the row's rules for a row status
|
|
* of ACTIVE.
|
|
*
|
|
* return 1 if the row could be ACTIVE
|
|
* return 0 if the row is not ready for the ACTIVE state
|
|
*/
|
|
int ${i}_can_activate(${i}_context *undo_ctx,
|
|
${i}_context *row_ctx,
|
|
netsnmp_request_group * rg)
|
|
{
|
|
/*
|
|
* TODO: check for activation requirements here
|
|
*/
|
|
|
|
|
|
/*
|
|
* be optimistic.
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
/************************************************************
|
|
* the *_can_deactivate routine is called when a row that is
|
|
* currently ACTIVE is set to a state other than ACTIVE. If
|
|
* there are conditions in which a row should not be allowed
|
|
* to transition out of the ACTIVE state (such as the row being
|
|
* referred to by another row or table), check for them here.
|
|
*
|
|
* return 1 if the row can be set to a non-ACTIVE state
|
|
* return 0 if the row must remain in the ACTIVE state
|
|
*/
|
|
int ${i}_can_deactivate(${i}_context *undo_ctx,
|
|
${i}_context *row_ctx,
|
|
netsnmp_request_group * rg)
|
|
{
|
|
/*
|
|
* TODO: check for deactivation requirements here
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
@end@
|
|
/************************************************************
|
|
* the *_can_delete routine is called to determine if a row
|
|
* can be deleted.
|
|
*
|
|
* return 1 if the row can be deleted
|
|
* return 0 if the row cannot be deleted
|
|
*/
|
|
int ${i}_can_delete(${i}_context *undo_ctx,
|
|
${i}_context *row_ctx,
|
|
netsnmp_request_group * rg)
|
|
{
|
|
@if "$rs_name" ne ""@
|
|
/*
|
|
* probably shouldn't delete a row that we can't
|
|
* deactivate.
|
|
*/
|
|
if(${i}_can_deactivate(undo_ctx,row_ctx,rg) != 1)
|
|
return 0;
|
|
@end@
|
|
|
|
/*
|
|
* TODO: check for other deletion requirements here
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
@if $i.creatable@
|
|
/************************************************************
|
|
* the *_create_row routine is called by the table handler
|
|
* to create a new row for a given index. If you need more
|
|
* information (such as column values) to make a decision
|
|
* on creating rows, you must create an initial row here
|
|
* (to hold the column values), and you can examine the
|
|
* situation in more detail in the *_set_reserve1 or later
|
|
* states of set processing. Simple check for a NULL undo_ctx
|
|
* in those states and do detailed creation checking there.
|
|
*
|
|
* returns a newly allocated ${i}_context
|
|
* structure if the specified indexes are not illegal
|
|
* returns NULL for errors or illegal index values.
|
|
*/
|
|
${i}_context *
|
|
${i}_create_row( netsnmp_index* hdr)
|
|
{
|
|
${i}_context * ctx =
|
|
SNMP_MALLOC_TYPEDEF(${i}_context);
|
|
if(!ctx)
|
|
return NULL;
|
|
|
|
/*
|
|
* TODO: check indexes, if necessary.
|
|
*/
|
|
if(${i}_extract_index( ctx, hdr )) {
|
|
if (NULL != ctx->index.oids)
|
|
free(ctx->index.oids);
|
|
free(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
/* netsnmp_mutex_init(ctx->lock);
|
|
netsnmp_mutex_lock(ctx->lock); */
|
|
|
|
/*
|
|
* TODO: initialize any default values here. This is also
|
|
* first place you really should allocate any memory for
|
|
* yourself to use. If you allocated memory earlier,
|
|
* make sure you free it for earlier error cases!
|
|
*/
|
|
/**
|
|
@foreach $c column@
|
|
@if $c.settable@
|
|
ctx->$c = 0;
|
|
@end@
|
|
@end@
|
|
*/
|
|
|
|
return ctx;
|
|
}
|
|
@end@
|
|
|
|
/************************************************************
|
|
* the *_duplicate row routine
|
|
*/
|
|
${i}_context *
|
|
${i}_duplicate_row( ${i}_context * row_ctx)
|
|
{
|
|
${i}_context * dup;
|
|
|
|
if(!row_ctx)
|
|
return NULL;
|
|
|
|
dup = SNMP_MALLOC_TYPEDEF(${i}_context);
|
|
if(!dup)
|
|
return NULL;
|
|
|
|
if(${i}_row_copy(dup,row_ctx)) {
|
|
free(dup);
|
|
dup = NULL;
|
|
}
|
|
|
|
return dup;
|
|
}
|
|
|
|
/************************************************************
|
|
* the *_delete_row method is called to delete a row.
|
|
*/
|
|
netsnmp_index * ${i}_delete_row( ${i}_context * ctx )
|
|
{
|
|
/* netsnmp_mutex_destroy(ctx->lock); */
|
|
|
|
if(ctx->index.oids)
|
|
free(ctx->index.oids);
|
|
|
|
/*
|
|
* TODO: release any memory you allocated here...
|
|
*/
|
|
|
|
/*
|
|
* release header
|
|
*/
|
|
free( ctx );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* RESERVE is used to check the syntax of all the variables
|
|
* provided, that the values being set are sensible and consistent,
|
|
* and to allocate any resources required for performing the SET.
|
|
* After this stage, the expectation is that the set ought to
|
|
* succeed, though this is not guaranteed. (In fact, with the UCD
|
|
* agent, this is done in two passes - RESERVE1, and
|
|
* RESERVE2, to allow for dependancies between variables).
|
|
*
|
|
* BEFORE calling this routine, the agent will call duplicate_row
|
|
* to create a copy of the row (unless this is a new row; i.e.
|
|
* row_created == 1).
|
|
*
|
|
* next state -> SET_RESERVE2 || SET_FREE
|
|
*/
|
|
void ${i}_set_reserve1( netsnmp_request_group *rg )
|
|
{
|
|
${i}_context *row_ctx =
|
|
(${i}_context *)rg->existing_row;
|
|
${i}_context *undo_ctx =
|
|
(${i}_context *)rg->undo_info;
|
|
netsnmp_variable_list *var;
|
|
netsnmp_request_group_item *current;
|
|
int rc;
|
|
|
|
@if "$st_name" ne ""@
|
|
/*
|
|
* Block all attempts to modify a readOnly row
|
|
*/
|
|
if( row_ctx && (row_ctx->$st_name == SNMP_STORAGE_READONLY) ) {
|
|
netsnmp_set_mode_request_error(MODE_SET_BEGIN, rg->list->ri,
|
|
SNMP_ERR_NOTWRITABLE);
|
|
return;
|
|
}
|
|
@end@
|
|
|
|
/*
|
|
* TODO: loop through columns, check syntax and lengths. For
|
|
* columns which have no dependencies, you could also move
|
|
* the value/range checking here to attempt to catch error
|
|
* cases as early as possible.
|
|
*/
|
|
for( current = rg->list; current; current = current->next ) {
|
|
|
|
var = current->ri->requestvb;
|
|
rc = SNMP_ERR_NOERROR;
|
|
|
|
switch(current->tri->colnum) {
|
|
|
|
@foreach $c column@
|
|
@if $c.settable@
|
|
case COLUMN_$c.uc:
|
|
/** $c.syntax = $c.type */
|
|
@if $c.needlength@
|
|
/* or possibly 'netsnmp_check_vb_type_and_size' */
|
|
rc = netsnmp_check_vb_type_and_max_size(var, $c.type,
|
|
sizeof(row_ctx->$c));
|
|
@else@
|
|
/* or possibly 'netsnmp_check_vb_int_range' */
|
|
rc = netsnmp_check_vb_int( var );
|
|
@end@
|
|
break;
|
|
|
|
@end@
|
|
@end@
|
|
default: /** We shouldn't get here */
|
|
rc = SNMP_ERR_GENERR;
|
|
snmp_log(LOG_ERR, "unknown column in "
|
|
"${i}_set_reserve1\n");
|
|
}
|
|
|
|
if (rc)
|
|
netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc );
|
|
rg->status = SNMP_MAX( rg->status, current->ri->status );
|
|
}
|
|
|
|
/*
|
|
* done with all the columns. Could check row related
|
|
* requirements here.
|
|
*/
|
|
}
|
|
|
|
void ${i}_set_reserve2( netsnmp_request_group *rg )
|
|
{
|
|
${i}_context *row_ctx = (${i}_context *)rg->existing_row;
|
|
${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
|
|
netsnmp_request_group_item *current;
|
|
netsnmp_variable_list *var;
|
|
int rc;
|
|
|
|
rg->rg_void = rg->list->ri;
|
|
|
|
/*
|
|
* TODO: loop through columns, check for valid
|
|
* values and any range constraints.
|
|
*/
|
|
for( current = rg->list; current; current = current->next ) {
|
|
|
|
var = current->ri->requestvb;
|
|
rc = SNMP_ERR_NOERROR;
|
|
|
|
switch(current->tri->colnum) {
|
|
|
|
@foreach $c column@
|
|
@if $c.settable@
|
|
case COLUMN_$c.uc:
|
|
/** $c.syntax = $c.type */
|
|
@eval $have_check = 0@
|
|
@if "$c" eq "$st_name"@
|
|
@eval $have_check = 1@
|
|
rc = netsnmp_check_vb_storagetype(current->ri->requestvb,
|
|
undo_ctx ?
|
|
undo_ctx->$c:0);
|
|
@end@
|
|
@if "$c" eq "$rs_name"@
|
|
@eval $have_check = 1@
|
|
rc = netsnmp_check_vb_rowstatus(current->ri->requestvb,
|
|
undo_ctx ?
|
|
undo_ctx->$c:0);
|
|
rg->rg_void = current->ri;
|
|
@end@
|
|
@if "$c.syntax" eq "TruthValue"@
|
|
@eval $have_check = 1@
|
|
rc = netsnmp_check_vb_truthvalue(current->ri->requestvb);
|
|
@end@
|
|
@if $have_check == 0@
|
|
/*
|
|
* TODO: routine to check valid values
|
|
*
|
|
* EXAMPLE:
|
|
*
|
|
@if "$c.type" eq "ASN_IPADDRESS"@
|
|
@eval $have_check = 1@
|
|
* if ( XXX_check_ip( *var->val.integer ) ) {
|
|
@end@
|
|
@if "$c.type" eq "ASN_OBJECT_ID"@
|
|
@eval $have_check = 1@
|
|
* if ( XXX_check_oid( var ) ) {
|
|
@end@
|
|
@if "$c.type" eq "ASN_OCTET_STR"@
|
|
@eval $have_check = 1@
|
|
* if ( XXX_check_value( var->val.string, XXX ) ) {
|
|
@end@
|
|
@if $have_check != 1@
|
|
* if ( *var->val.integer != XXX ) {
|
|
@end@
|
|
* rc = SNMP_ERR_INCONSISTENTVALUE;
|
|
* rc = SNMP_ERR_BADVALUE;
|
|
* }
|
|
*/
|
|
@end@
|
|
break;
|
|
|
|
@end@
|
|
@end@
|
|
default: /** We shouldn't get here */
|
|
netsnmp_assert(0); /** why wasn't this caught in reserve1? */
|
|
}
|
|
|
|
if (rc)
|
|
netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc);
|
|
}
|
|
|
|
/*
|
|
* done with all the columns. Could check row related
|
|
* requirements here.
|
|
*/
|
|
}
|
|
|
|
/************************************************************
|
|
* Assuming that the RESERVE phases were successful, the next
|
|
* stage is indicated by the action value ACTION. This is used
|
|
* to actually implement the set operation. However, this must
|
|
* either be done into temporary (persistent) storage, or the
|
|
* previous value stored similarly, in case any of the subsequent
|
|
* ACTION calls fail.
|
|
*
|
|
* In your case, changes should be made to row_ctx. A copy of
|
|
* the original row is in undo_ctx.
|
|
*/
|
|
void ${i}_set_action( netsnmp_request_group *rg )
|
|
{
|
|
netsnmp_variable_list *var;
|
|
${i}_context *row_ctx = (${i}_context *)rg->existing_row;
|
|
${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
|
|
netsnmp_request_group_item *current;
|
|
|
|
@if "$rs_name" ne ""@
|
|
int row_err = 0;
|
|
@end@
|
|
|
|
/*
|
|
* TODO: loop through columns, copy varbind values
|
|
* to context structure for the row.
|
|
*/
|
|
for( current = rg->list; current; current = current->next ) {
|
|
|
|
var = current->ri->requestvb;
|
|
|
|
switch(current->tri->colnum) {
|
|
|
|
@foreach $c column@
|
|
@if $c.settable@
|
|
case COLUMN_$c.uc:
|
|
/** $c.syntax = $c.type */
|
|
@eval $have_type = 0@
|
|
@if "$c.type" eq "ASN_OCTET_STR"@
|
|
@eval $have_type = 1@
|
|
memcpy(row_ctx->$c,var->val.string,var->val_len);
|
|
row_ctx->${c}_len = var->val_len;
|
|
@end@
|
|
@if "$c.type" eq "ASN_OBJECT_ID"@
|
|
@eval $have_type = 1@
|
|
memcpy(row_ctx->$c,var->val.objid,var->val_len);
|
|
row_ctx->${c}_len = var->val_len;
|
|
@end@
|
|
@if $have_type == 0@
|
|
row_ctx->$c = *var->val.integer;
|
|
@end@
|
|
break;
|
|
|
|
@end@
|
|
@end@
|
|
default: /** We shouldn't get here */
|
|
netsnmp_assert(0); /** why wasn't this caught in reserve1? */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* done with all the columns. Could check row related
|
|
* requirements here.
|
|
*/
|
|
@if "$rs_name" ne ""@
|
|
#ifndef ${i}_CAN_MODIFY_ACTIVE_ROW
|
|
if( undo_ctx && RS_IS_ACTIVE(undo_ctx->$rs_name) &&
|
|
row_ctx && RS_IS_ACTIVE(row_ctx->$rs_name) ) {
|
|
row_err = 1;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* check activation/deactivation
|
|
*/
|
|
row_err = netsnmp_table_array_check_row_status(&cb, rg,
|
|
row_ctx ? &row_ctx->$rs_name : NULL,
|
|
undo_ctx ? &undo_ctx->$rs_name : NULL);
|
|
if(row_err) {
|
|
netsnmp_set_mode_request_error(MODE_SET_BEGIN,
|
|
(netsnmp_request_info*)rg->rg_void,
|
|
row_err);
|
|
return;
|
|
}
|
|
|
|
@end@
|
|
/*
|
|
* TODO: if you have dependencies on other tables, this would be
|
|
* a good place to check those, too.
|
|
*/
|
|
}
|
|
|
|
/************************************************************
|
|
* Only once the ACTION phase has completed successfully, can
|
|
* the final COMMIT phase be run. This is used to complete any
|
|
* writes that were done into temporary storage, and then release
|
|
* any allocated resources. Note that all the code in this phase
|
|
* should be "safe" code that cannot possibly fail (cue
|
|
* hysterical laughter). The whole intent of the ACTION/COMMIT
|
|
* division is that all of the fallible code should be done in
|
|
* the ACTION phase, so that it can be backed out if necessary.
|
|
*
|
|
* BEFORE calling this routine, the agent will update the
|
|
* container (inserting a row if row_created == 1, or removing
|
|
* the row if row_deleted == 1).
|
|
*
|
|
* AFTER calling this routine, the agent will delete the
|
|
* undo_info.
|
|
*/
|
|
void ${i}_set_commit( netsnmp_request_group *rg )
|
|
{
|
|
netsnmp_variable_list *var;
|
|
${i}_context *row_ctx = (${i}_context *)rg->existing_row;
|
|
${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
|
|
netsnmp_request_group_item *current;
|
|
|
|
/*
|
|
* loop through columns
|
|
*/
|
|
for( current = rg->list; current; current = current->next ) {
|
|
|
|
var = current->ri->requestvb;
|
|
|
|
switch(current->tri->colnum) {
|
|
|
|
@foreach $c column@
|
|
@if $c.settable@
|
|
case COLUMN_$c.uc:
|
|
/** $c.syntax = $c.type */
|
|
break;
|
|
|
|
@end@
|
|
@end@
|
|
default: /** We shouldn't get here */
|
|
netsnmp_assert(0); /** why wasn't this caught in reserve1? */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* done with all the columns. Could check row related
|
|
* requirements here.
|
|
*/
|
|
}
|
|
|
|
/************************************************************
|
|
* If either of the RESERVE calls fail, the write routines
|
|
* are called again with the FREE action, to release any resources
|
|
* that have been allocated. The agent will then return a failure
|
|
* response to the requesting application.
|
|
*
|
|
* AFTER calling this routine, the agent will delete undo_info.
|
|
*/
|
|
void ${i}_set_free( netsnmp_request_group *rg )
|
|
{
|
|
netsnmp_variable_list *var;
|
|
${i}_context *row_ctx = (${i}_context *)rg->existing_row;
|
|
${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
|
|
netsnmp_request_group_item *current;
|
|
|
|
/*
|
|
* loop through columns
|
|
*/
|
|
for( current = rg->list; current; current = current->next ) {
|
|
|
|
var = current->ri->requestvb;
|
|
|
|
switch(current->tri->colnum) {
|
|
|
|
@foreach $c column@
|
|
@if $c.settable@
|
|
case COLUMN_$c.uc:
|
|
/** $c.syntax = $c.type */
|
|
break;
|
|
|
|
@end@
|
|
@end@
|
|
default: /** We shouldn't get here */
|
|
/** should have been logged in reserve1 */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* done with all the columns. Could check row related
|
|
* requirements here.
|
|
*/
|
|
}
|
|
|
|
/************************************************************
|
|
* If the ACTION phase does fail (for example due to an apparently
|
|
* valid, but unacceptable value, or an unforeseen problem), then
|
|
* the list of write routines are called again, with the UNDO
|
|
* action. This requires the routine to reset the value that was
|
|
* changed to its previous value (assuming it was actually changed),
|
|
* and then to release any resources that had been allocated. As
|
|
* with the FREE phase, the agent will then return an indication
|
|
* of the error to the requesting application.
|
|
*
|
|
* BEFORE calling this routine, the agent will update the container
|
|
* (remove any newly inserted row, re-insert any removed row).
|
|
*
|
|
* AFTER calling this routing, the agent will call row_copy
|
|
* to restore the data in existing_row from the date in undo_info.
|
|
* Then undo_info will be deleted (or existing row, if row_created
|
|
* == 1).
|
|
*/
|
|
void ${i}_set_undo( netsnmp_request_group *rg )
|
|
{
|
|
netsnmp_variable_list *var;
|
|
${i}_context *row_ctx = (${i}_context *)rg->existing_row;
|
|
${i}_context *undo_ctx = (${i}_context *)rg->undo_info;
|
|
netsnmp_request_group_item *current;
|
|
|
|
/*
|
|
* loop through columns
|
|
*/
|
|
for( current = rg->list; current; current = current->next ) {
|
|
|
|
var = current->ri->requestvb;
|
|
|
|
switch(current->tri->colnum) {
|
|
|
|
@foreach $c column@
|
|
@if $c.settable@
|
|
case COLUMN_$c.uc:
|
|
/** $c.syntax = $c.type */
|
|
break;
|
|
|
|
@end@
|
|
@end@
|
|
default: /** We shouldn't get here */
|
|
netsnmp_assert(0); /** why wasn't this caught in reserve1? */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* done with all the columns. Could check row related
|
|
* requirements here.
|
|
*/
|
|
}
|
|
|
|
@end@
|
|
|
|
|
|
/************************************************************
|
|
*
|
|
* Initialize the $i table by defining its contents and how it's structured
|
|
*/
|
|
void
|
|
initialize_table_$i(void)
|
|
{
|
|
netsnmp_table_registration_info *table_info;
|
|
|
|
if(my_handler) {
|
|
snmp_log(LOG_ERR, "initialize_table_${i}_handler called again\n");
|
|
return;
|
|
}
|
|
|
|
memset(&cb, 0x00, sizeof(cb));
|
|
|
|
/** create the table structure itself */
|
|
table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
|
|
|
|
my_handler = netsnmp_create_handler_registration("$i",
|
|
netsnmp_table_array_helper_handler,
|
|
${i}_oid,
|
|
${i}_oid_len,
|
|
@if $i.settable@
|
|
HANDLER_CAN_RWRITE
|
|
@else@
|
|
HANDLER_CAN_RONLY
|
|
@end@
|
|
);
|
|
|
|
if (!my_handler || !table_info) {
|
|
snmp_log(LOG_ERR, "malloc failed in "
|
|
"initialize_table_${i}_handler\n");
|
|
return; /** mallocs failed */
|
|
}
|
|
|
|
/***************************************************
|
|
* Setting up the table's definition
|
|
*/
|
|
/*
|
|
* TODO: add any external indexes here.
|
|
*/
|
|
@if $ext_index != 0@
|
|
/** TODO: add code for external index(s)! */
|
|
@end@
|
|
|
|
/*
|
|
* internal indexes
|
|
*/
|
|
@foreach $idx index@
|
|
/** index: $idx */
|
|
netsnmp_table_helper_add_index(table_info, $idx.type);
|
|
@end@
|
|
|
|
table_info->min_column = ${i}_COL_MIN;
|
|
table_info->max_column = ${i}_COL_MAX;
|
|
|
|
/***************************************************
|
|
* registering the table with the master agent
|
|
*/
|
|
cb.get_value = ${i}_get_value;
|
|
cb.container = netsnmp_container_find("${i}_primary:"
|
|
"${i}:"
|
|
"table_container");
|
|
#ifdef ${i}_CUSTOM_SORT
|
|
netsnmp_container_add_index(cb.container,
|
|
netsnmp_container_find("${i}_custom:"
|
|
"${i}:"
|
|
"table_container"));
|
|
cb.container->next->compare = ${i}_cmp;
|
|
#endif
|
|
@if $i.settable@
|
|
cb.can_set = 1;
|
|
@if $i.creatable@
|
|
cb.create_row = (UserRowMethod*)${i}_create_row;
|
|
@end@
|
|
cb.duplicate_row = (UserRowMethod*)${i}_duplicate_row;
|
|
cb.delete_row = (UserRowMethod*)${i}_delete_row;
|
|
cb.row_copy = (Netsnmp_User_Row_Operation *)${i}_row_copy;
|
|
|
|
@if "$rs_name" ne ""@
|
|
cb.can_activate = (Netsnmp_User_Row_Action *)${i}_can_activate;
|
|
cb.can_deactivate = (Netsnmp_User_Row_Action *)${i}_can_deactivate;
|
|
@end@
|
|
cb.can_delete = (Netsnmp_User_Row_Action *)${i}_can_delete;
|
|
|
|
cb.set_reserve1 = ${i}_set_reserve1;
|
|
cb.set_reserve2 = ${i}_set_reserve2;
|
|
cb.set_action = ${i}_set_action;
|
|
cb.set_commit = ${i}_set_commit;
|
|
cb.set_free = ${i}_set_free;
|
|
cb.set_undo = ${i}_set_undo;
|
|
@end@
|
|
DEBUGMSGTL(("initialize_table_$i",
|
|
"Registering table $i "
|
|
"as a table array\n"));
|
|
netsnmp_table_container_register(my_handler, table_info, &cb,
|
|
cb.container, 1);
|
|
}
|
|
|
|
/************************************************************
|
|
* ${i}_get_value
|
|
*
|
|
* This routine is called for get requests to copy the data
|
|
* from the context to the varbind for the request. If the
|
|
* context has been properly maintained, you don't need to
|
|
* change in code in this fuction.
|
|
*/
|
|
int ${i}_get_value(
|
|
netsnmp_request_info *request,
|
|
netsnmp_index *item,
|
|
netsnmp_table_request_info *table_info )
|
|
{
|
|
netsnmp_variable_list *var = request->requestvb;
|
|
${i}_context *context = (${i}_context *)item;
|
|
|
|
switch(table_info->colnum) {
|
|
|
|
@foreach $c column@
|
|
@if $c.readable@
|
|
@eval $have_type = 0@
|
|
case COLUMN_$c.uc:
|
|
/** $c.syntax = $c.type */
|
|
@if "$c.type" eq "ASN_OBJECT_ID"@
|
|
@eval $have_type = 1@
|
|
snmp_set_var_typed_value(var, $c.type,
|
|
(char*)&context->$c,
|
|
context->${c}_len );
|
|
@end@
|
|
@if "$c.type" eq "ASN_OCTET_STR"@
|
|
@eval $have_type = 1@
|
|
snmp_set_var_typed_value(var, $c.type,
|
|
(char*)&context->$c,
|
|
context->${c}_len );
|
|
@end@
|
|
@if $have_type == 0@
|
|
snmp_set_var_typed_value(var, $c.type,
|
|
(char*)&context->$c,
|
|
sizeof(context->$c) );
|
|
@end@
|
|
break;
|
|
|
|
@end@
|
|
@end@
|
|
default: /** We shouldn't get here */
|
|
snmp_log(LOG_ERR, "unknown column in "
|
|
"${i}_get_value\n");
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
/************************************************************
|
|
* ${i}_get_by_idx
|
|
*/
|
|
const ${i}_context *
|
|
${i}_get_by_idx(netsnmp_index * hdr)
|
|
{
|
|
return (const ${i}_context *)
|
|
CONTAINER_FIND(cb.container, hdr );
|
|
}
|
|
|
|
|
|
@end@
|