new feature PartialExec

master
Marc Wäckerlin 20 years ago
parent 4f856c3281
commit f2163f019c
  1. 256
      mrw/exec.cpp
  2. 294
      mrw/exec.hpp
  3. 35
      mrw/exec_test.cpp

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $Log$
Revision 1.11 2005/04/19 18:48:00 marc
new feature PartialExec
Revision 1.10 2005/03/14 16:26:34 marc Revision 1.10 2005/03/14 16:26:34 marc
bugs have been fixed a long time ago, now no more in buglist bugs have been fixed a long time ago, now no more in buglist
@ -48,7 +51,12 @@
#include <unistd.h> // fork, exec #include <unistd.h> // fork, exec
#include <string.h> // memcpy #include <string.h> // memcpy
#include <assert.h> // assert #include <assert.h> // assert
#include <sys/types.h> // kill
#include <signal.h> // kill
//=========================================================== ExecutionFailedExc
//------------------------------------------------------------------------------
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w, mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
const std::string& c) const std::string& c)
throw(std::bad_exception): 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) { mrw::Cmd::Cmd(const std::string& c) throw(std::bad_exception) {
_cmd.push_back(c); _cmd.push_back(c);
} }
//------------------------------------------------------------------------------
mrw::Cmd& mrw::Cmd::operator,(const std::string& arg) mrw::Cmd& mrw::Cmd::operator,(const std::string& arg)
throw(std::bad_exception) { throw(std::bad_exception) {
_cmd.push_back(arg); _cmd.push_back(arg);
return *this; return *this;
} }
//------------------------------------------------------------------------------
mrw::Cmd& mrw::Cmd::operator<<(const std::string& arg) mrw::Cmd& mrw::Cmd::operator<<(const std::string& arg)
throw(std::bad_exception) { throw(std::bad_exception) {
_cmd.push_back(arg); _cmd.push_back(arg);
return *this; return *this;
} }
//------------------------------------------------------------------------------
mrw::Cmd::operator std::string() const throw(std::bad_exception) { mrw::Cmd::operator std::string() const throw(std::bad_exception) {
ArgList::const_iterator it(_cmd.begin()); ArgList::const_iterator it(_cmd.begin());
std::string c(*it); std::string c(*it);
@ -88,22 +102,38 @@ mrw::Cmd::operator std::string() const throw(std::bad_exception) {
return c; return c;
} }
//------------------------------------------------------------------------------
mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) { mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) {
return mrw::Exec(*this); 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) { mrw::Exec mrw::Cmd::execute(bool exc) const throw(std::exception) {
return mrw::Exec(*this).execute(exc); return mrw::Exec(*this).execute(exc);
} }
//------------------------------------------------------------------------------
mrw::Exec mrw::Cmd::execute(const std::string& input, bool exc) const mrw::Exec mrw::Cmd::execute(const std::string& input, bool exc) const
throw(std::exception) { throw(std::exception) {
return mrw::Exec(*this).execute(input, exc); 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) { const char* mrw::Cmd::path() const throw(std::bad_exception) {
return _cmd.front().c_str(); return _cmd.front().c_str();
} }
//------------------------------------------------------------------------------
char** mrw::Cmd::args() const throw(std::bad_exception) { char** mrw::Cmd::args() const throw(std::bad_exception) {
if (_cmd.size()==0) return 0; if (_cmd.size()==0) return 0;
char** array = new char*[_cmd.size()+1]; char** array = new char*[_cmd.size()+1];
@ -114,25 +144,32 @@ char** mrw::Cmd::args() const throw(std::bad_exception) {
return array; return array;
} }
//========================================================================= Exec
//------------------------------------------------------------------------------
mrw::Exec::Exec(const mrw::Cmd& c) throw(std::bad_exception): mrw::Exec::Exec(const mrw::Cmd& c) throw(std::bad_exception):
_cmd(new mrw::Cmd(c)), _success(false) { _cmd(new mrw::Cmd(c)), _success(false) {
} }
//------------------------------------------------------------------------------
mrw::Exec::Exec(const mrw::Exec& e) throw(std::bad_exception): mrw::Exec::Exec(const mrw::Exec& e) throw(std::bad_exception):
_cmd(new mrw::Cmd(*e._cmd)), _cmd(new mrw::Cmd(*e._cmd)),
_res(e._res), _err(e._err), _success(e._success) { _res(e._res), _err(e._err), _success(e._success) {
} }
//------------------------------------------------------------------------------
mrw::Exec::~Exec() throw() { mrw::Exec::~Exec() throw() {
delete _cmd; delete _cmd;
} }
//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::operator=(const mrw::Exec& e) throw(std::bad_exception) { mrw::Exec& mrw::Exec::operator=(const mrw::Exec& e) throw(std::bad_exception) {
if (this==&e) return *this; if (this==&e) return *this;
*_cmd=*e._cmd; _res=e._res; _err=e._err; _success=e._success; *_cmd=*e._cmd; _res=e._res; _err=e._err; _success=e._success;
return *this; return *this;
} }
//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) { mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
/** This method calls @c fork, sets up a pipe connection to pass @c /** This method calls @c fork, sets up a pipe connection to pass @c
stdout and @c stderr from the child process to the parent 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 // check and handle stdout
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0) if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
_res += std::string(buf, num1); _res += std::string(buf, num1);
else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN)) else if (num1==-1)
if (errno!=EINTR&&errno!=EAGAIN)
throw ExecutionFailedExc("readin stdout", *_cmd); throw ExecutionFailedExc("readin stdout", *_cmd);
else
num1 = 1;
// check and handle stderr // check and handle stderr
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0) if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
_err += std::string(buf, num2); _err += std::string(buf, num2);
else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN)) else if (num2==-1)
if (errno!=EINTR&&errno!=EAGAIN)
throw ExecutionFailedExc("readin stderr", *_cmd); throw ExecutionFailedExc("readin stderr", *_cmd);
else
num2 = 1;
} catch (...) { } catch (...) {
if (exc) throw;
_success = false; _success = false;
if (exc) throw;
return *this; return *this;
} }
} else { // child } else { // child
@ -191,6 +234,7 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
return *this; return *this;
} }
//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc) mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
throw(std::exception) { throw(std::exception) {
/// @c input length must be smaller than @c SSIZE_MAX. /// @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 " "sdin input exeeds C library limit in mrw::Exec "
"please contact the author of the library"); "please contact the author of the library");
/** This method calls @c fork, sets up a pipe connection to pass @c /** 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 parent process using mrw::Pipe and calls @c execvp to execute
the program. */ the program. */
_success = false; _success = false;
@ -236,21 +280,30 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
in = in.substr(num0); in = in.substr(num0);
else if ((unsigned int)num0==in.size()) else if ((unsigned int)num0==in.size())
num0=0, stdIn.close_out(); num0=0, stdIn.close_out();
} else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN)) } else if (num0==-1)
if (errno!=EINTR&&errno!=EAGAIN)
throw ExecutionFailedExc("writing stdin", *_cmd); throw ExecutionFailedExc("writing stdin", *_cmd);
else
num0 = 1;
// check and handle stdout // check and handle stdout
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0) if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
_res += std::string(buf, num1); _res += std::string(buf, num1);
else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN)) else if (num1==-1)
if (errno!=EINTR&&errno!=EAGAIN)
throw ExecutionFailedExc("readin stdout", *_cmd); throw ExecutionFailedExc("readin stdout", *_cmd);
else
num1 = 1;
// check and handle stderr // check and handle stderr
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0) if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
_err += std::string(buf, num2); _err += std::string(buf, num2);
else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN)) else if (num2==-1)
if (errno!=EINTR&&errno!=EAGAIN)
throw ExecutionFailedExc("readin stderr", *_cmd); throw ExecutionFailedExc("readin stderr", *_cmd);
else
num2 = 1;
} catch (...) { } catch (...) {
if (exc) throw;
_success = false; _success = false;
if (exc) throw;
return *this; return *this;
} }
} else { // child } else { // child
@ -267,31 +320,218 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
return *this; return *this;
} }
//------------------------------------------------------------------------------
mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) { mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) {
execute(); execute();
res += _res; res += _res;
return *this; return *this;
} }
//------------------------------------------------------------------------------
mrw::Exec::operator std::string&() throw(std::exception) { mrw::Exec::operator std::string&() throw(std::exception) {
if (!_success) execute(); if (!_success) execute();
return _res; return _res;
} }
//------------------------------------------------------------------------------
mrw::Exec::operator bool() throw(std::bad_exception) { mrw::Exec::operator bool() throw(std::bad_exception) {
return _success; return _success;
} }
//------------------------------------------------------------------------------
std::string& mrw::Exec::result() throw(std::exception) { std::string& mrw::Exec::result() throw(std::exception) {
if (!_success) execute(); if (!_success) execute();
return _res; return _res;
} }
//------------------------------------------------------------------------------
std::string& mrw::Exec::error() throw(std::exception) { std::string& mrw::Exec::error() throw(std::exception) {
if (!_success) execute(); if (!_success) execute();
return _err; return _err;
} }
//------------------------------------------------------------------------------
bool mrw::Exec::success() throw(std::bad_exception) { bool mrw::Exec::success() throw(std::bad_exception) {
return _success; 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;
}

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $Log$
Revision 1.6 2005/04/19 18:48:00 marc
new feature PartialExec
Revision 1.5 2004/12/14 20:30:09 marc Revision 1.5 2004/12/14 20:30:09 marc
added possibility to pass string to stdin of child process added possibility to pass string to stdin of child process
@ -35,6 +38,7 @@
#include <string> #include <string>
#include <list> #include <list>
#include <mrw/exception.hpp> #include <mrw/exception.hpp>
#include <mrw/unistd.hpp>
namespace mrw { namespace mrw {
@ -81,12 +85,12 @@ namespace mrw {
// "cat" passes all from stdin to stdout, therefore: // "cat" passes all from stdin to stdout, therefore:
assert(cat.result()=="this is passed to stdin"); assert(cat.result()=="this is passed to stdin");
} catch (...) {} // ignore } catch (...) {} // ignore
@endcode @endcode */
*/
//@{ //@{
class Cmd; class Cmd;
//============================================================================
/** @brief Exception: Execution of command failed. /** @brief Exception: Execution of command failed.
@pre #include <mrw/exec.hpp> @pre #include <mrw/exec.hpp>
@ -95,8 +99,7 @@ namespace mrw {
to create the necessary pipes, or the command executing process to create the necessary pipes, or the command executing process
terminated with an error. In the last case, you can access the terminated with an error. In the last case, you can access the
error stream from @c stderr respectively @c cerr with method error stream from @c stderr respectively @c cerr with method
mrw::Exec::error(). mrw::Exec::error(). */
*/
class ExecutionFailedExc: public mrw::exception { class ExecutionFailedExc: public mrw::exception {
public: public:
ExecutionFailedExc(const std::string&, const std::string&) ExecutionFailedExc(const std::string&, const std::string&)
@ -107,6 +110,7 @@ namespace mrw {
std::string _what; std::string _what;
}; };
//============================================================================
/** @brief Execute a command in a new process. /** @brief Execute a command in a new process.
@pre #include <mrw/exec.hpp> @pre #include <mrw/exec.hpp>
@ -137,9 +141,10 @@ namespace mrw {
// ls.error() contains stderr // ls.error() contains stderr
@endcode @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 { class Exec {
//................................................................ methods
public: public:
/** @brief Create an executor given a command. /** @brief Create an executor given a command.
@ -160,8 +165,7 @@ namespace mrw {
- fork fails - fork fails
- creation or setup of pipes failed - creation or setup of pipes failed
- if given parameter is @c true (the default) also if the - if given parameter is @c true (the default) also if the
executed program terminates with an error executed program terminates with an error */
*/
Exec& execute(bool exc=true) throw(std::exception); Exec& execute(bool exc=true) throw(std::exception);
/** @brief Execute the command, pass @c stdin. /** @brief Execute the command, pass @c stdin.
@ -176,8 +180,7 @@ namespace mrw {
- fork fails - fork fails
- creation or setup of pipes failed - creation or setup of pipes failed
- if given parameter is @c true (the default) also if the - if given parameter is @c true (the default) also if the
executed program terminates with an error executed program terminates with an error */
*/
Exec& execute(const std::string& input, bool exc=true) Exec& execute(const std::string& input, bool exc=true)
throw(std::exception); throw(std::exception);
@ -193,22 +196,21 @@ namespace mrw {
- fork fails - fork fails
- creation or setup of pipes failed - creation or setup of pipes failed
- if given parameter is @c true (the default) also if the - if given parameter is @c true (the default) also if the
executed program terminates with an error executed program terminates with an error */
*/
Exec& execute(char const*const input, bool exc=true) Exec& execute(char const*const input, bool exc=true)
throw(std::exception) { throw(std::exception) {
return execute(std::string(input), exc); return execute(std::string(input), exc);
} }
/** @brief Executes the command if not done, streams @c stdout into a string /** @brief Executes the command if not done, streams @c stdout
into a string
If the command has not yet been executed successfully, it is If the command has not yet been executed successfully, it is
first executed, then the @c stdout output of the called first executed, then the @c stdout output of the called
program is appended to the string. program is appended to the string.
@throw ExecutionFailedExc in case of any failure or if the @throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status. executed program does not return a zero exit status. */
*/
Exec& operator>>(std::string&) throw(std::exception); Exec& operator>>(std::string&) throw(std::exception);
/** @brief Executes the command if not done, returns @c stdout as string /** @brief Executes the command if not done, returns @c stdout as string
@ -220,15 +222,13 @@ namespace mrw {
@return @c stdout of the called program @return @c stdout of the called program
@throw ExecutionFailedExc in case of any failure or if the @throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status. executed program does not return a zero exit status. */
*/
operator std::string&() throw(std::exception); operator std::string&() throw(std::exception);
/** @return /** @return
- @c true if the last execution was successful - @c true if the last execution was successful
- @c false if the last execution failed or the command was - @c false if the last execution failed or the command was
never executed never executed */
*/
operator bool() throw(std::bad_exception); operator bool() throw(std::bad_exception);
/** @brief Executes the command if not done, returns @c stdout as string /** @brief Executes the command if not done, returns @c stdout as string
@ -240,8 +240,7 @@ namespace mrw {
@return @c stdout of the called program @return @c stdout of the called program
@throw ExecutionFailedExc in case of any failure or if the @throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status. executed program does not return a zero exit status. */
*/
std::string& result() throw(std::exception); std::string& result() throw(std::exception);
/** @brief Executes the command if not done, returns @c stderr as string /** @brief Executes the command if not done, returns @c stderr as string
@ -253,24 +252,184 @@ namespace mrw {
@return @c stderr of the called program @return @c stderr of the called program
@throw ExecutionFailedExc in case of any failure or if the @throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status. executed program does not return a zero exit status. */
*/
std::string& error() throw(std::exception); std::string& error() throw(std::exception);
/** @return /** @return
- @c true if the last execution was successful - @c true if the last execution was successful
- @c false if the last execution failed or the command was - @c false if the last execution failed or the command was
never executed never executed */
*/
bool success() throw(std::bad_exception); bool success() throw(std::bad_exception);
//................................................................ methods
private: private:
Exec(); // no default constructor Exec(); // no default constructor
//.............................................................. variables
private:
friend class PartialExec; // don't want the variables protected
mrw::Cmd* _cmd; mrw::Cmd* _cmd;
std::string _res, _err; std::string _res, _err;
bool _success; 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 /** @brief A system command to be executed
@pre #include <mrw/exec.hpp> @pre #include <mrw/exec.hpp>
@ -315,93 +474,40 @@ namespace mrw {
/** @return a mrw::Exec that's constructed with this class */ /** @return a mrw::Exec that's constructed with this class */
operator mrw::Exec() const throw(std::bad_exception); 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 /** @brief Create a mrw::Exec and execute a child process.
- fork fails @see Exec::execute(bool) */
- 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); Exec execute(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(const std::string&, bool) */
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 Exec execute(const std::string& input, bool exc=true) const
throw(std::exception); 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) */
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 Exec execute(char const*const input, bool exc=true) const
throw(std::exception) { throw(std::exception) {
return execute(std::string(input), exc); 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: 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; friend class Exec;
/// No default constructor. friend class PartialExec;
Cmd(); Cmd(); // No default constructor.
const char* path() const throw(std::bad_exception); const char* path() const throw(std::bad_exception);
char** args() const throw(std::bad_exception); char** args() const throw(std::bad_exception);
typedef std::list<std::string> ArgList; typedef std::list<std::string> ArgList;
ArgList _cmd; ArgList _cmd;
}; };
//@} //@}
} }
#endif #endif

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $Log$
Revision 1.9 2005/04/19 18:48:00 marc
new feature PartialExec
Revision 1.8 2005/04/07 20:55:21 marc Revision 1.8 2005/04/07 20:55:21 marc
Oops, there's a make distcheck...? Now it works. Oops, there's a make distcheck...? Now it works.
@ -65,12 +68,44 @@ public:
void unexpectedExc() throw(std::bad_exception) { void unexpectedExc() throw(std::bad_exception) {
std::string res = (mrw::Cmd("/bin/false")).execute().result(); 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_SUITE(ExecTest);
CPPUNIT_TEST(lsTest); CPPUNIT_TEST(lsTest);
CPPUNIT_TEST(catTest); CPPUNIT_TEST(catTest);
CPPUNIT_TEST_EXCEPTION(excTest1, mrw::ExecutionFailedExc); CPPUNIT_TEST_EXCEPTION(excTest1, mrw::ExecutionFailedExc);
CPPUNIT_TEST_EXCEPTION(excTest2, mrw::ExecutionFailedExc); CPPUNIT_TEST_EXCEPTION(excTest2, mrw::ExecutionFailedExc);
CPPUNIT_TEST_EXCEPTION(unexpectedExc, std::bad_exception); 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_END();
}; };
CPPUNIT_TEST_SUITE_REGISTRATION(ExecTest); CPPUNIT_TEST_SUITE_REGISTRATION(ExecTest);

Loading…
Cancel
Save