|
1 /* |
|
2 * Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * Switches |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 |
|
21 /******************************************************************************* |
|
22 * |
|
23 * System Includes |
|
24 * |
|
25 ******************************************************************************/ |
|
26 #include <stdio.h> |
|
27 #include <stdlib.h> |
|
28 #include <unistd.h> |
|
29 #include <fcntl.h> |
|
30 #include <errno.h> |
|
31 #include <sys/types.h> |
|
32 #include <sys/wait.h> |
|
33 #include <signal.h> |
|
34 #include <time.h> |
|
35 |
|
36 |
|
37 /******************************************************************************* |
|
38 * |
|
39 * Local Includes |
|
40 * |
|
41 ******************************************************************************/ |
|
42 #include "proclib.h" |
|
43 #include "../include/standard_unix.h" |
|
44 |
|
45 |
|
46 /******************************************************************************* |
|
47 * |
|
48 * Macro Functions |
|
49 * |
|
50 ******************************************************************************/ |
|
51 #ifdef UT10 |
|
52 #undef fork |
|
53 #define fork() (-1) |
|
54 #endif |
|
55 |
|
56 |
|
57 /******************************************************************************* |
|
58 * |
|
59 * Definitions |
|
60 * |
|
61 ******************************************************************************/ |
|
62 #define INVALID_PIPE_DESC (-1) |
|
63 #define INVALID_PID (0) |
|
64 #define READ_BUFFER_SIZE (128) |
|
65 #define EXECUTE_PAUSE_PERIOD (100000) |
|
66 |
|
67 |
|
68 /******************************************************************************* |
|
69 * |
|
70 * PUBLIC: Constructor |
|
71 * |
|
72 ******************************************************************************/ |
|
73 CAProcess::CAProcess( void ) |
|
74 { |
|
75 // initialise all variables |
|
76 iCommand = NULL; |
|
77 iProcessStatus = PS_INIT; |
|
78 iProcessExitReason = ER_INVALID; |
|
79 iProcessExitCode = 0; |
|
80 iPID = INVALID_PID; |
|
81 iRecordStdOut = false; |
|
82 iRecordStdErr = false; |
|
83 iStdInPipe[0] = iStdInPipe[1] = INVALID_PIPE_DESC; |
|
84 iStdOutPipe[0] = iStdOutPipe[1] = INVALID_PIPE_DESC; |
|
85 iStdErrPipe[0] = iStdErrPipe[1] = INVALID_PIPE_DESC; |
|
86 iReadStdoutError = 0; |
|
87 iReadStderrError = 0; |
|
88 } |
|
89 |
|
90 /******************************************************************************* |
|
91 * |
|
92 * PUBLIC: Destructor |
|
93 * |
|
94 ******************************************************************************/ |
|
95 CAProcess::~CAProcess() |
|
96 { |
|
97 // I don't allow the object to be destroyed in the PS_STARTED state |
|
98 assert( iProcessStatus != PS_STARTED ); |
|
99 |
|
100 // clean up any handles |
|
101 if( iCommand != NULL ) { |
|
102 delete iCommand; |
|
103 iCommand = NULL; |
|
104 } |
|
105 ClosePipes(); |
|
106 } |
|
107 |
|
108 /******************************************************************************* |
|
109 * |
|
110 * SECTION-1: PROCESS CONTROL |
|
111 * |
|
112 ******************************************************************************/ |
|
113 |
|
114 /******************************************************************************* |
|
115 * |
|
116 * PUBLIC: StartProcess |
|
117 * |
|
118 ******************************************************************************/ |
|
119 TCAProcessError CAProcess::StartProcess( const char *aCommand, int *aErrorCode, bool aRecordStdOut, |
|
120 bool aRecordStdErr, bool aMakeNewProcessGroup ) |
|
121 { |
|
122 int err; |
|
123 |
|
124 // check params |
|
125 assert( aCommand != NULL ); |
|
126 assert( aErrorCode != NULL ); |
|
127 *aErrorCode = 0; |
|
128 |
|
129 // make sure the process is in the init state |
|
130 if( iProcessStatus != PS_INIT ) { |
|
131 return CAE_INVALID_STATE; |
|
132 } |
|
133 |
|
134 // verify that the recorded output is empty -- just checking no silly buggers |
|
135 assert( iRecordedStdOut.empty() == true ); |
|
136 assert( iRecordedStdErr.empty() == true ); |
|
137 |
|
138 // create the pipes that will be setup as the child process's input and output channels. Make the reading |
|
139 err = CreatePipes( aErrorCode ); |
|
140 if( err != CAE_NONE ) { |
|
141 return (TCAProcessError)err; |
|
142 } |
|
143 |
|
144 // flush all stream before forking (AE: why???) |
|
145 fflush( NULL ); |
|
146 |
|
147 // fork now |
|
148 iPID = fork(); |
|
149 |
|
150 // if an error occurred then return failed |
|
151 if( iPID == -1 ) { |
|
152 ClosePipes(); |
|
153 iPID = INVALID_PID; |
|
154 *aErrorCode = errno; |
|
155 return CAE_FORK_FAILED; |
|
156 } |
|
157 |
|
158 // if success and we are the parent then... |
|
159 if( iPID > 0 ) { |
|
160 |
|
161 // Close the write end of the output pipes and the read end of the input pipe |
|
162 ClosePipeDesc( &(iStdInPipe[0]) ); |
|
163 ClosePipeDesc( &(iStdOutPipe[1]) ); |
|
164 ClosePipeDesc( &(iStdErrPipe[1]) ); |
|
165 |
|
166 // Update the state |
|
167 iProcessStatus = PS_STARTED; |
|
168 iCommand = new string( aCommand ); |
|
169 iRecordStdOut = aRecordStdOut; |
|
170 iRecordStdErr = aRecordStdErr; |
|
171 |
|
172 // done |
|
173 return CAE_NONE; |
|
174 } |
|
175 |
|
176 // if success and we are the child then... |
|
177 if( iPID == 0 ) { |
|
178 |
|
179 // if requested - put the process in it's own process group |
|
180 if( aMakeNewProcessGroup ) { |
|
181 err = setpgid( 0, 0 ); |
|
182 if( err != 0 ) { |
|
183 fprintf( stderr, "WARNING: failed to setpgid for process %d - %s (%d).\n", getpid(), strerror(errno), errno ); |
|
184 } |
|
185 } |
|
186 |
|
187 // close the write end of stdin and make the read end desc 0 |
|
188 ClosePipeDesc( &(iStdInPipe[1]) ); |
|
189 close( stdin->_fileno ); |
|
190 err = dup( iStdInPipe[0] ); |
|
191 assert( err == (stdin->_fileno) ); |
|
192 |
|
193 // close the read end of stdout and make the write end desc 1 |
|
194 if( aRecordStdOut ) { |
|
195 ClosePipeDesc( &(iStdOutPipe[0]) ); |
|
196 close( stdout->_fileno ); |
|
197 err = dup( iStdOutPipe[1] ); |
|
198 assert( err == (stdout->_fileno) ); |
|
199 } |
|
200 |
|
201 // close the read end of stderr and make the write end desc 2 |
|
202 if( aRecordStdErr ) { |
|
203 ClosePipeDesc( &(iStdErrPipe[0]) ); |
|
204 close( stderr->_fileno ); |
|
205 err = dup( iStdErrPipe[1] ); |
|
206 assert( err == (stderr->_fileno) ); |
|
207 } |
|
208 |
|
209 // NOTE: A problem exists that because we have inherited all the descriptors |
|
210 // form our parent, any descriptors that are subsequently closed by the |
|
211 // parent will still be open until the child exits. |
|
212 |
|
213 // exec the target image. There is a choice here of whether to exec the target |
|
214 // directly or to use /bin/sh. The tradeoff is that /bin/sh gives you argument |
|
215 // parsing -- but exec will not fail with an invalid target because /bin/sh |
|
216 // is always available to exec. Currently I'm choosing the former but you |
|
217 // should be aware of the tradeoff.m |
|
218 execl("/bin/sh", "csh", "-c", aCommand, NULL); |
|
219 //execl( aCommand, NULL ); |
|
220 |
|
221 // The call to execl() failed. The following error message will be |
|
222 // sent back as if it came from the process (on stderr). The process |
|
223 // then exits. |
|
224 fprintf( stderr, "ERROR: execl() failed with error '%s' (%d)\n", strerror(errno), errno ); |
|
225 ClosePipeDesc( &(iStdInPipe[0]) ); |
|
226 ClosePipeDesc( &(iStdOutPipe[1]) ); |
|
227 ClosePipeDesc( &(iStdErrPipe[1]) ); |
|
228 exit( -1 ); |
|
229 } |
|
230 |
|
231 // should never get here |
|
232 assert( !"Invalid code path" ); |
|
233 return CAE_NONE; |
|
234 } |
|
235 |
|
236 |
|
237 /******************************************************************************* |
|
238 * |
|
239 * PUBLIC: RequestStop |
|
240 * |
|
241 ******************************************************************************/ |
|
242 TCAProcessError CAProcess::RequestStop( int aSignal ) |
|
243 { |
|
244 int err; |
|
245 |
|
246 // get the state |
|
247 err = GetProcessStatus( NULL ); |
|
248 assert( err == CAE_NONE ); |
|
249 |
|
250 // check the state |
|
251 if( iProcessStatus != PS_STARTED ) { |
|
252 return CAE_INVALID_STATE; |
|
253 } |
|
254 |
|
255 // send the specified signal to the process |
|
256 err = kill( iPID, aSignal ); |
|
257 |
|
258 // if kill() fails then we abandon the process -- STATE TRANSITION |
|
259 if( err != 0 ) { |
|
260 iProcessStatus = PS_ABANDONNED; |
|
261 iProcessExitReason = ER_SIGNALFAILED; |
|
262 iProcessExitCode = errno; |
|
263 ClosePipes(); |
|
264 return CAE_SIGNAL_FAILED; |
|
265 } |
|
266 |
|
267 // otherwise all is ok |
|
268 return CAE_NONE; |
|
269 } |
|
270 |
|
271 |
|
272 /******************************************************************************* |
|
273 * |
|
274 * PUBLIC: GetProcessStatus |
|
275 * |
|
276 ******************************************************************************/ |
|
277 TCAProcessError CAProcess::GetProcessStatus( TProcessStatus *aProcessStatus ) |
|
278 { |
|
279 int err; |
|
280 int status; |
|
281 |
|
282 // if the process is in the PS_STARTED state then we have to verify that it is still running |
|
283 if( iProcessStatus == PS_STARTED ) { |
|
284 |
|
285 // see if the process is still running |
|
286 assert( iPID > 0 ); |
|
287 err = waitpid( iPID, &status, WNOHANG ); |
|
288 |
|
289 // if an error occured then transition from started to abandonned state |
|
290 if( err == -1 ) { |
|
291 iProcessStatus = PS_ABANDONNED; |
|
292 iProcessExitReason = ER_WAITPIDFAILED; |
|
293 iProcessExitCode = errno; |
|
294 } |
|
295 |
|
296 // if the process is still running then no change |
|
297 if( err == 0 ) { |
|
298 } |
|
299 |
|
300 // if the process has finished then transition from started to stopped state |
|
301 if( err > 0 ) { |
|
302 iProcessStatus = PS_STOPPED; |
|
303 iProcessExitReason = GetExitReasonFromStatus( status, &iProcessExitCode ); |
|
304 } |
|
305 } |
|
306 |
|
307 // if the passed pointer is non-null then we return the status |
|
308 if( aProcessStatus != NULL ) { |
|
309 *aProcessStatus = iProcessStatus; |
|
310 } |
|
311 |
|
312 // done |
|
313 return CAE_NONE; |
|
314 } |
|
315 |
|
316 |
|
317 /******************************************************************************* |
|
318 * |
|
319 * PUBLIC: WaitForProcessToTerminate |
|
320 * |
|
321 ******************************************************************************/ |
|
322 TCAProcessError CAProcess::WaitForProcessToTerminate( int aMaxWait ) |
|
323 { |
|
324 int err; |
|
325 int status; |
|
326 int i; |
|
327 |
|
328 // if the process isn't in the PS_STARTED state then this call is invalid -- note |
|
329 // that GetProcessStatus is not used to update the status here since if the |
|
330 // process has stopped we want this to be caught in the waitpid below, not here. |
|
331 if( iProcessStatus != PS_STARTED ) { |
|
332 return CAE_INVALID_STATE; |
|
333 } |
|
334 |
|
335 // Wait for the process. We can't just do a blocking wait since the stdout/stderr |
|
336 // pipes would overflow and people wouldn't be able to read the output as |
|
337 // expected when the process has terminated. So we do a non-blocking wait in a loop. |
|
338 for( i = 0; (aMaxWait == -1) || (i < aMaxWait); i++ ) { |
|
339 |
|
340 // see if the process has finished |
|
341 err = waitpid( iPID, &status, WNOHANG ); |
|
342 if( err != 0 ) { |
|
343 break; |
|
344 } |
|
345 |
|
346 // see if there is any data to read in from the pipes -- this will transfer the data |
|
347 // from the pipes to the internal buffers |
|
348 InternalPollProcessForNewOutput( 0, NULL, NULL ); |
|
349 } |
|
350 |
|
351 // see if we've timedout |
|
352 if( err == 0 ) { |
|
353 assert( i == aMaxWait ); |
|
354 return CAE_TIMEOUT; |
|
355 } |
|
356 |
|
357 // if the call failed otherwise we abandon the process |
|
358 if( err == -1 ) { |
|
359 iProcessStatus = PS_ABANDONNED; |
|
360 iProcessExitReason = ER_WAITPIDFAILED; |
|
361 iProcessExitCode = errno; |
|
362 return CAE_WAITPID_FAILED; |
|
363 } |
|
364 |
|
365 // otherwise everything is good and we have stopped the process |
|
366 assert( err > 0 ); |
|
367 iProcessStatus = PS_STOPPED; |
|
368 iProcessExitReason = GetExitReasonFromStatus( status, &iProcessExitCode ); |
|
369 return CAE_NONE; |
|
370 } |
|
371 |
|
372 /******************************************************************************* |
|
373 * |
|
374 * PUBLIC: Execute() |
|
375 * |
|
376 ******************************************************************************/ |
|
377 TCAProcessError CAProcess::Execute( const char *aCommand, int *aErrorCode, int aTimeoutInMilliseconds, string *aStandardOutput, |
|
378 string *aStandardError ) |
|
379 { |
|
380 TCAProcessError err; |
|
381 int i, stdout_bytes_read, stderr_bytes_read, errcode, max_iterations, timeout_in_microseconds; |
|
382 TProcessStatus pstatus; |
|
383 bool record_std_out, record_std_err; |
|
384 struct timespec pause_period = { 0, EXECUTE_PAUSE_PERIOD }; |
|
385 |
|
386 // validate params |
|
387 assert( aCommand != NULL ); |
|
388 assert( aErrorCode != NULL ); |
|
389 record_std_out = (aStandardOutput != NULL); |
|
390 record_std_err = (aStandardError != NULL); |
|
391 |
|
392 // start the process |
|
393 err = StartProcess( aCommand, aErrorCode, record_std_out, record_std_err, false ); |
|
394 if( err != CAE_NONE ) { |
|
395 return err; |
|
396 } |
|
397 |
|
398 // work out the maximum number of iterations |
|
399 timeout_in_microseconds = aTimeoutInMilliseconds * 1000; |
|
400 max_iterations = (timeout_in_microseconds / EXECUTE_PAUSE_PERIOD); |
|
401 // fprintf( stderr, "DEBUG: timeout_in_micro = %d, pause_period = %d, timeout = %d, max_iterations = %d\n", |
|
402 // timeout_in_microseconds, EXECUTE_PAUSE_PERIOD, aTimeoutInMilliseconds, max_iterations ); |
|
403 |
|
404 // wait for the process to exit - store the output |
|
405 for( i = 0; (i < max_iterations) || (aTimeoutInMilliseconds == -1); i++ ) { |
|
406 |
|
407 // check the status of the process |
|
408 err = GetProcessStatus( &pstatus ); |
|
409 assert( err == CAE_NONE ); |
|
410 |
|
411 // sanity check on the status |
|
412 assert( (pstatus != PS_INVALID) && (pstatus != PS_INIT) ); |
|
413 |
|
414 // if the status isn't PS_STARTED then we exit |
|
415 if( pstatus != PS_STARTED ) { |
|
416 break; |
|
417 } |
|
418 |
|
419 // get any new output - errors are ignored |
|
420 if( record_std_out || record_std_err ) { |
|
421 PollProcessForNewOutput( 0, &stdout_bytes_read, &stderr_bytes_read ); |
|
422 GetRecordedStandardOutput( aStandardOutput, &errcode ); |
|
423 GetRecordedStandardError( aStandardError, &errcode ); |
|
424 } |
|
425 |
|
426 // now wait for one second and then lets do it all again!! |
|
427 nanosleep( &pause_period, NULL ); |
|
428 } |
|
429 |
|
430 // get the current status |
|
431 err = GetProcessStatus( &pstatus ); |
|
432 assert( err == CAE_NONE ); |
|
433 |
|
434 // if we are still running then send a kill signal and wait to exit |
|
435 if( pstatus == PS_STARTED ) { |
|
436 err = RequestStop( SIGKILL ); |
|
437 if( (err != CAE_NONE) && (err != CAE_INVALID_STATE) ) { |
|
438 return err; |
|
439 } |
|
440 err = WaitForProcessToTerminate( -1 ); |
|
441 if( err != CAE_NONE ) { |
|
442 return err; |
|
443 } |
|
444 } |
|
445 |
|
446 // get any last minute output |
|
447 if( record_std_out || record_std_err ) { |
|
448 PollProcessForNewOutput( 0, &stdout_bytes_read, &stderr_bytes_read ); |
|
449 GetRecordedStandardOutput( aStandardOutput, &errcode ); |
|
450 GetRecordedStandardError( aStandardError, &errcode ); |
|
451 } |
|
452 |
|
453 // we have to return an indication of whether the process timed out |
|
454 return ((i == max_iterations) ? CAE_TIMEOUT : CAE_NONE); |
|
455 } |
|
456 |
|
457 |
|
458 /******************************************************************************* |
|
459 * |
|
460 * SECTION-2: DATA CONTROL |
|
461 * |
|
462 ******************************************************************************/ |
|
463 |
|
464 /******************************************************************************* |
|
465 * |
|
466 * PUBLIC: PollProcessForNewOutput. Polls the executing process for new output. |
|
467 * Any output is then transferred to the internal buffers. |
|
468 * |
|
469 ******************************************************************************/ |
|
470 TCAProcessError CAProcess::PollProcessForNewOutput( int aMaxWait, int *aStandardOutputRead, int *aStandardErrorRead ) |
|
471 { |
|
472 GetProcessStatus( NULL ); |
|
473 if( iProcessStatus == PS_INIT ) { |
|
474 return CAE_INVALID_STATE; |
|
475 } |
|
476 return InternalPollProcessForNewOutput( aMaxWait, aStandardOutputRead, aStandardErrorRead ); |
|
477 } |
|
478 |
|
479 TCAProcessError CAProcess::InternalPollProcessForNewOutput( int aMaxWait, int *aStandardOutputRead, int *aStandardErrorRead ) |
|
480 { |
|
481 struct timeval maxWait; |
|
482 fd_set readset; |
|
483 int fdsToProcess; |
|
484 int is_set; |
|
485 int err_stdout = 0; |
|
486 int err_stderr = 0; |
|
487 |
|
488 // initialise the out params |
|
489 if( aStandardOutputRead != NULL ) { |
|
490 *aStandardOutputRead = 0; |
|
491 } |
|
492 if( aStandardErrorRead != NULL ) { |
|
493 *aStandardErrorRead = 0; |
|
494 } |
|
495 |
|
496 // make sure one of the pipes is still open |
|
497 if( (iStdOutPipe[0] == INVALID_PIPE_DESC) && (iStdErrPipe[0] == INVALID_PIPE_DESC) ) { |
|
498 return CAE_NONE; |
|
499 } |
|
500 |
|
501 // Perform a select (of upto the maximum allowed time) and read in the available data. |
|
502 FD_ZERO( &readset ); |
|
503 if( iStdOutPipe[0] != INVALID_PIPE_DESC ) { |
|
504 FD_SET( iStdOutPipe[0], &readset ); |
|
505 } |
|
506 if( iStdErrPipe[0] != INVALID_PIPE_DESC ) { |
|
507 FD_SET( iStdErrPipe[0], &readset ); |
|
508 } |
|
509 maxWait.tv_sec = aMaxWait; |
|
510 maxWait.tv_usec = 0; |
|
511 fdsToProcess = select( FD_SETSIZE, &readset, NULL, NULL, &maxWait ); |
|
512 if( fdsToProcess < 0 ) { |
|
513 iReadStdoutError = iReadStderrError = errno; |
|
514 return CAE_SELECT_FAILED; |
|
515 } |
|
516 |
|
517 // If there is no data to read then exit |
|
518 if( fdsToProcess == 0 ) { |
|
519 return CAE_NONE; |
|
520 } |
|
521 |
|
522 // Read the stdout pipe |
|
523 if( iStdOutPipe[0] != INVALID_PIPE_DESC ) { |
|
524 is_set = FD_ISSET( iStdOutPipe[0], &readset ); |
|
525 if( is_set != 0 ) { |
|
526 err_stdout = ReadOutput( &(iStdOutPipe[0]), &iRecordedStdOut, iRecordStdOut, &iReadStdoutError ); |
|
527 if( aStandardOutputRead != NULL ) { |
|
528 *aStandardOutputRead = iRecordedStdOut.length(); |
|
529 } |
|
530 } |
|
531 } |
|
532 |
|
533 // Read the stderr pipe |
|
534 if( iStdErrPipe[0] != INVALID_PIPE_DESC ) { |
|
535 is_set = FD_ISSET( iStdErrPipe[0], &readset ); |
|
536 if( is_set != 0 ) { |
|
537 err_stderr = ReadOutput( &(iStdErrPipe[0]), &iRecordedStdErr, iRecordStdErr, &iReadStderrError ); |
|
538 if( aStandardErrorRead != NULL ) { |
|
539 *aStandardErrorRead = iRecordedStdErr.length(); |
|
540 } |
|
541 } |
|
542 } |
|
543 |
|
544 // If one of the reads returned an error then return this |
|
545 if( (err_stdout != 0) || (err_stderr != 0) ) { |
|
546 return CAE_READ_FAILED; |
|
547 } |
|
548 return CAE_NONE; |
|
549 } |
|
550 |
|
551 |
|
552 /******************************************************************************* |
|
553 * |
|
554 * PUBLIC: GetRecordedStandardOutput |
|
555 * |
|
556 ******************************************************************************/ |
|
557 int CAProcess::GetRecordedStandardOutput( string *aStdout, int *aReadError ) |
|
558 { |
|
559 // validate params |
|
560 assert( aReadError != NULL ); |
|
561 if( aStdout == NULL ) { |
|
562 return 0; |
|
563 } |
|
564 |
|
565 // append a copy of the stored data to the passed string |
|
566 (*aStdout) += iRecordedStdOut; |
|
567 |
|
568 // clear the current string |
|
569 iRecordedStdOut.erase( 0, iRecordedStdOut.length() ); |
|
570 |
|
571 // set the read error |
|
572 *aReadError = iReadStdoutError; |
|
573 iReadStdoutError = 0; |
|
574 |
|
575 // return the number of bytes read |
|
576 return aStdout->size(); |
|
577 } |
|
578 |
|
579 |
|
580 /******************************************************************************* |
|
581 * |
|
582 * PUBLIC: GetRecordedStandardError |
|
583 * |
|
584 ******************************************************************************/ |
|
585 int CAProcess::GetRecordedStandardError( string *aStdErr, int *aReadError ) |
|
586 { |
|
587 // validate params |
|
588 assert( aReadError != NULL ); |
|
589 if( aStdErr == NULL ) { |
|
590 return 0; |
|
591 } |
|
592 |
|
593 // append a copy of the stored data to the passed string |
|
594 (*aStdErr) += iRecordedStdErr; |
|
595 |
|
596 // clear the current string |
|
597 iRecordedStdErr.erase( 0, iRecordedStdErr.length() ); |
|
598 |
|
599 // set the read error |
|
600 *aReadError = iReadStderrError; |
|
601 iReadStderrError = 0; |
|
602 |
|
603 // return the number of bytes read |
|
604 return aStdErr->size(); |
|
605 } |
|
606 |
|
607 |
|
608 /******************************************************************************* |
|
609 * |
|
610 * SECTION-3: General Accessors |
|
611 * |
|
612 ******************************************************************************/ |
|
613 |
|
614 /******************************************************************************* |
|
615 * |
|
616 * PUBLIC: GetCommandString |
|
617 * |
|
618 ******************************************************************************/ |
|
619 string CAProcess::GetCommandString() |
|
620 { |
|
621 string cmd; |
|
622 if( iProcessStatus == PS_INIT ) { |
|
623 return cmd; |
|
624 } |
|
625 cmd = *iCommand; |
|
626 return cmd; |
|
627 } |
|
628 |
|
629 /******************************************************************************* |
|
630 * |
|
631 * PUBLIC: GetExitReason |
|
632 * |
|
633 ******************************************************************************/ |
|
634 TCAProcessError CAProcess::GetExitReason( TProcessExitReason *aExitReason ) |
|
635 { |
|
636 // check the state -- otherwise this is not valid |
|
637 if( (iProcessStatus != PS_STOPPED) && (iProcessStatus != PS_ABANDONNED) ) { |
|
638 return CAE_INVALID_STATE; |
|
639 } |
|
640 |
|
641 // set the value |
|
642 if( aExitReason != NULL ) { |
|
643 *aExitReason = iProcessExitReason; |
|
644 } |
|
645 |
|
646 // done |
|
647 return CAE_NONE; |
|
648 } |
|
649 |
|
650 /******************************************************************************* |
|
651 * |
|
652 * PUBLIC: GetExitCode |
|
653 * |
|
654 ******************************************************************************/ |
|
655 TCAProcessError CAProcess::GetExitCode( int *aExitCode ) |
|
656 { |
|
657 // check the state -- otherwise this is not valid |
|
658 if( (iProcessStatus != PS_STOPPED) && (iProcessStatus != PS_ABANDONNED) ) { |
|
659 return CAE_INVALID_STATE; |
|
660 } |
|
661 |
|
662 // set the value |
|
663 if( aExitCode != NULL ) { |
|
664 *aExitCode = iProcessExitCode; |
|
665 } |
|
666 |
|
667 // done |
|
668 return CAE_NONE; |
|
669 } |
|
670 |
|
671 |
|
672 /******************************************************************************* |
|
673 * |
|
674 * SECTION-4: Helpers |
|
675 * |
|
676 ******************************************************************************/ |
|
677 |
|
678 /******************************************************************************* |
|
679 * |
|
680 * PRIVATE: GetExitReasonFromStatus |
|
681 * |
|
682 ******************************************************************************/ |
|
683 TProcessExitReason CAProcess::GetExitReasonFromStatus( int aStatus, int *aExitCode ) |
|
684 { |
|
685 assert( aExitCode != NULL ); |
|
686 if( WIFEXITED(aStatus) ) { |
|
687 *aExitCode = WEXITSTATUS(aStatus); |
|
688 return ER_EXITED; |
|
689 } |
|
690 if( WIFSIGNALED(aStatus) ) { |
|
691 *aExitCode = WTERMSIG(aStatus); |
|
692 return ER_SIGNALLED; |
|
693 } |
|
694 return ER_UNKNOWN; |
|
695 } |
|
696 |
|
697 /******************************************************************************* |
|
698 * |
|
699 * PRIVATE: ReadOutput |
|
700 * |
|
701 ******************************************************************************/ |
|
702 TCAProcessError CAProcess::ReadOutput( int *aFileDes, string *aBuffer, bool aStoreFlag, int *aReadError ) |
|
703 { |
|
704 char buff[READ_BUFFER_SIZE]; |
|
705 int bytesRead; |
|
706 |
|
707 // check params |
|
708 assert( aBuffer != NULL ); |
|
709 assert( aReadError != NULL ); |
|
710 assert( aFileDes != NULL ); |
|
711 |
|
712 // read until there is nothing left to read |
|
713 while( true ) { |
|
714 |
|
715 // read |
|
716 bytesRead = read( (*aFileDes), &buff, READ_BUFFER_SIZE-1 ); |
|
717 |
|
718 // null-terminate the buffer |
|
719 if( bytesRead > 0 ) { |
|
720 buff[bytesRead] = 0; |
|
721 } |
|
722 |
|
723 // temporarily unavailable resource is not an error |
|
724 if( (bytesRead == -1) && (errno == EAGAIN) ) { |
|
725 return CAE_NONE; |
|
726 } |
|
727 |
|
728 // check for error |
|
729 if( bytesRead == -1 ) { |
|
730 fprintf( stderr, "DEBUG: read() returned error %s\n", strerror(errno) ); |
|
731 *aReadError = errno; |
|
732 return CAE_READ_FAILED; |
|
733 } |
|
734 |
|
735 // check for no more data -- if this is the case then close the pipe |
|
736 if( bytesRead == 0 ) { |
|
737 ClosePipeDesc( aFileDes ); |
|
738 return CAE_NONE; |
|
739 } |
|
740 |
|
741 // store the data in the object if requested |
|
742 if( aStoreFlag ) { |
|
743 fflush( stderr ); |
|
744 (*aBuffer) += buff; |
|
745 } |
|
746 } |
|
747 |
|
748 // should never get here |
|
749 assert( !"Invalid code path" ); |
|
750 return CAE_NONE; |
|
751 } |
|
752 |
|
753 /******************************************************************************* |
|
754 * |
|
755 * PRIVATE: CreatePipes / ClosePipes / ClosePipePair |
|
756 * |
|
757 ******************************************************************************/ |
|
758 TCAProcessError CAProcess::CreatePipes( int *aErrorCode ) |
|
759 { |
|
760 int err; |
|
761 int flags; |
|
762 |
|
763 // check params |
|
764 assert( aErrorCode != NULL ); |
|
765 |
|
766 // create the stdin pipe |
|
767 err = pipe( iStdInPipe ); |
|
768 if( err != 0 ) { |
|
769 *aErrorCode = errno; |
|
770 return CAE_FAILED_TO_CREATE_PIPE; |
|
771 } |
|
772 |
|
773 // create the stdout pipe |
|
774 err = pipe( iStdOutPipe ); |
|
775 if( err != 0 ) { |
|
776 ClosePipes(); |
|
777 *aErrorCode = errno; |
|
778 return CAE_FAILED_TO_CREATE_PIPE; |
|
779 } |
|
780 |
|
781 // create the stderr pipe |
|
782 err = pipe( iStdErrPipe ); |
|
783 if( err != 0 ) { |
|
784 ClosePipes(); |
|
785 *aErrorCode = errno; |
|
786 return CAE_FAILED_TO_CREATE_PIPE; |
|
787 } |
|
788 |
|
789 // make the read end of the stdout pipe non-blocking |
|
790 flags = fcntl( iStdOutPipe[0], F_GETFL, 0 ); |
|
791 flags |= O_NONBLOCK; |
|
792 err = fcntl( iStdOutPipe[0], F_SETFL, flags ); |
|
793 if( err != 0 ) { |
|
794 ClosePipes(); |
|
795 *aErrorCode = errno; |
|
796 return CAE_FAILED_TO_SET_NONBLOCKING; |
|
797 } |
|
798 |
|
799 // make the read end of the stderr pipe non-blocking |
|
800 flags = fcntl( iStdErrPipe[0], F_GETFL, 0 ); |
|
801 flags |= O_NONBLOCK; |
|
802 err = fcntl( iStdErrPipe[0], F_SETFL, flags ); |
|
803 if( err != 0 ) { |
|
804 ClosePipes(); |
|
805 *aErrorCode = errno; |
|
806 return CAE_FAILED_TO_SET_NONBLOCKING; |
|
807 } |
|
808 |
|
809 // OK |
|
810 return CAE_NONE; |
|
811 } |
|
812 |
|
813 void CAProcess::ClosePipes() |
|
814 { |
|
815 ClosePipePair( iStdInPipe ); |
|
816 ClosePipePair( iStdOutPipe ); |
|
817 ClosePipePair( iStdErrPipe ); |
|
818 } |
|
819 |
|
820 |
|
821 void CAProcess::ClosePipePair( int *aPipes ) |
|
822 { |
|
823 assert( aPipes != NULL ); |
|
824 ClosePipeDesc( &(aPipes[0]) ); |
|
825 ClosePipeDesc( &(aPipes[1]) ); |
|
826 } |
|
827 |
|
828 |
|
829 void CAProcess::ClosePipeDesc( int *aPipeDescriptor ) |
|
830 { |
|
831 assert( aPipeDescriptor != NULL ); |
|
832 close( *aPipeDescriptor ); |
|
833 *aPipeDescriptor = INVALID_PIPE_DESC; |
|
834 } |
|
835 |