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_SetTimeout kwsys_ns(Process_SetTimeout)
#define kwsysProcess_SetWorkingDirectory kwsys_ns(Process_SetWorkingDirectory) #define kwsysProcess_SetWorkingDirectory kwsys_ns(Process_SetWorkingDirectory)
#define kwsysProcess_SetPipeFile kwsys_ns(Process_SetPipeFile) #define kwsysProcess_SetPipeFile kwsys_ns(Process_SetPipeFile)
#define kwsysProcess_SetPipeNative kwsys_ns(Process_SetPipeNative)
#define kwsysProcess_SetPipeShared kwsys_ns(Process_SetPipeShared) #define kwsysProcess_SetPipeShared kwsys_ns(Process_SetPipeShared)
#define kwsysProcess_Option_Detach kwsys_ns(Process_Option_Detach) #define kwsysProcess_Option_Detach kwsys_ns(Process_Option_Detach)
#define kwsysProcess_Option_HideWindow kwsys_ns(Process_Option_HideWindow) #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_STDOUT kwsys_ns(Process_Pipe_STDOUT)
#define kwsysProcess_Pipe_STDERR kwsys_ns(Process_Pipe_STDERR) #define kwsysProcess_Pipe_STDERR kwsys_ns(Process_Pipe_STDERR)
#define kwsysProcess_Pipe_Timeout kwsys_ns(Process_Pipe_Timeout) #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_WaitForExit kwsys_ns(Process_WaitForExit)
#define kwsysProcess_Kill kwsys_ns(Process_Kill) #define kwsysProcess_Kill kwsys_ns(Process_Kill)
@ -84,6 +86,13 @@ extern "C"
*/ */
typedef struct kwsysProcess_s kwsysProcess; 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. * 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, kwsysEXPORT void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe,
int shared); 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: * 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_SetTimeout
# undef kwsysProcess_SetWorkingDirectory # undef kwsysProcess_SetWorkingDirectory
# undef kwsysProcess_SetPipeFile # undef kwsysProcess_SetPipeFile
# undef kwsysProcess_SetPipeNative
# undef kwsysProcess_SetPipeShared # undef kwsysProcess_SetPipeShared
# undef kwsysProcess_Option_Detach # undef kwsysProcess_Option_Detach
# undef kwsysProcess_Option_HideWindow # undef kwsysProcess_Option_HideWindow
@ -387,6 +418,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
# undef kwsysProcess_Pipe_STDOUT # undef kwsysProcess_Pipe_STDOUT
# undef kwsysProcess_Pipe_STDERR # undef kwsysProcess_Pipe_STDERR
# undef kwsysProcess_Pipe_Timeout # undef kwsysProcess_Pipe_Timeout
# undef kwsysProcess_Pipe_Handle
# undef kwsysProcess_WaitForExit # undef kwsysProcess_WaitForExit
# undef kwsysProcess_Kill # undef kwsysProcess_Kill
#endif #endif

View File

