/**
* @file child_reader.h
* Facility for reading from child processes
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author Philippe Elie
* @author John Levon
*/
#ifndef CHILD_READER_H
#define CHILD_READER_H
#include <sys/types.h>
#include <vector>
#include <string>
/**
* a class to read stdout / stderr from a child process.
*
* two interfaces are provided. read line by line: getline() or read all data
* in one : get_data(). In all case get_data() must be called once to flush the
* stderr child output
*/
/*
* FIXME: code review is needed:
* - check the getline()/get_data()/block_read() interface.
* the expected behavior is:
* caller can call getline until nothing is available from the stdout of the
* child. in this case child stderr is acumulated in buf2 and can be read
* through get_data(). get_data() is blocking until the child close stderr /
* stdout (even if the child die by a signal ?). The following corner case must
* work but I'm unsure if the code reflect this behavior: the last line of the
* child stdout have not necessarilly a LF terminator. the child can output any
* size of data in stderr.
*/
class child_reader {
public:
/** fork a process. use error() to get error code. Do not try to
* use other public member interface if error() return non-zero */
child_reader(std::string const & cmd,
std::vector<std::string> const & args);
/** wait for the termination of the child process if this have not
* already occur. In this case return code of the child process is not
* available. */
~child_reader();
/** fill result from on line of stdout of the child process.
* must be used as:
* child_reader reader(...);
* while (reader.getline(line)) .... */
bool getline(std::string & result);
/** fill out / err with the stdout / stderr of the child process.
* You can call this after calling one or more time getline(...). This
* call is blocking until the child die and so on all subsequent
* call will fail */
bool get_data(std::ostream & out, std::ostream & err);
/** rather to rely on dtor to wait for the termination of the child you
* can use terminate_process() to get the return code of the child
* process */
int terminate_process();
/** return the status of the first error encoutered
* != 0 : something feel wrong, use error_str() to get an error
* message */
int error() const { return first_error; }
/**
* return an error message if appropriate, if the process has
* been successfully exec'ed and is not terminate the error message
* is always empty. Error message is also empty if the child process
* terminate successfully. Else three type of error message exist:
* - "unable to fork" followed by sterror(errno)
* - "process_name return xxx" xxx is return code
* - "process_name terminated by signal xxx" xxx is signal number
*/
std::string error_str() const;
private:
// ctor helper: create the child process.
void exec_command(std::string const & cmd,
std::vector<std::string> const & args);
// return false when eof condition is reached on fd1. fd2 can have
// already input in the pipe buffer or in buf2.
bool block_read();
int fd1;
int fd2;
ssize_t pos1;
ssize_t end1;
ssize_t pos2;
ssize_t end2;
pid_t pid;
int first_error;
// child stderr is handled especially, we need to retain data even
// if caller read only stdout of the child.
char * buf2;
ssize_t sz_buf2;
char * buf1;
std::string process_name;
bool is_terminated;
bool terminate_on_exception;
bool forked;
};
#endif // CHILD_READER_H