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)
This commit is contained in:
88
mrw/exec.cpp
88
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.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)
|
||||||
|
|
||||||
Revision 1.7 2004/12/17 17:38:57 marc
|
Revision 1.7 2004/12/17 17:38:57 marc
|
||||||
it works perfectly with blocking pipes and buffer size of 1
|
it works perfectly with blocking pipes and buffer size of 1
|
||||||
|
|
||||||
@@ -36,12 +39,10 @@
|
|||||||
#include <mrw/unistd.hpp>
|
#include <mrw/unistd.hpp>
|
||||||
#include <mrw/exception.hpp>
|
#include <mrw/exception.hpp>
|
||||||
#include <mrw/stdext.hpp> // max
|
#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
|
#include <assert.h> // assert
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
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):
|
||||||
@@ -133,7 +134,7 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
|
|||||||
program. */
|
program. */
|
||||||
_success = false;
|
_success = false;
|
||||||
_res = _err = "";
|
_res = _err = "";
|
||||||
mrw::Pipe stdOut(true), stdErr(true);
|
mrw::Pipe stdOut(mrw::Pipe::block_output), stdErr(mrw::Pipe::block_output);
|
||||||
if (!stdOut || !stdErr)
|
if (!stdOut || !stdErr)
|
||||||
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
|
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
|
||||||
pid_t pid(fork());
|
pid_t pid(fork());
|
||||||
@@ -144,37 +145,26 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
|
|||||||
stdErr.close_out();
|
stdErr.close_out();
|
||||||
if (!stdOut || !stdErr)
|
if (!stdOut || !stdErr)
|
||||||
throw ExecutionFailedExc("cannot close pipe", *_cmd);
|
throw ExecutionFailedExc("cannot close pipe", *_cmd);
|
||||||
ssize_t num1(-1), num2(-1);
|
ssize_t num1(1), num2(1);
|
||||||
fd_set readfds;
|
char buf[4096];
|
||||||
char buf[1]; // used to have larger buffer, but since non blocking fails...
|
|
||||||
int res(0), s(0);
|
int res(0), s(0);
|
||||||
/** @bug It sometimes did not get the whole input since I
|
/** @bug @b Solved: It sometimes did not get the whole input
|
||||||
changed to non blocking pipes. Now pipes are blocking
|
since I changed to non blocking pipes. Now I have found
|
||||||
again and buffer size is 1, so I hope all problems are
|
the solution to the problems: Non blocking IO does not
|
||||||
resolved. Please report any problems! */
|
work in conjunction with @c select! Also the pipe must
|
||||||
while (num1||num2) try { // not all end of file
|
not be nonblocking on both ends, but only on one. */
|
||||||
|
while (num1||num2||!res) try { // not end of files or child terminated
|
||||||
if (!res && (res=waitpid(pid, &s, WNOHANG)))
|
if (!res && (res=waitpid(pid, &s, WNOHANG)))
|
||||||
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
|
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
|
||||||
throw ExecutionFailedExc("execution failed", *_cmd);
|
throw ExecutionFailedExc("execution failed", *_cmd);
|
||||||
// select...
|
if (num1<=0&&num2<=0) usleep(10000); // nothing to read last time
|
||||||
FD_ZERO(&readfds);
|
|
||||||
if (num1) FD_SET(stdOut.istream(), &readfds);
|
|
||||||
if (num2) FD_SET(stdErr.istream(), &readfds);
|
|
||||||
int n(mrw::max((num1?stdOut.istream():0),
|
|
||||||
(num2?stdErr.istream():0)));
|
|
||||||
timeval tm = {0, 10000};
|
|
||||||
int num = select(n+1, &readfds, 0, 0, 0);
|
|
||||||
if (num<0) throw ExecutionFailedExc("select failed", *_cmd);
|
|
||||||
if (num==0 && res==pid) break; // process ended, no data left
|
|
||||||
// check and handle stdout
|
// check and handle stdout
|
||||||
if (num>0 && FD_ISSET(stdOut.istream(), &readfds))
|
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
|
||||||
if ((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 && (errno!=EINTR&&errno!=EAGAIN))
|
||||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||||||
// check and handle stderr
|
// check and handle stderr
|
||||||
if (num>0 && FD_ISSET(stdErr.istream(), &readfds))
|
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
|
||||||
if ((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 && (errno!=EINTR&&errno!=EAGAIN))
|
||||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||||||
@@ -183,7 +173,6 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
|
|||||||
_success = false;
|
_success = false;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
std::cout<<"num1="<<num1<<", num2="<<num2<<std::endl;
|
|
||||||
} else { // child
|
} else { // child
|
||||||
stdOut.close_in();
|
stdOut.close_in();
|
||||||
stdErr.close_in();
|
stdErr.close_in();
|
||||||
@@ -209,8 +198,8 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
|||||||
the program. */
|
the program. */
|
||||||
_success = false;
|
_success = false;
|
||||||
_res = _err = "";
|
_res = _err = "";
|
||||||
mrw::Pipe stdIn(true), // child terminates if stdin is non-blocking
|
mrw::Pipe stdIn(mrw::Pipe::block_input),
|
||||||
stdOut(true), stdErr(true); // non blocking is also a problem here
|
stdOut(mrw::Pipe::block_output), stdErr(mrw::Pipe::block_output);
|
||||||
if (!stdIn || !stdOut || !stdErr)
|
if (!stdIn || !stdOut || !stdErr)
|
||||||
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
|
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
|
||||||
pid_t pid(fork());
|
pid_t pid(fork());
|
||||||
@@ -221,36 +210,22 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
|||||||
stdErr.close_out();
|
stdErr.close_out();
|
||||||
if (!stdIn || !stdOut || !stdErr)
|
if (!stdIn || !stdOut || !stdErr)
|
||||||
throw ExecutionFailedExc("cannot close pipes", *_cmd);
|
throw ExecutionFailedExc("cannot close pipes", *_cmd);
|
||||||
ssize_t num0(-1), num1(-1), num2(-1);
|
ssize_t num0(1), num1(1), num2(1);
|
||||||
std::string in(input);
|
std::string in(input);
|
||||||
fd_set writefds, readfds;
|
char buf[4096];
|
||||||
char buf[1]; // used to have larger buffer, but since non blocking fails...
|
|
||||||
int res(0), s(0);
|
int res(0), s(0);
|
||||||
/** @bug It sometimes did not get the whole input since I
|
/** @bug @b Solved: It sometimes did not get the whole input
|
||||||
changed to non blocking pipes. Now pipes are blocking
|
since I changed to non blocking pipes. Now I have found
|
||||||
again and buffer size is 1, so I hope all problems are
|
the solution to the problems: Non blocking IO does not
|
||||||
resolved. Please report any problems! */
|
work in conjunction with @c select! Also the pipe must
|
||||||
while (num0||num1||num2) try { // not all end of file
|
not be nonblocking on both ends, but only on one. */
|
||||||
|
while (num0||num1||num2||!res) try { // not end of files or child terminated
|
||||||
if (!res && (res=waitpid(pid, &s, WNOHANG)))
|
if (!res && (res=waitpid(pid, &s, WNOHANG)))
|
||||||
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
|
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
|
||||||
throw ExecutionFailedExc("execution failed", *_cmd);
|
throw ExecutionFailedExc("execution failed", *_cmd);
|
||||||
// select...
|
if (num0<=0&&num1<=0&&num2<=0) usleep(10000); // no activity last time
|
||||||
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);
|
|
||||||
int n(mrw::max((num0?stdIn.ostream():0),
|
|
||||||
mrw::max((num1?stdOut.istream():0),
|
|
||||||
(num2?stdErr.istream():0))));
|
|
||||||
timeval tm = {0, 10000};
|
|
||||||
int num = select(n+1, &readfds, &writefds, 0, &tm);
|
|
||||||
if (num<0) throw ExecutionFailedExc("select failed", *_cmd);
|
|
||||||
if (num==0 && res==pid) break; // process ended, no data left
|
|
||||||
// check and handle stdin
|
// check and handle stdin
|
||||||
if (num0 && num>0
|
if (num0 && (num0=write(stdIn.ostream(), in.c_str(), in.size()))>0) {
|
||||||
&& FD_ISSET(stdIn.ostream(), &writefds))
|
|
||||||
if ((num0=write(stdIn.ostream(), in.c_str(), in.size()))>0) {
|
|
||||||
if ((unsigned int)num0<in.size())
|
if ((unsigned int)num0<in.size())
|
||||||
in = in.substr(num0);
|
in = in.substr(num0);
|
||||||
else if ((unsigned int)num0==in.size())
|
else if ((unsigned int)num0==in.size())
|
||||||
@@ -258,14 +233,12 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
|||||||
} else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN))
|
} else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN))
|
||||||
throw ExecutionFailedExc("writing stdin", *_cmd);
|
throw ExecutionFailedExc("writing stdin", *_cmd);
|
||||||
// check and handle stdout
|
// check and handle stdout
|
||||||
if (num>0 && FD_ISSET(stdOut.istream(), &readfds))
|
if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
|
||||||
if ((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 && (errno!=EINTR&&errno!=EAGAIN))
|
||||||
throw ExecutionFailedExc("readin stdout", *_cmd);
|
throw ExecutionFailedExc("readin stdout", *_cmd);
|
||||||
// check and handle stderr
|
// check and handle stderr
|
||||||
if (num>0 && FD_ISSET(stdErr.istream(), &readfds))
|
if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
|
||||||
if ((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 && (errno!=EINTR&&errno!=EAGAIN))
|
||||||
throw ExecutionFailedExc("readin stderr", *_cmd);
|
throw ExecutionFailedExc("readin stderr", *_cmd);
|
||||||
@@ -274,7 +247,6 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
|
|||||||
_success = false;
|
_success = false;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
std::cout<<"num0="<<num0<<", num1="<<num1<<", num2="<<num2<<std::endl;
|
|
||||||
} else { // child
|
} else { // child
|
||||||
stdIn.close_out();
|
stdIn.close_out();
|
||||||
stdOut.close_in();
|
stdOut.close_in();
|
||||||
|
Reference in New Issue
Block a user