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