| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ | |
| #include "kwsysPrivate.h" | |
| #include KWSYS_HEADER(Process.h) | |
| #include KWSYS_HEADER(Encoding.h) | |
| /* Work-around CMake dependency scanning limitation. This must | |
| duplicate the above list of headers. */ | |
| #if 0 | |
| # include "Encoding.h.in" | |
| # include "Process.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 */ | |
| #if defined(_MSC_VER) && _MSC_VER >= 1800 | |
| # define KWSYS_WINDOWS_DEPRECATED_GetVersionEx | |
| #endif | |
| #include <io.h> /* _unlink */ | |
| #include <stdio.h> /* snprintf */ | |
| #include <stdlib.h> /* malloc */ | |
| #include <string.h> /* strlen, strdup */ | |
| #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 | |
| /* 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. */ | |
| STARTUPINFOW StartupInfo; | |
| /* Original handles before making inherited duplicates. */ | |
| HANDLE hStdInput; | |
| HANDLE hStdOutput; | |
| HANDLE hStdError; | |
| } 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 DWORD kwsysProcessCreate(kwsysProcess* cp, int index, | |
| kwsysProcessCreateInformation* si); | |
| static void kwsysProcessDestroy(kwsysProcess* cp, int event); | |
| static DWORD kwsysProcessSetupOutputPipeFile(PHANDLE handle, char const* name); | |
| static void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle); | |
| static void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle); | |
| static void kwsysProcessCleanupHandle(PHANDLE h); | |
| static void kwsysProcessCleanup(kwsysProcess* cp, DWORD error); | |
| static void kwsysProcessCleanErrorMessage(kwsysProcess* cp); | |
| 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 kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int code, | |
| int idx); | |
| static void kwsysProcessKillTree(int pid); | |
| static void kwsysProcessDisablePipeThreads(kwsysProcess* cp); | |
| static int kwsysProcessesInitialize(void); | |
| static int kwsysTryEnterCreateProcessSection(void); | |
| static void kwsysLeaveCreateProcessSection(void); | |
| static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessId, | |
| int newProcessGroup); | |
| static void kwsysProcessesRemove(HANDLE hProcess); | |
| static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType); | |
| /* 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; | |
| }; | |
| /* A structure containing results data for each process. */ | |
| typedef struct kwsysProcessResults_s kwsysProcessResults; | |
| struct kwsysProcessResults_s | |
| { | |
| /* The status of the process. */ | |
| int State; | |
| /* 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; | |
| /* Description for the ExitException. */ | |
| char ExitExceptionString[KWSYSPE_PIPE_BUFFER_SIZE + 1]; | |
| }; | |
| /* 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. */ | |
| wchar_t** Commands; | |
| int NumberOfCommands; | |
| /* The exit code of each command. */ | |
| DWORD* CommandExitCodes; | |
| /* The working directory for the child process. */ | |
| wchar_t* 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; | |
| /* Whether to merge stdout/stderr of the child. */ | |
| int MergeOutput; | |
| /* Whether to create the process in a new process group. */ | |
| int CreateProcessGroup; | |
| /* 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 ------------- */ | |
| /* 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]; | |
| /* process results. */ | |
| kwsysProcessResults* ProcessResults; | |
| /* 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; | |
| wchar_t* RealWorkingDirectory; | |
| /* Own handles for the child's ends of the pipes in the parent process. | |
| Used temporarily during process creation. */ | |
| HANDLE PipeChildStd[3]; | |
| }; | |
| kwsysProcess* kwsysProcess_New(void) | |
| { | |
| int i; | |
| /* Process control structure. */ | |
| kwsysProcess* cp; | |
| /* Windows version number data. */ | |
| OSVERSIONINFO osv; | |
| /* Initialize list of processes before we get any farther. It's especially | |
| important that the console Ctrl handler be added BEFORE starting the | |
| first process. This prevents the risk of an orphaned process being | |
| started by the main thread while the default Ctrl handler is in | |
| progress. */ | |
| if (!kwsysProcessesInitialize()) { | |
| return 0; | |
| } | |
| /* 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); | |
| #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx | |
| # pragma warning(push) | |
| # ifdef __INTEL_COMPILER | |
| # pragma warning(disable : 1478) | |
| # elif defined __clang__ | |
| # pragma clang diagnostic push | |
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| # else | |
| # pragma warning(disable : 4996) | |
| # endif | |
| #endif | |
| GetVersionEx(&osv); | |
| #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx | |
| # ifdef __clang__ | |
| # pragma clang diagnostic pop | |
| # else | |
| # pragma warning(pop) | |
| # endif | |
| #endif | |
| 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; | |
| } | |
| } | |
| for (i = 0; i < 3; ++i) { | |
| cp->PipeChildStd[i] = INVALID_HANDLE_VALUE; | |
| } | |
| 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); | |
| free(cp->CommandExitCodes); | |
| free(cp->ProcessResults); | |
| 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; | |
| wchar_t** 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 = | |
| (wchar_t**)malloc(sizeof(wchar_t*) * 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]; | |
| } | |
| } | |
| if (cp->Verbatim) { | |
| /* Copy the verbatim command line into the buffer. */ | |
| newCommands[cp->NumberOfCommands] = kwsysEncoding_DupToWide(*command); | |
| } else { | |
| /* Encode the arguments so CommandLineToArgvW can decode | |
| them from the command line string in the child. */ | |
| char buffer[32768]; /* CreateProcess max command-line length. */ | |
| char* end = buffer + sizeof(buffer); | |
| char* out = buffer; | |
| char const* const* a; | |
| for (a = command; *a; ++a) { | |
| int quote = !**a; /* Quote the empty string. */ | |
| int slashes = 0; | |
| char const* c; | |
| if (a != command && out != end) { | |
| *out++ = ' '; | |
| } | |
| for (c = *a; !quote && *c; ++c) { | |
| quote = (*c == ' ' || *c == '\t'); | |
| } | |
| if (quote && out != end) { | |
| *out++ = '"'; | |
| } | |
| for (c = *a; *c; ++c) { | |
| if (*c == '\\') { | |
| ++slashes; | |
| } else { | |
| if (*c == '"') { | |
| // Add n+1 backslashes to total 2n+1 before internal '"'. | |
| while (slashes-- >= 0 && out != end) { | |
| *out++ = '\\'; | |
| } | |
| } | |
| slashes = 0; | |
| } | |
| if (out != end) { | |
| *out++ = *c; | |
| } | |
| } | |
| if (quote) { | |
| // Add n backslashes to total 2n before ending '"'. | |
| while (slashes-- > 0 && out != end) { | |
| *out++ = '\\'; | |
| } | |
| if (out != end) { | |
| *out++ = '"'; | |
| } | |
| } | |
| } | |
| if (out != end) { | |
| *out = '\0'; | |
| newCommands[cp->NumberOfCommands] = kwsysEncoding_DupToWide(buffer); | |
| } else { | |
| newCommands[cp->NumberOfCommands] = 0; | |
| } | |
| } | |
| if (!newCommands[cp->NumberOfCommands]) { | |
| /* Out of memory or command line too long. */ | |
| free(newCommands); | |
| return 0; | |
| } | |
| /* 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; | |
| } | |
| // Force recomputation of TimeoutTime. | |
| cp->TimeoutTime.QuadPart = -1; | |
| } | |
| int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, char const* dir) | |
| { | |
| if (!cp) { | |
| return 0; | |
| } | |
| if (cp->WorkingDirectory) { | |
| free(cp->WorkingDirectory); | |
| cp->WorkingDirectory = 0; | |
| } | |
| if (dir && dir[0]) { | |
| wchar_t* wdir = kwsysEncoding_DupToWide(dir); | |
| /* We must convert the working directory to a full path. */ | |
| DWORD length = GetFullPathNameW(wdir, 0, NULL, NULL); | |
| if (length > 0) { | |
| wchar_t* work_dir = malloc(length * sizeof(wchar_t)); | |
| if (!work_dir) { | |
| free(wdir); | |
| return 0; | |
| } | |
| if (!GetFullPathNameW(wdir, length, work_dir, NULL)) { | |
| free(work_dir); | |
| free(wdir); | |
| return 0; | |
| } | |
| cp->WorkingDirectory = work_dir; | |
| } | |
| free(wdir); | |
| } | |
| return 1; | |
| } | |
| int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, char const* 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 = strdup(file); | |
| if (!*pfile) { | |
| return 0; | |
| } | |
| } | |
| /* 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, const 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_MergeOutput: | |
| return cp->MergeOutput; | |
| case kwsysProcess_Option_Verbatim: | |
| return cp->Verbatim; | |
| case kwsysProcess_Option_CreateProcessGroup: | |
| return cp->CreateProcessGroup; | |
| 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_MergeOutput: | |
| cp->MergeOutput = value; | |
| break; | |
| case kwsysProcess_Option_Verbatim: | |
| cp->Verbatim = value; | |
| break; | |
| case kwsysProcess_Option_CreateProcessGroup: | |
| cp->CreateProcessGroup = value; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| int kwsysProcess_GetState(kwsysProcess* cp) | |
| { | |
| return cp ? cp->State : kwsysProcess_State_Error; | |
| } | |
| int kwsysProcess_GetExitException(kwsysProcess* cp) | |
| { | |
| return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) | |
| ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitException | |
| : kwsysProcess_Exception_Other; | |
| } | |
| int kwsysProcess_GetExitValue(kwsysProcess* cp) | |
| { | |
| return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) | |
| ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitValue | |
| : -1; | |
| } | |
| int kwsysProcess_GetExitCode(kwsysProcess* cp) | |
| { | |
| return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) | |
| ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitCode | |
| : 0; | |
| } | |
| char const* 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"; | |
| } | |
| char const* kwsysProcess_GetExceptionString(kwsysProcess* cp) | |
| { | |
| if (!(cp && cp->ProcessResults && (cp->NumberOfCommands > 0))) { | |
| return "GetExceptionString called with NULL process management structure"; | |
| } else if (cp->State == kwsysProcess_State_Exception) { | |
| return cp->ProcessResults[cp->NumberOfCommands - 1].ExitExceptionString; | |
| } | |
| return "No exception"; | |
| } | |
| /* the index should be in array bound. */ | |
| #define KWSYSPE_IDX_CHK(RET) \ | |
| if (!cp || idx >= cp->NumberOfCommands || idx < 0) { \ | |
| KWSYSPE_DEBUG((stderr, "array index out of bound\n")); \ | |
| return RET; \ | |
| } | |
| int kwsysProcess_GetStateByIndex(kwsysProcess* cp, int idx) | |
| { | |
| KWSYSPE_IDX_CHK(kwsysProcess_State_Error) | |
| return cp->ProcessResults[idx].State; | |
| } | |
| int kwsysProcess_GetExitExceptionByIndex(kwsysProcess* cp, int idx) | |
| { | |
| KWSYSPE_IDX_CHK(kwsysProcess_Exception_Other) | |
| return cp->ProcessResults[idx].ExitException; | |
| } | |
| int kwsysProcess_GetExitValueByIndex(kwsysProcess* cp, int idx) | |
| { | |
| KWSYSPE_IDX_CHK(-1) | |
| return cp->ProcessResults[idx].ExitValue; | |
| } | |
| int kwsysProcess_GetExitCodeByIndex(kwsysProcess* cp, int idx) | |
| { | |
| KWSYSPE_IDX_CHK(-1) | |
| return cp->CommandExitCodes[idx]; | |
| } | |
| char const* kwsysProcess_GetExceptionStringByIndex(kwsysProcess* cp, int idx) | |
| { | |
| KWSYSPE_IDX_CHK("GetExceptionString called with NULL process management " | |
| "structure or index out of bound") | |
| if (cp->ProcessResults[idx].State == kwsysProcess_StateByIndex_Exception) { | |
| return cp->ProcessResults[idx].ExitExceptionString; | |
| } | |
| return "No exception"; | |
| } | |
| #undef KWSYSPE_IDX_CHK | |
| void kwsysProcess_Execute(kwsysProcess* cp) | |
| { | |
| int i; | |
| /* 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 (!GetCurrentDirectoryW(cp->RealWorkingDirectoryLength, | |
| cp->RealWorkingDirectory)) { | |
| kwsysProcessCleanup(cp, GetLastError()); | |
| return; | |
| } | |
| if (!SetCurrentDirectoryW(cp->WorkingDirectory)) { | |
| kwsysProcessCleanup(cp, GetLastError()); | |
| return; | |
| } | |
| } | |
| /* Setup the stdin pipe for the first process. */ | |
| if (cp->PipeFileSTDIN) { | |
| /* Create a handle to read a file for stdin. */ | |
| wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN); | |
| DWORD error; | |
| cp->PipeChildStd[0] = | |
| CreateFileW(wstdin, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, | |
| OPEN_EXISTING, 0, 0); | |
| error = GetLastError(); /* Check now in case free changes this. */ | |
| free(wstdin); | |
| if (cp->PipeChildStd[0] == INVALID_HANDLE_VALUE) { | |
| kwsysProcessCleanup(cp, error); | |
| return; | |
| } | |
| } else if (cp->PipeSharedSTDIN) { | |
| /* Share this process's stdin with the child. */ | |
| kwsysProcessSetupSharedPipe(STD_INPUT_HANDLE, &cp->PipeChildStd[0]); | |
| } else if (cp->PipeNativeSTDIN[0]) { | |
| /* Use the provided native pipe. */ | |
| kwsysProcessSetupPipeNative(cp->PipeNativeSTDIN[0], &cp->PipeChildStd[0]); | |
| } else { | |
| /* Explicitly give the child no stdin. */ | |
| cp->PipeChildStd[0] = INVALID_HANDLE_VALUE; | |
| } | |
| /* Create the output pipe for the last process. | |
| We always create this so the pipe thread can run even if we | |
| do not end up giving the write end to the child below. */ | |
| if (!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDOUT].Read, | |
| &cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, 0, 0)) { | |
| kwsysProcessCleanup(cp, GetLastError()); | |
| return; | |
| } | |
| if (cp->PipeFileSTDOUT) { | |
| /* Use a file for stdout. */ | |
| DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1], | |
| cp->PipeFileSTDOUT); | |
| if (error) { | |
| kwsysProcessCleanup(cp, error); | |
| return; | |
| } | |
| } else if (cp->PipeSharedSTDOUT) { | |
| /* Use the parent stdout. */ | |
| kwsysProcessSetupSharedPipe(STD_OUTPUT_HANDLE, &cp->PipeChildStd[1]); | |
| } else if (cp->PipeNativeSTDOUT[1]) { | |
| /* Use the given handle for stdout. */ | |
| kwsysProcessSetupPipeNative(cp->PipeNativeSTDOUT[1], &cp->PipeChildStd[1]); | |
| } else { | |
| /* Use our pipe for stdout. Duplicate the handle since our waker | |
| thread will use the original. Do not make it inherited yet. */ | |
| if (!DuplicateHandle(GetCurrentProcess(), | |
| cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, | |
| GetCurrentProcess(), &cp->PipeChildStd[1], 0, FALSE, | |
| DUPLICATE_SAME_ACCESS)) { | |
| kwsysProcessCleanup(cp, GetLastError()); | |
| return; | |
| } | |
| } | |
| /* Create stderr pipe to be shared by all processes in the pipeline. | |
| We always create this so the pipe thread can run even if we do not | |
| end up giving the write end to the child below. */ | |
| if (!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read, | |
| &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0)) { | |
| kwsysProcessCleanup(cp, GetLastError()); | |
| return; | |
| } | |
| if (cp->PipeFileSTDERR) { | |
| /* Use a file for stderr. */ | |
| DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2], | |
| cp->PipeFileSTDERR); | |
| if (error) { | |
| kwsysProcessCleanup(cp, error); | |
| return; | |
| } | |
| } else if (cp->PipeSharedSTDERR) { | |
| /* Use the parent stderr. */ | |
| kwsysProcessSetupSharedPipe(STD_ERROR_HANDLE, &cp->PipeChildStd[2]); | |
| } else if (cp->PipeNativeSTDERR[1]) { | |
| /* Use the given handle for stderr. */ | |
| kwsysProcessSetupPipeNative(cp->PipeNativeSTDERR[1], &cp->PipeChildStd[2]); | |
| } else { | |
| /* Use our pipe for stderr. Duplicate the handle since our waker | |
| thread will use the original. Do not make it inherited yet. */ | |
| if (!DuplicateHandle(GetCurrentProcess(), | |
| cp->Pipe[KWSYSPE_PIPE_STDERR].Write, | |
| GetCurrentProcess(), &cp->PipeChildStd[2], 0, FALSE, | |
| DUPLICATE_SAME_ACCESS)) { | |
| kwsysProcessCleanup(cp, GetLastError()); | |
| return; | |
| } | |
| } | |
| /* Create the pipeline of processes. */ | |
| { | |
| /* Child startup control data. */ | |
| kwsysProcessCreateInformation si; | |
| HANDLE nextStdInput = cp->PipeChildStd[0]; | |
| /* 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; | |
| for (i = 0; i < cp->NumberOfCommands; ++i) { | |
| /* Setup the process's pipes. */ | |
| si.hStdInput = nextStdInput; | |
| if (i == cp->NumberOfCommands - 1) { | |
| /* The last child gets the overall stdout. */ | |
| nextStdInput = INVALID_HANDLE_VALUE; | |
| si.hStdOutput = cp->PipeChildStd[1]; | |
| } else { | |
| /* Create a pipe to sit between the children. */ | |
| HANDLE p[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; | |
| if (!CreatePipe(&p[0], &p[1], 0, 0)) { | |
| DWORD error = GetLastError(); | |
| if (nextStdInput != cp->PipeChildStd[0]) { | |
| kwsysProcessCleanupHandle(&nextStdInput); | |
| } | |
| kwsysProcessCleanup(cp, error); | |
| return; | |
| } | |
| nextStdInput = p[0]; | |
| si.hStdOutput = p[1]; | |
| } | |
| si.hStdError = | |
| cp->MergeOutput ? cp->PipeChildStd[1] : cp->PipeChildStd[2]; | |
| { | |
| DWORD error = kwsysProcessCreate(cp, i, &si); | |
| /* Close our copies of pipes used between children. */ | |
| if (si.hStdInput != cp->PipeChildStd[0]) { | |
| kwsysProcessCleanupHandle(&si.hStdInput); | |
| } | |
| if (si.hStdOutput != cp->PipeChildStd[1]) { | |
| kwsysProcessCleanupHandle(&si.hStdOutput); | |
| } | |
| if (si.hStdError != cp->PipeChildStd[2] && !cp->MergeOutput) { | |
| kwsysProcessCleanupHandle(&si.hStdError); | |
| } | |
| if (!error) { | |
| cp->ProcessEvents[i + 1] = cp->ProcessInformation[i].hProcess; | |
| } else { | |
| if (nextStdInput != cp->PipeChildStd[0]) { | |
| kwsysProcessCleanupHandle(&nextStdInput); | |
| } | |
| kwsysProcessCleanup(cp, error); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| /* The parent process does not need the child's pipe ends. */ | |
| for (i = 0; i < 3; ++i) { | |
| kwsysProcessCleanupHandle(&cp->PipeChildStd[i]); | |
| } | |
| /* Restore the working directory. */ | |
| if (cp->RealWorkingDirectory) { | |
| SetCurrentDirectoryW(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 child processes. */ | |
| for (i = 0; i < cp->NumberOfCommands; ++i) { | |
| cp->ProcessResults[i].ExitCode = cp->CommandExitCodes[i]; | |
| if ((cp->ProcessResults[i].ExitCode & 0xF0000000) == 0xC0000000) { | |
| /* Child terminated due to exceptional behavior. */ | |
| cp->ProcessResults[i].State = kwsysProcess_StateByIndex_Exception; | |
| cp->ProcessResults[i].ExitValue = 1; | |
| kwsysProcessSetExitExceptionByIndex(cp, cp->ProcessResults[i].ExitCode, | |
| i); | |
| } else { | |
| /* Child exited without exception. */ | |
| cp->ProcessResults[i].State = kwsysProcess_StateByIndex_Exited; | |
| cp->ProcessResults[i].ExitException = kwsysProcess_Exception_None; | |
| cp->ProcessResults[i].ExitValue = cp->ProcessResults[i].ExitCode; | |
| } | |
| } | |
| /* support legacy state status value */ | |
| cp->State = cp->ProcessResults[cp->NumberOfCommands - 1].State; | |
| } | |
| return 1; | |
| } | |
| void kwsysProcess_Interrupt(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, "interrupt: child not executing\n")); | |
| return; | |
| } | |
| /* Skip actually interrupting the child if it has already terminated. */ | |
| if (cp->Terminated) { | |
| KWSYSPE_DEBUG((stderr, "interrupt: child already terminated\n")); | |
| return; | |
| } | |
| /* Interrupt the children. */ | |
| if (cp->CreateProcessGroup) { | |
| if (cp->ProcessInformation) { | |
| for (i = 0; i < cp->NumberOfCommands; ++i) { | |
| /* Make sure the process handle isn't closed (e.g. from disowning). */ | |
| if (cp->ProcessInformation[i].hProcess) { | |
| /* The user created a process group for this process. The group ID | |
| is the process ID for the original process in the group. Note | |
| that we have to use Ctrl+Break: Ctrl+C is not allowed for process | |
| groups. */ | |
| GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, | |
| cp->ProcessInformation[i].dwProcessId); | |
| } | |
| } | |
| } | |
| } else { | |
| /* No process group was created. Kill our own process group... */ | |
| GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0); | |
| } | |
| } | |
| 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); | |
| /* Remove from global list of processes and close handles. */ | |
| kwsysProcessesRemove(cp->ProcessInformation[i].hProcess); | |
| 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. */ | |
| } | |
| void kwsysProcess_KillPID(unsigned long process_id) | |
| { | |
| kwsysProcessKillTree((DWORD)process_id); | |
| } | |
| /* | |
| 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) | |
| { | |
| int i; | |
| /* Reset internal status flags. */ | |
| cp->TimeoutExpired = 0; | |
| cp->Terminated = 0; | |
| cp->Killed = 0; | |
| free(cp->ProcessResults); | |
| /* Allocate process result information for each process. */ | |
| cp->ProcessResults = (kwsysProcessResults*)malloc( | |
| sizeof(kwsysProcessResults) * (cp->NumberOfCommands)); | |
| if (!cp->ProcessResults) { | |
| return 0; | |
| } | |
| ZeroMemory(cp->ProcessResults, | |
| sizeof(kwsysProcessResults) * cp->NumberOfCommands); | |
| for (i = 0; i < cp->NumberOfCommands; i++) { | |
| cp->ProcessResults[i].ExitException = kwsysProcess_Exception_None; | |
| cp->ProcessResults[i].State = kwsysProcess_StateByIndex_Starting; | |
| cp->ProcessResults[i].ExitCode = 1; | |
| cp->ProcessResults[i].ExitValue = 1; | |
| strcpy(cp->ProcessResults[i].ExitExceptionString, "No exception"); | |
| } | |
| /* Allocate process information for each process. */ | |
| free(cp->ProcessInformation); | |
| cp->ProcessInformation = (PROCESS_INFORMATION*)malloc( | |
| sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands); | |
| if (!cp->ProcessInformation) { | |
| return 0; | |
| } | |
| ZeroMemory(cp->ProcessInformation, | |
| sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands); | |
| 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 = GetCurrentDirectoryW(0, 0); | |
| if (cp->RealWorkingDirectoryLength > 0) { | |
| cp->RealWorkingDirectory = | |
| malloc(cp->RealWorkingDirectoryLength * sizeof(wchar_t)); | |
| if (!cp->RealWorkingDirectory) { | |
| return 0; | |
| } | |
| } | |
| } | |
| { | |
| for (i = 0; i < 3; ++i) { | |
| cp->PipeChildStd[i] = INVALID_HANDLE_VALUE; | |
| } | |
| } | |
| return 1; | |
| } | |
| static DWORD kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn) | |
| { | |
| DWORD flags; | |
| /* Check whether the handle is valid for this process. */ | |
| if (in != INVALID_HANDLE_VALUE && GetHandleInformation(in, &flags)) { | |
| /* Use the handle as-is if it is already inherited. */ | |
| if (flags & HANDLE_FLAG_INHERIT) { | |
| *out = in; | |
| return ERROR_SUCCESS; | |
| } | |
| /* Create an inherited copy of this handle. */ | |
| if (DuplicateHandle(GetCurrentProcess(), in, GetCurrentProcess(), out, 0, | |
| TRUE, DUPLICATE_SAME_ACCESS)) { | |
| return ERROR_SUCCESS; | |
| } else { | |
| return GetLastError(); | |
| } | |
| } else { | |
| /* The given handle is not valid for this process. Some child | |
| processes may break if they do not have a valid standard handle, | |
| so open NUL to give to the child. */ | |
| SECURITY_ATTRIBUTES sa; | |
| ZeroMemory(&sa, sizeof(sa)); | |
| sa.nLength = (DWORD)sizeof(sa); | |
| sa.bInheritHandle = 1; | |
| *out = CreateFileW( | |
| L"NUL", | |
| (isStdIn ? GENERIC_READ : (GENERIC_WRITE | FILE_READ_ATTRIBUTES)), | |
| FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0); | |
| return (*out != INVALID_HANDLE_VALUE) ? ERROR_SUCCESS : GetLastError(); | |
| } | |
| } | |
| DWORD kwsysProcessCreate(kwsysProcess* cp, int index, | |
| kwsysProcessCreateInformation* si) | |
| { | |
| DWORD creationFlags; | |
| DWORD error = ERROR_SUCCESS; | |
| /* Check if we are currently exiting. */ | |
| if (!kwsysTryEnterCreateProcessSection()) { | |
| /* The Ctrl handler is currently working on exiting our process. Rather | |
| than return an error code, which could cause incorrect conclusions to be | |
| reached by the caller, we simply hang. (For example, a CMake try_run | |
| configure step might cause the project to configure wrong.) */ | |
| Sleep(INFINITE); | |
| } | |
| /* Create the child in a suspended state so we can wait until all | |
| children have been created before running any one. */ | |
| creationFlags = CREATE_SUSPENDED; | |
| if (cp->CreateProcessGroup) { | |
| creationFlags |= CREATE_NEW_PROCESS_GROUP; | |
| } | |
| /* Create inherited copies of the handles. */ | |
| (void)((error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput, | |
| si->hStdInput, 1)) || | |
| (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput, | |
| si->hStdOutput, 0)) || | |
| (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError, | |
| si->hStdError, 0)) || | |
| /* Create the process. */ | |
| (!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, creationFlags, 0, | |
| 0, &si->StartupInfo, | |
| &cp->ProcessInformation[index]) && | |
| (error = GetLastError()))); | |
| /* Close the inherited copies of the handles. */ | |
| if (si->StartupInfo.hStdInput != si->hStdInput) { | |
| kwsysProcessCleanupHandle(&si->StartupInfo.hStdInput); | |
| } | |
| if (si->StartupInfo.hStdOutput != si->hStdOutput) { | |
| kwsysProcessCleanupHandle(&si->StartupInfo.hStdOutput); | |
| } | |
| if (si->StartupInfo.hStdError != si->hStdError) { | |
| kwsysProcessCleanupHandle(&si->StartupInfo.hStdError); | |
| } | |
| /* Add the process to the global list of processes. */ | |
| if (!error && | |
| !kwsysProcessesAdd(cp->ProcessInformation[index].hProcess, | |
| cp->ProcessInformation[index].dwProcessId, | |
| cp->CreateProcessGroup)) { | |
| /* This failed for some reason. Kill the suspended process. */ | |
| TerminateProcess(cp->ProcessInformation[index].hProcess, 1); | |
| /* And clean up... */ | |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess); | |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hThread); | |
| strcpy(cp->ErrorMessage, "kwsysProcessesAdd function failed"); | |
| error = ERROR_NOT_ENOUGH_MEMORY; /* Most likely reason. */ | |
| } | |
| /* If the console Ctrl handler is waiting for us, this will release it... */ | |
| kwsysLeaveCreateProcessSection(); | |
| return error; | |
| } | |
| 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]); | |
| /* Remove from global list of processes. */ | |
| kwsysProcessesRemove(cp->ProcessInformation[index].hProcess); | |
| /* 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); | |
| } | |
| } | |
| } | |
| DWORD kwsysProcessSetupOutputPipeFile(PHANDLE phandle, char const* name) | |
| { | |
| HANDLE fout; | |
| wchar_t* wname; | |
| DWORD error; | |
| if (!name) { | |
| return ERROR_INVALID_PARAMETER; | |
| } | |
| /* Close the existing handle. */ | |
| kwsysProcessCleanupHandle(phandle); | |
| /* Create a handle to write a file for the pipe. */ | |
| wname = kwsysEncoding_DupToWide(name); | |
| fout = | |
| CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); | |
| error = GetLastError(); | |
| free(wname); | |
| if (fout == INVALID_HANDLE_VALUE) { | |
| return error; | |
| } | |
| /* Assign the replacement handle. */ | |
| *phandle = fout; | |
| return ERROR_SUCCESS; | |
| } | |
| void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle) | |
| { | |
| /* Close the existing handle. */ | |
| kwsysProcessCleanupHandle(handle); | |
| /* Store the new standard handle. */ | |
| *handle = GetStdHandle(nStdHandle); | |
| } | |
| void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle) | |
| { | |
| /* Close the existing handle. */ | |
| kwsysProcessCleanupHandle(handle); | |
| /* Store the new given handle. */ | |
| *handle = native; | |
| } | |
| /* Close the given handle if it is open. Reset its value to 0. */ | |
| void kwsysProcessCleanupHandle(PHANDLE h) | |
| { | |
| if (h && *h && *h != INVALID_HANDLE_VALUE && | |
| *h != GetStdHandle(STD_INPUT_HANDLE) && | |
| *h != GetStdHandle(STD_OUTPUT_HANDLE) && | |
| *h != GetStdHandle(STD_ERROR_HANDLE)) { | |
| CloseHandle(*h); | |
| *h = INVALID_HANDLE_VALUE; | |
| } | |
| } | |
| /* Close all handles created by kwsysProcess_Execute. */ | |
| void kwsysProcessCleanup(kwsysProcess* cp, DWORD 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. */ | |
| wchar_t err_msg[KWSYSPE_PIPE_BUFFER_SIZE]; | |
| DWORD length = FormatMessageW( | |
| FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, | |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_msg, | |
| 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%lX. " | |
| "FormatMessage failed with error 0x%lX", | |
| error, GetLastError()); | |
| } | |
| if (!WideCharToMultiByte(CP_UTF8, 0, err_msg, -1, cp->ErrorMessage, | |
| KWSYSPE_PIPE_BUFFER_SIZE, NULL, NULL)) { | |
| /* WideCharToMultiByte failed. Use a default message. */ | |
| snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE, | |
| "Process execution failed with error 0x%lX. " | |
| "WideCharToMultiByte failed with error 0x%lX", | |
| error, 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) { | |
| /* Remove from global list of processes and close handles. */ | |
| kwsysProcessesRemove(cp->ProcessInformation[i].hProcess); | |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread); | |
| kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess); | |
| } | |
| } | |
| /* Restore the working directory. */ | |
| if (cp->RealWorkingDirectory) { | |
| SetCurrentDirectoryW(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; | |
| } | |
| for (i = 0; i < 3; ++i) { | |
| kwsysProcessCleanupHandle(&cp->PipeChildStd[i]); | |
| } | |
| } | |
| 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; | |
| } | |
| } | |
| /* 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->ProcessResults[idx].ExitException = kwsysProcess_Exception_##type; \ | |
| strcpy(cp->ProcessResults[idx].ExitExceptionString, str) | |
| static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int code, | |
| int idx) | |
| { | |
| 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 handle"); | |
| 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->ProcessResults[idx].ExitException = kwsysProcess_Exception_Other; | |
| snprintf(cp->ProcessResults[idx].ExitExceptionString, | |
| KWSYSPE_PIPE_BUFFER_SIZE, "Exit code 0x%x", 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); | |
| #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx | |
| # pragma warning(push) | |
| # ifdef __INTEL_COMPILER | |
| # pragma warning(disable : 1478) | |
| # elif defined __clang__ | |
| # pragma clang diagnostic push | |
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| # else | |
| # pragma warning(disable : 4996) | |
| # endif | |
| #endif | |
| GetVersionEx(&osv); | |
| #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx | |
| # ifdef __clang__ | |
| # pragma clang diagnostic pop | |
| # else | |
| # pragma warning(pop) | |
| # endif | |
| #endif | |
| 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 = GetModuleHandleW(L"ntdll.dll"); | |
| if (hNT) { | |
| /* Get pointers to the needed API functions. */ | |
| self->P_ZwQuerySystemInformation = | |
| ((ZwQuerySystemInformationType)(void*)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. */ | |
| 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 ? (int)self->CurrentInfo->ProcessId : -1; | |
| } | |
| static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self) | |
| { | |
| return self->CurrentInfo ? (int)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 = GetModuleHandleW(L"kernel32.dll"); | |
| if (hKernel) { | |
| self->P_CreateToolhelp32Snapshot = | |
| ((CreateToolhelp32SnapshotType)(void*)GetProcAddress( | |
| hKernel, "CreateToolhelp32Snapshot")); | |
| self->P_Process32First = | |
| ((Process32FirstType)(void*)GetProcAddress(hKernel, "Process32First")); | |
| self->P_Process32Next = | |
| ((Process32NextType)(void*)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 ? (int)self->CurrentEntry.th32ProcessID : -1; | |
| } | |
| static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self) | |
| { | |
| return self->Snapshot ? (int)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); | |
| } | |
| } | |
| /* Global set of executing processes for use by the Ctrl handler. | |
| This global instance will be zero-initialized by the compiler. | |
| Note that the console Ctrl handler runs on a background thread and so | |
| everything it does must be thread safe. Here, we track the hProcess | |
| HANDLEs directly instead of kwsysProcess instances, so that we don't have | |
| to make kwsysProcess thread safe. */ | |
| typedef struct kwsysProcessInstance_s | |
| { | |
| HANDLE hProcess; | |
| DWORD dwProcessId; | |
| int NewProcessGroup; /* Whether the process was created in a new group. */ | |
| } kwsysProcessInstance; | |
| typedef struct kwsysProcessInstances_s | |
| { | |
| /* Whether we have initialized key fields below, like critical sections. */ | |
| int Initialized; | |
| /* Ctrl handler runs on a different thread, so we must sync access. */ | |
| CRITICAL_SECTION Lock; | |
| int Exiting; | |
| size_t Count; | |
| size_t Size; | |
| kwsysProcessInstance* Processes; | |
| } kwsysProcessInstances; | |
| static kwsysProcessInstances kwsysProcesses; | |
| /* Initialize critial section and set up console Ctrl handler. You MUST call | |
| this before using any other kwsysProcesses* functions below. */ | |
| static int kwsysProcessesInitialize(void) | |
| { | |
| /* Initialize everything if not done already. */ | |
| if (!kwsysProcesses.Initialized) { | |
| InitializeCriticalSection(&kwsysProcesses.Lock); | |
| /* Set up console ctrl handler. */ | |
| if (!SetConsoleCtrlHandler(kwsysCtrlHandler, TRUE)) { | |
| return 0; | |
| } | |
| kwsysProcesses.Initialized = 1; | |
| } | |
| return 1; | |
| } | |
| /* The Ctrl handler waits on the global list of processes. To prevent an | |
| orphaned process, do not create a new process if the Ctrl handler is | |
| already running. Do so by using this function to check if it is ok to | |
| create a process. */ | |
| static int kwsysTryEnterCreateProcessSection(void) | |
| { | |
| /* Enter main critical section; this means creating a process and the Ctrl | |
| handler are mutually exclusive. */ | |
| EnterCriticalSection(&kwsysProcesses.Lock); | |
| /* Indicate to the caller if they can create a process. */ | |
| if (kwsysProcesses.Exiting) { | |
| LeaveCriticalSection(&kwsysProcesses.Lock); | |
| return 0; | |
| } else { | |
| return 1; | |
| } | |
| } | |
| /* Matching function on successful kwsysTryEnterCreateProcessSection return. | |
| Make sure you called kwsysProcessesAdd if applicable before calling this.*/ | |
| static void kwsysLeaveCreateProcessSection(void) | |
| { | |
| LeaveCriticalSection(&kwsysProcesses.Lock); | |
| } | |
| /* Add new process to global process list. The Ctrl handler will wait for | |
| the process to exit before it returns. Do not close the process handle | |
| until after calling kwsysProcessesRemove. The newProcessGroup parameter | |
| must be set if the process was created with CREATE_NEW_PROCESS_GROUP. */ | |
| static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessid, | |
| int newProcessGroup) | |
| { | |
| if (!kwsysProcessesInitialize() || !hProcess || | |
| hProcess == INVALID_HANDLE_VALUE) { | |
| return 0; | |
| } | |
| /* Enter the critical section. */ | |
| EnterCriticalSection(&kwsysProcesses.Lock); | |
| /* Make sure there is enough space for the new process handle. */ | |
| if (kwsysProcesses.Count == kwsysProcesses.Size) { | |
| size_t newSize; | |
| kwsysProcessInstance* newArray; | |
| /* Start with enough space for a small number of process handles | |
| and double the size each time more is needed. */ | |
| newSize = kwsysProcesses.Size ? kwsysProcesses.Size * 2 : 4; | |
| /* Try allocating the new block of memory. */ | |
| if ((newArray = (kwsysProcessInstance*)malloc( | |
| newSize * sizeof(kwsysProcessInstance)))) { | |
| /* Copy the old process handles to the new memory. */ | |
| if (kwsysProcesses.Count > 0) { | |
| memcpy(newArray, kwsysProcesses.Processes, | |
| kwsysProcesses.Count * sizeof(kwsysProcessInstance)); | |
| } | |
| } else { | |
| /* Failed to allocate memory for the new process handle set. */ | |
| LeaveCriticalSection(&kwsysProcesses.Lock); | |
| return 0; | |
| } | |
| /* Free original array. */ | |
| free(kwsysProcesses.Processes); | |
| /* Update original structure with new allocation. */ | |
| kwsysProcesses.Size = newSize; | |
| kwsysProcesses.Processes = newArray; | |
| } | |
| /* Append the new process information to the set. */ | |
| kwsysProcesses.Processes[kwsysProcesses.Count].hProcess = hProcess; | |
| kwsysProcesses.Processes[kwsysProcesses.Count].dwProcessId = dwProcessid; | |
| kwsysProcesses.Processes[kwsysProcesses.Count++].NewProcessGroup = | |
| newProcessGroup; | |
| /* Leave critical section and return success. */ | |
| LeaveCriticalSection(&kwsysProcesses.Lock); | |
| return 1; | |
| } | |
| /* Removes process to global process list. */ | |
| static void kwsysProcessesRemove(HANDLE hProcess) | |
| { | |
| size_t i; | |
| if (!hProcess || hProcess == INVALID_HANDLE_VALUE) { | |
| return; | |
| } | |
| EnterCriticalSection(&kwsysProcesses.Lock); | |
| /* Find the given process in the set. */ | |
| for (i = 0; i < kwsysProcesses.Count; ++i) { | |
| if (kwsysProcesses.Processes[i].hProcess == hProcess) { | |
| break; | |
| } | |
| } | |
| if (i < kwsysProcesses.Count) { | |
| /* Found it! Remove the process from the set. */ | |
| --kwsysProcesses.Count; | |
| for (; i < kwsysProcesses.Count; ++i) { | |
| kwsysProcesses.Processes[i] = kwsysProcesses.Processes[i + 1]; | |
| } | |
| /* If this was the last process, free the array. */ | |
| if (kwsysProcesses.Count == 0) { | |
| kwsysProcesses.Size = 0; | |
| free(kwsysProcesses.Processes); | |
| kwsysProcesses.Processes = 0; | |
| } | |
| } | |
| LeaveCriticalSection(&kwsysProcesses.Lock); | |
| } | |
| static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType) | |
| { | |
| size_t i; | |
| (void)dwCtrlType; | |
| /* Enter critical section. */ | |
| EnterCriticalSection(&kwsysProcesses.Lock); | |
| /* Set flag indicating that we are exiting. */ | |
| kwsysProcesses.Exiting = 1; | |
| /* If some of our processes were created in a new process group, we must | |
| manually interrupt them. They won't otherwise receive a Ctrl+C/Break. */ | |
| for (i = 0; i < kwsysProcesses.Count; ++i) { | |
| if (kwsysProcesses.Processes[i].NewProcessGroup) { | |
| DWORD groupId = kwsysProcesses.Processes[i].dwProcessId; | |
| if (groupId) { | |
| GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, groupId); | |
| } | |
| } | |
| } | |
| /* Wait for each child process to exit. This is the key step that prevents | |
| us from leaving several orphaned children processes running in the | |
| background when the user presses Ctrl+C. */ | |
| for (i = 0; i < kwsysProcesses.Count; ++i) { | |
| WaitForSingleObject(kwsysProcesses.Processes[i].hProcess, INFINITE); | |
| } | |
| /* Leave critical section. */ | |
| LeaveCriticalSection(&kwsysProcesses.Lock); | |
| /* Continue on to default Ctrl handler (which calls ExitProcess). */ | |
| return FALSE; | |
| } | |
| void kwsysProcess_ResetStartTime(kwsysProcess* cp) | |
| { | |
| if (!cp) { | |
| return; | |
| } | |
| /* Reset start time. */ | |
| cp->StartTime = kwsysProcessTimeGetCurrent(); | |
| } |