From 2a47dd786386d77d970e3abd8be72e2ae586b06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=A4ckerlin?= Date: Fri, 17 Dec 2004 16:28:51 +0000 Subject: [PATCH] stable implementation with select for both executes --- mrw/exec.cpp | 78 ++++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/mrw/exec.cpp b/mrw/exec.cpp index 7eec02a..6d4a2d7 100644 --- a/mrw/exec.cpp +++ b/mrw/exec.cpp @@ -9,6 +9,9 @@ @license LGPL, see file COPYING $Log$ + Revision 1.6 2004/12/17 16:28:51 marc + stable implementation with select for both executes + Revision 1.5 2004/12/14 20:30:09 marc added possibility to pass string to stdin of child process @@ -137,25 +140,40 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) { stdErr.close_out(); if (!stdOut || !stdErr) 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||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 - || num1==-1 || num2==-1) { - if (exc) { - throw ExecutionFailedExc("execution failed", *_cmd); - } else { - _success = false; - return *this; - } + ssize_t num1(-1), num2(-1); + fd_set readfds; + char buf[4096]; + int res(0), s(0); + while (num1||num2) try { // not all end of file + if (!res && (res=waitpid(pid, &s, WNOHANG))) + if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) + throw ExecutionFailedExc("execution failed", *_cmd); + // select... + 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, &readfds, 0, 0, &tm); + if (num<0) throw ExecutionFailedExc("select failed", *_cmd); + if (num==0 && res==pid) break; // process ended, no data left + // 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; } } else { // child stdOut.close_in(); @@ -198,26 +216,24 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc) 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))) { + int res(0), s(0); + while (num0||num1||num2) try { // not all end of file + if (!res && (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)))); + timeval tm = {0, 10000}; int num = select(n, &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 if (num0 && num>0 && FD_ISSET(stdIn.ostream(), &writefds)) @@ -245,16 +261,6 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc) _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();