testexecmgmt/ucc/Source/ProcessLibrary/proclib_linux.cpp
changeset 0 3da2a79470a7
equal deleted inserted replaced
-1:000000000000 0:3da2a79470a7
       
     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