child_reader.cpp

Go to the documentation of this file.
00001 
00012 #include <unistd.h>
00013 #include <sys/wait.h>
00014 #include <limits.h>
00015 
00016 #include <cerrno>
00017 #include <sstream>
00018 #include <iostream>
00019 #include <cstring>
00020 #include <cstdlib>
00021 
00022 #include "op_libiberty.h"
00023 #include "child_reader.h"
00024 
00025 using namespace std;
00026 
00027 child_reader::child_reader(string const & cmd, vector<string> const & args)
00028     :
00029     fd1(-1), fd2(-1),
00030     pos1(0), end1(0),
00031     pos2(0), end2(0),
00032     pid(0),
00033     first_error(0),
00034     buf2(0), sz_buf2(0),
00035     buf1(new char[PIPE_BUF]),
00036     process_name(cmd),
00037     is_terminated(true),
00038     terminate_on_exception(false),
00039     forked(false)
00040 {
00041     exec_command(cmd, args);
00042 }
00043 
00044 
00045 child_reader::~child_reader()
00046 {
00047     terminate_process();
00048     delete [] buf1;
00049     if (buf2) {
00050         // allocated through C alloc
00051         free(buf2);
00052     }
00053 }
00054 
00055 
00056 void child_reader::exec_command(string const & cmd, vector<string> const & args)
00057 {
00058     int pstdout[2];
00059     int pstderr[2];
00060 
00061     if (pipe(pstdout) == -1 || pipe(pstderr) == -1) {
00062         first_error = errno;
00063         return;
00064     }
00065 
00066     pid = fork();
00067     switch (pid) {
00068         case -1:
00069             first_error = errno;
00070             return;
00071 
00072         case 0: {
00073             char const ** argv = new char const *[args.size() + 2];
00074             size_t i;
00075             argv[0] = cmd.c_str();
00076 
00077             for (i = 1 ; i <= args.size() ; ++i)
00078                 argv[i] = args[i - 1].c_str();
00079 
00080             argv[i] = 0;
00081 
00082             // child: we can cleanup a few fd
00083             close(pstdout[0]);
00084             dup2(pstdout[1], STDOUT_FILENO);
00085             close(pstdout[1]);
00086             close(pstderr[0]);
00087             dup2(pstderr[1], STDERR_FILENO);
00088             close(pstderr[1]);
00089 
00090             execvp(cmd.c_str(), (char * const *)argv);
00091 
00092             int ret_code = errno;
00093 
00094             // we can communicate with parent by writing to stderr
00095             // and by returning a non zero error code. Setting
00096             // first_error in the child is a non-sense
00097 
00098             // we are in the child process: so this error message
00099             // is redirect to the parent process
00100             cerr << "Couldn't exec \"" << cmd << "\" : "
00101                  << strerror(errno) << endl;
00102             exit(ret_code);
00103         }
00104 
00105         default:;
00106             // parent: we do not write on these fd.
00107             close(pstdout[1]);
00108             close(pstderr[1]);
00109             forked = true;
00110             break;
00111     }
00112 
00113     fd1 = pstdout[0];
00114     fd2 = pstderr[0];
00115 
00116     is_terminated = false;
00117 
00118     return;
00119 }
00120 
00121 
00122 bool child_reader::block_read()
00123 {
00124     fd_set read_fs;
00125 
00126     FD_ZERO(&read_fs);
00127     FD_SET(fd1, &read_fs);
00128     FD_SET(fd2, &read_fs);
00129 
00130     if (select(max(fd1, fd2) + 1, &read_fs, 0, 0, 0) >= 0) {
00131         if (FD_ISSET(fd1, &read_fs)) {
00132             ssize_t temp = read(fd1, buf1, PIPE_BUF);
00133             if (temp >= 0)
00134                 end1 = temp;
00135             else
00136                 end1 = 0;
00137         }
00138 
00139         if (FD_ISSET(fd2, &read_fs)) {
00140             if (end2 >= sz_buf2) {
00141                 sz_buf2 = sz_buf2 ? sz_buf2 * 2 : PIPE_BUF;
00142                 buf2 = (char *)xrealloc(buf2, sz_buf2);
00143             }
00144 
00145             ssize_t temp = read(fd2, buf2 + end2, sz_buf2 - end2);
00146             if (temp > 0)
00147                 end2 += temp;
00148         }
00149     }
00150 
00151     bool ret = !(end1 == 0 && end2 == 0);
00152 
00153     if (end1 == -1)
00154         end1 = 0;
00155     if (end2 == -1)
00156         end2 = 0;
00157 
00158     return ret;
00159 }
00160 
00161 
00162 bool child_reader::getline(string & result)
00163 {
00164     // some stl lacks string::clear()
00165     result.erase(result.begin(), result.end());
00166 
00167     bool ok = true;
00168     bool can_stop = false;
00169     do {
00170         int temp = end2;
00171         if (pos1 >= end1) {
00172             pos1 = 0;
00173             block_read();
00174         }
00175 
00176         // for efficiency try to copy as much as we can of data
00177         ssize_t temp_pos = pos1;
00178         while (temp_pos < end1 && ok) {
00179             char ch = buf1[temp_pos++];
00180             if (ch == '\n')
00181                 ok = false;
00182         }
00183 
00184         // !ok ==> endl has been read so do not copy it.
00185         result.append(&buf1[pos1], (temp_pos - pos1) - !ok);
00186 
00187         if (!ok || !end1)
00188             can_stop = true;
00189 
00190         // reading zero byte from stdout don't mean than we exhausted
00191         // all stdout output, we must continue to try until reading
00192         // stdout and stderr return zero byte.
00193         if (ok && temp != end2)
00194             can_stop = false;
00195 
00196         pos1 = temp_pos;
00197     } while (!can_stop);
00198 
00199     // Is this correct ?
00200     return end1 != 0 || result.length() != 0;
00201 }
00202 
00203 
00204 bool child_reader::get_data(ostream & out, ostream & err)
00205 {
00206     bool ret = true;
00207     while (ret) {
00208         ret = block_read();
00209 
00210         out.write(buf1, end1);
00211         err.write(buf2, end2);
00212 
00213         end1 = end2 = 0;
00214     }
00215 
00216     return first_error == 0;
00217 }
00218 
00219 
00220 int child_reader::terminate_process()
00221 {
00222     // can be called explicitely or by dtor,
00223     // we must protect against multiple call
00224     if (!is_terminated) {
00225         int ret;
00226         waitpid(pid, &ret, 0);
00227 
00228         is_terminated = true;
00229 
00230         if (WIFEXITED(ret)) {
00231             first_error = WEXITSTATUS(ret) | WIFSIGNALED(ret);
00232         } else if (WIFSIGNALED(ret)) {
00233             terminate_on_exception = true;
00234             first_error = WTERMSIG(ret);
00235         } else {
00236             // FIXME: this seems impossible, waitpid *must* wait
00237             // and either the process terminate normally or through
00238             // a signal.
00239             first_error = -1;
00240         }
00241     }
00242 
00243     if (fd1 != -1) {
00244         close(fd1);
00245         fd1 = -1;
00246     }
00247     if (fd2 != -1) {
00248         close(fd2);
00249         fd2 = -1;
00250     }
00251 
00252     return first_error;
00253 }
00254 
00255 
00256 string child_reader::error_str() const
00257 {
00258     ostringstream err;
00259     if (!forked) {
00260         err << string("unable to fork, error: ")
00261             << strerror(first_error);
00262     } else if (is_terminated) {
00263         if (first_error) {
00264             if (terminate_on_exception) {
00265                 err << process_name << " terminated by signal "
00266                     << first_error;
00267             } else {
00268                 err << process_name << " return "
00269                     << first_error;
00270             }
00271         }
00272     }
00273 
00274     return err.str();
00275 }

Generated on 8 Nov 2012 for Oprofile by  doxygen 1.6.1