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