@ -9,6 +9,9 @@
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ 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
it works perfectly with blocking pipes and buffer size of 1
@ -36,12 +39,10 @@
# 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 <sys/wait.h> // waitpid#include <unistd.h> // fork, exec
# include <string.h> // memcpy
# include <assert.h> // assert
# include <iostream>
mrw : : ExecutionFailedExc : : ExecutionFailedExc ( const std : : string & w ,
const std : : string & c )
throw ( std : : bad_exception ) :
@ -133,7 +134,7 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
program . */
_success = false ;
_res = _err = " " ;
mrw : : Pipe stdOut ( true ) , stdErr ( true ) ;
mrw : : Pipe stdOut ( mrw : : Pipe : : block_output ) , stdErr ( mrw : : Pipe : : block_output ) ;
if ( ! stdOut | | ! stdErr )
throw mrw : : ExecutionFailedExc ( " cannot create pipe " , * _cmd ) ;
pid_t pid ( fork ( ) ) ;
@ -144,46 +145,34 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
stdErr . close_out ( ) ;
if ( ! stdOut | | ! stdErr )
throw ExecutionFailedExc ( " cannot close pipe " , * _cmd ) ;
ssize_t num1 ( - 1 ) , num2 ( - 1 ) ;
fd_set readfds ;
char buf [ 1 ] ; // used to have larger buffer, but since non blocking fails...
ssize_t num1 ( 1 ) , num2 ( 1 ) ;
char buf [ 4096 ] ;
int res ( 0 ) , s ( 0 ) ;
/** @bug It sometimes did not get the whole input since I
changed to non blocking pipes . Now pipes are blocking
again and buffer size is 1 , so I hope all problems are
resolved . Please report any problems ! */
while ( num1 | | num2 ) try { // not all end of file
/** @bug @b Solved: It sometimes did not get the whole input
since I changed to non blocking pipes . Now I have found
the solution to the problems : Non blocking IO does not
work in conjunction with @ c select ! Also the pipe must
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 ! = 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 + 1 , & readfds , 0 , 0 , 0 ) ;
if ( num < 0 ) throw ExecutionFailedExc ( " select failed " , * _cmd ) ;
if ( num = = 0 & & res = = pid ) break ; // process ended, no data left
if ( num1 < = 0 & & num2 < = 0 ) usleep ( 10000 ) ; // nothing to read last time
// 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 ) ;
if ( num1 & & ( 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 ) ;
if ( num2 & & ( 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 ;
}
std : : cout < < " num1= " < < num1 < < " , num2= " < < num2 < < std : : endl ;
} else { // child
stdOut . close_in ( ) ;
stdErr . close_in ( ) ;
@ -209,8 +198,8 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
the program . */
_success = false ;
_res = _err = " " ;
mrw : : Pipe stdIn ( true ) , // child terminates if stdin is non-blocking
stdOut ( true ) , stdErr ( true ) ; // non blocking is also a problem here
mrw : : Pipe stdIn ( mrw : : Pipe : : block_input ) ,
stdOut ( mrw : : Pipe : : block_output ) , stdErr ( mrw : : Pipe : : block_output ) ;
if ( ! stdIn | | ! stdOut | | ! stdErr )
throw mrw : : ExecutionFailedExc ( " cannot create pipe " , * _cmd ) ;
pid_t pid ( fork ( ) ) ;
@ -221,60 +210,43 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
stdErr . close_out ( ) ;
if ( ! stdIn | | ! stdOut | | ! stdErr )
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 ) ;
fd_set writefds , readfds ;
char buf [ 1 ] ; // used to have larger buffer, but since non blocking fails...
char buf [ 4096 ] ;
int res ( 0 ) , s ( 0 ) ;
/** @bug It sometimes did not get the whole input since I
changed to non blocking pipes . Now pipes are blocking
again and buffer size is 1 , so I hope all problems are
resolved . Please report any problems ! */
while ( num0 | | num1 | | num2 ) try { // not all end of file
/** @bug @b Solved: It sometimes did not get the whole input
since I changed to non blocking pipes . Now I have found
the solution to the problems : Non blocking IO does not
work in conjunction with @ c select ! Also the pipe must
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 ! = pid | | WIFEXITED ( s ) ! = 0 & & WEXITSTATUS ( s ) ! = 0 )
throw ExecutionFailedExc ( " execution failed " , * _cmd ) ;
// 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 ) ;
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
if ( num0 < = 0 & & num1 < = 0 & & num2 < = 0 ) usleep ( 10000 ) ; // no activity last time
// 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 ) ;
if ( num0 & & ( 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 ) ;
if ( num1 & & ( 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 ) ;
if ( num2 & & ( 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 ;
}
std : : cout < < " num0= " < < num0 < < " , num1= " < < num1 < < " , num2= " < < num2 < < std : : endl ;
} else { // child
stdIn . close_out ( ) ;
stdOut . close_in ( ) ;