diff options
-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; |