summaryrefslogtreecommitdiffstats
path: root/Source/kwsys/ProcessUNIX.c
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2003-12-03 14:20:05 (GMT)
committerBrad King <brad.king@kitware.com>2003-12-03 14:20:05 (GMT)
commitad8bc4b1a43dd017d361f189ec2b38d97e7de987 (patch)
tree5133b13c81008845db91c734e9872b66767d7c29 /Source/kwsys/ProcessUNIX.c
parentbe15d66e37d000993833a1e5a614ec969c86f7c8 (diff)
downloadCMake-ad8bc4b1a43dd017d361f189ec2b38d97e7de987.zip
CMake-ad8bc4b1a43dd017d361f189ec2b38d97e7de987.tar.gz
CMake-ad8bc4b1a43dd017d361f189ec2b38d97e7de987.tar.bz2
ENH: Merged changes from KWSys-MultiProcess-bp to KWSys-MultiProcess-b2t-1-mp to main tree. This introduces support for process pipelines.
Diffstat (limited to 'Source/kwsys/ProcessUNIX.c')
-rw-r--r--Source/kwsys/ProcessUNIX.c543
1 files changed, 415 insertions, 128 deletions
diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c
index b1c40ab..c5bae27 100644
--- a/Source/kwsys/ProcessUNIX.c
+++ b/Source/kwsys/ProcessUNIX.c
@@ -33,6 +33,17 @@ conjunction with the timeout on the select call to implement a
timeout for program even when it closes stdout and stderr.
*/
+/*
+
+TODO:
+
+We cannot create the pipeline of processes in suspended states. How
+do we cleanup processes already started when one fails to load? Right
+now we are just killing them, which is probably not the right thing to
+do.
+
+*/
+
#include <stdio.h> /* snprintf */
#include <stdlib.h> /* malloc, free */
#include <string.h> /* strdup, strerror, memset */
@@ -46,23 +57,35 @@ timeout for program even when it closes stdout and stderr.
#include <signal.h> /* sigaction */
/* The number of pipes for the child's output. The standard stdout
- and stderr pipes are the first two. One more pipe is used for the
- child to report errors to the parent before the real process is
- invoked. */
+ and stderr pipes are the first two. One more pipe is used to
+ detect when the child process has terminated. The third pipe is
+ not given to the child process, so it cannot close it until it
+ terminates. */
#define KWSYSPE_PIPE_COUNT 3
#define KWSYSPE_PIPE_STDOUT 0
#define KWSYSPE_PIPE_STDERR 1
-#define KWSYSPE_PIPE_ERROR 2
+#define KWSYSPE_PIPE_TERM 2
/* The maximum amount to read from a pipe at a time. */
#define KWSYSPE_PIPE_BUFFER_SIZE 1024
typedef struct timeval kwsysProcessTime;
+typedef struct kwsysProcessCreateInformation_s
+{
+ int stdin;
+ int stdout;
+ int stderr;
+ int term;
+ int error[2];
+} kwsysProcessCreateInformation;
+
/*--------------------------------------------------------------------------*/
-static void kwsysProcessInitialize(kwsysProcess* cp);
+static int kwsysProcessInitialize(kwsysProcess* cp);
static void kwsysProcessCleanup(kwsysProcess* cp, int error);
static void kwsysProcessCleanupDescriptor(int* pfd);
+static int kwsysProcessCreate(kwsysProcess* cp, int index,
+ kwsysProcessCreateInformation* si, int* readEnd);
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
kwsysProcessTime* timeoutTime);
static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime,
@@ -73,30 +96,28 @@ static kwsysProcessTime kwsysProcessTimeFromDouble(double d);
static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2);
static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2);
static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2);
-static void kwsysProcessChildErrorExit(kwsysProcess* cp);
+static void kwsysProcessChildErrorExit(int errorPipe);
static void kwsysProcessRestoreDefaultSignalHandlers();
/*--------------------------------------------------------------------------*/
/* Structure containing data used to implement the child's execution. */
struct kwsysProcess_s
{
- /* The command line to execute. */
- char** Command;
+ /* The command lines to execute. */
+ char*** Commands;
+ int NumberOfCommands;
/* Descriptors for the read ends of the child's output pipes. */
int PipeReadEnds[KWSYSPE_PIPE_COUNT];
- /* Descriptors for the write ends of the child's output pipes. */
- int PipeWriteEnds[KWSYSPE_PIPE_COUNT];
-
/* Buffer for pipe data. */
char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
- /* Process ID returned by the fork. */
- pid_t ForkPID;
+ /* Process IDs returned by the calls to fork. */
+ pid_t* ForkPIDs;
- /* Flag for whether the child reported an error. */
- int ChildError;
+ /* Flag for whether the children were terminated by a faild select. */
+ int SelectError;
/* The timeout length. */
double Timeout;
@@ -140,7 +161,9 @@ struct kwsysProcess_s
/* Buffer for error message in case of failure. */
char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
- int ErrorMessageLength;
+
+ /* The exit codes of each child process in the pipeline. */
+ int* CommandExitCodes;
};
/*--------------------------------------------------------------------------*/
@@ -169,36 +192,109 @@ void kwsysProcess_Delete(kwsysProcess* cp)
/* Free memory. */
kwsysProcess_SetCommand(cp, 0);
kwsysProcess_SetWorkingDirectory(cp, 0);
+ if(cp->CommandExitCodes)
+ {
+ free(cp->CommandExitCodes);
+ }
free(cp);
}
/*--------------------------------------------------------------------------*/
-void kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
+int kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
{
- if(cp->Command)
+ int i;
+ for(i=0; i < cp->NumberOfCommands; ++i)
{
- char** c = cp->Command;
+ char** c = cp->Commands[i];
while(*c)
{
free(*c++);
}
- free(cp->Command);
- cp->Command = 0;
+ free(cp->Commands[i]);
+ }
+ cp->NumberOfCommands = 0;
+ if(cp->Commands)
+ {
+ free(cp->Commands);
+ cp->Commands = 0;
}
if(command)
{
- char const* const* c = command;
- int n = 0;
- int i = 0;
- while(*c++);
- n = c - command - 1;
- cp->Command = (char**)malloc((n+1)*sizeof(char*));
- for(i=0; i < n; ++i)
+ return kwsysProcess_AddCommand(cp, command);
+ }
+ return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command)
+{
+ int newNumberOfCommands;
+ char*** newCommands;
+
+ /* Make sure we have a command to add. */
+ if(!command)
+ {
+ return 0;
+ }
+
+ /* Allocate a new array for command pointers. */
+ newNumberOfCommands = cp->NumberOfCommands + 1;
+ if(!(newCommands = (char***)malloc(sizeof(char**) * newNumberOfCommands)))
+ {
+ /* Out of memory. */
+ return 0;
+ }
+
+ /* Copy any existing commands into the new array. */
+ {
+ int i;
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ newCommands[i] = cp->Commands[i];
+ }
+ }
+
+ /* Add the new command. */
+ {
+ char const* const* c = command;
+ int n = 0;
+ int i = 0;
+ while(*c++);
+ n = c - command - 1;
+ newCommands[cp->NumberOfCommands] = (char**)malloc((n+1)*sizeof(char*));
+ if(!newCommands[cp->NumberOfCommands])
+ {
+ /* Out of memory. */
+ free(newCommands);
+ return 0;
+ }
+ for(i=0; i < n; ++i)
+ {
+ newCommands[cp->NumberOfCommands][i] = strdup(command[i]);
+ if(!newCommands[cp->NumberOfCommands][i])
+ {
+ break;
+ }
+ }
+ if(i < n)
+ {
+ /* Out of memory. */
+ for(;i > 0; --i)
{
- cp->Command[i] = strdup(command[i]);
+ free(newCommands[cp->NumberOfCommands][i-1]);
}
- cp->Command[n] = 0;
+ free(newCommands);
+ return 0;
}
+ newCommands[cp->NumberOfCommands][n] = 0;
+ }
+
+ /* Successfully allocated new command array. Free the old array. */
+ free(cp->Commands);
+ cp->Commands = newCommands;
+ cp->NumberOfCommands = newNumberOfCommands;
+
+ return 1;
}
/*--------------------------------------------------------------------------*/
@@ -289,6 +385,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
{
int i;
struct sigaction newSigChldAction;
+ kwsysProcessCreateInformation si = {-1, -1, -1, -1, {-1, -1}};
/* Do not execute a second copy simultaneously. */
if(cp->State == kwsysProcess_State_Executing)
@@ -297,7 +394,12 @@ void kwsysProcess_Execute(kwsysProcess* cp)
}
/* Initialize the control structure for a new process. */
- kwsysProcessInitialize(cp);
+ if(!kwsysProcessInitialize(cp))
+ {
+ strcpy(cp->ErrorMessage, "Out of memory");
+ cp->State = kwsysProcess_State_Error;
+ return;
+ }
/* We want no special handling of SIGCHLD. Repeat call until it is
not interrupted. */
@@ -306,29 +408,37 @@ void kwsysProcess_Execute(kwsysProcess* cp)
while((sigaction(SIGCHLD, &newSigChldAction, &cp->OldSigChldAction) < 0) &&
(errno == EINTR));
- /* Create pipes for subprocess output. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ /* Setup the stderr and termination pipes to be shared by all processes. */
+ for(i=KWSYSPE_PIPE_STDERR; i < KWSYSPE_PIPE_COUNT; ++i)
{
- int p[2];
-
/* Create the pipe. */
+ int p[2];
if(pipe(p) < 0)
{
kwsysProcessCleanup(cp, 1);
return;
}
+ /* Store the pipe. */
+ cp->PipeReadEnds[i] = p[0];
+ if(i == KWSYSPE_PIPE_STDERR)
+ {
+ si.stderr = p[1];
+ }
+ else
+ {
+ si.term = p[1];
+ }
+
/* Set close-on-exec flag on the pipe's ends. */
if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
(fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
{
kwsysProcessCleanup(cp, 1);
+ kwsysProcessCleanupDescriptor(&si.stderr);
+ kwsysProcessCleanupDescriptor(&si.term);
return;
}
-
- /* Store the pipe. */
- cp->PipeReadEnds[i] = p[0];
- cp->PipeWriteEnds[i] = p[1];
}
/* The timeout period starts now. */
@@ -336,62 +446,37 @@ void kwsysProcess_Execute(kwsysProcess* cp)
cp->TimeoutTime.tv_sec = -1;
cp->TimeoutTime.tv_usec = -1;
- /* Fork off a child process. */
- cp->ForkPID = fork();
- if(cp->ForkPID < 0)
- {
- kwsysProcessCleanup(cp, 1);
- return;
- }
-
- /* If this is the child process, run the real process. */
- if(cp->ForkPID == 0)
+ /* Create the pipeline of processes. */
+ {
+ int readEnd = 0;
+ for(i=0; i < cp->NumberOfCommands; ++i)
{
- /* We used to close stdin, but some programs do not like being run
- without stdin. Just use whatever stdin the parent program is
- using. */
- /*close(0);*/
-
- /* Setup the stdout/stderr pipes. */
- dup2(cp->PipeWriteEnds[KWSYSPE_PIPE_STDOUT], 1);
- dup2(cp->PipeWriteEnds[KWSYSPE_PIPE_STDERR], 2);
-
- /* Clear the close-on-exec flag for stdout, stderr, and the child
- error report pipe. All other pipe handles will be closed when
- exec succeeds. */
- fcntl(1, F_SETFD, 0);
- fcntl(2, F_SETFD, 0);
- fcntl(cp->PipeWriteEnds[KWSYSPE_PIPE_ERROR], F_SETFD, 0);
-
- /* Restore all default signal handlers. */
- kwsysProcessRestoreDefaultSignalHandlers();
-
- /* Change to the working directory specified, if any. */
- if(cp->WorkingDirectory)
+ if(!kwsysProcessCreate(cp, i, &si, &readEnd))
{
- /* Some platforms specify that the chdir call may be
- interrupted. Repeat the call until it finishes. */
- int r;
- while(((r = chdir(cp->WorkingDirectory)) < 0) && (errno == EINTR));
- if(r < 0)
+ kwsysProcessCleanup(cp, 1);
+
+ /* Release resources that may have been allocated for this
+ process before an error occurred. */
+ kwsysProcessCleanupDescriptor(&readEnd);
+ if(i > 0)
{
- /* Failure. Report error to parent and terminate. */
- kwsysProcessChildErrorExit(cp);
+ kwsysProcessCleanupDescriptor(&si.stdin);
}
+ kwsysProcessCleanupDescriptor(&si.stdout);
+ kwsysProcessCleanupDescriptor(&si.stderr);
+ kwsysProcessCleanupDescriptor(&si.term);
+ kwsysProcessCleanupDescriptor(&si.error[0]);
+ kwsysProcessCleanupDescriptor(&si.error[1]);
+ return;
}
-
- /* Execute the real process. If successful, this does not return. */
- execvp(cp->Command[0], cp->Command);
-
- /* Failure. Report error to parent and terminate. */
- kwsysProcessChildErrorExit(cp);
}
+ /* Save a handle to the output pipe for the last process. */
+ cp->PipeReadEnds[KWSYSPE_PIPE_STDOUT] = readEnd;
+ }
- /* The parent process does not need the pipe write ends. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- kwsysProcessCleanupDescriptor(&cp->PipeWriteEnds[i]);
- }
+ /* The parent process does not need the output pipe write ends. */
+ kwsysProcessCleanupDescriptor(&si.stderr);
+ kwsysProcessCleanupDescriptor(&si.term);
/* All the pipes are now open. */
cp->PipesLeft = KWSYSPE_PIPE_COUNT;
@@ -401,8 +486,8 @@ void kwsysProcess_Execute(kwsysProcess* cp)
}
/*--------------------------------------------------------------------------*/
-int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* length,
- double* userTimeout)
+int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data,
+ int* length, double* userTimeout)
{
int i;
int max = -1;
@@ -448,22 +533,9 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
if(n > 0)
{
/* We have data on this pipe. */
- if(i == KWSYSPE_PIPE_ERROR)
+ if(i == KWSYSPE_PIPE_TERM)
{
- /* This is data on the special error reporting pipe. The
- child process failed to execute the program. */
- cp->ChildError = 1;
- if(n > KWSYSPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength)
- {
- n = KWSYSPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength;
- }
- if(n > 0)
- {
- memcpy(cp->ErrorMessage+cp->ErrorMessageLength,
- cp->PipeBuffer, n);
- cp->ErrorMessageLength += n;
- cp->ErrorMessage[cp->ErrorMessageLength] = 0;
- }
+ /* This is data on the special termination pipe. Ignore it. */
}
else if(pipes & (1 << i))
{
@@ -548,10 +620,10 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
pipe buffer. */
strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
- /* Kill the child now. */
+ /* Kill the children now. */
kwsysProcess_Kill(cp);
cp->Killed = 0;
- cp->ChildError = 1;
+ cp->SelectError = 1;
cp->PipesLeft = 0;
}
}
@@ -586,7 +658,7 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
}
else
{
- /* The process timeout has expired. Kill the child now. */
+ /* The process timeout has expired. Kill the children now. */
kwsysProcess_Kill(cp);
cp->Killed = 0;
cp->TimeoutExpired = 1;
@@ -623,19 +695,36 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
}
}
- /* Wait for the child to terminate. The process should have already
- exited because KWSYSPE_PIPE_ERROR has been closed by this point.
- Repeat the call until it is not interrupted. */
- while(((result = waitpid(cp->ForkPID, &status, 0)) < 0) && (errno == EINTR));
- if(result <= 0)
+ /* Wait for each child to terminate. The process should have
+ already exited because KWSYSPE_PIPE_TERM has been closed by this
+ point. Repeat the call until it is not interrupted. */
+ {
+ int i;
+ for(i=0; i < cp->NumberOfCommands; ++i)
{
- /* Unexpected error. */
- kwsysProcessCleanup(cp, 1);
+ while(((result = waitpid(cp->ForkPIDs[i],
+ &cp->CommandExitCodes[i], 0)) < 0) &&
+ (errno == EINTR));
+ if(result <= 0 && cp->State != kwsysProcess_State_Error)
+ {
+ /* Unexpected error. Report the first time this happens. */
+ strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
+ cp->State = kwsysProcess_State_Error;
+ }
+ }
+ }
+
+ /* Check if there was an error in one of the waitpid calls. */
+ if(cp->State == kwsysProcess_State_Error)
+ {
+ /* The error message is already in its buffer. Tell
+ kwsysProcessCleanup to not create it. */
+ kwsysProcessCleanup(cp, 0);
return 1;
}
/* Check whether the child reported an error invoking the process. */
- if(cp->ChildError)
+ if(cp->SelectError)
{
/* The error message is already in its buffer. Tell
kwsysProcessCleanup to not create it. */
@@ -644,6 +733,9 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
return 1;
}
+ /* Use the status of the last process in the pipeline. */
+ status = cp->CommandExitCodes[cp->NumberOfCommands-1];
+
/* Determine the outcome. */
if(cp->Killed)
{
@@ -703,29 +795,35 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
/*--------------------------------------------------------------------------*/
void kwsysProcess_Kill(kwsysProcess* cp)
{
+ int i;
+
/* Make sure we are executing a process. */
if(cp->State != kwsysProcess_State_Executing)
{
return;
}
- /* Kill the child. */
+ /* Kill the children. */
cp->Killed = 1;
- kill(cp->ForkPID, SIGKILL);
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ if(cp->ForkPIDs[i])
+ {
+ kill(cp->ForkPIDs[i], SIGKILL);
+ }
+ }
}
/*--------------------------------------------------------------------------*/
/* Initialize a process control structure for kwsysProcess_Execute. */
-static void kwsysProcessInitialize(kwsysProcess* cp)
+static int kwsysProcessInitialize(kwsysProcess* cp)
{
int i;
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
cp->PipeReadEnds[i] = -1;
- cp->PipeWriteEnds[i] = -1;
}
- cp->ForkPID = -1;
- cp->ChildError = 0;
+ cp->SelectError = 0;
cp->StartTime.tv_sec = -1;
cp->StartTime.tv_usec = -1;
cp->TimeoutTime.tv_sec = -1;
@@ -739,7 +837,30 @@ static void kwsysProcessInitialize(kwsysProcess* cp)
cp->ExitCode = 1;
cp->ExitValue = 1;
cp->ErrorMessage[0] = 0;
- cp->ErrorMessageLength = 0;
+
+ if(cp->ForkPIDs)
+ {
+ free(cp->ForkPIDs);
+ }
+ cp->ForkPIDs = (pid_t*)malloc(sizeof(pid_t)*cp->NumberOfCommands);
+ if(!cp->ForkPIDs)
+ {
+ return 0;
+ }
+ memset(cp->ForkPIDs, 0, sizeof(pid_t)*cp->NumberOfCommands);
+
+ if(cp->CommandExitCodes)
+ {
+ free(cp->CommandExitCodes);
+ }
+ cp->CommandExitCodes = (int*)malloc(sizeof(int)*cp->NumberOfCommands);
+ if(!cp->CommandExitCodes)
+ {
+ return 0;
+ }
+ memset(cp->CommandExitCodes, 0, sizeof(int)*cp->NumberOfCommands);
+
+ return 1;
}
/*--------------------------------------------------------------------------*/
@@ -749,22 +870,46 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int error)
{
int i;
- /* If cleaning up due to an error, report the error message. */
if(error)
{
- strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
+ /* We are cleaning up due to an error. Report the error message
+ if one has not been provided already. */
+ if(cp->ErrorMessage[0] == 0)
+ {
+ strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
+ }
+
+ /* Set the error state. */
cp->State = kwsysProcess_State_Error;
+
+ /* Kill any children already started. */
+ if(cp->ForkPIDs)
+ {
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ if(cp->ForkPIDs[i])
+ {
+ kill(cp->ForkPIDs[i], SIGKILL);
+ }
+ }
+ }
}
/* Restore the SIGCHLD handler. */
while((sigaction(SIGCHLD, &cp->OldSigChldAction, 0) < 0) &&
(errno == EINTR));
+ /* Free memory. */
+ if(cp->ForkPIDs)
+ {
+ free(cp->ForkPIDs);
+ cp->ForkPIDs = 0;
+ }
+
/* Close pipe handles. */
for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
- kwsysProcessCleanupDescriptor(&cp->PipeWriteEnds[i]);
}
}
@@ -782,6 +927,148 @@ static void kwsysProcessCleanupDescriptor(int* pfd)
}
/*--------------------------------------------------------------------------*/
+int kwsysProcessCreate(kwsysProcess* cp, int index,
+ kwsysProcessCreateInformation* si, int* readEnd)
+{
+ /* Setup the process's stdin. */
+ if(index > 0)
+ {
+ si->stdin = *readEnd;
+ *readEnd = 0;
+ }
+ else
+ {
+ si->stdin = 0;
+ }
+
+ /* Setup the process's stdout. */
+ {
+ /* Create the pipe. */
+ int p[2];
+ if(pipe(p) < 0)
+ {
+ return 0;
+ }
+ *readEnd = p[0];
+ si->stdout = p[1];
+
+ /* Set close-on-exec flag on the pipe's ends. */
+ if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
+ (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
+ {
+ return 0;
+ }
+ }
+
+ /* Create the error reporting pipe. */
+ if(pipe(si->error) < 0)
+ {
+ return 0;
+ }
+
+ /* Set close-on-exec flag on the error pipe's write end. */
+ if(fcntl(si->error[1], F_SETFD, FD_CLOEXEC) < 0)
+ {
+ return 0;
+ }
+
+ /* Fork off a child process. */
+ cp->ForkPIDs[index] = fork();
+ if(cp->ForkPIDs[index] < 0)
+ {
+ return 0;
+ }
+
+ if(cp->ForkPIDs[index] == 0)
+ {
+ /* Close the read end of the error reporting pipe. */
+ close(si->error[0]);
+
+ /* Setup the stdin, stdout, and stderr pipes. */
+ if(index > 0)
+ {
+ dup2(si->stdin, 0);
+ }
+ dup2(si->stdout, 1);
+ dup2(si->stderr, 2);
+
+ /* Clear the close-on-exec flag for stdin, stdout, and stderr.
+ Also clear it for the termination pipe. All other pipe handles
+ will be closed when exec succeeds. */
+ fcntl(0, F_SETFD, 0);
+ fcntl(1, F_SETFD, 0);
+ fcntl(2, F_SETFD, 0);
+ fcntl(si->term, F_SETFD, 0);
+
+ /* Restore all default signal handlers. */
+ kwsysProcessRestoreDefaultSignalHandlers();
+
+ /* Change to the working directory specified, if any. */
+ if(cp->WorkingDirectory)
+ {
+ /* Some platforms specify that the chdir call may be
+ interrupted. Repeat the call until it finishes. */
+ int r;
+ while(((r = chdir(cp->WorkingDirectory)) < 0) && (errno == EINTR));
+ if(r < 0)
+ {
+ /* Failure. Report error to parent and terminate. */
+ kwsysProcessChildErrorExit(si->error[1]);
+ }
+ }
+
+ /* Execute the real process. If successful, this does not return. */
+ execvp(cp->Commands[index][0], cp->Commands[index]);
+
+ /* Failure. Report error to parent and terminate. */
+ kwsysProcessChildErrorExit(si->error[1]);
+ }
+
+ /* We are done with the error reporting pipe write end. */
+ kwsysProcessCleanupDescriptor(&si->error[1]);
+
+ /* Block until the child's exec call succeeds and closes the error
+ pipe or writes data to the pipe to report an error. */
+ {
+ int total = 0;
+ int n = 1;
+ /* Read the entire error message up to the length of our buffer. */
+ while(total < KWSYSPE_PIPE_BUFFER_SIZE && n > 0)
+ {
+ /* Keep trying to read until the operation is not interrupted. */
+ while(((n = read(si->error[0], cp->ErrorMessage+total,
+ KWSYSPE_PIPE_BUFFER_SIZE-total)) < 0) &&
+ (errno == EINTR));
+ if(n > 0)
+ {
+ total += n;
+ }
+ }
+
+ /* We are done with the error reporting pipe read end. */
+ kwsysProcessCleanupDescriptor(&si->error[0]);
+
+ if(total > 0)
+ {
+ /* The child failed to execute the process. */
+ return 0;
+ }
+ }
+
+ /* Successfully created this child process. */
+ if(index > 0)
+ {
+ /* The parent process does not need the input pipe read end. */
+ kwsysProcessCleanupDescriptor(&si->stdin);
+ }
+
+ /* The parent process does not need the output pipe write ends. */
+ kwsysProcessCleanupDescriptor(&si->stdout);
+
+ return 1;
+}
+
+/*--------------------------------------------------------------------------*/
/* Get the time at which either the process or user timeout will
expire. Returns 1 if the user timeout is first, and 0 otherwise. */
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
@@ -905,14 +1192,14 @@ static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProc
/* When the child process encounters an error before its program is
invoked, this is called to report the error to the parent and
exit. */
-static void kwsysProcessChildErrorExit(kwsysProcess* cp)
+static void kwsysProcessChildErrorExit(int errorPipe)
{
/* Construct the error message. */
char buffer[KWSYSPE_PIPE_BUFFER_SIZE];
strncpy(buffer, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE);
/* Report the error to the parent through the special pipe. */
- write(cp->PipeWriteEnds[KWSYSPE_PIPE_ERROR], buffer, strlen(buffer));
+ write(errorPipe, buffer, strlen(buffer));
/* Terminate without cleanup. */
_exit(1);