C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
558 lines
20 KiB
558 lines
20 KiB
20 years ago
|
/** @file
|
||
|
|
||
|
$Id$
|
||
|
|
||
|
$Date$
|
||
|
$Author$
|
||
|
|
||
|
@copy © Marc Wäckerlin
|
||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||
|
|
||
|
$Log$
|
||
19 years ago
|
Revision 1.14 2005/11/29 12:39:42 marc
|
||
|
make it compilable with gcc 4.0.2 and newer doxygen
|
||
|
|
||
20 years ago
|
Revision 1.13 2005/04/20 18:32:14 marc
|
||
|
*** empty log message ***
|
||
|
|
||
20 years ago
|
Revision 1.12 2005/04/20 18:12:55 marc
|
||
|
added kill() for PartialExec
|
||
|
|
||
20 years ago
|
Revision 1.11 2005/04/19 18:48:00 marc
|
||
|
new feature PartialExec
|
||
|
|
||
20 years ago
|
Revision 1.10 2005/03/14 16:26:34 marc
|
||
|
bugs have been fixed a long time ago, now no more in buglist
|
||
|
|
||
20 years ago
|
Revision 1.9 2004/12/20 07:40:35 marc
|
||
|
documentation improved, new grouping
|
||
|
|
||
20 years ago
|
Revision 1.8 2004/12/18 21:00:09 marc
|
||
|
everything is ok, when pipes are non blocking on parent's side and blocking on client's side, and select is not used (not necessary for non blocking IO)
|
||
|
|
||
20 years ago
|
Revision 1.7 2004/12/17 17:38:57 marc
|
||
|
it works perfectly with blocking pipes and buffer size of 1
|
||
|
|
||
20 years ago
|
Revision 1.6 2004/12/17 16:28:51 marc
|
||
|
stable implementation with select for both executes
|
||
|
|
||
20 years ago
|
Revision 1.5 2004/12/14 20:30:09 marc
|
||
|
added possibility to pass string to stdin of child process
|
||
|
|
||
20 years ago
|
Revision 1.4 2004/08/28 16:21:25 marc
|
||
|
mrw-c++-0.92 (mrw)
|
||
13 years ago
|
- new file: version.cxx
|
||
20 years ago
|
- new file header for all sources
|
||
|
- work around warning in mrw::auto<T>
|
||
|
- possibility to compile without log4cxx
|
||
|
- work around bugs in demangle.h and libiberty.h
|
||
|
- corrections in documentation
|
||
|
- added simple tracing mechanism
|
||
|
- more warnings
|
||
|
- small corrections in Auto<>::Free and a new test for it
|
||
|
- possibility to compile without stack trace
|
||
|
|
||
|
*/
|
||
13 years ago
|
#include <mrw/exec.hxx>
|
||
|
#include <mrw/unistd.hxx>
|
||
|
#include <mrw/exception.hxx>
|
||
20 years ago
|
#include <sys/wait.h> // waitpid
|
||
|
#include <unistd.h> // fork, exec
|
||
20 years ago
|
#include <string.h> // memcpy
|
||
20 years ago
|
#include <assert.h> // assert
|
||
20 years ago
|
#include <sys/types.h> // kill
|
||
|
#include <signal.h> // kill
|
||
13 years ago
|
#include <cstdlib> // exit
|
||
20 years ago
|
|
||
|
//=========================================================== ExecutionFailedExc
|
||
21 years ago
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
|
||
|
const std::string& c)
|
||
|
throw(std::bad_exception):
|
||
|
_what(std::string("mrw::Exec: command execution failed\n")+
|
||
|
std::string(" failed command was: \""+c+"\"\n")+
|
||
|
std::string(" error was: \"")+w+'"') {
|
||
|
/**
|
||
|
@c what looks like:
|
||
|
@verbatim
|
||
|
mrw::Exec: command execution failed
|
||
|
failed command was: "/bin/OOOOPS -v -q --crash"
|
||
|
error was: "execution failed"
|
||
|
@endverbatim
|
||
|
*/
|
||
|
}
|
||
|
|
||
20 years ago
|
//========================================================================== Cmd
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Cmd::Cmd(const std::string& c) throw(std::bad_exception) {
|
||
|
_cmd.push_back(c);
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Cmd& mrw::Cmd::operator,(const std::string& arg)
|
||
|
throw(std::bad_exception) {
|
||
|
_cmd.push_back(arg);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Cmd& mrw::Cmd::operator<<(const std::string& arg)
|
||
|
throw(std::bad_exception) {
|
||
|
_cmd.push_back(arg);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Cmd::operator std::string() const throw(std::bad_exception) {
|
||
|
ArgList::const_iterator it(_cmd.begin());
|
||
|
std::string c(*it);
|
||
|
while (++it!=_cmd.end()) c+=' '+*it;
|
||
|
return c;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) {
|
||
|
return mrw::Exec(*this);
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
|
mrw::Cmd::operator mrw::PartialExec() const throw(std::bad_exception) {
|
||
|
return mrw::PartialExec(*this);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
mrw::Exec mrw::Cmd::execute(bool exc) const throw(std::exception) {
|
||
|
return mrw::Exec(*this).execute(exc);
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
mrw::Exec mrw::Cmd::execute(const std::string& input, bool exc) const
|
||
|
throw(std::exception) {
|
||
|
return mrw::Exec(*this).execute(input, exc);
|
||
21 years ago
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
|
mrw::PartialExec mrw::Cmd::start(bool useInput) const throw(std::exception) {
|
||
|
return mrw::PartialExec(*this).start(useInput);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
const char* mrw::Cmd::path() const throw(std::bad_exception) {
|
||
|
return _cmd.front().c_str();
|
||
|
}
|
||
20 years ago
|
|
||
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
char** mrw::Cmd::args() const throw(std::bad_exception) {
|
||
|
if (_cmd.size()==0) return 0;
|
||
|
char** array = new char*[_cmd.size()+1];
|
||
|
int i(0);
|
||
|
for (ArgList::const_iterator it(_cmd.begin()); it!=_cmd.end(); ++it)
|
||
|
memcpy(array[i++]=new char[it->size()+1], it->c_str(), it->size()+1);
|
||
|
array[i] = 0;
|
||
|
return array;
|
||
|
}
|
||
|
|
||
20 years ago
|
//========================================================================= Exec
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Exec::Exec(const mrw::Cmd& c) throw(std::bad_exception):
|
||
|
_cmd(new mrw::Cmd(c)), _success(false) {
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Exec::Exec(const mrw::Exec& e) throw(std::bad_exception):
|
||
|
_cmd(new mrw::Cmd(*e._cmd)),
|
||
|
_res(e._res), _err(e._err), _success(e._success) {
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Exec::~Exec() throw() {
|
||
|
delete _cmd;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Exec& mrw::Exec::operator=(const mrw::Exec& e) throw(std::bad_exception) {
|
||
|
if (this==&e) return *this;
|
||
|
*_cmd=*e._cmd; _res=e._res; _err=e._err; _success=e._success;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
|
||
21 years ago
|
/** This method calls @c fork, sets up a pipe connection to pass @c
|
||
20 years ago
|
stdout and @c stderr from the child process to the parent
|
||
|
process using mrw::Pipe and calls @c execvp to execute the
|
||
|
program. */
|
||
21 years ago
|
_success = false;
|
||
|
_res = _err = "";
|
||
20 years ago
|
mrw::Pipe stdOut(mrw::Pipe::block_output), stdErr(mrw::Pipe::block_output);
|
||
21 years ago
|
if (!stdOut || !stdErr)
|
||
21 years ago
|
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
|
||
|
pid_t pid(fork());
|
||
|
if (pid<0)
|
||
|
throw ExecutionFailedExc("cannot fork", *_cmd);
|
||
|
if (pid) { // parent
|
||
21 years ago
|
stdOut.close_out();
|
||
|
stdErr.close_out();
|
||
|
if (!stdOut || !stdErr)
|
||
21 years ago
|
throw ExecutionFailedExc("cannot close pipe", *_cmd);
|
||
20 years ago
|
ssize_t num1(1), num2(1);
|
||
|
char buf[4096];
|
||
20 years ago
|
int res(0), s(0);
|
||
20 years ago
|
/* 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. */
|
||
20 years ago
|
while (num1||num2||!res) try { // not end of files or child terminated
|
||
20 years ago
|
if (!res && (res=waitpid(pid, &s, WNOHANG)))
|
||
|
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
|
||
|
throw ExecutionFailedExc("execution failed", *_cmd);
|
||
20 years ago
|
if (num1<=0&&num2<=0) usleep(10000); // nothing to read last time
|
||
20 years ago
|
// check and handle stdout
|
||
20 years ago
|
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
|
||
|
_res += std::string(buf, num1);
|
||
20 years ago
|
else if (num1==-1)
|
||
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||
|
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||
|
else
|
||
|
num1 = 1;
|
||
20 years ago
|
// check and handle stderr
|
||
20 years ago
|
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
|
||
|
_err += std::string(buf, num2);
|
||
20 years ago
|
else if (num2==-1)
|
||
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||
|
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||
|
else
|
||
|
num2 = 1;
|
||
20 years ago
|
} catch (...) {
|
||
|
_success = false;
|
||
20 years ago
|
if (exc) throw;
|
||
20 years ago
|
return *this;
|
||
21 years ago
|
}
|
||
|
} else { // child
|
||
21 years ago
|
stdOut.close_in();
|
||
|
stdErr.close_in();
|
||
|
stdOut.connect_cout();
|
||
|
stdErr.connect_cerr();
|
||
21 years ago
|
execvp(_cmd->path(), _cmd->args());
|
||
|
exit(1); // execute failed
|
||
|
}
|
||
|
_success = true;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
||
|
throw(std::exception) {
|
||
13 years ago
|
#ifdef SSIZE_MAX
|
||
20 years ago
|
/// @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");
|
||
13 years ago
|
#endif
|
||
20 years ago
|
/** This method calls @c fork, sets up a pipe connection to pass @c
|
||
20 years ago
|
stdin, @c stdout and @c stderr between the child process and the
|
||
20 years ago
|
parent process using mrw::Pipe and calls @c execvp to execute
|
||
|
the program. */
|
||
|
_success = false;
|
||
|
_res = _err = "";
|
||
20 years ago
|
mrw::Pipe stdIn(mrw::Pipe::block_input),
|
||
|
stdOut(mrw::Pipe::block_output), stdErr(mrw::Pipe::block_output);
|
||
20 years ago
|
if (!stdIn || !stdOut || !stdErr)
|
||
|
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
|
||
|
pid_t 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);
|
||
20 years ago
|
ssize_t num0(1), num1(1), num2(1);
|
||
20 years ago
|
std::string in(input);
|
||
20 years ago
|
char buf[4096];
|
||
20 years ago
|
int res(0), s(0);
|
||
20 years ago
|
/* 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. */
|
||
20 years ago
|
while (num0||num1||num2||!res) try { // not end of files or child terminated
|
||
20 years ago
|
if (!res && (res=waitpid(pid, &s, WNOHANG)))
|
||
20 years ago
|
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
|
||
|
throw ExecutionFailedExc("execution failed", *_cmd);
|
||
20 years ago
|
if (num0<=0&&num1<=0&&num2<=0) usleep(10000); // no activity last time
|
||
20 years ago
|
// check and handle stdin
|
||
20 years ago
|
if (num0 && (num0=write(stdIn.ostream(), in.c_str(), in.size()))>0) {
|
||
|
if ((unsigned int)num0<in.size())
|
||
|
in = in.substr(num0);
|
||
|
else if ((unsigned int)num0==in.size())
|
||
|
num0=0, stdIn.close_out();
|
||
20 years ago
|
} else if (num0==-1)
|
||
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||
|
throw ExecutionFailedExc("writing stdin", *_cmd);
|
||
|
else
|
||
|
num0 = 1;
|
||
20 years ago
|
// check and handle stdout
|
||
20 years ago
|
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
|
||
|
_res += std::string(buf, num1);
|
||
20 years ago
|
else if (num1==-1)
|
||
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||
|
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||
|
else
|
||
|
num1 = 1;
|
||
20 years ago
|
// check and handle stderr
|
||
20 years ago
|
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
|
||
|
_err += std::string(buf, num2);
|
||
20 years ago
|
else if (num2==-1)
|
||
|
if (errno!=EINTR&&errno!=EAGAIN)
|
||
|
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||
|
else
|
||
|
num2 = 1;
|
||
20 years ago
|
} catch (...) {
|
||
|
_success = false;
|
||
20 years ago
|
if (exc) throw;
|
||
20 years ago
|
return *this;
|
||
|
}
|
||
|
} else { // child
|
||
|
stdIn.close_out();
|
||
|
stdOut.close_in();
|
||
|
stdErr.close_in();
|
||
|
stdIn.connect_cin();
|
||
20 years ago
|
stdOut.connect_cout();
|
||
20 years ago
|
stdErr.connect_cerr();
|
||
|
execvp(_cmd->path(), _cmd->args());
|
||
|
exit(1); // execute failed
|
||
|
}
|
||
|
_success = true;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) {
|
||
21 years ago
|
execute();
|
||
|
res += _res;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
mrw::Exec::operator std::string&() throw(std::exception) {
|
||
21 years ago
|
if (!_success) execute();
|
||
|
return _res;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
mrw::Exec::operator bool() throw(std::bad_exception) {
|
||
|
return _success;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
std::string& mrw::Exec::result() throw(std::exception) {
|
||
21 years ago
|
if (!_success) execute();
|
||
|
return _res;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
20 years ago
|
std::string& mrw::Exec::error() throw(std::exception) {
|
||
21 years ago
|
if (!_success) execute();
|
||
|
return _err;
|
||
|
}
|
||
|
|
||
20 years ago
|
//------------------------------------------------------------------------------
|
||
21 years ago
|
bool mrw::Exec::success() throw(std::bad_exception) {
|
||
|
return _success;
|
||
|
}
|
||
20 years ago
|
|
||
|
//================================================================== 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) {
|
||
19 years ago
|
/** @warning @c const is casted away */
|
||
20 years ago
|
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;
|
||
13 years ago
|
#ifdef SSIZE_MAX
|
||
20 years ago
|
/** @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");
|
||
13 years ago
|
#endif
|
||
20 years ago
|
/** @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() {
|
||
20 years ago
|
::kill(_pid, SIGTERM);
|
||
20 years ago
|
return *this;
|
||
|
}
|
||
20 years ago
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
mrw::PartialExec& mrw::PartialExec::kill() throw() {
|
||
20 years ago
|
::kill(_pid, SIGKILL);
|
||
20 years ago
|
return *this;
|
||
|
}
|