Forráskód Böngészése

ENH: Added Process_SetPipeNative method to allow user code to override the pipes connected to the child pipeline.

Brad King 19 éve
szülő
commit
6eef6638a5
3 módosított fájl, 247 hozzáadás és 4 törlés
  1. 32 0
      Source/kwsys/Process.h.in
  2. 108 2
      Source/kwsys/ProcessUNIX.c
  3. 107 2
      Source/kwsys/ProcessWin32.c

+ 32 - 0
Source/kwsys/Process.h.in

@@ -33,6 +33,7 @@
 #define kwsysProcess_SetTimeout          kwsys_ns(Process_SetTimeout)
 #define kwsysProcess_SetWorkingDirectory kwsys_ns(Process_SetWorkingDirectory)
 #define kwsysProcess_SetPipeFile         kwsys_ns(Process_SetPipeFile)
+#define kwsysProcess_SetPipeNative       kwsys_ns(Process_SetPipeNative)
 #define kwsysProcess_SetPipeShared       kwsys_ns(Process_SetPipeShared)
 #define kwsysProcess_Option_Detach       kwsys_ns(Process_Option_Detach)
 #define kwsysProcess_Option_HideWindow   kwsys_ns(Process_Option_HideWindow)
@@ -71,6 +72,7 @@
 #define kwsysProcess_Pipe_STDOUT         kwsys_ns(Process_Pipe_STDOUT)
 #define kwsysProcess_Pipe_STDERR         kwsys_ns(Process_Pipe_STDERR)
 #define kwsysProcess_Pipe_Timeout        kwsys_ns(Process_Pipe_Timeout)
+#define kwsysProcess_Pipe_Handle         kwsys_ns(Process_Pipe_Handle)
 #define kwsysProcess_WaitForExit         kwsys_ns(Process_WaitForExit)
 #define kwsysProcess_Kill                kwsys_ns(Process_Kill)
 
@@ -84,6 +86,13 @@ extern "C"
  */
 typedef struct kwsysProcess_s kwsysProcess;
 
+/* Platform-specific pipe handle type.  */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+typedef void* kwsysProcess_Pipe_Handle;
+#else
+typedef int kwsysProcess_Pipe_Handle;
+#endif
+
 /**
  * Create a new Process instance.
  */
@@ -145,6 +154,27 @@ kwsysEXPORT int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe,
 kwsysEXPORT void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe,
                                             int shared);
 
+/**
+ * Specify a platform-specific native pipe for use as one of the child
+ * interface pipes.  The native pipe is specified by an array of two
+ * descriptors or handles.  The first entry in the array (index 0)
+ * should be the read end of the pipe.  The second entry in the array
+ * (index 1) should be the write end of the pipe.  If a null pointer
+ * is given the option will be disabled.
+ *
+ * For Pipe_STDIN the native pipe is connected to the first child in
+ * the pipeline as its stdin.  After the children are created the
+ * write end of the pipe will be closed in the child process and the
+ * read end will be closed in the parent process.
+ *
+ * For Pipe_STDOUT and Pipe_STDERR the pipe is connected to the last
+ * child as its stdout or stderr.  After the children are created the
+ * write end of the pipe will be closed in the parent process and the
+ * read end will be closed in the child process.
+ */
+kwsysEXPORT void kwsysProcess_SetPipeNative(kwsysProcess* cp, int pipe,
+                                            kwsysProcess_Pipe_Handle p[2]);
+
 /**
  * Get/Set a possibly platform-specific option.  Possible options are:
  *
@@ -349,6 +379,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
 # undef kwsysProcess_SetTimeout
 # undef kwsysProcess_SetWorkingDirectory
 # undef kwsysProcess_SetPipeFile
+# undef kwsysProcess_SetPipeNative
 # undef kwsysProcess_SetPipeShared
 # undef kwsysProcess_Option_Detach
 # undef kwsysProcess_Option_HideWindow
@@ -387,6 +418,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
 # undef kwsysProcess_Pipe_STDOUT
 # undef kwsysProcess_Pipe_STDERR
 # undef kwsysProcess_Pipe_Timeout
+# undef kwsysProcess_Pipe_Handle
 # undef kwsysProcess_WaitForExit
 # undef kwsysProcess_Kill
 #endif

+ 108 - 2
Source/kwsys/ProcessUNIX.c

@@ -102,6 +102,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
                               kwsysProcessCreateInformation* si, int* readEnd);
 static void kwsysProcessDestroy(kwsysProcess* cp);
 static int kwsysProcessSetupOutputPipeFile(int* p, const char* name);
+static int kwsysProcessSetupOutputPipeNative(int* p, int des[2]);
 static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
                                       kwsysProcessTime* timeoutTime);
 static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime,
@@ -217,6 +218,11 @@ struct kwsysProcess_s
   int PipeSharedSTDOUT;
   int PipeSharedSTDERR;
 
+  /* Native pipes provided by the user.  */
+  int PipeNativeSTDIN[2];
+  int PipeNativeSTDOUT[2];
+  int PipeNativeSTDERR[2];
+
   /* The real working directory of this process.  */
   int RealWorkingDirectoryLength;
   char* RealWorkingDirectory;
