execvp() is (usually) equivalent to execvpe(), without enforcing any change to the environment. However, unlike execvp(), execvpe() is not standardized by POSIX, and may therefore not be available nor detected when configuring the project (like on NetBSD). No place could be found in fwknop to be using execvpe() and changing the environment. Therefore it seems only logical (and safer) to use execvp() instead. This also updates the tests to reflect this change.
742 lines
20 KiB
C
742 lines
20 KiB
C
/**
|
|
* \file server/extcmd.c
|
|
*
|
|
* \brief Routines for executing and processing external commands.
|
|
*/
|
|
|
|
/* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
|
|
* Copyright (C) 2009-2015 fwknop developers and contributors. For a full
|
|
* list of contributors, see the file 'CREDITS'.
|
|
*
|
|
* License (GNU General Public License):
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
* USA
|
|
*
|
|
*****************************************************************************
|
|
*/
|
|
#include "fwknopd_common.h"
|
|
#include "extcmd.h"
|
|
#include "log_msg.h"
|
|
#include "utils.h"
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
#if HAVE_SYS_WAIT_H
|
|
#include <sys/wait.h>
|
|
#endif
|
|
|
|
/*
|
|
static sig_atomic_t got_sigalrm;
|
|
*/
|
|
|
|
/* Takes a file descriptor and makes it non-blocking.
|
|
static int
|
|
set_nonblock(int fd)
|
|
{
|
|
int val;
|
|
|
|
if((val = fcntl(fd, F_GETFL, 0)) < 0)
|
|
{
|
|
perror("fcntl F_GETFL error:");
|
|
return(-1);
|
|
}
|
|
|
|
val |= O_NONBLOCK;
|
|
|
|
if(fcntl(fd, F_SETFL, val) < 0)
|
|
{
|
|
perror("fcntl F_SETFL error setting O_NONBLOCK");
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
alarm_handler(int sig)
|
|
{
|
|
got_sigalrm = 1;
|
|
}
|
|
*/
|
|
|
|
static void
|
|
copy_or_search(char *so_read_buf, char *so_buf, const size_t so_buf_sz,
|
|
const char *substr_search, const int cflag, int *found_str,
|
|
int *do_break)
|
|
{
|
|
if(so_buf != NULL)
|
|
{
|
|
if(cflag & WANT_STDOUT_GETLINE)
|
|
{
|
|
memset(so_buf, 0x0, so_buf_sz);
|
|
strlcpy(so_buf, so_read_buf, so_buf_sz);
|
|
}
|
|
else
|
|
{
|
|
strlcat(so_buf, so_read_buf, so_buf_sz);
|
|
if(strlen(so_buf) >= so_buf_sz-1)
|
|
*do_break = 1;
|
|
}
|
|
}
|
|
|
|
if(substr_search != NULL) /* we are looking for a substring */
|
|
{
|
|
/* Search the current line in so_read_buf instead of
|
|
* so_buf (which may contain a partial line at the
|
|
* end at this point).
|
|
*/
|
|
if(!IS_EMPTY_LINE(so_read_buf[0])
|
|
&& strstr(so_read_buf, substr_search) != NULL)
|
|
{
|
|
*found_str = 1;
|
|
*do_break = 1;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Run an external command returning exit status, and optionally filling
|
|
* provided buffer with STDOUT output up to the size provided.
|
|
*
|
|
* Note: XXX: We are not using the timeout parameter at present. We still need
|
|
* to implement a reliable timeout mechanism.
|
|
*/
|
|
static int
|
|
_run_extcmd(uid_t uid, gid_t gid, const char *cmd, char *so_buf,
|
|
const size_t so_buf_sz, const int cflag, const int timeout,
|
|
const char *substr_search, int *pid_status,
|
|
const fko_srv_options_t * const opts)
|
|
{
|
|
char so_read_buf[IO_READ_BUF_LEN] = {0};
|
|
pid_t pid=0;
|
|
FILE *output;
|
|
int retval = EXTCMD_SUCCESS_ALL_OUTPUT;
|
|
int line_ctr = 0, found_str = 0, do_break = 0;
|
|
int es = 0;
|
|
|
|
char *argv_new[MAX_CMDLINE_ARGS]; /* for validation and/or execvp() */
|
|
int argc_new=0;
|
|
|
|
#if HAVE_EXECVP
|
|
int pipe_fd[2];
|
|
#endif
|
|
|
|
#if AFL_FUZZING
|
|
/* Don't allow command execution in AFL fuzzing mode
|
|
*/
|
|
return 0;
|
|
#endif
|
|
|
|
*pid_status = 0;
|
|
|
|
/* Even without execvp() we examine the command for basic validity
|
|
* in term of number of args
|
|
*/
|
|
memset(argv_new, 0x0, sizeof(argv_new));
|
|
|
|
if(strtoargv(cmd, argv_new, &argc_new) != 1)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"run_extcmd(): Error converting cmd str to argv via strtoargv()");
|
|
return EXTCMD_ARGV_ERROR;
|
|
}
|
|
|
|
#if !HAVE_EXECVP
|
|
/* if we are not using execvp() then free up argv_new unconditionally
|
|
* since was used only for validation
|
|
*/
|
|
free_argv(argv_new, &argc_new);
|
|
#endif
|
|
|
|
#if HAVE_EXECVP
|
|
if(opts->verbose > 1)
|
|
log_msg(LOG_INFO, "run_extcmd() (with execvp()): running CMD: %s", cmd);
|
|
|
|
if(so_buf != NULL || substr_search != NULL)
|
|
{
|
|
if(pipe(pipe_fd) < 0)
|
|
{
|
|
log_msg(LOG_ERR, "run_extcmd(): pipe() failed: %s", strerror(errno));
|
|
free_argv(argv_new, &argc_new);
|
|
return EXTCMD_PIPE_ERROR;
|
|
}
|
|
}
|
|
|
|
pid = fork();
|
|
if (pid == 0)
|
|
{
|
|
if(chdir("/") != 0)
|
|
exit(EXTCMD_CHDIR_ERROR);
|
|
|
|
if(so_buf != NULL || substr_search != NULL)
|
|
{
|
|
close(pipe_fd[0]);
|
|
dup2(pipe_fd[1], STDOUT_FILENO);
|
|
if(cflag & WANT_STDERR)
|
|
dup2(pipe_fd[1], STDERR_FILENO);
|
|
else
|
|
close(STDERR_FILENO);
|
|
}
|
|
|
|
/* Take care of gid/uid settings before running the command.
|
|
*/
|
|
if(gid > 0)
|
|
if(setgid(gid) < 0)
|
|
exit(EXTCMD_SETGID_ERROR);
|
|
|
|
if(uid > 0)
|
|
if(setuid(uid) < 0)
|
|
exit(EXTCMD_SETUID_ERROR);
|
|
|
|
/* don't use env
|
|
*/
|
|
es = execvp(argv_new[0], argv_new);
|
|
|
|
if(es == -1)
|
|
log_msg(LOG_ERR, "run_extcmd(): execvp() failed: %s", strerror(errno));
|
|
|
|
/* We only make it here if there was a problem with execvp(),
|
|
* so exit() here either way to not leave another fwknopd process
|
|
* running after fork().
|
|
*/
|
|
exit(es);
|
|
}
|
|
else if(pid == -1)
|
|
{
|
|
log_msg(LOG_ERR, "run_extcmd(): fork() failed: %s", strerror(errno));
|
|
free_argv(argv_new, &argc_new);
|
|
return EXTCMD_FORK_ERROR;
|
|
}
|
|
|
|
/* Only the parent process makes it here
|
|
*/
|
|
if(so_buf != NULL || substr_search != NULL)
|
|
{
|
|
close(pipe_fd[1]);
|
|
if ((output = fdopen(pipe_fd[0], "r")) != NULL)
|
|
{
|
|
if(so_buf != NULL)
|
|
memset(so_buf, 0x0, so_buf_sz);
|
|
|
|
while((fgets(so_read_buf, IO_READ_BUF_LEN, output)) != NULL)
|
|
{
|
|
line_ctr++;
|
|
|
|
copy_or_search(so_read_buf, so_buf, so_buf_sz,
|
|
substr_search, cflag, &found_str, &do_break);
|
|
|
|
if(do_break)
|
|
break;
|
|
}
|
|
fclose(output);
|
|
|
|
/* Make sure we only have complete lines
|
|
*/
|
|
if(!(cflag & ALLOW_PARTIAL_LINES))
|
|
truncate_partial_line(so_buf);
|
|
}
|
|
else
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"run_extcmd(): could not fdopen() pipe output file descriptor.");
|
|
free_argv(argv_new, &argc_new);
|
|
return EXTCMD_OPEN_ERROR;
|
|
}
|
|
}
|
|
|
|
free_argv(argv_new, &argc_new);
|
|
|
|
waitpid(pid, pid_status, 0);
|
|
|
|
#else
|
|
|
|
if(opts->verbose > 1)
|
|
log_msg(LOG_INFO, "run_extcmd() (without execvp()): running CMD: %s", cmd);
|
|
|
|
if(so_buf == NULL && substr_search == NULL)
|
|
{
|
|
/* Since we do not have to capture output, we will fork here (which we
|
|
* * would have to do anyway if we are running as another user as well).
|
|
* */
|
|
pid = fork();
|
|
if(pid == -1)
|
|
{
|
|
log_msg(LOG_ERR, "run_extcmd: fork failed: %s", strerror(errno));
|
|
return(EXTCMD_FORK_ERROR);
|
|
}
|
|
else if (pid == 0)
|
|
{
|
|
/* We are the child */
|
|
|
|
if(chdir("/") != 0)
|
|
exit(EXTCMD_CHDIR_ERROR);
|
|
|
|
/* Take care of gid/uid settings before running the command.
|
|
*/
|
|
if(gid > 0)
|
|
if(setgid(gid) < 0)
|
|
exit(EXTCMD_SETGID_ERROR);
|
|
|
|
if(uid > 0)
|
|
if(setuid(uid) < 0)
|
|
exit(EXTCMD_SETUID_ERROR);
|
|
|
|
*pid_status = system(cmd);
|
|
exit(*pid_status);
|
|
}
|
|
/* Retval is forced to 0 as we don't care about the exit status of
|
|
* the child (for now)
|
|
*/
|
|
retval = EXTCMD_SUCCESS_ALL_OUTPUT;
|
|
}
|
|
else
|
|
{
|
|
/* Looking for output use popen and fill the buffer to its limit.
|
|
*/
|
|
output = popen(cmd, "r");
|
|
if(output == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "Got popen error %i: %s", errno, strerror(errno));
|
|
retval = EXTCMD_OPEN_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if(so_buf != NULL)
|
|
memset(so_buf, 0x0, so_buf_sz);
|
|
|
|
while((fgets(so_read_buf, IO_READ_BUF_LEN, output)) != NULL)
|
|
{
|
|
line_ctr++;
|
|
|
|
copy_or_search(so_read_buf, so_buf, so_buf_sz,
|
|
substr_search, cflag, &found_str, &do_break);
|
|
|
|
if(do_break)
|
|
break;
|
|
}
|
|
pclose(output);
|
|
|
|
/* Make sure we only have complete lines
|
|
*/
|
|
if(!(cflag & ALLOW_PARTIAL_LINES))
|
|
truncate_partial_line(so_buf);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
if(substr_search != NULL)
|
|
{
|
|
/* The semantics of the return value changes in search mode to the line
|
|
* number where the substring match was found, or zero if it wasn't found
|
|
*/
|
|
if(found_str)
|
|
retval = line_ctr;
|
|
else
|
|
retval = 0;
|
|
}
|
|
else
|
|
{
|
|
if(WIFEXITED(*pid_status))
|
|
{
|
|
/* Even if the child exited with an error condition, if we make it here
|
|
* then the child exited normally as far as the OS is concerned (i.e. didn't
|
|
* crash or get hit with a signal)
|
|
*/
|
|
retval = EXTCMD_SUCCESS_ALL_OUTPUT;
|
|
}
|
|
else
|
|
retval = EXTCMD_EXECUTION_ERROR;
|
|
}
|
|
|
|
if(opts->verbose > 1)
|
|
log_msg(LOG_INFO,
|
|
"run_extcmd(): returning %d, pid_status: %d",
|
|
retval, WIFEXITED(*pid_status) ? WEXITSTATUS(*pid_status) : *pid_status);
|
|
|
|
return(retval);
|
|
}
|
|
|
|
|
|
#if 0 /* --DSS the original method that did not work on some systems */
|
|
|
|
/* Create the pipes we will use for getting stdout and stderr
|
|
* from the child process.
|
|
*/
|
|
if(pipe(so) != 0)
|
|
return(EXTCMD_PIPE_ERROR);
|
|
|
|
if(pipe(se) != 0)
|
|
return(EXTCMD_PIPE_ERROR);
|
|
|
|
/* Fork off a child process to run the command and provide its outputs.
|
|
*/
|
|
pid = fork();
|
|
if(pid == -1)
|
|
{
|
|
return(EXTCMD_FORK_ERROR);
|
|
}
|
|
else if (pid == 0)
|
|
{
|
|
/* We are the child, so we dup stdout and stderr to our respective
|
|
* write-end of the pipes, close stdin and the read-end of the pipes
|
|
* (since we don't need them here). Then use system() to run the
|
|
* command and exit with the exit status of that command so we can
|
|
* grab it from the waitpid call in the parent.
|
|
*/
|
|
close(fileno(stdin));
|
|
dup2(so[1], fileno(stdout));
|
|
dup2(se[1], fileno(stderr));
|
|
close(so[0]);
|
|
close(se[0]);
|
|
|
|
/* If user is not null, then we setuid to that user before running the
|
|
* command.
|
|
*/
|
|
if(uid > 0)
|
|
{
|
|
if(setuid(uid) < 0)
|
|
{
|
|
exit(EXTCMD_SETUID_ERROR);
|
|
}
|
|
}
|
|
|
|
/* --DSS XXX: Would it be more efficient to use one of the exec()
|
|
* calls (i.e. 'return(execvp(ext_cmd, &argv[1]));')?
|
|
* For now, we use system() and exit with the external
|
|
* command exit status.
|
|
*/
|
|
exit(WEXITSTATUS(system(cmd)));
|
|
}
|
|
|
|
/* Parent from here */
|
|
|
|
/* Give the exit status an initial value of -1.
|
|
*/
|
|
*status = -1;
|
|
|
|
/* Close the write-end of the pipes (we are only reading).
|
|
*/
|
|
close(so[1]);
|
|
close(se[1]);
|
|
|
|
/* Set our pipes to non-blocking
|
|
*/
|
|
set_nonblock(so[0]);
|
|
set_nonblock(se[0]);
|
|
|
|
tv.tv_sec = EXTCMD_DEF_TIMEOUT;
|
|
tv.tv_usec = 0;
|
|
|
|
/* Initialize and setup our file descriptor sets for select.
|
|
*/
|
|
FD_ZERO(&rfds);
|
|
FD_ZERO(&efds);
|
|
FD_SET(so[0], &rfds);
|
|
FD_SET(se[0], &rfds);
|
|
FD_SET(so[0], &efds);
|
|
FD_SET(se[0], &efds);
|
|
|
|
/* Start with fully clear buffers.
|
|
*/
|
|
memset(so_buf, 0x0, so_buf_sz);
|
|
memset(se_buf, 0x0, se_buf_sz);
|
|
|
|
/* Read both stdout and stderr piped from the child until we get eof,
|
|
* fill the buffers, or error out.
|
|
*/
|
|
while(so_buf_remaining > 0 || se_buf_remaining > 0)
|
|
{
|
|
selval = select(8, &rfds, NULL, &efds, &tv);
|
|
|
|
if(selval == -1)
|
|
{
|
|
/* Select error - so kill the child and bail.
|
|
*/
|
|
kill(pid, SIGTERM);
|
|
retval |= EXTCMD_SELECT_ERROR;
|
|
break;
|
|
}
|
|
|
|
if(selval == 0)
|
|
{
|
|
/* Timeout - so kill the child and bail
|
|
*/
|
|
kill(pid, SIGTERM);
|
|
retval |= EXTCMD_EXECUTION_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
/* The stdout pipe...
|
|
*/
|
|
bytes_read = read(so[0], so_read_buf, IO_READ_BUF_LEN);
|
|
if(so_buf_remaining > 0)
|
|
{
|
|
if(bytes_read > 0)
|
|
{
|
|
/* We have data, so process it...
|
|
*/
|
|
if(bytes_read > so_buf_remaining)
|
|
{
|
|
bytes_read = so_buf_remaining;
|
|
retval |= EXTCMD_SUCCESS_PARTIAL_STDOUT;
|
|
}
|
|
|
|
memcpy(so_buf, so_read_buf, bytes_read);
|
|
so_buf += bytes_read;
|
|
so_buf_remaining -= bytes_read;
|
|
}
|
|
else if(bytes_read < 0)
|
|
{
|
|
/* Anything other than EAGAIN or EWOULDBLOCK is conisdered
|
|
* error enough to bail. We are done here so we force the
|
|
* buf_remaining value to 0.
|
|
*/
|
|
if(errno != EAGAIN && errno != EWOULDBLOCK)
|
|
{
|
|
retval |= EXTCMD_STDOUT_READ_ERROR;
|
|
so_buf_remaining = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Bytes read was 0 which indicate end of file. So we are
|
|
* done.
|
|
*/
|
|
so_buf_remaining = 0;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
|
|
/* The stderr pipe...
|
|
*/
|
|
bytes_read = read(se[0], se_read_buf, IO_READ_BUF_LEN);
|
|
if(se_buf_remaining > 0)
|
|
{
|
|
if(bytes_read > 0)
|
|
{
|
|
/* We have data, so process it...
|
|
*/
|
|
if(bytes_read > se_buf_remaining)
|
|
{
|
|
bytes_read = se_buf_remaining;
|
|
retval |= EXTCMD_SUCCESS_PARTIAL_STDERR;
|
|
}
|
|
|
|
memcpy(se_buf, se_read_buf, bytes_read);
|
|
se_buf += bytes_read;
|
|
se_buf_remaining -= bytes_read;
|
|
}
|
|
else if(bytes_read < 0)
|
|
{
|
|
/* Anything other than EAGAIN or EWOULDBLOCK is conisdered
|
|
* error enough to bail. We are done here so we force the
|
|
* buf_remaining value to 0.
|
|
*/
|
|
if(errno != EAGAIN && errno != EWOULDBLOCK)
|
|
{
|
|
retval |= EXTCMD_STDERR_READ_ERROR;
|
|
se_buf_remaining = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Bytes read was 0 which indicate end of file. So we are
|
|
* done.
|
|
*/
|
|
se_buf_remaining = 0;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
close(so[0]);
|
|
close(se[0]);
|
|
|
|
/* Wait for the external command to finish and capture its exit status.
|
|
*/
|
|
waitpid(pid, status, 0);
|
|
|
|
if(*status != 0)
|
|
retval != EXTCMD_EXECUTION_ERROR;
|
|
|
|
/* Return the our status of this operation command.
|
|
*/
|
|
return(retval);
|
|
}
|
|
#endif
|
|
|
|
int _run_extcmd_write(const char *cmd, const char *cmd_write, int *pid_status,
|
|
const fko_srv_options_t * const opts)
|
|
{
|
|
int retval = EXTCMD_SUCCESS_ALL_OUTPUT;
|
|
char *argv_new[MAX_CMDLINE_ARGS]; /* for validation and/or execvp() */
|
|
int argc_new=0;
|
|
|
|
#if HAVE_EXECVP
|
|
int pipe_fd[2];
|
|
pid_t pid=0;
|
|
#else
|
|
FILE *fd = NULL;
|
|
#endif
|
|
|
|
#if AFL_FUZZING
|
|
return 0;
|
|
#endif
|
|
|
|
*pid_status = 0;
|
|
|
|
/* Even without execvp() we examine the command for basic validity
|
|
* in term of number of args
|
|
*/
|
|
memset(argv_new, 0x0, sizeof(argv_new));
|
|
|
|
if(strtoargv(cmd, argv_new, &argc_new) != 1)
|
|
{
|
|
log_msg(LOG_ERR,
|
|
"run_extcmd_write(): Error converting cmd str to argv via strtoargv()");
|
|
return EXTCMD_ARGV_ERROR;
|
|
}
|
|
|
|
#if !HAVE_EXECVP
|
|
/* if we are not using execvp() then free up argv_new unconditionally
|
|
* since was used only for validation
|
|
*/
|
|
free_argv(argv_new, &argc_new);
|
|
#endif
|
|
|
|
#if HAVE_EXECVP
|
|
if(opts->verbose > 1)
|
|
log_msg(LOG_INFO, "run_extcmd_write() (with execvp()): running CMD: %s | %s",
|
|
cmd_write, cmd);
|
|
|
|
if(pipe(pipe_fd) < 0)
|
|
{
|
|
log_msg(LOG_ERR, "run_extcmd_write(): pipe() failed: %s", strerror(errno));
|
|
free_argv(argv_new, &argc_new);
|
|
return EXTCMD_PIPE_ERROR;
|
|
}
|
|
|
|
pid = fork();
|
|
if (pid == 0)
|
|
{
|
|
if(chdir("/") != 0)
|
|
exit(EXTCMD_CHDIR_ERROR);
|
|
|
|
close(pipe_fd[1]);
|
|
dup2(pipe_fd[0], STDIN_FILENO);
|
|
|
|
/* don't use env
|
|
*/
|
|
execvp(argv_new[0], argv_new);
|
|
}
|
|
else if(pid == -1)
|
|
{
|
|
log_msg(LOG_ERR, "run_extcmd_write(): fork() failed: %s", strerror(errno));
|
|
free_argv(argv_new, &argc_new);
|
|
return EXTCMD_FORK_ERROR;
|
|
}
|
|
|
|
close(pipe_fd[0]);
|
|
if(write(pipe_fd[1], cmd_write, strlen(cmd_write)) < 0)
|
|
retval = EXTCMD_WRITE_ERROR;
|
|
close(pipe_fd[1]);
|
|
|
|
free_argv(argv_new, &argc_new);
|
|
|
|
waitpid(pid, pid_status, 0);
|
|
|
|
#else
|
|
if(opts->verbose > 1)
|
|
log_msg(LOG_INFO, "run_extcmd_write() (without execvp()): running CMD: %s | %s",
|
|
cmd_write, cmd);
|
|
|
|
if ((fd = popen(cmd, "w")) == NULL)
|
|
{
|
|
log_msg(LOG_ERR, "Got popen error %i: %s", errno, strerror(errno));
|
|
retval = EXTCMD_OPEN_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if (fwrite(cmd_write, strlen(cmd_write), 1, fd) != 1)
|
|
{
|
|
log_msg(LOG_ERR, "Could not write to cmd stdin");
|
|
retval = -1;
|
|
}
|
|
pclose(fd);
|
|
}
|
|
|
|
#endif
|
|
return retval;
|
|
}
|
|
|
|
/* _run_extcmd() wrapper, run an external command.
|
|
*/
|
|
int
|
|
run_extcmd(const char *cmd, char *so_buf, const size_t so_buf_sz,
|
|
const int want_stderr, const int timeout, int *pid_status,
|
|
const fko_srv_options_t * const opts)
|
|
{
|
|
return _run_extcmd(ROOT_UID, ROOT_GID, cmd, so_buf, so_buf_sz,
|
|
want_stderr, timeout, NULL, pid_status, opts);
|
|
}
|
|
|
|
/* _run_extcmd() wrapper, run an external command as the specified user.
|
|
*/
|
|
int
|
|
run_extcmd_as(uid_t uid, gid_t gid, const char *cmd,char *so_buf,
|
|
const size_t so_buf_sz, const int want_stderr, const int timeout,
|
|
int *pid_status, const fko_srv_options_t * const opts)
|
|
{
|
|
return _run_extcmd(uid, gid, cmd, so_buf, so_buf_sz,
|
|
want_stderr, timeout, NULL, pid_status, opts);
|
|
}
|
|
|
|
/* _run_extcmd() wrapper, search command output for a substring.
|
|
*/
|
|
int
|
|
search_extcmd(const char *cmd, const int want_stderr, const int timeout,
|
|
const char *substr_search, int *pid_status,
|
|
const fko_srv_options_t * const opts)
|
|
{
|
|
return _run_extcmd(ROOT_UID, ROOT_GID, cmd, NULL, 0, want_stderr,
|
|
timeout, substr_search, pid_status, opts);
|
|
}
|
|
|
|
/* _run_extcmd() wrapper, search command output for a substring and return
|
|
* the matching line.
|
|
*/
|
|
int
|
|
search_extcmd_getline(const char *cmd, char *so_buf, const size_t so_buf_sz,
|
|
const int timeout, const char *substr_search, int *pid_status,
|
|
const fko_srv_options_t * const opts)
|
|
{
|
|
return _run_extcmd(ROOT_UID, ROOT_GID, cmd, so_buf, so_buf_sz,
|
|
WANT_STDERR | WANT_STDOUT_GETLINE, timeout, substr_search,
|
|
pid_status, opts);
|
|
}
|
|
|
|
/* _run_extcmd_write() wrapper, run a command which is expecting input via stdin
|
|
*/
|
|
int run_extcmd_write(const char *cmd, const char *cmd_write, int *pid_status,
|
|
const fko_srv_options_t * const opts)
|
|
{
|
|
return _run_extcmd_write(cmd, cmd_write, pid_status, opts);
|
|
}
|