diff options
Diffstat (limited to 'agent/mibgroup/utilities/execute.c')
-rw-r--r-- | agent/mibgroup/utilities/execute.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/agent/mibgroup/utilities/execute.c b/agent/mibgroup/utilities/execute.c new file mode 100644 index 0000000..68f3654 --- /dev/null +++ b/agent/mibgroup/utilities/execute.c @@ -0,0 +1,408 @@ +/* + * Utility routines to assist with the running of sub-commands + */ + +#include <net-snmp/net-snmp-config.h> + +#if HAVE_IO_H +#include <io.h> +#endif +#include <stdio.h> +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <sys/types.h> +#include <ctype.h> +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#if HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#include <errno.h> + +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <ucd-snmp/errormib.h> + +#include "execute.h" +#include "struct.h" + +#define setPerrorstatus(x) snmp_log_perror(x) + +#ifdef _MSC_VER +#define popen _popen +#define pclose _pclose +#endif + + +int +run_shell_command( char *command, char *input, + char *output, int *out_len) /* Or realloc style ? */ +{ +#if HAVE_SYSTEM + int result; /* and the return value of the command */ + + if (!command) + return -1; + + DEBUGMSGTL(("run_shell_command", "running %s\n", command)); + DEBUGMSGTL(("run:shell", "running '%s'\n", command)); + + result = -1; + + /* + * Set up the command and run it. + */ + if (input) { + FILE *file; + + if (output) { + const char *ifname; + const char *ofname; /* Filename for output redirection */ + char shellline[STRMAX]; /* The full command to run */ + + ifname = netsnmp_mktemp(); + if(NULL == ifname) + return -1; + file = fopen(ifname, "w"); + if(NULL == file) { + snmp_log(LOG_ERR,"couldn't open temporary file %s\n", ifname); + unlink(ifname); + return -1; + } + fprintf(file, "%s", input); + fclose( file ); + + ofname = netsnmp_mktemp(); + if(NULL == ofname) { + if(ifname) + unlink(ifname); + return -1; + } + snprintf( shellline, sizeof(shellline), "(%s) < \"%s\" > \"%s\"", + command, ifname, ofname ); + result = system(shellline); + /* + * If output was requested, then retrieve & return it. + * Tidy up, and return the result of the command. + */ + if (out_len && *out_len != 0) { + int fd; /* For processing any output */ + int len = 0; + fd = open(ofname, O_RDONLY); + if(fd >= 0) + len = read( fd, output, *out_len-1 ); + *out_len = len; + if (len >= 0) output[len] = 0; + else output[0] = 0; + if (fd >= 0) close(fd); + } + unlink(ofname); + unlink(ifname); + } else { + file = popen(command, "w"); + if (file) { + fwrite(input, 1, strlen(input), file); + result = pclose(file); + } + } + } else { + if (output) { + FILE* file; + + file = popen(command, "r"); + if (file) { + *out_len = fread(output, 1, *out_len - 1, file); + if (*out_len >= 0) + output[*out_len] = 0; + else + output[0] = 0; + result = pclose(file); + } + } else + result = system(command); + } + + return result; +#else + return -1; +#endif +} + + +/* + * Split the given command up into separate tokens, + * ready to be passed to 'execv' + */ +char ** +tokenize_exec_command( char *command, int *argc ) +{ + char ctmp[STRMAX]; + char *cp; + char **argv; + int i; + + argv = (char **) calloc(100, sizeof(char *)); + cp = command; + + for ( i=0; cp; i++ ) { + memset( ctmp, 0, STRMAX ); + cp = copy_nword( cp, ctmp, STRMAX ); + argv[i] = strdup( ctmp ); + if (i == 99) + break; + } + if (cp) { + argv[i++] = strdup( cp ); + } + argv[i] = NULL; + *argc = i; + + return argv; +} + + +int +run_exec_command( char *command, char *input, + char *output, int *out_len) /* Or realloc style ? */ +{ +#if HAVE_EXECV + int ipipe[2]; + int opipe[2]; + int i; + int pid; + int result; + char **argv; + int argc; + + DEBUGMSGTL(("run:exec", "running '%s'\n", command)); + pipe(ipipe); + pipe(opipe); + if ((pid = fork()) == 0) { + /* + * Child process + */ + + /* + * Set stdin/out/err to use the pipe + * and close everything else + */ + close(0); + dup( ipipe[0]); + close(ipipe[1]); + + close(1); + dup( opipe[1]); + close(opipe[0]); + close(2); + dup(1); + for (i = getdtablesize()-1; i>2; i--) + close(i); + + /* + * Set up the argv array and execute it + * This is being run in the child process, + * so will release resources when it terminates. + */ + argv = tokenize_exec_command( command, &argc ); + execv( argv[0], argv ); + perror( argv[0] ); + exit(1); /* End of child */ + + } else if (pid > 0) { + char cache[NETSNMP_MAXCACHESIZE]; + char *cache_ptr; + ssize_t count, cache_size, offset = 0; + int waited = 0, numfds; + fd_set readfds; + struct timeval timeout; + + /* + * Parent process + */ + + /* + * Pass the input message (if any) to the child, + * wait for the child to finish executing, and read + * any output into the output buffer (if provided) + */ + close(ipipe[0]); + close(opipe[1]); + if (input) { + write(ipipe[1], input, strlen(input)); + close(ipipe[1]); /* or flush? */ + } + else close(ipipe[1]); + + /* + * child will block if it writes a lot of data and + * fills up the pipe before exiting, so we read data + * to keep the pipe empty. + */ + if (output && ((NULL == out_len) || (0 == *out_len))) { + DEBUGMSGTL(("run:exec", + "invalid params; no output will be returned\n")); + output = NULL; + } + if (output) { + cache_ptr = output; + cache_size = *out_len - 1; + } else { + cache_ptr = cache; + cache_size = sizeof(cache); + } + + /* + * xxx: some of this code was lifted from get_exec_output + * in util_funcs.c. Probably should be moved to a common + * routine for both to use. + */ + DEBUGMSGTL(("verbose:run:exec"," waiting for child %d...\n", pid)); + numfds = opipe[0] + 1; + i = NETSNMP_MAXREADCOUNT; + for (; i; --i) { + /* + * set up data for select + */ + FD_ZERO(&readfds); + FD_SET(opipe[0],&readfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + DEBUGMSGTL(("verbose:run:exec", " calling select\n")); + count = select(numfds, &readfds, NULL, NULL, &timeout); + if (count == -1) { + if (EAGAIN == errno) + continue; + else { + DEBUGMSGTL(("verbose:run:exec", " errno %d\n", + errno)); + setPerrorstatus("read"); + break; + } + } else if (0 == count) { + DEBUGMSGTL(("verbose:run:exec", " timeout\n")); + continue; + } + + if (! FD_ISSET(opipe[0], &readfds)) { + DEBUGMSGTL(("verbose:run:exec", " fd not ready!\n")); + continue; + } + + /* + * read data from the pipe, optionally saving to output buffer + */ + count = read(opipe[0], &cache_ptr[offset], cache_size); + DEBUGMSGTL(("verbose:run:exec", + " read %d bytes\n", (int)count)); + if (0 == count) { + int rc; + /* + * we shouldn't get no data, because select should + * wait til the fd is ready. before we go back around, + * check to see if the child exited. + */ + DEBUGMSGTL(("verbose:run:exec", " no data!\n")); + if ((rc = waitpid(pid, &result, WNOHANG)) <= 0) { + if (rc < 0) { + snmp_log_perror("waitpid"); + break; + } else + DEBUGMSGTL(("verbose:run:exec", + " child not done!?!\n"));; + } else { + DEBUGMSGTL(("verbose:run:exec", " child done\n")); + waited = 1; /* don't wait again */ + break; + } + } + else if (count > 0) { + /* + * got some data. fix up offset, if needed. + */ + if(output) { + offset += count; + cache_size -= count; + if (cache_size <= 0) { + DEBUGMSGTL(("verbose:run:exec", + " output full\n")); + break; + } + DEBUGMSGTL(("verbose:run:exec", + " %d left in buffer\n", (int)cache_size)); + } + } + else if ((count == -1) && (EAGAIN != errno)) { + /* + * if error, break + */ + DEBUGMSGTL(("verbose:run:exec", " errno %d\n", + errno)); + setPerrorstatus("read"); + break; + } + } + DEBUGMSGTL(("verbose:run:exec", " done reading\n")); + if (output) + DEBUGMSGTL(("run:exec", " got %d bytes\n", *out_len)); + + /* + * close pipe to signal that we aren't listenting any more. + */ + close(opipe[0]); + + /* + * if we didn't wait successfully above, wait now. + * xxx-rks: seems like this is a waste of the agent's + * time. maybe start a time to wait(WNOHANG) once a second, + * and late the agent continue? + */ + if ((!waited) && (waitpid(pid, &result, 0) < 0 )) { + snmp_log_perror("waitpid"); + return -1; + } + + /* + * null terminate any output + */ + if (output) { + output[offset] = 0; + *out_len = offset; + } + DEBUGMSGTL(("run:exec"," child %d finished. result=%d\n", + pid,result)); + + return WEXITSTATUS(result); + + } else { + /* + * Parent process - fork failed + */ + snmp_log_perror("fork"); + close(ipipe[0]); + close(ipipe[1]); + close(opipe[0]); + close(opipe[1]); + return -1; + } + +#else + /* + * If necessary, fall back to using 'system' + */ + DEBUGMSGTL(("run:exec", "running shell command '%s'\n", command)); + return run_shell_command( command, input, output, out_len ); +#endif +} |