summaryrefslogtreecommitdiffstats
path: root/lib/Utils/spawn.cpp
blob: 4b0eecbf59891e8a9e7f6927affa8dd62eb6418b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
 * Utility routines.
 *
 * Licensed under GPLv2, see file COPYING in this tarball for details.
 */
#include "abrtlib.h"

/* Returns pid */
pid_t fork_execv_on_steroids(int flags,
		char **argv,
		int *pipefds,
		char **unsetenv_vec,
		const char *dir,
		uid_t uid)
{
	pid_t child;
	/* Reminder: [0] is read end, [1] is write end */
	int pipe_to_child[2];
	int pipe_fm_child[2];

	/* Sanitize flags */
	if (!pipefds)
		flags &= ~(EXECFLG_INPUT | EXECFLG_OUTPUT);

	if (flags & EXECFLG_INPUT)
		xpipe(pipe_to_child);
	if (flags & EXECFLG_OUTPUT)
		xpipe(pipe_fm_child);

	child = fork();
	if (child == -1) {
		perror_msg_and_die("fork");
	}
	if (child == 0) {
		/* Child */

		/* Play with stdio descriptors */
		if (flags & EXECFLG_INPUT) {
			xmove_fd(pipe_to_child[0], STDIN_FILENO);
		} else if (flags & EXECFLG_INPUT_NUL) {
			xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
		}
		if (flags & EXECFLG_OUTPUT) {
			xmove_fd(pipe_fm_child[1], STDOUT_FILENO);
		} else if (flags & EXECFLG_OUTPUT_NUL) {
			xmove_fd(xopen("/dev/null", O_RDWR), STDOUT_FILENO);
		}
		if (flags & EXECFLG_ERR2OUT) {
			/* Want parent to see errors in the same stream */
			xdup2(STDOUT_FILENO, STDERR_FILENO);
		} else if (flags & EXECFLG_ERR_NUL) {
			xmove_fd(xopen("/dev/null", O_RDWR), STDERR_FILENO);
		}

		if (flags & EXECFLG_SETGUID) {
			struct passwd* pw = getpwuid(uid);
			gid_t gid = pw ? pw->pw_gid : uid;
			setgroups(1, &gid);
			xsetregid(gid, gid);
			xsetreuid(uid, uid);
		}
		if (flags & EXECFLG_SETSID)
			setsid();

		if (unsetenv_vec) {
			while (*unsetenv_vec)
				unsetenv(*unsetenv_vec++);
		}

		if (dir)
			xchdir(dir);

		execvp(argv[0], argv);
		if (!(flags & EXECFLG_QUIET))
			perror_msg("Can't execute '%s'", argv[0]);
		exit(127); /* shell uses this exitcode in this case */
	}

	if (flags & EXECFLG_INPUT) {
		close(pipe_to_child[0]);
		pipefds[1] = pipe_to_child[1];
	}
	if (flags & EXECFLG_OUTPUT) {
		close(pipe_fm_child[1]);
		pipefds[0] = pipe_fm_child[0];
	}

	return child;
}

char *run_in_shell_and_save_output(int flags,
		const char *cmd,
		const char *dir,
		size_t *size_p)
{
	flags |= EXECFLG_OUTPUT;
	flags &= ~EXECFLG_INPUT;

	const char *argv[] = { "/bin/sh", "sh", "-c", cmd, NULL };
	int pipeout[2];
	pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout,
		/*unsetenv_vec:*/ NULL, dir, /*uid (unused):*/ 0);

	size_t pos = 0;
	char *result = NULL;
	while (1) {
		result = (char*) xrealloc(result, pos + 4*1024 + 1);
		size_t sz = safe_read(pipeout[0], result + pos, 4*1024);
		if (sz <= 0) {
			break;
		}
		pos += sz;
	}
	result[pos] = '\0';
	if (size_p)
		*size_p = pos;
	close(pipeout[0]);
	waitpid(child, NULL, 0);

	return result;
}