| /*============================================================================ |
| KWSys - Kitware System Library |
| Copyright 2000-2009 Kitware, Inc., Insight Software Consortium |
| |
| Distributed under the OSI-approved BSD License (the "License"); |
| see accompanying file Copyright.txt for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even the |
| implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the License for more information. |
| ============================================================================*/ |
| #include "kwsysPrivate.h" |
| #include KWSYS_HEADER(Process.h) |
| #include KWSYS_HEADER(System.h) |
| |
| /* Work-around CMake dependency scanning limitation. This must |
| duplicate the above list of headers. */ |
| #if 0 |
| # include "Process.h.in" |
| # include "System.h.in" |
| #endif |
| |
| /* |
| |
| Implementation for Windows |
| |
| On windows, a thread is created to wait for data on each pipe. The |
| threads are synchronized with the main thread to simulate the use of |
| a UNIX-style select system call. |
| |
| */ |
| |
| #ifdef _MSC_VER |
| #pragma warning (push, 1) |
| #endif |
| #include <windows.h> /* Windows API */ |
| #include <string.h> /* strlen, strdup */ |
| #include <stdio.h> /* sprintf */ |
| #include <io.h> /* _unlink */ |
| #ifdef __WATCOMC__ |
| #define _unlink unlink |
| #endif |
| |
| #ifndef _MAX_FNAME |
| #define _MAX_FNAME 4096 |
| #endif |
| #ifndef _MAX_PATH |
| #define _MAX_PATH 4096 |
| #endif |
| |
| #ifdef _MSC_VER |
| #pragma warning (pop) |
| #pragma warning (disable: 4514) |
| #pragma warning (disable: 4706) |
| #endif |
| |
| #if defined(__BORLANDC__) |
| # pragma warn -8004 /* assigned a value that is never used */ |
| # pragma warn -8060 /* Assignment inside if() condition. */ |
| #endif |
| |
| /* There are pipes for the process pipeline's stdout and stderr. */ |
| #define KWSYSPE_PIPE_COUNT 2 |
| #define KWSYSPE_PIPE_STDOUT 0 |
| #define KWSYSPE_PIPE_STDERR 1 |
| |
| /* The maximum amount to read from a pipe at a time. */ |
| #define KWSYSPE_PIPE_BUFFER_SIZE 1024 |
| |
| /* Debug output macro. */ |
| #if 0 |
| # define KWSYSPE_DEBUG(x) \ |
| ( \ |
| (void*)cp == (void*)0x00226DE0? \ |
| ( \ |
| fprintf(stderr, "%d/%p/%d ", (int)GetCurrentProcessId(), cp, __LINE__), \ |
| fprintf x, \ |
| fflush(stderr), \ |
| 1 \ |
| ) : (1) \ |
| ) |
| #else |
| # define KWSYSPE_DEBUG(x) (void)1 |
| #endif |
| |
| typedef LARGE_INTEGER kwsysProcessTime; |
| |
| typedef struct kwsysProcessCreateInformation_s |
| { |
| /* Windows child startup control data. */ |
| STARTUPINFO StartupInfo; |
| } kwsysProcessCreateInformation; |
| |
| /*--------------------------------------------------------------------------*/ |
| typedef struct kwsysProcessPipeData_s kwsysProcessPipeData; |
| static DWORD WINAPI kwsysProcessPipeThreadRead(LPVOID ptd); |
| static void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, |
| kwsysProcessPipeData* td); |
| static DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID ptd); |
| static void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp, |
| kwsysProcessPipeData* td); |
| static int kwsysProcessInitialize(kwsysProcess* cp); |
| static int kwsysProcessCreate(kwsysProcess* cp, int index, |
| kwsysProcessCreateInformation* si, |
| PHANDLE readEnd); |
| 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); |
| static void kwsysProcessCleanErrorMessage(kwsysProcess* cp); |
| static int kwsysProcessComputeCommandLength(kwsysProcess* cp, |
| char const* const* command); |
| static void kwsysProcessComputeCommandLine(kwsysProcess* cp, |
| char const* const* command, |
| char* cmd); |
| static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, |
| kwsysProcessTime* timeoutTime); |
| static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, |
| double* userTimeout, |
| kwsysProcessTime* timeoutLength); |
| static kwsysProcessTime kwsysProcessTimeGetCurrent(void); |
| static DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t); |
| static double kwsysProcessTimeToDouble(kwsysProcessTime t); |
| 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 kwsysProcessSetExitException(kwsysProcess* cp, int code); |
| static void kwsysProcessKillTree(int pid); |
| static void kwsysProcessDisablePipeThreads(kwsysProcess* cp); |
| |
| /*--------------------------------------------------------------------------*/ |
| /* A structure containing synchronization data for each thread. */ |
| typedef struct kwsysProcessPipeSync_s kwsysProcessPipeSync; |
| struct kwsysProcessPipeSync_s |
| { |
| /* Handle to the thread. */ |
| HANDLE Thread; |
| |
| /* Semaphore indicating to the thread that a process has started. */ |
| HANDLE Ready; |
| |
| /* Semaphore indicating to the thread that it should begin work. */ |
| HANDLE Go; |
| |
| /* Semaphore indicating thread has reset for another process. */ |
| HANDLE Reset; |
| }; |
| |
| /*--------------------------------------------------------------------------*/ |
| /* A structure containing data for each pipe's threads. */ |
| struct kwsysProcessPipeData_s |
| { |
| /* ------------- Data managed per instance of kwsysProcess ------------- */ |
| |
| /* Synchronization data for reading thread. */ |
| kwsysProcessPipeSync Reader; |
| |
| /* Synchronization data for waking thread. */ |
| kwsysProcessPipeSync Waker; |
| |
| /* Index of this pipe. */ |
| int Index; |
| |
| /* The kwsysProcess instance owning this pipe. */ |
| kwsysProcess* Process; |
| |
| /* ------------- Data managed per call to Execute ------------- */ |
| |
| /* Buffer for data read in this pipe's thread. */ |
| char DataBuffer[KWSYSPE_PIPE_BUFFER_SIZE]; |
| |
| /* The length of the data stored in the buffer. */ |
| DWORD DataLength; |
| |
| /* Whether the pipe has been closed. */ |
| int Closed; |
| |
| /* Handle for the read end of this pipe. */ |
| HANDLE Read; |
| |
| /* Handle for the write end of this pipe. */ |
| HANDLE Write; |
| }; |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Structure containing data used to implement the child's execution. */ |
| struct kwsysProcess_s |
| { |
| /* ------------- Data managed per instance of kwsysProcess ------------- */ |
| |
| /* The status of the process structure. */ |
| int State; |
| |
| /* The command lines to execute. */ |
| char** Commands; |
| int NumberOfCommands; |
| |
| /* The exit code of each command. */ |
| DWORD* CommandExitCodes; |
| |
| /* The working directory for the child process. */ |
| char* WorkingDirectory; |
| |
| /* Whether to create the child as a detached process. */ |
| int OptionDetach; |
| |
| /* Whether the child was created as a detached process. */ |
| int Detached; |
| |
| /* Whether to hide the child process's window. */ |
| int HideWindow; |
| |
| /* Whether to treat command lines as verbatim. */ |
| int Verbatim; |
| |
| /* Mutex to protect the shared index used by threads to report data. */ |
| HANDLE SharedIndexMutex; |
| |
| /* Semaphore used by threads to signal data ready. */ |
| HANDLE Full; |
| |
| /* Whether we are currently deleting this kwsysProcess instance. */ |
| int Deleting; |
| |
| /* Data specific to each pipe and its thread. */ |
| kwsysProcessPipeData Pipe[KWSYSPE_PIPE_COUNT]; |
| |
| /* Name of files to which stdin and stdout pipes are attached. */ |
| char* PipeFileSTDIN; |
| char* PipeFileSTDOUT; |
| char* PipeFileSTDERR; |
| |
| /* Whether each pipe is shared with the parent process. */ |
| int PipeSharedSTDIN; |
| int PipeSharedSTDOUT; |
| int PipeSharedSTDERR; |
| |
| /* Native pipes provided by the user. */ |
| HANDLE PipeNativeSTDIN[2]; |
| HANDLE PipeNativeSTDOUT[2]; |
| HANDLE PipeNativeSTDERR[2]; |
| |
| /* ------------- Data managed per call to Execute ------------- */ |
| |
| /* The exceptional behavior that terminated the process, if any. */ |
| int ExitException; |
| |
| /* The process exit code. */ |
| DWORD ExitCode; |
| |
| /* The process return code, if any. */ |
| int ExitValue; |
| |
| /* Index of last pipe to report data, if any. */ |
| int CurrentIndex; |
| |
| /* Index shared by threads to report data. */ |
| int SharedIndex; |
| |
| /* The timeout length. */ |
| double Timeout; |
| |
| /* Time at which the child started. */ |
| kwsysProcessTime StartTime; |
| |
| /* Time at which the child will timeout. Negative for no timeout. */ |
| kwsysProcessTime TimeoutTime; |
| |
| /* Flag for whether the process was killed. */ |
| int Killed; |
| |
| /* Flag for whether the timeout expired. */ |
| int TimeoutExpired; |
| |
| /* Flag for whether the process has terminated. */ |
| int Terminated; |
| |
| /* The number of pipes still open during execution and while waiting |
| for pipes to close after process termination. */ |
| int PipesLeft; |
| |
| /* Buffer for error messages. */ |
| char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1]; |
| |
| /* Description for the ExitException. */ |
| char ExitExceptionString[KWSYSPE_PIPE_BUFFER_SIZE+1]; |
| |
| /* Windows process information data. */ |
| PROCESS_INFORMATION* ProcessInformation; |
| |
| /* Data and process termination events for which to wait. */ |
| PHANDLE ProcessEvents; |
| int ProcessEventsLength; |
| |
| /* Real working directory of our own process. */ |
| DWORD RealWorkingDirectoryLength; |
| char* RealWorkingDirectory; |
| }; |
| |
| /*--------------------------------------------------------------------------*/ |
| kwsysProcess* kwsysProcess_New(void) |
| { |
| int i; |
| |
| /* Process control structure. */ |
| kwsysProcess* cp; |
| |
| /* Windows version number data. */ |
| OSVERSIONINFO osv; |
| |
| /* Allocate a process control structure. */ |
| cp = (kwsysProcess*)malloc(sizeof(kwsysProcess)); |
| if(!cp) |
| { |
| /* Could not allocate memory for the control structure. */ |
| return 0; |
| } |
| ZeroMemory(cp, sizeof(*cp)); |
| |
| /* Share stdin with the parent process by default. */ |
| cp->PipeSharedSTDIN = 1; |
| |
| /* Set initial status. */ |
| cp->State = kwsysProcess_State_Starting; |
| |
| /* Choose a method of running the child based on version of |
| windows. */ |
| ZeroMemory(&osv, sizeof(osv)); |
| osv.dwOSVersionInfoSize = sizeof(osv); |
| GetVersionEx(&osv); |
| if(osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) |
| { |
| /* Win9x no longer supported. */ |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* Initially no thread owns the mutex. Initialize semaphore to 1. */ |
| if(!(cp->SharedIndexMutex = CreateSemaphore(0, 1, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* Initially no data are available. Initialize semaphore to 0. */ |
| if(!(cp->Full = CreateSemaphore(0, 0, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* Create the thread to read each pipe. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| DWORD dummy=0; |
| |
| /* Assign the thread its index. */ |
| cp->Pipe[i].Index = i; |
| |
| /* Give the thread a pointer back to the kwsysProcess instance. */ |
| cp->Pipe[i].Process = cp; |
| |
| /* No process is yet running. Initialize semaphore to 0. */ |
| if(!(cp->Pipe[i].Reader.Ready = CreateSemaphore(0, 0, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* The pipe is not yet reset. Initialize semaphore to 0. */ |
| if(!(cp->Pipe[i].Reader.Reset = CreateSemaphore(0, 0, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* The thread's buffer is initially empty. Initialize semaphore to 1. */ |
| if(!(cp->Pipe[i].Reader.Go = CreateSemaphore(0, 1, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* Create the reading thread. It will block immediately. The |
| thread will not make deeply nested calls, so we need only a |
| small stack. */ |
| if(!(cp->Pipe[i].Reader.Thread = CreateThread(0, 1024, |
| kwsysProcessPipeThreadRead, |
| &cp->Pipe[i], 0, &dummy))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* No process is yet running. Initialize semaphore to 0. */ |
| if(!(cp->Pipe[i].Waker.Ready = CreateSemaphore(0, 0, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* The pipe is not yet reset. Initialize semaphore to 0. */ |
| if(!(cp->Pipe[i].Waker.Reset = CreateSemaphore(0, 0, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* The waker should not wake immediately. Initialize semaphore to 0. */ |
| if(!(cp->Pipe[i].Waker.Go = CreateSemaphore(0, 0, 1, 0))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| |
| /* Create the waking thread. It will block immediately. The |
| thread will not make deeply nested calls, so we need only a |
| small stack. */ |
| if(!(cp->Pipe[i].Waker.Thread = CreateThread(0, 1024, |
| kwsysProcessPipeThreadWake, |
| &cp->Pipe[i], 0, &dummy))) |
| { |
| kwsysProcess_Delete(cp); |
| return 0; |
| } |
| } |
| |
| return cp; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_Delete(kwsysProcess* cp) |
| { |
| int i; |
| |
| /* Make sure we have an instance. */ |
| if(!cp) |
| { |
| return; |
| } |
| |
| /* If the process is executing, wait for it to finish. */ |
| if(cp->State == kwsysProcess_State_Executing) |
| { |
| if(cp->Detached) |
| { |
| kwsysProcess_Disown(cp); |
| } |
| else |
| { |
| kwsysProcess_WaitForExit(cp, 0); |
| } |
| } |
| |
| /* We are deleting the kwsysProcess instance. */ |
| cp->Deleting = 1; |
| |
| /* Terminate each of the threads. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| /* Terminate this reading thread. */ |
| if(cp->Pipe[i].Reader.Thread) |
| { |
| /* Signal the thread we are ready for it. It will terminate |
| immediately since Deleting is set. */ |
| ReleaseSemaphore(cp->Pipe[i].Reader.Ready, 1, 0); |
| |
| /* Wait for the thread to exit. */ |
| WaitForSingleObject(cp->Pipe[i].Reader.Thread, INFINITE); |
| |
| /* Close the handle to the thread. */ |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Thread); |
| } |
| |
| /* Terminate this waking thread. */ |
| if(cp->Pipe[i].Waker.Thread) |
| { |
| /* Signal the thread we are ready for it. It will terminate |
| immediately since Deleting is set. */ |
| ReleaseSemaphore(cp->Pipe[i].Waker.Ready, 1, 0); |
| |
| /* Wait for the thread to exit. */ |
| WaitForSingleObject(cp->Pipe[i].Waker.Thread, INFINITE); |
| |
| /* Close the handle to the thread. */ |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Thread); |
| } |
| |
| /* Cleanup the pipe's semaphores. */ |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Ready); |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Go); |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Reset); |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Ready); |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Go); |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Reset); |
| } |
| |
| /* Close the shared semaphores. */ |
| kwsysProcessCleanupHandle(&cp->SharedIndexMutex); |
| kwsysProcessCleanupHandle(&cp->Full); |
| |
| /* Free memory. */ |
| kwsysProcess_SetCommand(cp, 0); |
| kwsysProcess_SetWorkingDirectory(cp, 0); |
| kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDIN, 0); |
| kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDOUT, 0); |
| kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDERR, 0); |
| if(cp->CommandExitCodes) |
| { |
| free(cp->CommandExitCodes); |
| } |
| free(cp); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command) |
| { |
| int i; |
| if(!cp) |
| { |
| return 0; |
| } |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| free(cp->Commands[i]); |
| } |
| cp->NumberOfCommands = 0; |
| if(cp->Commands) |
| { |
| free(cp->Commands); |
| cp->Commands = 0; |
| } |
| if(command) |
| { |
| 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(!cp || !command || !*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]; |
| } |
| } |
| |
| /* We need to construct a single string representing the command |
| and its arguments. We will surround each argument containing |
| spaces with double-quotes. Inside a double-quoted argument, we |
| need to escape double-quotes and all backslashes before them. |
| We also need to escape backslashes at the end of an argument |
| because they come before the closing double-quote for the |
| argument. */ |
| { |
| /* First determine the length of the final string. */ |
| int length = kwsysProcessComputeCommandLength(cp, command); |
| |
| /* Allocate enough space for the command. We do not need an extra |
| byte for the terminating null because we allocated a space for |
| the first argument that we will not use. */ |
| newCommands[cp->NumberOfCommands] = (char*)malloc(length); |
| if(!newCommands[cp->NumberOfCommands]) |
| { |
| /* Out of memory. */ |
| free(newCommands); |
| return 0; |
| } |
| |
| /* Construct the command line in the allocated buffer. */ |
| kwsysProcessComputeCommandLine(cp, command, |
| newCommands[cp->NumberOfCommands]); |
| } |
| |
| /* Save the new array of commands. */ |
| free(cp->Commands); |
| cp->Commands = newCommands; |
| cp->NumberOfCommands = newNumberOfCommands; |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout) |
| { |
| if(!cp) |
| { |
| return; |
| } |
| cp->Timeout = timeout; |
| if(cp->Timeout < 0) |
| { |
| cp->Timeout = 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) |
| { |
| if(!cp) |
| { |
| return 0; |
| } |
| if(cp->WorkingDirectory) |
| { |
| free(cp->WorkingDirectory); |
| cp->WorkingDirectory = 0; |
| } |
| if(dir && dir[0]) |
| { |
| /* We must convert the working directory to a full path. */ |
| DWORD length = GetFullPathName(dir, 0, 0, 0); |
| if(length > 0) |
| { |
| cp->WorkingDirectory = (char*)malloc(length); |
| if(!cp->WorkingDirectory) |
| { |
| return 0; |
| } |
| if(!GetFullPathName(dir, length, cp->WorkingDirectory, 0)) |
| { |
| free(cp->WorkingDirectory); |
| cp->WorkingDirectory = 0; |
| return 0; |
| } |
| } |
| } |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, const char* file) |
| { |
| char** pfile; |
| if(!cp) |
| { |
| return 0; |
| } |
| switch(pipe) |
| { |
| case kwsysProcess_Pipe_STDIN: pfile = &cp->PipeFileSTDIN; break; |
| case kwsysProcess_Pipe_STDOUT: pfile = &cp->PipeFileSTDOUT; break; |
| case kwsysProcess_Pipe_STDERR: pfile = &cp->PipeFileSTDERR; break; |
| default: return 0; |
| } |
| if(*pfile) |
| { |
| free(*pfile); |
| *pfile = 0; |
| } |
| if(file) |
| { |
| *pfile = (char*)malloc(strlen(file)+1); |
| if(!*pfile) |
| { |
| return 0; |
| } |
| strcpy(*pfile, file); |
| } |
| |
| /* 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); |
| } |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, int shared) |
| { |
| if(!cp) |
| { |
| return; |
| } |
| |
| switch(pipe) |
| { |
| case kwsysProcess_Pipe_STDIN: cp->PipeSharedSTDIN = shared?1:0; break; |
| case kwsysProcess_Pipe_STDOUT: cp->PipeSharedSTDOUT = shared?1:0; break; |
| case kwsysProcess_Pipe_STDERR: cp->PipeSharedSTDERR = shared?1:0; break; |
| default: return; |
| } |
| |
| /* 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); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) |
| { |
| if(!cp) |
| { |
| return 0; |
| } |
| |
| switch(optionId) |
| { |
| case kwsysProcess_Option_Detach: return cp->OptionDetach; |
| case kwsysProcess_Option_HideWindow: return cp->HideWindow; |
| case kwsysProcess_Option_Verbatim: return cp->Verbatim; |
| default: return 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) |
| { |
| if(!cp) |
| { |
| return; |
| } |
| |
| switch(optionId) |
| { |
| case kwsysProcess_Option_Detach: cp->OptionDetach = value; break; |
| case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break; |
| case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break; |
| default: break; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetState(kwsysProcess* cp) |
| { |
| return cp? cp->State : kwsysProcess_State_Error; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetExitException(kwsysProcess* cp) |
| { |
| return cp? cp->ExitException : kwsysProcess_Exception_Other; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetExitValue(kwsysProcess* cp) |
| { |
| return cp? cp->ExitValue : -1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_GetExitCode(kwsysProcess* cp) |
| { |
| return cp? cp->ExitCode : 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| const char* kwsysProcess_GetErrorString(kwsysProcess* cp) |
| { |
| if(!cp) |
| { |
| return "Process management structure could not be allocated"; |
| } |
| else if(cp->State == kwsysProcess_State_Error) |
| { |
| return cp->ErrorMessage; |
| } |
| return "Success"; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| const char* kwsysProcess_GetExceptionString(kwsysProcess* cp) |
| { |
| if(!cp) |
| { |
| return "GetExceptionString called with NULL process management structure"; |
| } |
| else if(cp->State == kwsysProcess_State_Exception) |
| { |
| return cp->ExitExceptionString; |
| } |
| return "No exception"; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_Execute(kwsysProcess* cp) |
| { |
| int i; |
| |
| /* Child startup control data. */ |
| kwsysProcessCreateInformation si; |
| |
| /* Do not execute a second time. */ |
| if(!cp || cp->State == kwsysProcess_State_Executing) |
| { |
| return; |
| } |
| |
| /* Make sure we have something to run. */ |
| if(cp->NumberOfCommands < 1) |
| { |
| strcpy(cp->ErrorMessage, "No command"); |
| cp->State = kwsysProcess_State_Error; |
| return; |
| } |
| |
| /* Initialize the control structure for a new process. */ |
| if(!kwsysProcessInitialize(cp)) |
| { |
| strcpy(cp->ErrorMessage, "Out of memory"); |
| cp->State = kwsysProcess_State_Error; |
| return; |
| } |
| |
| /* Save the real working directory of this process and change to |
| the working directory for the child processes. This is needed |
| to make pipe file paths evaluate correctly. */ |
| if(cp->WorkingDirectory) |
| { |
| if(!GetCurrentDirectory(cp->RealWorkingDirectoryLength, |
| cp->RealWorkingDirectory)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| SetCurrentDirectory(cp->WorkingDirectory); |
| } |
| |
| /* Initialize startup info data. */ |
| ZeroMemory(&si, sizeof(si)); |
| si.StartupInfo.cb = sizeof(si.StartupInfo); |
| |
| /* Decide whether a child window should be shown. */ |
| si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; |
| si.StartupInfo.wShowWindow = |
| (unsigned short)(cp->HideWindow?SW_HIDE:SW_SHOWDEFAULT); |
| |
| /* Connect the child's output pipes to the threads. */ |
| si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; |
| |
| /* Create stderr pipe to be shared by all processes in the pipeline. |
| Neither end is directly inherited. */ |
| if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read, |
| &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| return; |
| } |
| |
| /* Create an inherited duplicate of the write end, but do not |
| close the non-inherited version. We need to keep it open |
| to use in waking up the pipe threads. */ |
| if(!DuplicateHandle(GetCurrentProcess(), cp->Pipe[KWSYSPE_PIPE_STDERR].Write, |
| GetCurrentProcess(), &si.StartupInfo.hStdError, |
| 0, TRUE, DUPLICATE_SAME_ACCESS)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); |
| return; |
| } |
| |
| /* Replace the stderr pipe with a file if requested. In this case |
| the pipe thread will still run but never report data. */ |
| if(cp->PipeFileSTDERR) |
| { |
| if(!kwsysProcessSetupOutputPipeFile(&si.StartupInfo.hStdError, |
| cp->PipeFileSTDERR)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); |
| return; |
| } |
| } |
| |
| /* Replace the stderr pipe with the parent process's if requested. |
| In this case the pipe thread will still run but never report |
| data. */ |
| if(cp->PipeSharedSTDERR) |
| { |
| if(!kwsysProcessSetupSharedPipe(STD_ERROR_HANDLE, |
| &si.StartupInfo.hStdError)) |
| { |
| kwsysProcessCleanup(cp, 1); |
| kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, |
| STD_ERROR_HANDLE); |
| return; |
| } |
| } |
| |
| /* 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; |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| if(kwsysProcessCreate(cp, i, &si, &readEnd)) |
| { |
| cp->ProcessEvents[i+1] = cp->ProcessInformation[i].hProcess; |
| } |
| else |
| { |
| kwsysProcessCleanup(cp, 1); |
| |
| /* Release resources that may have been allocated for this |
| process before an error occurred. */ |
| kwsysProcessCleanupHandle(&readEnd); |
| kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdInput, |
| STD_INPUT_HANDLE); |
| kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdOutput, |
| STD_OUTPUT_HANDLE); |
| kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, |
| STD_ERROR_HANDLE); |
| return; |
| } |
| } |
| |
| /* Save a handle to the output pipe for the last process. */ |
| cp->Pipe[KWSYSPE_PIPE_STDOUT].Read = readEnd; |
| } |
| |
| /* Close the inherited handles to the stderr pipe shared by all |
| processes in the pipeline. The stdout and stdin pipes are not |
| shared among all children and are therefore closed by |
| kwsysProcessCreate after each child is created. */ |
| kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, STD_ERROR_HANDLE); |
| |
| /* Restore the working directory. */ |
| if(cp->RealWorkingDirectory) |
| { |
| SetCurrentDirectory(cp->RealWorkingDirectory); |
| free(cp->RealWorkingDirectory); |
| cp->RealWorkingDirectory = 0; |
| } |
| |
| /* The timeout period starts now. */ |
| cp->StartTime = kwsysProcessTimeGetCurrent(); |
| cp->TimeoutTime = kwsysProcessTimeFromDouble(-1); |
| |
| /* All processes in the pipeline have been started in suspended |
| mode. Resume them all now. */ |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| ResumeThread(cp->ProcessInformation[i].hThread); |
| } |
| |
| /* ---- It is no longer safe to call kwsysProcessCleanup. ----- */ |
| /* Tell the pipe threads that a process has started. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| ReleaseSemaphore(cp->Pipe[i].Reader.Ready, 1, 0); |
| ReleaseSemaphore(cp->Pipe[i].Waker.Ready, 1, 0); |
| } |
| |
| /* We don't care about the children's main threads. */ |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread); |
| } |
| |
| /* No pipe has reported data. */ |
| cp->CurrentIndex = KWSYSPE_PIPE_COUNT; |
| cp->PipesLeft = KWSYSPE_PIPE_COUNT; |
| |
| /* The process has now started. */ |
| cp->State = kwsysProcess_State_Executing; |
| cp->Detached = cp->OptionDetach; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_Disown(kwsysProcess* cp) |
| { |
| int i; |
| |
| /* Make sure we are executing a detached process. */ |
| if(!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing || |
| cp->TimeoutExpired || cp->Killed || cp->Terminated) |
| { |
| return; |
| } |
| |
| /* Disable the reading threads. */ |
| kwsysProcessDisablePipeThreads(cp); |
| |
| /* Wait for all pipe threads to reset. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| WaitForSingleObject(cp->Pipe[i].Reader.Reset, INFINITE); |
| WaitForSingleObject(cp->Pipe[i].Waker.Reset, INFINITE); |
| } |
| |
| /* We will not wait for exit, so cleanup now. */ |
| kwsysProcessCleanup(cp, 0); |
| |
| /* The process has been disowned. */ |
| cp->State = kwsysProcess_State_Disowned; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length, |
| double* userTimeout) |
| { |
| kwsysProcessTime userStartTime; |
| kwsysProcessTime timeoutLength; |
| kwsysProcessTime timeoutTime; |
| DWORD timeout; |
| int user; |
| int done = 0; |
| int expired = 0; |
| int pipeId = kwsysProcess_Pipe_None; |
| DWORD w; |
| |
| /* Make sure we are executing a process. */ |
| if(!cp || cp->State != kwsysProcess_State_Executing || cp->Killed || |
| cp->TimeoutExpired) |
| { |
| return kwsysProcess_Pipe_None; |
| } |
| |
| /* Record the time at which user timeout period starts. */ |
| userStartTime = kwsysProcessTimeGetCurrent(); |
| |
| /* Calculate the time at which a timeout will expire, and whether it |
| is the user or process timeout. */ |
| user = kwsysProcessGetTimeoutTime(cp, userTimeout, &timeoutTime); |
| |
| /* Loop until we have a reason to return. */ |
| while(!done && cp->PipesLeft > 0) |
| { |
| /* If we previously got data from a thread, let it know we are |
| done with the data. */ |
| if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT) |
| { |
| KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex)); |
| ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); |
| cp->CurrentIndex = KWSYSPE_PIPE_COUNT; |
| } |
| |
| /* Setup a timeout if required. */ |
| if(kwsysProcessGetTimeoutLeft(&timeoutTime, user?userTimeout:0, |
| &timeoutLength)) |
| { |
| /* Timeout has already expired. */ |
| expired = 1; |
| break; |
| } |
| if(timeoutTime.QuadPart < 0) |
| { |
| timeout = INFINITE; |
| } |
| else |
| { |
| timeout = kwsysProcessTimeToDWORD(timeoutLength); |
| } |
| |
| /* Wait for a pipe's thread to signal or a process to terminate. */ |
| w = WaitForMultipleObjects(cp->ProcessEventsLength, cp->ProcessEvents, |
| 0, timeout); |
| if(w == WAIT_TIMEOUT) |
| { |
| /* Timeout has expired. */ |
| expired = 1; |
| done = 1; |
| } |
| else if(w == WAIT_OBJECT_0) |
| { |
| /* Save the index of the reporting thread and release the mutex. |
| The thread will block until we signal its Empty mutex. */ |
| cp->CurrentIndex = cp->SharedIndex; |
| ReleaseSemaphore(cp->SharedIndexMutex, 1, 0); |
| |
| /* Data are available or a pipe closed. */ |
| if(cp->Pipe[cp->CurrentIndex].Closed) |
| { |
| /* The pipe closed at the write end. Close the read end and |
| inform the wakeup thread it is done with this process. */ |
| kwsysProcessCleanupHandle(&cp->Pipe[cp->CurrentIndex].Read); |
| ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Waker.Go, 1, 0); |
| KWSYSPE_DEBUG((stderr, "wakeup %d\n", cp->CurrentIndex)); |
| --cp->PipesLeft; |
| } |
| else if(data && length) |
| { |
| /* Report this data. */ |
| *data = cp->Pipe[cp->CurrentIndex].DataBuffer; |
| *length = cp->Pipe[cp->CurrentIndex].DataLength; |
| switch(cp->CurrentIndex) |
| { |
| case KWSYSPE_PIPE_STDOUT: |
| pipeId = kwsysProcess_Pipe_STDOUT; break; |
| case KWSYSPE_PIPE_STDERR: |
| pipeId = kwsysProcess_Pipe_STDERR; break; |
| } |
| done = 1; |
| } |
| } |
| else |
| { |
| /* A process has terminated. */ |
| kwsysProcessDestroy(cp, w-WAIT_OBJECT_0); |
| } |
| } |
| |
| /* Update the user timeout. */ |
| if(userTimeout) |
| { |
| kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent(); |
| kwsysProcessTime difference = kwsysProcessTimeSubtract(userEndTime, |
| userStartTime); |
| double d = kwsysProcessTimeToDouble(difference); |
| *userTimeout -= d; |
| if(*userTimeout < 0) |
| { |
| *userTimeout = 0; |
| } |
| } |
| |
| /* Check what happened. */ |
| if(pipeId) |
| { |
| /* Data are ready on a pipe. */ |
| return pipeId; |
| } |
| else if(expired) |
| { |
| /* A timeout has expired. */ |
| if(user) |
| { |
| /* The user timeout has expired. It has no time left. */ |
| return kwsysProcess_Pipe_Timeout; |
| } |
| else |
| { |
| /* The process timeout has expired. Kill the child now. */ |
| KWSYSPE_DEBUG((stderr, "killing child because timeout expired\n")); |
| kwsysProcess_Kill(cp); |
| cp->TimeoutExpired = 1; |
| cp->Killed = 0; |
| return kwsysProcess_Pipe_None; |
| } |
| } |
| else |
| { |
| /* The children have terminated and no more data are available. */ |
| return kwsysProcess_Pipe_None; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout) |
| { |
| int i; |
| int pipe; |
| |
| /* Make sure we are executing a process. */ |
| if(!cp || cp->State != kwsysProcess_State_Executing) |
| { |
| return 1; |
| } |
| |
| /* Wait for the process to terminate. Ignore all data. */ |
| while((pipe = kwsysProcess_WaitForData(cp, 0, 0, userTimeout)) > 0) |
| { |
| if(pipe == kwsysProcess_Pipe_Timeout) |
| { |
| /* The user timeout has expired. */ |
| return 0; |
| } |
| } |
| |
| KWSYSPE_DEBUG((stderr, "no more data\n")); |
| |
| /* When the last pipe closes in WaitForData, the loop terminates |
| without releasing the pipe's thread. Release it now. */ |
| if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT) |
| { |
| KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex)); |
| ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); |
| cp->CurrentIndex = KWSYSPE_PIPE_COUNT; |
| } |
| |
| /* Wait for all pipe threads to reset. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| KWSYSPE_DEBUG((stderr, "waiting reader reset %d\n", i)); |
| WaitForSingleObject(cp->Pipe[i].Reader.Reset, INFINITE); |
| KWSYSPE_DEBUG((stderr, "waiting waker reset %d\n", i)); |
| WaitForSingleObject(cp->Pipe[i].Waker.Reset, INFINITE); |
| } |
| |
| /* ---- It is now safe again to call kwsysProcessCleanup. ----- */ |
| /* Close all the pipes. */ |
| kwsysProcessCleanup(cp, 0); |
| |
| /* Determine the outcome. */ |
| if(cp->Killed) |
| { |
| /* We killed the child. */ |
| cp->State = kwsysProcess_State_Killed; |
| } |
| else if(cp->TimeoutExpired) |
| { |
| /* The timeout expired. */ |
| cp->State = kwsysProcess_State_Expired; |
| } |
| else |
| { |
| /* The children exited. Report the outcome of the last process. */ |
| cp->ExitCode = cp->CommandExitCodes[cp->NumberOfCommands-1]; |
| if((cp->ExitCode & 0xF0000000) == 0xC0000000) |
| { |
| /* Child terminated due to exceptional behavior. */ |
| cp->State = kwsysProcess_State_Exception; |
| cp->ExitValue = 1; |
| kwsysProcessSetExitException(cp, cp->ExitCode); |
| } |
| else |
| { |
| /* Child exited without exception. */ |
| cp->State = kwsysProcess_State_Exited; |
| cp->ExitException = kwsysProcess_Exception_None; |
| cp->ExitValue = cp->ExitCode; |
| } |
| } |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcess_Kill(kwsysProcess* cp) |
| { |
| int i; |
| /* Make sure we are executing a process. */ |
| if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired || |
| cp->Killed) |
| { |
| KWSYSPE_DEBUG((stderr, "kill: child not executing\n")); |
| return; |
| } |
| |
| /* Disable the reading threads. */ |
| KWSYSPE_DEBUG((stderr, "kill: disabling pipe threads\n")); |
| kwsysProcessDisablePipeThreads(cp); |
| |
| /* Skip actually killing the child if it has already terminated. */ |
| if(cp->Terminated) |
| { |
| KWSYSPE_DEBUG((stderr, "kill: child already terminated\n")); |
| return; |
| } |
| |
| /* Kill the children. */ |
| cp->Killed = 1; |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| kwsysProcessKillTree(cp->ProcessInformation[i].dwProcessId); |
| // close the handle if we kill it |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread); |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess); |
| } |
| |
| /* We are killing the children and ignoring all data. Do not wait |
| for them to exit. */ |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| /* |
| Function executed for each pipe's thread. Argument is a pointer to |
| the kwsysProcessPipeData instance for this thread. |
| */ |
| DWORD WINAPI kwsysProcessPipeThreadRead(LPVOID ptd) |
| { |
| kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd; |
| kwsysProcess* cp = td->Process; |
| |
| /* Wait for a process to be ready. */ |
| while((WaitForSingleObject(td->Reader.Ready, INFINITE), !cp->Deleting)) |
| { |
| /* Read output from the process for this thread's pipe. */ |
| kwsysProcessPipeThreadReadPipe(cp, td); |
| |
| /* Signal the main thread we have reset for a new process. */ |
| ReleaseSemaphore(td->Reader.Reset, 1, 0); |
| } |
| return 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| /* |
| Function called in each pipe's thread to handle data for one |
| execution of a subprocess. |
| */ |
| void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td) |
| { |
| /* Wait for space in the thread's buffer. */ |
| while((KWSYSPE_DEBUG((stderr, "wait for read %d\n", td->Index)), |
| WaitForSingleObject(td->Reader.Go, INFINITE), !td->Closed)) |
| { |
| KWSYSPE_DEBUG((stderr, "reading %d\n", td->Index)); |
| |
| /* Read data from the pipe. This may block until data are available. */ |
| if(!ReadFile(td->Read, td->DataBuffer, KWSYSPE_PIPE_BUFFER_SIZE, |
| &td->DataLength, 0)) |
| { |
| if(GetLastError() != ERROR_BROKEN_PIPE) |
| { |
| /* UNEXPECTED failure to read the pipe. */ |
| } |
| |
| /* The pipe closed. There are no more data to read. */ |
| td->Closed = 1; |
| KWSYSPE_DEBUG((stderr, "read closed %d\n", td->Index)); |
| } |
| |
| KWSYSPE_DEBUG((stderr, "read %d\n", td->Index)); |
| |
| /* Wait for our turn to be handled by the main thread. */ |
| WaitForSingleObject(cp->SharedIndexMutex, INFINITE); |
| |
| KWSYSPE_DEBUG((stderr, "reporting read %d\n", td->Index)); |
| |
| /* Tell the main thread we have something to report. */ |
| cp->SharedIndex = td->Index; |
| ReleaseSemaphore(cp->Full, 1, 0); |
| } |
| |
| /* We were signalled to exit with our buffer empty. Reset the |
| mutex for a new process. */ |
| KWSYSPE_DEBUG((stderr, "self releasing reader %d\n", td->Index)); |
| ReleaseSemaphore(td->Reader.Go, 1, 0); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| /* |
| Function executed for each pipe's thread. Argument is a pointer to |
| the kwsysProcessPipeData instance for this thread. |
| */ |
| DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID ptd) |
| { |
| kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd; |
| kwsysProcess* cp = td->Process; |
| |
| /* Wait for a process to be ready. */ |
| while((WaitForSingleObject(td->Waker.Ready, INFINITE), !cp->Deleting)) |
| { |
| /* Wait for a possible wakeup. */ |
| kwsysProcessPipeThreadWakePipe(cp, td); |
| |
| /* Signal the main thread we have reset for a new process. */ |
| ReleaseSemaphore(td->Waker.Reset, 1, 0); |
| } |
| return 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| /* |
| Function called in each pipe's thread to handle reading thread |
| wakeup for one execution of a subprocess. |
| */ |
| void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp, kwsysProcessPipeData* td) |
| { |
| (void)cp; |
| |
| /* Wait for a possible wake command. */ |
| KWSYSPE_DEBUG((stderr, "wait for wake %d\n", td->Index)); |
| WaitForSingleObject(td->Waker.Go, INFINITE); |
| KWSYSPE_DEBUG((stderr, "waking %d\n", td->Index)); |
| |
| /* If the pipe is not closed, we need to wake up the reading thread. */ |
| if(!td->Closed) |
| { |
| DWORD dummy; |
| KWSYSPE_DEBUG((stderr, "waker %d writing byte\n", td->Index)); |
| WriteFile(td->Write, "", 1, &dummy, 0); |
| KWSYSPE_DEBUG((stderr, "waker %d wrote byte\n", td->Index)); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Initialize a process control structure for kwsysProcess_Execute. */ |
| int kwsysProcessInitialize(kwsysProcess* cp) |
| { |
| /* Reset internal status flags. */ |
| cp->TimeoutExpired = 0; |
| cp->Terminated = 0; |
| cp->Killed = 0; |
| cp->ExitException = kwsysProcess_Exception_None; |
| cp->ExitCode = 1; |
| cp->ExitValue = 1; |
| |
| /* Reset error data. */ |
| cp->ErrorMessage[0] = 0; |
| strcpy(cp->ExitExceptionString, "No exception"); |
| |
| /* Allocate process information for each process. */ |
| cp->ProcessInformation = |
| (PROCESS_INFORMATION*)malloc(sizeof(PROCESS_INFORMATION) * |
| cp->NumberOfCommands); |
| if(!cp->ProcessInformation) |
| { |
| return 0; |
| } |
| ZeroMemory(cp->ProcessInformation, |
| sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands); |
| if(cp->CommandExitCodes) |
| { |
| free(cp->CommandExitCodes); |
| } |
| cp->CommandExitCodes = (DWORD*)malloc(sizeof(DWORD)*cp->NumberOfCommands); |
| if(!cp->CommandExitCodes) |
| { |
| return 0; |
| } |
| ZeroMemory(cp->CommandExitCodes, sizeof(DWORD)*cp->NumberOfCommands); |
| |
| /* Allocate event wait array. The first event is cp->Full, the rest |
| are the process termination events. */ |
| cp->ProcessEvents = (PHANDLE)malloc(sizeof(HANDLE)*(cp->NumberOfCommands+1)); |
| if(!cp->ProcessEvents) |
| { |
| return 0; |
| } |
| ZeroMemory(cp->ProcessEvents, sizeof(HANDLE) * (cp->NumberOfCommands+1)); |
| cp->ProcessEvents[0] = cp->Full; |
| cp->ProcessEventsLength = cp->NumberOfCommands+1; |
| |
| /* Allocate space to save the real working directory of this process. */ |
| if(cp->WorkingDirectory) |
| { |
| cp->RealWorkingDirectoryLength = GetCurrentDirectory(0, 0); |
| if(cp->RealWorkingDirectoryLength > 0) |
| { |
| cp->RealWorkingDirectory = (char*)malloc(cp->RealWorkingDirectoryLength); |
| if(!cp->RealWorkingDirectory) |
| { |
| return 0; |
| } |
| } |
| } |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcessCreate(kwsysProcess* cp, int index, |
| kwsysProcessCreateInformation* si, |
| PHANDLE readEnd) |
| { |
| /* Setup the process's stdin. */ |
| if(*readEnd) |
| { |
| /* Create an inherited duplicate of the read end from the output |
| pipe of the previous process. This also closes the |
| non-inherited version. */ |
| if(!DuplicateHandle(GetCurrentProcess(), *readEnd, |
| GetCurrentProcess(), readEnd, |
| 0, TRUE, (DUPLICATE_CLOSE_SOURCE | |
| DUPLICATE_SAME_ACCESS))) |
| { |
| return 0; |
| } |
| si->StartupInfo.hStdInput = *readEnd; |
| |
| /* This function is done with this handle. */ |
| *readEnd = 0; |
| } |
| else if(cp->PipeFileSTDIN) |
| { |
| /* Create a handle to read a file for stdin. */ |
| HANDLE fin = CreateFile(cp->PipeFileSTDIN, GENERIC_READ|GENERIC_WRITE, |
| FILE_SHARE_READ|FILE_SHARE_WRITE, |
| 0, OPEN_EXISTING, 0, 0); |
| if(fin == INVALID_HANDLE_VALUE) |
| { |
| return 0; |
| } |
| /* Create an inherited duplicate of the handle. This also closes |
| the non-inherited version. */ |
| if(!DuplicateHandle(GetCurrentProcess(), fin, |
| GetCurrentProcess(), &fin, |
| 0, TRUE, (DUPLICATE_CLOSE_SOURCE | |
| DUPLICATE_SAME_ACCESS))) |
| { |
| return 0; |
| } |
| si->StartupInfo.hStdInput = fin; |
| } |
| else if(cp->PipeSharedSTDIN) |
| { |
| /* Share this process's stdin with the child. */ |
| if(!kwsysProcessSetupSharedPipe(STD_INPUT_HANDLE, |
| &si->StartupInfo.hStdInput)) |
| { |
| 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. */ |
| si->StartupInfo.hStdInput = INVALID_HANDLE_VALUE; |
| } |
| |
| /* Setup the process's stdout. */ |
| { |
| DWORD maybeClose = DUPLICATE_CLOSE_SOURCE; |
| HANDLE writeEnd; |
| |
| /* Create the output pipe for this process. Neither end is directly |
| inherited. */ |
| if(!CreatePipe(readEnd, &writeEnd, 0, 0)) |
| { |
| return 0; |
| } |
| |
| /* Create an inherited duplicate of the write end. Close the |
| non-inherited version unless this is the last process. Save the |
| non-inherited write end of the last process. */ |
| if(index == cp->NumberOfCommands-1) |
| { |
| cp->Pipe[KWSYSPE_PIPE_STDOUT].Write = writeEnd; |
| maybeClose = 0; |
| } |
| if(!DuplicateHandle(GetCurrentProcess(), writeEnd, |
| GetCurrentProcess(), &writeEnd, |
| 0, TRUE, (maybeClose | DUPLICATE_SAME_ACCESS))) |
| { |
| return 0; |
| } |
| si->StartupInfo.hStdOutput = writeEnd; |
| } |
| |
| /* Replace the stdout pipe with a file if requested. In this case |
| the pipe thread will still run but never report data. */ |
| if(index == cp->NumberOfCommands-1 && cp->PipeFileSTDOUT) |
| { |
| if(!kwsysProcessSetupOutputPipeFile(&si->StartupInfo.hStdOutput, |
| cp->PipeFileSTDOUT)) |
| { |
| return 0; |
| } |
| } |
| |
| /* Replace the stdout pipe of the last child with the parent |
| process's if requested. In this case the pipe thread will still |
| run but never report data. */ |
| if(index == cp->NumberOfCommands-1 && cp->PipeSharedSTDOUT) |
| { |
| if(!kwsysProcessSetupSharedPipe(STD_OUTPUT_HANDLE, |
| &si->StartupInfo.hStdOutput)) |
| { |
| return 0; |
| } |
| } |
| |
| /* 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 in a suspended state so we can wait until all |
| children have been created before running any one. */ |
| if(!CreateProcess(0, cp->Commands[index], 0, 0, TRUE, CREATE_SUSPENDED, 0, |
| 0, &si->StartupInfo, &cp->ProcessInformation[index])) |
| { |
| return 0; |
| } |
| |
| /* Successfully created this child process. Close the current |
| process's copies of the inherited stdout and stdin handles. The |
| stderr handle is shared among all children and is closed by |
| kwsysProcess_Execute after all children have been created. */ |
| kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdInput, |
| STD_INPUT_HANDLE); |
| kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdOutput, |
| STD_OUTPUT_HANDLE); |
| |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcessDestroy(kwsysProcess* cp, int event) |
| { |
| int i; |
| int index; |
| |
| /* Find the process index for the termination event. */ |
| for(index=0; index < cp->NumberOfCommands; ++index) |
| { |
| if(cp->ProcessInformation[index].hProcess == cp->ProcessEvents[event]) |
| { |
| break; |
| } |
| } |
| |
| /* Check the exit code of the process. */ |
| GetExitCodeProcess(cp->ProcessInformation[index].hProcess, |
| &cp->CommandExitCodes[index]); |
| |
| /* Close the process handle for the terminated process. */ |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess); |
| |
| /* Remove the process from the available events. */ |
| cp->ProcessEventsLength -= 1; |
| for(i=event; i < cp->ProcessEventsLength; ++i) |
| { |
| cp->ProcessEvents[i] = cp->ProcessEvents[i+1]; |
| } |
| |
| /* Check if all processes have terminated. */ |
| if(cp->ProcessEventsLength == 1) |
| { |
| cp->Terminated = 1; |
| |
| /* Close our copies of the pipe write handles so the pipe threads |
| can detect end-of-data. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| /* TODO: If the child created its own child (our grandchild) |
| which inherited a copy of the pipe write-end then the pipe |
| may not close and we will still need the waker write pipe. |
| However we still want to be able to detect end-of-data in the |
| normal case. The reader thread will have to switch to using |
| PeekNamedPipe to read the last bit of data from the pipe |
| without blocking. This is equivalent to using a non-blocking |
| read on posix. */ |
| KWSYSPE_DEBUG((stderr, "closing wakeup write %d\n", i)); |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Write); |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name) |
| { |
| HANDLE fout; |
| if(!name) |
| { |
| return 1; |
| } |
| |
| /* Close the existing inherited handle. */ |
| kwsysProcessCleanupHandle(phandle); |
| |
| /* Create a handle to write a file for the pipe. */ |
| fout = CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, 0, |
| CREATE_ALWAYS, 0, 0); |
| if(fout == INVALID_HANDLE_VALUE) |
| { |
| return 0; |
| } |
| |
| /* Create an inherited duplicate of the handle. This also closes |
| the non-inherited version. */ |
| if(!DuplicateHandle(GetCurrentProcess(), fout, |
| GetCurrentProcess(), &fout, |
| 0, TRUE, (DUPLICATE_CLOSE_SOURCE | |
| DUPLICATE_SAME_ACCESS))) |
| { |
| return 0; |
| } |
| |
| /* Assign the replacement handle. */ |
| *phandle = fout; |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle) |
| { |
| /* Check whether the handle to be shared is already inherited. */ |
| DWORD flags; |
| int inherited = 0; |
| if(GetHandleInformation(GetStdHandle(nStdHandle), &flags) && |
| (flags & HANDLE_FLAG_INHERIT)) |
| { |
| inherited = 1; |
| } |
| |
| /* Cleanup the previous handle. */ |
| kwsysProcessCleanupHandle(handle); |
| |
| /* If the standard handle is not inherited then duplicate it to |
| create an inherited copy. Do not close the original handle when |
| duplicating! */ |
| if(inherited) |
| { |
| *handle = GetStdHandle(nStdHandle); |
| return 1; |
| } |
| else if(DuplicateHandle(GetCurrentProcess(), GetStdHandle(nStdHandle), |
| GetCurrentProcess(), handle, |
| 0, TRUE, DUPLICATE_SAME_ACCESS)) |
| { |
| return 1; |
| } |
| else |
| { |
| /* The given standard handle is not valid for this process. Some |
| child processes may break if they do not have a valid standard |
| pipe, so give the child an empty pipe. For the stdin pipe we |
| want to close the write end and give the read end to the child. |
| For stdout and stderr we want to close the read end and give |
| the write end to the child. */ |
| int child_end = (nStdHandle == STD_INPUT_HANDLE)? 0:1; |
| int parent_end = (nStdHandle == STD_INPUT_HANDLE)? 1:0; |
| HANDLE emptyPipe[2]; |
| if(!CreatePipe(&emptyPipe[0], &emptyPipe[1], 0, 0)) |
| { |
| return 0; |
| } |
| |
| /* Close the non-inherited end so the pipe will be broken |
| immediately. */ |
| CloseHandle(emptyPipe[parent_end]); |
| |
| /* Create an inherited duplicate of the handle. This also |
| closes the non-inherited version. */ |
| if(!DuplicateHandle(GetCurrentProcess(), emptyPipe[child_end], |
| GetCurrentProcess(), &emptyPipe[child_end], |
| 0, TRUE, (DUPLICATE_CLOSE_SOURCE | |
| DUPLICATE_SAME_ACCESS))) |
| { |
| return 0; |
| } |
| |
| /* Give the inherited handle to the child. */ |
| *handle = emptyPipe[child_end]; |
| return 1; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| 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) |
| { |
| if(h && *h) |
| { |
| CloseHandle(*h); |
| *h = 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| /* Close the given handle if it is open and not a standard handle. |
| Reset its value to 0. */ |
| void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle) |
| { |
| if(h && *h && (*h != GetStdHandle(nStdHandle))) |
| { |
| CloseHandle(*h); |
| *h = 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| /* Close all handles created by kwsysProcess_Execute. */ |
| void kwsysProcessCleanup(kwsysProcess* cp, int error) |
| { |
| int i; |
| /* If this is an error case, report the error. */ |
| if(error) |
| { |
| /* Construct an error message if one has not been provided already. */ |
| if(cp->ErrorMessage[0] == 0) |
| { |
| /* Format the error message. */ |
| DWORD original = GetLastError(); |
| DWORD length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | |
| FORMAT_MESSAGE_IGNORE_INSERTS, 0, original, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE, 0); |
| if(length < 1) |
| { |
| /* FormatMessage failed. Use a default message. */ |
| _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE, |
| "Process execution failed with error 0x%X. " |
| "FormatMessage failed with error 0x%X", |
| original, GetLastError()); |
| } |
| } |
| |
| /* Remove trailing period and newline, if any. */ |
| kwsysProcessCleanErrorMessage(cp); |
| |
| /* Set the error state. */ |
| cp->State = kwsysProcess_State_Error; |
| |
| /* Cleanup any processes already started in a suspended state. */ |
| if(cp->ProcessInformation) |
| { |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| if(cp->ProcessInformation[i].hProcess) |
| { |
| TerminateProcess(cp->ProcessInformation[i].hProcess, 255); |
| WaitForSingleObject(cp->ProcessInformation[i].hProcess, INFINITE); |
| } |
| } |
| for(i=0; i < cp->NumberOfCommands; ++i) |
| { |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread); |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess); |
| } |
| } |
| |
| /* Restore the working directory. */ |
| if(cp->RealWorkingDirectory) |
| { |
| SetCurrentDirectory(cp->RealWorkingDirectory); |
| } |
| } |
| |
| /* Free memory. */ |
| if(cp->ProcessInformation) |
| { |
| free(cp->ProcessInformation); |
| cp->ProcessInformation = 0; |
| } |
| if(cp->ProcessEvents) |
| { |
| free(cp->ProcessEvents); |
| cp->ProcessEvents = 0; |
| } |
| if(cp->RealWorkingDirectory) |
| { |
| free(cp->RealWorkingDirectory); |
| cp->RealWorkingDirectory = 0; |
| } |
| |
| /* Close each pipe. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Write); |
| kwsysProcessCleanupHandle(&cp->Pipe[i].Read); |
| cp->Pipe[i].Closed = 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcessCleanErrorMessage(kwsysProcess* cp) |
| { |
| /* Remove trailing period and newline, if any. */ |
| size_t length = strlen(cp->ErrorMessage); |
| if(cp->ErrorMessage[length-1] == '\n') |
| { |
| cp->ErrorMessage[length-1] = 0; |
| --length; |
| if(length > 0 && cp->ErrorMessage[length-1] == '\r') |
| { |
| cp->ErrorMessage[length-1] = 0; |
| --length; |
| } |
| } |
| if(length > 0 && cp->ErrorMessage[length-1] == '.') |
| { |
| cp->ErrorMessage[length-1] = 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcessComputeCommandLength(kwsysProcess* cp, |
| char const* const* command) |
| { |
| int length = 0; |
| if(cp->Verbatim) |
| { |
| /* Treat the first argument as a verbatim command line. Use its |
| length directly and add space for the null-terminator. */ |
| length = (int)strlen(*command)+1; |
| } |
| else |
| { |
| /* Compute the length of the command line when it is converted to |
| a single string. Space for the null-terminator is allocated by |
| the whitespace character allocated for the first argument that |
| will not be used. */ |
| char const* const* arg; |
| for(arg = command; *arg; ++arg) |
| { |
| /* Add the length of this argument. It already includes room |
| for a separating space or terminating null. */ |
| length += kwsysSystem_Shell_GetArgumentSizeForWindows(*arg, 0); |
| } |
| } |
| |
| return length; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| void kwsysProcessComputeCommandLine(kwsysProcess* cp, |
| char const* const* command, |
| char* cmd) |
| { |
| if(cp->Verbatim) |
| { |
| /* Copy the verbatim command line into the buffer. */ |
| strcpy(cmd, *command); |
| } |
| else |
| { |
| /* Construct the command line in the allocated buffer. */ |
| char const* const* arg; |
| for(arg = command; *arg; ++arg) |
| { |
| /* Add the separating space if this is not the first argument. */ |
| if(arg != command) |
| { |
| *cmd++ = ' '; |
| } |
| |
| /* Add the current argument. */ |
| cmd = kwsysSystem_Shell_GetArgumentForWindows(*arg, cmd, 0); |
| } |
| |
| /* Add the terminating null character to the command line. */ |
| *cmd = 0; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Get the time at which either the process or user timeout will |
| expire. Returns 1 if the user timeout is first, and 0 otherwise. */ |
| int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, |
| kwsysProcessTime* timeoutTime) |
| { |
| /* The first time this is called, we need to calculate the time at |
| which the child will timeout. */ |
| if(cp->Timeout && cp->TimeoutTime.QuadPart < 0) |
| { |
| kwsysProcessTime length = kwsysProcessTimeFromDouble(cp->Timeout); |
| cp->TimeoutTime = kwsysProcessTimeAdd(cp->StartTime, length); |
| } |
| |
| /* Start with process timeout. */ |
| *timeoutTime = cp->TimeoutTime; |
| |
| /* Check if the user timeout is earlier. */ |
| if(userTimeout) |
| { |
| kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); |
| kwsysProcessTime userTimeoutLength = kwsysProcessTimeFromDouble(*userTimeout); |
| kwsysProcessTime userTimeoutTime = kwsysProcessTimeAdd(currentTime, |
| userTimeoutLength); |
| if(timeoutTime->QuadPart < 0 || |
| kwsysProcessTimeLess(userTimeoutTime, *timeoutTime)) |
| { |
| *timeoutTime = userTimeoutTime; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Get the length of time before the given timeout time arrives. |
| Returns 1 if the time has already arrived, and 0 otherwise. */ |
| int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, |
| double* userTimeout, |
| kwsysProcessTime* timeoutLength) |
| { |
| if(timeoutTime->QuadPart < 0) |
| { |
| /* No timeout time has been requested. */ |
| return 0; |
| } |
| else |
| { |
| /* Calculate the remaining time. */ |
| kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); |
| *timeoutLength = kwsysProcessTimeSubtract(*timeoutTime, currentTime); |
| |
| if(timeoutLength->QuadPart < 0 && userTimeout && *userTimeout <= 0) |
| { |
| /* Caller has explicitly requested a zero timeout. */ |
| timeoutLength->QuadPart = 0; |
| } |
| |
| if(timeoutLength->QuadPart < 0) |
| { |
| /* Timeout has already expired. */ |
| return 1; |
| } |
| else |
| { |
| /* There is some time left. */ |
| return 0; |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| kwsysProcessTime kwsysProcessTimeGetCurrent() |
| { |
| kwsysProcessTime current; |
| FILETIME ft; |
| GetSystemTimeAsFileTime(&ft); |
| current.LowPart = ft.dwLowDateTime; |
| current.HighPart = ft.dwHighDateTime; |
| return current; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t) |
| { |
| return (DWORD)(t.QuadPart * 0.0001); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| double kwsysProcessTimeToDouble(kwsysProcessTime t) |
| { |
| return t.QuadPart * 0.0000001; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| kwsysProcessTime kwsysProcessTimeFromDouble(double d) |
| { |
| kwsysProcessTime t; |
| t.QuadPart = (LONGLONG)(d*10000000); |
| return t; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2) |
| { |
| return in1.QuadPart < in2.QuadPart; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2) |
| { |
| kwsysProcessTime out; |
| out.QuadPart = in1.QuadPart + in2.QuadPart; |
| return out; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2) |
| { |
| kwsysProcessTime out; |
| out.QuadPart = in1.QuadPart - in2.QuadPart; |
| return out; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| #define KWSYSPE_CASE(type, str) \ |
| cp->ExitException = kwsysProcess_Exception_##type; \ |
| strcpy(cp->ExitExceptionString, str) |
| static void kwsysProcessSetExitException(kwsysProcess* cp, int code) |
| { |
| switch (code) |
| { |
| case STATUS_CONTROL_C_EXIT: |
| KWSYSPE_CASE(Interrupt, "User interrupt"); break; |
| |
| case STATUS_FLOAT_DENORMAL_OPERAND: |
| KWSYSPE_CASE(Numerical, "Floating-point exception (denormal operand)"); break; |
| case STATUS_FLOAT_DIVIDE_BY_ZERO: |
| KWSYSPE_CASE(Numerical, "Divide-by-zero"); break; |
| case STATUS_FLOAT_INEXACT_RESULT: |
| KWSYSPE_CASE(Numerical, "Floating-point exception (inexact result)"); break; |
| case STATUS_FLOAT_INVALID_OPERATION: |
| KWSYSPE_CASE(Numerical, "Invalid floating-point operation"); break; |
| case STATUS_FLOAT_OVERFLOW: |
| KWSYSPE_CASE(Numerical, "Floating-point overflow"); break; |
| case STATUS_FLOAT_STACK_CHECK: |
| KWSYSPE_CASE(Numerical, "Floating-point stack check failed"); break; |
| case STATUS_FLOAT_UNDERFLOW: |
| KWSYSPE_CASE(Numerical, "Floating-point underflow"); break; |
| #ifdef STATUS_FLOAT_MULTIPLE_FAULTS |
| case STATUS_FLOAT_MULTIPLE_FAULTS: |
| KWSYSPE_CASE(Numerical, "Floating-point exception (multiple faults)"); break; |
| #endif |
| #ifdef STATUS_FLOAT_MULTIPLE_TRAPS |
| case STATUS_FLOAT_MULTIPLE_TRAPS: |
| KWSYSPE_CASE(Numerical, "Floating-point exception (multiple traps)"); break; |
| #endif |
| case STATUS_INTEGER_DIVIDE_BY_ZERO: |
| KWSYSPE_CASE(Numerical, "Integer divide-by-zero"); break; |
| case STATUS_INTEGER_OVERFLOW: |
| KWSYSPE_CASE(Numerical, "Integer overflow"); break; |
| |
| case STATUS_DATATYPE_MISALIGNMENT: |
| KWSYSPE_CASE(Fault, "Datatype misalignment"); break; |
| case STATUS_ACCESS_VIOLATION: |
| KWSYSPE_CASE(Fault, "Access violation"); break; |
| case STATUS_IN_PAGE_ERROR: |
| KWSYSPE_CASE(Fault, "In-page error"); break; |
| case STATUS_INVALID_HANDLE: |
| KWSYSPE_CASE(Fault, "Invalid hanlde"); break; |
| case STATUS_NONCONTINUABLE_EXCEPTION: |
| KWSYSPE_CASE(Fault, "Noncontinuable exception"); break; |
| case STATUS_INVALID_DISPOSITION: |
| KWSYSPE_CASE(Fault, "Invalid disposition"); break; |
| case STATUS_ARRAY_BOUNDS_EXCEEDED: |
| KWSYSPE_CASE(Fault, "Array bounds exceeded"); break; |
| case STATUS_STACK_OVERFLOW: |
| KWSYSPE_CASE(Fault, "Stack overflow"); break; |
| |
| case STATUS_ILLEGAL_INSTRUCTION: |
| KWSYSPE_CASE(Illegal, "Illegal instruction"); break; |
| case STATUS_PRIVILEGED_INSTRUCTION: |
| KWSYSPE_CASE(Illegal, "Privileged instruction"); break; |
| |
| case STATUS_NO_MEMORY: |
| default: |
| cp->ExitException = kwsysProcess_Exception_Other; |
| sprintf(cp->ExitExceptionString, "Exit code 0x%x\n", code); |
| break; |
| } |
| } |
| #undef KWSYSPE_CASE |
| |
| typedef struct kwsysProcess_List_s kwsysProcess_List; |
| static kwsysProcess_List* kwsysProcess_List_New(void); |
| static void kwsysProcess_List_Delete(kwsysProcess_List* self); |
| static int kwsysProcess_List_Update(kwsysProcess_List* self); |
| static int kwsysProcess_List_NextProcess(kwsysProcess_List* self); |
| static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self); |
| static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self); |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Windows NT 4 API definitions. */ |
| #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) |
| typedef LONG NTSTATUS; |
| typedef LONG KPRIORITY; |
| typedef struct _UNICODE_STRING UNICODE_STRING; |
| struct _UNICODE_STRING |
| { |
| USHORT Length; |
| USHORT MaximumLength; |
| PWSTR Buffer; |
| }; |
| |
| /* The process information structure. Declare only enough to get |
| process identifiers. The rest may be ignored because we use the |
| NextEntryDelta to move through an array of instances. */ |
| typedef struct _SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION; |
| typedef SYSTEM_PROCESS_INFORMATION* PSYSTEM_PROCESS_INFORMATION; |
| struct _SYSTEM_PROCESS_INFORMATION |
| { |
| ULONG NextEntryDelta; |
| ULONG ThreadCount; |
| ULONG Reserved1[6]; |
| LARGE_INTEGER CreateTime; |
| LARGE_INTEGER UserTime; |
| LARGE_INTEGER KernelTime; |
| UNICODE_STRING ProcessName; |
| KPRIORITY BasePriority; |
| ULONG ProcessId; |
| ULONG InheritedFromProcessId; |
| }; |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Toolhelp32 API definitions. */ |
| #define TH32CS_SNAPPROCESS 0x00000002 |
| #if defined(_WIN64) |
| typedef unsigned __int64 ProcessULONG_PTR; |
| #else |
| typedef unsigned long ProcessULONG_PTR; |
| #endif |
| typedef struct tagPROCESSENTRY32 PROCESSENTRY32; |
| typedef PROCESSENTRY32* LPPROCESSENTRY32; |
| struct tagPROCESSENTRY32 |
| { |
| DWORD dwSize; |
| DWORD cntUsage; |
| DWORD th32ProcessID; |
| ProcessULONG_PTR th32DefaultHeapID; |
| DWORD th32ModuleID; |
| DWORD cntThreads; |
| DWORD th32ParentProcessID; |
| LONG pcPriClassBase; |
| DWORD dwFlags; |
| char szExeFile[MAX_PATH]; |
| }; |
| |
| /*--------------------------------------------------------------------------*/ |
| /* Windows API function types. */ |
| typedef HANDLE (WINAPI* CreateToolhelp32SnapshotType)(DWORD, DWORD); |
| typedef BOOL (WINAPI* Process32FirstType)(HANDLE, LPPROCESSENTRY32); |
| typedef BOOL (WINAPI* Process32NextType)(HANDLE, LPPROCESSENTRY32); |
| typedef NTSTATUS (WINAPI* ZwQuerySystemInformationType)(ULONG, PVOID, |
| ULONG, PULONG); |
| |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__New_NT4(kwsysProcess_List* self); |
| static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self); |
| static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self); |
| static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self); |
| static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self); |
| static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self); |
| static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self); |
| static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self); |
| static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self); |
| static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self); |
| static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self); |
| static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self); |
| |
| struct kwsysProcess_List_s |
| { |
| /* Implementation switches at runtime based on version of Windows. */ |
| int NT4; |
| |
| /* Implementation functions and data for NT 4. */ |
| ZwQuerySystemInformationType P_ZwQuerySystemInformation; |
| char* Buffer; |
| int BufferSize; |
| PSYSTEM_PROCESS_INFORMATION CurrentInfo; |
| |
| /* Implementation functions and data for other Windows versions. */ |
| CreateToolhelp32SnapshotType P_CreateToolhelp32Snapshot; |
| Process32FirstType P_Process32First; |
| Process32NextType P_Process32Next; |
| HANDLE Snapshot; |
| PROCESSENTRY32 CurrentEntry; |
| }; |
| |
| /*--------------------------------------------------------------------------*/ |
| static kwsysProcess_List* kwsysProcess_List_New(void) |
| { |
| OSVERSIONINFO osv; |
| kwsysProcess_List* self; |
| |
| /* Allocate and initialize the list object. */ |
| if(!(self = (kwsysProcess_List*)malloc(sizeof(kwsysProcess_List)))) |
| { |
| return 0; |
| } |
| memset(self, 0, sizeof(*self)); |
| |
| /* Select an implementation. */ |
| ZeroMemory(&osv, sizeof(osv)); |
| osv.dwOSVersionInfoSize = sizeof(osv); |
| GetVersionEx(&osv); |
| self->NT4 = (osv.dwPlatformId == VER_PLATFORM_WIN32_NT && |
| osv.dwMajorVersion < 5)? 1:0; |
| |
| /* Initialize the selected implementation. */ |
| if(!(self->NT4? |
| kwsysProcess_List__New_NT4(self) : |
| kwsysProcess_List__New_Snapshot(self))) |
| { |
| kwsysProcess_List_Delete(self); |
| return 0; |
| } |
| |
| /* Update to the current set of processes. */ |
| if(!kwsysProcess_List_Update(self)) |
| { |
| kwsysProcess_List_Delete(self); |
| return 0; |
| } |
| return self; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcess_List_Delete(kwsysProcess_List* self) |
| { |
| if(self) |
| { |
| if(self->NT4) |
| { |
| kwsysProcess_List__Delete_NT4(self); |
| } |
| else |
| { |
| kwsysProcess_List__Delete_Snapshot(self); |
| } |
| free(self); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List_Update(kwsysProcess_List* self) |
| { |
| return self? (self->NT4? |
| kwsysProcess_List__Update_NT4(self) : |
| kwsysProcess_List__Update_Snapshot(self)) : 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self) |
| { |
| return self? (self->NT4? |
| kwsysProcess_List__GetProcessId_NT4(self) : |
| kwsysProcess_List__GetProcessId_Snapshot(self)) : -1; |
| |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self) |
| { |
| return self? (self->NT4? |
| kwsysProcess_List__GetParentId_NT4(self) : |
| kwsysProcess_List__GetParentId_Snapshot(self)) : -1; |
| |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List_NextProcess(kwsysProcess_List* self) |
| { |
| return (self? (self->NT4? |
| kwsysProcess_List__Next_NT4(self) : |
| kwsysProcess_List__Next_Snapshot(self)) : 0); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__New_NT4(kwsysProcess_List* self) |
| { |
| /* Get a handle to the NT runtime module that should already be |
| loaded in this program. This does not actually increment the |
| reference count to the module so we do not need to close the |
| handle. */ |
| HMODULE hNT = GetModuleHandle("ntdll.dll"); |
| if(hNT) |
| { |
| /* Get pointers to the needed API functions. */ |
| self->P_ZwQuerySystemInformation = |
| ((ZwQuerySystemInformationType) |
| GetProcAddress(hNT, "ZwQuerySystemInformation")); |
| } |
| if(!self->P_ZwQuerySystemInformation) |
| { |
| return 0; |
| } |
| |
| /* Allocate an initial process information buffer. */ |
| self->BufferSize = 32768; |
| self->Buffer = (char*)malloc(self->BufferSize); |
| return self->Buffer? 1:0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self) |
| { |
| /* Free the process information buffer. */ |
| if(self->Buffer) |
| { |
| free(self->Buffer); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self) |
| { |
| self->CurrentInfo = 0; |
| for(;;) |
| { |
| /* Query number 5 is for system process list. */ |
| NTSTATUS status = |
| self->P_ZwQuerySystemInformation(5, self->Buffer, self->BufferSize, 0); |
| if(status == STATUS_INFO_LENGTH_MISMATCH) |
| { |
| /* The query requires a bigger buffer. */ |
| int newBufferSize = self->BufferSize * 2; |
| char* newBuffer = (char*)malloc(newBufferSize); |
| if(newBuffer) |
| { |
| free(self->Buffer); |
| self->Buffer = newBuffer; |
| self->BufferSize = newBufferSize; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| else if(status >= 0) |
| { |
| /* The query succeeded. Initialize traversal of the process list. */ |
| self->CurrentInfo = (PSYSTEM_PROCESS_INFORMATION)self->Buffer; |
| return 1; |
| } |
| else |
| { |
| /* The query failed. */ |
| return 0; |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self) |
| { |
| if(self->CurrentInfo) |
| { |
| if(self->CurrentInfo->NextEntryDelta > 0) |
| { |
| self->CurrentInfo = ((PSYSTEM_PROCESS_INFORMATION) |
| ((char*)self->CurrentInfo + |
| self->CurrentInfo->NextEntryDelta)); |
| return 1; |
| } |
| self->CurrentInfo = 0; |
| } |
| return 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self) |
| { |
| return self->CurrentInfo? self->CurrentInfo->ProcessId : -1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self) |
| { |
| return self->CurrentInfo? self->CurrentInfo->InheritedFromProcessId : -1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self) |
| { |
| /* Get a handle to the Windows runtime module that should already be |
| loaded in this program. This does not actually increment the |
| reference count to the module so we do not need to close the |
| handle. */ |
| HMODULE hKernel = GetModuleHandle("kernel32.dll"); |
| if(hKernel) |
| { |
| self->P_CreateToolhelp32Snapshot = |
| ((CreateToolhelp32SnapshotType) |
| GetProcAddress(hKernel, "CreateToolhelp32Snapshot")); |
| self->P_Process32First = |
| ((Process32FirstType) |
| GetProcAddress(hKernel, "Process32First")); |
| self->P_Process32Next = |
| ((Process32NextType) |
| GetProcAddress(hKernel, "Process32Next")); |
| } |
| return (self->P_CreateToolhelp32Snapshot && |
| self->P_Process32First && |
| self->P_Process32Next)? 1:0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self) |
| { |
| if(self->Snapshot) |
| { |
| CloseHandle(self->Snapshot); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self) |
| { |
| if(self->Snapshot) |
| { |
| CloseHandle(self->Snapshot); |
| } |
| if(!(self->Snapshot = |
| self->P_CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0))) |
| { |
| return 0; |
| } |
| ZeroMemory(&self->CurrentEntry, sizeof(self->CurrentEntry)); |
| self->CurrentEntry.dwSize = sizeof(self->CurrentEntry); |
| if(!self->P_Process32First(self->Snapshot, &self->CurrentEntry)) |
| { |
| CloseHandle(self->Snapshot); |
| self->Snapshot = 0; |
| return 0; |
| } |
| return 1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self) |
| { |
| if(self->Snapshot) |
| { |
| if(self->P_Process32Next(self->Snapshot, &self->CurrentEntry)) |
| { |
| return 1; |
| } |
| CloseHandle(self->Snapshot); |
| self->Snapshot = 0; |
| } |
| return 0; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self) |
| { |
| return self->Snapshot? self->CurrentEntry.th32ProcessID : -1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self) |
| { |
| return self->Snapshot? self->CurrentEntry.th32ParentProcessID : -1; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessKill(DWORD pid) |
| { |
| HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, pid); |
| if(h) |
| { |
| TerminateProcess(h, 255); |
| WaitForSingleObject(h, INFINITE); |
| CloseHandle(h); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessKillTree(int pid) |
| { |
| kwsysProcess_List* plist = kwsysProcess_List_New(); |
| kwsysProcessKill(pid); |
| if(plist) |
| { |
| do |
| { |
| if(kwsysProcess_List_GetCurrentParentId(plist) == pid) |
| { |
| int ppid = kwsysProcess_List_GetCurrentProcessId(plist); |
| kwsysProcessKillTree(ppid); |
| } |
| } while(kwsysProcess_List_NextProcess(plist)); |
| kwsysProcess_List_Delete(plist); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| static void kwsysProcessDisablePipeThreads(kwsysProcess* cp) |
| { |
| int i; |
| |
| /* If data were just reported data, release the pipe's thread. */ |
| if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT) |
| { |
| KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex)); |
| ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); |
| cp->CurrentIndex = KWSYSPE_PIPE_COUNT; |
| } |
| |
| /* Wakeup all reading threads that are not on closed pipes. */ |
| for(i=0; i < KWSYSPE_PIPE_COUNT; ++i) |
| { |
| /* The wakeup threads will write one byte to the pipe write ends. |
| If there are no data in the pipe then this is enough to wakeup |
| the reading threads. If there are already data in the pipe |
| this may block. We cannot use PeekNamedPipe to check whether |
| there are data because an outside process might still be |
| writing data if we are disowning it. Also, PeekNamedPipe will |
| block if checking a pipe on which the reading thread is |
| currently calling ReadPipe. Therefore we need a separate |
| thread to call WriteFile. If it blocks, that is okay because |
| it will unblock when we close the read end and break the pipe |
| below. */ |
| if(cp->Pipe[i].Read) |
| { |
| KWSYSPE_DEBUG((stderr, "releasing waker %d\n", i)); |
| ReleaseSemaphore(cp->Pipe[i].Waker.Go, 1, 0); |
| } |
| } |
| |
| /* Tell pipe threads to reset until we run another process. */ |
| while(cp->PipesLeft > 0) |
| { |
| /* The waking threads will cause all reading threads to report. |
| Wait for the next one and save its index. */ |
| KWSYSPE_DEBUG((stderr, "waiting for reader\n")); |
| WaitForSingleObject(cp->Full, INFINITE); |
| cp->CurrentIndex = cp->SharedIndex; |
| ReleaseSemaphore(cp->SharedIndexMutex, 1, 0); |
| KWSYSPE_DEBUG((stderr, "got reader %d\n", cp->CurrentIndex)); |
| |
| /* We are done reading this pipe. Close its read handle. */ |
| cp->Pipe[cp->CurrentIndex].Closed = 1; |
| kwsysProcessCleanupHandle(&cp->Pipe[cp->CurrentIndex].Read); |
| --cp->PipesLeft; |
| |
| /* Tell the reading thread we are done with the data. It will |
| reset immediately because the pipe is closed. */ |
| ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); |
| } |
| } |