diff options
Diffstat (limited to 'agent/mibgroup/ucd-snmp/pass_persist.c')
-rw-r--r-- | agent/mibgroup/ucd-snmp/pass_persist.c | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/agent/mibgroup/ucd-snmp/pass_persist.c b/agent/mibgroup/ucd-snmp/pass_persist.c new file mode 100644 index 0000000..cc44232 --- /dev/null +++ b/agent/mibgroup/ucd-snmp/pass_persist.c @@ -0,0 +1,730 @@ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> + +#if HAVE_IO_H +#include <io.h> +#endif +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#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 <signal.h> +#include <errno.h> + +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#include "struct.h" +#include "pass_persist.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 *persistpassthrus = NULL; +int numpersistpassthrus = 0; +struct persist_pipe_type { + FILE *fIn, *fOut; + int fdIn, fdOut; + netsnmp_pid_t pid; +} *persist_pipes = (struct persist_pipe_type *) NULL; +static unsigned pipe_check_alarm_id; +static int init_persist_pipes(void); +static void close_persist_pipe(int iindex); +static int open_persist_pipe(int iindex, char *command); +static void check_persist_pipes(unsigned clientreg, void *clientarg); +static void destruct_persist_pipes(void); +static int write_persist_pipe(int iindex, const char *data); + +/* + * the relocatable extensible commands variables + */ +struct variable2 extensible_persist_passthru_variables[] = { + /* + * bogus entry. Only some of it is actually used. + */ + {MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, + var_extensible_pass_persist, 0, {MIBINDEX}}, +}; + +void +init_pass_persist(void) +{ + snmpd_register_config_handler("pass_persist", + pass_persist_parse_config, + pass_persist_free_config, + "miboid program"); + pipe_check_alarm_id = snmp_alarm_register(10, SA_REPEAT, check_persist_pipes, NULL); +} + +void +shutdown_pass_persist(void) +{ + if (pipe_check_alarm_id) { + snmp_alarm_unregister(pipe_check_alarm_id); + pipe_check_alarm_id = 0; + } + + /* Close any open pipes. */ + destruct_persist_pipes(); +} + +void +pass_persist_parse_config(const char *token, char *cptr) +{ + struct extensible **ppass = &persistpassthrus, **etmp, *ptmp; + char *tcptr, *endopt; + int i; + long int 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; + } + numpersistpassthrus++; + + while (*ppass != NULL) + ppass = &((*ppass)->next); + (*ppass) = (struct extensible *) malloc(sizeof(struct extensible)); + if (*ppass == NULL) + return; + (*ppass)->type = PASSTHRU_PERSIST; + (*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_persist 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_persist", + (struct variable *) extensible_persist_passthru_variables, + sizeof(struct variable2), 1, (*ppass)->miboid, + (*ppass)->miblen, (*ppass)->mibpriority); + + /* + * argggg -- pasthrus must be sorted + */ + if (numpersistpassthrus > 1) { + etmp = (struct extensible **) + malloc(((sizeof(struct extensible *)) * numpersistpassthrus)); + if (etmp == NULL) + return; + for (i = 0, ptmp = (struct extensible *) persistpassthrus; + i < numpersistpassthrus && ptmp != NULL; i++, ptmp = ptmp->next) + etmp[i] = ptmp; + qsort(etmp, numpersistpassthrus, sizeof(struct extensible *), + pass_persist_compare); + persistpassthrus = (struct extensible *) etmp[0]; + ptmp = (struct extensible *) etmp[0]; + + for (i = 0; i < numpersistpassthrus - 1; i++) { + ptmp->next = etmp[i + 1]; + ptmp = ptmp->next; + } + ptmp->next = NULL; + free(etmp); + } +} + +void +pass_persist_free_config(void) +{ + struct extensible *etmp, *etmp2; + + for (etmp = persistpassthrus; etmp != NULL;) { + etmp2 = etmp; + etmp = etmp->next; + unregister_mib_priority(etmp2->miboid, etmp2->miblen, etmp2->mibpriority); + free(etmp2); + } + persistpassthrus = NULL; + numpersistpassthrus = 0; +} + +u_char * +var_extensible_pass_persist(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, newlen; + char buf[SNMP_MAXBUF]; + static char buf2[SNMP_MAXBUF]; + struct extensible *persistpassthru; + FILE *file; + + /* + * Make sure that our basic pipe structure is malloced + */ + init_persist_pipes(); + + for (i = 1; i <= numpersistpassthrus; i++) { + persistpassthru = get_exten_instance(persistpassthrus, i); + rtest = snmp_oidtree_compare(name, *length, + persistpassthru->miboid, + persistpassthru->miblen); + if ((exact && rtest == 0) || (!exact && rtest <= 0)) { + /* + * setup args + */ + if (persistpassthru->miblen >= *length || rtest < 0) + sprint_mib_oid(buf, persistpassthru->miboid, + persistpassthru->miblen); + else + sprint_mib_oid(buf, name, *length); + + /* + * Open our pipe if necessary + */ + if (!open_persist_pipe(i, persistpassthru->name)) { + return (NULL); + } + + if (exact) + snprintf(persistpassthru->command, + sizeof(persistpassthru->command), "get\n%s\n", buf); + else + snprintf(persistpassthru->command, + sizeof(persistpassthru->command), "getnext\n%s\n", buf); + persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0; + + DEBUGMSGTL(("ucd-snmp/pass_persist", + "persistpass-sending:\n%s", + persistpassthru->command)); + if (!write_persist_pipe(i, persistpassthru->command)) { + *var_len = 0; + /* + * close_persist_pipes is called in write_persist_pipe + */ + return (NULL); + } + + /* + * valid call. Exec and get output + */ + + if ((file = persist_pipes[i].fIn)) { + if (fgets(buf, sizeof(buf), file) == NULL) { + *var_len = 0; + close_persist_pipe(i); + return (NULL); + } + /* + * persistent scripts return "NONE\n" on invalid items + */ + if (!strncmp(buf, "NONE", 4)) { + if (exact) { + *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 = setPassPersist; + + if (newlen == 0 || fgets(buf, sizeof(buf), file) == NULL + || fgets(buf2, sizeof(buf2), file) == NULL) { + *var_len = 0; + close_persist_pipe(i); + return (NULL); + } + 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 +setPassPersist(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 *persistpassthru; + + char buf[SNMP_MAXBUF], buf2[SNMP_MAXBUF]; + + /* + * Make sure that our basic pipe structure is malloced + */ + init_persist_pipes(); + + for (i = 1; i <= numpersistpassthrus; i++) { + persistpassthru = get_exten_instance(persistpassthrus, i); + rtest = snmp_oidtree_compare(name, name_len, + persistpassthru->miboid, + persistpassthru->miblen); + if (rtest <= 0) { + if (action != ACTION) + return SNMP_ERR_NOERROR; + /* + * setup args + */ + if (persistpassthru->miblen >= name_len || rtest < 0) + sprint_mib_oid(buf, persistpassthru->miboid, + persistpassthru->miblen); + else + sprint_mib_oid(buf, name, name_len); + snprintf(persistpassthru->command, + sizeof(persistpassthru->command), "set\n%s\n", buf); + persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0; + netsnmp_internal_pass_set_format(buf, var_val, var_val_type, var_val_len); + strlcat(persistpassthru->command, buf, + sizeof(persistpassthru->command)); + persistpassthru->command[ sizeof(persistpassthru->command)-2 ] = '\n'; + persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0; + + if (!open_persist_pipe(i, persistpassthru->name)) { + return SNMP_ERR_NOTWRITABLE; + } + + DEBUGMSGTL(("ucd-snmp/pass_persist", + "persistpass-writing: %s\n", + persistpassthru->command)); + if (!write_persist_pipe(i, persistpassthru->command)) { + close_persist_pipe(i); + return SNMP_ERR_NOTWRITABLE; + } + + if (fgets(buf, sizeof(buf), persist_pipes[i].fIn) == NULL) { + close_persist_pipe(i); + return SNMP_ERR_NOTWRITABLE; + } + + return netsnmp_internal_pass_str_to_errno(buf); + } + } + if (snmp_get_do_debugging()) { + sprint_mib_oid(buf2, name, name_len); + DEBUGMSGTL(("ucd-snmp/pass_persist", "persistpass-notfound: %s\n", + buf2)); + } + return SNMP_ERR_NOSUCHNAME; +} + +int +pass_persist_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); +} + +/* + * Initialize our persistent pipes + * - Returns 1 on success, 0 on failure. + * - Initializes all FILE pointers to NULL to indicate "closed" + */ +static int +init_persist_pipes(void) +{ + int i; + + /* + * if we are already taken care of, just return + */ + if (persist_pipes) { + return persist_pipes ? 1 : 0; + } + + /* + * Otherwise malloc and initialize + */ + persist_pipes = (struct persist_pipe_type *) + malloc(sizeof(struct persist_pipe_type) * + (numpersistpassthrus + 1)); + if (persist_pipes) { + for (i = 0; i <= numpersistpassthrus; i++) { + persist_pipes[i].fIn = persist_pipes[i].fOut = (FILE *) 0; + persist_pipes[i].fdIn = persist_pipes[i].fdOut = -1; + persist_pipes[i].pid = NETSNMP_NO_SUCH_PROCESS; + } + } + return persist_pipes ? 1 : 0; +} + +/** + * Return true if and only if the process associated with the persistent + * pipe has stopped. + * + * @param[in] idx Persistent pipe index. + */ +static int process_stopped(int idx) +{ + if (persist_pipes[idx].pid != NETSNMP_NO_SUCH_PROCESS) { +#if HAVE_SYS_WAIT_H + return waitpid(persist_pipes[idx].pid, NULL, WNOHANG) > 0; +#endif +#if defined(WIN32) && !defined (mingw32) && !defined(HAVE_SIGNAL) + return WaitForSingleObject(persist_pipes[idx].pid, 0) == WAIT_OBJECT_0; +#endif + } + return 0; +} + +/** + * Iterate over all persistent pipes and close those pipes of which the + * associated process has stopped. + */ +static void check_persist_pipes(unsigned clientreg, void *clientarg) +{ + int i; + + if (!persist_pipes) + return; + + for (i = 0; i <= numpersistpassthrus; i++) { + if (process_stopped(i)) { + snmp_log(LOG_INFO, "pass_persist[%d]: child process stopped - closing pipe\n", i); + close_persist_pipe(i); + } + } +} + +/* + * Destruct our persistent pipes + * + */ +static void +destruct_persist_pipes(void) +{ + int i; + + /* + * Return if there are no pipes + */ + if (!persist_pipes) { + return; + } + + for (i = 0; i <= numpersistpassthrus; i++) { + close_persist_pipe(i); + } + + free(persist_pipes); + persist_pipes = (struct persist_pipe_type *) 0; +} + +/* + * returns 0 on failure, 1 on success + */ +static int +open_persist_pipe(int iindex, char *command) +{ + static int recurse = 0; /* used to allow one level of recursion */ + + DEBUGMSGTL(("ucd-snmp/pass_persist", "open_persist_pipe(%d,'%s') recurse=%d\n", + iindex, command, recurse)); + /* + * Open if it's not already open + */ + if (persist_pipes[iindex].pid == NETSNMP_NO_SUCH_PROCESS) { + int fdIn, fdOut; + netsnmp_pid_t pid; + + /* + * Did we fail? + */ + if ((0 == get_exec_pipes(command, &fdIn, &fdOut, &pid)) || + (pid == NETSNMP_NO_SUCH_PROCESS)) { + DEBUGMSGTL(("ucd-snmp/pass_persist", + "open_persist_pipe: pid == -1\n")); + recurse = 0; + return 0; + } + + /* + * If not, fill out our structure + */ + persist_pipes[iindex].pid = pid; + persist_pipes[iindex].fdIn = fdIn; + persist_pipes[iindex].fdOut = fdOut; + persist_pipes[iindex].fIn = fdopen(fdIn, "r"); + persist_pipes[iindex].fOut = fdopen(fdOut, "w"); + + /* + * Setup our -non-buffered-io- + */ + setbuf(persist_pipes[iindex].fOut, (char *) 0); + DEBUGMSGTL(("ucd-snmp/pass_persist", "open_persist_pipe: opened the pipes\n")); + } + + /* + * Send test packet always so we can self-catch + */ + { + char buf[SNMP_MAXBUF]; + /* + * Should catch SIGPIPE around this call! + */ + if (!write_persist_pipe(iindex, "PING\n")) { + DEBUGMSGTL(("ucd-snmp/pass_persist", + "open_persist_pipe: Error writing PING\n")); + close_persist_pipe(iindex); + + /* + * Recurse one time if we get a SIGPIPE + */ + if (!recurse) { + DEBUGMSGTL(("ucd-snmp/pass_persist", "open_persist_pipe: recursing to reopen\n")); + recurse = 1; + return open_persist_pipe(iindex, command); + } + recurse = 0; + return 0; + } + if (fgets(buf, sizeof(buf), persist_pipes[iindex].fIn) == NULL) { + DEBUGMSGTL(("ucd-snmp/pass_persist", + "open_persist_pipe: Error reading for PONG\n")); + close_persist_pipe(iindex); + recurse = 0; + return 0; + } + if (strncmp(buf, "PONG", 4)) { + DEBUGMSGTL(("ucd-snmp/pass_persist", + "open_persist_pipe: Got %s instead of PONG!\n", buf)); + close_persist_pipe(iindex); + recurse = 0; + return 0; + } + } + + recurse = 0; + return 1; +} + +static int +write_persist_pipe(int iindex, const char *data) +{ +#if HAVE_SIGNAL + struct sigaction sa, osa; + int wret = 0, werrno = 0; + + /* + * Don't write to a non-existant process + */ + if (persist_pipes[iindex].pid == NETSNMP_NO_SUCH_PROCESS) { + DEBUGMSGTL(("ucd-snmp/pass_persist", + "write_persist_pipe: not writing %s, process is non-existent", + data)); + return 0; + } + + /* + * Setup our signal action to ignore SIGPIPEs + */ + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGPIPE, &sa, &osa)) { + DEBUGMSGTL(("ucd-snmp/pass_persist", + "write_persist_pipe: sigaction failed: %d", errno)); + } + + /* + * Do the write + */ + wret = write(persist_pipes[iindex].fdOut, data, strlen(data)); + werrno = errno; + + /* + * Reset the signal handler + */ + sigaction(SIGPIPE, &osa, (struct sigaction *) 0); + + if (wret < 0) { + if (werrno != EPIPE) { + DEBUGMSGTL(("ucd-snmp/pass_persist", + "write_persist_pipe: write returned unknown error %d (%s)\n", + werrno, strerror(werrno))); + } + close_persist_pipe(iindex); + return 0; + } +#endif /* HAVE_SIGNAL */ +#if defined(WIN32) && !defined (mingw32) && !defined (HAVE_SIGNAL) +/* We have no signal here (maybe we can make a Thread?) so write may block, + * but probably never will. + */ + int wret = 0, werrno = 0; + + /* + * Do the write + */ + wret = write(persist_pipes[iindex].fdOut, data,strlen(data)); + werrno = errno; + + if (wret < 0) { + if (werrno != EINTR) { + DEBUGMSGTL(("ucd-snmp/pass_persist", "write_persist_pipe: write returned unknown error %d\n",errno)); + } + close_persist_pipe(iindex); + return 0; + } +#endif /* WIN32 */ + return 1; +} + +static void +close_persist_pipe(int iindex) +{ +/* Alexander Prömel, alexander@proemel.de 08/24/2006 + The hard coded pathnames, are temporary. + I'll fix it soon. + If you changed them here, you have to do it in ../util_funcs.c too. +*/ +#ifdef __uClinux__ + char fifo_in_path[256]; + char fifo_out_path[256]; + + snprintf(fifo_in_path, 256, "/flash/cp_%d", persist_pipes[iindex].pid); + snprintf(fifo_out_path, 256, "/flash/pc_%d", persist_pipes[iindex].pid); +#endif + + /* + * Check and nix every item + */ + if (persist_pipes[iindex].fOut) { + fclose(persist_pipes[iindex].fOut); + persist_pipes[iindex].fOut = (FILE *) 0; + } + if (persist_pipes[iindex].fdOut != -1) { +#ifndef WIN32 + /* + * The sequence open()/fdopen()/fclose()/close() triggers an access + * violation with the MSVC runtime. Hence skip the close() call when + * using the MSVC runtime. + */ + close(persist_pipes[iindex].fdOut); +#endif + persist_pipes[iindex].fdOut = -1; + } + if (persist_pipes[iindex].fIn) { + fclose(persist_pipes[iindex].fIn); + persist_pipes[iindex].fIn = (FILE *) 0; + } + if (persist_pipes[iindex].fdIn != -1) { +#ifndef WIN32 + /* + * The sequence open()/fdopen()/fclose()/close() triggers an access + * violation with the MSVC runtime. Hence skip the close() call when + * using the MSVC runtime. + */ + close(persist_pipes[iindex].fdIn); +#endif + persist_pipes[iindex].fdIn = -1; + } + +#ifdef __uClinux__ + /*remove the pipes*/ + unlink(fifo_in_path); + unlink(fifo_out_path); +#endif + + if (persist_pipes[iindex].pid != NETSNMP_NO_SUCH_PROCESS) { +#if HAVE_SYS_WAIT_H + waitpid(persist_pipes[iindex].pid, NULL, 0); +#endif +#if defined(WIN32) && !defined (mingw32) && !defined (HAVE_SIGNAL) + if (!CloseHandle(persist_pipes[iindex].pid)) { + DEBUGMSGTL(("ucd-snmp/pass_persist","close_persist_pipe pid: close error\n")); + } +#endif + persist_pipes[iindex].pid = NETSNMP_NO_SUCH_PROCESS; + } + +} |