/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ 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
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
Revision 1.4 2004 / 08 / 28 16 : 21 : 25 marc
mrw - c + + - 0.92 ( mrw )
- new file : version . cpp
- new file header for all sources
- work around warning in mrw : : auto < T >
- possibility to compile without log4cxx
- work around bugs in demangle . h and libiberty . h
- corrections in documentation
- added simple tracing mechanism
- more warnings
- small corrections in Auto < > : : Free and a new test for it
- possibility to compile without stack trace
*/
# include <mrw/exec.hpp>
# 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 <assert.h> // assert
mrw : : ExecutionFailedExc : : ExecutionFailedExc ( const std : : string & w ,
const std : : string & c )
throw ( std : : bad_exception ) :
_what ( std : : string ( " mrw::Exec: command execution failed \n " ) +
std : : string ( " failed command was: \" " + c + " \" \n " ) +
std : : string ( " error was: \" " ) + w + ' " ' ) {
/**
@ c what looks like :
@ verbatim
mrw : : Exec : command execution failed
failed command was : " /bin/OOOOPS -v -q --crash "
error was : " execution failed "
@ endverbatim
*/
}
mrw : : Cmd : : Cmd ( const std : : string & c ) throw ( std : : bad_exception ) {
_cmd . push_back ( c ) ;
}
mrw : : Cmd & mrw : : Cmd : : operator , ( const std : : string & arg )
throw ( std : : bad_exception ) {
_cmd . push_back ( arg ) ;
return * this ;
}
mrw : : Cmd & mrw : : Cmd : : operator < < ( const std : : string & arg )
throw ( std : : bad_exception ) {
_cmd . push_back ( arg ) ;
return * this ;
}
mrw : : Cmd : : operator std : : string ( ) const throw ( std : : bad_exception ) {
ArgList : : const_iterator it ( _cmd . begin ( ) ) ;
std : : string c ( * it ) ;
while ( + + it ! = _cmd . end ( ) ) c + = ' ' + * it ;
return c ;
}
mrw : : Cmd : : operator mrw : : Exec ( ) const throw ( std : : bad_exception ) {
return mrw : : Exec ( * this ) ;
}
mrw : : Exec mrw : : Cmd : : execute ( bool exc ) const throw ( std : : exception ) {
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 ) {
return _cmd . front ( ) . c_str ( ) ;
}
char * * mrw : : Cmd : : args ( ) const throw ( std : : bad_exception ) {
if ( _cmd . size ( ) = = 0 ) return 0 ;
char * * array = new char * [ _cmd . size ( ) + 1 ] ;
int i ( 0 ) ;
for ( ArgList : : const_iterator it ( _cmd . begin ( ) ) ; it ! = _cmd . end ( ) ; + + it )
memcpy ( array [ i + + ] = new char [ it - > size ( ) + 1 ] , it - > c_str ( ) , it - > size ( ) + 1 ) ;
array [ i ] = 0 ;
return array ;
}
mrw : : Exec : : Exec ( const mrw : : Cmd & c ) throw ( std : : bad_exception ) :
_cmd ( new mrw : : Cmd ( c ) ) , _success ( false ) {
}
mrw : : Exec : : Exec ( const mrw : : Exec & e ) throw ( std : : bad_exception ) :
_cmd ( new mrw : : Cmd ( * e . _cmd ) ) ,
_res ( e . _res ) , _err ( e . _err ) , _success ( e . _success ) {
}
mrw : : Exec : : ~ Exec ( ) throw ( ) {
delete _cmd ;
}
mrw : : Exec & mrw : : Exec : : operator = ( const mrw : : Exec & e ) throw ( std : : bad_exception ) {
if ( this = = & e ) return * this ;
* _cmd = * e . _cmd ; _res = e . _res ; _err = e . _err ; _success = e . _success ;
return * this ;
}
mrw : : Exec & mrw : : Exec : : execute ( bool exc ) throw ( std : : exception ) {
/** 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 using mrw : : Pipe and calls @ c execvp to execute the
program . */
_success = false ;
_res = _err = " " ;
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 ( ) ) ;
if ( pid < 0 )
throw ExecutionFailedExc ( " cannot fork " , * _cmd ) ;
if ( pid ) { // parent
stdOut . close_out ( ) ;
stdErr . close_out ( ) ;
if ( ! stdOut | | ! stdErr )
throw ExecutionFailedExc ( " cannot close pipe " , * _cmd ) ;
ssize_t num1 ( 1 ) , num2 ( 1 ) ;
char buf [ 4096 ] ;
int res ( 0 ) , s ( 0 ) ;
/** @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 ) ;
if ( num1 < = 0 & & num2 < = 0 ) usleep ( 10000 ) ; // nothing to read last time
// check and handle stdout
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 ( 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 ;
}
} else { // child
stdOut . close_in ( ) ;
stdErr . close_in ( ) ;
stdOut . connect_cout ( ) ;
stdErr . connect_cerr ( ) ;
execvp ( _cmd - > path ( ) , _cmd - > args ( ) ) ;
exit ( 1 ) ; // execute failed
}
_success = true ;
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 ( 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 ( ) ) ;
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 ) ;
char buf [ 4096 ] ;
int res ( 0 ) , s ( 0 ) ;
/** @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 ) ;
if ( num0 < = 0 & & num1 < = 0 & & num2 < = 0 ) usleep ( 10000 ) ; // no activity last time
// check and handle stdin
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 ( 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 ( 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 ;
}
} 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 ) {
execute ( ) ;
res + = _res ;
return * this ;
}
mrw : : Exec : : operator std : : string & ( ) throw ( std : : exception ) {
if ( ! _success ) execute ( ) ;
return _res ;
}
mrw : : Exec : : operator bool ( ) throw ( std : : bad_exception ) {
return _success ;
}
std : : string & mrw : : Exec : : result ( ) throw ( std : : exception ) {
if ( ! _success ) execute ( ) ;
return _res ;
}
std : : string & mrw : : Exec : : error ( ) throw ( std : : exception ) {
if ( ! _success ) execute ( ) ;
return _err ;
}
bool mrw : : Exec : : success ( ) throw ( std : : bad_exception ) {
return _success ;
}