From 184669188717673c4cb36698192fe8c14aa3af68 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Mon, 21 Dec 2009 17:56:32 +0000 Subject: Ok, Process has been updated. You now must specify flags as the first parameter of both constructors, this allows you to control which streams to bind to. To preserve the old behaviour, simply put Bu::Process::StdOut before your old first parameters. --- src/process.cpp | 241 +++++++++++++++++++++++++++++++++++++++------------- src/process.h | 51 ++++++++++- src/tests/procs.cpp | 28 +++--- 3 files changed, 245 insertions(+), 75 deletions(-) (limited to 'src') diff --git a/src/process.cpp b/src/process.cpp index 9340647..1c18626 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -14,14 +14,33 @@ #include #include -Bu::Process::Process( const char *sName, char *const argv[] ) : - iPid( 0 ) +#include +#include "bu/osx_compatibility.h" +#include "bu/win32_compatibility.h" +#include "bu/linux_compatibility.h" + +Bu::Process::Process( Flags eFlags, const char *sName, char *const argv[] ) : + iStdIn( -1 ), + iStdOut( -1 ), + iStdErr( -1 ), + iPid( 0 ), + iProcStatus( 0 ), + bBlocking( true ), + bStdOutEos( true ), + bStdErrEos( true ) { - gexec( sName, argv ); + gexec( eFlags, sName, argv ); } -Bu::Process::Process( const char *sName, const char *argv, ...) : - iPid( 0 ) +Bu::Process::Process( Flags eFlags, const char *sName, const char *argv, ...) : + iStdIn( -1 ), + iStdOut( -1 ), + iStdErr( -1 ), + iPid( 0 ), + iProcStatus( 0 ), + bBlocking( true ), + bStdOutEos( true ), + bStdErrEos( true ) { int iCnt = 0; va_list ap; @@ -39,7 +58,7 @@ Bu::Process::Process( const char *sName, const char *argv, ...) : list[iCnt+1] = NULL; va_end( ap ); - gexec( sName, (char *const *)list ); + gexec( eFlags, sName, (char *const *)list ); delete[] list; } @@ -48,18 +67,26 @@ Bu::Process::~Process() close(); } -void Bu::Process::gexec( const char *sName, char *const argv[] ) +void Bu::Process::gexec( Flags eFlags, const char *sName, char *const argv[] ) { int iaStdIn[2]; int iaStdOut[2]; -// int iaStdErr[2]; + int iaStdErr[2]; pipe( iaStdIn ); - pipe( iaStdOut ); -// pipe( iaStdErr ); + if( eFlags & StdOut ) + { + pipe( iaStdOut ); + iStdOut = iaStdOut[0]; + bStdOutEos = false; + } + if( eFlags & StdErr ) + { + pipe( iaStdErr ); + iStdErr = iaStdErr[0]; + bStdErrEos = false; + } iStdIn = iaStdIn[1]; - iStdOut = iaStdOut[0]; -// iStdErr = iaStdErr[0]; // fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); @@ -67,17 +94,25 @@ void Bu::Process::gexec( const char *sName, char *const argv[] ) if( iPid == 0 ) { ::close( iaStdIn[1] ); - ::close( iaStdOut[0] ); -// ::close( iaStdErr[0] ); dup2( iaStdIn[0], 0 ); - dup2( iaStdOut[1], 1 ); -// dup2( iaStdErr[1], 2 ); + if( eFlags & StdOut ) + { + ::close( iaStdOut[0] ); + dup2( iaStdOut[1], 1 ); + } + if( eFlags & StdErr ) + { + ::close( iaStdErr[0] ); + dup2( iaStdErr[1], 2 ); + } execvp( sName, argv ); throw Bu::ExceptionBase("Hey, execvp failed!"); } ::close( iaStdIn[0] ); - ::close( iaStdOut[1] ); -// ::close( iaStdErr[1] ); + if( eFlags & StdOut ) + ::close( iaStdOut[1] ); + if( eFlags & StdErr ) + ::close( iaStdErr[1] ); } void Bu::Process::close() @@ -85,63 +120,73 @@ void Bu::Process::close() if( iPid ) { ::close( iStdIn ); - ::close( iStdOut ); - int status; - waitpid( iPid, &status, 0 ); + if( iStdErr > -1 ) + ::close( iStdOut ); + if( iStdErr > -1 ) + ::close( iStdErr ); + waitpid( iPid, &iProcStatus, 0 ); iPid = 0; } } size_t Bu::Process::read( void *pBuf, size_t nBytes ) { - size_t nRead = TEMP_FAILURE_RETRY( ::read( iStdOut, pBuf, nBytes ) ); - if( nRead == 0 ) - { - close(); + if( bStdOutEos ) return 0; - } - return nRead; - /* - size_t iTotal = 0; - for(;;) + fd_set rfds; + FD_ZERO( &rfds ); + FD_SET( iStdOut, &rfds ); + struct timeval tv = {0, 0}; + if( ::bu_select( iStdOut+1, &rfds, NULL, NULL, &tv ) < 0 ) + throw Bu::ExceptionBase( strerror( errno ) ); + if( FD_ISSET( iStdOut, &rfds ) || bBlocking ) { - size_t iRet = ::read( iStdOut, (char *)pBuf+iTotal, nBytes-iTotal ); - if( iRet == 0 ) - return iTotal; - iTotal += iRet; - if( iTotal == nBytes ) - return iTotal; - } - */ - /* - size_t iTotal = 0; - fd_set rfs; - FD_ZERO( &rfs ); - for(;;) - { - if( waitpid( iPid, NULL, WNOHANG ) ) + ssize_t nRead = TEMP_FAILURE_RETRY( ::read( iStdOut, pBuf, nBytes ) ); + if( nRead == 0 ) { - printf("!!!wait failed!\n"); - size_t iRet = ::read( iStdOut, (char *)pBuf+iTotal, - nBytes-iTotal ); - iTotal += iRet; - return iTotal; + bStdOutEos = true; + checkClose(); + return 0; } - - FD_SET( iStdOut, &rfs ); - select( iStdOut+1, &rfs, NULL, &rfs, NULL ); - size_t iRet = ::read( iStdOut, (char *)pBuf+iTotal, nBytes-iTotal ); - printf("--read=%d / %d--\n", iRet, iTotal+iRet ); - iTotal += iRet; - if( iTotal == nBytes ) - return iTotal; + if( nRead < 0 ) + { + if( errno == EAGAIN ) + return 0; + throw Bu::ExceptionBase( strerror( errno ) ); + } + return nRead; } - */ + return 0; } size_t Bu::Process::readErr( void *pBuf, size_t nBytes ) { - return ::read( iStdErr, pBuf, nBytes ); + if( bStdErrEos ) + return 0; + fd_set rfds; + FD_ZERO( &rfds ); + FD_SET( iStdErr, &rfds ); + struct timeval tv = {0, 0}; + if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, &tv ) < 0 ) + throw Bu::ExceptionBase( strerror( errno ) ); + if( FD_ISSET( iStdErr, &rfds ) || bBlocking ) + { + ssize_t nRead = TEMP_FAILURE_RETRY( ::read( iStdErr, pBuf, nBytes ) ); + if( nRead == 0 ) + { + bStdErrEos = true; + checkClose(); + return 0; + } + if( nRead < 0 ) + { + if( errno == EAGAIN ) + return 0; + throw Bu::ExceptionBase( strerror( errno ) ); + } + return nRead; + } + return 0; } size_t Bu::Process::write( const void *pBuf, size_t nBytes ) @@ -213,9 +258,52 @@ bool Bu::Process::isBlocking() void Bu::Process::setBlocking( bool bBlocking ) { if( bBlocking ) + { fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )&(~O_NONBLOCK) ); + fcntl( iStdErr, F_SETFL, fcntl( iStdErr, F_GETFL, 0 )&(~O_NONBLOCK) ); + } else + { fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); + fcntl( iStdErr, F_SETFL, fcntl( iStdErr, F_GETFL, 0 )|O_NONBLOCK ); + } + this->bBlocking = bBlocking; +} + +void Bu::Process::select( bool &bStdOut, bool &bStdErr ) +{ + fd_set rfds; + FD_ZERO( &rfds ); + if( !bStdOutEos ) + FD_SET( iStdOut, &rfds ); + if( !bStdErrEos ) + FD_SET( iStdErr, &rfds ); + if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, NULL ) < 0 ) + throw Bu::ExceptionBase( strerror( errno ) ); + + if( FD_ISSET( iStdOut, &rfds ) ) + bStdOut = true; + else + bStdOut = false; + + if( FD_ISSET( iStdErr, &rfds ) ) + bStdErr = true; + else + bStdErr = false; +} + +bool Bu::Process::isRunning() +{ + return iPid != 0; +} + +void Bu::Process::ignoreStdErr() +{ + if( iStdErr == -1 ) + return; + ::close( iStdErr ); + iStdErr = -1; + bStdErrEos = true; } pid_t Bu::Process::getPid() @@ -223,3 +311,36 @@ pid_t Bu::Process::getPid() return iPid; } +bool Bu::Process::childExited() +{ + return WIFEXITED( iProcStatus ); +} + +int Bu::Process::childExitStatus() +{ + return WEXITSTATUS( iProcStatus ); +} + +bool Bu::Process::childSignaled() +{ + return WIFSIGNALED( iProcStatus ); +} + +int Bu::Process::childSignal() +{ + return WTERMSIG( iProcStatus ); +} + +bool Bu::Process::childCoreDumped() +{ + return WCOREDUMP( iProcStatus ); +} + +void Bu::Process::checkClose() +{ + if( bStdOutEos && bStdErrEos ) + { + close(); + } +} + diff --git a/src/process.h b/src/process.h index e04bb59..843ead1 100644 --- a/src/process.h +++ b/src/process.h @@ -24,8 +24,16 @@ namespace Bu class Process : public Bu::Stream { public: - Process( const char *sName, char *const argv[] ); - Process( const char *sName, const char *argv, ...); + enum Flags + { + StdOut = 0x01, + StdErr = 0x02, + Both = 0x03 + }; + + public: + Process( Flags eFlags, const char *sName, char *const argv[] ); + Process( Flags eFlags, const char *sName, const char *argv, ...); virtual ~Process(); virtual void close(); @@ -53,6 +61,11 @@ namespace Bu virtual bool isBlocking(); virtual void setBlocking( bool bBlocking=true ); + void select( bool &bStdOut, bool &bStdErr ); + + bool isRunning(); + void ignoreStdErr(); + /** * Returns the pid of the child process, or zero if there is no * currently running child. Note that a read operation must be @@ -60,13 +73,45 @@ namespace Bu */ pid_t getPid(); + /** + * Returns true if the child exited normally (by calling exit or + * returning from main). + */ + bool childExited(); + + /** + * Returns the 8 bit integer value returned from the child program if + * childExited returned true. + */ + int childExitStatus(); + + /** + * Returns true if the child exited because of a signal. + */ + bool childSignaled(); + + /** + * Returns the signal ID if the childSignaled return true. + */ + int childSignal(); + + /** + * Returns true if the child left a core dump behind when it exited. + */ + bool childCoreDumped(); + private: int iStdIn; int iStdOut; int iStdErr; pid_t iPid; + int iProcStatus; + bool bBlocking; + bool bStdOutEos; + bool bStdErrEos; - void gexec( const char *sName, char *const argv[] ); + void gexec( Flags eFlags, const char *sName, char *const argv[] ); + void checkClose(); }; } diff --git a/src/tests/procs.cpp b/src/tests/procs.cpp index 4f177c9..d914324 100644 --- a/src/tests/procs.cpp +++ b/src/tests/procs.cpp @@ -11,21 +11,25 @@ int main() { - //Bu::Process p( argv[1], argv+1 ); - Bu::Process p("mplayer", "mplayer", "dvd://", "-framedrop", - "-ao", "null", "-nosound", "-vf", "framestep=I,cropdetect", "-sstep", - "197", NULL ); + Bu::Process p( Bu::Process::Both, "/bin/bash", "/bin/bash", "-c", "echo Hello 1>&2; echo StdOut; sleep 1; echo Yup; echo Yup 1>&2", NULL ); char buf[1000]; - for(;;) + while( !p.isEos() ) { - int iSize = p.read( buf, 1000 ); - printf("::read=%d::\n", iSize ); - if( iSize == 0 ) - break; - fwrite( buf, iSize, 1, stdout ); - if( iSize < 1000 ) - break; + bool out, err; + p.select( out, err ); + if( out ) + { + int iSize = p.read( buf, 1000 ); + printf("::read=%d::\n", iSize ); + fwrite( buf, iSize, 1, stdout ); + } + if( err ) + { + int iSize = p.readErr( buf, 1000 ); + printf("::readErr=%d::\n", iSize ); + fwrite( buf, iSize, 1, stdout ); + } } return 0; -- cgit v1.2.3