@ -102,6 +102,7 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
kwsysProcessCreateInformation* si, int* readEnd); kwsysProcessCreateInformation* si, int* readEnd);
static void kwsysProcessDestroy(kwsysProcess* cp); static void kwsysProcessDestroy(kwsysProcess* cp);
static int kwsysProcessSetupOutputPipeFile(int* p, const char* name); static int kwsysProcessSetupOutputPipeFile(int* p, const char* name);
static int kwsysProcessSetupOutputPipeNative(int* p, int des[2]);
static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
kwsysProcessTime* timeoutTime); kwsysProcessTime* timeoutTime);
static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime,
@ -217,6 +218,11 @@ struct kwsysProcess_s
int PipeSharedSTDOUT; int PipeSharedSTDOUT;
int PipeSharedSTDERR; int PipeSharedSTDERR;
/* Native pipes provided by the user. */
int PipeNativeSTDIN[2];
int PipeNativeSTDOUT[2];
int PipeNativeSTDERR[2];
/* The real working directory of this process. */ /* The real working directory of this process. */
int RealWorkingDirectoryLength; int RealWorkingDirectoryLength;
char* RealWorkingDirectory; char* RealWorkingDirectory;
@ -470,9 +476,11 @@ int kwsysProcess_SetPipeFile(kwsysProcess* cp, int prPipe, const char* file)
strcpy(*pfile, 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) if(*pfile)
{ {
kwsysProcess_SetPipeNative(cp, prPipe, 0);
kwsysProcess_SetPipeShared(cp, prPipe, 0); kwsysProcess_SetPipeShared(cp, prPipe, 0);
} }
return 1; return 1;
@ -494,10 +502,51 @@ void kwsysProcess_SetPipeShared(kwsysProcess* cp, int prPipe, int shared)
default: return; 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) if(shared)
{ {
kwsysProcess_SetPipeFile(cp, prPipe, 0); 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; 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. */ /* The timeout period starts now. */
cp->StartTime = kwsysProcessTimeGetCurrent(); cp->StartTime = kwsysProcessTimeGetCurrent();
cp->TimeoutTime.tv_sec = -1; cp->TimeoutTime.tv_sec = -1;
@ -1297,6 +1359,19 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
{ {
si->StdIn = 0; 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 else
{ {
si->StdIn = -1; si->StdIn = -1;
@ -1340,6 +1415,17 @@ static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
si->StdOut = 1; 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. */ /* Create the error reporting pipe. */
if(pipe(si->ErrorPipe) < 0) if(pipe(si->ErrorPipe) < 0)
{ {
@ -1519,6 +1605,26 @@ static int kwsysProcessSetupOutputPipeFile(int* p, const char* name)
return 1; 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 /* Get the time at which either the process or user timeout will
expire. Returns 1 if the user timeout is first, and 0 otherwise. */ 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 void kwsysProcessDestroy(kwsysProcess* cp, int event);
static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name); static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name);
static int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle); static int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle);
static int kwsysProcessSetupPipeNative(PHANDLE handle, HANDLE p[2],
int isWrite);
static void kwsysProcessCleanupHandle(PHANDLE h); static void kwsysProcessCleanupHandle(PHANDLE h);
static void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle); static void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle);
static void kwsysProcessCleanup(kwsysProcess* cp, int error); static void kwsysProcessCleanup(kwsysProcess* cp, int error);
@ -247,6 +249,11 @@ struct kwsysProcess_s
int PipeSharedSTDOUT; int PipeSharedSTDOUT;
int PipeSharedSTDERR; 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 to automatically delete the Win9x forwarding executable. */
HANDLE Win9xHandle; HANDLE Win9xHandle;
@ -790,9 +797,11 @@ int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, const char* file)
strcpy(*pfile, 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) if(*pfile)
{ {
kwsysProcess_SetPipeNative(cp, pipe, 0);
kwsysProcess_SetPipeShared(cp, pipe, 0); kwsysProcess_SetPipeShared(cp, pipe, 0);
} }
@ -815,10 +824,51 @@ void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, int shared)
default: return; 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) if(shared)
{ {
kwsysProcess_SetPipeFile(cp, pipe, 0); 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. */ /* Create the pipeline of processes. */
{ {
HANDLE readEnd = 0; HANDLE readEnd = 0;
@ -1622,6 +1687,15 @@ int kwsysProcessCreate(kwsysProcess* cp, int index,
return 0; return 0;
} }
} }
else if(cp->PipeNativeSTDIN[0])
{
/* Use the provided native pipe. */
if(!kwsysProcessSetupPipeNative(&si->StartupInfo.hStdInput,
cp->PipeNativeSTDIN, 0))
{
return 0;
}
}
else else
{ {
/* Explicitly give the child no stdin. */ /* 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. */ /* Create the child process. */
{ {
BOOL r; 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. */ /* Close the given handle if it is open. Reset its value to 0. */