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

This commit is contained in:
Brad King 2006-10-03 09:10:03 -04:00
parent 9d566ee8bd
commit 6eef6638a5
3 changed files with 247 additions and 4 deletions

View File

@ -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

View File

@ -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. */

View File

@ -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. */