|
|
|
@ -9,6 +9,9 @@ |
|
|
|
|
@license LGPL, see file <a href="license.html">COPYING</a> |
|
|
|
|
|
|
|
|
|
$Log$ |
|
|
|
|
Revision 1.5 2004/12/14 20:30:09 marc |
|
|
|
|
added possibility to pass string to stdin of child process |
|
|
|
|
|
|
|
|
|
Revision 1.4 2004/08/28 16:21:25 marc |
|
|
|
|
mrw-c++-0.92 (mrw) |
|
|
|
|
- new file: version.cpp |
|
|
|
@ -26,9 +29,11 @@ |
|
|
|
|
#include <mrw/exec.hpp> |
|
|
|
|
#include <mrw/unistd.hpp> |
|
|
|
|
#include <mrw/exception.hpp> |
|
|
|
|
#include <mrw/stdext.hpp> // max |
|
|
|
|
#include <sys/wait.h> // waitpid |
|
|
|
|
#include <unistd.h> // fork, exec |
|
|
|
|
#include <string.h> // memcpy |
|
|
|
|
#include <assert.h> // assert |
|
|
|
|
|
|
|
|
|
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w, |
|
|
|
|
const std::string& c) |
|
|
|
@ -73,8 +78,13 @@ mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) { |
|
|
|
|
return mrw::Exec(*this); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mrw::Exec mrw::Cmd::execute(bool throwExc) const throw(std::exception) { |
|
|
|
|
return mrw::Exec(*this).execute(throwExc); |
|
|
|
|
mrw::Exec mrw::Cmd::execute(bool exc) const throw(std::exception) { |
|
|
|
|
return mrw::Exec(*this).execute(exc); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mrw::Exec mrw::Cmd::execute(const std::string& input, bool exc) const |
|
|
|
|
throw(std::exception) { |
|
|
|
|
return mrw::Exec(*this).execute(input, exc); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char* mrw::Cmd::path() const throw(std::bad_exception) { |
|
|
|
@ -109,10 +119,11 @@ mrw::Exec& mrw::Exec::operator=(const mrw::Exec& e) throw(std::bad_exception) { |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mrw::Exec& mrw::Exec::execute(bool throwExc) 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
|
|
|
|
|
stdout and @c stderr from the child process to the parent process |
|
|
|
|
using mrw::Pipe and calls @c execvp to execute the program. */ |
|
|
|
|
stdout and @c stderr from the child process to the parent |
|
|
|
|
process using mrw::Pipe and calls @c execvp to execute the |
|
|
|
|
program. */ |
|
|
|
|
_success = false; |
|
|
|
|
_res = _err = ""; |
|
|
|
|
mrw::Pipe stdOut, stdErr; |
|
|
|
@ -128,17 +139,18 @@ mrw::Exec& mrw::Exec::execute(bool throwExc) throw(std::exception) { |
|
|
|
|
throw ExecutionFailedExc("cannot close pipe", *_cmd); |
|
|
|
|
int num1(0), num2(0); |
|
|
|
|
for (char buf1[4096], buf2[4096]; |
|
|
|
|
(num1=read(stdOut.istream(), buf1, sizeof(buf1)))>0 || |
|
|
|
|
num1==-1 && errno==EINTR || |
|
|
|
|
(num2=read(stdErr.istream(), buf2, sizeof(buf2)))>0 || |
|
|
|
|
num2==-1 && errno==EINTR; |
|
|
|
|
_res += std::string(buf1, num1), _err += std::string(buf2, num2)); |
|
|
|
|
if (num1==-1 || num2==-1) |
|
|
|
|
throw ExecutionFailedExc("cannot_ read pipe", *_cmd); |
|
|
|
|
(num1=read(stdOut.istream(), buf1, sizeof(buf1)))>0 |
|
|
|
|
|| num1==-1 && (errno==EINTR||errno==EAGAIN) |
|
|
|
|
|| (num2=read(stdErr.istream(), buf2, sizeof(buf2)))>0 |
|
|
|
|
|| num2==-1 && (errno==EINTR||errno==EAGAIN);) { |
|
|
|
|
if (num1>0) _res += std::string(buf1, num1); |
|
|
|
|
if (num2>0) _err += std::string(buf2, num2); |
|
|
|
|
} |
|
|
|
|
// wait for child to get return code
|
|
|
|
|
int s(0); |
|
|
|
|
if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) { |
|
|
|
|
if (throwExc) { |
|
|
|
|
if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0 |
|
|
|
|
|| num1==-1 || num2==-1) { |
|
|
|
|
if (exc) { |
|
|
|
|
throw ExecutionFailedExc("execution failed", *_cmd); |
|
|
|
|
} else { |
|
|
|
|
_success = false; |
|
|
|
@ -157,6 +169,106 @@ mrw::Exec& mrw::Exec::execute(bool throwExc) throw(std::exception) { |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc) |
|
|
|
|
throw(std::exception) { |
|
|
|
|
/// @c input length must be smaller than @c SSIZE_MAX.
|
|
|
|
|
/// 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"); |
|
|
|
|
/** 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 |
|
|
|
|
parent process using mrw::Pipe and calls @c execvp to execute |
|
|
|
|
the program. */ |
|
|
|
|
_success = false; |
|
|
|
|
_res = _err = ""; |
|
|
|
|
mrw::Pipe stdIn(true), // child terminates if stdin is non-blocking
|
|
|
|
|
stdOut, stdErr; |
|
|
|
|
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); |
|
|
|
|
ssize_t num0(-1), num1(-1), num2(-1); |
|
|
|
|
std::string in(input); |
|
|
|
|
fd_set writefds, readfds; |
|
|
|
|
char buf[4096]; |
|
|
|
|
while (num0 || num1 || num2) try { // no end of file in stdin, -out, -err
|
|
|
|
|
int res(0), s(0); |
|
|
|
|
if ((res=waitpid(pid, &s, WNOHANG))) { |
|
|
|
|
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) |
|
|
|
|
throw ExecutionFailedExc("execution failed", *_cmd); |
|
|
|
|
_success = true; |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
// select...
|
|
|
|
|
FD_ZERO(&writefds); |
|
|
|
|
FD_ZERO(&readfds); |
|
|
|
|
if (num0) FD_SET(stdIn.ostream(), &writefds); |
|
|
|
|
if (num1) FD_SET(stdOut.istream(), &readfds); |
|
|
|
|
if (num2) FD_SET(stdErr.istream(), &readfds); |
|
|
|
|
timeval tm = {1, 0}; |
|
|
|
|
int n(mrw::max((num0?stdIn.ostream():0), |
|
|
|
|
mrw::max((num1?stdOut.istream():0), |
|
|
|
|
(num2?stdErr.istream():0)))); |
|
|
|
|
int num = select(n, &readfds, &writefds, 0, &tm); |
|
|
|
|
if (num<0) throw ExecutionFailedExc("select failed", *_cmd); |
|
|
|
|
// check and handle stdin
|
|
|
|
|
if (num0 && num>0 |
|
|
|
|
&& FD_ISSET(stdIn.ostream(), &writefds)) |
|
|
|
|
if ((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(); |
|
|
|
|
} else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN)) |
|
|
|
|
throw ExecutionFailedExc("writing stdin", *_cmd); |
|
|
|
|
// check and handle stdout
|
|
|
|
|
if (num>0 && FD_ISSET(stdOut.istream(), &readfds)) |
|
|
|
|
if ((num1=read(stdOut.istream(), buf, sizeof(buf)))>0) |
|
|
|
|
_res += std::string(buf, num1); |
|
|
|
|
else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN)) |
|
|
|
|
throw ExecutionFailedExc("readin stdout", *_cmd); |
|
|
|
|
// check and handle stderr
|
|
|
|
|
if (num>0 && FD_ISSET(stdErr.istream(), &readfds)) |
|
|
|
|
if ((num2=read(stdErr.istream(), buf, sizeof(buf)))>0) |
|
|
|
|
_err += std::string(buf, num2); |
|
|
|
|
else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN)) |
|
|
|
|
throw ExecutionFailedExc("readin stderr", *_cmd); |
|
|
|
|
} catch (...) { |
|
|
|
|
if (exc) throw; |
|
|
|
|
_success = false; |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
// wait for child to get return code
|
|
|
|
|
int s(0); |
|
|
|
|
if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) { |
|
|
|
|
if (exc) { |
|
|
|
|
throw ExecutionFailedExc("execution failed", *_cmd); |
|
|
|
|
} else { |
|
|
|
|
_success = false; |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { // child
|
|
|
|
|
stdIn.close_out(); |
|
|
|
|
stdOut.close_in(); |
|
|
|
|
stdErr.close_in(); |
|
|
|
|
stdIn.connect_cin(); |
|
|
|
|
stdOut.connect_cout(); // if cin is non blocking, child terminates here?!?
|
|
|
|
|
stdErr.connect_cerr(); |
|
|
|
|
execvp(_cmd->path(), _cmd->args()); |
|
|
|
|
exit(1); // execute failed
|
|
|
|
|
} |
|
|
|
|
_success = true; |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) { |
|
|
|
|
execute(); |
|
|
|
|
res += _res; |
|
|
|
|