new feature PartialExec
This commit is contained in:
266
mrw/exec.cpp
266
mrw/exec.cpp
@@ -9,6 +9,9 @@
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.11 2005/04/19 18:48:00 marc
|
||||
new feature PartialExec
|
||||
|
||||
Revision 1.10 2005/03/14 16:26:34 marc
|
||||
bugs have been fixed a long time ago, now no more in buglist
|
||||
|
||||
@@ -48,7 +51,12 @@
|
||||
#include <unistd.h> // fork, exec
|
||||
#include <string.h> // memcpy
|
||||
#include <assert.h> // assert
|
||||
#include <sys/types.h> // kill
|
||||
#include <signal.h> // kill
|
||||
|
||||
//=========================================================== ExecutionFailedExc
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
|
||||
const std::string& c)
|
||||
throw(std::bad_exception):
|
||||
@@ -65,22 +73,28 @@ mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
|
||||
*/
|
||||
}
|
||||
|
||||
//========================================================================== Cmd
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Cmd::Cmd(const std::string& c) throw(std::bad_exception) {
|
||||
_cmd.push_back(c);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Cmd& mrw::Cmd::operator,(const std::string& arg)
|
||||
throw(std::bad_exception) {
|
||||
_cmd.push_back(arg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Cmd& mrw::Cmd::operator<<(const std::string& arg)
|
||||
throw(std::bad_exception) {
|
||||
_cmd.push_back(arg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Cmd::operator std::string() const throw(std::bad_exception) {
|
||||
ArgList::const_iterator it(_cmd.begin());
|
||||
std::string c(*it);
|
||||
@@ -88,22 +102,38 @@ mrw::Cmd::operator std::string() const throw(std::bad_exception) {
|
||||
return c;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) {
|
||||
return mrw::Exec(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Cmd::operator mrw::PartialExec() const throw(std::bad_exception) {
|
||||
return mrw::PartialExec(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec mrw::Cmd::execute(bool exc) const throw(std::exception) {
|
||||
return mrw::Exec(*this).execute(exc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec mrw::Cmd::execute(const std::string& input, bool exc) const
|
||||
throw(std::exception) {
|
||||
return mrw::Exec(*this).execute(input, exc);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec mrw::Cmd::start(bool useInput) const throw(std::exception) {
|
||||
return mrw::PartialExec(*this).start(useInput);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* mrw::Cmd::path() const throw(std::bad_exception) {
|
||||
return _cmd.front().c_str();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
char** mrw::Cmd::args() const throw(std::bad_exception) {
|
||||
if (_cmd.size()==0) return 0;
|
||||
char** array = new char*[_cmd.size()+1];
|
||||
@@ -114,25 +144,32 @@ char** mrw::Cmd::args() const throw(std::bad_exception) {
|
||||
return array;
|
||||
}
|
||||
|
||||
//========================================================================= Exec
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec::Exec(const mrw::Cmd& c) throw(std::bad_exception):
|
||||
_cmd(new mrw::Cmd(c)), _success(false) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec::Exec(const mrw::Exec& e) throw(std::bad_exception):
|
||||
_cmd(new mrw::Cmd(*e._cmd)),
|
||||
_res(e._res), _err(e._err), _success(e._success) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec::~Exec() throw() {
|
||||
delete _cmd;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec& mrw::Exec::operator=(const mrw::Exec& e) throw(std::bad_exception) {
|
||||
if (this==&e) return *this;
|
||||
*_cmd=*e._cmd; _res=e._res; _err=e._err; _success=e._success;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
|
||||
/** This method calls @c fork, sets up a pipe connection to pass @c
|
||||
stdout and @c stderr from the child process to the parent
|
||||
@@ -167,16 +204,22 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
|
||||
// check and handle stdout
|
||||
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
|
||||
_res += std::string(buf, num1);
|
||||
else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN))
|
||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||||
else if (num1==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||||
else
|
||||
num1 = 1;
|
||||
// check and handle stderr
|
||||
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
|
||||
_err += std::string(buf, num2);
|
||||
else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN))
|
||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||||
else if (num2==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||||
else
|
||||
num2 = 1;
|
||||
} catch (...) {
|
||||
if (exc) throw;
|
||||
_success = false;
|
||||
if (exc) throw;
|
||||
return *this;
|
||||
}
|
||||
} else { // child
|
||||
@@ -191,6 +234,7 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
||||
throw(std::exception) {
|
||||
/// @c input length must be smaller than @c SSIZE_MAX.
|
||||
@@ -199,7 +243,7 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
||||
"sdin input exeeds C library limit in mrw::Exec "
|
||||
"please contact the author of the library");
|
||||
/** This method calls @c fork, sets up a pipe connection to pass @c
|
||||
stdin, @c stdout and @c stderr from the child process to the
|
||||
stdin, @c stdout and @c stderr between the child process and the
|
||||
parent process using mrw::Pipe and calls @c execvp to execute
|
||||
the program. */
|
||||
_success = false;
|
||||
@@ -236,21 +280,30 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
||||
in = in.substr(num0);
|
||||
else if ((unsigned int)num0==in.size())
|
||||
num0=0, stdIn.close_out();
|
||||
} else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN))
|
||||
throw ExecutionFailedExc("writing stdin", *_cmd);
|
||||
} else if (num0==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("writing stdin", *_cmd);
|
||||
else
|
||||
num0 = 1;
|
||||
// check and handle stdout
|
||||
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
|
||||
_res += std::string(buf, num1);
|
||||
else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN))
|
||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||||
else if (num1==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||||
else
|
||||
num1 = 1;
|
||||
// check and handle stderr
|
||||
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
|
||||
_err += std::string(buf, num2);
|
||||
else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN))
|
||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||||
else if (num2==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||||
else
|
||||
num2 = 1;
|
||||
} catch (...) {
|
||||
if (exc) throw;
|
||||
_success = false;
|
||||
if (exc) throw;
|
||||
return *this;
|
||||
}
|
||||
} else { // child
|
||||
@@ -267,31 +320,218 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) {
|
||||
execute();
|
||||
res += _res;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec::operator std::string&() throw(std::exception) {
|
||||
if (!_success) execute();
|
||||
return _res;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::Exec::operator bool() throw(std::bad_exception) {
|
||||
return _success;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string& mrw::Exec::result() throw(std::exception) {
|
||||
if (!_success) execute();
|
||||
return _res;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string& mrw::Exec::error() throw(std::exception) {
|
||||
if (!_success) execute();
|
||||
return _err;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool mrw::Exec::success() throw(std::bad_exception) {
|
||||
return _success;
|
||||
}
|
||||
|
||||
//================================================================== PartialExec
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec::PartialExec(const mrw::Cmd& c) throw(std::bad_exception):
|
||||
Exec(c), _finished(true), _finish(false) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec::PartialExec(mrw::PartialExec& e)
|
||||
throw(std::bad_exception):
|
||||
Exec(e), _finished(e._finished), _finish(e._finish),
|
||||
_stdIn(e._stdIn), _stdOut(e._stdOut), _stdErr(e._stdErr), _input(e._input),
|
||||
_num0(e._num0), _num1(e._num1), _num2(e._num2),
|
||||
_lastPid(e._lastPid), _pid(e._pid) {
|
||||
e._finished = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec::PartialExec(const mrw::PartialExec& e)
|
||||
throw(std::bad_exception):
|
||||
Exec(e), _finished(e._finished), _finish(e._finish),
|
||||
_input(e._input),
|
||||
_num0(e._num0), _num1(e._num1), _num2(e._num2),
|
||||
_lastPid(e._lastPid), _pid(e._pid) {
|
||||
/// @warning @c const is casted away
|
||||
mrw::PartialExec& nonConstE(const_cast<mrw::PartialExec&>(e));
|
||||
_stdIn = nonConstE._stdIn;
|
||||
_stdOut = nonConstE._stdOut;
|
||||
_stdErr = nonConstE._stdErr;
|
||||
nonConstE._finished = true;
|
||||
nonConstE._finish = false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec& mrw::PartialExec::operator=(mrw::PartialExec& e)
|
||||
throw(std::bad_exception) {
|
||||
if (this==&e) return *this;
|
||||
*_cmd=*e._cmd; _res=e._res; _err=e._err; _success=e._success;
|
||||
_finished = e._finished;
|
||||
_finish = e._finish;
|
||||
_stdIn = e._stdIn; _stdOut = e._stdOut; _stdErr = e._stdErr;
|
||||
_input = e._input; _num0 = e._num0; _num1 = e._num1; _num2 = e._num2;
|
||||
_lastPid = e._lastPid;
|
||||
_pid = e._pid;
|
||||
e._finished = true;
|
||||
e._finish = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec& mrw::PartialExec::finish() throw() {
|
||||
_finish = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool mrw::PartialExec::finished() throw() {
|
||||
return _finished;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec& mrw::PartialExec::start(bool useInput)
|
||||
throw(std::exception) {
|
||||
if (!_finished) throw mrw::runtime_error("running process not yet finished");
|
||||
/** This method calls @c fork, sets up a pipe connection to pass @c
|
||||
stdin, @c stdout and @c stderr between the child process and the
|
||||
parent process using mrw::Pipe and calls @c execvp to execute
|
||||
the program. */
|
||||
_success = false;
|
||||
_finish = !useInput;
|
||||
_input = "";
|
||||
_res = _err = "";
|
||||
_stdIn = std::auto_ptr<mrw::Pipe>(new mrw::Pipe(mrw::Pipe::block_input));
|
||||
_stdOut = std::auto_ptr<mrw::Pipe>(new mrw::Pipe(mrw::Pipe::block_output));
|
||||
_stdErr = std::auto_ptr<mrw::Pipe>(new mrw::Pipe(mrw::Pipe::block_output));
|
||||
if (!*_stdIn || !*_stdOut || !*_stdErr)
|
||||
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
|
||||
_lastPid = 0;
|
||||
_pid = fork();
|
||||
if (_pid<0) throw ExecutionFailedExc("cannot fork", *_cmd);
|
||||
if (_pid) { // parent
|
||||
_stdIn->close_in();
|
||||
_stdOut->close_out();
|
||||
_stdErr->close_out();
|
||||
if (!*_stdIn || !*_stdOut || !*_stdErr)
|
||||
throw ExecutionFailedExc("cannot close pipes", *_cmd);
|
||||
_num0 = _num1 = _num2 = 1;
|
||||
_finished = false;
|
||||
} else { // child
|
||||
_stdIn->close_out();
|
||||
_stdOut->close_in();
|
||||
_stdErr->close_in();
|
||||
_stdIn->connect_cin();
|
||||
_stdOut->connect_cout();
|
||||
_stdErr->connect_cerr();
|
||||
execvp(_cmd->path(), _cmd->args());
|
||||
exit(1); // execute failed
|
||||
}
|
||||
_success = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::pair<std::string, std::string>
|
||||
mrw::PartialExec::read(const std::string& input, bool exc)
|
||||
throw(std::exception) {
|
||||
std::pair<std::string, std::string> output;
|
||||
/** @note @c input length must be smaller than @c SSIZE_MAX.
|
||||
I'll only add support for longer strings upon request. */
|
||||
assert(input.size()<=SSIZE_MAX &&
|
||||
"sdin input exeeds C library limit in mrw::Exec "
|
||||
"please contact the author of the library");
|
||||
/** @warning After calling finish(), it is forbidden to pass
|
||||
any @c input string, it must then always be empty,
|
||||
because the pipe is already closed! */
|
||||
assert(!(_finish && input.size()) &&
|
||||
"after calling PartialExec::finish(), it is forbidden "
|
||||
"to pass new input text, because the pipe is already "
|
||||
"closed! this is a programming but that must be solved!");
|
||||
_input += input;
|
||||
/* It sometimes did not get the whole input since I changed to non
|
||||
blocking pipes. Now I have found the solution to the problems:
|
||||
Non blocking IO does not work in conjunction with select! Also
|
||||
the pipe must not be nonblocking on both ends, but only on
|
||||
one. */
|
||||
if (!_finished) try { // not finished
|
||||
char buf[4096];
|
||||
int s(0);
|
||||
if (!_lastPid && (_lastPid=waitpid(_pid, &s, WNOHANG))) {
|
||||
if (_lastPid!=_pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
|
||||
throw ExecutionFailedExc("execution failed", *_cmd);
|
||||
}
|
||||
// check and handle stdin
|
||||
if (_input.size() && _num0!=-1 &&
|
||||
(_num0=write(_stdIn->ostream(), _input.c_str(), _input.size()))>0) {
|
||||
if ((unsigned int)_num0<_input.size())
|
||||
_input = _input.substr(_num0);
|
||||
else if ((unsigned int)_num0==_input.size())
|
||||
_input.clear();
|
||||
} else if (_num0==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("writing stdin", *_cmd);
|
||||
else
|
||||
_num0 = 1;
|
||||
// check and handle stdout
|
||||
if (_num1 && (_num1=::read(_stdOut->istream(), buf, sizeof(buf)))>0)
|
||||
_res += output.first=std::string(buf, _num1);
|
||||
else if (_num1==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||||
else
|
||||
_num1 = 1;
|
||||
// check and handle stderr
|
||||
if (_num2 && (_num2=::read(_stdErr->istream(), buf, sizeof(buf)))>0)
|
||||
_err += output.second=std::string(buf, _num2);
|
||||
else if (_num2==-1)
|
||||
if (errno!=EINTR&&errno!=EAGAIN)
|
||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||||
else
|
||||
_num2 = 1;
|
||||
if (_finish && !_input.size()) {
|
||||
_stdIn->close_out();
|
||||
_num0 = 0;
|
||||
}
|
||||
_finished = _finish && !_input.size() && !_num1 && !_num2 && _lastPid;
|
||||
} catch (mrw::exception& x) {
|
||||
_finished = true;
|
||||
_success = false;
|
||||
if (exc) throw;
|
||||
return output;
|
||||
}
|
||||
if (_finished) _success = true;
|
||||
return output;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
mrw::PartialExec& mrw::PartialExec::terminate() throw() {
|
||||
kill(_pid, SIGTERM);
|
||||
return *this;
|
||||
}
|
||||
|
536
mrw/exec.hpp
536
mrw/exec.hpp
@@ -9,6 +9,9 @@
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.6 2005/04/19 18:48:00 marc
|
||||
new feature PartialExec
|
||||
|
||||
Revision 1.5 2004/12/14 20:30:09 marc
|
||||
added possibility to pass string to stdin of child process
|
||||
|
||||
@@ -35,6 +38,7 @@
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/unistd.hpp>
|
||||
|
||||
namespace mrw {
|
||||
|
||||
@@ -81,12 +85,12 @@ namespace mrw {
|
||||
// "cat" passes all from stdin to stdout, therefore:
|
||||
assert(cat.result()=="this is passed to stdin");
|
||||
} catch (...) {} // ignore
|
||||
@endcode
|
||||
*/
|
||||
@endcode */
|
||||
//@{
|
||||
|
||||
class Cmd;
|
||||
|
||||
|
||||
//============================================================================
|
||||
/** @brief Exception: Execution of command failed.
|
||||
@pre #include <mrw/exec.hpp>
|
||||
|
||||
@@ -95,18 +99,18 @@ namespace mrw {
|
||||
to create the necessary pipes, or the command executing process
|
||||
terminated with an error. In the last case, you can access the
|
||||
error stream from @c stderr respectively @c cerr with method
|
||||
mrw::Exec::error().
|
||||
*/
|
||||
mrw::Exec::error(). */
|
||||
class ExecutionFailedExc: public mrw::exception {
|
||||
public:
|
||||
ExecutionFailedExc(const std::string&, const std::string&)
|
||||
throw(std::bad_exception);
|
||||
virtual ~ExecutionFailedExc() throw() {}
|
||||
virtual const char* what() const throw() {return _what.c_str();}
|
||||
private:
|
||||
std::string _what;
|
||||
public:
|
||||
ExecutionFailedExc(const std::string&, const std::string&)
|
||||
throw(std::bad_exception);
|
||||
virtual ~ExecutionFailedExc() throw() {}
|
||||
virtual const char* what() const throw() {return _what.c_str();}
|
||||
private:
|
||||
std::string _what;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
/** @brief Execute a command in a new process.
|
||||
@pre #include <mrw/exec.hpp>
|
||||
|
||||
@@ -137,140 +141,295 @@ namespace mrw {
|
||||
// ls.error() contains stderr
|
||||
@endcode
|
||||
|
||||
@note Please note that the command execution may throw an exception.
|
||||
*/
|
||||
@note Please note that the command execution may throw an exception. */
|
||||
class Exec {
|
||||
public:
|
||||
|
||||
//................................................................ methods
|
||||
public:
|
||||
|
||||
/** @brief Create an executor given a command.
|
||||
Construction without passing a command is not possible. */
|
||||
Exec(const mrw::Cmd&) throw(std::bad_exception);
|
||||
/** @brief Create an executor given a command.
|
||||
Construction without passing a command is not possible. */
|
||||
Exec(const mrw::Cmd&) throw(std::bad_exception);
|
||||
|
||||
Exec(const mrw::Exec&) throw(std::bad_exception);
|
||||
~Exec() throw();
|
||||
Exec& operator=(const mrw::Exec&) throw(std::bad_exception);
|
||||
Exec(const mrw::Exec&) throw(std::bad_exception);
|
||||
~Exec() throw();
|
||||
Exec& operator=(const mrw::Exec&) throw(std::bad_exception);
|
||||
|
||||
/** @brief Execute the command.
|
||||
/** @brief Execute the command.
|
||||
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error
|
||||
*/
|
||||
Exec& execute(bool exc=true) throw(std::exception);
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error */
|
||||
Exec& execute(bool exc=true) throw(std::exception);
|
||||
|
||||
/** @brief Execute the command, pass @c stdin.
|
||||
/** @brief Execute the command, pass @c stdin.
|
||||
|
||||
@param input Input that is passed to @c stdin of the child process.
|
||||
@param input Input that is passed to @c stdin of the child process.
|
||||
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error
|
||||
*/
|
||||
Exec& execute(const std::string& input, bool exc=true)
|
||||
throw(std::exception);
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error */
|
||||
Exec& execute(const std::string& input, bool exc=true)
|
||||
throw(std::exception);
|
||||
|
||||
/** @brief Execute the command, pass @c stdin.
|
||||
/** @brief Execute the command, pass @c stdin.
|
||||
|
||||
@param input Input that is passed to @c stdin of the child process.
|
||||
@param input Input that is passed to @c stdin of the child process.
|
||||
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error
|
||||
*/
|
||||
Exec& execute(char const*const input, bool exc=true)
|
||||
throw(std::exception) {
|
||||
return execute(std::string(input), exc);
|
||||
}
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error */
|
||||
Exec& execute(char const*const input, bool exc=true)
|
||||
throw(std::exception) {
|
||||
return execute(std::string(input), exc);
|
||||
}
|
||||
|
||||
/** @brief Executes the command if not done, streams @c stdout
|
||||
into a string
|
||||
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stdout output of the called
|
||||
program is appended to the string.
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status. */
|
||||
Exec& operator>>(std::string&) throw(std::exception);
|
||||
|
||||
/** @brief Executes the command if not done, streams @c stdout into a string
|
||||
/** @brief Executes the command if not done, returns @c stdout as string
|
||||
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stdout output of the called
|
||||
program is appended to the string.
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stdout output of the called
|
||||
program is returned.
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status.
|
||||
*/
|
||||
Exec& operator>>(std::string&) throw(std::exception);
|
||||
@return @c stdout of the called program
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status. */
|
||||
operator std::string&() throw(std::exception);
|
||||
|
||||
/** @return
|
||||
- @c true if the last execution was successful
|
||||
- @c false if the last execution failed or the command was
|
||||
never executed */
|
||||
operator bool() throw(std::bad_exception);
|
||||
|
||||
/** @brief Executes the command if not done, returns @c stdout as string
|
||||
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stdout output of the called
|
||||
program is returned.
|
||||
|
||||
@return @c stdout of the called program
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status. */
|
||||
std::string& result() throw(std::exception);
|
||||
|
||||
/** @brief Executes the command if not done, returns @c stderr as string
|
||||
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stderr error output of the called
|
||||
program is returned.
|
||||
|
||||
@return @c stderr of the called program
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status. */
|
||||
std::string& error() throw(std::exception);
|
||||
|
||||
/** @brief Executes the command if not done, returns @c stdout as string
|
||||
/** @return
|
||||
- @c true if the last execution was successful
|
||||
- @c false if the last execution failed or the command was
|
||||
never executed */
|
||||
bool success() throw(std::bad_exception);
|
||||
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stdout output of the called
|
||||
program is returned.
|
||||
//................................................................ methods
|
||||
private:
|
||||
|
||||
Exec(); // no default constructor
|
||||
|
||||
@return @c stdout of the called program
|
||||
//.............................................................. variables
|
||||
private:
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status.
|
||||
*/
|
||||
operator std::string&() throw(std::exception);
|
||||
|
||||
/** @return
|
||||
- @c true if the last execution was successful
|
||||
- @c false if the last execution failed or the command was
|
||||
never executed
|
||||
*/
|
||||
operator bool() throw(std::bad_exception);
|
||||
|
||||
/** @brief Executes the command if not done, returns @c stdout as string
|
||||
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stdout output of the called
|
||||
program is returned.
|
||||
|
||||
@return @c stdout of the called program
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status.
|
||||
*/
|
||||
std::string& result() throw(std::exception);
|
||||
|
||||
/** @brief Executes the command if not done, returns @c stderr as string
|
||||
|
||||
If the command has not yet been executed successfully, it is
|
||||
first executed, then the @c stderr error output of the called
|
||||
program is returned.
|
||||
|
||||
@return @c stderr of the called program
|
||||
|
||||
@throw ExecutionFailedExc in case of any failure or if the
|
||||
executed program does not return a zero exit status.
|
||||
*/
|
||||
std::string& error() throw(std::exception);
|
||||
|
||||
/** @return
|
||||
- @c true if the last execution was successful
|
||||
- @c false if the last execution failed or the command was
|
||||
never executed
|
||||
*/
|
||||
bool success() throw(std::bad_exception);
|
||||
|
||||
private:
|
||||
Exec(); // no default constructor
|
||||
mrw::Cmd* _cmd;
|
||||
std::string _res, _err;
|
||||
bool _success;
|
||||
friend class PartialExec; // don't want the variables protected
|
||||
mrw::Cmd* _cmd;
|
||||
std::string _res, _err;
|
||||
bool _success;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
/** @brief Execute a UNIX program in non blocking parts.
|
||||
@pre #include <mrw/exec.hpp>
|
||||
|
||||
A given UNIX command is executed, but the class does not wait
|
||||
until it is finished, instead it gives back the control to the
|
||||
caller. This behaviour is achieved using non blocking
|
||||
communication. But the caller is responsible to retrieve all
|
||||
information from the client, and if necessary to close the input
|
||||
pipe of the client executable program. Therefore you have to
|
||||
give back control from time to time, normally this is doen in a
|
||||
@c while loop, where you can execute also different thing,
|
||||
e.g. update a display of the result or similar.
|
||||
|
||||
With this class, you can communicate with a child process, and
|
||||
do other things at the same time, without the need for multi
|
||||
threading.
|
||||
|
||||
Execution of a program works the following way:
|
||||
- do not use execute() (otherwise the behaviours is identical
|
||||
to class mrw::Exec, you gain nothing, but also loose nothing)
|
||||
- use start() to start the external program
|
||||
- use start() or @c start(false) if you don't want to pass
|
||||
input to the child process
|
||||
- use @c start(true) if you want to pass input to the
|
||||
child process
|
||||
- if you called @c start(true), call finish() if you have no more
|
||||
input to send to the child process (it's like an end-of-file)
|
||||
- the execution is not terminated, before finished() returns
|
||||
@c true
|
||||
- while finished() is false, subsequently call read() to read
|
||||
the output of the child process
|
||||
|
||||
@warning After calling finish(), or if you did not call
|
||||
start() with parameter @c true, it is forbidden to
|
||||
pass anything but an empty string as first parameter to
|
||||
read()! Anything else is a programming error and
|
||||
results in an assertion failure and a core dump!
|
||||
|
||||
@note If your program seems to hang, check if you call finish()
|
||||
correctly!
|
||||
|
||||
Here an example:
|
||||
@code
|
||||
mrw::PartialExec exec = mrw::Cmd("/bin/cat").start(true);
|
||||
std::string res = exec.read("This is a test\n").first;
|
||||
res += exec.read("This is another test\n").first;
|
||||
exec.finish(); // close the input pipe of @c cat
|
||||
while (!exec.finished()) res+=exec.read().first;
|
||||
@endcode */
|
||||
class PartialExec: public Exec {
|
||||
|
||||
//................................................................ methods
|
||||
public:
|
||||
|
||||
/** @brief Create an executor given a command.
|
||||
Construction without passing a command is not possible. */
|
||||
PartialExec(const mrw::Cmd&) throw(std::bad_exception);
|
||||
|
||||
/** @brief Copy construction invalidates the original object.
|
||||
|
||||
All opened pipes (opened with start()) are lost in the
|
||||
original object and are then owned by the new object. */
|
||||
PartialExec(mrw::PartialExec&) throw(std::bad_exception);
|
||||
|
||||
/** @brief Copy construction invalidates the original object.
|
||||
|
||||
@copydoc PartialExec(mrw::PartialExec&)
|
||||
|
||||
@warning @c const for the argument is a fake! It is casted away!
|
||||
|
||||
@param e @b Warning: const is casted away! */
|
||||
PartialExec(const mrw::PartialExec& e) throw(std::bad_exception);
|
||||
|
||||
/** @brief Assignment invalidates the original object.
|
||||
|
||||
@copydoc PartialExec(mrw::PartialExec&) */
|
||||
PartialExec& operator=(mrw::PartialExec&) throw(std::bad_exception);
|
||||
|
||||
/** @brief Close the input pipe of the child process.
|
||||
|
||||
If start() is called with argument @c false, then you
|
||||
can pass input to @c stdin of the child process, but you @b
|
||||
must call this method, after passing the last input
|
||||
string. Otherwise, the child's input pipe won't be closed,
|
||||
the child process does not stop waiting for more input! If
|
||||
your program seems to hang, check if you call finish()
|
||||
correctly! */
|
||||
PartialExec& finish() throw();
|
||||
|
||||
/** @brief Check if there's more data left to read().
|
||||
@return @c true if the child process has finished and all
|
||||
data is read. */
|
||||
bool finished() throw();
|
||||
|
||||
/** @brief Start a new child process.
|
||||
|
||||
At most one child process can run at the same time.
|
||||
|
||||
@throw mrw::runtime_error if a previous child has not finished() yet
|
||||
@throw mrw::ExecutionFailedExc if the child process cannot be started
|
||||
@param useInput
|
||||
- @c true if input will be sent to the child's @c stdin
|
||||
- pass all input in the first parameter of read()
|
||||
- finish() must be called when all input is sent
|
||||
- @c false if no input is sent to the child's @c stdin
|
||||
- the first parameter of read must allways be passed an
|
||||
empty string */
|
||||
PartialExec& start(bool useInput=false) throw(std::exception);
|
||||
|
||||
/** @brief Read from the subprocess, optionally pass an @c input to
|
||||
@c stdin of the subprocess.
|
||||
|
||||
@param input a string to pass to the child processes @c stdin
|
||||
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
|
||||
@return a pair containing the last read @c stdout and @c stderr
|
||||
of the child
|
||||
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error
|
||||
|
||||
@note If start() was not called with parameter @c true, then
|
||||
@c input must always be an empty string!
|
||||
|
||||
@pre start() was called */
|
||||
std::pair<std::string, std::string> read(const std::string& input="",
|
||||
bool exc=true)
|
||||
throw(std::exception);
|
||||
|
||||
/// Terminates a running job by sending @c SIGTERM to the child process.
|
||||
PartialExec& terminate() throw();
|
||||
|
||||
//................................................................ methods
|
||||
private:
|
||||
|
||||
PartialExec(); // no default constructor
|
||||
|
||||
//.............................................................. variables
|
||||
private:
|
||||
|
||||
bool _finished;
|
||||
bool _finish;
|
||||
std::auto_ptr<mrw::Pipe> _stdIn, _stdOut, _stdErr;
|
||||
std::string _input;
|
||||
int _num0, _num1, _num2, _lastPid, _pid;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
/** @brief A system command to be executed
|
||||
@pre #include <mrw/exec.hpp>
|
||||
|
||||
@@ -293,115 +452,62 @@ namespace mrw {
|
||||
@endcode
|
||||
*/
|
||||
class Cmd {
|
||||
public:
|
||||
/** @brief Create a command given the name of the executable
|
||||
@param command the name of the program to execute (no parameter)
|
||||
@note There is no default constructor. */
|
||||
Cmd(const std::string& command) throw(std::bad_exception);
|
||||
public:
|
||||
/** @brief Create a command given the name of the executable
|
||||
@param command the name of the program to execute (no parameter)
|
||||
@note There is no default constructor. */
|
||||
Cmd(const std::string& command) throw(std::bad_exception);
|
||||
|
||||
/** @brief Append a parameter to a command
|
||||
@param param a parameter / commandline argument
|
||||
to append to the command */
|
||||
Cmd& operator,(const std::string& param) throw(std::bad_exception);
|
||||
/** @brief Append a parameter to a command
|
||||
@param param a parameter / commandline argument
|
||||
to append to the command */
|
||||
Cmd& operator,(const std::string& param) throw(std::bad_exception);
|
||||
|
||||
/** @brief Append a parameter to a command
|
||||
@param param a parameter / commandline argument
|
||||
to append to the command */
|
||||
Cmd& operator<<(const std::string& param) throw(std::bad_exception);
|
||||
/** @brief Append a parameter to a command
|
||||
@param param a parameter / commandline argument
|
||||
to append to the command */
|
||||
Cmd& operator<<(const std::string& param) throw(std::bad_exception);
|
||||
|
||||
/** @return the command including parameter */
|
||||
operator std::string() const throw(std::bad_exception);
|
||||
/** @return the command including parameter */
|
||||
operator std::string() const throw(std::bad_exception);
|
||||
|
||||
/** @return a mrw::Exec that's constructed with this class */
|
||||
operator mrw::Exec() const throw(std::bad_exception);
|
||||
/** @return a mrw::Exec that's constructed with this class */
|
||||
operator mrw::Exec() const throw(std::bad_exception);
|
||||
|
||||
/** @brief Create a mrw::Exec and execute the command
|
||||
/** @return a mrw::PartialExec that's constructed with this class */
|
||||
operator mrw::PartialExec() const throw(std::bad_exception);
|
||||
|
||||
Creates a mrw::Exec, executes the command, passes the flag to
|
||||
mrw::Exec::execute() and returns the created mrw::Exec. The
|
||||
result of the execution can be retrieved through the returned
|
||||
mrw::Exec object: The methods mrw::Exec::success(),
|
||||
mrw::Exec::result() and mrw::Exec::error() provide the
|
||||
necessary information.
|
||||
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
|
||||
@return the mrw::Exec that has executed the command
|
||||
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error
|
||||
*/
|
||||
Exec execute(bool exc=true) const throw(std::exception);
|
||||
/** @brief Create a mrw::Exec and execute a child process.
|
||||
@see Exec::execute(bool) */
|
||||
Exec execute(bool exc=true) const throw(std::exception);
|
||||
|
||||
/** @brief Create a mrw::Exec and execute the command given an input
|
||||
|
||||
Creates a mrw::Exec, executes the command, passes the input
|
||||
and the flag to mrw::Exec::execute() and returns the created
|
||||
mrw::Exec. The result of the execution can be retrieved
|
||||
through the returned mrw::Exec object: The methods
|
||||
mrw::Exec::success(), mrw::Exec::result() and
|
||||
mrw::Exec::error() provide the necessary information.
|
||||
|
||||
@param input Input that is passed to @c stdin of the child process.
|
||||
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
|
||||
@return the mrw::Exec that has executed the command
|
||||
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error
|
||||
*/
|
||||
Exec execute(const std::string& input, bool exc=true) const
|
||||
throw(std::exception);
|
||||
/** @brief Create a mrw::Exec and execute a child process.
|
||||
@see Exec::execute(const std::string&, bool) */
|
||||
Exec execute(const std::string& input, bool exc=true) const
|
||||
throw(std::exception);
|
||||
|
||||
/** @brief Create a mrw::Exec and execute the command given an input
|
||||
/** @brief Create a mrw::Exec and execute a child process.
|
||||
@see Exec::execute(char const*const, bool) */
|
||||
Exec execute(char const*const input, bool exc=true) const
|
||||
throw(std::exception) {
|
||||
return execute(std::string(input), exc);
|
||||
}
|
||||
|
||||
Creates a mrw::Exec, executes the command, passes the input
|
||||
and the flag to mrw::Exec::execute() and returns the created
|
||||
mrw::Exec. The result of the execution can be retrieved
|
||||
through the returned mrw::Exec object: The methods
|
||||
mrw::Exec::success(), mrw::Exec::result() and
|
||||
mrw::Exec::error() provide the necessary information.
|
||||
|
||||
@param input Input that is passed to @c stdin of the child process.
|
||||
|
||||
@param exc
|
||||
- @c true throw an exception if return status is not zero
|
||||
- @c false throw only an exception in case of a fatal error
|
||||
|
||||
@return the mrw::Exec that has executed the command
|
||||
|
||||
@throw ExecutionFailedExc is thrown if
|
||||
- fork fails
|
||||
- creation or setup of pipes failed
|
||||
- if given parameter is @c true (the default) also if the
|
||||
executed program terminates with an error
|
||||
*/
|
||||
Exec execute(char const*const input, bool exc=true) const
|
||||
throw(std::exception) {
|
||||
return execute(std::string(input), exc);
|
||||
}
|
||||
/** @brief Create a new mrw::PartialExec and start a new child process.
|
||||
@see PartialExec::start(bool) */
|
||||
PartialExec start(bool useInput=false) const throw(std::exception);
|
||||
|
||||
private:
|
||||
/// Exec is allowed to call @c path() and @c args().
|
||||
// Exec and PartialExec are allowed to call @c path() and @c args().
|
||||
friend class Exec;
|
||||
/// No default constructor.
|
||||
Cmd();
|
||||
friend class PartialExec;
|
||||
Cmd(); // No default constructor.
|
||||
const char* path() const throw(std::bad_exception);
|
||||
char** args() const throw(std::bad_exception);
|
||||
typedef std::list<std::string> ArgList;
|
||||
ArgList _cmd;
|
||||
};
|
||||
|
||||
//@}
|
||||
}
|
||||
#endif
|
||||
|
@@ -9,6 +9,9 @@
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.9 2005/04/19 18:48:00 marc
|
||||
new feature PartialExec
|
||||
|
||||
Revision 1.8 2005/04/07 20:55:21 marc
|
||||
Oops, there's a make distcheck...? Now it works.
|
||||
|
||||
@@ -65,12 +68,44 @@ public:
|
||||
void unexpectedExc() throw(std::bad_exception) {
|
||||
std::string res = (mrw::Cmd("/bin/false")).execute().result();
|
||||
}
|
||||
void lsTest2() {
|
||||
std::string res;
|
||||
mrw::PartialExec exec = (mrw::Cmd("/bin/ls"), "-l",
|
||||
std::string(getenv("srcdir"))+"/..").start();
|
||||
while (!exec.finished()) res+=exec.read().first;
|
||||
CPPUNIT_ASSERT(res.find("COPYING")<res.size());
|
||||
}
|
||||
void catTest2() {
|
||||
mrw::PartialExec exec = mrw::Cmd("/bin/cat").start(true);
|
||||
std::string res = exec.read("This is a test\n").first;
|
||||
res += exec.read("More to come...\n").first;
|
||||
exec.finish();
|
||||
while (!exec.finished()) res+=exec.read().first;
|
||||
CPPUNIT_ASSERT(res=="This is a test\nMore to come...\n");
|
||||
}
|
||||
void excTest12() {
|
||||
mrw::PartialExec exec = (mrw::Cmd("/bin/false")).start();
|
||||
while (!exec.finished()) exec.read();
|
||||
}
|
||||
void excTest22() {
|
||||
mrw::PartialExec exec = (mrw::Cmd("/bin/false")).start(true);
|
||||
while (!exec.finished()) exec.read("xxx");
|
||||
}
|
||||
void unexpectedExc2() throw(std::bad_exception) {
|
||||
mrw::PartialExec exec = (mrw::Cmd("/bin/false")).start();
|
||||
while (!exec.finished()) exec.read();
|
||||
}
|
||||
CPPUNIT_TEST_SUITE(ExecTest);
|
||||
CPPUNIT_TEST(lsTest);
|
||||
CPPUNIT_TEST(catTest);
|
||||
CPPUNIT_TEST_EXCEPTION(excTest1, mrw::ExecutionFailedExc);
|
||||
CPPUNIT_TEST_EXCEPTION(excTest2, mrw::ExecutionFailedExc);
|
||||
CPPUNIT_TEST_EXCEPTION(unexpectedExc, std::bad_exception);
|
||||
CPPUNIT_TEST(lsTest2);
|
||||
CPPUNIT_TEST(catTest2);
|
||||
CPPUNIT_TEST_EXCEPTION(excTest12, mrw::ExecutionFailedExc);
|
||||
CPPUNIT_TEST_EXCEPTION(excTest22, mrw::ExecutionFailedExc);
|
||||
CPPUNIT_TEST_EXCEPTION(unexpectedExc2, std::bad_exception);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(ExecTest);
|
||||
|
Reference in New Issue
Block a user