|
Packit Service |
779887 |
/*
|
|
Packit Service |
779887 |
* Utility routines.
|
|
Packit Service |
779887 |
*
|
|
Packit Service |
779887 |
* Copyright (C) 2010 ABRT team
|
|
Packit Service |
779887 |
* Copyright (C) 2010 RedHat Inc
|
|
Packit Service |
779887 |
*
|
|
Packit Service |
779887 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit Service |
779887 |
* it under the terms of the GNU General Public License as published by
|
|
Packit Service |
779887 |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit Service |
779887 |
* (at your option) any later version.
|
|
Packit Service |
779887 |
*
|
|
Packit Service |
779887 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
779887 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
779887 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
779887 |
* GNU General Public License for more details.
|
|
Packit Service |
779887 |
*
|
|
Packit Service |
779887 |
* You should have received a copy of the GNU General Public License along
|
|
Packit Service |
779887 |
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit Service |
779887 |
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit Service |
779887 |
*/
|
|
Packit Service |
779887 |
#include "internal_libreport.h"
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
static char *concat_str_vector(char **strings)
|
|
Packit Service |
779887 |
{
|
|
Packit Service |
779887 |
if (!strings[0])
|
|
Packit Service |
779887 |
return xzalloc(1); // returns ""
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
unsigned len = 0;
|
|
Packit Service |
779887 |
char **spp = strings;
|
|
Packit Service |
779887 |
while (*spp)
|
|
Packit Service |
779887 |
len += strlen(*spp++) + 1;
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
char *result = xmalloc(len);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
char *r = result;
|
|
Packit Service |
779887 |
spp = strings;
|
|
Packit Service |
779887 |
while (*spp) {
|
|
Packit Service |
779887 |
r = stpcpy(r, *spp++);
|
|
Packit Service |
779887 |
*r++ = ' ';
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
*--r = '\0';
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
return result;
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
/* Returns pid */
|
|
Packit Service |
779887 |
pid_t fork_execv_on_steroids(int flags,
|
|
Packit Service |
779887 |
char **argv,
|
|
Packit Service |
779887 |
int *pipefds,
|
|
Packit Service |
779887 |
char **env_vec,
|
|
Packit Service |
779887 |
const char *dir,
|
|
Packit Service |
779887 |
uid_t uid)
|
|
Packit Service |
779887 |
{
|
|
Packit Service |
779887 |
pid_t child;
|
|
Packit Service |
779887 |
/* Reminder: [0] is read end, [1] is write end */
|
|
Packit Service |
779887 |
int pipe_to_child[2];
|
|
Packit Service |
779887 |
int pipe_fm_child[2];
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
/* Sanitize flags */
|
|
Packit Service |
779887 |
if (!pipefds)
|
|
Packit Service |
779887 |
flags &= ~(EXECFLG_INPUT | EXECFLG_OUTPUT);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
if (flags & EXECFLG_INPUT)
|
|
Packit Service |
779887 |
xpipe(pipe_to_child);
|
|
Packit Service |
779887 |
if (flags & EXECFLG_OUTPUT)
|
|
Packit Service |
779887 |
xpipe(pipe_fm_child);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
/* Prepare it before fork, to avoid thread-unsafe malloc there */
|
|
Packit Service |
779887 |
char *prog_as_string = NULL;
|
|
Packit Service |
779887 |
prog_as_string = concat_str_vector(argv);
|
|
Packit Service |
779887 |
gid_t gid;
|
|
Packit Service |
779887 |
if (flags & EXECFLG_SETGUID) {
|
|
Packit Service |
779887 |
struct passwd* pw = getpwuid(uid);
|
|
Packit Service |
779887 |
gid = pw ? pw->pw_gid : uid;
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
fflush(NULL);
|
|
Packit Service |
779887 |
child = fork();
|
|
Packit Service |
779887 |
if (child == -1) {
|
|
Packit Service |
779887 |
perror_msg_and_die("fork");
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
if (child == 0) {
|
|
Packit Service |
779887 |
/* Child */
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
if (dir)
|
|
Packit Service |
779887 |
xchdir(dir);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
if (flags & EXECFLG_SETGUID) {
|
|
Packit Service |
779887 |
setgroups(1, &gid;;
|
|
Packit Service |
779887 |
xsetregid(gid, gid);
|
|
Packit Service |
779887 |
xsetreuid(uid, uid);
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
if (env_vec) {
|
|
Packit Service |
779887 |
/* Note: we use the glibc extension that putenv("var")
|
|
Packit Service |
779887 |
* *unsets* $var if "var" string has no '=' */
|
|
Packit Service |
779887 |
while (*env_vec)
|
|
Packit Service |
779887 |
putenv(*env_vec++);
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
/* Play with stdio descriptors */
|
|
Packit Service |
779887 |
if (flags & EXECFLG_INPUT) {
|
|
Packit Service |
779887 |
/* NB: close must be first, because
|
|
Packit Service |
779887 |
* pipe_to_child[1] may be equal to STDIN_FILENO
|
|
Packit Service |
779887 |
*/
|
|
Packit Service |
779887 |
close(pipe_to_child[1]);
|
|
Packit Service |
779887 |
xmove_fd(pipe_to_child[0], STDIN_FILENO);
|
|
Packit Service |
779887 |
} else if (flags & EXECFLG_INPUT_NUL) {
|
|
Packit Service |
779887 |
xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
if (flags & EXECFLG_OUTPUT) {
|
|
Packit Service |
779887 |
close(pipe_fm_child[0]);
|
|
Packit Service |
779887 |
xmove_fd(pipe_fm_child[1], STDOUT_FILENO);
|
|
Packit Service |
779887 |
} else if (flags & EXECFLG_OUTPUT_NUL) {
|
|
Packit Service |
779887 |
xmove_fd(xopen("/dev/null", O_RDWR), STDOUT_FILENO);
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
/* This should be done BEFORE stderr redirect */
|
|
Packit Service |
779887 |
log_info("Executing: %s", prog_as_string);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
if (flags & EXECFLG_ERR2OUT) {
|
|
Packit Service |
779887 |
/* Want parent to see errors in the same stream */
|
|
Packit Service |
779887 |
xdup2(STDOUT_FILENO, STDERR_FILENO);
|
|
Packit Service |
779887 |
} else if (flags & EXECFLG_ERR_NUL) {
|
|
Packit Service |
779887 |
xmove_fd(xopen("/dev/null", O_RDWR), STDERR_FILENO);
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
if (flags & EXECFLG_SETSID)
|
|
Packit Service |
779887 |
setsid();
|
|
Packit Service |
779887 |
if (flags & EXECFLG_SETPGID)
|
|
Packit Service |
779887 |
setpgid(0, 0);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
execvp(argv[0], argv);
|
|
Packit Service |
779887 |
if (!(flags & EXECFLG_QUIET))
|
|
Packit Service |
779887 |
perror_msg("Can't execute '%s'", argv[0]);
|
|
Packit Service |
779887 |
_exit(127); /* shell uses this exit code in this case */
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
/* Parent */
|
|
Packit Service |
779887 |
free(prog_as_string);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
if (flags & EXECFLG_INPUT) {
|
|
Packit Service |
779887 |
close(pipe_to_child[0]);
|
|
Packit Service |
779887 |
pipefds[1] = pipe_to_child[1];
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
if (flags & EXECFLG_OUTPUT) {
|
|
Packit Service |
779887 |
close(pipe_fm_child[1]);
|
|
Packit Service |
779887 |
pipefds[0] = pipe_fm_child[0];
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
return child;
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
char *run_in_shell_and_save_output(int flags,
|
|
Packit Service |
779887 |
const char *cmd,
|
|
Packit Service |
779887 |
const char *dir,
|
|
Packit Service |
779887 |
size_t *size_p)
|
|
Packit Service |
779887 |
{
|
|
Packit Service |
779887 |
flags |= EXECFLG_OUTPUT;
|
|
Packit Service |
779887 |
flags &= ~EXECFLG_INPUT;
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
const char *argv[] = { "/bin/sh", "-c", cmd, NULL };
|
|
Packit Service |
779887 |
int pipeout[2];
|
|
Packit Service |
779887 |
pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout,
|
|
Packit Service |
779887 |
/*env_vec:*/ NULL, dir, /*uid (unused):*/ 0);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
size_t pos = 0;
|
|
Packit Service |
779887 |
char *result = NULL;
|
|
Packit Service |
779887 |
while (1) {
|
|
Packit Service |
779887 |
result = (char*) xrealloc(result, pos + 4*1024 + 1);
|
|
Packit Service |
779887 |
size_t sz = safe_read(pipeout[0], result + pos, 4*1024);
|
|
Packit Service |
779887 |
if (sz <= 0) {
|
|
Packit Service |
779887 |
break;
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
pos += sz;
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
result[pos] = '\0';
|
|
Packit Service |
779887 |
if (size_p)
|
|
Packit Service |
779887 |
*size_p = pos;
|
|
Packit Service |
779887 |
close(pipeout[0]);
|
|
Packit Service |
779887 |
safe_waitpid(child, NULL, 0);
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
return result;
|
|
Packit Service |
779887 |
}
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
pid_t safe_waitpid(pid_t pid, int *wstat, int options)
|
|
Packit Service |
779887 |
{
|
|
Packit Service |
779887 |
pid_t r;
|
|
Packit Service |
779887 |
|
|
Packit Service |
779887 |
do
|
|
Packit Service |
779887 |
r = waitpid(pid, wstat, options);
|
|
Packit Service |
779887 |
while ((r == -1) && (errno == EINTR));
|
|
Packit Service |
779887 |
return r;
|
|
Packit Service |
779887 |
}
|