| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-features.h> |
| |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #include <stdio.h> |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <ctype.h> |
| #include <sys/types.h> |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if HAVE_SYS_WAIT_H |
| # include <sys/wait.h> |
| #endif |
| #ifdef HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| #ifdef WIN32 |
| #include <limits.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include "struct.h" |
| #include "pass.h" |
| #include "pass_common.h" |
| #include "extensible.h" |
| #include "util_funcs.h" |
| |
| netsnmp_feature_require(get_exten_instance) |
| netsnmp_feature_require(parse_miboid) |
| |
| struct extensible *passthrus = NULL; |
| int numpassthrus = 0; |
| |
| /* |
| * the relocatable extensible commands variables |
| */ |
| struct variable2 extensible_passthru_variables[] = { |
| /* |
| * bogus entry. Only some of it is actually used. |
| */ |
| {MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, |
| var_extensible_pass, 0, {MIBINDEX}}, |
| }; |
| |
| |
| |
| void |
| init_pass(void) |
| { |
| snmpd_register_config_handler("pass", pass_parse_config, |
| pass_free_config, "miboid command"); |
| } |
| |
| void |
| pass_parse_config(const char *token, char *cptr) |
| { |
| struct extensible **ppass = &passthrus, **etmp, *ptmp; |
| char *tcptr, *endopt; |
| int i; |
| unsigned long priority; |
| |
| /* |
| * options |
| */ |
| priority = DEFAULT_MIB_PRIORITY; |
| while (*cptr == '-') { |
| cptr++; |
| switch (*cptr) { |
| case 'p': |
| /* change priority level */ |
| cptr++; |
| cptr = skip_white(cptr); |
| if (! isdigit((unsigned char)(*cptr))) { |
| config_perror("priority must be an integer"); |
| return; |
| } |
| priority = strtol((const char*) cptr, &endopt, 0); |
| if ((priority == LONG_MIN) || (priority == LONG_MAX)) { |
| config_perror("priority under/overflow"); |
| return; |
| } |
| cptr = endopt; |
| cptr = skip_white(cptr); |
| break; |
| default: |
| config_perror("unknown option for pass directive"); |
| return; |
| } |
| } |
| |
| /* |
| * MIB |
| */ |
| if (*cptr == '.') |
| cptr++; |
| if (!isdigit((unsigned char)(*cptr))) { |
| config_perror("second token is not a OID"); |
| return; |
| } |
| numpassthrus++; |
| |
| while (*ppass != NULL) |
| ppass = &((*ppass)->next); |
| (*ppass) = (struct extensible *) malloc(sizeof(struct extensible)); |
| if (*ppass == NULL) |
| return; |
| (*ppass)->type = PASSTHRU; |
| (*ppass)->mibpriority = priority; |
| |
| (*ppass)->miblen = parse_miboid(cptr, (*ppass)->miboid); |
| while (isdigit((unsigned char)(*cptr)) || *cptr == '.') |
| cptr++; |
| /* |
| * path |
| */ |
| cptr = skip_white(cptr); |
| if (cptr == NULL) { |
| config_perror("No command specified on pass line"); |
| (*ppass)->command[0] = 0; |
| } else { |
| for (tcptr = cptr; *tcptr != 0 && *tcptr != '#' && *tcptr != ';'; |
| tcptr++); |
| sprintf((*ppass)->command, "%.*s", (int) (tcptr - cptr), cptr); |
| } |
| strlcpy((*ppass)->name, (*ppass)->command, sizeof((*ppass)->name)); |
| (*ppass)->next = NULL; |
| |
| register_mib_priority("pass", |
| (struct variable *) extensible_passthru_variables, |
| sizeof(struct variable2), 1, (*ppass)->miboid, |
| (*ppass)->miblen, (*ppass)->mibpriority); |
| |
| /* |
| * argggg -- passthrus must be sorted |
| */ |
| if (numpassthrus > 1) { |
| etmp = (struct extensible **) |
| malloc(((sizeof(struct extensible *)) * numpassthrus)); |
| if (etmp == NULL) |
| return; |
| |
| for (i = 0, ptmp = (struct extensible *) passthrus; |
| i < numpassthrus && ptmp != NULL; i++, ptmp = ptmp->next) |
| etmp[i] = ptmp; |
| qsort(etmp, numpassthrus, sizeof(struct extensible *), |
| pass_compare); |
| passthrus = (struct extensible *) etmp[0]; |
| ptmp = (struct extensible *) etmp[0]; |
| |
| for (i = 0; i < numpassthrus - 1; i++) { |
| ptmp->next = etmp[i + 1]; |
| ptmp = ptmp->next; |
| } |
| ptmp->next = NULL; |
| free(etmp); |
| } |
| } |
| |
| void |
| pass_free_config(void) |
| { |
| struct extensible *etmp, *etmp2; |
| |
| for (etmp = passthrus; etmp != NULL;) { |
| etmp2 = etmp; |
| etmp = etmp->next; |
| unregister_mib_priority(etmp2->miboid, etmp2->miblen, etmp2->mibpriority); |
| free(etmp2); |
| } |
| passthrus = NULL; |
| numpassthrus = 0; |
| } |
| |
| u_char * |
| var_extensible_pass(struct variable *vp, |
| oid * name, |
| size_t * length, |
| int exact, |
| size_t * var_len, WriteMethod ** write_method) |
| { |
| oid newname[MAX_OID_LEN]; |
| int i, rtest, fd, newlen; |
| char buf[SNMP_MAXBUF]; |
| static char buf2[SNMP_MAXBUF]; |
| struct extensible *passthru; |
| FILE *file; |
| |
| for (i = 1; i <= numpassthrus; i++) { |
| passthru = get_exten_instance(passthrus, i); |
| rtest = snmp_oidtree_compare(name, *length, |
| passthru->miboid, passthru->miblen); |
| if ((exact && rtest == 0) || (!exact && rtest <= 0)) { |
| /* |
| * setup args |
| */ |
| if (passthru->miblen >= *length || rtest < 0) |
| sprint_mib_oid(buf, passthru->miboid, passthru->miblen); |
| else |
| sprint_mib_oid(buf, name, *length); |
| if (exact) |
| snprintf(passthru->command, sizeof(passthru->command), |
| "%s -g %s", passthru->name, buf); |
| else |
| snprintf(passthru->command, sizeof(passthru->command), |
| "%s -n %s", passthru->name, buf); |
| passthru->command[ sizeof(passthru->command)-1 ] = 0; |
| DEBUGMSGTL(("ucd-snmp/pass", "pass-running: %s\n", |
| passthru->command)); |
| /* |
| * valid call. Exec and get output |
| */ |
| if ((fd = get_exec_output(passthru)) != -1) { |
| file = fdopen(fd, "r"); |
| if (fgets(buf, sizeof(buf), file) == NULL) { |
| fclose(file); |
| wait_on_exec(passthru); |
| if (exact) { |
| /* |
| * to enable creation |
| */ |
| *write_method = setPass; |
| *var_len = 0; |
| return (NULL); |
| } |
| continue; |
| } |
| newlen = parse_miboid(buf, newname); |
| |
| /* |
| * its good, so copy onto name/length |
| */ |
| memcpy((char *) name, (char *) newname, |
| (int) newlen * sizeof(oid)); |
| *length = newlen; |
| |
| /* |
| * set up return pointer for setable stuff |
| */ |
| *write_method = setPass; |
| |
| if (newlen == 0 || fgets(buf, sizeof(buf), file) == NULL |
| || fgets(buf2, sizeof(buf2), file) == NULL) { |
| *var_len = 0; |
| fclose(file); |
| wait_on_exec(passthru); |
| return (NULL); |
| } |
| fclose(file); |
| wait_on_exec(passthru); |
| |
| return netsnmp_internal_pass_parse(buf, buf2, var_len, vp); |
| } |
| *var_len = 0; |
| return (NULL); |
| } |
| } |
| if (var_len) |
| *var_len = 0; |
| *write_method = NULL; |
| return (NULL); |
| } |
| |
| int |
| setPass(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) |
| { |
| int i, rtest; |
| struct extensible *passthru; |
| char buf[SNMP_MAXBUF], buf2[SNMP_MAXBUF]; |
| |
| for (i = 1; i <= numpassthrus; i++) { |
| passthru = get_exten_instance(passthrus, i); |
| rtest = snmp_oidtree_compare(name, name_len, |
| passthru->miboid, passthru->miblen); |
| if (rtest <= 0) { |
| if (action != ACTION) |
| return SNMP_ERR_NOERROR; |
| /* |
| * setup args |
| */ |
| if (passthru->miblen >= name_len || rtest < 0) |
| sprint_mib_oid(buf, passthru->miboid, passthru->miblen); |
| else |
| sprint_mib_oid(buf, name, name_len); |
| snprintf(passthru->command, sizeof(passthru->command), |
| "%s -s %s ", passthru->name, buf); |
| passthru->command[ sizeof(passthru->command)-1 ] = 0; |
| netsnmp_internal_pass_set_format(buf, var_val, var_val_type, var_val_len); |
| strlcat(passthru->command, buf, sizeof(passthru->command)); |
| DEBUGMSGTL(("ucd-snmp/pass", "pass-running: %s", |
| passthru->command)); |
| exec_command(passthru); |
| DEBUGMSGTL(("ucd-snmp/pass", "pass-running returned: %s", |
| passthru->output)); |
| return netsnmp_internal_pass_str_to_errno(passthru->output); |
| } |
| } |
| if (snmp_get_do_debugging()) { |
| sprint_mib_oid(buf2, name, name_len); |
| DEBUGMSGTL(("ucd-snmp/pass", "pass-notfound: %s\n", buf2)); |
| } |
| return SNMP_ERR_NOSUCHNAME; |
| } |
| |
| int |
| pass_compare(const void *a, const void *b) |
| { |
| const struct extensible *const *ap, *const *bp; |
| ap = (const struct extensible * const *) a; |
| bp = (const struct extensible * const *) b; |
| return snmp_oid_compare((*ap)->miboid, (*ap)->miblen, (*bp)->miboid, |
| (*bp)->miblen); |
| } |