@@ -470,9 +476,11 @@ int kwsysProcess_SetPipeFile(kwsysProcess* cp, int prPipe, const char* file)
     strcpy(*pfile, file);
     }
 
-  /* If we are redirecting the pipe, do not share it.  */
+  /* If we are redirecting the pipe, do not share it or use a native
+     pipe.  */
   if(*pfile)
     {
+    kwsysProcess_SetPipeNative(cp, prPipe, 0);
     kwsysProcess_SetPipeShared(cp, prPipe, 0);
     }
   return 1;
@@ -494,10 +502,51 @@ void kwsysProcess_SetPipeShared(kwsysProcess* cp, int prPipe, int shared)
     default: return;
     }
 
-  /* If we are sharing the pipe, do not redirect it to a file.  */
+  /* If we are sharing the pipe, do not redirect it to a file or use a
+     native pipe.  */
   if(shared)
     {
     kwsysProcess_SetPipeFile(cp, prPipe, 0);
+    kwsysProcess_SetPipeNative(cp, prPipe, 0);
+    }
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_SetPipeNative(kwsysProcess* cp, int prPipe, int p[2])
+{
+  int* pPipeNative = 0;
+
+  if(!cp)
+    {
+    return;
+    }
+
+  switch(prPipe)
+    {
+    case kwsysProcess_Pipe_STDIN: pPipeNative = cp->PipeNativeSTDIN; break;
+    case kwsysProcess_Pipe_STDOUT: pPipeNative = cp->PipeNativeSTDOUT; break;
+    case kwsysProcess_Pipe_STDERR: pPipeNative = cp->PipeNativeSTDERR; break;
+    default: return;
+    }
+
+  /* Copy the native pipe descriptors provided.  */
+  if(p)
+    {
+    pPipeNative[0] = p[0];
+    pPipeNative[1] = p[1];
+    }
+  else
+    {
+    pPipeNative[0] = -1;
+    pPipeNative[1] = -1;
+    }
+
+  /* If we are using a native pipe, do not share it or redirect it to
+     a file.  */
+  if(p)
+    {
+    kwsysProcess_SetPipeFile(cp, prPipe, 0);
+    kwsysProcess_SetPipeShared(cp, prPipe, 0);
     }
 }
 
@@ -684,6 +733,19 @@ void kwsysProcess_Execute(kwsysProcess* cp)
     si.StdErr = 2;
     }
 
+  /* Replace the stderr pipe with the native pipe provided if any.  In
+     this case the select call will report that stderr is closed
+     immediately.  */
+  if(cp->PipeNativeSTDERR[1] >= 0)
+    {
+    if(!kwsysProcessSetupOutputPipeNative(&si.StdErr, cp->PipeNativeSTDERR))
+      {
+      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanupDescriptor(&si.StdErr);
+      return;
+      }
+    }
+
   /* The timeout period starts now.  */
   cp->StartTime = kwsysProcessTimeGetCurrent();
   cp->TimeoutTime.tv_sec = -1;
