1 /*
2 * Utility routines to assist with the running of sub-commands
3 */
4
5 #include <net-snmp/net-snmp-config.h>
6
7 #if HAVE_IO_H
8 #include <io.h>
9 #endif
10 #include <stdio.h>
11 #if HAVE_STDLIB_H
12 #include <stdlib.h>
13 #endif
14 #if HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #if HAVE_MALLOC_H
18 #include <malloc.h>
19 #endif
20 #include <sys/types.h>
21 #include <ctype.h>
22 #if HAVE_FCNTL_H
23 #include <fcntl.h>
24 #endif
25 #if HAVE_SYS_WAIT_H
26 #include <sys/wait.h>
27 #endif
28 #if HAVE_SYS_SELECT_H
29 #include <sys/select.h>
30 #endif
31
32 #include <errno.h>
33
34 #include <net-snmp/net-snmp-includes.h>
35 #include <net-snmp/agent/net-snmp-agent-includes.h>
36 #include <ucd-snmp/errormib.h>
37
38 #include <net-snmp/agent/netsnmp_close_fds.h>
39
40 #include "execute.h"
41 #include "struct.h"
42
43 #ifdef _MSC_VER
44 #define popen _popen
45 #define pclose _pclose
46 #endif
47
48
49 /**
50 * Run a shell command by calling system() or popen().
51 *
52 * @command: Shell command to run.
53 * @input: Data to send to stdin. May be NULL.
54 * @output: Buffer in which to store the output written to stdout. May be NULL.
55 * @out_len: Size of the output buffer. The actual number of bytes written is
56 * stored in *@out_len.
57 *
58 * @return >= 0 if the command has been executed; -1 if the command could not
59 * be executed.
60 */
61 int
run_shell_command(const char * command,const char * input,char * output,int * out_len)62 run_shell_command(const char *command, const char *input,
63 char *output, int *out_len)
64 {
65 #if HAVE_SYSTEM
66 int result; /* and the return value of the command */
67
68 if (!command)
69 return -1;
70
71 DEBUGMSGTL(("run_shell_command", "running %s\n", command));
72 DEBUGMSGTL(("run:shell", "running '%s'\n", command));
73
74 result = -1;
75
76 /*
77 * Set up the command and run it.
78 */
79 if (input) {
80 if (output) {
81 const char *ifname;
82 const char *ofname; /* Filename for output redirection */
83 char shellline[STRMAX]; /* The full command to run */
84 FILE *file;
85
86 ifname = netsnmp_mktemp();
87 if(NULL == ifname)
88 return -1;
89 file = fopen(ifname, "w");
90 if(NULL == file) {
91 snmp_log(LOG_ERR,"couldn't open temporary file %s\n", ifname);
92 unlink(ifname);
93 return -1;
94 }
95 fprintf(file, "%s", input);
96 fclose( file );
97
98 ofname = netsnmp_mktemp();
99 if(NULL == ofname) {
100 if(ifname)
101 unlink(ifname);
102 return -1;
103 }
104 snprintf( shellline, sizeof(shellline), "(%s) < \"%s\" > \"%s\"",
105 command, ifname, ofname );
106 result = system(shellline);
107 /*
108 * If output was requested, then retrieve & return it.
109 * Tidy up, and return the result of the command.
110 */
111 if (out_len && *out_len != 0) {
112 int fd; /* For processing any output */
113 int len = 0;
114
115 fd = open(ofname, O_RDONLY);
116 if(fd >= 0)
117 len = read(fd, output, *out_len - 1);
118 *out_len = len;
119 if (len >= 0)
120 output[len] = 0;
121 else
122 output[0] = 0;
123 if (fd >= 0)
124 close(fd);
125 }
126 unlink(ofname);
127 unlink(ifname);
128 } else {
129 FILE *file;
130
131 file = popen(command, "w");
132 if (file) {
133 fwrite(input, 1, strlen(input), file);
134 result = pclose(file);
135 }
136 }
137 } else {
138 if (output) {
139 FILE* file;
140
141 file = popen(command, "r");
142 if (file) {
143 *out_len = fread(output, 1, *out_len - 1, file);
144 if (*out_len >= 0)
145 output[*out_len] = 0;
146 else
147 output[0] = 0;
148 result = pclose(file);
149 }
150 } else {
151 result = system(command);
152 }
153 }
154
155 return result;
156 #else
157 return -1;
158 #endif
159 }
160
161 #if HAVE_EXECV
162 /*
163 * Split the given command up into separate tokens,
164 * ready to be passed to 'execv'
165 */
166 static char **
tokenize_exec_command(const char * command,int * argc)167 tokenize_exec_command(const char *command, int *argc)
168 {
169 char ctmp[STRMAX];
170 const char *cp = command;
171 char **argv;
172 int i;
173
174 argv = calloc(100, sizeof(char *));
175 if (!argv)
176 return argv;
177
178 for (i = 0; cp && i + 2 < 100; i++) {
179 cp = copy_nword_const(cp, ctmp, sizeof(ctmp));
180 argv[i] = strdup(ctmp);
181 }
182 if (cp)
183 argv[i++] = strdup(cp);
184 argv[i] = NULL;
185 *argc = i;
186
187 return argv;
188 }
189 #endif
190
191 /**
192 * Run a command by calling execv().
193 *
194 * @command: Shell command to run.
195 * @input: Data to send to stdin. May be NULL.
196 * @output: Buffer in which to store the output written to stdout. May be NULL.
197 * @out_len: Size of the output buffer. The actual number of bytes written is
198 * stored in *@out_len.
199 *
200 * @return >= 0 if the command has been executed; -1 if the command could not
201 * be executed.
202 */
203 int
run_exec_command(const char * command,const char * input,char * output,int * out_len)204 run_exec_command(const char *command, const char *input,
205 char *output, int *out_len)
206 {
207 #if HAVE_EXECV
208 int ipipe[2];
209 int opipe[2];
210 int i;
211 int pid;
212 int result;
213 char **argv;
214 int argc;
215
216 DEBUGMSGTL(("run:exec", "running '%s'\n", command));
217 if (pipe(ipipe) < 0) {
218 snmp_log_perror("pipe");
219 return -1;
220 }
221 if (pipe(opipe) < 0) {
222 snmp_log_perror("pipe");
223 close(ipipe[0]);
224 close(ipipe[1]);
225 return -1;
226 }
227 if ((pid = fork()) == 0) {
228 /*
229 * Child process
230 */
231
232 /*
233 * Set stdin/out/err to use the pipe
234 * and close everything else
235 */
236 if (dup2(ipipe[0], STDIN_FILENO) < 0) {
237 snmp_log_perror("dup2(STDIN_FILENO)");
238 exit(1);
239 }
240 close(ipipe[0]);
241 close(ipipe[1]);
242
243 if (dup2(opipe[1], STDOUT_FILENO) < 0) {
244 snmp_log_perror("dup2(STDOUT_FILENO)");
245 exit(1);
246 }
247 close(opipe[0]);
248 close(opipe[1]);
249
250 if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) {
251 snmp_log_perror("dup2(STDERR_FILENO)");
252 exit(1);
253 }
254
255 netsnmp_close_fds(2);
256
257 /*
258 * Set up the argv array and execute it
259 * This is being run in the child process,
260 * so will release resources when it terminates.
261 */
262 argv = tokenize_exec_command(command, &argc);
263 if (!argv)
264 exit(1);
265 execv(argv[0], argv);
266 snmp_log_perror(argv[0]);
267 for (i = 0; i < argc; i++)
268 free(argv[i]);
269 free(argv);
270 exit(1); /* End of child */
271
272 } else if (pid > 0) {
273 char cache[NETSNMP_MAXCACHESIZE];
274 char *cache_ptr;
275 ssize_t count, cache_size, offset = 0;
276 int waited = 0, numfds;
277 fd_set readfds;
278 struct timeval timeout;
279
280 /*
281 * Parent process
282 */
283
284 /*
285 * Pass the input message (if any) to the child,
286 * wait for the child to finish executing, and read
287 * any output into the output buffer (if provided)
288 */
289 close(ipipe[0]);
290 close(opipe[1]);
291 if (input && write(ipipe[1], input, strlen(input)) < 0)
292 snmp_log_perror("write() to input pipe");
293 close(ipipe[1]);
294
295 /*
296 * child will block if it writes a lot of data and
297 * fills up the pipe before exiting, so we read data
298 * to keep the pipe empty.
299 */
300 if (output && ((NULL == out_len) || (0 == *out_len))) {
301 DEBUGMSGTL(("run:exec",
302 "invalid params; no output will be returned\n"));
303 output = NULL;
304 }
305 if (output) {
306 cache_ptr = output;
307 cache_size = *out_len - 1;
308 } else {
309 cache_ptr = cache;
310 cache_size = sizeof(cache);
311 }
312
313 /*
314 * xxx: some of this code was lifted from get_exec_output
315 * in util_funcs.c. Probably should be moved to a common
316 * routine for both to use.
317 */
318 DEBUGMSGTL(("verbose:run:exec"," waiting for child %d...\n", pid));
319 numfds = opipe[0] + 1;
320 i = NETSNMP_MAXREADCOUNT;
321 for (; i; --i) {
322 /*
323 * set up data for select
324 */
325 FD_ZERO(&readfds);
326 FD_SET(opipe[0], &readfds);
327 timeout.tv_sec = 1;
328 timeout.tv_usec = 0;
329
330 DEBUGMSGTL(("verbose:run:exec", " calling select\n"));
331 count = select(numfds, &readfds, NULL, NULL, &timeout);
332 if (count == -1) {
333 if (EAGAIN == errno) {
334 continue;
335 } else {
336 DEBUGMSGTL(("verbose:run:exec", " errno %d\n",
337 errno));
338 snmp_log_perror("read");
339 break;
340 }
341 } else if (0 == count) {
342 DEBUGMSGTL(("verbose:run:exec", " timeout\n"));
343 continue;
344 }
345
346 if (!FD_ISSET(opipe[0], &readfds)) {
347 DEBUGMSGTL(("verbose:run:exec", " fd not ready!\n"));
348 continue;
349 }
350
351 /*
352 * read data from the pipe, optionally saving to output buffer
353 */
354 count = read(opipe[0], &cache_ptr[offset], cache_size);
355 DEBUGMSGTL(("verbose:run:exec",
356 " read %d bytes\n", (int)count));
357 if (0 == count) {
358 int rc;
359 /*
360 * we shouldn't get no data, because select should
361 * wait til the fd is ready. before we go back around,
362 * check to see if the child exited.
363 */
364 DEBUGMSGTL(("verbose:run:exec", " no data!\n"));
365 if ((rc = waitpid(pid, &result, WNOHANG)) <= 0) {
366 if (rc < 0) {
367 snmp_log_perror("waitpid");
368 break;
369 } else
370 DEBUGMSGTL(("verbose:run:exec",
371 " child not done!?!\n"));
372 } else {
373 DEBUGMSGTL(("verbose:run:exec", " child done\n"));
374 waited = 1; /* don't wait again */
375 break;
376 }
377 } else if (count > 0) {
378 /*
379 * got some data. fix up offset, if needed.
380 */
381 if(output) {
382 offset += count;
383 cache_size -= count;
384 if (cache_size <= 0) {
385 DEBUGMSGTL(("verbose:run:exec",
386 " output full\n"));
387 break;
388 }
389 DEBUGMSGTL(("verbose:run:exec",
390 " %d left in buffer\n", (int)cache_size));
391 }
392 } else if (count == -1 && EAGAIN != errno) {
393 /*
394 * if error, break
395 */
396 DEBUGMSGTL(("verbose:run:exec", " errno %d\n",
397 errno));
398 snmp_log_perror("read");
399 break;
400 }
401 }
402 DEBUGMSGTL(("verbose:run:exec", " done reading\n"));
403 if (output)
404 DEBUGMSGTL(("run:exec", " got %d bytes\n", *out_len));
405
406 /*
407 * close pipe to signal that we aren't listenting any more.
408 */
409 close(opipe[0]);
410
411 /*
412 * if we didn't wait successfully above, wait now.
413 * xxx-rks: seems like this is a waste of the agent's
414 * time. maybe start a time to wait(WNOHANG) once a second,
415 * and late the agent continue?
416 */
417 if (!waited && waitpid(pid, &result, 0) < 0) {
418 snmp_log_perror("waitpid");
419 return -1;
420 }
421
422 /*
423 * null terminate any output
424 */
425 if (output) {
426 output[offset] = 0;
427 *out_len = offset;
428 }
429 DEBUGMSGTL(("run:exec"," child %d finished. result=%d\n",
430 pid,result));
431
432 return WEXITSTATUS(result);
433
434 } else {
435 /*
436 * Parent process - fork failed
437 */
438 snmp_log_perror("fork");
439 close(ipipe[0]);
440 close(ipipe[1]);
441 close(opipe[0]);
442 close(opipe[1]);
443 return -1;
444 }
445
446 #else
447 /*
448 * If necessary, fall back to using 'system'
449 */
450 DEBUGMSGTL(("run:exec", "running shell command '%s'\n", command));
451 return run_shell_command( command, input, output, out_len );
452 #endif
453 }
454