1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-21 21:52:10 +02:00

Bug 568079: Rework spawner to avoid memory leaks on Win32

Change-Id: I1253351d47d52e848867d7f9df61a66f9bd82d41
Signed-off-by: Torbjörn Svensson <azoff@svenskalinuxforeningen.se>
This commit is contained in:
Torbjörn Svensson 2020-11-01 20:35:18 +01:00
parent c598eedffa
commit 35530c50ef
7 changed files with 578 additions and 573 deletions

View file

@ -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, "<init>", "(J)V");
jmethodID channelConstructor = (*env)->GetMethodID(env, channelClass, "<init>", "(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;
}

View file

@ -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,

View file

@ -15,6 +15,7 @@
#include "util.h"
#include <stdio.h>
#include <tchar.h>
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;
}

View file

@ -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 */