#include #include #include #include #include #include #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 /* This mib is potentially dangerous to turn on by default, since it * allows arbitrary commands to be set by anyone with SNMP WRITE * access to the MIB table. If all of your users are "root" level * users, then it may be safe to turn on. */ #define ENABLE_EXTEND_WRITE_ACCESS 0 netsnmp_feature_require(extract_table_row_data); netsnmp_feature_require(table_data_delete_table); #ifndef NETSNMP_NO_WRITE_SUPPORT netsnmp_feature_require(insert_table_row); #endif /* NETSNMP_NO_WRITE_SUPPORT */ oid ns_extend_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2 }; 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[4]; 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 }; #ifndef NETSNMP_NO_WRITE_SUPPORT 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}} }; #else /* !NETSNMP_NO_WRITE_SUPPORT */ 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_RONLY, var_extensible_old, 1, {ERRORFIX}}, {ERRORFIXCMD, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, var_extensible_old, 1, {ERRORFIXCMD}} }; #endif /* !NETSNMP_NO_WRITE_SUPPORT */ #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 = NULL; int rc; 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 ); if (!eptr) return NULL; 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; #ifndef NETSNMP_NO_WRITE_SUPPORT reg = netsnmp_create_handler_registration( "nsExtendConfigTable", handle_nsExtendConfigTable, oid_buf, len+1, HANDLER_CAN_RWRITE); #else /* !NETSNMP_NO_WRITE_SUPPORT */ reg = netsnmp_create_handler_registration( "nsExtendConfigTable", handle_nsExtendConfigTable, oid_buf, len+1, HANDLER_CAN_RONLY); #endif /* !NETSNMP_NO_WRITE_SUPPORT */ rc = netsnmp_register_table_data( reg, dinfo, tinfo ); if (rc != SNMPERR_SUCCESS) { goto bail; } netsnmp_handler_owns_table_info(reg->handler->next); 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); rc = netsnmp_register_table_data( reg, dinfo, tinfo ); if (rc != SNMPERR_SUCCESS) goto bail; netsnmp_handler_owns_table_info(reg->handler->next); 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); rc = netsnmp_register_table( reg, tinfo ); if (rc != SNMPERR_SUCCESS) goto bail; netsnmp_handler_owns_table_info(reg->handler->next); 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); rc = netsnmp_register_watched_scalar2( reg, winfo ); if (rc != SNMPERR_SUCCESS) goto bail; eptr->reg[3] = reg; return eptr; bail: if (eptr->reg[3]) netsnmp_unregister_handler(eptr->reg[3]); if (eptr->reg[2]) netsnmp_unregister_handler(eptr->reg[2]); if (eptr->reg[1]) netsnmp_unregister_handler(eptr->reg[1]); if (eptr->reg[0]) netsnmp_unregister_handler(eptr->reg[0]); return NULL; } static void _unregister_extend(extend_registration_block *eptr) { extend_registration_block *prev; netsnmp_assert(eptr); for (prev = ereg_head; prev && prev->next != eptr; prev = prev->next) ; if (prev) { netsnmp_assert(eptr == prev->next); prev->next = eptr->next; } else { netsnmp_assert(eptr == ereg_head); ereg_head = eptr->next; } netsnmp_table_data_delete_table(eptr->dinfo); free(eptr->root_oid); free(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] ); netsnmp_unregister_handler( eptr->reg[3] ); 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); } void shutdown_extend(void) { #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE free(compatability_entries); compatability_entries = NULL; #endif while (ereg_head) _unregister_extend(ereg_head); } /************************* * * 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; int cache_timeout = 0; int exec_type = NS_EXTEND_ETYPE_EXEC; cptr = copy_nword(cptr, exec_name, sizeof(exec_name)); if (strcmp(exec_name, "-cacheTime") == 0) { char cache_timeout_str[32]; cptr = copy_nword(cptr, cache_timeout_str, sizeof(cache_timeout_str)); /* If atoi can't do the conversion, it returns 0 */ cache_timeout = atoi(cache_timeout_str); cptr = copy_nword(cptr, exec_name, sizeof(exec_name)); } if (strcmp(exec_name, "-execType") == 0) { char exec_type_str[16]; cptr = copy_nword(cptr, exec_type_str, sizeof(exec_type_str)); if (strcmp(exec_type_str, "sh") == 0) exec_type = NS_EXTEND_ETYPE_SHELL; else exec_type = NS_EXTEND_ETYPE_EXEC; 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") || exec_type == NS_EXTEND_ETYPE_SHELL) 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 ); if (!eptr) { snmp_log(LOG_ERR, "Failed to register extend entry '%s' - possibly duplicate name.\n", exec_name ); return; } extension = _new_extension( exec_name, flags, eptr ); if (extension) { extension->command = strdup( exec_command ); if (cptr) extension->args = strdup( cptr ); if (cache_timeout != 0) extension->cache->timeout = cache_timeout; } 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 NETSNMP_ATTRIBUTE_UNUSED; int i; int need_to_validate NETSNMP_ATTRIBUTE_UNUSED = 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. * **********/ #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS 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; #endif /* !NETSNMP_NO_WRITE_SUPPORT and ENABLE_EXTEND_WRITE_ACCESS */ default: netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); return SNMP_ERR_GENERR; } } #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS /* * 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; } } } } #endif /* !NETSNMP_NO_WRITE_SUPPORT && ENABLE_EXTEND_WRITE_ACCESS */ 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]; 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; } 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 ]; #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS 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 ); } #endif /* !NETSNMP_NO_WRITE_SUPPORT && ENABLE_EXTEND_WRITE_ACCESS */ return SNMP_ERR_NOERROR; } #endif /* USING_UCD_SNMP_EXTENSIBLE_MODULE */