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