summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/process.cpp241
-rw-r--r--src/process.h51
-rw-r--r--src/tests/procs.cpp28
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
17Bu::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
22Bu::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
23Bu::Process::Process( const char *sName, const char *argv, ...) : 35Bu::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
51void Bu::Process::gexec( const char *sName, char *const argv[] ) 70void 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
83void Bu::Process::close() 118void 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
95size_t Bu::Process::read( void *pBuf, size_t nBytes ) 132size_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
142size_t Bu::Process::readErr( void *pBuf, size_t nBytes ) 162size_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
147size_t Bu::Process::write( const void *pBuf, size_t nBytes ) 192size_t Bu::Process::write( const void *pBuf, size_t nBytes )
@@ -213,9 +258,52 @@ bool Bu::Process::isBlocking()
213void Bu::Process::setBlocking( bool bBlocking ) 258void 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
273void 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
295bool Bu::Process::isRunning()
296{
297 return iPid != 0;
298}
299
300void Bu::Process::ignoreStdErr()
301{
302 if( iStdErr == -1 )
303 return;
304 ::close( iStdErr );
305 iStdErr = -1;
306 bStdErrEos = true;
219} 307}
220 308
221pid_t Bu::Process::getPid() 309pid_t Bu::Process::getPid()
@@ -223,3 +311,36 @@ pid_t Bu::Process::getPid()
223 return iPid; 311 return iPid;
224} 312}
225 313
314bool Bu::Process::childExited()
315{
316 return WIFEXITED( iProcStatus );
317}
318
319int Bu::Process::childExitStatus()
320{
321 return WEXITSTATUS( iProcStatus );
322}
323
324bool Bu::Process::childSignaled()
325{
326 return WIFSIGNALED( iProcStatus );
327}
328
329int Bu::Process::childSignal()
330{
331 return WTERMSIG( iProcStatus );
332}
333
334bool Bu::Process::childCoreDumped()
335{
336 return WCOREDUMP( iProcStatus );
337}
338
339void 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
12int main() 12int 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;