stable implementation with select for both executes
This commit is contained in:
		
							
								
								
									
										78
									
								
								mrw/exec.cpp
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								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.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
 | 
					    Revision 1.5  2004/12/14 20:30:09  marc
 | 
				
			||||||
    added possibility to pass string to stdin of child process
 | 
					    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();
 | 
					    stdErr.close_out();
 | 
				
			||||||
    if (!stdOut || !stdErr)
 | 
					    if (!stdOut || !stdErr)
 | 
				
			||||||
      throw ExecutionFailedExc("cannot close pipe", *_cmd);
 | 
					      throw ExecutionFailedExc("cannot close pipe", *_cmd);
 | 
				
			||||||
    int num1(0), num2(0);
 | 
					    ssize_t num1(-1), num2(-1);
 | 
				
			||||||
    for (char buf1[4096], buf2[4096];
 | 
					    fd_set readfds;
 | 
				
			||||||
         (num1=read(stdOut.istream(), buf1, sizeof(buf1)))>0
 | 
					    char buf[4096];
 | 
				
			||||||
           || num1==-1 && (errno==EINTR||errno==EAGAIN)
 | 
					    int res(0), s(0);
 | 
				
			||||||
           || (num2=read(stdErr.istream(), buf2, sizeof(buf2)))>0
 | 
					    while (num1||num2) try { // not all end of file
 | 
				
			||||||
           || num2==-1 && (errno==EINTR||errno==EAGAIN);) {
 | 
					      if (!res && (res=waitpid(pid, &s, WNOHANG)))
 | 
				
			||||||
      if (num1>0) _res += std::string(buf1, num1);
 | 
					        if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
 | 
				
			||||||
      if (num2>0) _err += std::string(buf2, num2);
 | 
					          throw ExecutionFailedExc("execution failed", *_cmd);
 | 
				
			||||||
    }
 | 
					      // select...
 | 
				
			||||||
    // wait for child to get return code
 | 
					      FD_ZERO(&readfds);
 | 
				
			||||||
    int s(0);
 | 
					      if (num1) FD_SET(stdOut.istream(), &readfds);
 | 
				
			||||||
    if (waitpid(pid, &s, 0)!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0
 | 
					      if (num2) FD_SET(stdErr.istream(), &readfds);
 | 
				
			||||||
        || num1==-1 || num2==-1) {
 | 
					      int n(mrw::max((num1?stdOut.istream():0),
 | 
				
			||||||
      if (exc) {
 | 
					                     (num2?stdErr.istream():0)));
 | 
				
			||||||
        throw ExecutionFailedExc("execution failed", *_cmd);
 | 
					      timeval tm = {0, 10000};
 | 
				
			||||||
      } else {
 | 
					      int num = select(n, &readfds, 0, 0, &tm);
 | 
				
			||||||
        _success = false;
 | 
					      if (num<0) throw ExecutionFailedExc("select failed", *_cmd);
 | 
				
			||||||
        return *this;
 | 
					      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
 | 
					  } else { // child
 | 
				
			||||||
    stdOut.close_in();
 | 
					    stdOut.close_in();
 | 
				
			||||||
@@ -198,26 +216,24 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
 | 
				
			|||||||
    std::string in(input);
 | 
					    std::string in(input);
 | 
				
			||||||
    fd_set writefds, readfds;
 | 
					    fd_set writefds, readfds;
 | 
				
			||||||
    char buf[4096];
 | 
					    char buf[4096];
 | 
				
			||||||
    while (num0 || num1 || num2) try { // no end of file in stdin, -out, -err
 | 
					    int res(0), s(0);
 | 
				
			||||||
      int res(0), s(0);
 | 
					    while (num0||num1||num2) try { // not all end of file
 | 
				
			||||||
      if ((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);
 | 
				
			||||||
        _success = true;
 | 
					 | 
				
			||||||
        return *this;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // select...
 | 
					      // select...
 | 
				
			||||||
      FD_ZERO(&writefds);
 | 
					      FD_ZERO(&writefds);
 | 
				
			||||||
      FD_ZERO(&readfds);
 | 
					      FD_ZERO(&readfds);
 | 
				
			||||||
      if (num0) FD_SET(stdIn.ostream(), &writefds);
 | 
					      if (num0) FD_SET(stdIn.ostream(), &writefds);
 | 
				
			||||||
      if (num1) FD_SET(stdOut.istream(), &readfds);
 | 
					      if (num1) FD_SET(stdOut.istream(), &readfds);
 | 
				
			||||||
      if (num2) FD_SET(stdErr.istream(), &readfds);
 | 
					      if (num2) FD_SET(stdErr.istream(), &readfds);
 | 
				
			||||||
      timeval tm = {1, 0};
 | 
					 | 
				
			||||||
      int n(mrw::max((num0?stdIn.ostream():0),
 | 
					      int n(mrw::max((num0?stdIn.ostream():0),
 | 
				
			||||||
                     mrw::max((num1?stdOut.istream():0),
 | 
					                     mrw::max((num1?stdOut.istream():0),
 | 
				
			||||||
                              (num2?stdErr.istream():0))));
 | 
					                              (num2?stdErr.istream():0))));
 | 
				
			||||||
 | 
					      timeval tm = {0, 10000};
 | 
				
			||||||
      int num = select(n, &readfds, &writefds, 0, &tm);
 | 
					      int num = select(n, &readfds, &writefds, 0, &tm);
 | 
				
			||||||
      if (num<0) throw ExecutionFailedExc("select failed", *_cmd);
 | 
					      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 && num>0
 | 
				
			||||||
          && FD_ISSET(stdIn.ostream(), &writefds))
 | 
					          && FD_ISSET(stdIn.ostream(), &writefds))
 | 
				
			||||||
@@ -245,16 +261,6 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
 | 
				
			|||||||
      _success = false;
 | 
					      _success = false;
 | 
				
			||||||
      return *this;
 | 
					      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
 | 
					  } else { // child
 | 
				
			||||||
    stdIn.close_out();
 | 
					    stdIn.close_out();
 | 
				
			||||||
    stdOut.close_in();
 | 
					    stdOut.close_in();
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user