/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.8 2005 / 11 / 29 12 : 39 : 42 marc
make it compilable with gcc 4.0 .2 and newer doxygen
Revision 1.7 2005 / 04 / 20 18 : 12 : 55 marc
added kill ( ) for PartialExec
Revision 1.6 2005 / 04 / 19 18 : 48 : 00 marc
new feature PartialExec
Revision 1.5 2004 / 12 / 14 20 : 30 : 09 marc
added possibility to pass string to stdin of child process
Revision 1.4 2004 / 10 / 07 09 : 27 : 01 marc
errors in documentation
Revision 1.3 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
*/
# ifndef __MRW_EXEC_HPP__
# define __MRW_EXEC_HPP__
# include <string>
# include <list>
# include <mrw/exception.hpp>
# include <mrw/unistd.hpp>
namespace mrw {
/** @defgroup CmdExec Execute UNIX Commands
There is no easy way to safely execute UNIX commands and to
return the output of the callee to the caller . @ c system ist
first of all known to be unsafe , because it opens a shell , and
second there is no way to transfer the output back to the
caller . On the other hand , starting a new process with @ c fork
and @ c exec and passing the output of the callee to the caller
using pipes is quite complex and needs much more than one simple
line of code . This is the gap that is filled with this command
execution classes . There ' s a class for the command to be
executed and a class for the execution of the command .
Forking a subprocess and evaluating the result becomes so easy :
@ code
try {
// execute the command: /bin/ls -l /tmp
mrw : : Exec ls =
( mrw : : Cmd ( " /bin/ls " ) , " -l " , " /tmp " ) . execute ( false ) ;
// evaluate the result
if ( ls . success ( ) )
std : : cout < < " Execution successful, result was: " < < std : : endl ;
else
std : : cerr < < " Error in execution, error was: " < < std : : endl ;
std : : cout < < ls . result ( ) < < std : : endl ;
std : : cerr < < ls . error ( ) < < std : : endl ;
} catch ( ExecutionFailedExc & x ) {
// a fatal execution error occurred
// you can trace x.what() and x.stacktrace()
}
@ endcode
It is also possible to pass an @ c stdin input argument to the
subprocess called :
@ code
try {
mrw : : Exec cat = mrw : : Cmd ( " /bin/cat " )
. execute ( " this is passed to stdin " ) ;
// "cat" passes all from stdin to stdout, therefore:
assert ( cat . result ( ) = = " this is passed to stdin " ) ;
} catch ( . . . ) { } // ignore
@ endcode */
//@{
class Cmd ;
//============================================================================
/** @brief Exception: Execution of command failed.
@ pre \ # include < mrw / exec . hpp >
This exception is thrown , if the exection of a command in
mrw : : Exec is failed . That means , it was not possible to fork or
to create the necessary pipes , or the command executing process
terminated with an error . In the last case , you can access the
error stream from @ c stderr respectively @ c cerr with method
mrw : : Exec : : error ( ) . */
class ExecutionFailedExc : public mrw : : exception {
public :
ExecutionFailedExc ( const std : : string & , const std : : string & )
throw ( std : : bad_exception ) ;
virtual ~ ExecutionFailedExc ( ) throw ( ) { }
virtual const char * what ( ) const throw ( ) { return _what . c_str ( ) ; }
private :
std : : string _what ;
} ;
//============================================================================
/** @brief Execute a command in a new process.
@ pre \ # include < mrw / exec . hpp >
This class handles the execution of a command in a new process
and returns the two streams @ c cout and @ c cerr , also known as @ c
stderr and @ c stdout .
Method @ c execute can optionally also take a string parameter
that is passed to @ c stdin of the child process .
There are different ways of usage for this class . A simple way ,
one line of code , to get only the resulting stream ( no error )
is :
@ code
string stdout =
( mrw : : Cmd ( " /bin/ls " ) , " -l " , " /tmp " ) . execute ( false ) . result ( ) ;
@ endcode
If you need not only the resulting @ c stdout stream , but also
the error stream @ c stderr , then you need to store the result :
@ code
mrw : : Exec ls =
( mrw : : Cmd ( " /bin/ls " ) , " -l " , " /tmp " ) . execute ( false ) ;
if ( ! ls ) . . . ; // command termianted with error
// ls.result() contains stdout
// ls.error() contains stderr
@ endcode
@ note Please note that the command execution may throw an exception . */
class Exec {
//................................................................ methods
public :
/** @brief Create an executor given a command.
Construction without passing a command is not possible . */
Exec ( const mrw : : Cmd & ) throw ( std : : bad_exception ) ;
Exec ( const mrw : : Exec & ) throw ( std : : bad_exception ) ;
~ Exec ( ) throw ( ) ;
Exec & operator = ( const mrw : : Exec & ) throw ( std : : bad_exception ) ;
/** @brief Execute the command.
@ param exc
- @ c true throw an exception if return status is not zero
- @ c false throw only an exception in case of a fatal error
@ throw ExecutionFailedExc is thrown if
- fork fails
- creation or setup of pipes failed
- if given parameter is @ c true ( the default ) also if the
executed program terminates with an error */
Exec & execute ( bool exc = true ) throw ( std : : exception ) ;
/** @brief Execute the command, pass @c stdin.
@ param input Input that is passed to @ c stdin of the child process .
@ param exc
- @ c true throw an exception if return status is not zero
- @ c false throw only an exception in case of a fatal error
@ throw ExecutionFailedExc is thrown if
- fork fails
- creation or setup of pipes failed
- if given parameter is @ c true ( the default ) also if the
executed program terminates with an error */
Exec & execute ( const std : : string & input , bool exc = true )
throw ( std : : exception ) ;
/** @brief Execute the command, pass @c stdin.
@ param input Input that is passed to @ c stdin of the child process .
@ param exc
- @ c true throw an exception if return status is not zero
- @ c false throw only an exception in case of a fatal error
@ throw ExecutionFailedExc is thrown if
- fork fails
- creation or setup of pipes failed
- if given parameter is @ c true ( the default ) also if the
executed program terminates with an error */
Exec & execute ( char const * const input , bool exc = true )
throw ( std : : exception ) {
return execute ( std : : string ( input ) , exc ) ;
}
/** @brief Executes the command if not done, streams @c stdout
into a string
If the command has not yet been executed successfully , it is
first executed , then the @ c stdout output of the called
program is appended to the string .
@ throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status . */
Exec & operator > > ( std : : string & ) throw ( std : : exception ) ;
/** @brief Executes the command if not done, returns @c stdout as string
If the command has not yet been executed successfully , it is
first executed , then the @ c stdout output of the called
program is returned .
@ return @ c stdout of the called program
@ throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status . */
operator std : : string & ( ) throw ( std : : exception ) ;
/** @return
- @ c true if the last execution was successful
- @ c false if the last execution failed or the command was
never executed */
operator bool ( ) throw ( std : : bad_exception ) ;
/** @brief Executes the command if not done, returns @c stdout as string
If the command has not yet been executed successfully , it is
first executed , then the @ c stdout output of the called
program is returned .
@ return @ c stdout of the called program
@ throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status . */
std : : string & result ( ) throw ( std : : exception ) ;
/** @brief Executes the command if not done, returns @c stderr as string
If the command has not yet been executed successfully , it is
first executed , then the @ c stderr error output of the called
program is returned .
@ return @ c stderr of the called program
@ throw ExecutionFailedExc in case of any failure or if the
executed program does not return a zero exit status . */
std : : string & error ( ) throw ( std : : exception ) ;
/** @return
- @ c true if the last execution was successful
- @ c false if the last execution failed or the command was
never executed */
bool success ( ) throw ( std : : bad_exception ) ;
//................................................................ methods
private :
Exec ( ) ; // no default constructor
//.............................................................. variables
private :
friend class PartialExec ; // don't want the variables protected
mrw : : Cmd * _cmd ;
std : : string _res , _err ;
bool _success ;
} ;
//============================================================================
/** @brief Execute a UNIX program in non blocking parts.
@ pre \ # include < mrw / exec . hpp >
A given UNIX command is executed , but the class does not wait
until it is finished , instead it gives back the control to the
caller . This behaviour is achieved using non blocking
communication . But the caller is responsible to retrieve all
information from the client , and if necessary to close the input
pipe of the client executable program . Therefore you have to
give back control from time to time , normally this is doen in a
@ c while loop , where you can execute also different thing ,
e . g . update a display of the result or similar .
With this class , you can communicate with a child process , and
do other things at the same time , without the need for multi
threading .
Execution of a program works the following way :
- do not use execute ( ) ( otherwise the behaviours is identical
to class mrw : : Exec , you gain nothing , but also loose nothing )
- use start ( ) to start the external program
- use start ( ) or @ c start ( false ) if you don ' t want to pass
input to the child process
- use @ c start ( true ) if you want to pass input to the
child process
- if you called @ c start ( true ) , call finish ( ) if you have no more
input to send to the child process ( it ' s like an end - of - file )
- the execution is not terminated , before finished ( ) returns
@ c true
- while finished ( ) is false , subsequently call read ( ) to read
the output of the child process
@ warning After calling finish ( ) , or if you did not call
start ( ) with parameter @ c true , it is forbidden to
pass anything but an empty string as first parameter to
read ( ) ! Anything else is a programming error and
results in an assertion failure and a core dump !
@ note If your program seems to hang , check if you call finish ( )
correctly !
Here an example :
@ code
mrw : : PartialExec exec = mrw : : Cmd ( " /bin/cat " ) . start ( true ) ;
std : : string res = exec . read ( " This is a test \n " ) . first ;
res + = exec . read ( " This is another test \n " ) . first ;
exec . finish ( ) ; // close the input pipe of @c cat
while ( ! exec . finished ( ) ) res + = exec . read ( ) . first ;
@ endcode */
class PartialExec : public Exec {
//................................................................ methods
public :
/** @brief Create an executor given a command.
Construction without passing a command is not possible . */
PartialExec ( const mrw : : Cmd & ) throw ( std : : bad_exception ) ;
/** @brief Copy construction invalidates the original object.
All opened pipes ( opened with start ( ) ) are lost in the
original object and are then owned by the new object . */
PartialExec ( mrw : : PartialExec & ) throw ( std : : bad_exception ) ;
/** @brief Copy construction invalidates the original object.
@ copydoc PartialExec ( mrw : : PartialExec & )
@ warning @ c const for the argument is a fake ! It is casted away !
@ param e @ b Warning : const is casted away ! */
PartialExec ( const mrw : : PartialExec & e ) throw ( std : : bad_exception ) ;
/** @brief Assignment invalidates the original object.
@ copydoc PartialExec ( mrw : : PartialExec & ) */
PartialExec & operator = ( mrw : : PartialExec & ) throw ( std : : bad_exception ) ;
/** @brief Close the input pipe of the child process.
If start ( ) is called with argument @ c false , then you
can pass input to @ c stdin of the child process , but you @ b
must call this method , after passing the last input
string . Otherwise , the child ' s input pipe won ' t be closed ,
the child process does not stop waiting for more input ! If
your program seems to hang , check if you call finish ( )
correctly ! */
PartialExec & finish ( ) throw ( ) ;
/** @brief Check if there's more data left to read().
@ return @ c true if the child process has finished and all
data is read . */
bool finished ( ) throw ( ) ;
/** @brief Start a new child process.
At most one child process can run at the same time .
@ throw mrw : : runtime_error if a previous child has not finished ( ) yet
@ throw mrw : : ExecutionFailedExc if the child process cannot be started
@ param useInput
- @ c true if input will be sent to the child ' s @ c stdin
- pass all input in the first parameter of read ( )
- finish ( ) must be called when all input is sent
- @ c false if no input is sent to the child ' s @ c stdin
- the first parameter of read must allways be passed an
empty string */
PartialExec & start ( bool useInput = false ) throw ( std : : exception ) ;
/** @brief Read from the subprocess, optionally pass an @c input to
@ c stdin of the subprocess .
@ param input a string to pass to the child processes @ c stdin
@ param exc
- @ c true throw an exception if return status is not zero
- @ c false throw only an exception in case of a fatal error
@ return a pair containing the last read @ c stdout and @ c stderr
of the child
@ throw ExecutionFailedExc is thrown if
- fork fails
- creation or setup of pipes failed
- if given parameter is @ c true ( the default ) also if the
executed program terminates with an error
@ note If start ( ) was not called with parameter @ c true , then
@ c input must always be an empty string !
@ pre start ( ) was called */
std : : pair < std : : string , std : : string > read ( const std : : string & input = " " ,
bool exc = true )
throw ( std : : exception ) ;
/// Terminates a running job by sending @c SIGTERM to the child process.
PartialExec & terminate ( ) throw ( ) ;
/// Kills a running job by sending @c SIGKILL to the child process.
PartialExec & kill ( ) throw ( ) ;
//................................................................ methods
private :
PartialExec ( ) ; // no default constructor
//.............................................................. variables
private :
bool _finished ;
bool _finish ;
std : : auto_ptr < mrw : : Pipe > _stdIn , _stdOut , _stdErr ;
std : : string _input ;
int _num0 , _num1 , _num2 , _lastPid , _pid ;
} ;
//============================================================================
/** @brief A system command to be executed
@ pre \ # include < mrw / exec . hpp >
This class is used in conjunction with mrw : : Exec . It must be
initialized with the command name , then the command parameters
are appended either with commas , or by streaming them into the
command , whatever you like .
You can stream the data into the class :
@ code
mrw : : Cmd ls ( " /bin/ls " ) ; // the command to execute is: /bin/ls
ls < < " -l " < < " /tmp " ; // the command is now: /bin/ls -l /tmp
@ endcode
Or you can setup your command with commas :
@ code
mrw : : Cmd ls = ( mrw : : Cmd ( / bin / ls ) , " -l " , " /tmp " ) ;
@ endcode
*/
class Cmd {
public :
/** @brief Create a command given the name of the executable
@ param command the name of the program to execute ( no parameter )
@ note There is no default constructor . */
Cmd ( const std : : string & command ) throw ( std : : bad_exception ) ;
/** @brief Append a parameter to a command
@ param param a parameter & nbsp ; / commandline argument
to append to the command */
Cmd & operator , ( const std : : string & param ) throw ( std : : bad_exception ) ;
/** @brief Append a parameter to a command
@ param param a parameter & nbsp ; / commandline argument
to append to the command */
Cmd & operator < < ( const std : : string & param ) throw ( std : : bad_exception ) ;
/** @return the command including parameter */
operator std : : string ( ) const throw ( std : : bad_exception ) ;
/** @return a mrw::Exec that's constructed with this class */
operator mrw : : Exec ( ) const throw ( std : : bad_exception ) ;
/** @return a mrw::PartialExec that's constructed with this class */
operator mrw : : PartialExec ( ) const throw ( std : : bad_exception ) ;
/** @brief Create a mrw::Exec and execute a child process.
@ see Exec : : execute ( bool ) */
Exec execute ( bool exc = true ) const throw ( std : : exception ) ;
/** @brief Create a mrw::Exec and execute a child process.
@ see Exec : : execute ( const std : : string & , bool ) */
Exec execute ( const std : : string & input , bool exc = true ) const
throw ( std : : exception ) ;
/** @brief Create a mrw::Exec and execute a child process.
@ see Exec : : execute ( char const * const , bool ) */
Exec execute ( char const * const input , bool exc = true ) const
throw ( std : : exception ) {
return execute ( std : : string ( input ) , exc ) ;
}
/** @brief Create a new mrw::PartialExec and start a new child process.
@ see PartialExec : : start ( bool ) */
PartialExec start ( bool useInput = false ) const throw ( std : : exception ) ;
private :
// Exec and PartialExec are allowed to call @c path() and @c args().
friend class Exec ;
friend class PartialExec ;
Cmd ( ) ; // No default constructor.
const char * path ( ) const throw ( std : : bad_exception ) ;
char * * args ( ) const throw ( std : : bad_exception ) ;
typedef std : : list < std : : string > ArgList ;
ArgList _cmd ;
} ;
//@}
}
# endif