1
2 #include <net-snmp/net-snmp-config.h>
3 #include <net-snmp/net-snmp-features.h>
4 #include <net-snmp/net-snmp-includes.h>
5 #include <net-snmp/agent/net-snmp-agent-includes.h>
6 #include <net-snmp/agent/watcher.h>
7 #include <net-snmp/agent/agent_callbacks.h>
8
9 #include "agent/extend.h"
10 #include "utilities/execute.h"
11 #include "struct.h"
12
13 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
14 #include "util_funcs/header_simple_table.h"
15 #include "mibdefs.h"
16 #define SHELLCOMMAND 3
17 #endif
18
19 /* This mib is potentially dangerous to turn on by default, since it
20 * allows arbitrary commands to be set by anyone with SNMP WRITE
21 * access to the MIB table. If all of your users are "root" level
22 * users, then it may be safe to turn on. */
23 #define ENABLE_EXTEND_WRITE_ACCESS 0
24
25 netsnmp_feature_require(extract_table_row_data);
26 netsnmp_feature_require(table_data_delete_table);
27 #ifndef NETSNMP_NO_WRITE_SUPPORT
28 netsnmp_feature_require(insert_table_row);
29 #endif /* NETSNMP_NO_WRITE_SUPPORT */
30
31 oid ns_extend_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2 };
32
33 typedef struct extend_registration_block_s {
34 netsnmp_table_data *dinfo;
35 oid *root_oid;
36 size_t oid_len;
37 long num_entries;
38 netsnmp_extend *ehead;
39 netsnmp_handler_registration *reg[4];
40 struct extend_registration_block_s *next;
41 } extend_registration_block;
42 extend_registration_block *ereg_head = NULL;
43
44
45 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
46 typedef struct netsnmp_old_extend_s {
47 unsigned int idx;
48 netsnmp_extend *exec_entry;
49 netsnmp_extend *efix_entry;
50 } netsnmp_old_extend;
51
52 unsigned int num_compatability_entries = 0;
53 unsigned int max_compatability_entries = 50;
54 netsnmp_old_extend *compatability_entries;
55
56 char *cmdlinebuf;
57 size_t cmdlinesize;
58
59 WriteMethod fixExec2Error;
60 FindVarMethod var_extensible_old;
61 oid old_extensible_variables_oid[] = { NETSNMP_UCDAVIS_MIB, NETSNMP_SHELLMIBNUM, 1 };
62 #ifndef NETSNMP_NO_WRITE_SUPPORT
63 struct variable2 old_extensible_variables[] = {
64 {MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
65 var_extensible_old, 1, {MIBINDEX}},
66 {ERRORNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
67 var_extensible_old, 1, {ERRORNAME}},
68 {SHELLCOMMAND, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
69 var_extensible_old, 1, {SHELLCOMMAND}},
70 {ERRORFLAG, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
71 var_extensible_old, 1, {ERRORFLAG}},
72 {ERRORMSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
73 var_extensible_old, 1, {ERRORMSG}},
74 {ERRORFIX, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
75 var_extensible_old, 1, {ERRORFIX}},
76 {ERRORFIXCMD, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
77 var_extensible_old, 1, {ERRORFIXCMD}}
78 };
79 #else /* !NETSNMP_NO_WRITE_SUPPORT */
80 struct variable2 old_extensible_variables[] = {
81 {MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
82 var_extensible_old, 1, {MIBINDEX}},
83 {ERRORNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
84 var_extensible_old, 1, {ERRORNAME}},
85 {SHELLCOMMAND, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
86 var_extensible_old, 1, {SHELLCOMMAND}},
87 {ERRORFLAG, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
88 var_extensible_old, 1, {ERRORFLAG}},
89 {ERRORMSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
90 var_extensible_old, 1, {ERRORMSG}},
91 {ERRORFIX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
92 var_extensible_old, 1, {ERRORFIX}},
93 {ERRORFIXCMD, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
94 var_extensible_old, 1, {ERRORFIXCMD}}
95 };
96 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
97 #endif
98
99
100 /*************************
101 *
102 * Main initialisation routine
103 *
104 *************************/
105
106 extend_registration_block *
_find_extension_block(oid * name,size_t name_len)107 _find_extension_block( oid *name, size_t name_len )
108 {
109 extend_registration_block *eptr;
110 size_t len;
111 for ( eptr=ereg_head; eptr; eptr=eptr->next ) {
112 len = SNMP_MIN(name_len, eptr->oid_len);
113 if (!snmp_oid_compare( name, len, eptr->root_oid, eptr->oid_len))
114 return eptr;
115 }
116 return NULL;
117 }
118
119 extend_registration_block *
_register_extend(oid * base,size_t len)120 _register_extend( oid *base, size_t len )
121 {
122 extend_registration_block *eptr;
123 oid oid_buf[MAX_OID_LEN];
124
125 netsnmp_table_data *dinfo;
126 netsnmp_table_registration_info *tinfo;
127 netsnmp_watcher_info *winfo;
128 netsnmp_handler_registration *reg = NULL;
129 int rc;
130
131 for ( eptr=ereg_head; eptr; eptr=eptr->next ) {
132 if (!snmp_oid_compare( base, len, eptr->root_oid, eptr->oid_len))
133 return eptr;
134 }
135 if (!eptr) {
136 eptr = SNMP_MALLOC_TYPEDEF( extend_registration_block );
137 if (!eptr)
138 return NULL;
139 eptr->root_oid = snmp_duplicate_objid( base, len );
140 eptr->oid_len = len;
141 eptr->num_entries = 0;
142 eptr->ehead = NULL;
143 eptr->dinfo = netsnmp_create_table_data( "nsExtendTable" );
144 eptr->next = ereg_head;
145 ereg_head = eptr;
146 }
147
148 dinfo = eptr->dinfo;
149 memcpy( oid_buf, base, len*sizeof(oid) );
150
151 /*
152 * Register the configuration table
153 */
154 tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
155 netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
156 tinfo->min_column = COLUMN_EXTCFG_FIRST_COLUMN;
157 tinfo->max_column = COLUMN_EXTCFG_LAST_COLUMN;
158 oid_buf[len] = 2;
159 #ifndef NETSNMP_NO_WRITE_SUPPORT
160 reg = netsnmp_create_handler_registration(
161 "nsExtendConfigTable", handle_nsExtendConfigTable,
162 oid_buf, len+1, HANDLER_CAN_RWRITE);
163 #else /* !NETSNMP_NO_WRITE_SUPPORT */
164 reg = netsnmp_create_handler_registration(
165 "nsExtendConfigTable", handle_nsExtendConfigTable,
166 oid_buf, len+1, HANDLER_CAN_RONLY);
167 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
168 rc = netsnmp_register_table_data( reg, dinfo, tinfo );
169 if (rc != SNMPERR_SUCCESS) {
170 goto bail;
171 }
172 netsnmp_handler_owns_table_info(reg->handler->next);
173 eptr->reg[0] = reg;
174
175 /*
176 * Register the main output table
177 * using the same table_data handle.
178 * This is sufficient to link the two tables,
179 * and implement the AUGMENTS behaviour
180 */
181 tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
182 netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
183 tinfo->min_column = COLUMN_EXTOUT1_FIRST_COLUMN;
184 tinfo->max_column = COLUMN_EXTOUT1_LAST_COLUMN;
185 oid_buf[len] = 3;
186 reg = netsnmp_create_handler_registration(
187 "nsExtendOut1Table", handle_nsExtendOutput1Table,
188 oid_buf, len+1, HANDLER_CAN_RONLY);
189 rc = netsnmp_register_table_data( reg, dinfo, tinfo );
190 if (rc != SNMPERR_SUCCESS)
191 goto bail;
192 netsnmp_handler_owns_table_info(reg->handler->next);
193 eptr->reg[1] = reg;
194
195 /*
196 * Register the multi-line output table
197 * using a simple table helper.
198 * This handles extracting the indexes from
199 * the request OID, but leaves most of
200 * the work to our handler routine.
201 * Still, it was nice while it lasted...
202 */
203 tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
204 netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, ASN_INTEGER, 0 );
205 tinfo->min_column = COLUMN_EXTOUT2_FIRST_COLUMN;
206 tinfo->max_column = COLUMN_EXTOUT2_LAST_COLUMN;
207 oid_buf[len] = 4;
208 reg = netsnmp_create_handler_registration(
209 "nsExtendOut2Table", handle_nsExtendOutput2Table,
210 oid_buf, len+1, HANDLER_CAN_RONLY);
211 rc = netsnmp_register_table( reg, tinfo );
212 if (rc != SNMPERR_SUCCESS)
213 goto bail;
214 netsnmp_handler_owns_table_info(reg->handler->next);
215 eptr->reg[2] = reg;
216
217 /*
218 * Register a watched scalar to keep track of the number of entries
219 */
220 oid_buf[len] = 1;
221 reg = netsnmp_create_handler_registration(
222 "nsExtendNumEntries", NULL,
223 oid_buf, len+1, HANDLER_CAN_RONLY);
224 winfo = netsnmp_create_watcher_info(
225 &(eptr->num_entries), sizeof(eptr->num_entries),
226 ASN_INTEGER, WATCHER_FIXED_SIZE);
227 rc = netsnmp_register_watched_scalar2( reg, winfo );
228 if (rc != SNMPERR_SUCCESS)
229 goto bail;
230 eptr->reg[3] = reg;
231
232 return eptr;
233
234 bail:
235 if (eptr->reg[3])
236 netsnmp_unregister_handler(eptr->reg[3]);
237 if (eptr->reg[2])
238 netsnmp_unregister_handler(eptr->reg[2]);
239 if (eptr->reg[1])
240 netsnmp_unregister_handler(eptr->reg[1]);
241 if (eptr->reg[0])
242 netsnmp_unregister_handler(eptr->reg[0]);
243 return NULL;
244 }
245
246 static void
_unregister_extend(extend_registration_block * eptr)247 _unregister_extend(extend_registration_block *eptr)
248 {
249 extend_registration_block *prev;
250
251 netsnmp_assert(eptr);
252 for (prev = ereg_head; prev && prev->next != eptr; prev = prev->next)
253 ;
254 if (prev) {
255 netsnmp_assert(eptr == prev->next);
256 prev->next = eptr->next;
257 } else {
258 netsnmp_assert(eptr == ereg_head);
259 ereg_head = eptr->next;
260 }
261
262 netsnmp_table_data_delete_table(eptr->dinfo);
263 free(eptr->root_oid);
264 free(eptr);
265 }
266
267 int
extend_clear_callback(int majorID,int minorID,void * serverarg,void * clientarg)268 extend_clear_callback(int majorID, int minorID,
269 void *serverarg, void *clientarg)
270 {
271 extend_registration_block *eptr, *enext = NULL;
272
273 for ( eptr=ereg_head; eptr; eptr=enext ) {
274 enext=eptr->next;
275 netsnmp_unregister_handler( eptr->reg[0] );
276 netsnmp_unregister_handler( eptr->reg[1] );
277 netsnmp_unregister_handler( eptr->reg[2] );
278 netsnmp_unregister_handler( eptr->reg[3] );
279 SNMP_FREE(eptr);
280 }
281 ereg_head = NULL;
282 return 0;
283 }
284
init_extend(void)285 void init_extend( void )
286 {
287 snmpd_register_config_handler("extend", extend_parse_config, NULL, NULL);
288 snmpd_register_config_handler("extend-sh", extend_parse_config, NULL, NULL);
289 snmpd_register_config_handler("extendfix", extend_parse_config, NULL, NULL);
290 snmpd_register_config_handler("exec2", extend_parse_config, NULL, NULL);
291 snmpd_register_config_handler("sh2", extend_parse_config, NULL, NULL);
292 snmpd_register_config_handler("execFix2", extend_parse_config, NULL, NULL);
293 (void)_register_extend( ns_extend_oid, OID_LENGTH(ns_extend_oid));
294
295 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
296 snmpd_register_config_handler("exec", extend_parse_config, NULL, NULL);
297 snmpd_register_config_handler("sh", extend_parse_config, NULL, NULL);
298 snmpd_register_config_handler("execFix", extend_parse_config, NULL, NULL);
299 compatability_entries = (netsnmp_old_extend *)
300 calloc( max_compatability_entries, sizeof(netsnmp_old_extend));
301 REGISTER_MIB("ucd-extensible", old_extensible_variables,
302 variable2, old_extensible_variables_oid);
303 #endif
304
305 snmp_register_callback(SNMP_CALLBACK_APPLICATION,
306 SNMPD_CALLBACK_PRE_UPDATE_CONFIG,
307 extend_clear_callback, NULL);
308 }
309
310 void
shutdown_extend(void)311 shutdown_extend(void)
312 {
313 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
314 free(compatability_entries);
315 compatability_entries = NULL;
316 #endif
317 while (ereg_head)
318 _unregister_extend(ereg_head);
319 }
320
321 /*************************
322 *
323 * Cached-data hooks
324 * see 'cache_handler' helper
325 *
326 *************************/
327
328 int
extend_load_cache(netsnmp_cache * cache,void * magic)329 extend_load_cache(netsnmp_cache *cache, void *magic)
330 {
331 #ifndef USING_UTILITIES_EXECUTE_MODULE
332 NETSNMP_LOGONCE((LOG_WARNING,"support for run_exec_command not available\n"));
333 return -1;
334 #else
335 int out_len = 1024*100;
336 char out_buf[ 1024*100 ];
337 int cmd_len = 255*2 + 2; /* 2 * DisplayStrings */
338 char cmd_buf[ 255*2 + 2 ];
339 int ret;
340 char *cp;
341 char *line_buf[ 1024 ];
342 netsnmp_extend *extension = (netsnmp_extend *)magic;
343
344 if (!magic)
345 return -1;
346 DEBUGMSGTL(( "nsExtendTable:cache", "load %s", extension->token ));
347 if ( extension->args )
348 snprintf( cmd_buf, cmd_len, "%s %s", extension->command, extension->args );
349 else
350 snprintf( cmd_buf, cmd_len, "%s", extension->command );
351 if ( extension->flags & NS_EXTEND_FLAGS_SHELL )
352 ret = run_shell_command( cmd_buf, extension->input, out_buf, &out_len);
353 else
354 ret = run_exec_command( cmd_buf, extension->input, out_buf, &out_len);
355 DEBUGMSG(( "nsExtendTable:cache", ": %s : %d\n", cmd_buf, ret));
356 if (ret >= 0) {
357 if (out_buf[ out_len-1 ] == '\n')
358 out_buf[ --out_len ] = '\0'; /* Stomp on trailing newline */
359 extension->output = strdup( out_buf );
360 extension->out_len = out_len;
361 /*
362 * Now we need to pick the output apart into separate lines.
363 * Start by counting how many lines we've got, and keeping
364 * track of where each line starts in a static buffer
365 */
366 extension->numlines = 1;
367 line_buf[ 0 ] = extension->output;
368 for (cp=extension->output; *cp; cp++) {
369 if (*cp == '\n') {
370 line_buf[ extension->numlines++ ] = cp+1;
371 }
372 }
373 if ( extension->numlines > 1 ) {
374 extension->lines = (char**)calloc( sizeof(char *), extension->numlines );
375 memcpy( extension->lines, line_buf,
376 sizeof(char *) * extension->numlines );
377 } else {
378 extension->lines = &extension->output;
379 }
380 }
381 extension->result = ret;
382 return ret;
383 #endif /* !defined(USING_UTILITIES_EXECUTE_MODULE) */
384 }
385
386 void
extend_free_cache(netsnmp_cache * cache,void * magic)387 extend_free_cache(netsnmp_cache *cache, void *magic)
388 {
389 netsnmp_extend *extension = (netsnmp_extend *)magic;
390 if (!magic)
391 return;
392
393 DEBUGMSGTL(( "nsExtendTable:cache", "free %s\n", extension->token ));
394 if (extension->output) {
395 SNMP_FREE(extension->output);
396 extension->output = NULL;
397 }
398 if ( extension->numlines > 1 ) {
399 SNMP_FREE(extension->lines);
400 }
401 extension->lines = NULL;
402 extension->out_len = 0;
403 extension->numlines = 0;
404 }
405
406
407 /*************************
408 *
409 * Utility routines for setting up a new entry
410 * (either via SET requests, or the config file)
411 *
412 *************************/
413
414 void
_free_extension(netsnmp_extend * extension,extend_registration_block * ereg)415 _free_extension( netsnmp_extend *extension, extend_registration_block *ereg )
416 {
417 netsnmp_extend *eptr = NULL;
418 netsnmp_extend *eprev = NULL;
419
420 if (!extension)
421 return;
422
423 if (ereg) {
424 /* Unlink from 'ehead' list */
425 for (eptr=ereg->ehead; eptr; eptr=eptr->next) {
426 if (eptr == extension)
427 break;
428 eprev = eptr;
429 }
430 if (!eptr) {
431 snmp_log(LOG_ERR,
432 "extend: fell off end of list before finding extension\n");
433 return;
434 }
435 if (eprev)
436 eprev->next = eptr->next;
437 else
438 ereg->ehead = eptr->next;
439 netsnmp_table_data_remove_and_delete_row( ereg->dinfo, extension->row);
440 }
441
442 SNMP_FREE( extension->token );
443 SNMP_FREE( extension->cache );
444 SNMP_FREE( extension->command );
445 SNMP_FREE( extension->args );
446 SNMP_FREE( extension->input );
447 SNMP_FREE( extension );
448 return;
449 }
450
451 netsnmp_extend *
_new_extension(char * exec_name,int exec_flags,extend_registration_block * ereg)452 _new_extension( char *exec_name, int exec_flags, extend_registration_block *ereg )
453 {
454 netsnmp_extend *extension;
455 netsnmp_table_row *row;
456 netsnmp_extend *eptr1, *eptr2;
457 netsnmp_table_data *dinfo = ereg->dinfo;
458
459 if (!exec_name)
460 return NULL;
461 extension = SNMP_MALLOC_TYPEDEF( netsnmp_extend );
462 if (!extension)
463 return NULL;
464 extension->token = strdup( exec_name );
465 extension->flags = exec_flags;
466 extension->cache = netsnmp_cache_create( 0, extend_load_cache,
467 extend_free_cache, NULL, 0 );
468 if (extension->cache)
469 extension->cache->magic = extension;
470
471 row = netsnmp_create_table_data_row();
472 if (!row || !extension->cache) {
473 _free_extension( extension, ereg );
474 SNMP_FREE( row );
475 return NULL;
476 }
477 row->data = (void *)extension;
478 extension->row = row;
479 netsnmp_table_row_add_index( row, ASN_OCTET_STR,
480 exec_name, strlen(exec_name));
481 if ( netsnmp_table_data_add_row( dinfo, row) != SNMPERR_SUCCESS ) {
482 /* _free_extension( extension, ereg ); */
483 SNMP_FREE( extension ); /* Probably not sufficient */
484 SNMP_FREE( row );
485 return NULL;
486 }
487
488 ereg->num_entries++;
489 /*
490 * Now add this structure to a private linked list.
491 * We don't need this for the main tables - the
492 * 'table_data' helper will take care of those.
493 * But it's probably easier to handle the multi-line
494 * output table ourselves, for which we need access
495 * to the underlying data.
496 * So we'll keep a list internally as well.
497 */
498 for ( eptr1 = ereg->ehead, eptr2 = NULL;
499 eptr1;
500 eptr2 = eptr1, eptr1 = eptr1->next ) {
501
502 if (strlen( eptr1->token ) > strlen( exec_name ))
503 break;
504 if (strlen( eptr1->token ) == strlen( exec_name ) &&
505 strcmp( eptr1->token, exec_name ) > 0 )
506 break;
507 }
508 if ( eptr2 )
509 eptr2->next = extension;
510 else
511 ereg->ehead = extension;
512 extension->next = eptr1;
513 return extension;
514 }
515
516 void
extend_parse_config(const char * token,char * cptr)517 extend_parse_config(const char *token, char *cptr)
518 {
519 netsnmp_extend *extension;
520 char exec_name[STRMAX];
521 char exec_name2[STRMAX]; /* For use with UCD execFix directive */
522 char exec_command[STRMAX];
523 oid oid_buf[MAX_OID_LEN];
524 size_t oid_len;
525 extend_registration_block *eptr;
526 int flags;
527 int cache_timeout = 0;
528 int exec_type = NS_EXTEND_ETYPE_EXEC;
529
530 cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
531 if (strcmp(exec_name, "-cacheTime") == 0) {
532 char cache_timeout_str[32];
533
534 cptr = copy_nword(cptr, cache_timeout_str, sizeof(cache_timeout_str));
535 /* If atoi can't do the conversion, it returns 0 */
536 cache_timeout = atoi(cache_timeout_str);
537 cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
538 }
539 if (strcmp(exec_name, "-execType") == 0) {
540 char exec_type_str[16];
541
542 cptr = copy_nword(cptr, exec_type_str, sizeof(exec_type_str));
543 if (strcmp(exec_type_str, "sh") == 0)
544 exec_type = NS_EXTEND_ETYPE_SHELL;
545 else
546 exec_type = NS_EXTEND_ETYPE_EXEC;
547 cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
548 }
549 if ( *exec_name == '.' ) {
550 oid_len = MAX_OID_LEN - 2;
551 if (0 == read_objid( exec_name, oid_buf, &oid_len )) {
552 config_perror("ERROR: Unrecognised OID" );
553 return;
554 }
555 cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
556 if (!strcmp( token, "sh" ) ||
557 !strcmp( token, "exec" )) {
558 config_perror("ERROR: This output format has been deprecated - Please use the 'extend' directive instead" );
559 return;
560 }
561 } else {
562 memcpy( oid_buf, ns_extend_oid, sizeof(ns_extend_oid));
563 oid_len = OID_LENGTH(ns_extend_oid);
564 }
565 cptr = copy_nword(cptr, exec_command, sizeof(exec_command));
566 /* XXX - check 'exec_command' exists & is executable */
567 flags = (NS_EXTEND_FLAGS_ACTIVE | NS_EXTEND_FLAGS_CONFIG);
568 if (!strcmp( token, "sh" ) ||
569 !strcmp( token, "extend-sh" ) ||
570 !strcmp( token, "sh2") ||
571 exec_type == NS_EXTEND_ETYPE_SHELL)
572 flags |= NS_EXTEND_FLAGS_SHELL;
573 if (!strcmp( token, "execFix" ) ||
574 !strcmp( token, "extendfix" ) ||
575 !strcmp( token, "execFix2" )) {
576 strcpy( exec_name2, exec_name );
577 strcat( exec_name, "Fix" );
578 flags |= NS_EXTEND_FLAGS_WRITEABLE;
579 /* XXX - Check for shell... */
580 }
581
582 eptr = _register_extend( oid_buf, oid_len );
583 if (!eptr) {
584 snmp_log(LOG_ERR, "Failed to register extend entry '%s' - possibly duplicate name.\n", exec_name );
585 return;
586 }
587 extension = _new_extension( exec_name, flags, eptr );
588 if (extension) {
589 extension->command = strdup( exec_command );
590 if (cptr)
591 extension->args = strdup( cptr );
592 if (cache_timeout != 0)
593 extension->cache->timeout = cache_timeout;
594 } else {
595 snmp_log(LOG_ERR, "Failed to register extend entry '%s' - possibly duplicate name.\n", exec_name );
596 return;
597 }
598
599 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
600 /*
601 * Compatability with the UCD extTable
602 */
603 if (!strcmp( token, "execFix" )) {
604 int i;
605 for ( i=0; i < num_compatability_entries; i++ ) {
606 if (!strcmp( exec_name2,
607 compatability_entries[i].exec_entry->token))
608 break;
609 }
610 if ( i == num_compatability_entries )
611 config_perror("No matching exec entry" );
612 else
613 compatability_entries[ i ].efix_entry = extension;
614
615 } else if (!strcmp( token, "sh" ) ||
616 !strcmp( token, "exec" )) {
617 if ( num_compatability_entries == max_compatability_entries ) {
618 /* XXX - should really use dynamic allocation */
619 netsnmp_old_extend *new_compatability_entries;
620 new_compatability_entries = realloc(compatability_entries,
621 max_compatability_entries*2*sizeof(netsnmp_old_extend));
622 if (!new_compatability_entries)
623 config_perror("No further UCD-compatible entries" );
624 else {
625 memset(new_compatability_entries+num_compatability_entries, 0,
626 sizeof(netsnmp_old_extend)*max_compatability_entries);
627 max_compatability_entries *= 2;
628 compatability_entries = new_compatability_entries;
629 }
630 }
631 if (num_compatability_entries != max_compatability_entries)
632 compatability_entries[
633 num_compatability_entries++ ].exec_entry = extension;
634 }
635 #endif
636 }
637
638 /*************************
639 *
640 * Main table handlers
641 * Most of the work is handled
642 * by the 'table_data' helper.
643 *
644 *************************/
645
646 int
handle_nsExtendConfigTable(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)647 handle_nsExtendConfigTable(netsnmp_mib_handler *handler,
648 netsnmp_handler_registration *reginfo,
649 netsnmp_agent_request_info *reqinfo,
650 netsnmp_request_info *requests)
651 {
652 netsnmp_request_info *request;
653 netsnmp_table_request_info *table_info;
654 netsnmp_extend *extension;
655 extend_registration_block *eptr NETSNMP_ATTRIBUTE_UNUSED;
656 int i;
657 int need_to_validate NETSNMP_ATTRIBUTE_UNUSED = 0;
658
659 for ( request=requests; request; request=request->next ) {
660 if (request->processed)
661 continue;
662 table_info = netsnmp_extract_table_info( request );
663 extension = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
664
665 DEBUGMSGTL(( "nsExtendTable:config", "varbind: "));
666 DEBUGMSGOID(("nsExtendTable:config", request->requestvb->name,
667 request->requestvb->name_length));
668 DEBUGMSG(( "nsExtendTable:config", " (%s)\n",
669 se_find_label_in_slist("agent_mode", reqinfo->mode)));
670
671 switch (reqinfo->mode) {
672 case MODE_GET:
673 switch (table_info->colnum) {
674 case COLUMN_EXTCFG_COMMAND:
675 snmp_set_var_typed_value(
676 request->requestvb, ASN_OCTET_STR,
677 extension->command,
678 (extension->command)?strlen(extension->command):0);
679 break;
680 case COLUMN_EXTCFG_ARGS:
681 snmp_set_var_typed_value(
682 request->requestvb, ASN_OCTET_STR,
683 extension->args,
684 (extension->args)?strlen(extension->args):0);
685 break;
686 case COLUMN_EXTCFG_INPUT:
687 snmp_set_var_typed_value(
688 request->requestvb, ASN_OCTET_STR,
689 extension->input,
690 (extension->input)?strlen(extension->input):0);
691 break;
692 case COLUMN_EXTCFG_CACHETIME:
693 snmp_set_var_typed_value(
694 request->requestvb, ASN_INTEGER,
695 (u_char*)&extension->cache->timeout, sizeof(int));
696 break;
697 case COLUMN_EXTCFG_EXECTYPE:
698 i = ((extension->flags & NS_EXTEND_FLAGS_SHELL) ?
699 NS_EXTEND_ETYPE_SHELL :
700 NS_EXTEND_ETYPE_EXEC);
701 snmp_set_var_typed_value(
702 request->requestvb, ASN_INTEGER,
703 (u_char*)&i, sizeof(i));
704 break;
705 case COLUMN_EXTCFG_RUNTYPE:
706 i = ((extension->flags & NS_EXTEND_FLAGS_WRITEABLE) ?
707 NS_EXTEND_RTYPE_RWRITE :
708 NS_EXTEND_RTYPE_RONLY);
709 snmp_set_var_typed_value(
710 request->requestvb, ASN_INTEGER,
711 (u_char*)&i, sizeof(i));
712 break;
713
714 case COLUMN_EXTCFG_STORAGE:
715 i = ((extension->flags & NS_EXTEND_FLAGS_CONFIG) ?
716 ST_PERMANENT : ST_VOLATILE);
717 snmp_set_var_typed_value(
718 request->requestvb, ASN_INTEGER,
719 (u_char*)&i, sizeof(i));
720 break;
721 case COLUMN_EXTCFG_STATUS:
722 i = ((extension->flags & NS_EXTEND_FLAGS_ACTIVE) ?
723 RS_ACTIVE :
724 RS_NOTINSERVICE);
725 snmp_set_var_typed_value(
726 request->requestvb, ASN_INTEGER,
727 (u_char*)&i, sizeof(i));
728 break;
729
730 default:
731 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
732 continue;
733 }
734 break;
735
736 /**********
737 *
738 * Start of SET handling
739 *
740 * All config objects are potentially writable except
741 * nsExtendStorage which is fixed as either 'permanent'
742 * (if read from a config file) or 'volatile' (if set via SNMP)
743 * The string-based settings of a 'permanent' entry cannot
744 * be changed - neither can the execution or run type.
745 * Such entries can be (temporarily) marked as inactive,
746 * and the cache timeout adjusted, but these changes are
747 * not persistent.
748 *
749 **********/
750
751 #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS
752 case MODE_SET_RESERVE1:
753 /*
754 * Validate the new assignments
755 */
756 switch (table_info->colnum) {
757 case COLUMN_EXTCFG_COMMAND:
758 if (request->requestvb->type != ASN_OCTET_STR) {
759 netsnmp_set_request_error(reqinfo, request,
760 SNMP_ERR_WRONGTYPE);
761 return SNMP_ERR_WRONGTYPE;
762 }
763 /*
764 * Must have a full path to the command
765 * XXX - Assumes Unix-style paths
766 */
767 if (request->requestvb->val_len == 0 ||
768 request->requestvb->val.string[0] != '/') {
769 netsnmp_set_request_error(reqinfo, request,
770 SNMP_ERR_WRONGVALUE);
771 return SNMP_ERR_WRONGVALUE;
772 }
773 /*
774 * XXX - need to check this file exists
775 * (and is executable)
776 */
777
778 if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
779 /*
780 * config entries are "permanent" so can't be changed
781 */
782 netsnmp_set_request_error(reqinfo, request,
783 SNMP_ERR_NOTWRITABLE);
784 return SNMP_ERR_NOTWRITABLE;
785 }
786 break;
787
788 case COLUMN_EXTCFG_ARGS:
789 case COLUMN_EXTCFG_INPUT:
790 if (request->requestvb->type != ASN_OCTET_STR) {
791 netsnmp_set_request_error(reqinfo, request,
792 SNMP_ERR_WRONGTYPE);
793 return SNMP_ERR_WRONGTYPE;
794 }
795
796 if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
797 /*
798 * config entries are "permanent" so can't be changed
799 */
800 netsnmp_set_request_error(reqinfo, request,
801 SNMP_ERR_NOTWRITABLE);
802 return SNMP_ERR_NOTWRITABLE;
803 }
804 break;
805
806 case COLUMN_EXTCFG_CACHETIME:
807 if (request->requestvb->type != ASN_INTEGER) {
808 netsnmp_set_request_error(reqinfo, request,
809 SNMP_ERR_WRONGTYPE);
810 return SNMP_ERR_WRONGTYPE;
811 }
812 i = *request->requestvb->val.integer;
813 /*
814 * -1 is a special value indicating "don't cache"
815 * [[ XXX - should this be 0 ?? ]]
816 * Otherwise, cache times must be non-negative
817 */
818 if (i < -1 ) {
819 netsnmp_set_request_error(reqinfo, request,
820 SNMP_ERR_WRONGVALUE);
821 return SNMP_ERR_WRONGVALUE;
822 }
823 break;
824
825 case COLUMN_EXTCFG_EXECTYPE:
826 if (request->requestvb->type != ASN_INTEGER) {
827 netsnmp_set_request_error(reqinfo, request,
828 SNMP_ERR_WRONGTYPE);
829 return SNMP_ERR_WRONGTYPE;
830 }
831 i = *request->requestvb->val.integer;
832 if (i<1 || i>2) { /* 'exec(1)' or 'shell(2)' only */
833 netsnmp_set_request_error(reqinfo, request,
834 SNMP_ERR_WRONGVALUE);
835 return SNMP_ERR_WRONGVALUE;
836 }
837 if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
838 /*
839 * config entries are "permanent" so can't be changed
840 */
841 netsnmp_set_request_error(reqinfo, request,
842 SNMP_ERR_NOTWRITABLE);
843 return SNMP_ERR_NOTWRITABLE;
844 }
845 break;
846
847 case COLUMN_EXTCFG_RUNTYPE:
848 if (request->requestvb->type != ASN_INTEGER) {
849 netsnmp_set_request_error(reqinfo, request,
850 SNMP_ERR_WRONGTYPE);
851 return SNMP_ERR_WRONGTYPE;
852 }
853 /*
854 * 'run-on-read(1)', 'run-on-set(2)'
855 * or 'run-command(3)' only
856 */
857 i = *request->requestvb->val.integer;
858 if (i<1 || i>3) {
859 netsnmp_set_request_error(reqinfo, request,
860 SNMP_ERR_WRONGVALUE);
861 return SNMP_ERR_WRONGVALUE;
862 }
863 /*
864 * 'run-command(3)' can only be used with
865 * a pre-existing 'run-on-set(2)' entry.
866 */
867 if (i==3 && !(extension && (extension->flags & NS_EXTEND_FLAGS_WRITEABLE))) {
868 netsnmp_set_request_error(reqinfo, request,
869 SNMP_ERR_INCONSISTENTVALUE);
870 return SNMP_ERR_INCONSISTENTVALUE;
871 }
872 /*
873 * 'run-command(3)' is the only valid assignment
874 * for permanent (i.e. config) entries
875 */
876 if ((extension && extension->flags & NS_EXTEND_FLAGS_CONFIG)
877 && i!=3 ) {
878 netsnmp_set_request_error(reqinfo, request,
879 SNMP_ERR_INCONSISTENTVALUE);
880 return SNMP_ERR_INCONSISTENTVALUE;
881 }
882 break;
883
884 case COLUMN_EXTCFG_STATUS:
885 if (request->requestvb->type != ASN_INTEGER) {
886 netsnmp_set_request_error(reqinfo, request,
887 SNMP_ERR_WRONGTYPE);
888 return SNMP_ERR_WRONGTYPE;
889 }
890 i = *request->requestvb->val.integer;
891 switch (i) {
892 case RS_ACTIVE:
893 case RS_NOTINSERVICE:
894 if (!extension) {
895 /* Must be used with existing rows */
896 netsnmp_set_request_error(reqinfo, request,
897 SNMP_ERR_INCONSISTENTVALUE);
898 return SNMP_ERR_INCONSISTENTVALUE;
899 }
900 break; /* OK */
901 case RS_CREATEANDGO:
902 case RS_CREATEANDWAIT:
903 if (extension) {
904 /* Can only be used to create new rows */
905 netsnmp_set_request_error(reqinfo, request,
906 SNMP_ERR_INCONSISTENTVALUE);
907 return SNMP_ERR_INCONSISTENTVALUE;
908 }
909 break;
910 case RS_DESTROY:
911 break;
912 default:
913 netsnmp_set_request_error(reqinfo, request,
914 SNMP_ERR_WRONGVALUE);
915 return SNMP_ERR_WRONGVALUE;
916 }
917 break;
918
919 default:
920 netsnmp_set_request_error(reqinfo, request,
921 SNMP_ERR_NOTWRITABLE);
922 return SNMP_ERR_NOTWRITABLE;
923 }
924 break;
925
926 case MODE_SET_RESERVE2:
927 switch (table_info->colnum) {
928 case COLUMN_EXTCFG_STATUS:
929 i = *request->requestvb->val.integer;
930 switch (i) {
931 case RS_CREATEANDGO:
932 case RS_CREATEANDWAIT:
933 eptr = _find_extension_block( request->requestvb->name,
934 request->requestvb->name_length );
935 extension = _new_extension( (char *) table_info->indexes->val.string,
936 0, eptr );
937 if (!extension) { /* failed */
938 netsnmp_set_request_error(reqinfo, request,
939 SNMP_ERR_RESOURCEUNAVAILABLE);
940 return SNMP_ERR_RESOURCEUNAVAILABLE;
941 }
942 netsnmp_insert_table_row( request, extension->row );
943 }
944 }
945 break;
946
947 case MODE_SET_FREE:
948 switch (table_info->colnum) {
949 case COLUMN_EXTCFG_STATUS:
950 i = *request->requestvb->val.integer;
951 switch (i) {
952 case RS_CREATEANDGO:
953 case RS_CREATEANDWAIT:
954 eptr = _find_extension_block( request->requestvb->name,
955 request->requestvb->name_length );
956 _free_extension( extension, eptr );
957 }
958 }
959 break;
960
961 case MODE_SET_ACTION:
962 switch (table_info->colnum) {
963 case COLUMN_EXTCFG_COMMAND:
964 extension->old_command = extension->command;
965 extension->command = netsnmp_strdup_and_null(
966 request->requestvb->val.string,
967 request->requestvb->val_len);
968 break;
969 case COLUMN_EXTCFG_ARGS:
970 extension->old_args = extension->args;
971 extension->args = netsnmp_strdup_and_null(
972 request->requestvb->val.string,
973 request->requestvb->val_len);
974 break;
975 case COLUMN_EXTCFG_INPUT:
976 extension->old_input = extension->input;
977 extension->input = netsnmp_strdup_and_null(
978 request->requestvb->val.string,
979 request->requestvb->val_len);
980 break;
981 case COLUMN_EXTCFG_STATUS:
982 i = *request->requestvb->val.integer;
983 switch (i) {
984 case RS_ACTIVE:
985 case RS_CREATEANDGO:
986 need_to_validate = 1;
987 }
988 break;
989 }
990 break;
991
992 case MODE_SET_UNDO:
993 switch (table_info->colnum) {
994 case COLUMN_EXTCFG_COMMAND:
995 if ( extension && extension->old_command ) {
996 SNMP_FREE(extension->command);
997 extension->command = extension->old_command;
998 extension->old_command = NULL;
999 }
1000 break;
1001 case COLUMN_EXTCFG_ARGS:
1002 if ( extension && extension->old_args ) {
1003 SNMP_FREE(extension->args);
1004 extension->args = extension->old_args;
1005 extension->old_args = NULL;
1006 }
1007 break;
1008 case COLUMN_EXTCFG_INPUT:
1009 if ( extension && extension->old_input ) {
1010 SNMP_FREE(extension->input);
1011 extension->input = extension->old_input;
1012 extension->old_input = NULL;
1013 }
1014 break;
1015 case COLUMN_EXTCFG_STATUS:
1016 i = *request->requestvb->val.integer;
1017 switch (i) {
1018 case RS_CREATEANDGO:
1019 case RS_CREATEANDWAIT:
1020 eptr = _find_extension_block( request->requestvb->name,
1021 request->requestvb->name_length );
1022 _free_extension( extension, eptr );
1023 }
1024 break;
1025 }
1026 break;
1027
1028 case MODE_SET_COMMIT:
1029 switch (table_info->colnum) {
1030 case COLUMN_EXTCFG_CACHETIME:
1031 i = *request->requestvb->val.integer;
1032 extension->cache->timeout = i;
1033 break;
1034
1035 case COLUMN_EXTCFG_RUNTYPE:
1036 i = *request->requestvb->val.integer;
1037 switch (i) {
1038 case 1:
1039 extension->flags &= ~NS_EXTEND_FLAGS_WRITEABLE;
1040 break;
1041 case 2:
1042 extension->flags |= NS_EXTEND_FLAGS_WRITEABLE;
1043 break;
1044 case 3:
1045 (void)netsnmp_cache_check_and_reload( extension->cache );
1046 break;
1047 }
1048 break;
1049
1050 case COLUMN_EXTCFG_EXECTYPE:
1051 i = *request->requestvb->val.integer;
1052 if ( i == NS_EXTEND_ETYPE_SHELL )
1053 extension->flags |= NS_EXTEND_FLAGS_SHELL;
1054 else
1055 extension->flags &= ~NS_EXTEND_FLAGS_SHELL;
1056 break;
1057
1058 case COLUMN_EXTCFG_STATUS:
1059 i = *request->requestvb->val.integer;
1060 switch (i) {
1061 case RS_ACTIVE:
1062 case RS_CREATEANDGO:
1063 extension->flags |= NS_EXTEND_FLAGS_ACTIVE;
1064 break;
1065 case RS_NOTINSERVICE:
1066 case RS_CREATEANDWAIT:
1067 extension->flags &= ~NS_EXTEND_FLAGS_ACTIVE;
1068 break;
1069 case RS_DESTROY:
1070 eptr = _find_extension_block( request->requestvb->name,
1071 request->requestvb->name_length );
1072 _free_extension( extension, eptr );
1073 break;
1074 }
1075 }
1076 break;
1077 #endif /* !NETSNMP_NO_WRITE_SUPPORT and ENABLE_EXTEND_WRITE_ACCESS */
1078
1079 default:
1080 netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
1081 return SNMP_ERR_GENERR;
1082 }
1083 }
1084
1085 #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS
1086 /*
1087 * If we're marking a given row as active,
1088 * then we need to check that it's ready.
1089 */
1090 if (need_to_validate) {
1091 for ( request=requests; request; request=request->next ) {
1092 if (request->processed)
1093 continue;
1094 table_info = netsnmp_extract_table_info( request );
1095 extension = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
1096 switch (table_info->colnum) {
1097 case COLUMN_EXTCFG_STATUS:
1098 i = *request->requestvb->val.integer;
1099 if (( i == RS_ACTIVE || i == RS_CREATEANDGO ) &&
1100 !(extension && extension->command &&
1101 extension->command[0] == '/' /* &&
1102 is_executable(extension->command) */)) {
1103 netsnmp_set_request_error(reqinfo, request,
1104 SNMP_ERR_INCONSISTENTVALUE);
1105 return SNMP_ERR_INCONSISTENTVALUE;
1106 }
1107 }
1108 }
1109 }
1110 #endif /* !NETSNMP_NO_WRITE_SUPPORT && ENABLE_EXTEND_WRITE_ACCESS */
1111
1112 return SNMP_ERR_NOERROR;
1113 }
1114
1115
1116 int
handle_nsExtendOutput1Table(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)1117 handle_nsExtendOutput1Table(netsnmp_mib_handler *handler,
1118 netsnmp_handler_registration *reginfo,
1119 netsnmp_agent_request_info *reqinfo,
1120 netsnmp_request_info *requests)
1121 {
1122 netsnmp_request_info *request;
1123 netsnmp_table_request_info *table_info;
1124 netsnmp_extend *extension;
1125 int len;
1126
1127 for ( request=requests; request; request=request->next ) {
1128 if (request->processed)
1129 continue;
1130 table_info = netsnmp_extract_table_info( request );
1131 extension = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
1132
1133 DEBUGMSGTL(( "nsExtendTable:output1", "varbind: "));
1134 DEBUGMSGOID(("nsExtendTable:output1", request->requestvb->name,
1135 request->requestvb->name_length));
1136 DEBUGMSG(( "nsExtendTable:output1", "\n"));
1137
1138 switch (reqinfo->mode) {
1139 case MODE_GET:
1140 if (!extension || !(extension->flags & NS_EXTEND_FLAGS_ACTIVE)) {
1141 /*
1142 * If this row is inactive, then skip it.
1143 */
1144 netsnmp_set_request_error(reqinfo, request,
1145 SNMP_NOSUCHINSTANCE);
1146 continue;
1147 }
1148 if (!(extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
1149 (netsnmp_cache_check_and_reload( extension->cache ) < 0 )) {
1150 /*
1151 * If reloading the output cache of a 'run-on-read'
1152 * entry fails, then skip it.
1153 */
1154 netsnmp_set_request_error(reqinfo, request,
1155 SNMP_NOSUCHINSTANCE);
1156 continue;
1157 }
1158 if ((extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
1159 (netsnmp_cache_check_expired( extension->cache ) == 1 )) {
1160 /*
1161 * If the output cache of a 'run-on-write'
1162 * entry has expired, then skip it.
1163 */
1164 netsnmp_set_request_error(reqinfo, request,
1165 SNMP_NOSUCHINSTANCE);
1166 continue;
1167 }
1168
1169 switch (table_info->colnum) {
1170 case COLUMN_EXTOUT1_OUTLEN:
1171 snmp_set_var_typed_value(
1172 request->requestvb, ASN_INTEGER,
1173 (u_char*)&extension->out_len, sizeof(int));
1174 break;
1175 case COLUMN_EXTOUT1_OUTPUT1:
1176 /*
1177 * If we've got more than one line,
1178 * find the length of the first one.
1179 * Otherwise find the length of the whole string.
1180 */
1181 if (extension->numlines > 1) {
1182 len = (extension->lines[1])-(extension->output) -1;
1183 } else if (extension->output) {
1184 len = strlen(extension->output);
1185 } else {
1186 len = 0;
1187 }
1188 snmp_set_var_typed_value(
1189 request->requestvb, ASN_OCTET_STR,
1190 extension->output, len);
1191 break;
1192 case COLUMN_EXTOUT1_OUTPUT2:
1193 snmp_set_var_typed_value(
1194 request->requestvb, ASN_OCTET_STR,
1195 extension->output,
1196 (extension->output)?extension->out_len:0);
1197 break;
1198 case COLUMN_EXTOUT1_NUMLINES:
1199 snmp_set_var_typed_value(
1200 request->requestvb, ASN_INTEGER,
1201 (u_char*)&extension->numlines, sizeof(int));
1202 break;
1203 case COLUMN_EXTOUT1_RESULT:
1204 snmp_set_var_typed_value(
1205 request->requestvb, ASN_INTEGER,
1206 (u_char*)&extension->result, sizeof(int));
1207 break;
1208 default:
1209 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
1210 continue;
1211 }
1212 break;
1213 default:
1214 netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
1215 return SNMP_ERR_GENERR;
1216 }
1217 }
1218 return SNMP_ERR_NOERROR;
1219 }
1220
1221
1222 /*************************
1223 *
1224 * Multi-line output table handler
1225 * Most of the work is handled here.
1226 *
1227 *************************/
1228
1229
1230 /*
1231 * Locate the appropriate entry for a given request
1232 */
1233 netsnmp_extend *
_extend_find_entry(netsnmp_request_info * request,netsnmp_table_request_info * table_info,int mode)1234 _extend_find_entry( netsnmp_request_info *request,
1235 netsnmp_table_request_info *table_info,
1236 int mode )
1237 {
1238 netsnmp_extend *eptr;
1239 extend_registration_block *ereg;
1240 unsigned int line_idx;
1241 oid oid_buf[MAX_OID_LEN];
1242 int oid_len;
1243 int i;
1244 char *token;
1245 size_t token_len;
1246
1247 if (!request || !table_info || !table_info->indexes
1248 || !table_info->indexes->next_variable) {
1249 DEBUGMSGTL(( "nsExtendTable:output2", "invalid invocation\n"));
1250 return NULL;
1251 }
1252
1253 ereg = _find_extension_block( request->requestvb->name,
1254 request->requestvb->name_length );
1255
1256 /***
1257 * GET handling - find the exact entry being requested
1258 ***/
1259 if ( mode == MODE_GET ) {
1260 DEBUGMSGTL(( "nsExtendTable:output2", "GET: %s / %ld\n ",
1261 table_info->indexes->val.string,
1262 *table_info->indexes->next_variable->val.integer));
1263 for ( eptr = ereg->ehead; eptr; eptr = eptr->next ) {
1264 if ( !strcmp( eptr->token, (char *) table_info->indexes->val.string ))
1265 break;
1266 }
1267
1268 if ( eptr ) {
1269 /*
1270 * Ensure the output is available...
1271 */
1272 if (!(eptr->flags & NS_EXTEND_FLAGS_ACTIVE) ||
1273 (netsnmp_cache_check_and_reload( eptr->cache ) < 0 ))
1274 return NULL;
1275
1276 /*
1277 * ...and check the line requested is valid
1278 */
1279 line_idx = *table_info->indexes->next_variable->val.integer;
1280 if (line_idx < 1 || line_idx > eptr->numlines)
1281 return NULL;
1282 }
1283 }
1284
1285 /***
1286 * GETNEXT handling - find the first suitable entry
1287 ***/
1288 else {
1289 if (!table_info->indexes->val_len ) {
1290 DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: first entry\n"));
1291 /*
1292 * Beginning of the table - find the first active
1293 * (and successful) entry, and use the first line of it
1294 */
1295 for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
1296 if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
1297 (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
1298 line_idx = 1;
1299 break;
1300 }
1301 }
1302 } else {
1303 token = (char *) table_info->indexes->val.string;
1304 token_len = table_info->indexes->val_len;
1305 line_idx = *table_info->indexes->next_variable->val.integer;
1306 DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: %s / %d\n ",
1307 token, line_idx ));
1308 /*
1309 * Otherwise, find the first entry not earlier
1310 * than the requested token...
1311 */
1312 for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
1313 if ( strlen(eptr->token) > token_len )
1314 break;
1315 if ( strlen(eptr->token) == token_len &&
1316 strcmp(eptr->token, token) >= 0 )
1317 break;
1318 }
1319 if (!eptr)
1320 return NULL; /* (assuming there is one) */
1321
1322 /*
1323 * ... and make sure it's active & the output is available
1324 * (or use the first following entry that is)
1325 */
1326 for ( ; eptr; eptr = eptr->next ) {
1327 if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
1328 (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
1329 break;
1330 }
1331 line_idx = 1;
1332 }
1333
1334 if (!eptr)
1335 return NULL; /* (assuming there is one) */
1336
1337 /*
1338 * If we're working with the same entry that was requested,
1339 * see whether we've reached the end of the output...
1340 */
1341 if (!strcmp( eptr->token, token )) {
1342 if ( eptr->numlines <= line_idx ) {
1343 /*
1344 * ... and if so, move on to the first line
1345 * of the next (active and successful) entry.
1346 */
1347 line_idx = 1;
1348 for (eptr = eptr->next ; eptr; eptr = eptr->next ) {
1349 if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
1350 (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
1351 break;
1352 }
1353 }
1354 } else {
1355 /*
1356 * Otherwise just use the next line of this entry.
1357 */
1358 line_idx++;
1359 }
1360 }
1361 else {
1362 /*
1363 * If this is not the same entry that was requested,
1364 * then we should return the first line.
1365 */
1366 line_idx = 1;
1367 }
1368 }
1369 if (eptr) {
1370 DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT -> %s / %d\n ",
1371 eptr->token, line_idx));
1372 /*
1373 * Since we're processing a GETNEXT request,
1374 * now we've found the appropriate entry (and line),
1375 * we need to update the varbind OID ...
1376 */
1377 memset(oid_buf, 0, sizeof(oid_buf));
1378 oid_len = ereg->oid_len;
1379 memcpy( oid_buf, ereg->root_oid, oid_len*sizeof(oid));
1380 oid_buf[ oid_len++ ] = 4; /* nsExtendOutput2Table */
1381 oid_buf[ oid_len++ ] = 1; /* nsExtendOutput2Entry */
1382 oid_buf[ oid_len++ ] = COLUMN_EXTOUT2_OUTLINE;
1383 /* string token index */
1384 oid_buf[ oid_len++ ] = strlen(eptr->token);
1385 for ( i=0; i < (int)strlen(eptr->token); i++ )
1386 oid_buf[ oid_len+i ] = eptr->token[i];
1387 oid_len += strlen( eptr->token );
1388 /* plus line number */
1389 oid_buf[ oid_len++ ] = line_idx;
1390 snmp_set_var_objid( request->requestvb, oid_buf, oid_len );
1391 /*
1392 * ... and index values to match.
1393 */
1394 snmp_set_var_value( table_info->indexes,
1395 eptr->token, strlen(eptr->token));
1396 snmp_set_var_value( table_info->indexes->next_variable,
1397 (const u_char*)&line_idx, sizeof(line_idx));
1398 }
1399 }
1400 return eptr; /* Finally, signal success */
1401 }
1402
1403 /*
1404 * Multi-line output handler
1405 * Locate the appropriate entry (using _extend_find_entry)
1406 * and return the appropriate output line
1407 */
1408 int
handle_nsExtendOutput2Table(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)1409 handle_nsExtendOutput2Table(netsnmp_mib_handler *handler,
1410 netsnmp_handler_registration *reginfo,
1411 netsnmp_agent_request_info *reqinfo,
1412 netsnmp_request_info *requests)
1413 {
1414 netsnmp_request_info *request;
1415 netsnmp_table_request_info *table_info;
1416 netsnmp_extend *extension;
1417 char *cp;
1418 unsigned int line_idx;
1419 int len;
1420
1421 for ( request=requests; request; request=request->next ) {
1422 if (request->processed)
1423 continue;
1424
1425 table_info = netsnmp_extract_table_info( request );
1426 extension = _extend_find_entry( request, table_info, reqinfo->mode );
1427
1428 DEBUGMSGTL(( "nsExtendTable:output2", "varbind: "));
1429 DEBUGMSGOID(("nsExtendTable:output2", request->requestvb->name,
1430 request->requestvb->name_length));
1431 DEBUGMSG(( "nsExtendTable:output2", " (%s)\n",
1432 (extension) ? extension->token : "[none]"));
1433
1434 if (!extension) {
1435 if (reqinfo->mode == MODE_GET)
1436 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
1437 else
1438 netsnmp_set_request_error(reqinfo, request, SNMP_ENDOFMIBVIEW);
1439 continue;
1440 }
1441
1442 switch (reqinfo->mode) {
1443 case MODE_GET:
1444 case MODE_GETNEXT:
1445 switch (table_info->colnum) {
1446 case COLUMN_EXTOUT2_OUTLINE:
1447 /*
1448 * Determine which line we've been asked for....
1449 */
1450 line_idx = *table_info->indexes->next_variable->val.integer;
1451 if (line_idx < 1 || line_idx > extension->numlines) {
1452 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
1453 continue;
1454 }
1455 cp = extension->lines[line_idx-1];
1456
1457 /*
1458 * ... and how long it is.
1459 */
1460 if ( extension->numlines > line_idx )
1461 len = (extension->lines[line_idx])-cp -1;
1462 else if (cp)
1463 len = strlen(cp);
1464 else
1465 len = 0;
1466
1467 snmp_set_var_typed_value( request->requestvb,
1468 ASN_OCTET_STR, cp, len );
1469 break;
1470 default:
1471 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
1472 continue;
1473 }
1474 break;
1475 default:
1476 netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
1477 return SNMP_ERR_GENERR;
1478 }
1479 }
1480 return SNMP_ERR_NOERROR;
1481 }
1482
1483 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
1484 /*************************
1485 *
1486 * Compatability with the UCD extTable
1487 *
1488 *************************/
1489
_get_cmdline(netsnmp_extend * extend)1490 char * _get_cmdline(netsnmp_extend *extend)
1491 {
1492 size_t size;
1493 char *newbuf;
1494 const char *args = extend->args;
1495
1496 if (args == NULL)
1497 /* Use empty string for processes without arguments. */
1498 args = "";
1499
1500 size = strlen(extend->command) + strlen(args) + 2;
1501 if (size > cmdlinesize) {
1502 newbuf = realloc(cmdlinebuf, size);
1503 if (!newbuf) {
1504 free(cmdlinebuf);
1505 cmdlinebuf = NULL;
1506 cmdlinesize = 0;
1507 return NULL;
1508 }
1509 cmdlinebuf = newbuf;
1510 cmdlinesize = size;
1511 }
1512 sprintf(cmdlinebuf, "%s %s", extend->command, args);
1513 return cmdlinebuf;
1514 }
1515
1516 u_char *
var_extensible_old(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)1517 var_extensible_old(struct variable * vp,
1518 oid * name,
1519 size_t * length,
1520 int exact,
1521 size_t * var_len, WriteMethod ** write_method)
1522 {
1523 netsnmp_old_extend *exten = NULL;
1524 static long long_ret;
1525 unsigned int idx;
1526 char *cmdline;
1527
1528 if (header_simple_table
1529 (vp, name, length, exact, var_len, write_method, num_compatability_entries))
1530 return (NULL);
1531
1532 idx = name[*length-1] -1;
1533 if (idx > max_compatability_entries)
1534 return NULL;
1535 exten = &compatability_entries[idx];
1536 switch (vp->magic) {
1537 case MIBINDEX:
1538 long_ret = name[*length - 1];
1539 return (u_char *) &long_ret;
1540 case ERRORNAME: /* name defined in config file */
1541 *var_len = strlen(exten->exec_entry->token);
1542 return ((u_char *) (exten->exec_entry->token));
1543 case SHELLCOMMAND:
1544 cmdline = _get_cmdline(exten->exec_entry);
1545 if (cmdline)
1546 *var_len = strlen(cmdline);
1547 return (u_char *) cmdline;
1548 case ERRORFLAG: /* return code from the process */
1549 netsnmp_cache_check_and_reload( exten->exec_entry->cache );
1550 long_ret = exten->exec_entry->result;
1551 return (u_char *) &long_ret;
1552 case ERRORMSG: /* first line of text returned from the process */
1553 netsnmp_cache_check_and_reload( exten->exec_entry->cache );
1554 if (exten->exec_entry->numlines > 1) {
1555 *var_len = (exten->exec_entry->lines[1])-
1556 (exten->exec_entry->output) -1;
1557 } else if (exten->exec_entry->output) {
1558 *var_len = strlen(exten->exec_entry->output);
1559 } else {
1560 *var_len = 0;
1561 }
1562 return (u_char *) exten->exec_entry->output;
1563 case ERRORFIX:
1564 *write_method = fixExec2Error;
1565 long_return = 0;
1566 return (u_char *) &long_return;
1567
1568 case ERRORFIXCMD:
1569 if (exten->efix_entry) {
1570 cmdline = _get_cmdline(exten->efix_entry);
1571 if (cmdline)
1572 *var_len = strlen(cmdline);
1573 return (u_char *) cmdline;
1574 } else {
1575 *var_len = 0;
1576 return (u_char *) &long_return; /* Just needs to be non-null! */
1577 }
1578 }
1579 return NULL;
1580 }
1581
1582
1583 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)1584 fixExec2Error(int action,
1585 u_char * var_val,
1586 u_char var_val_type,
1587 size_t var_val_len,
1588 u_char * statP, oid * name, size_t name_len)
1589 {
1590 netsnmp_old_extend *exten = NULL;
1591 unsigned int idx;
1592
1593 idx = name[name_len-1] -1;
1594 exten = &compatability_entries[ idx ];
1595
1596 #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS
1597 switch (action) {
1598 case MODE_SET_RESERVE1:
1599 if (var_val_type != ASN_INTEGER) {
1600 snmp_log(LOG_ERR, "Wrong type != int\n");
1601 return SNMP_ERR_WRONGTYPE;
1602 }
1603 idx = *((long *) var_val);
1604 if (idx != 1) {
1605 snmp_log(LOG_ERR, "Wrong value != 1\n");
1606 return SNMP_ERR_WRONGVALUE;
1607 }
1608 if (!exten || !exten->efix_entry) {
1609 snmp_log(LOG_ERR, "No command to run\n");
1610 return SNMP_ERR_GENERR;
1611 }
1612 return SNMP_ERR_NOERROR;
1613
1614 case MODE_SET_COMMIT:
1615 netsnmp_cache_check_and_reload( exten->efix_entry->cache );
1616 }
1617 #endif /* !NETSNMP_NO_WRITE_SUPPORT && ENABLE_EXTEND_WRITE_ACCESS */
1618 return SNMP_ERR_NOERROR;
1619 }
1620 #endif /* USING_UCD_SNMP_EXTENSIBLE_MODULE */
1621