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