@@ -1297,6 +1359,19 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
     {
     si->StdIn = 0;
     }
+  else if(cp->PipeNativeSTDIN[0] >= 0)
+    {
+    si->StdIn = cp->PipeNativeSTDIN[0];
+
+    /* Set close-on-exec flag on the pipe's ends.  The read end will
+       be dup2-ed into the stdin descriptor after the fork but before
+       the exec.  */
+    if((fcntl(cp->PipeNativeSTDIN[0], F_SETFD, FD_CLOEXEC) < 0) ||
+       (fcntl(cp->PipeNativeSTDIN[1], F_SETFD, FD_CLOEXEC) < 0))
+      {
+      return 0;
+      }
+    }
   else
     {
     si->StdIn = -1;
@@ -1340,6 +1415,17 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
     si->StdOut = 1;
     }
 
+  /* Replace the stdout pipe with the native pipe provided if any.  In
+     this case the select call will report that stdout is closed
+     immediately.  */
+  if(prIndex == cp->NumberOfCommands-1 && cp->PipeNativeSTDOUT[1] >= 0)
+    {
+    if(!kwsysProcessSetupOutputPipeNative(&si->StdOut, cp->PipeNativeSTDOUT))
+      {
+      return 0;
+      }
+    }
+
   /* Create the error reporting pipe.  */
   if(pipe(si->ErrorPipe) < 0)
     {
@@ -1519,6 +1605,26 @@ static int kwsysProcessSetupOutputPipeFile(int* p, const char* name)
   return 1;  
 }
 
+/*--------------------------------------------------------------------------*/
+static int kwsysProcessSetupOutputPipeNative(int* p, int des[2])
+{
+  /* Close the existing descriptor.  */
+  kwsysProcessCleanupDescriptor(p);
+
+  /* Set close-on-exec flag on the pipe's ends.  The proper end will
+     be dup2-ed into the standard descriptor number after fork but
+     before exec.  */
+  if((fcntl(des[0], F_SETFD, FD_CLOEXEC) < 0) ||
+     (fcntl(des[1], F_SETFD, FD_CLOEXEC) < 0))
+    {
+    return 0;
+    }
+
+  /* Assign the replacement descriptor.  */
+  *p = des[1];
+  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.  */

+ 107 - 2
Source/kwsys/ProcessWin32.c

@@ -105,6 +105,8 @@ static int kwsysProcessCreate(kwsysProcess* cp, int index,
 static void kwsysProcessDestroy(kwsysProcess* cp, int event);
 static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name);
 static int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle);
+static int kwsysProcessSetupPipeNative(PHANDLE handle, HANDLE p[2],
+                                       int isWrite);
 static void kwsysProcessCleanupHandle(PHANDLE h);
 static void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle);
 static void kwsysProcessCleanup(kwsysProcess* cp, int error);
@@ -247,6 +249,11 @@ struct kwsysProcess_s
   int PipeSharedSTDOUT;
   int PipeSharedSTDERR;
 
+  /* Native pipes provided by the user.  */
+  HANDLE PipeNativeSTDIN[2];
+  HANDLE PipeNativeSTDOUT[2];
+  HANDLE PipeNativeSTDERR[2];
+
   /* Handle to automatically delete the Win9x forwarding executable.  */
   HANDLE Win9xHandle;
 
@@ -790,9 +797,11 @@ int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, const char* file)
     strcpy(*pfile, file);
     }
 
-  /* If we are redirecting the pipe, do not share it.  */
+  /* If we are redirecting the pipe, do not share it or use a native
+     pipe.  */
   if(*pfile)
     {
+    kwsysProcess_SetPipeNative(cp, pipe, 0);
     kwsysProcess_SetPipeShared(cp, pipe, 0);
     }
 
@@ -815,10 +824,51 @@ void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, int shared)
     default: return;
     }
 
