• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 using namespace std;
26 
child_reader(string const & cmd,vector<string> const & args)27 child_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 
~child_reader()45 child_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 
exec_command(string const & cmd,vector<string> const & args)56 void 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 
block_read()122 bool 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 
getline(string & result)162 bool 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 
get_data(ostream & out,ostream & err)205 bool 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 
terminate_process()221 int 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 
error_str() const257 string 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