testexecmgmt/ucc/Source/ProcessLibrary/proclib_linux.cpp
changeset 0 3da2a79470a7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testexecmgmt/ucc/Source/ProcessLibrary/proclib_linux.cpp	Mon Mar 08 15:04:18 2010 +0800
@@ -0,0 +1,835 @@
+/*
+* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  
+* Switches
+*
+*/
+
+
+
+/*******************************************************************************
+ *
+ * System Includes
+ *
+ ******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <time.h>
+
+
+/*******************************************************************************
+ *
+ * Local Includes
+ *
+ ******************************************************************************/
+#include "proclib.h"
+#include "../include/standard_unix.h"
+
+
+/*******************************************************************************
+ *
+ * Macro Functions
+ *
+ ******************************************************************************/
+#ifdef UT10
+#undef fork
+#define fork() (-1)
+#endif
+
+
+/*******************************************************************************
+ *
+ * Definitions
+ *
+ ******************************************************************************/
+#define INVALID_PIPE_DESC           (-1)
+#define INVALID_PID                 (0)
+#define READ_BUFFER_SIZE            (128)
+#define EXECUTE_PAUSE_PERIOD        (100000)
+
+
+/*******************************************************************************
+ *
+ * PUBLIC: Constructor
+ *
+ ******************************************************************************/
+CAProcess::CAProcess( void )
+{
+  // initialise all variables
+  iCommand = NULL;
+  iProcessStatus = PS_INIT;
+  iProcessExitReason = ER_INVALID;
+  iProcessExitCode = 0;
+  iPID = INVALID_PID;
+  iRecordStdOut = false;
+  iRecordStdErr = false;
+  iStdInPipe[0] = iStdInPipe[1] = INVALID_PIPE_DESC;
+  iStdOutPipe[0] = iStdOutPipe[1] = INVALID_PIPE_DESC;
+  iStdErrPipe[0] = iStdErrPipe[1] = INVALID_PIPE_DESC;
+  iReadStdoutError = 0;
+  iReadStderrError = 0;
+}
+
+/*******************************************************************************
+ *
+ * PUBLIC: Destructor 
+ *
+ ******************************************************************************/
+CAProcess::~CAProcess()
+{
+  // I don't allow the object to be destroyed in the PS_STARTED state
+  assert( iProcessStatus != PS_STARTED );
+
+  // clean up any handles
+  if( iCommand != NULL ) {
+    delete iCommand;
+    iCommand = NULL;
+  }
+  ClosePipes();
+} 
+
+/*******************************************************************************
+ *
+ * SECTION-1: PROCESS CONTROL 
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ * PUBLIC: StartProcess 
+ *
+ ******************************************************************************/
+TCAProcessError CAProcess::StartProcess( const char *aCommand, int *aErrorCode, bool aRecordStdOut, 
+					 bool aRecordStdErr, bool aMakeNewProcessGroup )
+{
+  int err;
+
+  // check params 
+  assert( aCommand != NULL );
+  assert( aErrorCode != NULL );
+  *aErrorCode = 0;
+
+  // make sure the process is in the init state
+  if( iProcessStatus != PS_INIT ) {
+    return CAE_INVALID_STATE;
+  }
+
+  // verify that the recorded output is empty -- just checking no silly buggers
+  assert( iRecordedStdOut.empty() == true );
+  assert( iRecordedStdErr.empty() == true );
+
+  // create the pipes that will be setup as the child process's input and output channels. Make the reading 
+  err = CreatePipes( aErrorCode );
+  if( err != CAE_NONE ) {
+    return (TCAProcessError)err;
+  }
+
+  // flush all stream before forking (AE: why???)
+  fflush( NULL );
+
+  // fork now
+  iPID = fork();
+
+  // if an error occurred then return failed
+  if( iPID == -1 ) {
+    ClosePipes();
+    iPID = INVALID_PID;
+    *aErrorCode = errno;
+    return CAE_FORK_FAILED;
+  }
+
+  // if success and we are the parent then...
+  if( iPID > 0 ) { 
+
+    // Close the write end of the output pipes and the read end of the input pipe
+  	ClosePipeDesc( &(iStdInPipe[0]) );
+  	ClosePipeDesc( &(iStdOutPipe[1]) );
+  	ClosePipeDesc( &(iStdErrPipe[1]) );
+
+    // Update the state
+    iProcessStatus = PS_STARTED;
+    iCommand = new string( aCommand );
+    iRecordStdOut = aRecordStdOut;
+    iRecordStdErr = aRecordStdErr;
+    
+    // done
+    return CAE_NONE;
+  }
+
+  // if success and we are the child then...
+  if( iPID == 0 ) {
+
+    // if requested - put the process in it's own process group
+    if( aMakeNewProcessGroup ) {
+      err = setpgid( 0, 0 );
+      if( err != 0 ) {
+        fprintf( stderr, "WARNING: failed to setpgid for process %d - %s (%d).\n", getpid(), strerror(errno), errno );
+      }
+    } 
+
+    // close the write end of stdin and make the read end desc 0
+  	ClosePipeDesc( &(iStdInPipe[1]) );
+    close( stdin->_fileno );
+    err = dup( iStdInPipe[0] );
+    assert( err == (stdin->_fileno) );
+
+    // close the read end of stdout and make the write end desc 1
+    if( aRecordStdOut ) {
+      ClosePipeDesc( &(iStdOutPipe[0]) );
+      close( stdout->_fileno );
+      err = dup( iStdOutPipe[1] );
+      assert( err == (stdout->_fileno) );
+    }
+
+    // close the read end of stderr and make the write end desc 2
+    if( aRecordStdErr ) {
+      ClosePipeDesc( &(iStdErrPipe[0]) );
+      close( stderr->_fileno );
+      err = dup( iStdErrPipe[1] );
+      assert( err == (stderr->_fileno) );
+    }
+
+    // NOTE: A problem exists that because we have inherited all the descriptors
+    // form our parent, any descriptors that are subsequently closed by the
+    // parent will still be open until the child exits.
+    
+    // exec the target image. There is a choice here of whether to exec the target
+    // directly or to use /bin/sh. The tradeoff is that /bin/sh gives you argument
+    // parsing -- but exec will not fail with an invalid target because /bin/sh
+    // is always available to exec. Currently I'm choosing the former but you 
+    // should be aware of the tradeoff.m
+    execl("/bin/sh", "csh", "-c", aCommand, NULL);
+    //execl( aCommand, NULL );
+
+    // The call to execl() failed.  The following error message will be
+    // sent back as if it came from the process (on stderr). The process
+    // then exits.
+    fprintf( stderr, "ERROR: execl() failed with error '%s' (%d)\n", strerror(errno), errno );
+    ClosePipeDesc( &(iStdInPipe[0]) );
+    ClosePipeDesc( &(iStdOutPipe[1]) );
+    ClosePipeDesc( &(iStdErrPipe[1]) );
+    exit( -1 );
+  }
+
+  // should never get here
+  assert( !"Invalid code path" );
+  return CAE_NONE;
+}
+
+
+/*******************************************************************************
+ *
+ * PUBLIC: RequestStop 
+ *
+ ******************************************************************************/
+TCAProcessError CAProcess::RequestStop( int aSignal )
+{
+  int err;
+
+  // get the state
+  err = GetProcessStatus( NULL );
+  assert( err == CAE_NONE );
+
+  // check the state
+  if( iProcessStatus != PS_STARTED ) {
+    return CAE_INVALID_STATE;
+  }
+
+  // send the specified signal to the process
+  err = kill( iPID, aSignal );
+  
+  // if kill() fails then we abandon the process -- STATE TRANSITION
+  if( err != 0 ) {
+    iProcessStatus = PS_ABANDONNED;
+    iProcessExitReason = ER_SIGNALFAILED;
+    iProcessExitCode = errno;
+    ClosePipes();
+    return CAE_SIGNAL_FAILED;
+  }
+
+  // otherwise all is ok
+  return CAE_NONE;
+}
+
+
+/*******************************************************************************
+ *
+ * PUBLIC: GetProcessStatus
+ *
+ ******************************************************************************/
+TCAProcessError CAProcess::GetProcessStatus( TProcessStatus *aProcessStatus )
+{
+  int err;
+  int status;
+
+  // if the process is in the PS_STARTED state then we have to verify that it is still running
+  if( iProcessStatus == PS_STARTED ) {
+
+    // see if the process is still running 
+    assert( iPID > 0 );
+    err = waitpid( iPID, &status, WNOHANG );
+
+    // if an error occured then transition from started to abandonned state
+    if( err == -1 ) {
+      iProcessStatus = PS_ABANDONNED;
+      iProcessExitReason = ER_WAITPIDFAILED;
+      iProcessExitCode = errno;
+    }
+
+    // if the process is still running then no change
+    if( err == 0 ) {
+    }
+
+    // if the process has finished then transition from started to stopped state
+    if( err > 0 ) {
+      iProcessStatus = PS_STOPPED;
+      iProcessExitReason = GetExitReasonFromStatus( status, &iProcessExitCode );
+    }
+  }      
+    
+  // if the passed pointer is non-null then we return the status
+  if( aProcessStatus != NULL ) {
+    *aProcessStatus = iProcessStatus;
+  }
+
+  // done
+  return CAE_NONE;
+}
+
+
+/*******************************************************************************
+ *
+ * PUBLIC: WaitForProcessToTerminate
+ *
+ ******************************************************************************/
+TCAProcessError CAProcess::WaitForProcessToTerminate( int aMaxWait )
+{
+  int err;
+  int status;
+  int i;
+
+  // if the process isn't in the PS_STARTED state then this call is invalid -- note
+  // that GetProcessStatus is not used to update the status here since if the 
+  // process has stopped we want this to be caught in the waitpid below, not here.
+  if( iProcessStatus != PS_STARTED ) {
+    return CAE_INVALID_STATE;
+  }
+
+  // Wait for the process. We can't just do a blocking wait since the stdout/stderr
+  // pipes would overflow and people wouldn't be able to read the output as 
+  // expected when the process has terminated. So we do a non-blocking wait in a loop.
+  for( i = 0; (aMaxWait == -1) || (i < aMaxWait); i++ ) {
+    
+    // see if the process has finished
+    err = waitpid( iPID, &status, WNOHANG );
+    if( err != 0 ) {
+      break;
+    }
+
+    // see if there is any data to read in from the pipes -- this will transfer the data 
+    // from the pipes to the internal buffers
+    InternalPollProcessForNewOutput( 0, NULL, NULL );
+  }
+
+  // see if we've timedout
+  if( err == 0 ) {
+    assert( i == aMaxWait );
+    return CAE_TIMEOUT;
+  }
+   
+  // if the call failed otherwise we abandon the process
+  if( err == -1 ) {
+    iProcessStatus = PS_ABANDONNED;
+    iProcessExitReason = ER_WAITPIDFAILED;
+    iProcessExitCode = errno;
+    return CAE_WAITPID_FAILED;
+  }
+
+  // otherwise everything is good and we have stopped the process
+  assert( err > 0 );
+  iProcessStatus = PS_STOPPED;
+  iProcessExitReason = GetExitReasonFromStatus( status, &iProcessExitCode );
+  return CAE_NONE;
+}
+
+/*******************************************************************************
+ *
+ * PUBLIC: Execute() 
+ *
+ ******************************************************************************/
+TCAProcessError CAProcess::Execute( const char *aCommand, int *aErrorCode, int aTimeoutInMilliseconds, string *aStandardOutput, 
+				    string *aStandardError )
+{
+	TCAProcessError err;
+	int i, stdout_bytes_read, stderr_bytes_read, errcode, max_iterations, timeout_in_microseconds;
+	TProcessStatus pstatus;
+	bool record_std_out, record_std_err;
+	struct timespec pause_period = { 0, EXECUTE_PAUSE_PERIOD };
+
+	// validate params
+	assert( aCommand != NULL );
+	assert( aErrorCode != NULL );
+	record_std_out = (aStandardOutput != NULL);
+	record_std_err = (aStandardError != NULL);
+	
+	// start the process
+	err = StartProcess( aCommand, aErrorCode, record_std_out, record_std_err, false );
+	if( err != CAE_NONE ) {
+		return err;
+	}
+
+	// work out the maximum number of iterations
+	timeout_in_microseconds = aTimeoutInMilliseconds * 1000;
+	max_iterations = (timeout_in_microseconds / EXECUTE_PAUSE_PERIOD);
+	//	fprintf( stderr, "DEBUG: timeout_in_micro = %d, pause_period = %d, timeout = %d, max_iterations = %d\n", 
+	//		 timeout_in_microseconds, EXECUTE_PAUSE_PERIOD, aTimeoutInMilliseconds, max_iterations );
+
+	// wait for the process to exit - store the output
+	for( i = 0; (i < max_iterations) || (aTimeoutInMilliseconds == -1); i++ ) {
+
+		// check the status of the process 
+		err = GetProcessStatus( &pstatus );
+		assert( err == CAE_NONE );
+
+		// sanity check on the status
+		assert( (pstatus != PS_INVALID) && (pstatus != PS_INIT) );
+
+		// if the status isn't PS_STARTED then we exit
+		if( pstatus != PS_STARTED ) {
+			break;
+		}
+
+		// get any new output - errors are ignored
+		if( record_std_out || record_std_err ) {
+			PollProcessForNewOutput( 0, &stdout_bytes_read, &stderr_bytes_read );
+			GetRecordedStandardOutput( aStandardOutput, &errcode );
+			GetRecordedStandardError( aStandardError, &errcode );
+		}
+
+		// now wait for one second and then lets do it all again!!
+		nanosleep( &pause_period, NULL );
+	}
+
+	// get the current status
+	err = GetProcessStatus( &pstatus );
+	assert( err == CAE_NONE );
+
+	// if we are still running then send a kill signal and wait to exit
+	if( pstatus == PS_STARTED ) {
+		err = RequestStop( SIGKILL );
+		if( (err != CAE_NONE) && (err != CAE_INVALID_STATE) ) {
+			return err;
+		}
+		err = WaitForProcessToTerminate( -1 );
+		if( err != CAE_NONE ) {
+			return err;
+		}
+	}
+
+	// get any last minute output
+	if( record_std_out || record_std_err ) {
+	  PollProcessForNewOutput( 0, &stdout_bytes_read, &stderr_bytes_read );
+	  GetRecordedStandardOutput( aStandardOutput, &errcode );
+	  GetRecordedStandardError( aStandardError, &errcode );
+	}
+	
+	// we have to return an indication of whether the process timed out
+	return ((i == max_iterations) ? CAE_TIMEOUT : CAE_NONE);
+}
+
+
+/*******************************************************************************
+ *
+ * SECTION-2: DATA CONTROL 
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ * PUBLIC: PollProcessForNewOutput. Polls the executing process for new output.
+ * Any output is then transferred to the internal buffers.
+ *
+ ******************************************************************************/
+TCAProcessError CAProcess::PollProcessForNewOutput( int aMaxWait, int *aStandardOutputRead, int *aStandardErrorRead )
+{
+  GetProcessStatus( NULL );
+  if( iProcessStatus == PS_INIT ) {
+    return CAE_INVALID_STATE;
+  }
+  return InternalPollProcessForNewOutput( aMaxWait, aStandardOutputRead, aStandardErrorRead );
+}
+
+TCAProcessError CAProcess::InternalPollProcessForNewOutput( int aMaxWait, int *aStandardOutputRead, int *aStandardErrorRead )
+{
+    struct timeval maxWait;
+    fd_set readset;
+    int fdsToProcess;
+    int is_set;
+    int err_stdout = 0;
+    int err_stderr = 0;
+
+    // initialise the out params
+    if( aStandardOutputRead != NULL ) {
+      *aStandardOutputRead = 0;
+    }
+    if( aStandardErrorRead != NULL ) {
+      *aStandardErrorRead = 0;
+    }
+
+	// make sure one of the pipes is still open
+	if( (iStdOutPipe[0] == INVALID_PIPE_DESC) && (iStdErrPipe[0] == INVALID_PIPE_DESC) ) {
+		return CAE_NONE;
+	}
+
+	// Perform a select (of upto the maximum allowed time) and read in the available data.
+    FD_ZERO( &readset );
+	if( iStdOutPipe[0] != INVALID_PIPE_DESC ) {
+	    FD_SET( iStdOutPipe[0], &readset );
+	}
+	if( iStdErrPipe[0] != INVALID_PIPE_DESC ) {
+	    FD_SET( iStdErrPipe[0], &readset );
+	}
+    maxWait.tv_sec = aMaxWait;
+    maxWait.tv_usec = 0;
+    fdsToProcess = select( FD_SETSIZE, &readset, NULL, NULL, &maxWait );
+    if( fdsToProcess < 0 ) {
+      iReadStdoutError = iReadStderrError = errno;
+      return CAE_SELECT_FAILED;
+    }
+
+    // If there is no data to read then exit
+    if( fdsToProcess == 0 ) {
+      return CAE_NONE;
+    }
+
+    // Read the stdout pipe
+	if( iStdOutPipe[0] != INVALID_PIPE_DESC ) {
+	    is_set = FD_ISSET( iStdOutPipe[0], &readset );
+		if( is_set != 0 ) {
+			err_stdout = ReadOutput( &(iStdOutPipe[0]), &iRecordedStdOut, iRecordStdOut, &iReadStdoutError );
+			if( aStandardOutputRead != NULL ) {
+				*aStandardOutputRead = iRecordedStdOut.length();
+			}
+		}
+    }
+
+    // Read the stderr pipe
+	if( iStdErrPipe[0] != INVALID_PIPE_DESC ) {
+	    is_set = FD_ISSET( iStdErrPipe[0], &readset );
+		if( is_set != 0 ) { 
+			err_stderr = ReadOutput( &(iStdErrPipe[0]), &iRecordedStdErr, iRecordStdErr, &iReadStderrError );
+			if( aStandardErrorRead != NULL ) {
+				*aStandardErrorRead = iRecordedStdErr.length();
+			}
+		}
+    }
+    
+    // If one of the reads returned an error then return this
+    if( (err_stdout != 0) || (err_stderr != 0) ) {
+      return CAE_READ_FAILED;
+    }
+    return CAE_NONE;
+}
+
+
+/*******************************************************************************
+ *
+ * PUBLIC: GetRecordedStandardOutput
+ *
+ ******************************************************************************/
+int CAProcess::GetRecordedStandardOutput( string *aStdout, int *aReadError )
+{
+  // validate params
+  assert( aReadError != NULL );
+  if( aStdout == NULL ) {
+    return 0;
+  }
+
+  // append a copy of the stored data to the passed string
+  (*aStdout) += iRecordedStdOut;
+
+  // clear the current string
+  iRecordedStdOut.erase( 0, iRecordedStdOut.length() );
+
+  // set the read error 
+  *aReadError = iReadStdoutError;
+  iReadStdoutError = 0;
+
+  // return the number of bytes read
+  return aStdout->size();
+}
+
+
+/*******************************************************************************
+ *
+ * PUBLIC: GetRecordedStandardError
+ *
+ ******************************************************************************/
+int CAProcess::GetRecordedStandardError( string *aStdErr, int *aReadError )
+{
+  // validate params
+  assert( aReadError != NULL );
+  if( aStdErr == NULL ) {
+    return 0;
+  }
+  
+  // append a copy of the stored data to the passed string
+  (*aStdErr) += iRecordedStdErr;
+
+  // clear the current string
+  iRecordedStdErr.erase( 0, iRecordedStdErr.length() );
+
+  // set the read error 
+  *aReadError = iReadStderrError;
+  iReadStderrError = 0;
+
+  // return the number of bytes read
+  return aStdErr->size();
+}
+
+
+/*******************************************************************************
+ *
+ * SECTION-3: General Accessors
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ * PUBLIC: GetCommandString
+ *
+ ******************************************************************************/
+string CAProcess::GetCommandString()
+{
+  string cmd;
+  if( iProcessStatus == PS_INIT ) {
+    return cmd;
+  }
+  cmd = *iCommand;
+  return cmd;
+}
+
+/*******************************************************************************
+ *
+ * PUBLIC: GetExitReason
+ *
+ ******************************************************************************/  
+TCAProcessError CAProcess::GetExitReason( TProcessExitReason *aExitReason )
+{
+  // check the state -- otherwise this is not valid
+  if( (iProcessStatus != PS_STOPPED) && (iProcessStatus != PS_ABANDONNED) ) {
+    return CAE_INVALID_STATE;
+  }
+
+  // set the value
+  if( aExitReason != NULL ) {
+    *aExitReason = iProcessExitReason;
+  }
+ 
+  // done
+  return CAE_NONE;
+}
+
+/*******************************************************************************
+ *
+ * PUBLIC: GetExitCode
+ *
+ ******************************************************************************/  
+TCAProcessError CAProcess::GetExitCode( int *aExitCode )
+{
+  // check the state -- otherwise this is not valid
+  if( (iProcessStatus != PS_STOPPED) && (iProcessStatus != PS_ABANDONNED) ) {
+    return CAE_INVALID_STATE;
+  }
+
+  // set the value
+  if( aExitCode != NULL ) {
+    *aExitCode = iProcessExitCode;
+  }
+ 
+  // done
+  return CAE_NONE;
+}
+
+
+/*******************************************************************************
+ *
+ * SECTION-4: Helpers
+ *
+ ******************************************************************************/  
+
+/*******************************************************************************
+ *
+ * PRIVATE: GetExitReasonFromStatus
+ *
+ ******************************************************************************/
+TProcessExitReason CAProcess::GetExitReasonFromStatus( int aStatus, int *aExitCode )
+{
+  assert( aExitCode != NULL );
+  if( WIFEXITED(aStatus) ) {
+    *aExitCode = WEXITSTATUS(aStatus);
+    return ER_EXITED;
+  }
+  if( WIFSIGNALED(aStatus) ) {
+    *aExitCode = WTERMSIG(aStatus);
+    return ER_SIGNALLED;
+  }
+  return ER_UNKNOWN;
+}
+
+/*******************************************************************************
+ *
+ * PRIVATE: ReadOutput
+ *
+ ******************************************************************************/  
+TCAProcessError CAProcess::ReadOutput( int *aFileDes, string *aBuffer, bool aStoreFlag, int *aReadError )
+{
+    char buff[READ_BUFFER_SIZE];
+    int bytesRead;
+
+    // check params
+    assert( aBuffer != NULL );
+    assert( aReadError != NULL );
+    assert( aFileDes != NULL );
+
+    // read until there is nothing left to read
+    while( true ) {
+
+      // read
+      bytesRead = read( (*aFileDes), &buff, READ_BUFFER_SIZE-1 );
+      
+      // null-terminate the buffer
+      if( bytesRead > 0 ) {
+	buff[bytesRead] = 0;
+      }
+
+      // temporarily unavailable resource is not an error 
+      if( (bytesRead == -1) && (errno == EAGAIN) ) {
+	return CAE_NONE;
+      }
+
+      // check for error 
+      if( bytesRead == -1 ) {
+	fprintf( stderr, "DEBUG: read() returned error %s\n", strerror(errno) );
+	*aReadError = errno;
+	return CAE_READ_FAILED;
+      }
+
+      // check for no more data -- if this is the case then close the pipe
+      if( bytesRead == 0 ) {
+	ClosePipeDesc( aFileDes );
+	return CAE_NONE;
+      }
+
+      // store the data in the object if requested 
+      if( aStoreFlag ) {
+	fflush( stderr );
+	(*aBuffer) += buff;
+      }
+    }
+ 
+    // should never get here
+    assert( !"Invalid code path" );
+    return CAE_NONE;
+}
+
+/*******************************************************************************
+ *
+ * PRIVATE: CreatePipes / ClosePipes / ClosePipePair
+ *
+ ******************************************************************************/
+TCAProcessError CAProcess::CreatePipes( int *aErrorCode )
+{
+  int err;
+  int flags;
+
+  // check params
+  assert( aErrorCode != NULL );
+
+  // create the stdin pipe
+  err = pipe( iStdInPipe );
+  if( err != 0 ) {
+    *aErrorCode = errno;
+    return CAE_FAILED_TO_CREATE_PIPE;
+  }
+
+  // create the stdout pipe
+  err = pipe( iStdOutPipe );
+  if( err != 0 ) {
+    ClosePipes();
+    *aErrorCode = errno;
+    return CAE_FAILED_TO_CREATE_PIPE;
+  }
+
+  // create the stderr pipe
+  err = pipe( iStdErrPipe );
+  if( err != 0 ) {
+    ClosePipes();
+    *aErrorCode = errno;
+    return CAE_FAILED_TO_CREATE_PIPE;
+  }
+
+  // make the read end of the stdout pipe non-blocking
+  flags = fcntl( iStdOutPipe[0], F_GETFL, 0 );
+  flags |= O_NONBLOCK;
+  err = fcntl( iStdOutPipe[0], F_SETFL, flags );
+  if( err != 0 ) {
+    ClosePipes();
+    *aErrorCode = errno;
+    return CAE_FAILED_TO_SET_NONBLOCKING;
+  }
+
+  // make the read end of the stderr pipe non-blocking    
+  flags = fcntl( iStdErrPipe[0], F_GETFL, 0 );
+  flags |= O_NONBLOCK;
+  err = fcntl( iStdErrPipe[0], F_SETFL, flags );
+  if( err != 0 ) {
+    ClosePipes();
+    *aErrorCode = errno;
+    return CAE_FAILED_TO_SET_NONBLOCKING;
+  }
+
+  // OK
+  return CAE_NONE;
+}
+
+void CAProcess::ClosePipes()
+{
+  ClosePipePair( iStdInPipe );
+  ClosePipePair( iStdOutPipe );
+  ClosePipePair( iStdErrPipe );
+}
+
+
+void CAProcess::ClosePipePair( int *aPipes )
+{
+	assert( aPipes != NULL );
+	ClosePipeDesc( &(aPipes[0]) );
+	ClosePipeDesc( &(aPipes[1]) );
+}
+
+
+void CAProcess::ClosePipeDesc( int *aPipeDescriptor )
+{
+	assert( aPipeDescriptor != NULL );
+	close( *aPipeDescriptor );
+	*aPipeDescriptor = INVALID_PIPE_DESC;
+}
+