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