diff --git a/core/org.eclipse.cdt.core.native/native_src/win/Win32ProcessEx.c b/core/org.eclipse.cdt.core.native/native_src/win/Win32ProcessEx.c index 38959c98125..feaf4faca5a 100644 --- a/core/org.eclipse.cdt.core.native/native_src/win/Win32ProcessEx.c +++ b/core/org.eclipse.cdt.core.native/native_src/win/Win32ProcessEx.c @@ -36,6 +36,11 @@ #define MAX_PROCS (100) // Maximum number of simultaneously running processes +typedef struct _eventInfo { + HANDLE handle; + wchar_t *name; +} EventInfo_t; + // Process description block. Should be created for each launched process typedef struct _procInfo { int pid; // Process ID @@ -43,15 +48,15 @@ typedef struct _procInfo { // (actually this impossible from OS point of view but it is still possible // a clash of new created and already finished process with one and the same PID. // 4 events connected to this process (see starter) - HANDLE eventBreak; // signaled when Spawner.interrupt() is called; mildest of the terminate requests (SIGINT signal - // in UNIX world) - HANDLE eventWait; - HANDLE eventTerminate; // signaled when Spawner.terminate() is called; more forceful terminate request (SIGTERM - // signal in UNIX world) - HANDLE eventKill; // signaled when Spawner.kill() is called; most forceful terminate request (SIGKILL signal in UNIX - // world) - HANDLE eventCtrlc; // signaled when Spawner.interruptCTRLC() is called; like interrupt() but sends CTRL-C in all - // cases, even when inferior is a Cygwin program + EventInfo_t eventBreak; // signaled when Spawner.interrupt() is called; mildest of the terminate requests (SIGINT + // signal in UNIX world) + EventInfo_t eventWait; + EventInfo_t eventTerminate; // signaled when Spawner.terminate() is called; more forceful terminate request (SIGTERM + // signal in UNIX world) + EventInfo_t eventKill; // signaled when Spawner.kill() is called; most forceful terminate request (SIGKILL signal in + // UNIX world) + EventInfo_t eventCtrlc; // signaled when Spawner.interruptCTRLC() is called; like interrupt() but sends CTRL-C in + // all cases, even when inferior is a Cygwin program } procInfo_t, *pProcInfo_t; static int procCounter = 0; // Number of running processes @@ -68,9 +73,6 @@ pProcInfo_t findProcInfo(int pid); // We launch separate thread for each project to trap it termination void _cdecl waitProcTermination(void *pv); -// This is a helper function to prevent losing of quotation marks -static int copyTo(wchar_t *target, const wchar_t *source, int cpyLenght, int availSpace); - // Use this function to clean project descriptor and return it to the pool of available blocks. static void cleanUpProcBlock(pProcInfo_t pCurProcInfo); @@ -113,20 +115,217 @@ extern "C" return -1; } -void ensureSize(wchar_t **ptr, int *psize, int requiredLength) { - int size = *psize; - if (requiredLength > size) { - size = 2 * size; - if (size < requiredLength) { - size = requiredLength; +static bool createStandardNamedPipe(HANDLE *handle, DWORD stdHandle, int pid, int counter) { + wchar_t pipeName[PIPE_NAME_LENGTH]; + DWORD dwOpenMode; + + switch (stdHandle) { + case STD_INPUT_HANDLE: + BUILD_PIPE_NAME(pipeName, L"stdin", pid, counter); + dwOpenMode = PIPE_ACCESS_OUTBOUND; + break; + case STD_OUTPUT_HANDLE: + BUILD_PIPE_NAME(pipeName, L"stdout", pid, counter); + dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED; + break; + case STD_ERROR_HANDLE: + BUILD_PIPE_NAME(pipeName, L"stderr", pid, counter); + dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED; + break; + default: + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Invalid STD handle given %i", stdHandle); } - *ptr = (wchar_t *)realloc(*ptr, size * sizeof(wchar_t)); - if (*ptr) { - *psize = size; + return false; + } + + HANDLE pipe = CreateNamedPipeW(pipeName, dwOpenMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL); + if (INVALID_HANDLE_VALUE == pipe) { + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Failed to create named pipe: %s\n", pipeName); + } + return false; + } + + SetHandleInformation(pipe, HANDLE_FLAG_INHERIT, TRUE); + + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Successfully created pipe %s -> %p\n", pipeName, pipe); + } + + *handle = pipe; + return true; +} + +static bool createNamedEvent(EventInfo_t *eventInfo, BOOL manualReset, const wchar_t *prefix, int pid, int counter) { + wchar_t eventName[50]; + swprintf(eventName, sizeof(eventName) / sizeof(eventName[0]), L"%s%04x%08x", prefix, pid, counter); + + HANDLE event = CreateEventW(NULL, manualReset, FALSE, eventName); + if (!event) { + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Failed to create event %s -> %i\n", eventName, GetLastError()); + } + return false; + } else if (GetLastError() == ERROR_ALREADY_EXISTS) { + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Event %s already exist -> %p\n", eventName, event); + } + return false; + } + + eventInfo->handle = event; + eventInfo->name = wcsdup(eventName); + + if (!eventInfo->name) { + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Failed to allocate memory for event %s -> %p\n", eventName, event); + } + return false; + } + + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Successfully created event %s -> %p\n", eventName, event); + } + + return true; +} + +static bool createCommandLine(JNIEnv *env, jobjectArray cmdarray, wchar_t **cmdLine, const wchar_t *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + + wchar_t *buffer = NULL; + int size = MAX_CMD_SIZE; + int required = 0; + do { + // Free previous buffer + free(buffer); + + size *= 2; + buffer = (wchar_t *)malloc(size * sizeof(wchar_t)); + + if (buffer) { + // Try to format the string + required = vswprintf(buffer, size, fmt, ap); } else { - *psize = 0; + // malloc failed, clean up and return + va_end(ap); + ThrowByName(env, "java/io/IOException", "Not enough memory"); + return false; + } + } while (size <= required); + va_end(ap); + + int nPos = wcslen(buffer); + int nCmdTokens = (*env)->GetArrayLength(env, cmdarray); + for (int i = 0; i < nCmdTokens; ++i) { + jstring item = (jstring)(*env)->GetObjectArrayElement(env, cmdarray, i); + jsize len = (*env)->GetStringLength(env, item); + const jchar *str = (*env)->GetStringChars(env, item, NULL); + if (str) { + required = nPos + len + 2; // 2 => space + \0 + if (required > 32 * 1024) { + free(buffer); + ThrowByName(env, "java/io/IOException", "Command line too long"); + return false; + } + + while (1) { + // Ensure enough space in buffer + if (required > size) { + size *= 2; + if (size < required) { + size = required; + } + + wchar_t *tmp = (wchar_t *)realloc(buffer, size * sizeof(wchar_t)); + if (tmp) { + // Allocation successful + buffer = tmp; + } else { + // Failed to realloc memory + free(buffer); + ThrowByName(env, "java/io/IOException", "Not enough memory"); + return false; + } + } + + int nCpyLen = copyTo(buffer + nPos, (const wchar_t *)str, len, size - nPos); + if (nCpyLen < 0) { // Buffer too small + // Do a real count of number of chars required + required = nPos + copyTo(NULL, (const wchar_t *)str, len, INT_MAX) + 2; // 2 => space + \0 + continue; + } + + // Buffer was big enough. + nPos += nCpyLen; + break; + } + + buffer[nPos++] = _T(' '); + buffer[nPos] = _T('\0'); + (*env)->ReleaseStringChars(env, item, str); + } else { + free(buffer); + ThrowByName(env, "java/io/IOException", "Command line contained null string"); + return false; } } + + *cmdLine = buffer; + return true; +} + +static bool createEnvironmentBlock(JNIEnv *env, jobjectArray envp, wchar_t **block) { + int nEnvVars = (*env)->GetArrayLength(env, envp); + + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"There are %i environment variables \n", nEnvVars); + } + + if (nEnvVars == 0) { + *block = NULL; + return true; + } + + int nPos = 0; + int nBlkSize = MAX_ENV_SIZE; + wchar_t *buffer = (wchar_t *)malloc(nBlkSize * sizeof(wchar_t)); + for (int i = 0; i < nEnvVars; ++i) { + jstring item = (jstring)(*env)->GetObjectArrayElement(env, envp, i); + jsize len = (*env)->GetStringLength(env, item); + const jchar *str = (*env)->GetStringChars(env, item, 0); + if (str) { + while (nBlkSize - nPos <= len + 2) { // +2 for two '\0' + nBlkSize += MAX_ENV_SIZE; + wchar_t *tmp = (wchar_t *)realloc(buffer, nBlkSize * sizeof(wchar_t)); + if (tmp) { + buffer = tmp; + } else { + free(buffer); + ThrowByName(env, "java/io/IOException", "Not enough memory"); + return false; + } + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Realloc environment block; new length is %i \n", nBlkSize); + } + } + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"%s\n", (const wchar_t *)str); + } + wcsncpy(buffer + nPos, (const wchar_t *)str, len); + nPos += len; + buffer[nPos++] = _T('\0'); + (*env)->ReleaseStringChars(env, item, str); + } + } + buffer[nPos] = _T('\0'); + *block = buffer; + + return true; } #ifdef __cplusplus @@ -135,202 +334,95 @@ extern "C" JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv *env, jobject process, jobjectArray cmdarray, jobjectArray envp, jstring dir, jobjectArray channels) { - HANDLE stdHandles[3]; - PROCESS_INFORMATION pi = {0}, *piCopy; - STARTUPINFOW si; - DWORD flags = 0; - wchar_t *cwd = NULL; - int ret = 0; - int nCmdLineLength = 0; - wchar_t *szCmdLine = 0; - int nBlkSize = MAX_ENV_SIZE; - wchar_t *szEnvBlock = NULL; - jsize nCmdTokens = 0; - jsize nEnvVars = 0; - DWORD pid = GetCurrentProcessId(); - int nPos; - pProcInfo_t pCurProcInfo; - - // This needs to be big enough to contain the name of the event used when calling CreateEventW bellow. - // It is made of a prefix (7 characters max) plus the value of a pointer that gets output in characters. - // This will be bigger in the case of 64 bit. - static const int MAX_EVENT_NAME_LENGTH = 50; - wchar_t eventBreakName[MAX_EVENT_NAME_LENGTH]; - wchar_t eventWaitName[MAX_EVENT_NAME_LENGTH]; - wchar_t eventTerminateName[MAX_EVENT_NAME_LENGTH]; - wchar_t eventKillName[MAX_EVENT_NAME_LENGTH]; - wchar_t eventCtrlcName[MAX_EVENT_NAME_LENGTH]; - int nLocalCounter; - wchar_t inPipeName[PIPE_NAME_LENGTH]; - wchar_t outPipeName[PIPE_NAME_LENGTH]; - wchar_t errPipeName[PIPE_NAME_LENGTH]; - jclass channelClass = NULL; - jmethodID channelConstructor = NULL; - if (!channels) { ThrowByName(env, "java/io/IOException", "Channels can't be null"); return 0; } - channelClass = (*env)->FindClass(env, "org/eclipse/cdt/utils/spawner/Spawner$WinChannel"); + jclass channelClass = (*env)->FindClass(env, "org/eclipse/cdt/utils/spawner/Spawner$WinChannel"); if (!channelClass) { ThrowByName(env, "java/io/IOException", "Unable to find channel class"); return 0; } - channelConstructor = (*env)->GetMethodID(env, channelClass, "", "(J)V"); + jmethodID channelConstructor = (*env)->GetMethodID(env, channelClass, "", "(J)V"); if (!channelConstructor) { ThrowByName(env, "java/io/IOException", "Unable to find channel constructor"); return 0; } - nCmdLineLength = MAX_CMD_SIZE; - szCmdLine = (wchar_t *)malloc(nCmdLineLength * sizeof(wchar_t)); - szCmdLine[0] = _T('\0'); if ((HIBYTE(LOWORD(GetVersion()))) & 0x80) { ThrowByName(env, "java/io/IOException", "Does not support Windows 3.1/95/98/Me"); return 0; } - if (cmdarray == 0) { + if (!cmdarray) { ThrowByName(env, "java/lang/NullPointerException", "No command line specified"); return 0; } - ZeroMemory(stdHandles, sizeof(stdHandles)); + DWORD pid = GetCurrentProcessId(); // Create pipe names EnterCriticalSection(&cs); - swprintf(inPipeName, sizeof(inPipeName) / sizeof(inPipeName[0]), L"\\\\.\\pipe\\stdin%08i%010i", pid, nCounter); - swprintf(outPipeName, sizeof(outPipeName) / sizeof(outPipeName[0]), L"\\\\.\\pipe\\stdout%08i%010i", pid, nCounter); - swprintf(errPipeName, sizeof(errPipeName) / sizeof(errPipeName[0]), L"\\\\.\\pipe\\stderr%08i%010i", pid, nCounter); - nLocalCounter = nCounter; - ++nCounter; + int nLocalCounter = nCounter++; LeaveCriticalSection(&cs); - if ((INVALID_HANDLE_VALUE == (stdHandles[0] = CreateNamedPipeW( - inPipeName, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL))) || - (INVALID_HANDLE_VALUE == - (stdHandles[1] = CreateNamedPipeW(outPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL))) || - (INVALID_HANDLE_VALUE == - (stdHandles[2] = CreateNamedPipeW(errPipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - PIPE_SIZE, PIPE_SIZE, PIPE_TIMEOUT, NULL)))) { - CloseHandle(stdHandles[0]); - CloseHandle(stdHandles[1]); - CloseHandle(stdHandles[2]); + HANDLE stdHandles[] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + if (!createStandardNamedPipe(&stdHandles[0], STD_INPUT_HANDLE, pid, nLocalCounter) || + !createStandardNamedPipe(&stdHandles[1], STD_OUTPUT_HANDLE, pid, nLocalCounter) || + !createStandardNamedPipe(&stdHandles[2], STD_ERROR_HANDLE, pid, nLocalCounter)) { + CLOSE_HANDLES(stdHandles); ThrowByName(env, "java/io/IOException", "CreatePipe"); return 0; } - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Opened pipes: %s, %s, %s\n", inPipeName, outPipeName, errPipeName); - } - - nCmdTokens = (*env)->GetArrayLength(env, cmdarray); - nEnvVars = (*env)->GetArrayLength(env, envp); - - pCurProcInfo = createProcInfo(); - + pProcInfo_t pCurProcInfo = createProcInfo(); if (!pCurProcInfo) { + CLOSE_HANDLES(stdHandles); ThrowByName(env, "java/io/IOException", "Too many processes"); return 0; } - // Construct starter's command line - swprintf(eventBreakName, sizeof(eventBreakName) / sizeof(eventBreakName[0]), L"SABreak%04x%08x", pid, - nLocalCounter); - swprintf(eventWaitName, sizeof(eventWaitName) / sizeof(eventWaitName[0]), L"SAWait%04x%08x", pid, nLocalCounter); - swprintf(eventTerminateName, sizeof(eventTerminateName) / sizeof(eventTerminateName[0]), L"SATerm%04x%08x", pid, - nLocalCounter); - swprintf(eventKillName, sizeof(eventKillName) / sizeof(eventKillName[0]), L"SAKill%04x%08x", pid, nLocalCounter); - swprintf(eventCtrlcName, sizeof(eventCtrlcName) / sizeof(eventCtrlcName[0]), L"SACtrlc%04x%08x", pid, - nLocalCounter); - - pCurProcInfo->eventBreak = CreateEventW(NULL, FALSE, FALSE, eventBreakName); - if (!pCurProcInfo->eventBreak || GetLastError() == ERROR_ALREADY_EXISTS) { + // Create events + if (!createNamedEvent(&pCurProcInfo->eventBreak, FALSE, L"SABreak", pid, nLocalCounter) || + !createNamedEvent(&pCurProcInfo->eventWait, TRUE, L"SAWait", pid, nLocalCounter) || + !createNamedEvent(&pCurProcInfo->eventTerminate, FALSE, L"SATerm", pid, nLocalCounter) || + !createNamedEvent(&pCurProcInfo->eventKill, FALSE, L"SAKill", pid, nLocalCounter) || + !createNamedEvent(&pCurProcInfo->eventCtrlc, FALSE, L"SACtrlc", pid, nLocalCounter)) { + cleanUpProcBlock(pCurProcInfo); + CLOSE_HANDLES(stdHandles); ThrowByName(env, "java/io/IOException", "Cannot create event"); return 0; } - pCurProcInfo->eventWait = CreateEventW(NULL, TRUE, FALSE, eventWaitName); - pCurProcInfo->eventTerminate = CreateEventW(NULL, FALSE, FALSE, eventTerminateName); - pCurProcInfo->eventKill = CreateEventW(NULL, FALSE, FALSE, eventKillName); - pCurProcInfo->eventCtrlc = CreateEventW(NULL, FALSE, FALSE, eventCtrlcName); - - swprintf(szCmdLine, nCmdLineLength, L"\"%sstarter.exe\" %i %i %s %s %s %s %s ", path, pid, nLocalCounter, - eventBreakName, eventWaitName, eventTerminateName, eventKillName, eventCtrlcName); - nPos = wcslen(szCmdLine); // Prepare command line - for (int i = 0; i < nCmdTokens; ++i) { - jstring item = (jstring)(*env)->GetObjectArrayElement(env, cmdarray, i); - jsize len = (*env)->GetStringLength(env, item); - int nCpyLen; - const wchar_t *str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); - if (str) { - int requiredSize = nPos + len + 2; - if (requiredSize > 32 * 1024) { - ThrowByName(env, "java/io/IOException", "Command line too long"); - return 0; - } - ensureSize(&szCmdLine, &nCmdLineLength, requiredSize); - if (!szCmdLine) { - ThrowByName(env, "java/io/IOException", "Not enough memory"); - return 0; - } - - if (0 > (nCpyLen = copyTo(szCmdLine + nPos, str, len, nCmdLineLength - nPos))) { - ThrowByName(env, "java/io/IOException", "Command line too long"); - return 0; - } - nPos += nCpyLen; - szCmdLine[nPos] = _T(' '); - ++nPos; - (*env)->ReleaseStringChars(env, item, (const jchar *)str); - } - } - szCmdLine[nPos] = _T('\0'); - - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"There are %i environment variables \n", nEnvVars); + wchar_t *cmdLine = NULL; + if (!createCommandLine(env, cmdarray, &cmdLine, L"\"%sstarter.exe\" %i %i %s %s %s %s %s ", path, // + pid, // + nLocalCounter, // + pCurProcInfo->eventBreak.name, // + pCurProcInfo->eventWait.name, // + pCurProcInfo->eventTerminate.name, // + pCurProcInfo->eventKill.name, // + pCurProcInfo->eventCtrlc.name)) { + // Exception already thrown, just clean up + cleanUpProcBlock(pCurProcInfo); + CLOSE_HANDLES(stdHandles); + return 0; } // Prepare environment block - if (nEnvVars > 0) { - nPos = 0; - szEnvBlock = (wchar_t *)malloc(nBlkSize * sizeof(wchar_t)); - for (int i = 0; i < nEnvVars; ++i) { - jstring item = (jstring)(*env)->GetObjectArrayElement(env, envp, i); - jsize len = (*env)->GetStringLength(env, item); - const wchar_t *str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); - if (str) { - while ((nBlkSize - nPos) <= (len + 2)) { // +2 for two '\0' - nBlkSize += MAX_ENV_SIZE; - szEnvBlock = (wchar_t *)realloc(szEnvBlock, nBlkSize * sizeof(wchar_t)); - if (!szEnvBlock) { - ThrowByName(env, "java/io/IOException", "Not enough memory"); - return 0; - } - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Realloc environment block; new length is %i \n", nBlkSize); - } - } - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"%s\n", str); - } - wcsncpy(szEnvBlock + nPos, str, len); - nPos += len; - szEnvBlock[nPos] = _T('\0'); - ++nPos; - (*env)->ReleaseStringChars(env, item, (const jchar *)str); - } - } - szEnvBlock[nPos] = _T('\0'); + wchar_t *envBlock = NULL; + if (!createEnvironmentBlock(env, envp, &envBlock)) { + // Exception already thrown, just clean up + free(cmdLine); + cleanUpProcBlock(pCurProcInfo); + CLOSE_HANDLES(stdHandles); + return 0; } + wchar_t *cwd = NULL; if (dir) { const jchar *str = (*env)->GetStringChars(env, dir, NULL); if (str) { @@ -339,68 +431,54 @@ extern "C" } } + STARTUPINFOW si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; // Processes in the Process Group are hidden - SetHandleInformation(stdHandles[0], HANDLE_FLAG_INHERIT, FALSE); - SetHandleInformation(stdHandles[1], HANDLE_FLAG_INHERIT, FALSE); - SetHandleInformation(stdHandles[2], HANDLE_FLAG_INHERIT, FALSE); - - flags = CREATE_NEW_CONSOLE; + DWORD flags = CREATE_NEW_CONSOLE; flags |= CREATE_NO_WINDOW; flags |= CREATE_UNICODE_ENVIRONMENT; if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(szCmdLine); + cdtTrace(cmdLine); } + // launches starter; we need it to create another console group to correctly process // emulation of SYSint signal (Ctrl-C) - ret = CreateProcessW(0, /* executable name */ - szCmdLine, /* command line */ - 0, /* process security attribute */ - 0, /* thread security attribute */ - FALSE, /* inherits system handles */ - flags, /* normal attached process */ - szEnvBlock, /* environment block */ - cwd, /* change to the new current directory */ - &si, /* (in) startup information */ - &pi); /* (out) process information */ + PROCESS_INFORMATION pi = {0}; + int ret = CreateProcessW(NULL, /* executable name */ + cmdLine, /* command line */ + 0, /* process security attribute */ + 0, /* thread security attribute */ + FALSE, /* inherits system handles */ + flags, /* normal attached process */ + envBlock, /* environment block */ + cwd, /* change to the new current directory */ + &si, /* (in) startup information */ + &pi); /* (out) process information */ free(cwd); - free(szEnvBlock); - free(szCmdLine); + free(envBlock); + free(cmdLine); - if (!ret) { // Launching error - char *lpMsgBuf; - CloseHandle(stdHandles[0]); - CloseHandle(stdHandles[1]); - CloseHandle(stdHandles[2]); - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (char *)&lpMsgBuf, 0, NULL); - ThrowByName(env, "java/io/IOException", lpMsgBuf); - // Free the buffer. - LocalFree(lpMsgBuf); - cleanUpProcBlock(pCurProcInfo); - ret = -1; - } else { + if (ret) { HANDLE h[2]; - int what; EnterCriticalSection(&cs); pCurProcInfo->pid = pi.dwProcessId; - h[0] = pCurProcInfo->eventWait; + h[0] = pCurProcInfo->eventWait.handle; h[1] = pi.hProcess; - what = WaitForMultipleObjects(2, h, FALSE, INFINITE); + int what = WaitForMultipleObjects(2, h, FALSE, INFINITE); if (what != WAIT_OBJECT_0) { // CreateProcess failed if (isTraceEnabled(CDT_TRACE_MONITOR)) { cdtTrace(L"Process %i failed\n", pi.dwProcessId); } cleanUpProcBlock(pCurProcInfo); + CLOSE_HANDLES(stdHandles); ThrowByName(env, "java/io/IOException", "Launching failed"); if (isTraceEnabled(CDT_TRACE_MONITOR)) { cdtTrace(L"Process failed\n"); @@ -416,7 +494,7 @@ extern "C" // do the cleanup so launch the according thread // create a copy of the PROCESS_INFORMATION as this might get destroyed - piCopy = (PROCESS_INFORMATION *)malloc(sizeof(PROCESS_INFORMATION)); + PROCESS_INFORMATION *piCopy = (PROCESS_INFORMATION *)malloc(sizeof(PROCESS_INFORMATION)); memcpy(piCopy, &pi, sizeof(PROCESS_INFORMATION)); _beginthread(waitProcTermination, 0, (void *)piCopy); @@ -425,6 +503,17 @@ extern "C" } } LeaveCriticalSection(&cs); + } else { // Launching error + char *lpMsgBuf; + CLOSE_HANDLES(stdHandles); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (char *)&lpMsgBuf, 0, NULL); + ThrowByName(env, "java/io/IOException", lpMsgBuf); + // Free the buffer. + LocalFree(lpMsgBuf); + cleanUpProcBlock(pCurProcInfo); + ret = -1; } CloseHandle(pi.hThread); @@ -446,94 +535,21 @@ extern "C" Java_org_eclipse_cdt_utils_spawner_Spawner_exec1(JNIEnv *env, jobject process, jobjectArray cmdarray, jobjectArray envp, jstring dir) { - SECURITY_ATTRIBUTES sa; - PROCESS_INFORMATION pi = {0}; - STARTUPINFOW si; - DWORD flags = 0; - wchar_t *cwd = NULL; - wchar_t *envBlk = NULL; - int ret = 0; - jsize nCmdTokens = 0; - jsize nEnvVars = 0; - int i; - int nPos; - int nCmdLineLength = 0; - wchar_t *szCmdLine = 0; - int nBlkSize = MAX_ENV_SIZE; - wchar_t *szEnvBlock = NULL; - - nCmdLineLength = MAX_CMD_SIZE; - szCmdLine = (wchar_t *)malloc(nCmdLineLength * sizeof(wchar_t)); - szCmdLine[0] = 0; - - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = 0; - sa.bInheritHandle = TRUE; - - nCmdTokens = (*env)->GetArrayLength(env, cmdarray); - nEnvVars = (*env)->GetArrayLength(env, envp); - - nPos = 0; - // Prepare command line - for (i = 0; i < nCmdTokens; ++i) { - jstring item = (jstring)(*env)->GetObjectArrayElement(env, cmdarray, i); - jsize len = (*env)->GetStringLength(env, item); - int nCpyLen; - const wchar_t *str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); - if (str) { - int requiredSize = nPos + len + 2; - if (requiredSize > 32 * 1024) { - ThrowByName(env, "java/io/IOException", "Command line too long"); - return 0; - } - ensureSize(&szCmdLine, &nCmdLineLength, requiredSize); - if (!szCmdLine) { - ThrowByName(env, "java/io/IOException", "Not enough memory"); - return 0; - } - - if (0 > (nCpyLen = copyTo(szCmdLine + nPos, str, len, nCmdLineLength - nPos))) { - ThrowByName(env, "java/io/Exception", "Command line too long"); - return 0; - } - nPos += nCpyLen; - szCmdLine[nPos] = _T(' '); - ++nPos; - (*env)->ReleaseStringChars(env, item, (const jchar *)str); - } + wchar_t *cmdLine = NULL; + if (!createCommandLine(env, cmdarray, &cmdLine, L"")) { + // Exception already thrown + return 0; } - szCmdLine[nPos] = _T('\0'); - // Prepare environment block - if (nEnvVars > 0) { - szEnvBlock = (wchar_t *)malloc(nBlkSize * sizeof(wchar_t)); - nPos = 0; - for (i = 0; i < nEnvVars; ++i) { - jstring item = (jstring)(*env)->GetObjectArrayElement(env, envp, i); - jsize len = (*env)->GetStringLength(env, item); - const wchar_t *str = (const wchar_t *)(*env)->GetStringChars(env, item, 0); - if (str) { - while ((nBlkSize - nPos) <= (len + 2)) { // +2 for two '\0' - nBlkSize += MAX_ENV_SIZE; - szEnvBlock = (wchar_t *)realloc(szEnvBlock, nBlkSize * sizeof(wchar_t)); - if (!szEnvBlock) { - ThrowByName(env, "java/io/Exception", "Not enough memory"); - return 0; - } - } - wcsncpy(szEnvBlock + nPos, str, len); - nPos += len; - szEnvBlock[nPos] = _T('\0'); - ++nPos; - (*env)->ReleaseStringChars(env, item, (const jchar *)str); - } - } - szEnvBlock[nPos] = _T('\0'); - envBlk = szEnvBlock; + wchar_t *envBlock = NULL; + if (!createEnvironmentBlock(env, envp, &envBlock)) { + free(cmdLine); + return 0; } + wchar_t *cwd = NULL; if (dir) { const jchar *str = (*env)->GetStringChars(env, dir, NULL); if (str) { @@ -542,27 +558,35 @@ extern "C" } } + STARTUPINFOW si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); - flags = CREATE_NEW_CONSOLE; + DWORD flags = CREATE_NEW_CONSOLE; flags |= CREATE_UNICODE_ENVIRONMENT; - ret = CreateProcessW(0, /* executable name */ - szCmdLine, /* command line */ - 0, /* process security attribute */ - 0, /* thread security attribute */ - TRUE, /* inherits system handles */ - flags, /* normal attached process */ - envBlk, /* environment block */ - cwd, /* change to the new current directory */ - &si, /* (in) startup information */ - &pi); /* (out) process information */ + + PROCESS_INFORMATION pi = {0}; + int ret = CreateProcessW(NULL, /* executable name */ + cmdLine, /* command line */ + 0, /* process security attribute */ + 0, /* thread security attribute */ + TRUE, /* inherits system handles */ + flags, /* normal attached process */ + envBlock, /* environment block */ + cwd, /* change to the new current directory */ + &si, /* (in) startup information */ + &pi); /* (out) process information */ free(cwd); - free(szEnvBlock); - free(szCmdLine); + free(cmdLine); + free(envBlock); - if (!ret) { // error + if (ret) { + // Clean-up + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + ret = (long)pi.dwProcessId; // hProcess; + } else { // error char *lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, @@ -572,11 +596,6 @@ extern "C" // Free the buffer. LocalFree(lpMsgBuf); ret = -1; - } else { - // Clean-up - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - ret = (long)pi.dwProcessId; // hProcess; } return ret; @@ -628,7 +647,7 @@ extern "C" if (isTraceEnabled(CDT_TRACE_MONITOR)) { cdtTrace(L"Spawner received TERM signal for process %i\n", pCurProcInfo->pid); } - SetEvent(pCurProcInfo->eventTerminate); + SetEvent(pCurProcInfo->eventTerminate.handle); if (isTraceEnabled(CDT_TRACE_MONITOR)) { cdtTrace(L"Spawner signaled TERM event\n"); } @@ -639,21 +658,21 @@ extern "C" if (isTraceEnabled(CDT_TRACE_MONITOR)) { cdtTrace(L"Spawner received KILL signal for process %i\n", pCurProcInfo->pid); } - SetEvent(pCurProcInfo->eventKill); + SetEvent(pCurProcInfo->eventKill.handle); if (isTraceEnabled(CDT_TRACE_MONITOR)) { cdtTrace(L"Spawner signaled KILL event\n"); } ret = 0; break; case SIG_INT: - ResetEvent(pCurProcInfo->eventWait); - SetEvent(pCurProcInfo->eventBreak); - ret = (WaitForSingleObject(pCurProcInfo->eventWait, 100) == WAIT_OBJECT_0); + ResetEvent(pCurProcInfo->eventWait.handle); + SetEvent(pCurProcInfo->eventBreak.handle); + ret = (WaitForSingleObject(pCurProcInfo->eventWait.handle, 100) == WAIT_OBJECT_0); break; case CTRLC: - ResetEvent(pCurProcInfo->eventWait); - SetEvent(pCurProcInfo->eventCtrlc); - ret = (WaitForSingleObject(pCurProcInfo->eventWait, 100) == WAIT_OBJECT_0); + ResetEvent(pCurProcInfo->eventWait.handle); + SetEvent(pCurProcInfo->eventCtrlc.handle); + ret = (WaitForSingleObject(pCurProcInfo->eventWait.handle, 100) == WAIT_OBJECT_0); break; default: break; @@ -772,27 +791,20 @@ pProcInfo_t findProcInfo(int uid) { // pCurProcInfo - pointer to descriptor to clean up // Return : no void cleanUpProcBlock(pProcInfo_t pCurProcInfo) { - if (0 != pCurProcInfo->eventBreak) { - CloseHandle(pCurProcInfo->eventBreak); - pCurProcInfo->eventBreak = 0; - } - if (0 != pCurProcInfo->eventWait) { - CloseHandle(pCurProcInfo->eventWait); - pCurProcInfo->eventWait = 0; - } - if (0 != pCurProcInfo->eventTerminate) { - CloseHandle(pCurProcInfo->eventTerminate); - pCurProcInfo->eventTerminate = 0; - } + EventInfo_t *eventInfos[] = { + &pCurProcInfo->eventBreak, &pCurProcInfo->eventWait, &pCurProcInfo->eventTerminate, + &pCurProcInfo->eventKill, &pCurProcInfo->eventCtrlc, + }; - if (0 != pCurProcInfo->eventKill) { - CloseHandle(pCurProcInfo->eventKill); - pCurProcInfo->eventKill = 0; - } + for (int i = 0; i < sizeof(eventInfos) / sizeof(eventInfos[0]); i++) { + EventInfo_t *p = eventInfos[i]; + if (p->handle) { + CloseHandle(p->handle); + p->handle = NULL; + } - if (0 != pCurProcInfo->eventCtrlc) { - CloseHandle(pCurProcInfo->eventCtrlc); - pCurProcInfo->eventCtrlc = 0; + free(p->name); + p->name = NULL; } pCurProcInfo->pid = 0; @@ -822,69 +834,3 @@ void _cdecl waitProcTermination(void *pv) { free(pi); } - -///////////////////////////////////////////////////////////////////////////////////// -// Use this utility program to process correctly quotation marks in the command line -// Arguments: -// target - string to copy to -// source - string to copy from -// cpyLength - copy length -// availSpace - size of the target buffer -// Return :number of bytes used in target, or -1 in case of error -///////////////////////////////////////////////////////////////////////////////////// -int copyTo(wchar_t *target, const wchar_t *source, int cpyLength, int availSpace) { - bool bSlash = false; - int i = 0, j = 0; - - enum { QUOTATION_DO, QUOTATION_DONE, QUOTATION_NONE } nQuotationMode = QUOTATION_DO; - - if (availSpace <= cpyLength) { // = to reserve space for final '\0' - return -1; - } - - if ((_T('\"') == *source) && (_T('\"') == *(source + cpyLength - 1))) { - nQuotationMode = QUOTATION_DONE; - } else if (wcschr(source, _T(' '))) { - // Needs to be quoted - nQuotationMode = QUOTATION_DO; - *target = _T('\"'); - ++j; - } else { - // No reason to quote term because it doesn't have embedded spaces - nQuotationMode = QUOTATION_NONE; - } - - for (; i < cpyLength; ++i, ++j) { - if (source[i] == _T('\\')) { - bSlash = true; - } else { - // Don't escape embracing quotation marks - if ((source[i] == _T('\"')) && - !((nQuotationMode == QUOTATION_DONE) && ((i == 0) || (i == (cpyLength - 1))))) { - if (!bSlash) { // If still not escaped - if (j == availSpace) { - return -1; - } - target[j] = _T('\\'); - ++j; - } - } - bSlash = false; - } - - if (j == availSpace) { - return -1; - } - target[j] = source[i]; - } - - if (nQuotationMode == QUOTATION_DO) { - if (j == availSpace) { - return -1; - } - target[j] = _T('\"'); - ++j; - } - - return j; -} diff --git a/core/org.eclipse.cdt.core.native/native_src/win/starter.c b/core/org.eclipse.cdt.core.native/native_src/win/starter.c index 85a9748813d..1622f3a0cda 100644 --- a/core/org.eclipse.cdt.core.native/native_src/win/starter.c +++ b/core/org.eclipse.cdt.core.native/native_src/win/starter.c @@ -120,20 +120,130 @@ bool runCygwinCommand(wchar_t *command) { return false; } -void ensureSize(wchar_t **ptr, int *psize, int requiredLength) { - int size = *psize; - if (requiredLength > size) { - size = 2 * size; - if (size < requiredLength) { - size = requiredLength; +static bool openNamedPipeAsStdHandle(HANDLE *handle, DWORD stdHandle, int parentPid, int counter, + SECURITY_ATTRIBUTES *sa) { + wchar_t pipeName[PIPE_NAME_LENGTH]; + DWORD dwDesiredAccess; + DWORD dwShareMode; + + switch (stdHandle) { + case STD_INPUT_HANDLE: + BUILD_PIPE_NAME(pipeName, L"stdin", parentPid, counter); + dwDesiredAccess = GENERIC_READ; + dwShareMode = FILE_SHARE_READ; + break; + case STD_OUTPUT_HANDLE: + BUILD_PIPE_NAME(pipeName, L"stdout", parentPid, counter); + dwDesiredAccess = GENERIC_WRITE; + dwShareMode = FILE_SHARE_WRITE; + break; + case STD_ERROR_HANDLE: + BUILD_PIPE_NAME(pipeName, L"stderr", parentPid, counter); + dwDesiredAccess = GENERIC_WRITE; + dwShareMode = FILE_SHARE_WRITE; + break; + default: + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Invalid STD handle given %i", stdHandle); } - *ptr = (wchar_t *)realloc(*ptr, size * sizeof(wchar_t)); - if (*ptr) { - *psize = size; + return false; + } + + *handle = CreateFileW(pipeName, dwDesiredAccess, dwShareMode, NULL, OPEN_EXISTING, 0, sa); + if (INVALID_HANDLE_VALUE == *handle) { + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Failed to open pipe: %s -> %p\n", pipeName, handle); + } + return false; + } + + SetHandleInformation(*handle, HANDLE_FLAG_INHERIT, TRUE); + + if (!SetStdHandle(stdHandle, *handle)) { + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Failed to reassign standard stream to pipe %s: %i\n", pipeName, GetLastError()); + } + return false; + } + + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Successfully assigned pipe %s -> %p\n", pipeName, *handle); + } + + return true; +} + +bool createCommandLine(int argc, wchar_t **argv, wchar_t **cmdLine) { + int size = MAX_CMD_LINE_LENGTH; + wchar_t *buffer = (wchar_t *)malloc(size * sizeof(wchar_t)); + + if (!buffer) { + // malloc failed + cdtTrace(L"Not enough memory to build cmd line!\n"); + return false; + } + + int nPos = 0; + for (int i = 0; i < argc; ++i) { + wchar_t *str = *(argv + i); + int len = wcslen(str); + if (str) { + int required = nPos + len + 2; // 2 => space + \0 + if (required > 32 * 1024) { + free(buffer); + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Command line too long!\n"); + } + return false; + } + + while (1) { + // Ensure enough space in buffer + if (required > size) { + size *= 2; + if (size < required) { + size = required; + } + + wchar_t *tmp = (wchar_t *)realloc(buffer, size * sizeof(wchar_t)); + if (tmp) { + // realloc successful + buffer = tmp; + } else { + // Failed to realloc memory + free(buffer); + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Not enough memory to build cmd line!\n"); + } + return false; + } + } + + int nCpyLen = copyTo(buffer + nPos, (const wchar_t *)str, len, size - nPos); + if (nCpyLen < 0) { // Buffer too small + // Do a real count of number of chars required + required = nPos + copyTo(NULL, (const wchar_t *)str, len, INT_MAX) + 2; // 2 => space + \0 + continue; + } + + // Buffer was big enough. + nPos += nCpyLen; + break; + } + + buffer[nPos++] = _T(' '); + buffer[nPos] = _T('\0'); } else { - *psize = 0; + free(buffer); + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Invalid argument!\n"); + } + return false; } } + + *cmdLine = buffer; + return true; } int main() { @@ -143,45 +253,10 @@ int main() { // Make sure that we've been passed the right number of arguments if (argc < 8) { - wprintf(L"Usage: %s (four inheritable event handles) (CommandLineToSpawn)\n", argv[0]); + wprintf(L"Usage: %s (parent pid) (counter) (four inheritable event handles) (CommandLineToSpawn)\n", argv[0]); return 0; } - // Construct the full command line - int nCmdLineLength = MAX_CMD_LINE_LENGTH; - wchar_t *szCmdLine = (wchar_t *)malloc(nCmdLineLength * sizeof(wchar_t)); - szCmdLine[0] = 0; - int nPos = 0; - - for (int i = 8; i < argc; ++i) { - int nCpyLen; - int len = wcslen(argv[i]); - int requiredSize = nPos + len + 2; - if (requiredSize > 32 * 1024) { - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Command line too long!\n"); - } - return 0; - } - ensureSize(&szCmdLine, &nCmdLineLength, requiredSize); - if (!szCmdLine) { - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Not enough memory to build cmd line!\n"); - } - return 0; - } - if (0 > (nCpyLen = copyTo(szCmdLine + nPos, argv[i], len, nCmdLineLength - nPos))) { - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Not enough space to build command line\n"); - } - return 0; - } - nPos += nCpyLen; - szCmdLine[nPos] = _T(' '); - ++nPos; - } - szCmdLine[nPos] = _T('\0'); - STARTUPINFOW si = {sizeof(si)}; PROCESS_INFORMATION pi = {0}; DWORD dwExitCode = 0; @@ -199,56 +274,22 @@ int main() { int parentPid = wcstol(argv[1], NULL, 10); int nCounter = wcstol(argv[2], NULL, 10); - wchar_t inPipeName[PIPE_NAME_LENGTH]; - wchar_t outPipeName[PIPE_NAME_LENGTH]; - wchar_t errPipeName[PIPE_NAME_LENGTH]; - swprintf(inPipeName, sizeof(inPipeName) / sizeof(inPipeName[0]), L"\\\\.\\pipe\\stdin%08i%010i", parentPid, - nCounter); - swprintf(outPipeName, sizeof(outPipeName) / sizeof(outPipeName[0]), L"\\\\.\\pipe\\stdout%08i%010i", parentPid, - nCounter); - swprintf(errPipeName, sizeof(errPipeName) / sizeof(errPipeName[0]), L"\\\\.\\pipe\\stderr%08i%010i", parentPid, - nCounter); - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Pipes: %s, %s, %s\n", inPipeName, outPipeName, errPipeName); - } - - HANDLE stdHandles[3]; + HANDLE stdHandles[] = { + INVALID_HANDLE_VALUE, // STDIN + INVALID_HANDLE_VALUE, // STDOUT + INVALID_HANDLE_VALUE // STDERR + }; SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; - if ((INVALID_HANDLE_VALUE == - (stdHandles[0] = CreateFileW(inPipeName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, &sa))) || - (INVALID_HANDLE_VALUE == - (stdHandles[1] = CreateFileW(outPipeName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, &sa))) || - (INVALID_HANDLE_VALUE == - (stdHandles[2] = CreateFileW(errPipeName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, &sa)))) { - - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Failed to open pipe %i, %i, %i: %i\n", stdHandles[0], stdHandles[1], stdHandles[2], - GetLastError()); - } - CloseHandle(stdHandles[0]); - CloseHandle(stdHandles[1]); - CloseHandle(stdHandles[2]); - return -1; - } - SetHandleInformation(stdHandles[0], HANDLE_FLAG_INHERIT, TRUE); - SetHandleInformation(stdHandles[1], HANDLE_FLAG_INHERIT, TRUE); - SetHandleInformation(stdHandles[2], HANDLE_FLAG_INHERIT, TRUE); - - if (!SetStdHandle(STD_INPUT_HANDLE, stdHandles[0]) || !SetStdHandle(STD_OUTPUT_HANDLE, stdHandles[1]) || - !SetStdHandle(STD_ERROR_HANDLE, stdHandles[2])) { - - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Failed to reassign standard streams: %i\n", GetLastError()); - } - CloseHandle(stdHandles[0]); - CloseHandle(stdHandles[1]); - CloseHandle(stdHandles[2]); + if (!openNamedPipeAsStdHandle(&stdHandles[0], STD_INPUT_HANDLE, parentPid, nCounter, &sa) || + !openNamedPipeAsStdHandle(&stdHandles[1], STD_OUTPUT_HANDLE, parentPid, nCounter, &sa) || + !openNamedPipeAsStdHandle(&stdHandles[2], STD_ERROR_HANDLE, parentPid, nCounter, &sa)) { + CLOSE_HANDLES(stdHandles); return -1; } @@ -270,9 +311,7 @@ int main() { cdtTrace(L"Cannot Read Environment\n"); } } - if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Starting: %s\n", szCmdLine); - } + // Create job object HANDLE hJob = CreateJobObject(NULL, NULL); if (hJob) { @@ -292,22 +331,34 @@ int main() { cdtTrace(L"Cannot create job object\n"); DisplayErrorMessage(); } + + // Construct the full command line + wchar_t *cmdLine = NULL; + if (!createCommandLine(argc - 8, &argv[8], &cmdLine)) { + return 0; + } + + if (isTraceEnabled(CDT_TRACE_MONITOR)) { + cdtTrace(L"Starting: %s\n", cmdLine); + } + // Spawn the other processes as part of this Process Group // If this process is already part of a job, the flag CREATE_BREAKAWAY_FROM_JOB // makes the child process detach from the job, such that we can assign it // to our own job object. - BOOL f = CreateProcessW(NULL, szCmdLine, NULL, NULL, TRUE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi); + BOOL f = CreateProcessW(NULL, cmdLine, NULL, NULL, TRUE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi); // If breaking away from job is not permitted, retry without breakaway flag if (!f) { - f = CreateProcessW(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); + f = CreateProcessW(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); } // We don't need them any more - CloseHandle(stdHandles[0]); - CloseHandle(stdHandles[1]); - CloseHandle(stdHandles[2]); + CLOSE_HANDLES(stdHandles); if (f) { + free(cmdLine); + cmdLine = NULL; + if (isTraceEnabled(CDT_TRACE_MONITOR)) { cdtTrace(L"Process %i started\n", pi.dwProcessId); } @@ -406,91 +457,18 @@ int main() { } } } else if (isTraceEnabled(CDT_TRACE_MONITOR)) { - cdtTrace(L"Cannot start: %s\n", szCmdLine); + cdtTrace(L"Cannot start: %s\n", cmdLine); + free(cmdLine); DisplayErrorMessage(); } - free(szCmdLine); - CloseHandle(waitEvent); - CloseHandle(h[0]); - CloseHandle(h[1]); - CloseHandle(h[2]); - CloseHandle(h[3]); - CloseHandle(h[4]); + CLOSE_HANDLES(h); return dwExitCode; } -///////////////////////////////////////////////////////////////////////////////////// -// Use this utility program to process correctly quotation marks in the command line -// Arguments: -// target - string to copy to -// source - string to copy from -// cpyLength - copy length -// availSpace - size of the target buffer -// Return :number of bytes used in target, or -1 in case of error -///////////////////////////////////////////////////////////////////////////////////// -int copyTo(wchar_t *target, const wchar_t *source, int cpyLength, int availSpace) { - bool bSlash = false; - int i = 0, j = 0; - - enum { QUOTATION_DO, QUOTATION_DONE, QUOTATION_NONE } nQuotationMode = QUOTATION_DO; - - if (availSpace <= cpyLength) { // = to reserve space for '\0' - return -1; - } - - if ((_T('\"') == *source) && (_T('\"') == *(source + cpyLength - 1))) { - // Already done - nQuotationMode = QUOTATION_DONE; - } else if (wcschr(source, _T(' '))) { - // Needs to be quotated - nQuotationMode = QUOTATION_DO; - *target = _T('\"'); - ++j; - } else { - // No reason to quotate term because it doesn't have embedded spaces - nQuotationMode = QUOTATION_NONE; - } - - for (; i < cpyLength; ++i, ++j) { - if (source[i] == _T('\\')) { - bSlash = true; - } else { - // Don't escape embracing quotation marks - if ((source[i] == _T('\"')) && - !((nQuotationMode == QUOTATION_DONE) && ((i == 0) || (i == (cpyLength - 1))))) { - if (!bSlash) { - if (j == availSpace) { - return -1; - } - target[j] = _T('\\'); - ++j; - } - bSlash = false; - } else { - bSlash = false; - } - } - - if (j == availSpace) { - return -1; - } - target[j] = source[i]; - } - - if (nQuotationMode == QUOTATION_DO) { - if (j == availSpace) { - return -1; - } - target[j] = _T('\"'); - ++j; - } - return j; -} - void DisplayErrorMessage() { wchar_t *lpMsgBuf; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, diff --git a/core/org.eclipse.cdt.core.native/native_src/win/util.c b/core/org.eclipse.cdt.core.native/native_src/win/util.c index 3191751c972..46b6c90b051 100644 --- a/core/org.eclipse.cdt.core.native/native_src/win/util.c +++ b/core/org.eclipse.cdt.core.native/native_src/win/util.c @@ -15,6 +15,7 @@ #include "util.h" #include +#include bool isTraceEnabled(const TraceKind_t traceKind) { static bool initialized = false; @@ -73,3 +74,66 @@ void cdtTrace(const wchar_t *fmt, ...) { // Clean up free(buffer); } + +int copyTo(wchar_t *target, const wchar_t *source, int cpyLength, int availSpace) { + bool bSlash = false; + int i = 0, j = 0; + + enum { QUOTATION_DO, QUOTATION_DONE, QUOTATION_NONE } nQuotationMode = QUOTATION_DO; + + if (availSpace <= cpyLength) { // = to reserve space for final '\0' + return -1; + } + + if ((_T('\"') == *source) && (_T('\"') == *(source + cpyLength - 1))) { + nQuotationMode = QUOTATION_DONE; + } else if (wcschr(source, _T(' '))) { + // Needs to be quoted + nQuotationMode = QUOTATION_DO; + if (target) { + *target = _T('\"'); + } + ++j; + } else { + // No reason to quote term because it doesn't have embedded spaces + nQuotationMode = QUOTATION_NONE; + } + + for (; i < cpyLength; ++i, ++j) { + if (source[i] == _T('\\')) { + bSlash = true; + } else { + // Don't escape embracing quotation marks + if ((source[i] == _T('\"')) && + !((nQuotationMode == QUOTATION_DONE) && ((i == 0) || (i == (cpyLength - 1))))) { + if (!bSlash) { // If still not escaped + if (j == availSpace) { + return -1; + } + target[j] = _T('\\'); + ++j; + } + } + bSlash = false; + } + + if (j == availSpace) { + return -1; + } + if (target) { + target[j] = source[i]; + } + } + + if (nQuotationMode == QUOTATION_DO) { + if (j == availSpace) { + return -1; + } + if (target) { + target[j] = _T('\"'); + } + ++j; + } + + return j; +} diff --git a/core/org.eclipse.cdt.core.native/native_src/win/util.h b/core/org.eclipse.cdt.core.native/native_src/win/util.h index d5991da771b..56a43460bdb 100644 --- a/core/org.eclipse.cdt.core.native/native_src/win/util.h +++ b/core/org.eclipse.cdt.core.native/native_src/win/util.h @@ -23,4 +23,21 @@ typedef enum { CDT_TRACE_MONITOR, CDT_TRACE_MONITOR_DETAILS, CDT_TRACE_READ_REPO bool isTraceEnabled(const TraceKind_t traceKind); void cdtTrace(const wchar_t *fmt, ...); +#define BUILD_PIPE_NAME(pipe, name, pid, counter) \ + do { \ + swprintf(pipe, sizeof(pipe) / sizeof(pipe[0]), L"\\\\.\\pipe\\%s%08i%010i", name, pid, counter); \ + } while (0) + +#define CLOSE_HANDLES(handles) \ + do { \ + for (int i = 0; i < sizeof(handles) / sizeof(handles[0]); i++) { \ + if (INVALID_HANDLE_VALUE != handles[i]) { \ + CloseHandle(handles[i]); \ + handles[i] = INVALID_HANDLE_VALUE; \ + } \ + } \ + } while (0) + +int copyTo(wchar_t *target, const wchar_t *source, int cpyLength, int availSpace); + #endif /* UTIL_H */ diff --git a/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/pty.dll b/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/pty.dll index 5751bb16ad0..21a533ee5c1 100755 Binary files a/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/pty.dll and b/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/pty.dll differ diff --git a/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/spawner.dll b/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/spawner.dll index ae55557671a..d73a80aadef 100755 Binary files a/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/spawner.dll and b/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/spawner.dll differ diff --git a/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/starter.exe b/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/starter.exe index d86256e27d1..2a1bc90e617 100755 Binary files a/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/starter.exe and b/core/org.eclipse.cdt.core.win32.x86_64/os/win32/x86_64/starter.exe differ