diff options
Diffstat (limited to 'test/API/driver/kwsys/ProcessWin32.c')
-rw-r--r-- | test/API/driver/kwsys/ProcessWin32.c | 2786 |
1 files changed, 0 insertions, 2786 deletions
diff --git a/test/API/driver/kwsys/ProcessWin32.c b/test/API/driver/kwsys/ProcessWin32.c deleted file mode 100644 index a963862..0000000 --- a/test/API/driver/kwsys/ProcessWin32.c +++ /dev/null @@ -1,2786 +0,0 @@ -/* 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> /* sprintf */ -#include <string.h> /* strlen, strdup */ -#ifdef __WATCOMC__ -# define _unlink unlink -#endif - -#ifndef _MAX_FNAME -# define _MAX_FNAME 4096 -#endif -#ifndef _MAX_PATH -# define _MAX_PATH 4096 -#endif - -#ifdef _MSC_VER -# pragma warning(pop) -# pragma warning(disable : 4514) -# pragma warning(disable : 4706) -#endif - -#if defined(__BORLANDC__) -# pragma warn - 8004 /* assigned a value that is never used */ -# pragma warn - 8060 /* Assignment inside if() condition. */ -#endif - -/* There are pipes for the process pipeline's stdout and stderr. */ -#define KWSYSPE_PIPE_COUNT 2 -#define KWSYSPE_PIPE_STDOUT 0 -#define KWSYSPE_PIPE_STDERR 1 - -/* The maximum amount to read from a pipe at a time. */ -#define KWSYSPE_PIPE_BUFFER_SIZE 1024 - -/* Debug output macro. */ -#if 0 -# define KWSYSPE_DEBUG(x) \ - ((void*)cp == (void*)0x00226DE0 \ - ? (fprintf(stderr, "%d/%p/%d ", (int)GetCurrentProcessId(), cp, \ - __LINE__), \ - fprintf x, fflush(stderr), 1) \ - : (1)) -#else -# define KWSYSPE_DEBUG(x) (void)1 -#endif - -typedef LARGE_INTEGER kwsysProcessTime; - -typedef struct kwsysProcessCreateInformation_s -{ - /* Windows child startup control data. */ - 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, const char* 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, const char* 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, 0, 0); - 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, 0)) { - free(work_dir); - free(wdir); - return 0; - } - cp->WorkingDirectory = work_dir; - } - free(wdir); - } - return 1; -} - -int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, const char* file) -{ - char** pfile; - if (!cp) { - return 0; - } - switch (pipe) { - case kwsysProcess_Pipe_STDIN: - pfile = &cp->PipeFileSTDIN; - break; - case kwsysProcess_Pipe_STDOUT: - pfile = &cp->PipeFileSTDOUT; - break; - case kwsysProcess_Pipe_STDERR: - pfile = &cp->PipeFileSTDERR; - break; - default: - return 0; - } - if (*pfile) { - free(*pfile); - *pfile = 0; - } - if (file) { - *pfile = 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, 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; -} - -const char* kwsysProcess_GetErrorString(kwsysProcess* cp) -{ - if (!cp) { - return "Process management structure could not be allocated"; - } else if (cp->State == kwsysProcess_State_Error) { - return cp->ErrorMessage; - } - return "Success"; -} - -const char* kwsysProcess_GetExceptionString(kwsysProcess* cp) -{ - if (!(cp && 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]; -} - -const char* 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; - } - SetCurrentDirectoryW(cp->WorkingDirectory); - } - - /* 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. */ - (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, const char* 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%X. " - "FormatMessage failed with error 0x%X", - 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%X. " - "WideCharToMultiByte failed with error 0x%X", - 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 hanlde"); - break; - case STATUS_NONCONTINUABLE_EXCEPTION: - KWSYSPE_CASE(Fault, "Noncontinuable exception"); - break; - case STATUS_INVALID_DISPOSITION: - KWSYSPE_CASE(Fault, "Invalid disposition"); - break; - case STATUS_ARRAY_BOUNDS_EXCEEDED: - KWSYSPE_CASE(Fault, "Array bounds exceeded"); - break; - case STATUS_STACK_OVERFLOW: - KWSYSPE_CASE(Fault, "Stack overflow"); - break; - - case STATUS_ILLEGAL_INSTRUCTION: - KWSYSPE_CASE(Illegal, "Illegal instruction"); - break; - case STATUS_PRIVILEGED_INSTRUCTION: - KWSYSPE_CASE(Illegal, "Privileged instruction"); - break; - - case STATUS_NO_MEMORY: - default: - cp->ProcessResults[idx].ExitException = kwsysProcess_Exception_Other; - _snprintf(cp->ProcessResults[idx].ExitExceptionString, - KWSYSPE_PIPE_BUFFER_SIZE, "Exit code 0x%x\n", code); - break; - } -} -#undef KWSYSPE_CASE - -typedef struct kwsysProcess_List_s kwsysProcess_List; -static kwsysProcess_List* kwsysProcess_List_New(void); -static void kwsysProcess_List_Delete(kwsysProcess_List* self); -static int kwsysProcess_List_Update(kwsysProcess_List* self); -static int kwsysProcess_List_NextProcess(kwsysProcess_List* self); -static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self); -static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self); - -/* Windows NT 4 API definitions. */ -#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) -typedef LONG NTSTATUS; -typedef LONG KPRIORITY; -typedef struct _UNICODE_STRING UNICODE_STRING; -struct _UNICODE_STRING -{ - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -}; - -/* The process information structure. Declare only enough to get - process identifiers. The rest may be ignored because we use the - NextEntryDelta to move through an array of instances. */ -typedef struct _SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION; -typedef SYSTEM_PROCESS_INFORMATION* PSYSTEM_PROCESS_INFORMATION; -struct _SYSTEM_PROCESS_INFORMATION -{ - ULONG NextEntryDelta; - ULONG ThreadCount; - ULONG Reserved1[6]; - LARGE_INTEGER CreateTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER KernelTime; - UNICODE_STRING ProcessName; - KPRIORITY BasePriority; - ULONG ProcessId; - ULONG InheritedFromProcessId; -}; - -/* Toolhelp32 API definitions. */ -#define TH32CS_SNAPPROCESS 0x00000002 -#if defined(_WIN64) -typedef unsigned __int64 ProcessULONG_PTR; -#else -typedef unsigned long ProcessULONG_PTR; -#endif -typedef struct tagPROCESSENTRY32 PROCESSENTRY32; -typedef PROCESSENTRY32* LPPROCESSENTRY32; -struct tagPROCESSENTRY32 -{ - DWORD dwSize; - DWORD cntUsage; - DWORD th32ProcessID; - ProcessULONG_PTR th32DefaultHeapID; - DWORD th32ModuleID; - DWORD cntThreads; - DWORD th32ParentProcessID; - LONG pcPriClassBase; - DWORD dwFlags; - char szExeFile[MAX_PATH]; -}; - -/* Windows API function types. */ -typedef HANDLE(WINAPI* CreateToolhelp32SnapshotType)(DWORD, DWORD); -typedef BOOL(WINAPI* Process32FirstType)(HANDLE, LPPROCESSENTRY32); -typedef BOOL(WINAPI* Process32NextType)(HANDLE, LPPROCESSENTRY32); -typedef NTSTATUS(WINAPI* ZwQuerySystemInformationType)(ULONG, PVOID, ULONG, - PULONG); - -static int kwsysProcess_List__New_NT4(kwsysProcess_List* self); -static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self); -static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self); -static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self); -static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self); -static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self); -static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self); -static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self); -static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self); -static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self); -static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self); -static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self); - -struct kwsysProcess_List_s -{ - /* Implementation switches at runtime based on version of Windows. */ - int NT4; - - /* Implementation functions and data for NT 4. */ - ZwQuerySystemInformationType P_ZwQuerySystemInformation; - char* Buffer; - int BufferSize; - PSYSTEM_PROCESS_INFORMATION CurrentInfo; - - /* Implementation functions and data for other Windows versions. */ - CreateToolhelp32SnapshotType P_CreateToolhelp32Snapshot; - Process32FirstType P_Process32First; - Process32NextType P_Process32Next; - HANDLE Snapshot; - PROCESSENTRY32 CurrentEntry; -}; - -static kwsysProcess_List* kwsysProcess_List_New(void) -{ - OSVERSIONINFO osv; - kwsysProcess_List* self; - - /* Allocate and initialize the list object. */ - if (!(self = (kwsysProcess_List*)malloc(sizeof(kwsysProcess_List)))) { - return 0; - } - memset(self, 0, sizeof(*self)); - - /* Select an implementation. */ - ZeroMemory(&osv, sizeof(osv)); - osv.dwOSVersionInfoSize = sizeof(osv); -#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)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 ? self->CurrentInfo->ProcessId : -1; -} - -static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self) -{ - return self->CurrentInfo ? self->CurrentInfo->InheritedFromProcessId : -1; -} - -static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self) -{ - /* Get a handle to the Windows runtime module that should already be - loaded in this program. This does not actually increment the - reference count to the module so we do not need to close the - handle. */ - HMODULE hKernel = GetModuleHandleW(L"kernel32.dll"); - if (hKernel) { - self->P_CreateToolhelp32Snapshot = - ((CreateToolhelp32SnapshotType)GetProcAddress( - hKernel, "CreateToolhelp32Snapshot")); - self->P_Process32First = - ((Process32FirstType)GetProcAddress(hKernel, "Process32First")); - self->P_Process32Next = - ((Process32NextType)GetProcAddress(hKernel, "Process32Next")); - } - return (self->P_CreateToolhelp32Snapshot && self->P_Process32First && - self->P_Process32Next) - ? 1 - : 0; -} - -static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self) -{ - if (self->Snapshot) { - CloseHandle(self->Snapshot); - } -} - -static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self) -{ - if (self->Snapshot) { - CloseHandle(self->Snapshot); - } - if (!(self->Snapshot = - self->P_CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0))) { - return 0; - } - ZeroMemory(&self->CurrentEntry, sizeof(self->CurrentEntry)); - self->CurrentEntry.dwSize = sizeof(self->CurrentEntry); - if (!self->P_Process32First(self->Snapshot, &self->CurrentEntry)) { - CloseHandle(self->Snapshot); - self->Snapshot = 0; - return 0; - } - return 1; -} - -static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self) -{ - if (self->Snapshot) { - if (self->P_Process32Next(self->Snapshot, &self->CurrentEntry)) { - return 1; - } - CloseHandle(self->Snapshot); - self->Snapshot = 0; - } - return 0; -} - -static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self) -{ - return self->Snapshot ? self->CurrentEntry.th32ProcessID : -1; -} - -static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self) -{ - return self->Snapshot ? self->CurrentEntry.th32ParentProcessID : -1; -} - -static void kwsysProcessKill(DWORD pid) -{ - HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, pid); - if (h) { - TerminateProcess(h, 255); - WaitForSingleObject(h, INFINITE); - CloseHandle(h); - } -} - -static void kwsysProcessKillTree(int pid) -{ - kwsysProcess_List* plist = kwsysProcess_List_New(); - kwsysProcessKill(pid); - if (plist) { - do { - if (kwsysProcess_List_GetCurrentParentId(plist) == pid) { - int ppid = kwsysProcess_List_GetCurrentProcessId(plist); - kwsysProcessKillTree(ppid); - } - } while (kwsysProcess_List_NextProcess(plist)); - kwsysProcess_List_Delete(plist); - } -} - -static void kwsysProcessDisablePipeThreads(kwsysProcess* cp) -{ - int i; - - /* If data were just reported data, release the pipe's thread. */ - if (cp->CurrentIndex < KWSYSPE_PIPE_COUNT) { - KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex)); - ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); - cp->CurrentIndex = KWSYSPE_PIPE_COUNT; - } - - /* Wakeup all reading threads that are not on closed pipes. */ - for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { - /* The wakeup threads will write one byte to the pipe write ends. - If there are no data in the pipe then this is enough to wakeup - the reading threads. If there are already data in the pipe - this may block. We cannot use PeekNamedPipe to check whether - there are data because an outside process might still be - writing data if we are disowning it. Also, PeekNamedPipe will - block if checking a pipe on which the reading thread is - currently calling ReadPipe. Therefore we need a separate - thread to call WriteFile. If it blocks, that is okay because - it will unblock when we close the read end and break the pipe - below. */ - if (cp->Pipe[i].Read) { - KWSYSPE_DEBUG((stderr, "releasing waker %d\n", i)); - ReleaseSemaphore(cp->Pipe[i].Waker.Go, 1, 0); - } - } - - /* Tell pipe threads to reset until we run another process. */ - while (cp->PipesLeft > 0) { - /* The waking threads will cause all reading threads to report. - Wait for the next one and save its index. */ - KWSYSPE_DEBUG((stderr, "waiting for reader\n")); - WaitForSingleObject(cp->Full, INFINITE); - cp->CurrentIndex = cp->SharedIndex; - ReleaseSemaphore(cp->SharedIndexMutex, 1, 0); - KWSYSPE_DEBUG((stderr, "got reader %d\n", cp->CurrentIndex)); - - /* We are done reading this pipe. Close its read handle. */ - cp->Pipe[cp->CurrentIndex].Closed = 1; - kwsysProcessCleanupHandle(&cp->Pipe[cp->CurrentIndex].Read); - --cp->PipesLeft; - - /* Tell the reading thread we are done with the data. It will - reset immediately because the pipe is closed. */ - ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); - } -} - -/* 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(); -} |