diff options
Diffstat (limited to '')
| -rw-r--r-- | src/process.cpp | 241 | ||||
| -rw-r--r-- | src/process.h | 51 | ||||
| -rw-r--r-- | src/tests/procs.cpp | 28 |
3 files changed, 245 insertions, 75 deletions
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 @@ | |||
| 14 | #include <fcntl.h> | 14 | #include <fcntl.h> |
| 15 | #include <errno.h> | 15 | #include <errno.h> |
| 16 | 16 | ||
| 17 | Bu::Process::Process( const char *sName, char *const argv[] ) : | 17 | #include <sys/select.h> |
| 18 | iPid( 0 ) | 18 | #include "bu/osx_compatibility.h" |
| 19 | #include "bu/win32_compatibility.h" | ||
| 20 | #include "bu/linux_compatibility.h" | ||
| 21 | |||
| 22 | Bu::Process::Process( Flags eFlags, const char *sName, char *const argv[] ) : | ||
| 23 | iStdIn( -1 ), | ||
| 24 | iStdOut( -1 ), | ||
| 25 | iStdErr( -1 ), | ||
| 26 | iPid( 0 ), | ||
| 27 | iProcStatus( 0 ), | ||
| 28 | bBlocking( true ), | ||
| 29 | bStdOutEos( true ), | ||
| 30 | bStdErrEos( true ) | ||
| 19 | { | 31 | { |
| 20 | gexec( sName, argv ); | 32 | gexec( eFlags, sName, argv ); |
| 21 | } | 33 | } |
| 22 | 34 | ||
| 23 | Bu::Process::Process( const char *sName, const char *argv, ...) : | 35 | Bu::Process::Process( Flags eFlags, const char *sName, const char *argv, ...) : |
| 24 | iPid( 0 ) | 36 | iStdIn( -1 ), |
| 37 | iStdOut( -1 ), | ||
| 38 | iStdErr( -1 ), | ||
| 39 | iPid( 0 ), | ||
| 40 | iProcStatus( 0 ), | ||
| 41 | bBlocking( true ), | ||
| 42 | bStdOutEos( true ), | ||
| 43 | bStdErrEos( true ) | ||
| 25 | { | 44 | { |
| 26 | int iCnt = 0; | 45 | int iCnt = 0; |
| 27 | va_list ap; | 46 | va_list ap; |
| @@ -39,7 +58,7 @@ Bu::Process::Process( const char *sName, const char *argv, ...) : | |||
| 39 | list[iCnt+1] = NULL; | 58 | list[iCnt+1] = NULL; |
| 40 | va_end( ap ); | 59 | va_end( ap ); |
| 41 | 60 | ||
| 42 | gexec( sName, (char *const *)list ); | 61 | gexec( eFlags, sName, (char *const *)list ); |
| 43 | delete[] list; | 62 | delete[] list; |
| 44 | } | 63 | } |
| 45 | 64 | ||
| @@ -48,18 +67,26 @@ Bu::Process::~Process() | |||
| 48 | close(); | 67 | close(); |
| 49 | } | 68 | } |
| 50 | 69 | ||
| 51 | void Bu::Process::gexec( const char *sName, char *const argv[] ) | 70 | void Bu::Process::gexec( Flags eFlags, const char *sName, char *const argv[] ) |
| 52 | { | 71 | { |
| 53 | int iaStdIn[2]; | 72 | int iaStdIn[2]; |
| 54 | int iaStdOut[2]; | 73 | int iaStdOut[2]; |
| 55 | // int iaStdErr[2]; | 74 | int iaStdErr[2]; |
| 56 | pipe( iaStdIn ); | 75 | pipe( iaStdIn ); |
| 57 | pipe( iaStdOut ); | 76 | if( eFlags & StdOut ) |
| 58 | // pipe( iaStdErr ); | 77 | { |
| 78 | pipe( iaStdOut ); | ||
| 79 | iStdOut = iaStdOut[0]; | ||
| 80 | bStdOutEos = false; | ||
| 81 | } | ||
| 82 | if( eFlags & StdErr ) | ||
| 83 | { | ||
| 84 | pipe( iaStdErr ); | ||
| 85 | iStdErr = iaStdErr[0]; | ||
| 86 | bStdErrEos = false; | ||
| 87 | } | ||
| 59 | 88 | ||
| 60 | iStdIn = iaStdIn[1]; | 89 | iStdIn = iaStdIn[1]; |
| 61 | iStdOut = iaStdOut[0]; | ||
| 62 | // iStdErr = iaStdErr[0]; | ||
| 63 | 90 | ||
| 64 | // fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); | 91 | // fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); |
| 65 | 92 | ||
| @@ -67,17 +94,25 @@ void Bu::Process::gexec( const char *sName, char *const argv[] ) | |||
| 67 | if( iPid == 0 ) | 94 | if( iPid == 0 ) |
| 68 | { | 95 | { |
| 69 | ::close( iaStdIn[1] ); | 96 | ::close( iaStdIn[1] ); |
| 70 | ::close( iaStdOut[0] ); | ||
| 71 | // ::close( iaStdErr[0] ); | ||
| 72 | dup2( iaStdIn[0], 0 ); | 97 | dup2( iaStdIn[0], 0 ); |
| 73 | dup2( iaStdOut[1], 1 ); | 98 | if( eFlags & StdOut ) |
| 74 | // dup2( iaStdErr[1], 2 ); | 99 | { |
| 100 | ::close( iaStdOut[0] ); | ||
| 101 | dup2( iaStdOut[1], 1 ); | ||
| 102 | } | ||
| 103 | if( eFlags & StdErr ) | ||
| 104 | { | ||
| 105 | ::close( iaStdErr[0] ); | ||
| 106 | dup2( iaStdErr[1], 2 ); | ||
| 107 | } | ||
| 75 | execvp( sName, argv ); | 108 | execvp( sName, argv ); |
| 76 | throw Bu::ExceptionBase("Hey, execvp failed!"); | 109 | throw Bu::ExceptionBase("Hey, execvp failed!"); |
| 77 | } | 110 | } |
| 78 | ::close( iaStdIn[0] ); | 111 | ::close( iaStdIn[0] ); |
| 79 | ::close( iaStdOut[1] ); | 112 | if( eFlags & StdOut ) |
| 80 | // ::close( iaStdErr[1] ); | 113 | ::close( iaStdOut[1] ); |
| 114 | if( eFlags & StdErr ) | ||
| 115 | ::close( iaStdErr[1] ); | ||
| 81 | } | 116 | } |
| 82 | 117 | ||
| 83 | void Bu::Process::close() | 118 | void Bu::Process::close() |
| @@ -85,63 +120,73 @@ void Bu::Process::close() | |||
| 85 | if( iPid ) | 120 | if( iPid ) |
| 86 | { | 121 | { |
| 87 | ::close( iStdIn ); | 122 | ::close( iStdIn ); |
| 88 | ::close( iStdOut ); | 123 | if( iStdErr > -1 ) |
| 89 | int status; | 124 | ::close( iStdOut ); |
| 90 | waitpid( iPid, &status, 0 ); | 125 | if( iStdErr > -1 ) |
| 126 | ::close( iStdErr ); | ||
| 127 | waitpid( iPid, &iProcStatus, 0 ); | ||
| 91 | iPid = 0; | 128 | iPid = 0; |
| 92 | } | 129 | } |
| 93 | } | 130 | } |
| 94 | 131 | ||
| 95 | size_t Bu::Process::read( void *pBuf, size_t nBytes ) | 132 | size_t Bu::Process::read( void *pBuf, size_t nBytes ) |
| 96 | { | 133 | { |
| 97 | size_t nRead = TEMP_FAILURE_RETRY( ::read( iStdOut, pBuf, nBytes ) ); | 134 | if( bStdOutEos ) |
| 98 | if( nRead == 0 ) | ||
| 99 | { | ||
| 100 | close(); | ||
| 101 | return 0; | 135 | return 0; |
| 102 | } | 136 | fd_set rfds; |
| 103 | return nRead; | 137 | FD_ZERO( &rfds ); |
| 104 | /* | 138 | FD_SET( iStdOut, &rfds ); |
| 105 | size_t iTotal = 0; | 139 | struct timeval tv = {0, 0}; |
| 106 | for(;;) | 140 | if( ::bu_select( iStdOut+1, &rfds, NULL, NULL, &tv ) < 0 ) |
| 141 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 142 | if( FD_ISSET( iStdOut, &rfds ) || bBlocking ) | ||
| 107 | { | 143 | { |
| 108 | size_t iRet = ::read( iStdOut, (char *)pBuf+iTotal, nBytes-iTotal ); | 144 | ssize_t nRead = TEMP_FAILURE_RETRY( ::read( iStdOut, pBuf, nBytes ) ); |
| 109 | if( iRet == 0 ) | 145 | if( nRead == 0 ) |
| 110 | return iTotal; | ||
| 111 | iTotal += iRet; | ||
| 112 | if( iTotal == nBytes ) | ||
| 113 | return iTotal; | ||
| 114 | } | ||
| 115 | */ | ||
| 116 | /* | ||
| 117 | size_t iTotal = 0; | ||
| 118 | fd_set rfs; | ||
| 119 | FD_ZERO( &rfs ); | ||
| 120 | for(;;) | ||
| 121 | { | ||
| 122 | if( waitpid( iPid, NULL, WNOHANG ) ) | ||
| 123 | { | 146 | { |
| 124 | printf("!!!wait failed!\n"); | 147 | bStdOutEos = true; |
| 125 | size_t iRet = ::read( iStdOut, (char *)pBuf+iTotal, | 148 | checkClose(); |
| 126 | nBytes-iTotal ); | 149 | return 0; |
| 127 | iTotal += iRet; | ||
| 128 | return iTotal; | ||
| 129 | } | 150 | } |
| 130 | 151 | if( nRead < 0 ) | |
| 131 | FD_SET( iStdOut, &rfs ); | 152 | { |
| 132 | select( iStdOut+1, &rfs, NULL, &rfs, NULL ); | 153 | if( errno == EAGAIN ) |
| 133 | size_t iRet = ::read( iStdOut, (char *)pBuf+iTotal, nBytes-iTotal ); | 154 | return 0; |
| 134 | printf("--read=%d / %d--\n", iRet, iTotal+iRet ); | 155 | throw Bu::ExceptionBase( strerror( errno ) ); |
| 135 | iTotal += iRet; | 156 | } |
| 136 | if( iTotal == nBytes ) | 157 | return nRead; |
| 137 | return iTotal; | ||
| 138 | } | 158 | } |
| 139 | */ | 159 | return 0; |
| 140 | } | 160 | } |
| 141 | 161 | ||
| 142 | size_t Bu::Process::readErr( void *pBuf, size_t nBytes ) | 162 | size_t Bu::Process::readErr( void *pBuf, size_t nBytes ) |
| 143 | { | 163 | { |
| 144 | return ::read( iStdErr, pBuf, nBytes ); | 164 | if( bStdErrEos ) |
| 165 | return 0; | ||
| 166 | fd_set rfds; | ||
| 167 | FD_ZERO( &rfds ); | ||
| 168 | FD_SET( iStdErr, &rfds ); | ||
| 169 | struct timeval tv = {0, 0}; | ||
| 170 | if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, &tv ) < 0 ) | ||
| 171 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 172 | if( FD_ISSET( iStdErr, &rfds ) || bBlocking ) | ||
| 173 | { | ||
| 174 | ssize_t nRead = TEMP_FAILURE_RETRY( ::read( iStdErr, pBuf, nBytes ) ); | ||
| 175 | if( nRead == 0 ) | ||
| 176 | { | ||
| 177 | bStdErrEos = true; | ||
| 178 | checkClose(); | ||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | if( nRead < 0 ) | ||
| 182 | { | ||
| 183 | if( errno == EAGAIN ) | ||
| 184 | return 0; | ||
| 185 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 186 | } | ||
| 187 | return nRead; | ||
| 188 | } | ||
| 189 | return 0; | ||
| 145 | } | 190 | } |
| 146 | 191 | ||
| 147 | size_t Bu::Process::write( const void *pBuf, size_t nBytes ) | 192 | size_t Bu::Process::write( const void *pBuf, size_t nBytes ) |
| @@ -213,9 +258,52 @@ bool Bu::Process::isBlocking() | |||
| 213 | void Bu::Process::setBlocking( bool bBlocking ) | 258 | void Bu::Process::setBlocking( bool bBlocking ) |
| 214 | { | 259 | { |
| 215 | if( bBlocking ) | 260 | if( bBlocking ) |
| 261 | { | ||
| 216 | fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )&(~O_NONBLOCK) ); | 262 | fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )&(~O_NONBLOCK) ); |
| 263 | fcntl( iStdErr, F_SETFL, fcntl( iStdErr, F_GETFL, 0 )&(~O_NONBLOCK) ); | ||
| 264 | } | ||
| 217 | else | 265 | else |
| 266 | { | ||
| 218 | fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); | 267 | fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); |
| 268 | fcntl( iStdErr, F_SETFL, fcntl( iStdErr, F_GETFL, 0 )|O_NONBLOCK ); | ||
| 269 | } | ||
| 270 | this->bBlocking = bBlocking; | ||
| 271 | } | ||
| 272 | |||
| 273 | void Bu::Process::select( bool &bStdOut, bool &bStdErr ) | ||
| 274 | { | ||
| 275 | fd_set rfds; | ||
| 276 | FD_ZERO( &rfds ); | ||
| 277 | if( !bStdOutEos ) | ||
| 278 | FD_SET( iStdOut, &rfds ); | ||
| 279 | if( !bStdErrEos ) | ||
| 280 | FD_SET( iStdErr, &rfds ); | ||
| 281 | if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, NULL ) < 0 ) | ||
| 282 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
| 283 | |||
| 284 | if( FD_ISSET( iStdOut, &rfds ) ) | ||
| 285 | bStdOut = true; | ||
| 286 | else | ||
| 287 | bStdOut = false; | ||
| 288 | |||
| 289 | if( FD_ISSET( iStdErr, &rfds ) ) | ||
| 290 | bStdErr = true; | ||
| 291 | else | ||
| 292 | bStdErr = false; | ||
| 293 | } | ||
| 294 | |||
| 295 | bool Bu::Process::isRunning() | ||
| 296 | { | ||
| 297 | return iPid != 0; | ||
| 298 | } | ||
| 299 | |||
| 300 | void Bu::Process::ignoreStdErr() | ||
| 301 | { | ||
| 302 | if( iStdErr == -1 ) | ||
| 303 | return; | ||
| 304 | ::close( iStdErr ); | ||
| 305 | iStdErr = -1; | ||
| 306 | bStdErrEos = true; | ||
| 219 | } | 307 | } |
| 220 | 308 | ||
| 221 | pid_t Bu::Process::getPid() | 309 | pid_t Bu::Process::getPid() |
| @@ -223,3 +311,36 @@ pid_t Bu::Process::getPid() | |||
| 223 | return iPid; | 311 | return iPid; |
| 224 | } | 312 | } |
| 225 | 313 | ||
| 314 | bool Bu::Process::childExited() | ||
| 315 | { | ||
| 316 | return WIFEXITED( iProcStatus ); | ||
| 317 | } | ||
| 318 | |||
| 319 | int Bu::Process::childExitStatus() | ||
| 320 | { | ||
| 321 | return WEXITSTATUS( iProcStatus ); | ||
| 322 | } | ||
| 323 | |||
| 324 | bool Bu::Process::childSignaled() | ||
| 325 | { | ||
| 326 | return WIFSIGNALED( iProcStatus ); | ||
| 327 | } | ||
| 328 | |||
| 329 | int Bu::Process::childSignal() | ||
| 330 | { | ||
| 331 | return WTERMSIG( iProcStatus ); | ||
| 332 | } | ||
| 333 | |||
| 334 | bool Bu::Process::childCoreDumped() | ||
| 335 | { | ||
| 336 | return WCOREDUMP( iProcStatus ); | ||
| 337 | } | ||
| 338 | |||
| 339 | void Bu::Process::checkClose() | ||
| 340 | { | ||
| 341 | if( bStdOutEos && bStdErrEos ) | ||
| 342 | { | ||
| 343 | close(); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
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 | |||
| 24 | class Process : public Bu::Stream | 24 | class Process : public Bu::Stream |
| 25 | { | 25 | { |
| 26 | public: | 26 | public: |
| 27 | Process( const char *sName, char *const argv[] ); | 27 | enum Flags |
| 28 | Process( const char *sName, const char *argv, ...); | 28 | { |
| 29 | StdOut = 0x01, | ||
| 30 | StdErr = 0x02, | ||
| 31 | Both = 0x03 | ||
| 32 | }; | ||
| 33 | |||
| 34 | public: | ||
| 35 | Process( Flags eFlags, const char *sName, char *const argv[] ); | ||
| 36 | Process( Flags eFlags, const char *sName, const char *argv, ...); | ||
| 29 | virtual ~Process(); | 37 | virtual ~Process(); |
| 30 | 38 | ||
| 31 | virtual void close(); | 39 | virtual void close(); |
| @@ -53,6 +61,11 @@ namespace Bu | |||
| 53 | virtual bool isBlocking(); | 61 | virtual bool isBlocking(); |
| 54 | virtual void setBlocking( bool bBlocking=true ); | 62 | virtual void setBlocking( bool bBlocking=true ); |
| 55 | 63 | ||
| 64 | void select( bool &bStdOut, bool &bStdErr ); | ||
| 65 | |||
| 66 | bool isRunning(); | ||
| 67 | void ignoreStdErr(); | ||
| 68 | |||
| 56 | /** | 69 | /** |
| 57 | * Returns the pid of the child process, or zero if there is no | 70 | * Returns the pid of the child process, or zero if there is no |
| 58 | * currently running child. Note that a read operation must be | 71 | * currently running child. Note that a read operation must be |
| @@ -60,13 +73,45 @@ namespace Bu | |||
| 60 | */ | 73 | */ |
| 61 | pid_t getPid(); | 74 | pid_t getPid(); |
| 62 | 75 | ||
| 76 | /** | ||
| 77 | * Returns true if the child exited normally (by calling exit or | ||
| 78 | * returning from main). | ||
| 79 | */ | ||
| 80 | bool childExited(); | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Returns the 8 bit integer value returned from the child program if | ||
| 84 | * childExited returned true. | ||
| 85 | */ | ||
| 86 | int childExitStatus(); | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Returns true if the child exited because of a signal. | ||
| 90 | */ | ||
| 91 | bool childSignaled(); | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Returns the signal ID if the childSignaled return true. | ||
| 95 | */ | ||
| 96 | int childSignal(); | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Returns true if the child left a core dump behind when it exited. | ||
| 100 | */ | ||
| 101 | bool childCoreDumped(); | ||
| 102 | |||
| 63 | private: | 103 | private: |
| 64 | int iStdIn; | 104 | int iStdIn; |
| 65 | int iStdOut; | 105 | int iStdOut; |
| 66 | int iStdErr; | 106 | int iStdErr; |
| 67 | pid_t iPid; | 107 | pid_t iPid; |
| 108 | int iProcStatus; | ||
| 109 | bool bBlocking; | ||
| 110 | bool bStdOutEos; | ||
| 111 | bool bStdErrEos; | ||
| 68 | 112 | ||
| 69 | void gexec( const char *sName, char *const argv[] ); | 113 | void gexec( Flags eFlags, const char *sName, char *const argv[] ); |
| 114 | void checkClose(); | ||
| 70 | }; | 115 | }; |
| 71 | } | 116 | } |
| 72 | 117 | ||
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 @@ | |||
| 11 | 11 | ||
| 12 | int main() | 12 | int main() |
| 13 | { | 13 | { |
| 14 | //Bu::Process p( argv[1], argv+1 ); | 14 | 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 ); |
| 15 | Bu::Process p("mplayer", "mplayer", "dvd://", "-framedrop", | ||
| 16 | "-ao", "null", "-nosound", "-vf", "framestep=I,cropdetect", "-sstep", | ||
| 17 | "197", NULL ); | ||
| 18 | 15 | ||
| 19 | char buf[1000]; | 16 | char buf[1000]; |
| 20 | for(;;) | 17 | while( !p.isEos() ) |
| 21 | { | 18 | { |
| 22 | int iSize = p.read( buf, 1000 ); | 19 | bool out, err; |
| 23 | printf("::read=%d::\n", iSize ); | 20 | p.select( out, err ); |
| 24 | if( iSize == 0 ) | 21 | if( out ) |
| 25 | break; | 22 | { |
| 26 | fwrite( buf, iSize, 1, stdout ); | 23 | int iSize = p.read( buf, 1000 ); |
| 27 | if( iSize < 1000 ) | 24 | printf("::read=%d::\n", iSize ); |
| 28 | break; | 25 | fwrite( buf, iSize, 1, stdout ); |
| 26 | } | ||
| 27 | if( err ) | ||
| 28 | { | ||
| 29 | int iSize = p.readErr( buf, 1000 ); | ||
| 30 | printf("::readErr=%d::\n", iSize ); | ||
| 31 | fwrite( buf, iSize, 1, stdout ); | ||
| 32 | } | ||
| 29 | } | 33 | } |
| 30 | 34 | ||
| 31 | return 0; | 35 | return 0; |
