added possibility to pass string to stdin of child process
This commit is contained in:
140
mrw/exec.cpp
140
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.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
|
Revision 1.4 2004/08/28 16:21:25 marc
|
||||||
mrw-c++-0.92 (mrw)
|
mrw-c++-0.92 (mrw)
|
||||||
- new file: version.cpp
|
- new file: version.cpp
|
||||||
@@ -26,9 +29,11 @@
|
|||||||
#include <mrw/exec.hpp>
|
#include <mrw/exec.hpp>
|
||||||
#include <mrw/unistd.hpp>
|
#include <mrw/unistd.hpp>
|
||||||
#include <mrw/exception.hpp>
|
#include <mrw/exception.hpp>
|
||||||
|
#include <mrw/stdext.hpp> // max
|
||||||
#include <sys/wait.h> // waitpid
|
#include <sys/wait.h> // waitpid
|
||||||
#include <unistd.h> // fork, exec
|
#include <unistd.h> // fork, exec
|
||||||
#include <string.h> // memcpy
|
#include <string.h> // memcpy
|
||||||
|
#include <assert.h> // assert
|
||||||
|
|
||||||
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
|
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
|
||||||
const std::string& c)
|
const std::string& c)
|
||||||
@@ -73,8 +78,13 @@ mrw::Cmd::operator mrw::Exec() const throw(std::bad_exception) {
|
|||||||
return mrw::Exec(*this);
|
return mrw::Exec(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
mrw::Exec mrw::Cmd::execute(bool throwExc) const throw(std::exception) {
|
mrw::Exec mrw::Cmd::execute(bool exc) const throw(std::exception) {
|
||||||
return mrw::Exec(*this).execute(throwExc);
|
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) {
|
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;
|
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
|
/** 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
|
stdout and @c stderr from the child process to the parent
|
||||||
using mrw::Pipe and calls @c execvp to execute the program. */
|
process using mrw::Pipe and calls @c execvp to execute the
|
||||||
|
program. */
|
||||||
_success = false;
|
_success = false;
|
||||||
_res = _err = "";
|
_res = _err = "";
|
||||||
mrw::Pipe stdOut, stdErr;
|
mrw::Pipe stdOut, stdErr;
|
||||||
@@ -128,17 +139,18 @@ mrw::Exec& mrw::Exec::execute(bool throwExc) throw(std::exception) {
|
|||||||
throw ExecutionFailedExc("cannot close pipe", *_cmd);
|
throw ExecutionFailedExc("cannot close pipe", *_cmd);
|
||||||
int num1(0), num2(0);
|
int num1(0), num2(0);
|
||||||
for (char buf1[4096], buf2[4096];
|
for (char buf1[4096], buf2[4096];
|
||||||
(num1=read(stdOut.istream(), buf1, sizeof(buf1)))>0 ||
|
(num1=read(stdOut.istream(), buf1, sizeof(buf1)))>0
|
||||||
num1==-1 && errno==EINTR ||
|
|| num1==-1 && (errno==EINTR||errno==EAGAIN)
|
||||||
(num2=read(stdErr.istream(), buf2, sizeof(buf2)))>0 ||
|
|| (num2=read(stdErr.istream(), buf2, sizeof(buf2)))>0
|
||||||
num2==-1 && errno==EINTR;
|
|| num2==-1 && (errno==EINTR||errno==EAGAIN);) {
|
||||||
_res += std::string(buf1, num1), _err += std::string(buf2, num2));
|
if (num1>0) _res += std::string(buf1, num1);
|
||||||
if (num1==-1 || num2==-1)
|
if (num2>0) _err += std::string(buf2, num2);
|
||||||
throw ExecutionFailedExc("cannot_ read pipe", *_cmd);
|
}
|
||||||
// wait for child to get return code
|
// wait for child to get return code
|
||||||
int s(0);
|
int s(0);
|
||||||
if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) {
|
if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0
|
||||||
if (throwExc) {
|
|| num1==-1 || num2==-1) {
|
||||||
|
if (exc) {
|
||||||
throw ExecutionFailedExc("execution failed", *_cmd);
|
throw ExecutionFailedExc("execution failed", *_cmd);
|
||||||
} else {
|
} else {
|
||||||
_success = false;
|
_success = false;
|
||||||
@@ -157,6 +169,106 @@ mrw::Exec& mrw::Exec::execute(bool throwExc) throw(std::exception) {
|
|||||||
return *this;
|
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) {
|
mrw::Exec& mrw::Exec::operator>>(std::string& res) throw(std::exception) {
|
||||||
execute();
|
execute();
|
||||||
res += _res;
|
res += _res;
|
||||||
|
110
mrw/exec.hpp
110
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.5 2004/12/14 20:30:09 marc
|
||||||
|
added possibility to pass string to stdin of child process
|
||||||
|
|
||||||
Revision 1.4 2004/10/07 09:27:01 marc
|
Revision 1.4 2004/10/07 09:27:01 marc
|
||||||
errors in documentation
|
errors in documentation
|
||||||
|
|
||||||
@@ -67,6 +70,18 @@ namespace mrw {
|
|||||||
// you can trace x.what() and x.stacktrace()
|
// you can trace x.what() and x.stacktrace()
|
||||||
}
|
}
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
|
It is also possible to pass an @c stdin input argument to the
|
||||||
|
subprocess called:
|
||||||
|
|
||||||
|
@code
|
||||||
|
try {
|
||||||
|
mrw::Exec cat = mrw::Cmd("/bin/cat")
|
||||||
|
.execute("this is passed to stdin");
|
||||||
|
// "cat" passes all from stdin to stdout, therefore:
|
||||||
|
assert(cat.result()=="this is passed to stdin");
|
||||||
|
} catch (...) {} // ignore
|
||||||
|
@endcode
|
||||||
*/
|
*/
|
||||||
//@{
|
//@{
|
||||||
|
|
||||||
@@ -99,6 +114,9 @@ namespace mrw {
|
|||||||
and returns the two streams @c cout and @c cerr, also known as @c
|
and returns the two streams @c cout and @c cerr, also known as @c
|
||||||
stderr and @c stdout.
|
stderr and @c stdout.
|
||||||
|
|
||||||
|
Method @c execute can optionally also take a string parameter
|
||||||
|
that is passed to @c stdin of the child process.
|
||||||
|
|
||||||
There are different ways of usage for this class. A simple way,
|
There are different ways of usage for this class. A simple way,
|
||||||
one line of code, to get only the resulting stream (no error)
|
one line of code, to get only the resulting stream (no error)
|
||||||
is:
|
is:
|
||||||
@@ -146,6 +164,42 @@ namespace mrw {
|
|||||||
*/
|
*/
|
||||||
Exec& execute(bool exc=true) throw(std::exception);
|
Exec& execute(bool exc=true) throw(std::exception);
|
||||||
|
|
||||||
|
/** @brief Execute the command, pass @c stdin.
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
@throw ExecutionFailedExc is thrown if
|
||||||
|
- fork fails
|
||||||
|
- creation or setup of pipes failed
|
||||||
|
- if given parameter is @c true (the default) also if the
|
||||||
|
executed program terminates with an error
|
||||||
|
*/
|
||||||
|
Exec& execute(const std::string& input, bool exc=true)
|
||||||
|
throw(std::exception);
|
||||||
|
|
||||||
|
/** @brief Execute the command, pass @c stdin.
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
@throw ExecutionFailedExc is thrown if
|
||||||
|
- fork fails
|
||||||
|
- creation or setup of pipes failed
|
||||||
|
- if given parameter is @c true (the default) also if the
|
||||||
|
executed program terminates with an error
|
||||||
|
*/
|
||||||
|
Exec& execute(char const*const input, bool exc=true)
|
||||||
|
throw(std::exception) {
|
||||||
|
return execute(std::string(input), exc);
|
||||||
|
}
|
||||||
|
|
||||||
/** @brief Executes the command if not done, streams @c stdout into a string
|
/** @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
|
||||||
@@ -220,7 +274,7 @@ namespace mrw {
|
|||||||
/** @brief A system command to be executed
|
/** @brief A system command to be executed
|
||||||
@pre #include <mrw/exec.hpp>
|
@pre #include <mrw/exec.hpp>
|
||||||
|
|
||||||
This class is used in conjunction with mrw::Exec. It mus be
|
This class is used in conjunction with mrw::Exec. It must be
|
||||||
initialized with the command name, then the command parameters
|
initialized with the command name, then the command parameters
|
||||||
are appended either with commas, or by streaming them into the
|
are appended either with commas, or by streaming them into the
|
||||||
command, whatever you like.
|
command, whatever you like.
|
||||||
@@ -284,6 +338,60 @@ namespace mrw {
|
|||||||
*/
|
*/
|
||||||
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
|
||||||
|
|
||||||
|
Creates a mrw::Exec, executes the command, passes the input
|
||||||
|
and the flag to mrw::Exec::execute() and returns the created
|
||||||
|
mrw::Exec. The result of the execution can be retrieved
|
||||||
|
through the returned mrw::Exec object: The methods
|
||||||
|
mrw::Exec::success(), mrw::Exec::result() and
|
||||||
|
mrw::Exec::error() provide the necessary information.
|
||||||
|
|
||||||
|
@param input Input that is passed to @c stdin of the child process.
|
||||||
|
|
||||||
|
@param exc
|
||||||
|
- @c true throw an exception if return status is not zero
|
||||||
|
- @c false throw only an exception in case of a fatal error
|
||||||
|
|
||||||
|
@return the mrw::Exec that has executed the command
|
||||||
|
|
||||||
|
@throw ExecutionFailedExc is thrown if
|
||||||
|
- fork fails
|
||||||
|
- creation or setup of pipes failed
|
||||||
|
- if given parameter is @c true (the default) also if the
|
||||||
|
executed program terminates with an error
|
||||||
|
*/
|
||||||
|
Exec execute(const std::string& input, bool exc=true) const
|
||||||
|
throw(std::exception);
|
||||||
|
|
||||||
|
/** @brief Create a mrw::Exec and execute the command given an input
|
||||||
|
|
||||||
|
Creates a mrw::Exec, executes the command, passes the input
|
||||||
|
and the flag to mrw::Exec::execute() and returns the created
|
||||||
|
mrw::Exec. The result of the execution can be retrieved
|
||||||
|
through the returned mrw::Exec object: The methods
|
||||||
|
mrw::Exec::success(), mrw::Exec::result() and
|
||||||
|
mrw::Exec::error() provide the necessary information.
|
||||||
|
|
||||||
|
@param input Input that is passed to @c stdin of the child process.
|
||||||
|
|
||||||
|
@param exc
|
||||||
|
- @c true throw an exception if return status is not zero
|
||||||
|
- @c false throw only an exception in case of a fatal error
|
||||||
|
|
||||||
|
@return the mrw::Exec that has executed the command
|
||||||
|
|
||||||
|
@throw ExecutionFailedExc is thrown if
|
||||||
|
- fork fails
|
||||||
|
- creation or setup of pipes failed
|
||||||
|
- if given parameter is @c true (the default) also if the
|
||||||
|
executed program terminates with an error
|
||||||
|
*/
|
||||||
|
Exec execute(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 is allowed to call @c path() and @c args().
|
||||||
friend class Exec;
|
friend class Exec;
|
||||||
|
@@ -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 2004/12/14 20:30:10 marc
|
||||||
|
added possibility to pass string to stdin of child process
|
||||||
|
|
||||||
Revision 1.5 2004/10/13 10:43:11 marc
|
Revision 1.5 2004/10/13 10:43:11 marc
|
||||||
test for bad exception specification
|
test for bad exception specification
|
||||||
|
|
||||||
@@ -40,14 +43,20 @@ public:
|
|||||||
std::string res = (mrw::Cmd("/bin/ls"), "-l", "..").execute();
|
std::string res = (mrw::Cmd("/bin/ls"), "-l", "..").execute();
|
||||||
CPPUNIT_ASSERT(res.find("COPYING")<res.size());
|
CPPUNIT_ASSERT(res.find("COPYING")<res.size());
|
||||||
}
|
}
|
||||||
|
void catTest() {
|
||||||
|
std::string res =mrw::Cmd("/bin/cat").execute("This is a test");
|
||||||
|
CPPUNIT_ASSERT(res=="This is a test");
|
||||||
|
}
|
||||||
void excTest() {
|
void excTest() {
|
||||||
std::string res = (mrw::Cmd("/bin/false")).execute().result();
|
std::string res1 = (mrw::Cmd("/bin/false")).execute().result();
|
||||||
|
std::string res2 = (mrw::Cmd("/bin/false")).execute("").result();
|
||||||
}
|
}
|
||||||
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();
|
||||||
}
|
}
|
||||||
CPPUNIT_TEST_SUITE(ExecTest);
|
CPPUNIT_TEST_SUITE(ExecTest);
|
||||||
CPPUNIT_TEST(lsTest);
|
CPPUNIT_TEST(lsTest);
|
||||||
|
CPPUNIT_TEST(catTest);
|
||||||
CPPUNIT_TEST_EXCEPTION(excTest, mrw::ExecutionFailedExc);
|
CPPUNIT_TEST_EXCEPTION(excTest, mrw::ExecutionFailedExc);
|
||||||
CPPUNIT_TEST_EXCEPTION(unexpectedExc, std::bad_exception);
|
CPPUNIT_TEST_EXCEPTION(unexpectedExc, std::bad_exception);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
Reference in New Issue
Block a user