Blame src/lib/spawn.c

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
}