diff options
Diffstat (limited to 'src/stable/process.cpp')
-rw-r--r-- | src/stable/process.cpp | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/stable/process.cpp b/src/stable/process.cpp new file mode 100644 index 0000000..a98936e --- /dev/null +++ b/src/stable/process.cpp | |||
@@ -0,0 +1,441 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
3 | * | ||
4 | * This file is part of the libbu++ library and is released under the | ||
5 | * terms of the license contained in the file LICENSE. | ||
6 | */ | ||
7 | |||
8 | #include "bu/process.h" | ||
9 | #include <sys/types.h> | ||
10 | #include <sys/wait.h> | ||
11 | #include <unistd.h> | ||
12 | #include <stdarg.h> | ||
13 | #include <signal.h> | ||
14 | #include <fcntl.h> | ||
15 | #include <errno.h> | ||
16 | |||
17 | #include <sys/select.h> | ||
18 | |||
19 | #include "bu/config.h" | ||
20 | |||
21 | Bu::Process::Process( Flags eFlags, const char *sName, char *const argv[] ) : | ||
22 | iStdIn( -1 ), | ||
23 | iStdOut( -1 ), | ||
24 | iStdErr( -1 ), | ||
25 | iPid( 0 ), | ||
26 | iProcStatus( 0 ), | ||
27 | bBlocking( true ), | ||
28 | bStdOutEos( true ), | ||
29 | bStdErrEos( true ) | ||
30 | { | ||
31 | gexec( eFlags, sName, argv ); | ||
32 | } | ||
33 | |||
34 | Bu::Process::Process( Flags eFlags, const char *sName, const char *argv, ...) : | ||
35 | iStdIn( -1 ), | ||
36 | iStdOut( -1 ), | ||
37 | iStdErr( -1 ), | ||
38 | iPid( 0 ), | ||
39 | iProcStatus( 0 ), | ||
40 | bBlocking( true ), | ||
41 | bStdOutEos( true ), | ||
42 | bStdErrEos( true ) | ||
43 | { | ||
44 | int iCnt = 0; | ||
45 | va_list ap; | ||
46 | va_start( ap, argv ); | ||
47 | for(; va_arg( ap, const char *); iCnt++ ) { } | ||
48 | va_end( ap ); | ||
49 | |||
50 | char const **list = new char const *[iCnt+2]; | ||
51 | va_start( ap, argv ); | ||
52 | list[0] = argv; | ||
53 | for( int j = 1; j <= iCnt; j++ ) | ||
54 | { | ||
55 | list[j] = va_arg( ap, const char *); | ||
56 | } | ||
57 | list[iCnt+1] = NULL; | ||
58 | va_end( ap ); | ||
59 | |||
60 | gexec( eFlags, sName, (char *const *)list ); | ||
61 | delete[] list; | ||
62 | } | ||
63 | |||
64 | Bu::Process::Process( Flags eFlags, const Bu::Process::Options &opt, const char *sName, char *const argv[] ) : | ||
65 | iStdIn( -1 ), | ||
66 | iStdOut( -1 ), | ||
67 | iStdErr( -1 ), | ||
68 | iPid( 0 ), | ||
69 | iProcStatus( 0 ), | ||
70 | bBlocking( true ), | ||
71 | bStdOutEos( true ), | ||
72 | bStdErrEos( true ), | ||
73 | opt( opt ) | ||
74 | { | ||
75 | gexec( eFlags, sName, argv ); | ||
76 | } | ||
77 | |||
78 | Bu::Process::Process( Flags eFlags, const Bu::Process::Options &opt, const char *sName, const char *argv, ...) : | ||
79 | iStdIn( -1 ), | ||
80 | iStdOut( -1 ), | ||
81 | iStdErr( -1 ), | ||
82 | iPid( 0 ), | ||
83 | iProcStatus( 0 ), | ||
84 | bBlocking( true ), | ||
85 | bStdOutEos( true ), | ||
86 | bStdErrEos( true ), | ||
87 | opt( opt ) | ||
88 | { | ||
89 | int iCnt = 0; | ||
90 | va_list ap; | ||
91 | va_start( ap, argv ); | ||
92 | for(; va_arg( ap, const char *); iCnt++ ) { } | ||
93 | va_end( ap ); | ||
94 | |||
95 | char const **list = new char const *[iCnt+2]; | ||
96 | va_start( ap, argv ); | ||
97 | list[0] = argv; | ||
98 | for( int j = 1; j <= iCnt; j++ ) | ||
99 | { | ||
100 | list[j] = va_arg( ap, const char *); | ||
101 | } | ||
102 | list[iCnt+1] = NULL; | ||
103 | va_end( ap ); | ||
104 | |||
105 | gexec( eFlags, sName, (char *const *)list ); | ||
106 | delete[] list; | ||
107 | } | ||
108 | |||
109 | Bu::Process::~Process() | ||
110 | { | ||
111 | close(); | ||
112 | } | ||
113 | |||
114 | void Bu::Process::wait() | ||
115 | { | ||
116 | close(); | ||
117 | } | ||
118 | |||
119 | void Bu::Process::gexec( Flags eFlags, const char *sName, char *const argv[] ) | ||
120 | { | ||
121 | int iaStdIn[2]; | ||
122 | int iaStdOut[2]; | ||
123 | int iaStdErr[2]; | ||
124 | pipe( iaStdIn ); | ||
125 | if( eFlags & StdOut ) | ||
126 | { | ||
127 | pipe( iaStdOut ); | ||
128 | iStdOut = iaStdOut[0]; | ||
129 | bStdOutEos = false; | ||
130 | } | ||
131 | if( eFlags & StdErr ) | ||
132 | { | ||
133 | pipe( iaStdErr ); | ||
134 | iStdErr = iaStdErr[0]; | ||
135 | bStdErrEos = false; | ||
136 | } | ||
137 | |||
138 | iStdIn = iaStdIn[1]; | ||
139 | |||
140 | // fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); | ||
141 | |||
142 | iPid = fork(); | ||
143 | if( iPid == 0 ) | ||
144 | { | ||
145 | ::close( iaStdIn[1] ); | ||
146 | dup2( iaStdIn[0], 0 ); | ||
147 | if( eFlags & StdOut ) | ||
148 | { | ||
149 | ::close( iaStdOut[0] ); | ||
150 | dup2( iaStdOut[1], 1 ); | ||
151 | } | ||
152 | if( eFlags & StdErr ) | ||
153 | { | ||
154 | ::close( iaStdErr[0] ); | ||
155 | dup2( iaStdErr[1], 2 ); | ||
156 | } | ||
157 | if( (opt.eFlags&Options::SetGid) ) | ||
158 | { | ||
159 | setgid( opt.iGid ); | ||
160 | } | ||
161 | if( (opt.eFlags&Options::SetUid) ) | ||
162 | { | ||
163 | setuid( opt.iUid ); | ||
164 | } | ||
165 | execvp( sName, argv ); | ||
166 | throw Bu::ExceptionBase("Hey, execvp failed!"); | ||
167 | } | ||
168 | ::close( iaStdIn[0] ); | ||
169 | if( eFlags & StdOut ) | ||
170 | ::close( iaStdOut[1] ); | ||
171 | if( eFlags & StdErr ) | ||
172 | ::close( iaStdErr[1] ); | ||
173 | } | ||
174 | |||
175 | void Bu::Process::close() | ||
176 | { | ||
177 | if( iPid ) | ||
178 | { | ||
179 | if( iStdIn > -1 ) | ||
180 | ::close( iStdIn ); | ||
181 | if( iStdOut > -1 ) | ||
182 | ::close( iStdOut ); | ||
183 | if( iStdErr > -1 ) | ||
184 | ::close( iStdErr ); | ||
185 | waitpid( iPid, &iProcStatus, 0 ); | ||
186 | iPid = 0; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | void Bu::Process::closeStdIn() | ||
191 | { | ||
192 | ::close( iStdIn ); | ||
193 | iStdIn = -1; | ||
194 | } | ||
195 | |||
196 | void Bu::Process::closeStdOut() | ||
197 | { | ||
198 | ::close( iStdOut ); | ||
199 | iStdOut = -1; | ||
200 | } | ||
201 | |||
202 | Bu::size Bu::Process::read( void *pBuf, Bu::size nBytes ) | ||
203 | { | ||
204 | if( bStdOutEos ) | ||
205 | return 0; | ||
206 | fd_set rfds; | ||
207 | FD_ZERO( &rfds ); | ||
208 | FD_SET( iStdOut, &rfds ); | ||
209 | struct timeval tv = {0, 0}; | ||
210 | if( ::bu_select( iStdOut+1, &rfds, NULL, NULL, &tv ) < 0 ) | ||
211 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
212 | if( FD_ISSET( iStdOut, &rfds ) || bBlocking ) | ||
213 | { | ||
214 | Bu::size nRead = TEMP_FAILURE_RETRY( ::read( iStdOut, pBuf, nBytes ) ); | ||
215 | if( nRead == 0 ) | ||
216 | { | ||
217 | bStdOutEos = true; | ||
218 | checkClose(); | ||
219 | return 0; | ||
220 | } | ||
221 | if( nRead < 0 ) | ||
222 | { | ||
223 | if( errno == EAGAIN ) | ||
224 | return 0; | ||
225 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
226 | } | ||
227 | return nRead; | ||
228 | } | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | Bu::size Bu::Process::readErr( void *pBuf, Bu::size nBytes ) | ||
233 | { | ||
234 | if( bStdErrEos ) | ||
235 | return 0; | ||
236 | fd_set rfds; | ||
237 | FD_ZERO( &rfds ); | ||
238 | FD_SET( iStdErr, &rfds ); | ||
239 | struct timeval tv = {0, 0}; | ||
240 | if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, &tv ) < 0 ) | ||
241 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
242 | if( FD_ISSET( iStdErr, &rfds ) || bBlocking ) | ||
243 | { | ||
244 | Bu::size nRead = TEMP_FAILURE_RETRY( ::read( iStdErr, pBuf, nBytes ) ); | ||
245 | if( nRead == 0 ) | ||
246 | { | ||
247 | bStdErrEos = true; | ||
248 | checkClose(); | ||
249 | return 0; | ||
250 | } | ||
251 | if( nRead < 0 ) | ||
252 | { | ||
253 | if( errno == EAGAIN ) | ||
254 | return 0; | ||
255 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
256 | } | ||
257 | return nRead; | ||
258 | } | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | Bu::size Bu::Process::write( const void *pBuf, Bu::size nBytes ) | ||
263 | { | ||
264 | return TEMP_FAILURE_RETRY( ::write( iStdIn, pBuf, nBytes ) ); | ||
265 | } | ||
266 | |||
267 | Bu::size Bu::Process::tell() | ||
268 | { | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | void Bu::Process::seek( Bu::size ) | ||
273 | { | ||
274 | } | ||
275 | |||
276 | void Bu::Process::setPos( Bu::size ) | ||
277 | { | ||
278 | } | ||
279 | |||
280 | void Bu::Process::setPosEnd( Bu::size ) | ||
281 | { | ||
282 | } | ||
283 | |||
284 | bool Bu::Process::isEos() | ||
285 | { | ||
286 | return (iPid == 0); | ||
287 | } | ||
288 | |||
289 | bool Bu::Process::isOpen() | ||
290 | { | ||
291 | return (iPid != 0); | ||
292 | } | ||
293 | |||
294 | void Bu::Process::flush() | ||
295 | { | ||
296 | } | ||
297 | |||
298 | bool Bu::Process::canRead() | ||
299 | { | ||
300 | return true; | ||
301 | } | ||
302 | |||
303 | bool Bu::Process::canWrite() | ||
304 | { | ||
305 | return true; | ||
306 | } | ||
307 | |||
308 | bool Bu::Process::isReadable() | ||
309 | { | ||
310 | return true; | ||
311 | } | ||
312 | |||
313 | bool Bu::Process::isWritable() | ||
314 | { | ||
315 | return true; | ||
316 | } | ||
317 | |||
318 | bool Bu::Process::isSeekable() | ||
319 | { | ||
320 | return false; | ||
321 | } | ||
322 | |||
323 | bool Bu::Process::isBlocking() | ||
324 | { | ||
325 | return true; | ||
326 | } | ||
327 | |||
328 | void Bu::Process::setBlocking( bool bBlocking ) | ||
329 | { | ||
330 | if( bBlocking ) | ||
331 | { | ||
332 | if( !bStdOutEos ) | ||
333 | fcntl( iStdOut, F_SETFL, fcntl(iStdOut,F_GETFL,0 )&(~O_NONBLOCK) ); | ||
334 | if( !bStdErrEos ) | ||
335 | fcntl( iStdErr, F_SETFL, fcntl(iStdErr,F_GETFL,0 )&(~O_NONBLOCK) ); | ||
336 | } | ||
337 | else | ||
338 | { | ||
339 | if( !bStdOutEos ) | ||
340 | fcntl( iStdOut, F_SETFL, fcntl( iStdOut, F_GETFL, 0 )|O_NONBLOCK ); | ||
341 | if( !bStdErrEos ) | ||
342 | fcntl( iStdErr, F_SETFL, fcntl( iStdErr, F_GETFL, 0 )|O_NONBLOCK ); | ||
343 | } | ||
344 | this->bBlocking = bBlocking; | ||
345 | } | ||
346 | |||
347 | void Bu::Process::setSize( Bu::size ) | ||
348 | { | ||
349 | } | ||
350 | |||
351 | Bu::size Bu::Process::getBlockSize() const | ||
352 | { | ||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | Bu::size Bu::Process::getSize() const | ||
357 | { | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | Bu::String Bu::Process::getLocation() const | ||
362 | { | ||
363 | return ""; | ||
364 | } | ||
365 | |||
366 | void Bu::Process::select( bool &bStdOut, bool &bStdErr ) | ||
367 | { | ||
368 | fd_set rfds; | ||
369 | FD_ZERO( &rfds ); | ||
370 | if( !bStdOutEos ) | ||
371 | FD_SET( iStdOut, &rfds ); | ||
372 | if( !bStdErrEos ) | ||
373 | FD_SET( iStdErr, &rfds ); | ||
374 | if( ::bu_select( iStdErr+1, &rfds, NULL, NULL, NULL ) < 0 ) | ||
375 | throw Bu::ExceptionBase( strerror( errno ) ); | ||
376 | |||
377 | if( FD_ISSET( iStdOut, &rfds ) ) | ||
378 | bStdOut = true; | ||
379 | else | ||
380 | bStdOut = false; | ||
381 | |||
382 | if( FD_ISSET( iStdErr, &rfds ) ) | ||
383 | bStdErr = true; | ||
384 | else | ||
385 | bStdErr = false; | ||
386 | } | ||
387 | |||
388 | bool Bu::Process::isRunning() | ||
389 | { | ||
390 | if( waitpid( iPid, NULL, WNOHANG ) == iPid ) | ||
391 | checkClose(); | ||
392 | return iPid != 0; | ||
393 | } | ||
394 | |||
395 | void Bu::Process::ignoreStdErr() | ||
396 | { | ||
397 | if( iStdErr == -1 ) | ||
398 | return; | ||
399 | ::close( iStdErr ); | ||
400 | iStdErr = -1; | ||
401 | bStdErrEos = true; | ||
402 | } | ||
403 | |||
404 | pid_t Bu::Process::getPid() | ||
405 | { | ||
406 | return iPid; | ||
407 | } | ||
408 | |||
409 | bool Bu::Process::childExited() | ||
410 | { | ||
411 | return WIFEXITED( iProcStatus ); | ||
412 | } | ||
413 | |||
414 | int Bu::Process::childExitStatus() | ||
415 | { | ||
416 | return WEXITSTATUS( iProcStatus ); | ||
417 | } | ||
418 | |||
419 | bool Bu::Process::childSignaled() | ||
420 | { | ||
421 | return WIFSIGNALED( iProcStatus ); | ||
422 | } | ||
423 | |||
424 | int Bu::Process::childSignal() | ||
425 | { | ||
426 | return WTERMSIG( iProcStatus ); | ||
427 | } | ||
428 | |||
429 | bool Bu::Process::childCoreDumped() | ||
430 | { | ||
431 | return WCOREDUMP( iProcStatus ); | ||
432 | } | ||
433 | |||
434 | void Bu::Process::checkClose() | ||
435 | { | ||
436 | if( bStdOutEos && bStdErrEos ) | ||
437 | { | ||
438 | close(); | ||
439 | } | ||
440 | } | ||
441 | |||