From 6eef6638a5ca1c76b7d51e9d71bc5ef9c96875e0 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 3 Oct 2006 09:10:03 -0400 Subject: ENH: Added Process_SetPipeNative method to allow user code to override the pipes connected to the child pipeline. --- Source/kwsys/Process.h.in | 32 +++++++++++++ Source/kwsys/ProcessUNIX.c | 110 +++++++++++++++++++++++++++++++++++++++++++- Source/kwsys/ProcessWin32.c | 109 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 247 insertions(+), 4 deletions(-) diff --git a/Source/kwsys/Process.h.in b/Source/kwsys/Process.h.in index 3d2db7b..26ff75e 100644 --- a/Source/kwsys/Process.h.in +++ b/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. */ @@ -146,6 +155,27 @@ 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: * * kwsysProcess_Option_Detach = Whether to detach the process. @@ -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 diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index fadc5f6..e4b4219 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/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) { @@ -1520,6 +1606,26 @@ static int kwsysProcessSetupOutputPipeFile(int* p, const char* name) } /*--------------------------------------------------------------------------*/ +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. */ static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c index 4f50d36..8f8aca6 100644 --- a/Source/kwsys/ProcessWin32.c +++ b/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; @@ -1929,6 +2015,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. */ void kwsysProcessCleanupHandle(PHANDLE h) -- cgit v0.12