-  /* If we are sharing the pipe, do not redirect it to a file.  */
+  /* If we are sharing the pipe, do not redirect it to a file or use a
+     native pipe.  */
   if(shared)
     {
     kwsysProcess_SetPipeFile(cp, pipe, 0);
+    kwsysProcess_SetPipeNative(cp, pipe, 0);
+    }
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_SetPipeNative(kwsysProcess* cp, int pipe, HANDLE p[2])
+{
+  HANDLE* pPipeNative = 0;
+
+  if(!cp)
+    {
+    return;
+    }
+
+  switch(pipe)
+    {
+    case kwsysProcess_Pipe_STDIN: pPipeNative = cp->PipeNativeSTDIN; break;
+    case kwsysProcess_Pipe_STDOUT: pPipeNative = cp->PipeNativeSTDOUT; break;
+    case kwsysProcess_Pipe_STDERR: pPipeNative = cp->PipeNativeSTDERR; break;
+    default: return;
+    }
+
+  /* Copy the native pipe handles provided.  */
+  if(p)
+    {
+    pPipeNative[0] = p[0];
+    pPipeNative[1] = p[1];
+    }
+  else
+    {
+    pPipeNative[0] = 0;
+    pPipeNative[1] = 0;
+    }
+
+  /* If we are using a native pipe, do not share it or redirect it to
+     a file.  */
+  if(p)
+    {
+    kwsysProcess_SetPipeFile(cp, pipe, 0);
+    kwsysProcess_SetPipeShared(cp, pipe, 0);
     }
 }
 
@@ -1020,6 +1070,21 @@ void kwsysProcess_Execute(kwsysProcess* cp)
       }
     }
 
+  /* Replace the stderr pipe with the native pipe provided if any.  In
+     this case the pipe thread will still run but never report
+     data.  */
+  if(cp->PipeNativeSTDERR[1])
+    {
+    if(!kwsysProcessSetupPipeNative(&si.StartupInfo.hStdError,
+                                    cp->PipeNativeSTDERR, 1))
+      {
+      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError,
+                                    STD_ERROR_HANDLE);
+      return;
+      }
+    }
+
   /* Create the pipeline of processes.  */
   {
   HANDLE readEnd = 0;
@@ -1622,6 +1687,15 @@ int kwsysProcessCreate(kwsysProcess* cp, int index,
       return 0;
       }
     }
+  else if(cp->PipeNativeSTDIN[0])
+    {
+    /* Use the provided native pipe.  */
+    if(!kwsysProcessSetupPipeNative(&si->StartupInfo.hStdInput,
+                                    cp->PipeNativeSTDIN, 0))
+      {
+      return 0;
+      }
+    }
   else
     {
     /* Explicitly give the child no stdin.  */
@@ -1680,6 +1754,18 @@ int kwsysProcessCreate(kwsysProcess* cp, int index,
       }
     }
 
+  /* Replace the stdout pipe with the native pipe provided if any.  In
+     this case the pipe thread will still run but never report
+     data.  */
+  if(index == cp->NumberOfCommands-1 && cp->PipeNativeSTDOUT[1])
+    {
+    if(!kwsysProcessSetupPipeNative(&si->StartupInfo.hStdOutput,
+                                    cp->PipeNativeSTDOUT, 1))
+      {
+      return 0;
+      }
+    }
+
   /* Create the child process.  */
   {
   BOOL r;
@@ -1928,6 +2014,25 @@ int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle)
     }
 }
 
+/*--------------------------------------------------------------------------*/
+int kwsysProcessSetupPipeNative(PHANDLE handle, HANDLE p[2], int isWrite)
+{
+  /* Close the existing inherited handle.  */
+  kwsysProcessCleanupHandle(handle);
+
+  /* Create an inherited duplicate of the handle.  This also closes
+     the non-inherited version.  */
+  if(!DuplicateHandle(GetCurrentProcess(), p[isWrite? 1:0],
+                      GetCurrentProcess(), handle,
+                      0, TRUE, (DUPLICATE_CLOSE_SOURCE |
+                                DUPLICATE_SAME_ACCESS)))
+    {
+    return 0;
+    }
+
+  return 1;
+}
+
 /*--------------------------------------------------------------------------*/
 
 /* Close the given handle if it is open.  Reset its value to 0.  */