summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--Source/kwsys/Process.h.in19
-rw-r--r--Source/kwsys/ProcessFwd9x.c109
-rw-r--r--Source/kwsys/ProcessUNIX.c543
-rw-r--r--Source/kwsys/ProcessWin32.c1001
4 files changed, 1182 insertions, 490 deletions
diff --git a/Source/kwsys/Process.h.in b/Source/kwsys/Process.h.in
index c97212f..36d24f0 100644
--- a/Source/kwsys/Process.h.in
+++ b/Source/kwsys/Process.h.in
@@ -30,6 +30,7 @@
#define kwsysProcess_New kwsys(Process_New)
#define kwsysProcess_Delete kwsys(Process_Delete)
#define kwsysProcess_SetCommand kwsys(Process_SetCommand)
+#define kwsysProcess_AddCommand kwsys(Process_AddCommand)
#define kwsysProcess_SetTimeout kwsys(Process_SetTimeout)
#define kwsysProcess_SetWorkingDirectory kwsys(Process_SetWorkingDirectory)
#define kwsysProcess_Option_HideWindow kwsys(Process_Option_HideWindow)
@@ -89,10 +90,21 @@ kwsysEXPORT void kwsysProcess_Delete(kwsysProcess* cp);
/**
* Set the command line to be executed. Argument is an array of
* pointers to the command and each argument. Ths array must end with
- * a NULL pointer.
+ * a NULL pointer. Any previous command lines are removed. Returns
+ * 1 for success and 0 otherwise.
*/
-kwsysEXPORT void kwsysProcess_SetCommand(kwsysProcess* cp,
- char const* const* command);
+kwsysEXPORT int kwsysProcess_SetCommand(kwsysProcess* cp,
+ char const* const* command);
+
+/**
+ * Add a command line to be executed. Argument is an array of
+ * pointers to the command and each argument. Ths array must end with
+ * a NULL pointer. If this is not the first command added, its
+ * standard input will be connected to the standard output of the
+ * previous command. Returns 1 for success and 0 otherwise.
+ */
+kwsysEXPORT int kwsysProcess_AddCommand(kwsysProcess* cp,
+ char const* const* command);
/**
* Set the timeout for the child process. The timeout period begins
@@ -280,6 +292,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_New
# undef kwsysProcess_Delete
# undef kwsysProcess_SetCommand
+# undef kwsysProcess_AddCommand
# undef kwsysProcess_SetTimeout
# undef kwsysProcess_SetWorkingDirectory
# undef kwsysProcess_Option_HideWindow
diff --git a/Source/kwsys/ProcessFwd9x.c b/Source/kwsys/ProcessFwd9x.c
index 7c03a68..689fbb0 100644
--- a/Source/kwsys/ProcessFwd9x.c
+++ b/Source/kwsys/ProcessFwd9x.c
@@ -27,6 +27,8 @@ PURPOSE. See the above copyright notices for more information.
#include <windows.h>
#include <stdio.h>
+void ReportLastError(HANDLE errorPipe);
+
int main()
{
/* Process startup information for the real child. */
@@ -49,6 +51,11 @@ int main()
/* Handle to the error reporting pipe provided by the parent. This
is parsed off the command line. */
HANDLE errorPipe = 0;
+ HANDLE errorPipeOrig = 0;
+
+ /* Handle to the event the parent uses to tell us to resume the child.
+ This is parsed off the command line. */
+ HANDLE resumeEvent = 0;
/* Handle to the event the parent uses to tell us to kill the child.
This is parsed off the command line. */
@@ -75,7 +82,12 @@ int main()
/* Parse the error pipe handle. */
while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
- sscanf(cmdLine, "%p", &errorPipe);
+ sscanf(cmdLine, "%p", &errorPipeOrig);
+
+ /* Parse the resume event handle. */
+ while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
+ while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
+ sscanf(cmdLine, "%p", &resumeEvent);
/* Parse the kill event handle. */
while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
@@ -91,6 +103,22 @@ int main()
while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
+ /* Create a non-inherited copy of the error pipe. We do not want
+ the child to get it. */
+ if(DuplicateHandle(GetCurrentProcess(), errorPipeOrig,
+ GetCurrentProcess(), &errorPipe,
+ 0, FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ /* Have a non-inherited duplicate. Close the inherited one. */
+ CloseHandle(errorPipeOrig);
+ }
+ else
+ {
+ /* Could not duplicate handle. Report the error. */
+ ReportLastError(errorPipeOrig);
+ return 1;
+ }
+
/* Create the subprocess. */
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
@@ -100,32 +128,23 @@ int main()
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
- if(!CreateProcess(0, cmdLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
+ if(CreateProcess(0, cmdLine, 0, 0, TRUE, CREATE_SUSPENDED, 0, 0, &si, &pi))
+ {
+ /* Process created successfully. Close the error reporting pipe
+ to notify the parent of success. */
+ CloseHandle(errorPipe);
+ }
+ else
{
/* Error creating the process. Report the error to the parent
process through the special error reporting pipe. */
- LPVOID lpMsgBuf;
- DWORD n;
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
- (LPTSTR) &lpMsgBuf,
- 0,
- NULL
- );
- WriteFile(errorPipe, lpMsgBuf, strlen(lpMsgBuf)+1, &n, 0);
- LocalFree( lpMsgBuf );
+ ReportLastError(errorPipe);
return 1;
}
- CloseHandle(pi.hThread);
- /* Wait for subprocess to exit or for kill event from parent. */
+ /* Wait for resume or kill event from parent. */
waitHandles[0] = killEvent;
- waitHandles[1] = pi.hProcess;
+ waitHandles[1] = resumeEvent;
waitResult = WaitForMultipleObjects(2, waitHandles, 0, INFINITE);
/* Check what happened. */
@@ -135,22 +154,54 @@ int main()
TerminateProcess(pi.hProcess, 255);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
return 1;
}
- else if(GetExitCodeProcess(pi.hProcess, &retVal))
+ else
+ {
+ /* We were asked to resume the child. */
+ ResumeThread(pi.hThread);
+ CloseHandle(pi.hThread);
+ }
+
+ /* Wait for subprocess to exit or for kill event from parent. */
+ waitHandles[0] = killEvent;
+ waitHandles[1] = pi.hProcess;
+ waitResult = WaitForMultipleObjects(2, waitHandles, 0, INFINITE);
+
+ /* Check what happened. */
+ if(waitResult == WAIT_OBJECT_0)
{
- /* The child exited and we could get the return code. */
+ /* We were asked to kill the child. */
+ TerminateProcess(pi.hProcess, 255);
+ WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
- return retVal;
+ return 1;
}
else
{
- /* The child exited and we could not get the return code. Report
- the problem to the parent process. */
- DWORD n;
- const char* msg = "Failed to get process return code.";
- WriteFile(errorPipe, msg, strlen(msg)+1, &n, 0);
+ /* The child exited. Get the return code. */
+ GetExitCodeProcess(pi.hProcess, &retVal);
CloseHandle(pi.hProcess);
- return -1;
+ return retVal;
}
}
+
+void ReportLastError(HANDLE errorPipe)
+{
+ LPVOID lpMsgBuf;
+ DWORD n;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ WriteFile(errorPipe, lpMsgBuf, strlen(lpMsgBuf)+1, &n, 0);
+ LocalFree( lpMsgBuf );
+}
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);
diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c
index 1253faa..a0f8e08 100644
--- a/Source/kwsys/ProcessWin32.c
+++ b/Source/kwsys/ProcessWin32.c
@@ -49,25 +49,37 @@ Q190351 and Q150956.
#pragma warning (disable: 4706)
#endif
-/* The number of pipes for the child's output. The standard stdout
- and stderr pipes are the first two. One more pipe is used on Win9x
- for the forwarding executable to use in reporting problems. */
-#define CMPE_PIPE_COUNT 3
-#define CMPE_PIPE_STDOUT 0
-#define CMPE_PIPE_STDERR 1
-#define CMPE_PIPE_ERROR 2
+/* There are pipes for the process pipeline's stdout and stderr. */
+#define KWSYSPE_PIPE_COUNT 2
+#define KWSYSPE_PIPE_STDOUT 0
+#define KWSYSPE_PIPE_STDERR 1
/* The maximum amount to read from a pipe at a time. */
-#define CMPE_PIPE_BUFFER_SIZE 1024
+#define KWSYSPE_PIPE_BUFFER_SIZE 1024
#define kwsysEncodedWriteArrayProcessFwd9x kwsys(EncodedWriteArrayProcessFwd9x)
typedef LARGE_INTEGER kwsysProcessTime;
+typedef struct kwsysProcessCreateInformation_s
+{
+ /* Windows child startup control data. */
+ STARTUPINFO StartupInfo;
+
+ /* Special error reporting pipe for Win9x forwarding executable. */
+ HANDLE ErrorPipeRead;
+ HANDLE ErrorPipeWrite;
+} kwsysProcessCreateInformation;
+
/*--------------------------------------------------------------------------*/
typedef struct kwsysProcessPipeData_s kwsysProcessPipeData;
static DWORD WINAPI kwsysProcessPipeThread(LPVOID ptd);
static void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td);
+static int kwsysProcessInitialize(kwsysProcess* cp);
+static int kwsysProcessCreate(kwsysProcess* cp, int index,
+ kwsysProcessCreateInformation* si,
+ PHANDLE readEnd);
+static void kwsysProcessDestroy(kwsysProcess* cp, int event);
static void kwsysProcessCleanupHandle(PHANDLE h);
static void kwsysProcessCleanup(kwsysProcess* cp, int error);
static void kwsysProcessCleanErrorMessage(kwsysProcess* cp);
@@ -111,7 +123,7 @@ struct kwsysProcessPipeData_s
/* ------------- Data managed per call to Execute ------------- */
/* Buffer for data read in this pipe's thread. */
- char DataBuffer[CMPE_PIPE_BUFFER_SIZE];
+ char DataBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
/* The length of the data stored in the buffer. */
DWORD DataLength;
@@ -132,11 +144,15 @@ struct kwsysProcess_s
{
/* ------------- Data managed per instance of kwsysProcess ------------- */
- /* The status of the process. */
+ /* The status of the process structure. */
int State;
- /* The command line to execute. */
- char* Command;
+ /* The command lines to execute. */
+ char** Commands;
+ int NumberOfCommands;
+
+ /* The exit code of each command. */
+ DWORD* CommandExitCodes;
/* The working directory for the child process. */
char* WorkingDirectory;
@@ -147,6 +163,9 @@ struct kwsysProcess_s
/* On Win9x platforms, the path to the forwarding executable. */
char* Win9x;
+ /* On Win9x platforms, the resume event for the forwarding executable. */
+ HANDLE Win9xResumeEvent;
+
/* On Win9x platforms, the kill event for the forwarding executable. */
HANDLE Win9xKillEvent;
@@ -156,15 +175,11 @@ struct kwsysProcess_s
/* Semaphore used by threads to signal data ready. */
HANDLE Full;
- /* The number of pipes needed to implement the child's execution.
- This is 3 on Win9x and 2 otherwise. */
- int PipeCount;
-
/* Whether we are currently deleting this kwsysProcess instance. */
int Deleting;
/* Data specific to each pipe and its thread. */
- kwsysProcessPipeData Pipe[CMPE_PIPE_COUNT];
+ kwsysProcessPipeData Pipe[KWSYSPE_PIPE_COUNT];
/* ------------- Data managed per call to Execute ------------- */
@@ -206,14 +221,14 @@ struct kwsysProcess_s
int PipesLeft;
/* Buffer for error messages (possibly from Win9x child). */
- char ErrorMessage[CMPE_PIPE_BUFFER_SIZE+1];
- int ErrorMessageLength;
-
- /* The actual command line that will be used to create the process. */
- char* RealCommand;
+ char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
/* Windows process information data. */
- PROCESS_INFORMATION ProcessInformation;
+ PROCESS_INFORMATION* ProcessInformation;
+
+ /* Data and process termination events for which to wait. */
+ PHANDLE ProcessEvents;
+ int ProcessEventsLength;
};
/*--------------------------------------------------------------------------*/
@@ -299,9 +314,8 @@ kwsysProcess* kwsysProcess_New()
}
}
- /* We need the extra error pipe on Win9x. */
+ /* Save the path to the forwarding executable. */
cp->Win9x = win9x;
- cp->PipeCount = cp->Win9x? 3:2;
/* Initially no thread owns the mutex. Initialize semaphore to 1. */
if(!(cp->SharedIndexMutex = CreateSemaphore(0, 1, 1, 0)))
@@ -319,12 +333,21 @@ kwsysProcess* kwsysProcess_New()
if(cp->Win9x)
{
- /* Create an event to tell the forwarding executable to kill the
- child. */
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(sa));
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
+
+ /* Create an event to tell the forwarding executable to resume the
+ child. */
+ if(!(cp->Win9xResumeEvent = CreateEvent(&sa, TRUE, 0, 0)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+
+ /* Create an event to tell the forwarding executable to kill the
+ child. */
if(!(cp->Win9xKillEvent = CreateEvent(&sa, TRUE, 0, 0)))
{
kwsysProcess_Delete(cp);
@@ -333,7 +356,7 @@ kwsysProcess* kwsysProcess_New()
}
/* Create the thread to read each pipe. */
- for(i=0; i < cp->PipeCount; ++i)
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
DWORD dummy=0;
@@ -393,7 +416,7 @@ void kwsysProcess_Delete(kwsysProcess* cp)
cp->Deleting = 1;
/* Terminate each of the threads. */
- for(i=0; i < cp->PipeCount; ++i)
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
if(cp->Pipe[i].Thread)
{
@@ -417,15 +440,20 @@ void kwsysProcess_Delete(kwsysProcess* cp)
kwsysProcessCleanupHandle(&cp->SharedIndexMutex);
kwsysProcessCleanupHandle(&cp->Full);
- /* Close the Win9x kill event handle. */
+ /* Close the Win9x resume and kill event handles. */
if(cp->Win9x)
{
+ kwsysProcessCleanupHandle(&cp->Win9xResumeEvent);
kwsysProcessCleanupHandle(&cp->Win9xKillEvent);
}
/* Free memory. */
kwsysProcess_SetCommand(cp, 0);
kwsysProcess_SetWorkingDirectory(cp, 0);
+ if(cp->CommandExitCodes)
+ {
+ free(cp->CommandExitCodes);
+ }
if(cp->Win9x)
{
_unlink(cp->Win9x);
@@ -435,178 +463,231 @@ void kwsysProcess_Delete(kwsysProcess* 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)
+ {
+ free(cp->Commands[i]);
+ }
+ cp->NumberOfCommands = 0;
+ if(cp->Commands)
{
- free(cp->Command);
- cp->Command = 0;
+ free(cp->Commands);
+ cp->Commands = 0;
}
if(command)
{
- /* We need to construct a single string representing the command
+ 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];
+ }
+ }
+
+ /* We need to construct a single string representing the command
and its arguments. We will surround each argument containing
spaces with double-quotes. Inside a double-quoted argument, we
need to escape double-quotes and all backslashes before them.
We also need to escape backslashes at the end of an argument
because they come before the closing double-quote for the
argument. */
- char* cmd;
- char const* const* arg;
- int length = 0;
- /* First determine the length of the final string. */
- for(arg = command; *arg; ++arg)
- {
- /* Keep track of how many backslashes have been encountered in a
- row in this argument. */
- int backslashes = 0;
- int spaces = 0;
- const char* c;
+ {
+ char* cmd;
+ char const* const* arg;
+ int length = 0;
+ /* First determine the length of the final string. */
+ for(arg = command; *arg; ++arg)
+ {
+ /* Keep track of how many backslashes have been encountered in a
+ row in this argument. */
+ int backslashes = 0;
+ int spaces = 0;
+ const char* c;
- /* Scan the string for spaces. If there are no spaces, we can
+ /* Scan the string for spaces. If there are no spaces, we can
pass the argument verbatim. */
- for(c=*arg; *c; ++c)
+ for(c=*arg; *c; ++c)
+ {
+ if(*c == ' ' || *c == '\t')
{
- if(*c == ' ' || *c == '\t')
- {
- spaces = 1;
- break;
- }
+ spaces = 1;
+ break;
}
+ }
- /* Add the length of the argument, plus 1 for the space
+ /* Add the length of the argument, plus 1 for the space
separating the arguments. */
- length += (int)strlen(*arg) + 1;
+ length += (int)strlen(*arg) + 1;
- if(spaces)
- {
- /* Add 2 for double quotes since spaces are present. */
- length += 2;
+ if(spaces)
+ {
+ /* Add 2 for double quotes since spaces are present. */
+ length += 2;
/* Scan the string to find characters that need escaping. */
- for(c=*arg; *c; ++c)
+ for(c=*arg; *c; ++c)
+ {
+ if(*c == '\\')
{
- if(*c == '\\')
- {
- /* Found a backslash. It may need to be escaped later. */
- ++backslashes;
- }
- else if(*c == '"')
- {
- /* Found a double-quote. We need to escape it and all
- immediately preceding backslashes. */
- length += backslashes + 1;
- backslashes = 0;
- }
- else
- {
- /* Found another character. This eliminates the possibility
- that any immediately preceding backslashes will be
- escaped. */
- backslashes = 0;
- }
+ /* Found a backslash. It may need to be escaped later. */
+ ++backslashes;
+ }
+ else if(*c == '"')
+ {
+ /* Found a double-quote. We need to escape it and all
+ immediately preceding backslashes. */
+ length += backslashes + 1;
+ backslashes = 0;
+ }
+ else
+ {
+ /* Found another character. This eliminates the possibility
+ that any immediately preceding backslashes will be
+ escaped. */
+ backslashes = 0;
}
-
- /* We need to escape all ending backslashes. */
- length += backslashes;
}
+
+ /* We need to escape all ending backslashes. */
+ length += backslashes;
}
+ }
- /* Allocate enough space for the command. We do not need an extra
+ /* Allocate enough space for the command. We do not need an extra
byte for the terminating null because we allocated a space for
the first argument that we will not use. */
- cp->Command = (char*)malloc(length);
+ newCommands[cp->NumberOfCommands] = (char*)malloc(length);
+ if(!newCommands[cp->NumberOfCommands])
+ {
+ /* Out of memory. */
+ free(newCommands);
+ return 0;
+ }
- /* Construct the command line in the allocated buffer. */
- cmd = cp->Command;
- for(arg = command; *arg; ++arg)
- {
- /* Keep track of how many backslashes have been encountered in a
- row in an argument. */
- int backslashes = 0;
- int spaces = 0;
- const char* c;
+ /* Construct the command line in the allocated buffer. */
+ cmd = newCommands[cp->NumberOfCommands];
+ for(arg = command; *arg; ++arg)
+ {
+ /* Keep track of how many backslashes have been encountered in a
+ row in an argument. */
+ int backslashes = 0;
+ int spaces = 0;
+ const char* c;
- /* Scan the string for spaces. If there are no spaces, we can
+ /* Scan the string for spaces. If there are no spaces, we can
pass the argument verbatim. */
- for(c=*arg; *c; ++c)
- {
- if(*c == ' ' || *c == '\t')
- {
- spaces = 1;
- break;
- }
- }
-
- /* Add the separating space if this is not the first argument. */
- if(arg != command)
+ for(c=*arg; *c; ++c)
+ {
+ if(*c == ' ' || *c == '\t')
{
- *cmd++ = ' ';
+ spaces = 1;
+ break;
}
+ }
- if(spaces)
- {
- /* Add the opening double-quote for this argument. */
- *cmd++ = '"';
+ /* Add the separating space if this is not the first argument. */
+ if(arg != command)
+ {
+ *cmd++ = ' ';
+ }
+
+ if(spaces)
+ {
+ /* Add the opening double-quote for this argument. */
+ *cmd++ = '"';
/* Add the characters of the argument, possibly escaping them. */
- for(c=*arg; *c; ++c)
+ for(c=*arg; *c; ++c)
+ {
+ if(*c == '\\')
+ {
+ /* Found a backslash. It may need to be escaped later. */
+ ++backslashes;
+ *cmd++ = '\\';
+ }
+ else if(*c == '"')
{
- if(*c == '\\')
+ /* Add enough backslashes to escape any that preceded the
+ double-quote. */
+ while(backslashes > 0)
{
- /* Found a backslash. It may need to be escaped later. */
- ++backslashes;
+ --backslashes;
*cmd++ = '\\';
}
- else if(*c == '"')
- {
- /* Add enough backslashes to escape any that preceded the
- double-quote. */
- while(backslashes > 0)
- {
- --backslashes;
- *cmd++ = '\\';
- }
- /* Add the backslash to escape the double-quote. */
- *cmd++ = '\\';
+ /* Add the backslash to escape the double-quote. */
+ *cmd++ = '\\';
- /* Add the double-quote itself. */
- *cmd++ = '"';
- }
- else
- {
- /* We encountered a normal character. This eliminates any
- escaping needed for preceding backslashes. Add the
- character. */
- backslashes = 0;
- *cmd++ = *c;
- }
+ /* Add the double-quote itself. */
+ *cmd++ = '"';
}
-
- /* Add enough backslashes to escape any trailing ones. */
- while(backslashes > 0)
+ else
{
- --backslashes;
- *cmd++ = '\\';
+ /* We encountered a normal character. This eliminates any
+ escaping needed for preceding backslashes. Add the
+ character. */
+ backslashes = 0;
+ *cmd++ = *c;
}
-
- /* Add the closing double-quote for this argument. */
- *cmd++ = '"';
}
- else
+
+ /* Add enough backslashes to escape any trailing ones. */
+ while(backslashes > 0)
{
- /* No spaces. Add the argument verbatim. */
- for(c=*arg; *c; ++c)
- {
- *cmd++ = *c;
- }
+ --backslashes;
+ *cmd++ = '\\';
+ }
+
+ /* Add the closing double-quote for this argument. */
+ *cmd++ = '"';
+ }
+ else
+ {
+ /* No spaces. Add the argument verbatim. */
+ for(c=*arg; *c; ++c)
+ {
+ *cmd++ = *c;
}
}
-
- /* Add the terminating null character to the command line. */
- *cmd = 0;
}
+
+ /* Add the terminating null character to the command line. */
+ *cmd = 0;
+ }
+
+ /* Save the new array of commands. */
+ free(cp->Commands);
+ cp->Commands = newCommands;
+ cp->NumberOfCommands = newNumberOfCommands;
+ return 1;
}
/*--------------------------------------------------------------------------*/
@@ -698,122 +779,141 @@ void kwsysProcess_Execute(kwsysProcess* cp)
{
int i;
- /* Windows child startup control data. */
- STARTUPINFO si;
- DWORD dwCreationFlags=0;
+ /* Child startup control data. */
+ kwsysProcessCreateInformation si;
/* Do not execute a second time. */
if(cp->State == kwsysProcess_State_Executing)
{
return;
}
+
+ /* Initialize the control structure for a new process. */
+ if(!kwsysProcessInitialize(cp))
+ {
+ strcpy(cp->ErrorMessage, "Out of memory");
+ cp->State = kwsysProcess_State_Error;
+ return;
+ }
- /* Initialize startup info data. */
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- /* Reset internal status flags. */
- cp->TimeoutExpired = 0;
- cp->Terminated = 0;
- cp->Killed = 0;
- cp->ExitException = kwsysProcess_Exception_None;
- cp->ExitCode = 1;
- cp->ExitValue = 1;
-
- /* Reset error data. */
- cp->ErrorMessage[0] = 0;
- cp->ErrorMessageLength = 0;
-
- /* Reset the Win9x kill event. */
+ /* Reset the Win9x resume and kill events. */
if(cp->Win9x)
{
+ if(!ResetEvent(cp->Win9xResumeEvent))
+ {
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
if(!ResetEvent(cp->Win9xKillEvent))
{
kwsysProcessCleanup(cp, 1);
return;
}
}
+
+ /* Initialize startup info data. */
+ ZeroMemory(&si, sizeof(si));
+ si.StartupInfo.cb = sizeof(si.StartupInfo);
+
+ /* Decide whether a child window should be shown. */
+ si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ si.StartupInfo.wShowWindow =
+ (unsigned short)(cp->HideWindow?SW_HIDE:SW_SHOWDEFAULT);
- /* Create a pipe for each child output. */
- for(i=0; i < cp->PipeCount; ++i)
+ /* Connect the child's output pipes to the threads. */
+ si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+
+ /* Create stderr pipe to be shared by all processes in the pipeline.
+ Neither end is directly inherited. */
+ if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read,
+ &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0))
{
- HANDLE writeEnd;
-
- /* The pipe is not closed. */
- cp->Pipe[i].Closed = 0;
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
- /* Create the pipe. Neither end is directly inherited. */
- if(!CreatePipe(&cp->Pipe[i].Read, &writeEnd, 0, 0))
+ /* Create an inherited duplicate of the write end, but do not
+ close the non-inherited version. We need to keep it open
+ to use in waking up the pipe threads. */
+ if(!DuplicateHandle(GetCurrentProcess(), cp->Pipe[KWSYSPE_PIPE_STDERR].Write,
+ GetCurrentProcess(), &si.StartupInfo.hStdError,
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ kwsysProcessCleanup(cp, 1);
+ kwsysProcessCleanupHandle(&si.StartupInfo.hStdError);
+ return;
+ }
+
+ /* Create the pipeline of processes. */
+ {
+ HANDLE readEnd = 0;
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ if(kwsysProcessCreate(cp, i, &si, &readEnd))
{
- kwsysProcessCleanup(cp, 1);
- return;
+ cp->ProcessEvents[i+1] = cp->ProcessInformation[i].hProcess;
}
-
- /* Create an inherited duplicate of the write end. This also closes
- the non-inherited version. */
- if(!DuplicateHandle(GetCurrentProcess(), writeEnd,
- GetCurrentProcess(), &cp->Pipe[i].Write,
- 0, TRUE, (DUPLICATE_CLOSE_SOURCE |
- DUPLICATE_SAME_ACCESS)))
+ else
{
kwsysProcessCleanup(cp, 1);
+
+ /* Release resources that may have been allocated for this
+ process before an error occurred. */
+ kwsysProcessCleanupHandle(&readEnd);
+ if(i > 0)
+ {
+ kwsysProcessCleanupHandle(&si.StartupInfo.hStdInput);
+ }
+ kwsysProcessCleanupHandle(&si.StartupInfo.hStdOutput);
+ kwsysProcessCleanupHandle(&si.StartupInfo.hStdError);
+ kwsysProcessCleanupHandle(&si.ErrorPipeRead);
+ kwsysProcessCleanupHandle(&si.ErrorPipeWrite);
return;
}
}
+
+ /* Save a handle to the output pipe for the last process. */
+ cp->Pipe[KWSYSPE_PIPE_STDOUT].Read = readEnd;
+ }
+
+ /* Close the inherited handles to the stderr pipe shared by all
+ processes in the pipeline. */
+ kwsysProcessCleanupHandle(&si.StartupInfo.hStdError);
+
+ /* The timeout period starts now. */
+ cp->StartTime = kwsysProcessTimeGetCurrent();
+ cp->TimeoutTime = kwsysProcessTimeFromDouble(-1);
- /* Construct the real command line. */
+ /* All processes in the pipeline have been started in suspended
+ mode. Resume them all now. */
if(cp->Win9x)
{
- /* Windows 9x */
-
- /* The forwarding executable is given a handle to the error pipe
- and a handle to the kill event. */
- cp->RealCommand = malloc(strlen(cp->Win9x)+strlen(cp->Command)+100);
- sprintf(cp->RealCommand, "%s %p %p %d %s", cp->Win9x,
- cp->Pipe[CMPE_PIPE_ERROR].Write, cp->Win9xKillEvent,
- cp->HideWindow, cp->Command);
+ SetEvent(cp->Win9xResumeEvent);
}
else
{
- /* Not Windows 9x */
- cp->RealCommand = strdup(cp->Command);
- }
-
- /* Connect the child's output pipes to the threads. */
- si.dwFlags = STARTF_USESTDHANDLES;
- si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
- si.hStdOutput = cp->Pipe[CMPE_PIPE_STDOUT].Write;
- si.hStdError = cp->Pipe[CMPE_PIPE_STDERR].Write;
-
- /* Decide whether a child window should be shown. */
- si.dwFlags |= STARTF_USESHOWWINDOW;
- si.wShowWindow = (unsigned short)(cp->HideWindow?SW_HIDE:SW_SHOWDEFAULT);
-
- /* The timeout period starts now. */
- cp->StartTime = kwsysProcessTimeGetCurrent();
- cp->TimeoutTime = kwsysProcessTimeFromDouble(-1);
-
- /* CREATE THE CHILD PROCESS */
- if(!CreateProcess(0, cp->RealCommand, 0, 0, TRUE, dwCreationFlags, 0,
- cp->WorkingDirectory, &si, &cp->ProcessInformation))
- {
- kwsysProcessCleanup(cp, 1);
- return;
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ ResumeThread(cp->ProcessInformation[i].hThread);
+ }
}
-
- /* ---- It is no longer safe to call kwsysProcessCleanup. ----- */
+
+ /* ---- It is no longer safe to call kwsysProcessCleanup. ----- */
/* Tell the pipe threads that a process has started. */
- for(i=0; i < cp->PipeCount; ++i)
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
ReleaseSemaphore(cp->Pipe[i].Ready, 1, 0);
}
-
- /* We don't care about the child's main thread. */
- kwsysProcessCleanupHandle(&cp->ProcessInformation.hThread);
+
+ /* We don't care about the children's main threads. */
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
+ }
/* No pipe has reported data. */
- cp->CurrentIndex = CMPE_PIPE_COUNT;
- cp->PipesLeft = cp->PipeCount;
+ cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
+ cp->PipesLeft = KWSYSPE_PIPE_COUNT;
/* The process has now started. */
cp->State = kwsysProcess_State_Executing;
@@ -833,7 +933,6 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
int expired = 0;
int pipeId = 0;
DWORD w;
- HANDLE events[2];
/* Make sure we are executing a process. */
if(cp->State != kwsysProcess_State_Executing || cp->Killed ||
@@ -842,11 +941,6 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
return 0;
}
- /* We will wait for data until the process termiantes or data are
- available. */
- events[0] = cp->Full;
- events[1] = cp->ProcessInformation.hProcess;
-
/* Record the time at which user timeout period starts. */
userStartTime = kwsysProcessTimeGetCurrent();
@@ -859,10 +953,10 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
{
/* If we previously got data from a thread, let it know we are
done with the data. */
- if(cp->CurrentIndex < CMPE_PIPE_COUNT)
+ if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT)
{
ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
- cp->CurrentIndex = CMPE_PIPE_COUNT;
+ cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
}
/* Setup a timeout if required. */
@@ -881,9 +975,9 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
timeout = kwsysProcessTimeToDWORD(timeoutLength);
}
- /* Wait for a pipe's thread to signal or the application to
- terminate. */
- w = WaitForMultipleObjects(cp->Terminated?1:2, events, 0, timeout);
+ /* Wait for a pipe's thread to signal or a process to terminate. */
+ w = WaitForMultipleObjects(cp->ProcessEventsLength, cp->ProcessEvents,
+ 0, timeout);
if(w == WAIT_TIMEOUT)
{
/* Timeout has expired. */
@@ -903,26 +997,6 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
/* The pipe closed. */
--cp->PipesLeft;
}
- else if(cp->CurrentIndex == CMPE_PIPE_ERROR)
- {
- /* This is data on the special error reporting pipe for Win9x.
- Append it to the error buffer. */
- int length = cp->Pipe[cp->CurrentIndex].DataLength;
- if(length > CMPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength)
- {
- length = CMPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength;
- }
- if(length > 0)
- {
- memcpy(cp->ErrorMessage+cp->ErrorMessageLength,
- cp->Pipe[cp->CurrentIndex].DataBuffer, length);
- cp->ErrorMessageLength += length;
- }
- else
- {
- cp->ErrorMessage[cp->ErrorMessageLength] = 0;
- }
- }
else if(pipes & (1 << cp->CurrentIndex))
{
/* Caller wants this data. Report it. */
@@ -938,17 +1012,8 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
}
else
{
- int i;
-
- /* Process has terminated. */
- cp->Terminated = 1;
-
- /* Close our copies of the pipe write handles so the pipe
- threads can detect end-of-data. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
- }
+ /* A process has terminated. */
+ kwsysProcessDestroy(cp, w-WAIT_OBJECT_0);
}
}
@@ -991,7 +1056,7 @@ int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* leng
}
else
{
- /* The process has terminated and no more data are available. */
+ /* The children have terminated and no more data are available. */
return 0;
}
}
@@ -1020,14 +1085,14 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
/* When the last pipe closes in WaitForData, the loop terminates
without releaseing the pipe's thread. Release it now. */
- if(cp->CurrentIndex < CMPE_PIPE_COUNT)
+ if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT)
{
ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
- cp->CurrentIndex = CMPE_PIPE_COUNT;
+ cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
}
/* Wait for all pipe threads to reset. */
- for(i=0; i < cp->PipeCount; ++i)
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
WaitForSingleObject(cp->Pipe[i].Reset, INFINITE);
}
@@ -1036,35 +1101,21 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
/* Close all the pipes. */
kwsysProcessCleanup(cp, 0);
- /* We are done reading all data. Wait for the child to terminate.
- This will only block if we killed the child and are waiting for
- it to cleanup. */
- WaitForSingleObject(cp->ProcessInformation.hProcess, INFINITE);
-
/* Determine the outcome. */
if(cp->Killed)
{
/* We killed the child. */
cp->State = kwsysProcess_State_Killed;
}
- else if(cp->ErrorMessageLength)
- {
- /* The Win9x forwarding executing repored data on the special
- error pipe. Failed to run the process. */
- cp->State = kwsysProcess_State_Error;
-
- /* Remove trailing period and newline from message, if any. */
- kwsysProcessCleanErrorMessage(cp);
- }
else if(cp->TimeoutExpired)
{
/* The timeout expired. */
cp->State = kwsysProcess_State_Expired;
}
- else if(GetExitCodeProcess(cp->ProcessInformation.hProcess,
- &cp->ExitCode))
+ else
{
- /* The child exited. */
+ /* The children exited. Report the outcome of the last process. */
+ cp->ExitCode = cp->CommandExitCodes[cp->NumberOfCommands-1];
if(cp->ExitCode & 0xC0000000)
{
/* Child terminated due to exceptional behavior. */
@@ -1111,16 +1162,7 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
cp->ExitValue = cp->ExitCode & 0x000000FF;
}
}
- else
- {
- /* Error getting the child return code. */
- strcpy(cp->ErrorMessage, "Error getting child return code");
- cp->State = kwsysProcess_State_Error;
- }
-
- /* The child process is terminated. */
- CloseHandle(cp->ProcessInformation.hProcess);
-
+
return 1;
}
@@ -1138,14 +1180,14 @@ void kwsysProcess_Kill(kwsysProcess* cp)
/* If we are killing a process that just reported data, release
the pipe's thread. */
- if(cp->CurrentIndex < CMPE_PIPE_COUNT)
+ if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT)
{
ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
- cp->CurrentIndex = CMPE_PIPE_COUNT;
+ cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
}
/* Wake up all the pipe threads with dummy data. */
- for(i=0; i < cp->PipeCount; ++i)
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
DWORD dummy;
WriteFile(cp->Pipe[i].Write, "", 1, &dummy, 0);
@@ -1162,7 +1204,7 @@ void kwsysProcess_Kill(kwsysProcess* cp)
--cp->PipesLeft;
}
- /* Kill the child. */
+ /* Kill the children. */
cp->Killed = 1;
if(cp->Win9x)
{
@@ -1171,8 +1213,17 @@ void kwsysProcess_Kill(kwsysProcess* cp)
}
else
{
- /* Not Windows 9x. Just terminate the child. */
- TerminateProcess(cp->ProcessInformation.hProcess, 255);
+ /* Not Windows 9x. Just terminate the children. */
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ TerminateProcess(cp->ProcessInformation[i].hProcess, 255);
+ }
+ }
+
+ /* Wait for windows to finish cleaning up the children. */
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ WaitForSingleObject(cp->ProcessInformation[i].hProcess, INFINITE);
}
}
@@ -1215,7 +1266,7 @@ void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td)
while((WaitForSingleObject(td->Empty, INFINITE), !td->Closed))
{
/* Read data from the pipe. This may block until data are available. */
- if(!ReadFile(td->Read, td->DataBuffer, CMPE_PIPE_BUFFER_SIZE,
+ if(!ReadFile(td->Read, td->DataBuffer, KWSYSPE_PIPE_BUFFER_SIZE,
&td->DataLength, 0))
{
if(GetLastError() != ERROR_BROKEN_PIPE)
@@ -1237,6 +1288,262 @@ void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td)
}
/*--------------------------------------------------------------------------*/
+/* Initialize a process control structure for kwsysProcess_Execute. */
+int kwsysProcessInitialize(kwsysProcess* cp)
+{
+ /* Reset internal status flags. */
+ cp->TimeoutExpired = 0;
+ cp->Terminated = 0;
+ cp->Killed = 0;
+ cp->ExitException = kwsysProcess_Exception_None;
+ cp->ExitCode = 1;
+ cp->ExitValue = 1;
+
+ /* Reset error data. */
+ cp->ErrorMessage[0] = 0;
+
+ /* Allocate process information for each process. */
+ cp->ProcessInformation =
+ (PROCESS_INFORMATION*)malloc(sizeof(PROCESS_INFORMATION) *
+ cp->NumberOfCommands);
+ if(!cp->ProcessInformation)
+ {
+ return 0;
+ }
+ ZeroMemory(cp->ProcessInformation,
+ sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands);
+ if(cp->CommandExitCodes)
+ {
+ free(cp->CommandExitCodes);
+ }
+ cp->CommandExitCodes = (DWORD*)malloc(sizeof(DWORD)*cp->NumberOfCommands);
+ if(!cp->CommandExitCodes)
+ {
+ return 0;
+ }
+ ZeroMemory(cp->CommandExitCodes, sizeof(DWORD)*cp->NumberOfCommands);
+
+ /* Allocate event wait array. The first event is cp->Full, the rest
+ are the process termination events. */
+ cp->ProcessEvents = (PHANDLE)malloc(sizeof(HANDLE)*(cp->NumberOfCommands+1));
+ if(!cp->ProcessEvents)
+ {
+ return 0;
+ }
+ ZeroMemory(cp->ProcessEvents, sizeof(HANDLE) * (cp->NumberOfCommands+1));
+ cp->ProcessEvents[0] = cp->Full;
+ cp->ProcessEventsLength = cp->NumberOfCommands+1;
+
+ return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcessCreate(kwsysProcess* cp, int index,
+ kwsysProcessCreateInformation* si,
+ PHANDLE readEnd)
+{
+ /* Setup the process's stdin. */
+ if(*readEnd)
+ {
+ /* Create an inherited duplicate of the read end from the output
+ pipe of the previous process. This also closes the
+ non-inherited version. */
+ if(!DuplicateHandle(GetCurrentProcess(), *readEnd,
+ GetCurrentProcess(), readEnd,
+ 0, TRUE, (DUPLICATE_CLOSE_SOURCE |
+ DUPLICATE_SAME_ACCESS)))
+ {
+ return 0;
+ }
+ si->StartupInfo.hStdInput = *readEnd;
+
+ /* This function is done with this handle. */
+ *readEnd = 0;
+ }
+ else
+ {
+ si->StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ }
+
+ /* Setup the process's stdout. */
+ {
+ DWORD maybeClose = DUPLICATE_CLOSE_SOURCE;
+ HANDLE writeEnd;
+
+ /* Create the output pipe for this process. Neither end is directly
+ inherited. */
+ if(!CreatePipe(readEnd, &writeEnd, 0, 0))
+ {
+ return 0;
+ }
+
+ /* Create an inherited duplicate of the write end. Close the
+ non-inherited version unless this is the last process. Save the
+ non-inherited write end of the last process. */
+ if(index == cp->NumberOfCommands-1)
+ {
+ cp->Pipe[KWSYSPE_PIPE_STDOUT].Write = writeEnd;
+ maybeClose = 0;
+ }
+ if(!DuplicateHandle(GetCurrentProcess(), writeEnd,
+ GetCurrentProcess(), &si->StartupInfo.hStdOutput,
+ 0, TRUE, (maybeClose | DUPLICATE_SAME_ACCESS)))
+ {
+ return 0;
+ }
+ }
+
+ /* Create the child process. */
+ {
+ BOOL r;
+ char* realCommand;
+ if(cp->Win9x)
+ {
+ /* Create an error reporting pipe for the forwarding executable.
+ Neither end is directly inherited. */
+ if(!CreatePipe(&si->ErrorPipeRead, &si->ErrorPipeWrite, 0, 0))
+ {
+ return 0;
+ }
+
+ /* Create an inherited duplicate of the write end. This also closes
+ the non-inherited version. */
+ if(!DuplicateHandle(GetCurrentProcess(), si->ErrorPipeWrite,
+ GetCurrentProcess(), &si->ErrorPipeWrite,
+ 0, TRUE, (DUPLICATE_CLOSE_SOURCE |
+ DUPLICATE_SAME_ACCESS)))
+ {
+ return 0;
+ }
+
+ /* The forwarding executable is given a handle to the error pipe
+ and resume and kill events. */
+ realCommand = malloc(strlen(cp->Win9x)+strlen(cp->Commands[index])+100);
+ if(!realCommand)
+ {
+ return 0;
+ }
+ sprintf(realCommand, "%s %p %p %p %d %s", cp->Win9x,
+ si->ErrorPipeWrite, cp->Win9xResumeEvent, cp->Win9xKillEvent,
+ cp->HideWindow, cp->Commands[index]);
+ }
+ else
+ {
+ realCommand = cp->Commands[index];
+ }
+
+ /* Create the child in a suspended state so we can wait until all
+ children have been created before running any one. */
+ r = CreateProcess(0, realCommand, 0, 0, TRUE,
+ cp->Win9x? 0 : CREATE_SUSPENDED, 0,
+ cp->WorkingDirectory, &si->StartupInfo,
+ &cp->ProcessInformation[index]);
+
+ if(cp->Win9x)
+ {
+ /* Free memory. */
+ free(realCommand);
+
+ /* Close the error pipe write end so we can detect when the
+ forwarding executable closes it. */
+ kwsysProcessCleanupHandle(&si->ErrorPipeWrite);
+ if(r)
+ {
+ /* Wait for the forwarding executable to report an error or
+ close the error pipe to report success. */
+ DWORD total = 0;
+ DWORD n = 1;
+ while(total < KWSYSPE_PIPE_BUFFER_SIZE && n > 0)
+ {
+ if(ReadFile(si->ErrorPipeRead, cp->ErrorMessage+total,
+ KWSYSPE_PIPE_BUFFER_SIZE-total, &n, 0))
+ {
+ total += n;
+ }
+ else
+ {
+ n = 0;
+ }
+ }
+ if(total > 0 || GetLastError() != ERROR_BROKEN_PIPE)
+ {
+ /* The forwarding executable could not run the process, or
+ there was an error reading from its error pipe. Preserve
+ the last error while cleaning up the forwarding executable
+ so the cleanup our caller does reports the proper error. */
+ DWORD error = GetLastError();
+ kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hThread);
+ kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
+ SetLastError(error);
+ return 0;
+ }
+ }
+ kwsysProcessCleanupHandle(&si->ErrorPipeRead);
+ }
+
+ if(!r)
+ {
+ return 0;
+ }
+ }
+
+ /* Successfully created this child process. */
+ if(index > 0)
+ {
+ /* Close our handle to the input pipe for the current process. */
+ kwsysProcessCleanupHandle(&si->StartupInfo.hStdInput);
+ }
+
+ /* The parent process does not need the inhertied pipe write end. */
+ kwsysProcessCleanupHandle(&si->StartupInfo.hStdOutput);
+
+ return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcessDestroy(kwsysProcess* cp, int event)
+{
+ int i;
+ int index;
+
+ /* Find the process index for the termination event. */
+ for(index=0; index < cp->NumberOfCommands; ++index)
+ {
+ if(cp->ProcessInformation[index].hProcess == cp->ProcessEvents[event])
+ {
+ break;
+ }
+ }
+
+ /* Check the exit code of the process. */
+ GetExitCodeProcess(cp->ProcessInformation[index].hProcess,
+ &cp->CommandExitCodes[index]);
+
+ /* Close the process handle for the terminated process. */
+ kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
+
+ /* Remove the process from the available events. */
+ cp->ProcessEventsLength -= 1;
+ for(i=event; i < cp->ProcessEventsLength; ++i)
+ {
+ cp->ProcessEvents[i] = cp->ProcessEvents[i+1];
+ }
+
+ /* Check if all processes have terminated. */
+ if(cp->ProcessEventsLength == 1)
+ {
+ cp->Terminated = 1;
+
+ /* Close our copies of the pipe write handles so the pipe threads
+ can detect end-of-data. */
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ {
+ kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
/* Close the given handle if it is open. Reset its value to 0. */
void kwsysProcessCleanupHandle(PHANDLE h)
@@ -1258,37 +1565,71 @@ void kwsysProcessCleanup(kwsysProcess* cp, int error)
/* If this is an error case, report the error. */
if(error)
{
- /* Format the error message. */
- DWORD original = GetLastError();
- DWORD length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, 0, original,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- cp->ErrorMessage, CMPE_PIPE_BUFFER_SIZE, 0);
- if(length < 1)
+ /* Construct an error message if one has not been provided already. */
+ if(cp->ErrorMessage[0] == 0)
{
- /* FormatMessage failed. Use a default message. */
- _snprintf(cp->ErrorMessage, CMPE_PIPE_BUFFER_SIZE,
- "Process execution failed with error 0x%X. "
- "FormatMessage failed with error 0x%X",
- original, GetLastError());
+ /* Format the error message. */
+ DWORD original = GetLastError();
+ DWORD length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, 0, original,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE, 0);
+ if(length < 1)
+ {
+ /* FormatMessage failed. Use a default message. */
+ _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
+ "Process execution failed with error 0x%X. "
+ "FormatMessage failed with error 0x%X",
+ original, GetLastError());
+ }
}
+ /* Remove trailing period and newline, if any. */
+ kwsysProcessCleanErrorMessage(cp);
+
/* Set the error state. */
cp->State = kwsysProcess_State_Error;
- /* Remove trailing period and newline, if any. */
- kwsysProcessCleanErrorMessage(cp);
+ /* Cleanup any processes already started in a suspended state. */
+ if(cp->ProcessInformation)
+ {
+ if(cp->Win9x)
+ {
+ SetEvent(cp->Win9xKillEvent);
+ }
+ else
+ {
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ if(cp->ProcessInformation[i].hProcess)
+ {
+ TerminateProcess(cp->ProcessInformation[i].hProcess, 255);
+ WaitForSingleObject(cp->ProcessInformation[i].hProcess, INFINITE);
+ }
+ }
+ }
+ for(i=0; i < cp->NumberOfCommands; ++i)
+ {
+ kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
+ kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
+ }
+ }
}
-
+
/* Free memory. */
- if(cp->RealCommand)
+ if(cp->ProcessInformation)
+ {
+ free(cp->ProcessInformation);
+ cp->ProcessInformation = 0;
+ }
+ if(cp->ProcessEvents)
{
- free(cp->RealCommand);
- cp->RealCommand = 0;
+ free(cp->ProcessEvents);
+ cp->ProcessEvents = 0;
}
/* Close each pipe. */
- for(i=0; i < cp->PipeCount; ++i)
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
{
kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
kwsysProcessCleanupHandle(&cp->Pipe[i].Read);