diff options
Diffstat (limited to 'agent/mibgroup/agent/extend.c')
-rw-r--r-- | agent/mibgroup/agent/extend.c | 1433 |
1 files changed, 1433 insertions, 0 deletions
diff --git a/agent/mibgroup/agent/extend.c b/agent/mibgroup/agent/extend.c new file mode 100644 index 0000000..34ddf16 --- /dev/null +++ b/agent/mibgroup/agent/extend.c @@ -0,0 +1,1433 @@ + +#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.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 { + int idx; + netsnmp_extend *exec_entry; + netsnmp_extend *efix_entry; +} netsnmp_old_extend; + +int num_compatability_entries = 0; +int max_compatability_entries = 50; +netsnmp_old_extend *compatability_entries; + +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, RONLY, var_extensible_old, 1, {MIBINDEX}}, + {ERRORNAME, ASN_OCTET_STR, RONLY, var_extensible_old, 1, {ERRORNAME}}, + {SHELLCOMMAND, ASN_OCTET_STR, RONLY, var_extensible_old, 1, {SHELLCOMMAND}}, + {ERRORFLAG, ASN_INTEGER, RONLY, var_extensible_old, 1, {ERRORFLAG}}, + {ERRORMSG, ASN_OCTET_STR, RONLY, var_extensible_old, 1, {ERRORMSG}}, + {ERRORFIX, ASN_INTEGER, RWRITE, var_extensible_old, 1, {ERRORFIX}}, + {ERRORFIXCMD, ASN_OCTET_STR, 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 = 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) +{ + 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 = calloc( sizeof(char *), extension->numlines ); + memcpy( extension->lines, line_buf, + sizeof(char *) * extension->numlines ); + } else { + extension->lines = &extension->output; + } + } + extension->result = ret; + return ret; +} + +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_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" )) { + 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_name, + 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 */ + config_perror("No further UCD-compatible entries" ); + else + 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( 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; + int line_idx; + oid oid_buf[MAX_OID_LEN]; + int oid_len; + int i; + char *token; + int 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 / %d\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, 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 (eptr->numlines < line_idx) + return NULL; + } + return eptr; + } + + /*** + * 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 = 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 */ + } + return NULL; +} + +/* + * 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; + 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; + 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 + * + *************************/ + +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; + int idx; + + if (header_simple_table + (vp, name, length, exact, var_len, write_method, num_compatability_entries)) + return (NULL); + + idx = name[*length-1] -1; + 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: + *var_len = strlen(exten->exec_entry->command); + return ((u_char *) (exten->exec_entry->command)); + 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) { + *var_len = strlen(exten->efix_entry->command); + return ((u_char *) exten->efix_entry->command); + } 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; + 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 |