Branch data Line data Source code
1 : : /*
2 : : *****************************************************************************
3 : : *
4 : : * File: extcmd.c
5 : : *
6 : : * Purpose: Routines for executing and processing external commands.
7 : : *
8 : : * Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9 : : * Copyright (C) 2009-2014 fwknop developers and contributors. For a full
10 : : * list of contributors, see the file 'CREDITS'.
11 : : *
12 : : * License (GNU General Public License):
13 : : *
14 : : * This program is free software; you can redistribute it and/or
15 : : * modify it under the terms of the GNU General Public License
16 : : * as published by the Free Software Foundation; either version 2
17 : : * of the License, or (at your option) any later version.
18 : : *
19 : : * This program is distributed in the hope that it will be useful,
20 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : : * GNU General Public License for more details.
23 : : *
24 : : * You should have received a copy of the GNU General Public License
25 : : * along with this program; if not, write to the Free Software
26 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 : : * USA
28 : : *
29 : : *****************************************************************************
30 : : */
31 : : #include "fwknopd_common.h"
32 : : #include "extcmd.h"
33 : : #include "log_msg.h"
34 : : #include "utils.h"
35 : :
36 : : #include <errno.h>
37 : : #include <signal.h>
38 : :
39 : : #if HAVE_SYS_WAIT_H
40 : : #include <sys/wait.h>
41 : : #endif
42 : :
43 : : /*
44 : : static sig_atomic_t got_sigalrm;
45 : : */
46 : :
47 : : /* Takes a file descriptor and makes it non-blocking.
48 : : static int
49 : : set_nonblock(int fd)
50 : : {
51 : : int val;
52 : :
53 : : if((val = fcntl(fd, F_GETFL, 0)) < 0)
54 : : {
55 : : perror("fcntl F_GETFL error:");
56 : : return(-1);
57 : : }
58 : :
59 : : val |= O_NONBLOCK;
60 : :
61 : : if(fcntl(fd, F_SETFL, val) < 0)
62 : : {
63 : : perror("fcntl F_SETFL error setting O_NONBLOCK");
64 : : return(-1);
65 : : }
66 : :
67 : : return(0);
68 : : }
69 : :
70 : : static void
71 : : alarm_handler(int sig)
72 : : {
73 : : got_sigalrm = 1;
74 : : }
75 : : */
76 : :
77 : : static void
78 : 18580 : copy_or_search(char *so_read_buf, char *so_buf, const size_t so_buf_sz,
79 : : const char *substr_search, const int cflag, int *found_str,
80 : : int *do_break)
81 : : {
82 [ + + ]: 18580 : if(so_buf != NULL)
83 : : {
84 [ + + ]: 18340 : if(cflag & WANT_STDOUT_GETLINE)
85 : : {
86 : : memset(so_buf, 0x0, so_buf_sz);
87 : 61 : strlcpy(so_buf, so_read_buf, so_buf_sz);
88 : : }
89 : : else
90 : : {
91 : 18279 : strlcat(so_buf, so_read_buf, so_buf_sz);
92 [ + + ]: 18279 : if(strlen(so_buf) >= so_buf_sz-1)
93 : 35 : *do_break = 1;
94 : : }
95 : : }
96 : :
97 [ + + ]: 18580 : if(substr_search != NULL) /* we are looking for a substring */
98 : : {
99 : : /* Search the current line in so_read_buf instead of
100 : : * so_buf (which may contain a partial line at the
101 : : * end at this point).
102 : : */
103 [ + - ][ + - ]: 301 : if(!IS_EMPTY_LINE(so_read_buf[0])
[ + - ][ + - ]
104 [ + + ]: 301 : && strstr(so_read_buf, substr_search) != NULL)
105 : : {
106 : 32 : *found_str = 1;
107 : 32 : *do_break = 1;
108 : : }
109 : : }
110 : 18580 : return;
111 : : }
112 : :
113 : : /* Run an external command returning exit status, and optionally filling
114 : : * provided buffer with STDOUT output up to the size provided.
115 : : *
116 : : * Note: XXX: We are not using the timeout parameter at present. We still need
117 : : * to implement a reliable timeout mechanism.
118 : : */
119 : : static int
120 : 13075 : _run_extcmd(uid_t uid, gid_t gid, const char *cmd, char *so_buf,
121 : : const size_t so_buf_sz, const int cflag, const int timeout,
122 : : const char *substr_search, int *pid_status,
123 : : const fko_srv_options_t * const opts)
124 : : {
125 : 13075 : char so_read_buf[IO_READ_BUF_LEN] = {0};
126 : 13075 : pid_t pid=0;
127 : : FILE *output;
128 : 13075 : int retval = EXTCMD_SUCCESS_ALL_OUTPUT;
129 : 13075 : int line_ctr = 0, found_str = 0, do_break = 0;
130 : :
131 : : char *argv_new[MAX_CMDLINE_ARGS]; /* for validation and/or execvpe() */
132 : 13075 : int argc_new=0;
133 : :
134 : : #if HAVE_EXECVPE
135 : : int pipe_fd[2];
136 : : #endif
137 : :
138 : : #if AFL_FUZZING
139 : : /* Don't allow command execution in AFL fuzzing mode
140 : : */
141 : : return 0;
142 : : #endif
143 : :
144 : 13075 : *pid_status = 0;
145 : :
146 : : /* Even without execvpe() we examine the command for basic validity
147 : : * in term of number of args
148 : : */
149 : : memset(argv_new, 0x0, sizeof(argv_new));
150 : :
151 [ + + ]: 13075 : if(strtoargv(cmd, argv_new, &argc_new, opts) != 1)
152 : : {
153 : 2 : log_msg(LOG_ERR,
154 : : "run_extcmd(): Error converting cmd str to argv via strtoargv()");
155 : : return EXTCMD_ARGV_ERROR;
156 : : }
157 : :
158 : : #if !HAVE_EXECVPE
159 : : /* if we are not using execvpe() then free up argv_new unconditionally
160 : : * since was used only for validation
161 : : */
162 : : free_argv(argv_new, &argc_new);
163 : : #endif
164 : :
165 : : #if HAVE_EXECVPE
166 [ + + ]: 13073 : if(opts->verbose > 1)
167 : 11609 : log_msg(LOG_INFO, "run_extcmd() (with execvpe()): running CMD: %s", cmd);
168 : :
169 [ + + ]: 13073 : if(so_buf != NULL || substr_search != NULL)
170 : : {
171 [ - + ]: 11765 : if(pipe(pipe_fd) < 0)
172 : : {
173 : 0 : log_msg(LOG_ERR, "run_extcmd(): pipe() failed: %s", strerror(errno));
174 : 0 : free_argv(argv_new, &argc_new);
175 : : return EXTCMD_PIPE_ERROR;
176 : : }
177 : : }
178 : :
179 : 13073 : pid = fork();
180 [ + + ]: 13029 : if (pid == 0)
181 : : {
182 [ - + ]: 7 : if(chdir("/") != 0)
183 : 0 : exit(EXTCMD_CHDIR_ERROR);
184 : :
185 [ - + ]: 7 : if(so_buf != NULL || substr_search != NULL)
186 : : {
187 : 0 : close(pipe_fd[0]);
188 : 0 : dup2(pipe_fd[1], STDOUT_FILENO);
189 [ # # ]: 0 : if(cflag & WANT_STDERR)
190 : 0 : dup2(pipe_fd[1], STDERR_FILENO);
191 : : else
192 : 0 : close(STDERR_FILENO);
193 : : }
194 : :
195 : : /* Take care of gid/uid settings before running the command.
196 : : */
197 [ - + ]: 7 : if(gid > 0)
198 [ # # ]: 0 : if(setgid(gid) < 0)
199 : 0 : exit(EXTCMD_SETGID_ERROR);
200 : :
201 [ + + ]: 7 : if(uid > 0)
202 [ - + ]: 6 : if(setuid(uid) < 0)
203 : 0 : exit(EXTCMD_SETUID_ERROR);
204 : :
205 : : /* don't use env
206 : : */
207 : 7 : execvpe(argv_new[0], argv_new, (char * const *)NULL);
208 : : }
209 [ - + ]: 13022 : else if(pid == -1)
210 : : {
211 : 0 : log_msg(LOG_ERR, "run_extcmd(): fork() failed: %s", strerror(errno));
212 : 0 : free_argv(argv_new, &argc_new);
213 : : return EXTCMD_FORK_ERROR;
214 : : }
215 : :
216 : : /* Only the parent process makes it here
217 : : */
218 [ + + ]: 13029 : if(so_buf != NULL || substr_search != NULL)
219 : : {
220 : 11721 : close(pipe_fd[1]);
221 [ + - ]: 11721 : if ((output = fdopen(pipe_fd[0], "r")) != NULL)
222 : : {
223 [ + + ]: 11721 : if(so_buf != NULL)
224 : : memset(so_buf, 0x0, so_buf_sz);
225 : :
226 [ + + ]: 30234 : while((fgets(so_read_buf, IO_READ_BUF_LEN, output)) != NULL)
227 : : {
228 : 18580 : line_ctr++;
229 : :
230 : 18580 : copy_or_search(so_read_buf, so_buf, so_buf_sz,
231 : : substr_search, cflag, &found_str, &do_break);
232 : :
233 [ + + ]: 30301 : if(do_break)
234 : : break;
235 : : }
236 : 11721 : fclose(output);
237 : :
238 : : /* Make sure we only have complete lines
239 : : */
240 [ + - ]: 11721 : if(!(cflag & ALLOW_PARTIAL_LINES))
241 : 11721 : truncate_partial_line(so_buf);
242 : : }
243 : : else
244 : : {
245 : 0 : log_msg(LOG_ERR,
246 : : "run_extcmd(): could not fdopen() pipe output file descriptor.");
247 : 0 : free_argv(argv_new, &argc_new);
248 : : return EXTCMD_OPEN_ERROR;
249 : : }
250 : : }
251 : :
252 : 13029 : free_argv(argv_new, &argc_new);
253 : :
254 : 13029 : waitpid(pid, pid_status, 0);
255 : :
256 : : #else
257 : :
258 : : if(opts->verbose > 1)
259 : : log_msg(LOG_INFO, "run_extcmd() (without execvpe()): running CMD: %s", cmd);
260 : :
261 : : if(so_buf == NULL && substr_search == NULL)
262 : : {
263 : : /* Since we do not have to capture output, we will fork here (which we
264 : : * * would have to do anyway if we are running as another user as well).
265 : : * */
266 : : pid = fork();
267 : : if(pid == -1)
268 : : {
269 : : log_msg(LOG_ERR, "run_extcmd: fork failed: %s", strerror(errno));
270 : : return(EXTCMD_FORK_ERROR);
271 : : }
272 : : else if (pid == 0)
273 : : {
274 : : /* We are the child */
275 : :
276 : : if(chdir("/") != 0)
277 : : exit(EXTCMD_CHDIR_ERROR);
278 : :
279 : : /* Take care of gid/uid settings before running the command.
280 : : */
281 : : if(gid > 0)
282 : : if(setgid(gid) < 0)
283 : : exit(EXTCMD_SETGID_ERROR);
284 : :
285 : : if(uid > 0)
286 : : if(setuid(uid) < 0)
287 : : exit(EXTCMD_SETUID_ERROR);
288 : :
289 : : *pid_status = system(cmd);
290 : : exit(*pid_status);
291 : : }
292 : : /* Retval is forced to 0 as we don't care about the exit status of
293 : : * the child (for now)
294 : : */
295 : : retval = EXTCMD_SUCCESS_ALL_OUTPUT;
296 : : }
297 : : else
298 : : {
299 : : /* Looking for output use popen and fill the buffer to its limit.
300 : : */
301 : : output = popen(cmd, "r");
302 : : if(output == NULL)
303 : : {
304 : : log_msg(LOG_ERR, "Got popen error %i: %s", errno, strerror(errno));
305 : : retval = EXTCMD_OPEN_ERROR;
306 : : }
307 : : else
308 : : {
309 : : if(so_buf != NULL)
310 : : memset(so_buf, 0x0, so_buf_sz);
311 : :
312 : : while((fgets(so_read_buf, IO_READ_BUF_LEN, output)) != NULL)
313 : : {
314 : : line_ctr++;
315 : :
316 : : copy_or_search(so_read_buf, so_buf, so_buf_sz,
317 : : substr_search, cflag, &found_str, &do_break);
318 : :
319 : : if(do_break)
320 : : break;
321 : : }
322 : : pclose(output);
323 : :
324 : : /* Make sure we only have complete lines
325 : : */
326 : : if(!(cflag & ALLOW_PARTIAL_LINES))
327 : : truncate_partial_line(so_buf);
328 : : }
329 : : }
330 : :
331 : : #endif
332 : :
333 [ + + ]: 13029 : if(substr_search != NULL)
334 : : {
335 : : /* The semantics of the return value changes in search mode to the line
336 : : * number where the substring match was found, or zero if it wasn't found
337 : : */
338 [ + + ]: 91 : if(found_str)
339 : 32 : retval = line_ctr;
340 : : else
341 : : retval = 0;
342 : : }
343 : : else
344 : : {
345 [ - + ]: 12938 : if(WIFEXITED(*pid_status))
346 : : {
347 : : /* Even if the child exited with an error condition, if we make it here
348 : : * then the child exited normally as far as the OS is concerned (i.e. didn't
349 : : * crash or get hit with a signal)
350 : : */
351 : : retval = EXTCMD_SUCCESS_ALL_OUTPUT;
352 : : }
353 : : else
354 : 0 : retval = EXTCMD_EXECUTION_ERROR;
355 : : }
356 : :
357 [ + + ]: 13029 : if(opts->verbose > 1)
358 [ + - ]: 13029 : log_msg(LOG_INFO,
359 : : "run_extcmd(): returning %d, pid_status: %d",
360 : 23132 : retval, WIFEXITED(*pid_status) ? WEXITSTATUS(*pid_status) : *pid_status);
361 : :
362 : : return(retval);
363 : : }
364 : :
365 : :
366 : : #if 0 /* --DSS the original method that did not work on some systems */
367 : :
368 : : /* Create the pipes we will use for getting stdout and stderr
369 : : * from the child process.
370 : : */
371 : : if(pipe(so) != 0)
372 : : return(EXTCMD_PIPE_ERROR);
373 : :
374 : : if(pipe(se) != 0)
375 : : return(EXTCMD_PIPE_ERROR);
376 : :
377 : : /* Fork off a child process to run the command and provide its outputs.
378 : : */
379 : : pid = fork();
380 : : if(pid == -1)
381 : : {
382 : : return(EXTCMD_FORK_ERROR);
383 : : }
384 : : else if (pid == 0)
385 : : {
386 : : /* We are the child, so we dup stdout and stderr to our respective
387 : : * write-end of the pipes, close stdin and the read-end of the pipes
388 : : * (since we don't need them here). Then use system() to run the
389 : : * command and exit with the exit status of that command so we can
390 : : * grab it from the waitpid call in the parent.
391 : : */
392 : : close(fileno(stdin));
393 : : dup2(so[1], fileno(stdout));
394 : : dup2(se[1], fileno(stderr));
395 : : close(so[0]);
396 : : close(se[0]);
397 : :
398 : : /* If user is not null, then we setuid to that user before running the
399 : : * command.
400 : : */
401 : : if(uid > 0)
402 : : {
403 : : if(setuid(uid) < 0)
404 : : {
405 : : exit(EXTCMD_SETUID_ERROR);
406 : : }
407 : : }
408 : :
409 : : /* --DSS XXX: Would it be more efficient to use one of the exec()
410 : : * calls (i.e. 'return(execvp(ext_cmd, &argv[1]));')?
411 : : * For now, we use system() and exit with the external
412 : : * command exit status.
413 : : */
414 : : exit(WEXITSTATUS(system(cmd)));
415 : : }
416 : :
417 : : /* Parent from here */
418 : :
419 : : /* Give the exit status an initial value of -1.
420 : : */
421 : : *status = -1;
422 : :
423 : : /* Close the write-end of the pipes (we are only reading).
424 : : */
425 : : close(so[1]);
426 : : close(se[1]);
427 : :
428 : : /* Set our pipes to non-blocking
429 : : */
430 : : set_nonblock(so[0]);
431 : : set_nonblock(se[0]);
432 : :
433 : : tv.tv_sec = EXTCMD_DEF_TIMEOUT;
434 : : tv.tv_usec = 0;
435 : :
436 : : /* Initialize and setup our file descriptor sets for select.
437 : : */
438 : : FD_ZERO(&rfds);
439 : : FD_ZERO(&efds);
440 : : FD_SET(so[0], &rfds);
441 : : FD_SET(se[0], &rfds);
442 : : FD_SET(so[0], &efds);
443 : : FD_SET(se[0], &efds);
444 : :
445 : : /* Start with fully clear buffers.
446 : : */
447 : : memset(so_buf, 0x0, so_buf_sz);
448 : : memset(se_buf, 0x0, se_buf_sz);
449 : :
450 : : /* Read both stdout and stderr piped from the child until we get eof,
451 : : * fill the buffers, or error out.
452 : : */
453 : : while(so_buf_remaining > 0 || se_buf_remaining > 0)
454 : : {
455 : : selval = select(8, &rfds, NULL, &efds, &tv);
456 : :
457 : : if(selval == -1)
458 : : {
459 : : /* Select error - so kill the child and bail.
460 : : */
461 : : kill(pid, SIGTERM);
462 : : retval |= EXTCMD_SELECT_ERROR;
463 : : break;
464 : : }
465 : :
466 : : if(selval == 0)
467 : : {
468 : : /* Timeout - so kill the child and bail
469 : : */
470 : : kill(pid, SIGTERM);
471 : : retval |= EXTCMD_EXECUTION_TIMEOUT;
472 : : break;
473 : : }
474 : :
475 : : /* The stdout pipe...
476 : : */
477 : : bytes_read = read(so[0], so_read_buf, IO_READ_BUF_LEN);
478 : : if(so_buf_remaining > 0)
479 : : {
480 : : if(bytes_read > 0)
481 : : {
482 : : /* We have data, so process it...
483 : : */
484 : : if(bytes_read > so_buf_remaining)
485 : : {
486 : : bytes_read = so_buf_remaining;
487 : : retval |= EXTCMD_SUCCESS_PARTIAL_STDOUT;
488 : : }
489 : :
490 : : memcpy(so_buf, so_read_buf, bytes_read);
491 : : so_buf += bytes_read;
492 : : so_buf_remaining -= bytes_read;
493 : : }
494 : : else if(bytes_read < 0)
495 : : {
496 : : /* Anything other than EAGAIN or EWOULDBLOCK is conisdered
497 : : * error enough to bail. We are done here so we force the
498 : : * buf_remaining value to 0.
499 : : */
500 : : if(errno != EAGAIN && errno != EWOULDBLOCK)
501 : : {
502 : : retval |= EXTCMD_STDOUT_READ_ERROR;
503 : : so_buf_remaining = 0;
504 : : }
505 : : }
506 : : else
507 : : {
508 : : /* Bytes read was 0 which indicate end of file. So we are
509 : : * done.
510 : : */
511 : : so_buf_remaining = 0;
512 : : }
513 : : }
514 : : else
515 : : break;
516 : :
517 : : /* The stderr pipe...
518 : : */
519 : : bytes_read = read(se[0], se_read_buf, IO_READ_BUF_LEN);
520 : : if(se_buf_remaining > 0)
521 : : {
522 : : if(bytes_read > 0)
523 : : {
524 : : /* We have data, so process it...
525 : : */
526 : : if(bytes_read > se_buf_remaining)
527 : : {
528 : : bytes_read = se_buf_remaining;
529 : : retval |= EXTCMD_SUCCESS_PARTIAL_STDERR;
530 : : }
531 : :
532 : : memcpy(se_buf, se_read_buf, bytes_read);
533 : : se_buf += bytes_read;
534 : : se_buf_remaining -= bytes_read;
535 : : }
536 : : else if(bytes_read < 0)
537 : : {
538 : : /* Anything other than EAGAIN or EWOULDBLOCK is conisdered
539 : : * error enough to bail. We are done here so we force the
540 : : * buf_remaining value to 0.
541 : : */
542 : : if(errno != EAGAIN && errno != EWOULDBLOCK)
543 : : {
544 : : retval |= EXTCMD_STDERR_READ_ERROR;
545 : : se_buf_remaining = 0;
546 : : }
547 : : }
548 : : else
549 : : {
550 : : /* Bytes read was 0 which indicate end of file. So we are
551 : : * done.
552 : : */
553 : : se_buf_remaining = 0;
554 : : }
555 : : }
556 : : else
557 : : break;
558 : : }
559 : :
560 : : close(so[0]);
561 : : close(se[0]);
562 : :
563 : : /* Wait for the external command to finish and capture its exit status.
564 : : */
565 : : waitpid(pid, status, 0);
566 : :
567 : : if(*status != 0)
568 : : retval != EXTCMD_EXECUTION_ERROR;
569 : :
570 : : /* Return the our status of this operation command.
571 : : */
572 : : return(retval);
573 : : }
574 : : #endif
575 : :
576 : 14 : int _run_extcmd_write(const char *cmd, const char *cmd_write, int *pid_status,
577 : : const fko_srv_options_t * const opts)
578 : : {
579 : 14 : int retval = EXTCMD_SUCCESS_ALL_OUTPUT;
580 : : char *argv_new[MAX_CMDLINE_ARGS]; /* for validation and/or execvpe() */
581 : 14 : int argc_new=0;
582 : :
583 : : #if HAVE_EXECVPE
584 : : int pipe_fd[2];
585 : 14 : pid_t pid=0;
586 : : #else
587 : : FILE *fd = NULL;
588 : : #endif
589 : :
590 : : #if AFL_FUZZING
591 : : return 0;
592 : : #endif
593 : :
594 : 14 : *pid_status = 0;
595 : :
596 : : /* Even without execvpe() we examine the command for basic validity
597 : : * in term of number of args
598 : : */
599 : : memset(argv_new, 0x0, sizeof(argv_new));
600 : :
601 [ - + ]: 14 : if(strtoargv(cmd, argv_new, &argc_new, opts) != 1)
602 : : {
603 : 0 : log_msg(LOG_ERR,
604 : : "run_extcmd_write(): Error converting cmd str to argv via strtoargv()");
605 : 0 : return EXTCMD_ARGV_ERROR;
606 : : }
607 : :
608 : : #if !HAVE_EXECVPE
609 : : /* if we are not using execvpe() then free up argv_new unconditionally
610 : : * since was used only for validation
611 : : */
612 : : free_argv(argv_new, &argc_new);
613 : : #endif
614 : :
615 : : #if HAVE_EXECVPE
616 [ + - ]: 14 : if(opts->verbose > 1)
617 : 14 : log_msg(LOG_INFO, "run_extcmd_write() (with execvpe()): running CMD: %s | %s",
618 : : cmd_write, cmd);
619 : :
620 [ - + ]: 14 : if(pipe(pipe_fd) < 0)
621 : : {
622 : 0 : log_msg(LOG_ERR, "run_extcmd_write(): pipe() failed: %s", strerror(errno));
623 : 0 : free_argv(argv_new, &argc_new);
624 : 0 : return EXTCMD_PIPE_ERROR;
625 : : }
626 : :
627 : 14 : pid = fork();
628 [ - + ]: 14 : if (pid == 0)
629 : : {
630 [ # # ]: 0 : if(chdir("/") != 0)
631 : 0 : exit(EXTCMD_CHDIR_ERROR);
632 : :
633 : 0 : close(pipe_fd[1]);
634 : 0 : dup2(pipe_fd[0], STDIN_FILENO);
635 : :
636 : : /* don't use env
637 : : */
638 : 0 : execvpe(argv_new[0], argv_new, (char * const *)NULL);
639 : : }
640 [ - + ]: 14 : else if(pid == -1)
641 : : {
642 : 0 : log_msg(LOG_ERR, "run_extcmd_write(): fork() failed: %s", strerror(errno));
643 : 0 : free_argv(argv_new, &argc_new);
644 : 0 : return EXTCMD_FORK_ERROR;
645 : : }
646 : :
647 : 14 : close(pipe_fd[0]);
648 [ - + ]: 14 : if(write(pipe_fd[1], cmd_write, strlen(cmd_write)) < 0)
649 : 0 : retval = EXTCMD_WRITE_ERROR;
650 : 14 : close(pipe_fd[1]);
651 : :
652 : 14 : free_argv(argv_new, &argc_new);
653 : :
654 : 14 : waitpid(pid, pid_status, 0);
655 : :
656 : : #else
657 : : if(opts->verbose > 1)
658 : : log_msg(LOG_INFO, "run_extcmd_write() (without execvpe()): running CMD: %s | %s",
659 : : cmd_write, cmd);
660 : :
661 : : if ((fd = popen(cmd, "w")) == NULL)
662 : : {
663 : : log_msg(LOG_ERR, "Got popen error %i: %s", errno, strerror(errno));
664 : : retval = EXTCMD_OPEN_ERROR;
665 : : }
666 : : else
667 : : {
668 : : if (fwrite(cmd_write, strlen(cmd_write), 1, fd) != 1)
669 : : {
670 : : log_msg(LOG_ERR, "Could not write to cmd stdin");
671 : : retval = -1;
672 : : }
673 : : pclose(fd);
674 : : }
675 : :
676 : : #endif
677 : 14 : return retval;
678 : : }
679 : :
680 : : /* _run_extcmd() wrapper, run an external command.
681 : : */
682 : : int
683 : 12973 : run_extcmd(const char *cmd, char *so_buf, const size_t so_buf_sz,
684 : : const int want_stderr, const int timeout, int *pid_status,
685 : : const fko_srv_options_t * const opts)
686 : : {
687 : 12973 : return _run_extcmd(ROOT_UID, ROOT_GID, cmd, so_buf, so_buf_sz,
688 : : want_stderr, timeout, NULL, pid_status, opts);
689 : : }
690 : :
691 : : /* _run_extcmd() wrapper, run an external command as the specified user.
692 : : */
693 : : int
694 : 10 : run_extcmd_as(uid_t uid, gid_t gid, const char *cmd,char *so_buf,
695 : : const size_t so_buf_sz, const int want_stderr, const int timeout,
696 : : int *pid_status, const fko_srv_options_t * const opts)
697 : : {
698 : 10 : return _run_extcmd(uid, gid, cmd, so_buf, so_buf_sz,
699 : : want_stderr, timeout, NULL, pid_status, opts);
700 : : }
701 : :
702 : : /* _run_extcmd() wrapper, search command output for a substring.
703 : : */
704 : : int
705 : 78 : search_extcmd(const char *cmd, const int want_stderr, const int timeout,
706 : : const char *substr_search, int *pid_status,
707 : : const fko_srv_options_t * const opts)
708 : : {
709 : 78 : return _run_extcmd(ROOT_UID, ROOT_GID, cmd, NULL, 0, want_stderr,
710 : : timeout, substr_search, pid_status, opts);
711 : : }
712 : :
713 : : /* _run_extcmd() wrapper, search command output for a substring and return
714 : : * the matching line.
715 : : */
716 : : int
717 : 14 : search_extcmd_getline(const char *cmd, char *so_buf, const size_t so_buf_sz,
718 : : const int timeout, const char *substr_search, int *pid_status,
719 : : const fko_srv_options_t * const opts)
720 : : {
721 : 14 : return _run_extcmd(ROOT_UID, ROOT_GID, cmd, so_buf, so_buf_sz,
722 : : WANT_STDERR | WANT_STDOUT_GETLINE, timeout, substr_search,
723 : : pid_status, opts);
724 : : }
725 : :
726 : : /* _run_extcmd_write() wrapper, run a command which is expecting input via stdin
727 : : */
728 : 14 : int run_extcmd_write(const char *cmd, const char *cmd_write, int *pid_status,
729 : : const fko_srv_options_t * const opts)
730 : : {
731 : 14 : return _run_extcmd_write(cmd, cmd_write, pid_status, opts);
732 : : }
|