1/**
2 * @file child_reader.cpp
3 * Facility for reading from child processes
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Philippe Elie
9 * @author John Levon
10 */
11
12#include <unistd.h>
13#include <sys/wait.h>
14#include <limits.h>
15
16#include <cerrno>
17#include <sstream>
18#include <iostream>
19#include <cstring>
20#include <cstdlib>
21
22#include "op_libiberty.h"
23#include "child_reader.h"
24
25using namespace std;
26
27child_reader::child_reader(string const & cmd, vector<string> const & args)
28	:
29	fd1(-1), fd2(-1),
30	pos1(0), end1(0),
31	pos2(0), end2(0),
32	pid(0),
33	first_error(0),
34	buf2(0), sz_buf2(0),
35	buf1(new char[PIPE_BUF]),
36	process_name(cmd),
37	is_terminated(true),
38	terminate_on_exception(false),
39	forked(false)
40{
41	exec_command(cmd, args);
42}
43
44
45child_reader::~child_reader()
46{
47	terminate_process();
48	delete [] buf1;
49	if (buf2) {
50		// allocated through C alloc
51		free(buf2);
52	}
53}
54
55
56void child_reader::exec_command(string const & cmd, vector<string> const & args)
57{
58	int pstdout[2];
59	int pstderr[2];
60
61	if (pipe(pstdout) == -1 || pipe(pstderr) == -1) {
62		first_error = errno;
63		return;
64	}
65
66	pid = fork();
67	switch (pid) {
68		case -1:
69			first_error = errno;
70			return;
71
72		case 0: {
73			char const ** argv = new char const *[args.size() + 2];
74			size_t i;
75			argv[0] = cmd.c_str();
76
77			for (i = 1 ; i <= args.size() ; ++i)
78				argv[i] = args[i - 1].c_str();
79
80			argv[i] = 0;
81
82			// child: we can cleanup a few fd
83			close(pstdout[0]);
84			dup2(pstdout[1], STDOUT_FILENO);
85			close(pstdout[1]);
86			close(pstderr[0]);
87			dup2(pstderr[1], STDERR_FILENO);
88			close(pstderr[1]);
89
90			execvp(cmd.c_str(), (char * const *)argv);
91
92			int ret_code = errno;
93
94			// we can communicate with parent by writing to stderr
95			// and by returning a non zero error code. Setting
96			// first_error in the child is a non-sense
97
98			// we are in the child process: so this error message
99			// is redirect to the parent process
100			cerr << "Couldn't exec \"" << cmd << "\" : "
101			     << strerror(errno) << endl;
102			exit(ret_code);
103		}
104
105		default:;
106			// parent: we do not write on these fd.
107			close(pstdout[1]);
108			close(pstderr[1]);
109			forked = true;
110			break;
111	}
112
113	fd1 = pstdout[0];
114	fd2 = pstderr[0];
115
116	is_terminated = false;
117
118	return;
119}
120
121
122bool child_reader::block_read()
123{
124	fd_set read_fs;
125
126	FD_ZERO(&read_fs);
127	FD_SET(fd1, &read_fs);
128	FD_SET(fd2, &read_fs);
129
130	if (select(max(fd1, fd2) + 1, &read_fs, 0, 0, 0) >= 0) {
131		if (FD_ISSET(fd1, &read_fs)) {
132			ssize_t temp = read(fd1, buf1, PIPE_BUF);
133			if (temp >= 0)
134				end1 = temp;
135			else
136				end1 = 0;
137		}
138
139		if (FD_ISSET(fd2, &read_fs)) {
140			if (end2 >= sz_buf2) {
141				sz_buf2 = sz_buf2 ? sz_buf2 * 2 : PIPE_BUF;
142				buf2 = (char *)xrealloc(buf2, sz_buf2);
143			}
144
145			ssize_t temp = read(fd2, buf2 + end2, sz_buf2 - end2);
146			if (temp > 0)
147				end2 += temp;
148		}
149	}
150
151	bool ret = !(end1 == 0 && end2 == 0);
152
153	if (end1 == -1)
154		end1 = 0;
155	if (end2 == -1)
156		end2 = 0;
157
158	return ret;
159}
160
161
162bool child_reader::getline(string & result)
163{
164	// some stl lacks string::clear()
165	result.erase(result.begin(), result.end());
166
167	bool ok = true;
168	bool ret = true;
169	bool can_stop = false;
170	do {
171		int temp = end2;
172		if (pos1 >= end1) {
173			pos1 = 0;
174			ret = block_read();
175		}
176
177		// for efficiency try to copy as much as we can of data
178		ssize_t temp_pos = pos1;
179		while (temp_pos < end1 && ok) {
180			char ch = buf1[temp_pos++];
181			if (ch == '\n')
182				ok = false;
183		}
184
185		// !ok ==> endl has been read so do not copy it.
186		result.append(&buf1[pos1], (temp_pos - pos1) - !ok);
187
188		if (!ok || !end1)
189			can_stop = true;
190
191		// reading zero byte from stdout don't mean than we exhausted
192		// all stdout output, we must continue to try until reading
193		// stdout and stderr return zero byte.
194		if (ok && temp != end2)
195			can_stop = false;
196
197		pos1 = temp_pos;
198	} while (!can_stop);
199
200	// Is this correct ?
201	return end1 != 0 || result.length() != 0;
202}
203
204
205bool child_reader::get_data(ostream & out, ostream & err)
206{
207	bool ret = true;
208	while (ret) {
209		ret = block_read();
210
211		out.write(buf1, end1);
212		err.write(buf2, end2);
213
214		end1 = end2 = 0;
215	}
216
217	return first_error == 0;
218}
219
220
221int child_reader::terminate_process()
222{
223	// can be called explicitely or by dtor,
224	// we must protect against multiple call
225	if (!is_terminated) {
226		int ret;
227		waitpid(pid, &ret, 0);
228
229		is_terminated = true;
230
231		if (WIFEXITED(ret)) {
232			first_error = WEXITSTATUS(ret) | WIFSIGNALED(ret);
233		} else if (WIFSIGNALED(ret)) {
234			terminate_on_exception = true;
235			first_error = WTERMSIG(ret);
236		} else {
237			// FIXME: this seems impossible, waitpid *must* wait
238			// and either the process terminate normally or through
239			// a signal.
240			first_error = -1;
241		}
242	}
243
244	if (fd1 != -1) {
245		close(fd1);
246		fd1 = -1;
247	}
248	if (fd2 != -1) {
249		close(fd2);
250		fd2 = -1;
251	}
252
253	return first_error;
254}
255
256
257string child_reader::error_str() const
258{
259	ostringstream err;
260	if (!forked) {
261		err << string("unable to fork, error: ")
262		    << strerror(first_error);
263	} else if (is_terminated) {
264		if (first_error) {
265			if (terminate_on_exception) {
266				err << process_name << " terminated by signal "
267				    << first_error;
268			} else {
269				err << process_name << " return "
270				    << first_error;
271			}
272		}
273	}
274
275	return err.str();
276}
277