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>
|
@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)
|
||||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||||||
|
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)
|
||||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||||||
|
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)
|
||||||
throw ExecutionFailedExc("writing stdin", *_cmd);
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||||||
|
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)
|
||||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||||||
|
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)
|
||||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
536
mrw/exec.hpp
536
mrw/exec.hpp
@@ -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,18 +99,18 @@ 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&)
|
||||||
throw(std::bad_exception);
|
throw(std::bad_exception);
|
||||||
virtual ~ExecutionFailedExc() throw() {}
|
virtual ~ExecutionFailedExc() throw() {}
|
||||||
virtual const char* what() const throw() {return _what.c_str();}
|
virtual const char* what() const throw() {return _what.c_str();}
|
||||||
private:
|
private:
|
||||||
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,140 +141,295 @@ 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 {
|
||||||
public:
|
|
||||||
|
//................................................................ methods
|
||||||
|
public:
|
||||||
|
|
||||||
/** @brief Create an executor given a command.
|
/** @brief Create an executor given a command.
|
||||||
Construction without passing a command is not possible. */
|
Construction without passing a command is not possible. */
|
||||||
Exec(const mrw::Cmd&) throw(std::bad_exception);
|
Exec(const mrw::Cmd&) throw(std::bad_exception);
|
||||||
|
|
||||||
Exec(const mrw::Exec&) throw(std::bad_exception);
|
Exec(const mrw::Exec&) throw(std::bad_exception);
|
||||||
~Exec() throw();
|
~Exec() throw();
|
||||||
Exec& operator=(const mrw::Exec&) throw(std::bad_exception);
|
Exec& operator=(const mrw::Exec&) throw(std::bad_exception);
|
||||||
|
|
||||||
/** @brief Execute the command.
|
/** @brief Execute the command.
|
||||||
|
|
||||||
@param exc
|
@param exc
|
||||||
- @c true throw an exception if return status is not zero
|
- @c true throw an exception if return status is not zero
|
||||||
- @c false throw only an exception in case of a fatal error
|
- @c false throw only an exception in case of a fatal error
|
||||||
|
|
||||||
@throw ExecutionFailedExc is thrown if
|
@throw ExecutionFailedExc is thrown if
|
||||||
- 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.
|
||||||
|
|
||||||
@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
|
@param exc
|
||||||
- @c true throw an exception if return status is not zero
|
- @c true throw an exception if return status is not zero
|
||||||
- @c false throw only an exception in case of a fatal error
|
- @c false throw only an exception in case of a fatal error
|
||||||
|
|
||||||
@throw ExecutionFailedExc is thrown if
|
@throw ExecutionFailedExc is thrown if
|
||||||
- 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);
|
|
||||||
|
|
||||||
/** @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
|
@param exc
|
||||||
- @c true throw an exception if return status is not zero
|
- @c true throw an exception if return status is not zero
|
||||||
- @c false throw only an exception in case of a fatal error
|
- @c false throw only an exception in case of a fatal error
|
||||||
|
|
||||||
@throw ExecutionFailedExc is thrown if
|
@throw ExecutionFailedExc is thrown if
|
||||||
- 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
|
||||||
|
|
||||||
|
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
|
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 returned.
|
||||||
|
|
||||||
@throw ExecutionFailedExc in case of any failure or if the
|
@return @c stdout of the called program
|
||||||
executed program does not return a zero exit status.
|
|
||||||
*/
|
@throw ExecutionFailedExc in case of any failure or if the
|
||||||
Exec& operator>>(std::string&) throw(std::exception);
|
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
|
//................................................................ methods
|
||||||
first executed, then the @c stdout output of the called
|
private:
|
||||||
program is returned.
|
|
||||||
|
Exec(); // no default constructor
|
||||||
|
|
||||||
@return @c stdout of the called program
|
//.............................................................. variables
|
||||||
|
private:
|
||||||
|
|
||||||
@throw ExecutionFailedExc in case of any failure or if the
|
friend class PartialExec; // don't want the variables protected
|
||||||
executed program does not return a zero exit status.
|
mrw::Cmd* _cmd;
|
||||||
*/
|
std::string _res, _err;
|
||||||
operator std::string&() throw(std::exception);
|
bool _success;
|
||||||
|
|
||||||
/** @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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
/** @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>
|
||||||
|
|
||||||
@@ -293,115 +452,62 @@ namespace mrw {
|
|||||||
@endcode
|
@endcode
|
||||||
*/
|
*/
|
||||||
class Cmd {
|
class Cmd {
|
||||||
public:
|
public:
|
||||||
/** @brief Create a command given the name of the executable
|
/** @brief Create a command given the name of the executable
|
||||||
@param command the name of the program to execute (no parameter)
|
@param command the name of the program to execute (no parameter)
|
||||||
@note There is no default constructor. */
|
@note There is no default constructor. */
|
||||||
Cmd(const std::string& command) throw(std::bad_exception);
|
Cmd(const std::string& command) throw(std::bad_exception);
|
||||||
|
|
||||||
/** @brief Append a parameter to a command
|
/** @brief Append a parameter to a command
|
||||||
@param param a parameter / commandline argument
|
@param param a parameter / commandline argument
|
||||||
to append to the command */
|
to append to the command */
|
||||||
Cmd& operator,(const std::string& param) throw(std::bad_exception);
|
Cmd& operator,(const std::string& param) throw(std::bad_exception);
|
||||||
|
|
||||||
/** @brief Append a parameter to a command
|
/** @brief Append a parameter to a command
|
||||||
@param param a parameter / commandline argument
|
@param param a parameter / commandline argument
|
||||||
to append to the command */
|
to append to the command */
|
||||||
Cmd& operator<<(const std::string& param) throw(std::bad_exception);
|
Cmd& operator<<(const std::string& param) throw(std::bad_exception);
|
||||||
|
|
||||||
/** @return the command including parameter */
|
/** @return the command including parameter */
|
||||||
operator std::string() const throw(std::bad_exception);
|
operator std::string() const throw(std::bad_exception);
|
||||||
|
|
||||||
/** @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
|
/** @brief Create a mrw::Exec and execute a child process.
|
||||||
mrw::Exec::execute() and returns the created mrw::Exec. The
|
@see Exec::execute(bool) */
|
||||||
result of the execution can be retrieved through the returned
|
Exec execute(bool exc=true) const throw(std::exception);
|
||||||
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 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
|
Exec execute(const std::string& input, bool exc=true) const
|
||||||
and the flag to mrw::Exec::execute() and returns the created
|
throw(std::exception);
|
||||||
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 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
|
/** @brief Create a new mrw::PartialExec and start a new child process.
|
||||||
and the flag to mrw::Exec::execute() and returns the created
|
@see PartialExec::start(bool) */
|
||||||
mrw::Exec. The result of the execution can be retrieved
|
PartialExec start(bool useInput=false) const throw(std::exception);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
Reference in New Issue
Block a user