Home | History | Annotate | Download | only in libutil++
      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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    257 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