blob: 239eeb382d3489cd47af4a7c0c864c9f5bd17af2 [file] [log] [blame]
#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/agent/watcher.h>
#include <net-snmp/agent/agent_callbacks.h>
#include "agent/extend.h"
#include "utilities/execute.h"
#include "struct.h"
#ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
#include "util_funcs/header_simple_table.h"
#include "mibdefs.h"
#define SHELLCOMMAND 3
#endif
oid ns_extend_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2 };
oid extend_count_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2, 1 };
oid extend_config_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2, 2 };
oid extend_out1_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2, 3 };
oid extend_out2_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2, 4 };
typedef struct extend_registration_block_s {
netsnmp_table_data *dinfo;
oid *root_oid;
size_t oid_len;
long num_entries;
netsnmp_extend *ehead;
netsnmp_handler_registration *reg[3];
struct extend_registration_block_s *next;
} extend_registration_block;
extend_registration_block *ereg_head = NULL;
#ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
typedef struct netsnmp_old_extend_s {
unsigned int idx;
netsnmp_extend *exec_entry;
netsnmp_extend *efix_entry;
} netsnmp_old_extend;
unsigned int num_compatability_entries = 0;
unsigned int max_compatability_entries = 50;
netsnmp_old_extend *compatability_entries;
char *cmdlinebuf;
size_t cmdlinesize;
WriteMethod fixExec2Error;
FindVarMethod var_extensible_old;
oid old_extensible_variables_oid[] = { NETSNMP_UCDAVIS_MIB, NETSNMP_SHELLMIBNUM, 1 };
struct variable2 old_extensible_variables[] = {
{MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_extensible_old, 1, {MIBINDEX}},
{ERRORNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_old, 1, {ERRORNAME}},
{SHELLCOMMAND, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_old, 1, {SHELLCOMMAND}},
{ERRORFLAG, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_extensible_old, 1, {ERRORFLAG}},
{ERRORMSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_old, 1, {ERRORMSG}},
{ERRORFIX, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
var_extensible_old, 1, {ERRORFIX}},
{ERRORFIXCMD, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_old, 1, {ERRORFIXCMD}}
};
#endif
/*************************
*
* Main initialisation routine
*
*************************/
extend_registration_block *
_find_extension_block( oid *name, size_t name_len )
{
extend_registration_block *eptr;
size_t len;
for ( eptr=ereg_head; eptr; eptr=eptr->next ) {
len = SNMP_MIN(name_len, eptr->oid_len);
if (!snmp_oid_compare( name, len, eptr->root_oid, eptr->oid_len))
return eptr;
}
return NULL;
}
extend_registration_block *
_register_extend( oid *base, size_t len )
{
extend_registration_block *eptr;
oid oid_buf[MAX_OID_LEN];
netsnmp_table_data *dinfo;
netsnmp_table_registration_info *tinfo;
netsnmp_watcher_info *winfo;
netsnmp_handler_registration *reg;
for ( eptr=ereg_head; eptr; eptr=eptr->next ) {
if (!snmp_oid_compare( base, len, eptr->root_oid, eptr->oid_len))
return eptr;
}
if (!eptr) {
eptr = SNMP_MALLOC_TYPEDEF( extend_registration_block );
eptr->root_oid = snmp_duplicate_objid( base, len );
eptr->oid_len = len;
eptr->num_entries = 0;
eptr->ehead = NULL;
eptr->dinfo = netsnmp_create_table_data( "nsExtendTable" );
eptr->next = ereg_head;
ereg_head = eptr;
}
dinfo = eptr->dinfo;
memcpy( oid_buf, base, len*sizeof(oid) );
/*
* Register the configuration table
*/
tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
tinfo->min_column = COLUMN_EXTCFG_FIRST_COLUMN;
tinfo->max_column = COLUMN_EXTCFG_LAST_COLUMN;
oid_buf[len] = 2;
reg = netsnmp_create_handler_registration(
"nsExtendConfigTable", handle_nsExtendConfigTable,
oid_buf, len+1, HANDLER_CAN_RWRITE);
netsnmp_register_table_data( reg, dinfo, tinfo );
eptr->reg[0] = reg;
/*
* Register the main output table
* using the same table_data handle.
* This is sufficient to link the two tables,
* and implement the AUGMENTS behaviour
*/
tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
tinfo->min_column = COLUMN_EXTOUT1_FIRST_COLUMN;
tinfo->max_column = COLUMN_EXTOUT1_LAST_COLUMN;
oid_buf[len] = 3;
reg = netsnmp_create_handler_registration(
"nsExtendOut1Table", handle_nsExtendOutput1Table,
oid_buf, len+1, HANDLER_CAN_RONLY);
netsnmp_register_table_data( reg, dinfo, tinfo );
eptr->reg[1] = reg;
/*
* Register the multi-line output table
* using a simple table helper.
* This handles extracting the indexes from
* the request OID, but leaves most of
* the work to our handler routine.
* Still, it was nice while it lasted...
*/
tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, ASN_INTEGER, 0 );
tinfo->min_column = COLUMN_EXTOUT2_FIRST_COLUMN;
tinfo->max_column = COLUMN_EXTOUT2_LAST_COLUMN;
oid_buf[len] = 4;
reg = netsnmp_create_handler_registration(
"nsExtendOut2Table", handle_nsExtendOutput2Table,
oid_buf, len+1, HANDLER_CAN_RONLY);
netsnmp_register_table( reg, tinfo );
eptr->reg[2] = reg;
/*
* Register a watched scalar to keep track of the number of entries
*/
oid_buf[len] = 1;
reg = netsnmp_create_handler_registration(
"nsExtendNumEntries", NULL,
oid_buf, len+1, HANDLER_CAN_RONLY);
winfo = netsnmp_create_watcher_info(
&(eptr->num_entries), sizeof(eptr->num_entries),
ASN_INTEGER, WATCHER_FIXED_SIZE);
netsnmp_register_watched_scalar( reg, winfo );
return eptr;
}
int
extend_clear_callback(int majorID, int minorID,
void *serverarg, void *clientarg)
{
extend_registration_block *eptr, *enext = NULL;
for ( eptr=ereg_head; eptr; eptr=enext ) {
enext=eptr->next;
netsnmp_unregister_handler( eptr->reg[0] );
netsnmp_unregister_handler( eptr->reg[1] );
netsnmp_unregister_handler( eptr->reg[2] );
SNMP_FREE(eptr);
}
ereg_head = NULL;
return 0;
}
void init_extend( void )
{
snmpd_register_config_handler("extend", extend_parse_config, NULL, NULL);
snmpd_register_config_handler("extend-sh", extend_parse_config, NULL, NULL);
snmpd_register_config_handler("extendfix", extend_parse_config, NULL, NULL);
snmpd_register_config_handler("exec2", extend_parse_config, NULL, NULL);
snmpd_register_config_handler("sh2", extend_parse_config, NULL, NULL);
snmpd_register_config_handler("execFix2", extend_parse_config, NULL, NULL);
(void)_register_extend( ns_extend_oid, OID_LENGTH(ns_extend_oid));
#ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
snmpd_register_config_handler("exec", extend_parse_config, NULL, NULL);
snmpd_register_config_handler("sh", extend_parse_config, NULL, NULL);
snmpd_register_config_handler("execFix", extend_parse_config, NULL, NULL);
compatability_entries = (netsnmp_old_extend *)
calloc( max_compatability_entries, sizeof(netsnmp_old_extend));
REGISTER_MIB("ucd-extensible", old_extensible_variables,
variable2, old_extensible_variables_oid);
#endif
snmp_register_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_PRE_UPDATE_CONFIG,
extend_clear_callback, NULL);
}
/*************************
*
* Cached-data hooks
* see 'cache_handler' helper
*
*************************/
int
extend_load_cache(netsnmp_cache *cache, void *magic)
{
#ifndef USING_UTILITIES_EXECUTE_MODULE
NETSNMP_LOGONCE((LOG_WARNING,"support for run_exec_command not available\n"));
return -1;
#else
int out_len = 1024*100;
char out_buf[ 1024*100 ];
int cmd_len = 255*2 + 2; /* 2 * DisplayStrings */
char cmd_buf[ 255*2 + 2 ];
int ret;
char *cp;
char *line_buf[ 1024 ];
netsnmp_extend *extension = (netsnmp_extend *)magic;
if (!magic)
return -1;
DEBUGMSGTL(( "nsExtendTable:cache", "load %s", extension->token ));
if ( extension->args )
snprintf( cmd_buf, cmd_len, "%s %s", extension->command, extension->args );
else
snprintf( cmd_buf, cmd_len, "%s", extension->command );
if ( extension->flags & NS_EXTEND_FLAGS_SHELL )
ret = run_shell_command( cmd_buf, extension->input, out_buf, &out_len);
else
ret = run_exec_command( cmd_buf, extension->input, out_buf, &out_len);
DEBUGMSG(( "nsExtendTable:cache", ": %s : %d\n", cmd_buf, ret));
if (ret >= 0) {
if (out_buf[ out_len-1 ] == '\n')
out_buf[ --out_len ] = '\0'; /* Stomp on trailing newline */
extension->output = strdup( out_buf );
extension->out_len = out_len;
/*
* Now we need to pick the output apart into separate lines.
* Start by counting how many lines we've got, and keeping
* track of where each line starts in a static buffer
*/
extension->numlines = 1;
line_buf[ 0 ] = extension->output;
for (cp=extension->output; *cp; cp++) {
if (*cp == '\n') {
line_buf[ extension->numlines++ ] = cp+1;
}
}
if ( extension->numlines > 1 ) {
extension->lines = (char**)calloc( sizeof(char *), extension->numlines );
memcpy( extension->lines, line_buf,
sizeof(char *) * extension->numlines );
} else {
extension->lines = &extension->output;
}
}
extension->result = ret;
return ret;
#endif /* !defined(USING_UTILITIES_EXECUTE_MODULE) */
}
void
extend_free_cache(netsnmp_cache *cache, void *magic)
{
netsnmp_extend *extension = (netsnmp_extend *)magic;
if (!magic)
return;
DEBUGMSGTL(( "nsExtendTable:cache", "free %s\n", extension->token ));
if (extension->output) {
SNMP_FREE(extension->output);
extension->output = NULL;
}
if ( extension->numlines > 1 ) {
SNMP_FREE(extension->lines);
}
extension->lines = NULL;
extension->out_len = 0;
extension->numlines = 0;
}
/*************************
*
* Utility routines for setting up a new entry
* (either via SET requests, or the config file)
*
*************************/
void
_free_extension( netsnmp_extend *extension, extend_registration_block *ereg )
{
netsnmp_extend *eptr = NULL;
netsnmp_extend *eprev = NULL;
if (!extension)
return;
if (ereg) {
/* Unlink from 'ehead' list */
for (eptr=ereg->ehead; eptr; eptr=eptr->next) {
if (eptr == extension)
break;
eprev = eptr;
}
if (!eptr) {
snmp_log(LOG_ERR,
"extend: fell off end of list before finding extension\n");
return;
}
if (eprev)
eprev->next = eptr->next;
else
ereg->ehead = eptr->next;
netsnmp_table_data_remove_and_delete_row( ereg->dinfo, extension->row);
}
SNMP_FREE( extension->token );
SNMP_FREE( extension->cache );
SNMP_FREE( extension->command );
SNMP_FREE( extension->args );
SNMP_FREE( extension->input );
SNMP_FREE( extension );
return;
}
netsnmp_extend *
_new_extension( char *exec_name, int exec_flags, extend_registration_block *ereg )
{
netsnmp_extend *extension;
netsnmp_table_row *row;
netsnmp_extend *eptr1, *eptr2;
netsnmp_table_data *dinfo = ereg->dinfo;
if (!exec_name)
return NULL;
extension = SNMP_MALLOC_TYPEDEF( netsnmp_extend );
if (!extension)
return NULL;
extension->token = strdup( exec_name );
extension->flags = exec_flags;
extension->cache = netsnmp_cache_create( 0, extend_load_cache,
extend_free_cache, NULL, 0 );
if (extension->cache)
extension->cache->magic = extension;
row = netsnmp_create_table_data_row();
if (!row || !extension->cache) {
_free_extension( extension, ereg );
SNMP_FREE( row );
return NULL;
}
row->data = (void *)extension;
extension->row = row;
netsnmp_table_row_add_index( row, ASN_OCTET_STR,
exec_name, strlen(exec_name));
if ( netsnmp_table_data_add_row( dinfo, row) != SNMPERR_SUCCESS ) {
/* _free_extension( extension, ereg ); */
SNMP_FREE( extension ); /* Probably not sufficient */
SNMP_FREE( row );
return NULL;
}
ereg->num_entries++;
/*
* Now add this structure to a private linked list.
* We don't need this for the main tables - the
* 'table_data' helper will take care of those.
* But it's probably easier to handle the multi-line
* output table ourselves, for which we need access
* to the underlying data.
* So we'll keep a list internally as well.
*/
for ( eptr1 = ereg->ehead, eptr2 = NULL;
eptr1;
eptr2 = eptr1, eptr1 = eptr1->next ) {
if (strlen( eptr1->token ) > strlen( exec_name ))
break;
if (strlen( eptr1->token ) == strlen( exec_name ) &&
strcmp( eptr1->token, exec_name ) > 0 )
break;
}
if ( eptr2 )
eptr2->next = extension;
else
ereg->ehead = extension;
extension->next = eptr1;
return extension;
}
void
extend_parse_config(const char *token, char *cptr)
{
netsnmp_extend *extension;
char exec_name[STRMAX];
char exec_name2[STRMAX]; /* For use with UCD execFix directive */
char exec_command[STRMAX];
oid oid_buf[MAX_OID_LEN];
size_t oid_len;
extend_registration_block *eptr;
int flags;
cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
if ( *exec_name == '.' ) {
oid_len = MAX_OID_LEN - 2;
if (0 == read_objid( exec_name, oid_buf, &oid_len )) {
config_perror("ERROR: Unrecognised OID" );
return;
}
cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
if (!strcmp( token, "sh" ) ||
!strcmp( token, "exec" )) {
config_perror("ERROR: This output format has been deprecated - Please use the 'extend' directive instead" );
return;
}
} else {
memcpy( oid_buf, ns_extend_oid, sizeof(ns_extend_oid));
oid_len = OID_LENGTH(ns_extend_oid);
}
cptr = copy_nword(cptr, exec_command, sizeof(exec_command));
/* XXX - check 'exec_command' exists & is executable */
flags = (NS_EXTEND_FLAGS_ACTIVE | NS_EXTEND_FLAGS_CONFIG);
if (!strcmp( token, "sh" ) ||
!strcmp( token, "extend-sh" ) ||
!strcmp( token, "sh2" ))
flags |= NS_EXTEND_FLAGS_SHELL;
if (!strcmp( token, "execFix" ) ||
!strcmp( token, "extendfix" ) ||
!strcmp( token, "execFix2" )) {
strcpy( exec_name2, exec_name );
strcat( exec_name, "Fix" );
flags |= NS_EXTEND_FLAGS_WRITEABLE;
/* XXX - Check for shell... */
}
eptr = _register_extend( oid_buf, oid_len );
extension = _new_extension( exec_name, flags, eptr );
if (extension) {
extension->command = strdup( exec_command );
if (cptr)
extension->args = strdup( cptr );
} else {
snmp_log(LOG_ERR, "Failed to register extend entry '%s' - possibly duplicate name.\n", exec_name );
return;
}
#ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
/*
* Compatability with the UCD extTable
*/
if (!strcmp( token, "execFix" )) {
int i;
for ( i=0; i < num_compatability_entries; i++ ) {
if (!strcmp( exec_name2,
compatability_entries[i].exec_entry->token))
break;
}
if ( i == num_compatability_entries )
config_perror("No matching exec entry" );
else
compatability_entries[ i ].efix_entry = extension;
} else if (!strcmp( token, "sh" ) ||
!strcmp( token, "exec" )) {
if ( num_compatability_entries == max_compatability_entries ) {
/* XXX - should really use dynamic allocation */
netsnmp_old_extend *new_compatability_entries;
new_compatability_entries = realloc(compatability_entries,
max_compatability_entries*2*sizeof(netsnmp_old_extend));
if (!new_compatability_entries)
config_perror("No further UCD-compatible entries" );
else {
memset(new_compatability_entries+num_compatability_entries, 0,
sizeof(netsnmp_old_extend)*max_compatability_entries);
max_compatability_entries *= 2;
compatability_entries = new_compatability_entries;
}
}
if (num_compatability_entries != max_compatability_entries)
compatability_entries[
num_compatability_entries++ ].exec_entry = extension;
}
#endif
}
/*************************
*
* Main table handlers
* Most of the work is handled
* by the 'table_data' helper.
*
*************************/
int
handle_nsExtendConfigTable(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;
netsnmp_extend *extension;
extend_registration_block *eptr;
int i;
int need_to_validate = 0;
for ( request=requests; request; request=request->next ) {
if (request->processed)
continue;
table_info = netsnmp_extract_table_info( request );
extension = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
DEBUGMSGTL(( "nsExtendTable:config", "varbind: "));
DEBUGMSGOID(("nsExtendTable:config", request->requestvb->name,
request->requestvb->name_length));
DEBUGMSG(( "nsExtendTable:config", " (%s)\n",
se_find_label_in_slist("agent_mode", reqinfo->mode)));
switch (reqinfo->mode) {
case MODE_GET:
switch (table_info->colnum) {
case COLUMN_EXTCFG_COMMAND:
snmp_set_var_typed_value(
request->requestvb, ASN_OCTET_STR,
extension->command,
(extension->command)?strlen(extension->command):0);
break;
case COLUMN_EXTCFG_ARGS:
snmp_set_var_typed_value(
request->requestvb, ASN_OCTET_STR,
extension->args,
(extension->args)?strlen(extension->args):0);
break;
case COLUMN_EXTCFG_INPUT:
snmp_set_var_typed_value(
request->requestvb, ASN_OCTET_STR,
extension->input,
(extension->input)?strlen(extension->input):0);
break;
case COLUMN_EXTCFG_CACHETIME:
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&extension->cache->timeout, sizeof(int));
break;
case COLUMN_EXTCFG_EXECTYPE:
i = ((extension->flags & NS_EXTEND_FLAGS_SHELL) ?
NS_EXTEND_ETYPE_SHELL :
NS_EXTEND_ETYPE_EXEC);
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&i, sizeof(i));
break;
case COLUMN_EXTCFG_RUNTYPE:
i = ((extension->flags & NS_EXTEND_FLAGS_WRITEABLE) ?
NS_EXTEND_RTYPE_RWRITE :
NS_EXTEND_RTYPE_RONLY);
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&i, sizeof(i));
break;
case COLUMN_EXTCFG_STORAGE:
i = ((extension->flags & NS_EXTEND_FLAGS_CONFIG) ?
ST_PERMANENT : ST_VOLATILE);
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&i, sizeof(i));
break;
case COLUMN_EXTCFG_STATUS:
i = ((extension->flags & NS_EXTEND_FLAGS_ACTIVE) ?
RS_ACTIVE :
RS_NOTINSERVICE);
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&i, sizeof(i));
break;
default:
netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
continue;
}
break;
/**********
*
* Start of SET handling
*
* All config objects are potentially writable except
* nsExtendStorage which is fixed as either 'permanent'
* (if read from a config file) or 'volatile' (if set via SNMP)
* The string-based settings of a 'permanent' entry cannot
* be changed - neither can the execution or run type.
* Such entries can be (temporarily) marked as inactive,
* and the cache timeout adjusted, but these changes are
* not persistent.
*
**********/
case MODE_SET_RESERVE1:
/*
* Validate the new assignments
*/
switch (table_info->colnum) {
case COLUMN_EXTCFG_COMMAND:
if (request->requestvb->type != ASN_OCTET_STR) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGTYPE);
return SNMP_ERR_WRONGTYPE;
}
/*
* Must have a full path to the command
* XXX - Assumes Unix-style paths
*/
if (request->requestvb->val_len == 0 ||
request->requestvb->val.string[0] != '/') {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGVALUE);
return SNMP_ERR_WRONGVALUE;
}
/*
* XXX - need to check this file exists
* (and is executable)
*/
if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
/*
* config entries are "permanent" so can't be changed
*/
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_NOTWRITABLE);
return SNMP_ERR_NOTWRITABLE;
}
break;
case COLUMN_EXTCFG_ARGS:
case COLUMN_EXTCFG_INPUT:
if (request->requestvb->type != ASN_OCTET_STR) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGTYPE);
return SNMP_ERR_WRONGTYPE;
}
if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
/*
* config entries are "permanent" so can't be changed
*/
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_NOTWRITABLE);
return SNMP_ERR_NOTWRITABLE;
}
break;
case COLUMN_EXTCFG_CACHETIME:
if (request->requestvb->type != ASN_INTEGER) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGTYPE);
return SNMP_ERR_WRONGTYPE;
}
i = *request->requestvb->val.integer;
/*
* -1 is a special value indicating "don't cache"
* [[ XXX - should this be 0 ?? ]]
* Otherwise, cache times must be non-negative
*/
if (i < -1 ) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGVALUE);
return SNMP_ERR_WRONGVALUE;
}
break;
case COLUMN_EXTCFG_EXECTYPE:
if (request->requestvb->type != ASN_INTEGER) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGTYPE);
return SNMP_ERR_WRONGTYPE;
}
i = *request->requestvb->val.integer;
if (i<1 || i>2) { /* 'exec(1)' or 'shell(2)' only */
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGVALUE);
return SNMP_ERR_WRONGVALUE;
}
if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
/*
* config entries are "permanent" so can't be changed
*/
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_NOTWRITABLE);
return SNMP_ERR_NOTWRITABLE;
}
break;
case COLUMN_EXTCFG_RUNTYPE:
if (request->requestvb->type != ASN_INTEGER) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGTYPE);
return SNMP_ERR_WRONGTYPE;
}
/*
* 'run-on-read(1)', 'run-on-set(2)'
* or 'run-command(3)' only
*/
i = *request->requestvb->val.integer;
if (i<1 || i>3) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGVALUE);
return SNMP_ERR_WRONGVALUE;
}
/*
* 'run-command(3)' can only be used with
* a pre-existing 'run-on-set(2)' entry.
*/
if (i==3 && !(extension && (extension->flags & NS_EXTEND_FLAGS_WRITEABLE))) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_INCONSISTENTVALUE);
return SNMP_ERR_INCONSISTENTVALUE;
}
/*
* 'run-command(3)' is the only valid assignment
* for permanent (i.e. config) entries
*/
if ((extension && extension->flags & NS_EXTEND_FLAGS_CONFIG)
&& i!=3 ) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_INCONSISTENTVALUE);
return SNMP_ERR_INCONSISTENTVALUE;
}
break;
case COLUMN_EXTCFG_STATUS:
if (request->requestvb->type != ASN_INTEGER) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGTYPE);
return SNMP_ERR_WRONGTYPE;
}
i = *request->requestvb->val.integer;
switch (i) {
case RS_ACTIVE:
case RS_NOTINSERVICE:
if (!extension) {
/* Must be used with existing rows */
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_INCONSISTENTVALUE);
return SNMP_ERR_INCONSISTENTVALUE;
}
break; /* OK */
case RS_CREATEANDGO:
case RS_CREATEANDWAIT:
if (extension) {
/* Can only be used to create new rows */
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_INCONSISTENTVALUE);
return SNMP_ERR_INCONSISTENTVALUE;
}
break;
case RS_DESTROY:
break;
default:
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_WRONGVALUE);
return SNMP_ERR_WRONGVALUE;
}
break;
default:
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_NOTWRITABLE);
return SNMP_ERR_NOTWRITABLE;
}
break;
case MODE_SET_RESERVE2:
switch (table_info->colnum) {
case COLUMN_EXTCFG_STATUS:
i = *request->requestvb->val.integer;
switch (i) {
case RS_CREATEANDGO:
case RS_CREATEANDWAIT:
eptr = _find_extension_block( request->requestvb->name,
request->requestvb->name_length );
extension = _new_extension( (char *) table_info->indexes->val.string,
0, eptr );
if (!extension) { /* failed */
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_RESOURCEUNAVAILABLE);
return SNMP_ERR_RESOURCEUNAVAILABLE;
}
netsnmp_insert_table_row( request, extension->row );
}
}
break;
case MODE_SET_FREE:
switch (table_info->colnum) {
case COLUMN_EXTCFG_STATUS:
i = *request->requestvb->val.integer;
switch (i) {
case RS_CREATEANDGO:
case RS_CREATEANDWAIT:
eptr = _find_extension_block( request->requestvb->name,
request->requestvb->name_length );
_free_extension( extension, eptr );
}
}
break;
case MODE_SET_ACTION:
switch (table_info->colnum) {
case COLUMN_EXTCFG_COMMAND:
extension->old_command = extension->command;
extension->command = netsnmp_strdup_and_null(
request->requestvb->val.string,
request->requestvb->val_len);
break;
case COLUMN_EXTCFG_ARGS:
extension->old_args = extension->args;
extension->args = netsnmp_strdup_and_null(
request->requestvb->val.string,
request->requestvb->val_len);
break;
case COLUMN_EXTCFG_INPUT:
extension->old_input = extension->input;
extension->input = netsnmp_strdup_and_null(
request->requestvb->val.string,
request->requestvb->val_len);
break;
case COLUMN_EXTCFG_STATUS:
i = *request->requestvb->val.integer;
switch (i) {
case RS_ACTIVE:
case RS_CREATEANDGO:
need_to_validate = 1;
}
break;
}
break;
case MODE_SET_UNDO:
switch (table_info->colnum) {
case COLUMN_EXTCFG_COMMAND:
if ( extension && extension->old_command ) {
SNMP_FREE(extension->command);
extension->command = extension->old_command;
extension->old_command = NULL;
}
break;
case COLUMN_EXTCFG_ARGS:
if ( extension && extension->old_args ) {
SNMP_FREE(extension->args);
extension->args = extension->old_args;
extension->old_args = NULL;
}
break;
case COLUMN_EXTCFG_INPUT:
if ( extension && extension->old_input ) {
SNMP_FREE(extension->input);
extension->input = extension->old_input;
extension->old_input = NULL;
}
break;
case COLUMN_EXTCFG_STATUS:
i = *request->requestvb->val.integer;
switch (i) {
case RS_CREATEANDGO:
case RS_CREATEANDWAIT:
eptr = _find_extension_block( request->requestvb->name,
request->requestvb->name_length );
_free_extension( extension, eptr );
}
break;
}
break;
case MODE_SET_COMMIT:
switch (table_info->colnum) {
case COLUMN_EXTCFG_CACHETIME:
i = *request->requestvb->val.integer;
extension->cache->timeout = i;
break;
case COLUMN_EXTCFG_RUNTYPE:
i = *request->requestvb->val.integer;
switch (i) {
case 1:
extension->flags &= ~NS_EXTEND_FLAGS_WRITEABLE;
break;
case 2:
extension->flags |= NS_EXTEND_FLAGS_WRITEABLE;
break;
case 3:
(void)netsnmp_cache_check_and_reload( extension->cache );
break;
}
break;
case COLUMN_EXTCFG_EXECTYPE:
i = *request->requestvb->val.integer;
if ( i == NS_EXTEND_ETYPE_SHELL )
extension->flags |= NS_EXTEND_FLAGS_SHELL;
else
extension->flags &= ~NS_EXTEND_FLAGS_SHELL;
break;
case COLUMN_EXTCFG_STATUS:
i = *request->requestvb->val.integer;
switch (i) {
case RS_ACTIVE:
case RS_CREATEANDGO:
extension->flags |= NS_EXTEND_FLAGS_ACTIVE;
break;
case RS_NOTINSERVICE:
case RS_CREATEANDWAIT:
extension->flags &= ~NS_EXTEND_FLAGS_ACTIVE;
break;
case RS_DESTROY:
eptr = _find_extension_block( request->requestvb->name,
request->requestvb->name_length );
_free_extension( extension, eptr );
break;
}
}
break;
default:
netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
return SNMP_ERR_GENERR;
}
}
/*
* If we're marking a given row as active,
* then we need to check that it's ready.
*/
if (need_to_validate) {
for ( request=requests; request; request=request->next ) {
if (request->processed)
continue;
table_info = netsnmp_extract_table_info( request );
extension = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
switch (table_info->colnum) {
case COLUMN_EXTCFG_STATUS:
i = *request->requestvb->val.integer;
if (( i == RS_ACTIVE || i == RS_CREATEANDGO ) &&
!(extension && extension->command &&
extension->command[0] == '/' /* &&
is_executable(extension->command) */)) {
netsnmp_set_request_error(reqinfo, request,
SNMP_ERR_INCONSISTENTVALUE);
return SNMP_ERR_INCONSISTENTVALUE;
}
}
}
}
return SNMP_ERR_NOERROR;
}
int
handle_nsExtendOutput1Table(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;
netsnmp_extend *extension;
int len;
for ( request=requests; request; request=request->next ) {
if (request->processed)
continue;
table_info = netsnmp_extract_table_info( request );
extension = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
DEBUGMSGTL(( "nsExtendTable:output1", "varbind: "));
DEBUGMSGOID(("nsExtendTable:output1", request->requestvb->name,
request->requestvb->name_length));
DEBUGMSG(( "nsExtendTable:output1", "\n"));
switch (reqinfo->mode) {
case MODE_GET:
if (!extension || !(extension->flags & NS_EXTEND_FLAGS_ACTIVE)) {
/*
* If this row is inactive, then skip it.
*/
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
if (!(extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
(netsnmp_cache_check_and_reload( extension->cache ) < 0 )) {
/*
* If reloading the output cache of a 'run-on-read'
* entry fails, then skip it.
*/
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
if ((extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
(netsnmp_cache_check_expired( extension->cache ) == 1 )) {
/*
* If the output cache of a 'run-on-write'
* entry has expired, then skip it.
*/
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
switch (table_info->colnum) {
case COLUMN_EXTOUT1_OUTLEN:
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&extension->out_len, sizeof(int));
break;
case COLUMN_EXTOUT1_OUTPUT1:
/*
* If we've got more than one line,
* find the length of the first one.
* Otherwise find the length of the whole string.
*/
if (extension->numlines > 1) {
len = (extension->lines[1])-(extension->output) -1;
} else if (extension->output) {
len = strlen(extension->output);
} else {
len = 0;
}
snmp_set_var_typed_value(
request->requestvb, ASN_OCTET_STR,
extension->output, len);
break;
case COLUMN_EXTOUT1_OUTPUT2:
snmp_set_var_typed_value(
request->requestvb, ASN_OCTET_STR,
extension->output,
(extension->output)?extension->out_len:0);
break;
case COLUMN_EXTOUT1_NUMLINES:
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&extension->numlines, sizeof(int));
break;
case COLUMN_EXTOUT1_RESULT:
snmp_set_var_typed_value(
request->requestvb, ASN_INTEGER,
(u_char*)&extension->result, sizeof(int));
break;
default:
netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
continue;
}
break;
default:
netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
return SNMP_ERR_GENERR;
}
}
return SNMP_ERR_NOERROR;
}
/*************************
*
* Multi-line output table handler
* Most of the work is handled here.
*
*************************/
/*
* Locate the appropriate entry for a given request
*/
netsnmp_extend *
_extend_find_entry( netsnmp_request_info *request,
netsnmp_table_request_info *table_info,
int mode )
{
netsnmp_extend *eptr;
extend_registration_block *ereg;
unsigned int line_idx;
oid oid_buf[MAX_OID_LEN];
int oid_len;
int i;
char *token;
size_t token_len;
if (!request || !table_info || !table_info->indexes
|| !table_info->indexes->next_variable) {
DEBUGMSGTL(( "nsExtendTable:output2", "invalid invocation\n"));
return NULL;
}
ereg = _find_extension_block( request->requestvb->name,
request->requestvb->name_length );
/***
* GET handling - find the exact entry being requested
***/
if ( mode == MODE_GET ) {
DEBUGMSGTL(( "nsExtendTable:output2", "GET: %s / %ld\n ",
table_info->indexes->val.string,
*table_info->indexes->next_variable->val.integer));
for ( eptr = ereg->ehead; eptr; eptr = eptr->next ) {
if ( !strcmp( eptr->token, (char *) table_info->indexes->val.string ))
break;
}
if ( eptr ) {
/*
* Ensure the output is available...
*/
if (!(eptr->flags & NS_EXTEND_FLAGS_ACTIVE) ||
(netsnmp_cache_check_and_reload( eptr->cache ) < 0 ))
return NULL;
/*
* ...and check the line requested is valid
*/
line_idx = *table_info->indexes->next_variable->val.integer;
if (line_idx < 1 || line_idx > eptr->numlines)
return NULL;
}
}
/***
* GETNEXT handling - find the first suitable entry
***/
else {
if (!table_info->indexes->val_len ) {
DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: first entry\n"));
/*
* Beginning of the table - find the first active
* (and successful) entry, and use the first line of it
*/
for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
(netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
line_idx = 1;
break;
}
}
} else {
token = (char *) table_info->indexes->val.string;
token_len = table_info->indexes->val_len;
line_idx = *table_info->indexes->next_variable->val.integer;
DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: %s / %d\n ",
token, line_idx ));
/*
* Otherwise, find the first entry not earlier
* than the requested token...
*/
for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
if ( strlen(eptr->token) > token_len )
break;
if ( strlen(eptr->token) == token_len &&
strcmp(eptr->token, token) >= 0 )
break;
}
if (!eptr)
return NULL; /* (assuming there is one) */
/*
* ... and make sure it's active & the output is available
* (or use the first following entry that is)
*/
for ( ; eptr; eptr = eptr->next ) {
if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
(netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
break;
}
line_idx = 1;
}
if (!eptr)
return NULL; /* (assuming there is one) */
/*
* If we're working with the same entry that was requested,
* see whether we've reached the end of the output...
*/
if (!strcmp( eptr->token, token )) {
if ( eptr->numlines <= line_idx ) {
/*
* ... and if so, move on to the first line
* of the next (active and successful) entry.
*/
line_idx = 1;
for (eptr = eptr->next ; eptr; eptr = eptr->next ) {
if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
(netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
break;
}
}
} else {
/*
* Otherwise just use the next line of this entry.
*/
line_idx++;
}
}
else {
/*
* If this is not the same entry that was requested,
* then we should return the first line.
*/
line_idx = 1;
}
}
if (eptr) {
DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT -> %s / %d\n ",
eptr->token, line_idx));
/*
* Since we're processing a GETNEXT request,
* now we've found the appropriate entry (and line),
* we need to update the varbind OID ...
*/
memset(oid_buf, 0, sizeof(oid_buf));
oid_len = ereg->oid_len;
memcpy( oid_buf, ereg->root_oid, oid_len*sizeof(oid));
oid_buf[ oid_len++ ] = 4; /* nsExtendOutput2Table */
oid_buf[ oid_len++ ] = 1; /* nsExtendOutput2Entry */
oid_buf[ oid_len++ ] = COLUMN_EXTOUT2_OUTLINE;
/* string token index */
oid_buf[ oid_len++ ] = strlen(eptr->token);
for ( i=0; i < (int)strlen(eptr->token); i++ )
oid_buf[ oid_len+i ] = eptr->token[i];
oid_len += strlen( eptr->token );
/* plus line number */
oid_buf[ oid_len++ ] = line_idx;
snmp_set_var_objid( request->requestvb, oid_buf, oid_len );
/*
* ... and index values to match.
*/
snmp_set_var_value( table_info->indexes,
eptr->token, strlen(eptr->token));
snmp_set_var_value( table_info->indexes->next_variable,
(const u_char*)&line_idx, sizeof(line_idx));
}
}
return eptr; /* Finally, signal success */
}
/*
* Multi-line output handler
* Locate the appropriate entry (using _extend_find_entry)
* and return the appropriate output line
*/
int
handle_nsExtendOutput2Table(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;
netsnmp_extend *extension;
char *cp;
unsigned int line_idx;
int len;
for ( request=requests; request; request=request->next ) {
if (request->processed)
continue;
table_info = netsnmp_extract_table_info( request );
extension = _extend_find_entry( request, table_info, reqinfo->mode );
DEBUGMSGTL(( "nsExtendTable:output2", "varbind: "));
DEBUGMSGOID(("nsExtendTable:output2", request->requestvb->name,
request->requestvb->name_length));
DEBUGMSG(( "nsExtendTable:output2", " (%s)\n",
(extension) ? extension->token : "[none]"));
if (!extension) {
if (reqinfo->mode == MODE_GET)
netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
else
netsnmp_set_request_error(reqinfo, request, SNMP_ENDOFMIBVIEW);
continue;
}
switch (reqinfo->mode) {
case MODE_GET:
case MODE_GETNEXT:
switch (table_info->colnum) {
case COLUMN_EXTOUT2_OUTLINE:
/*
* Determine which line we've been asked for....
*/
line_idx = *table_info->indexes->next_variable->val.integer;
if (line_idx < 1 || line_idx > extension->numlines) {
netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
continue;
}
cp = extension->lines[line_idx-1];
/*
* ... and how long it is.
*/
if ( extension->numlines > line_idx )
len = (extension->lines[line_idx])-cp -1;
else if (cp)
len = strlen(cp);
else
len = 0;
snmp_set_var_typed_value( request->requestvb,
ASN_OCTET_STR, cp, len );
break;
default:
netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
continue;
}
break;
default:
netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
return SNMP_ERR_GENERR;
}
}
return SNMP_ERR_NOERROR;
}
#ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
/*************************
*
* Compatability with the UCD extTable
*
*************************/
char * _get_cmdline(netsnmp_extend *extend)
{
size_t size;
char *newbuf;
const char *args = extend->args;
if (args == NULL)
/* Use empty string for processes without arguments. */
args = "";
size = strlen(extend->command) + strlen(args) + 2;
if (size > cmdlinesize) {
newbuf = realloc(cmdlinebuf, size);
if (!newbuf) {
free(cmdlinebuf);
cmdlinebuf = NULL;
cmdlinesize = 0;
return NULL;
}
cmdlinebuf = newbuf;
cmdlinesize = size;
}
sprintf(cmdlinebuf, "%s %s", extend->command, args);
return cmdlinebuf;
}
u_char *
var_extensible_old(struct variable * vp,
oid * name,
size_t * length,
int exact,
size_t * var_len, WriteMethod ** write_method)
{
netsnmp_old_extend *exten = NULL;
static long long_ret;
unsigned int idx;
char *cmdline;
if (header_simple_table
(vp, name, length, exact, var_len, write_method, num_compatability_entries))
return (NULL);
idx = name[*length-1] -1;
if (idx > max_compatability_entries)
return NULL;
exten = &compatability_entries[ idx ];
if (exten) {
switch (vp->magic) {
case MIBINDEX:
long_ret = name[*length - 1];
return ((u_char *) (&long_ret));
case ERRORNAME: /* name defined in config file */
*var_len = strlen(exten->exec_entry->token);
return ((u_char *) (exten->exec_entry->token));
case SHELLCOMMAND:
cmdline = _get_cmdline(exten->exec_entry);
if (cmdline)
*var_len = strlen(cmdline);
return ((u_char *) cmdline);
case ERRORFLAG: /* return code from the process */
netsnmp_cache_check_and_reload( exten->exec_entry->cache );
long_ret = exten->exec_entry->result;
return ((u_char *) (&long_ret));
case ERRORMSG: /* first line of text returned from the process */
netsnmp_cache_check_and_reload( exten->exec_entry->cache );
if (exten->exec_entry->numlines > 1) {
*var_len = (exten->exec_entry->lines[1])-
(exten->exec_entry->output) -1;
} else if (exten->exec_entry->output) {
*var_len = strlen(exten->exec_entry->output);
} else {
*var_len = 0;
}
return ((u_char *) (exten->exec_entry->output));
case ERRORFIX:
*write_method = fixExec2Error;
long_return = 0;
return ((u_char *) &long_return);
case ERRORFIXCMD:
if (exten->efix_entry) {
cmdline = _get_cmdline(exten->efix_entry);
if (cmdline)
*var_len = strlen(cmdline);
return ((u_char *) cmdline);
} else {
*var_len = 0;
return ((u_char *) &long_return); /* Just needs to be non-null! */
}
}
return NULL;
}
return NULL;
}
int
fixExec2Error(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)
{
netsnmp_old_extend *exten = NULL;
unsigned int idx;
idx = name[name_len-1] -1;
exten = &compatability_entries[ idx ];
switch (action) {
case MODE_SET_RESERVE1:
if (var_val_type != ASN_INTEGER) {
snmp_log(LOG_ERR, "Wrong type != int\n");
return SNMP_ERR_WRONGTYPE;
}
idx = *((long *) var_val);
if (idx != 1) {
snmp_log(LOG_ERR, "Wrong value != 1\n");
return SNMP_ERR_WRONGVALUE;
}
if (!exten || !exten->efix_entry) {
snmp_log(LOG_ERR, "No command to run\n");
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
case MODE_SET_COMMIT:
netsnmp_cache_check_and_reload( exten->efix_entry->cache );
}
return SNMP_ERR_NOERROR;
}
#endif