/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.13 2005 / 04 / 20 18 : 32 : 14 marc
* * * empty log message * * *
Revision 1.12 2005 / 04 / 20 18 : 12 : 55 marc
added kill ( ) for PartialExec
Revision 1.11 2005 / 04 / 19 18 : 48 : 00 marc
new feature PartialExec
Revision 1.10 2005 / 03 / 14 16 : 26 : 34 marc
bugs have been fixed a long time ago , now no more in buglist
Revision 1.9 2004 / 12 / 20 07 : 40 : 35 marc
documentation improved , new grouping
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 <sys/wait.h> // waitpid
# include <unistd.h> // fork, exec
# include <string.h> // memcpy
# include <assert.h> // assert
# include <sys/types.h> // kill
# include <signal.h> // kill
//=========================================================== ExecutionFailedExc
//------------------------------------------------------------------------------
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
*/
}
//========================================================================== Cmd
//------------------------------------------------------------------------------
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 : : Cmd : : operator mrw : : PartialExec ( ) const throw ( std : : bad_exception ) {
return mrw : : PartialExec ( * 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 ) ;
}
//------------------------------------------------------------------------------
mrw : : PartialExec mrw : : Cmd : : start ( bool useInput ) const throw ( std : : exception ) {
return mrw : : PartialExec ( * this ) . start ( useInput ) ;
}
//------------------------------------------------------------------------------
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 ;
}
//========================================================================= Exec
//------------------------------------------------------------------------------
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 ) ;
/* 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 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 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " readin stdout " , * _cmd ) ;
else
num1 = 1 ;
// check and handle stderr
if ( num2 & & ( num2 = read ( stdErr . istream ( ) , buf , sizeof ( buf ) ) ) > 0 )
_err + = std : : string ( buf , num2 ) ;
else if ( num2 = = - 1 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " readin stderr " , * _cmd ) ;
else
num2 = 1 ;
} catch ( . . . ) {
_success = false ;
if ( exc ) throw ;
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 between the child process and 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 ) ;
/* 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 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 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " writing stdin " , * _cmd ) ;
else
num0 = 1 ;
// check and handle stdout
if ( num1 & & ( num1 = read ( stdOut . istream ( ) , buf , sizeof ( buf ) ) ) > 0 )
_res + = std : : string ( buf , num1 ) ;
else if ( num1 = = - 1 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " readin stdout " , * _cmd ) ;
else
num1 = 1 ;
// check and handle stderr
if ( num2 & & ( num2 = read ( stdErr . istream ( ) , buf , sizeof ( buf ) ) ) > 0 )
_err + = std : : string ( buf , num2 ) ;
else if ( num2 = = - 1 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " readin stderr " , * _cmd ) ;
else
num2 = 1 ;
} catch ( . . . ) {
_success = false ;
if ( exc ) throw ;
return * this ;
}
} else { // child
stdIn . close_out ( ) ;
stdOut . close_in ( ) ;
stdErr . close_in ( ) ;
stdIn . connect_cin ( ) ;
stdOut . connect_cout ( ) ;
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 ;
}
//================================================================== PartialExec
//------------------------------------------------------------------------------
mrw : : PartialExec : : PartialExec ( const mrw : : Cmd & c ) throw ( std : : bad_exception ) :
Exec ( c ) , _finished ( true ) , _finish ( false ) {
}
//------------------------------------------------------------------------------
mrw : : PartialExec : : PartialExec ( mrw : : PartialExec & e )
throw ( std : : bad_exception ) :
Exec ( e ) , _finished ( e . _finished ) , _finish ( e . _finish ) ,
_stdIn ( e . _stdIn ) , _stdOut ( e . _stdOut ) , _stdErr ( e . _stdErr ) , _input ( e . _input ) ,
_num0 ( e . _num0 ) , _num1 ( e . _num1 ) , _num2 ( e . _num2 ) ,
_lastPid ( e . _lastPid ) , _pid ( e . _pid ) {
e . _finished = true ;
}
//------------------------------------------------------------------------------
mrw : : PartialExec : : PartialExec ( const mrw : : PartialExec & e )
throw ( std : : bad_exception ) :
Exec ( e ) , _finished ( e . _finished ) , _finish ( e . _finish ) ,
_input ( e . _input ) ,
_num0 ( e . _num0 ) , _num1 ( e . _num1 ) , _num2 ( e . _num2 ) ,
_lastPid ( e . _lastPid ) , _pid ( e . _pid ) {
/// @warning @c const is casted away
mrw : : PartialExec & nonConstE ( const_cast < mrw : : PartialExec & > ( e ) ) ;
_stdIn = nonConstE . _stdIn ;
_stdOut = nonConstE . _stdOut ;
_stdErr = nonConstE . _stdErr ;
nonConstE . _finished = true ;
nonConstE . _finish = false ;
}
//------------------------------------------------------------------------------
mrw : : PartialExec & mrw : : PartialExec : : operator = ( mrw : : PartialExec & e )
throw ( std : : bad_exception ) {
if ( this = = & e ) return * this ;
* _cmd = * e . _cmd ; _res = e . _res ; _err = e . _err ; _success = e . _success ;
_finished = e . _finished ;
_finish = e . _finish ;
_stdIn = e . _stdIn ; _stdOut = e . _stdOut ; _stdErr = e . _stdErr ;
_input = e . _input ; _num0 = e . _num0 ; _num1 = e . _num1 ; _num2 = e . _num2 ;
_lastPid = e . _lastPid ;
_pid = e . _pid ;
e . _finished = true ;
e . _finish = false ;
return * this ;
}
//------------------------------------------------------------------------------
mrw : : PartialExec & mrw : : PartialExec : : finish ( ) throw ( ) {
_finish = true ;
return * this ;
}
//------------------------------------------------------------------------------
bool mrw : : PartialExec : : finished ( ) throw ( ) {
return _finished ;
}
//------------------------------------------------------------------------------
mrw : : PartialExec & mrw : : PartialExec : : start ( bool useInput )
throw ( std : : exception ) {
if ( ! _finished ) throw mrw : : runtime_error ( " running process not yet finished " ) ;
/** This method calls @c fork, sets up a pipe connection to pass @c
stdin , @ c stdout and @ c stderr between the child process and the
parent process using mrw : : Pipe and calls @ c execvp to execute
the program . */
_success = false ;
_finish = ! useInput ;
_input = " " ;
_res = _err = " " ;
_stdIn = std : : auto_ptr < mrw : : Pipe > ( new mrw : : Pipe ( mrw : : Pipe : : block_input ) ) ;
_stdOut = std : : auto_ptr < mrw : : Pipe > ( new mrw : : Pipe ( mrw : : Pipe : : block_output ) ) ;
_stdErr = std : : auto_ptr < mrw : : Pipe > ( new mrw : : Pipe ( mrw : : Pipe : : block_output ) ) ;
if ( ! * _stdIn | | ! * _stdOut | | ! * _stdErr )
throw mrw : : ExecutionFailedExc ( " cannot create pipe " , * _cmd ) ;
_lastPid = 0 ;
_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 ) ;
_num0 = _num1 = _num2 = 1 ;
_finished = false ;
} else { // child
_stdIn - > close_out ( ) ;
_stdOut - > close_in ( ) ;
_stdErr - > close_in ( ) ;
_stdIn - > connect_cin ( ) ;
_stdOut - > connect_cout ( ) ;
_stdErr - > connect_cerr ( ) ;
execvp ( _cmd - > path ( ) , _cmd - > args ( ) ) ;
exit ( 1 ) ; // execute failed
}
_success = true ;
return * this ;
}
//------------------------------------------------------------------------------
std : : pair < std : : string , std : : string >
mrw : : PartialExec : : read ( const std : : string & input , bool exc )
throw ( std : : exception ) {
std : : pair < std : : string , std : : string > output ;
/** @note @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 " ) ;
/** @warning After calling finish(), it is forbidden to pass
any @ c input string , it must then always be empty ,
because the pipe is already closed ! */
assert ( ! ( _finish & & input . size ( ) ) & &
" after calling PartialExec::finish(), it is forbidden "
" to pass new input text, because the pipe is already "
" closed! this is a programming but that must be solved! " ) ;
_input + = input ;
/* 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 select ! Also
the pipe must not be nonblocking on both ends , but only on
one . */
if ( ! _finished ) try { // not finished
char buf [ 4096 ] ;
int s ( 0 ) ;
if ( ! _lastPid & & ( _lastPid = waitpid ( _pid , & s , WNOHANG ) ) ) {
if ( _lastPid ! = _pid | | WIFEXITED ( s ) ! = 0 & & WEXITSTATUS ( s ) ! = 0 )
throw ExecutionFailedExc ( " execution failed " , * _cmd ) ;
}
// check and handle stdin
if ( _input . size ( ) & & _num0 ! = - 1 & &
( _num0 = write ( _stdIn - > ostream ( ) , _input . c_str ( ) , _input . size ( ) ) ) > 0 ) {
if ( ( unsigned int ) _num0 < _input . size ( ) )
_input = _input . substr ( _num0 ) ;
else if ( ( unsigned int ) _num0 = = _input . size ( ) )
_input . clear ( ) ;
} else if ( _num0 = = - 1 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " writing stdin " , * _cmd ) ;
else
_num0 = 1 ;
// check and handle stdout
if ( _num1 & & ( _num1 = : : read ( _stdOut - > istream ( ) , buf , sizeof ( buf ) ) ) > 0 )
_res + = output . first = std : : string ( buf , _num1 ) ;
else if ( _num1 = = - 1 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " readin stdout " , * _cmd ) ;
else
_num1 = 1 ;
// check and handle stderr
if ( _num2 & & ( _num2 = : : read ( _stdErr - > istream ( ) , buf , sizeof ( buf ) ) ) > 0 )
_err + = output . second = std : : string ( buf , _num2 ) ;
else if ( _num2 = = - 1 )
if ( errno ! = EINTR & & errno ! = EAGAIN )
throw ExecutionFailedExc ( " readin stderr " , * _cmd ) ;
else
_num2 = 1 ;
if ( _finish & & ! _input . size ( ) ) {
_stdIn - > close_out ( ) ;
_num0 = 0 ;
}
_finished = _finish & & ! _input . size ( ) & & ! _num1 & & ! _num2 & & _lastPid ;
} catch ( mrw : : exception & x ) {
_finished = true ;
_success = false ;
if ( exc ) throw ;
return output ;
}
if ( _finished ) _success = true ;
return output ;
}
//------------------------------------------------------------------------------
mrw : : PartialExec & mrw : : PartialExec : : terminate ( ) throw ( ) {
: : kill ( _pid , SIGTERM ) ;
return * this ;
}
//------------------------------------------------------------------------------
mrw : : PartialExec & mrw : : PartialExec : : kill ( ) throw ( ) {
: : kill ( _pid , SIGKILL ) ;
return * this ;
}