aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2009-12-21 17:56:32 +0000
committerMike Buland <eichlan@xagasoft.com>2009-12-21 17:56:32 +0000
commit184669188717673c4cb36698192fe8c14aa3af68 (patch)
treea91583c0e383a38852d6b64f3bebd5778630c0bb
parent5399dd17a944464ced0c8618c46a367e4188d29b (diff)
downloadlibbu++-184669188717673c4cb36698192fe8c14aa3af68.tar.gz
libbu++-184669188717673c4cb36698192fe8c14aa3af68.tar.bz2
libbu++-184669188717673c4cb36698192fe8c14aa3af68.tar.xz
libbu++-184669188717673c4cb36698192fe8c14aa3af68.zip
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.
-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;