Browse Source

ENH: Merged changes from KWSys-MultiProcess-bp to KWSys-MultiProcess-b2t-1-mp to main tree. This introduces support for process pipelines.

Brad King 22 years ago
parent
commit
ad8bc4b1a4
4 changed files with 873 additions and 471 deletions
  1. 16 3
      Source/kwsys/Process.h.in
  2. 80 29
      Source/kwsys/ProcessFwd9x.c
  3. 415 128
      Source/kwsys/ProcessUNIX.c
  4. 362 311
      Source/kwsys/ProcessWin32.c

+ 16 - 3
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

+ 80 - 29
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 );
+}

+ 415 - 128
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]);
     }
 }
 
@@ -781,6 +926,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.  */
@@ -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);

File diff suppressed because it is too large
+ 362 - 311
Source/kwsys/ProcessWin32.c


Some files were not shown because too many files changed in this diff