/* Smart recycling of pipe handles in [pHolder]. For the failed create process attempts, both ends of pipe need to be released. The [complete] has the [TRUE] value in the failed attempt. */ static void releaseHolder(BOOL complete, STDHOLDER *pHolder) { closeSafely(pHolder->pipe[pHolder->offset]); if (complete) { /* Error occur, close this process pipe end */ closeSafely(pHolder->pipe[OPPOSITE_END(pHolder->offset)]); } }
JNIEXPORT jlong JNICALL Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, jstring cmd, jstring envBlock, jstring dir, jlongArray stdHandles, jboolean redirectErrorStream) { HANDLE inRead = INVALID_HANDLE_VALUE; HANDLE inWrite = INVALID_HANDLE_VALUE; HANDLE outRead = INVALID_HANDLE_VALUE; HANDLE outWrite = INVALID_HANDLE_VALUE; HANDLE errRead = INVALID_HANDLE_VALUE; HANDLE errWrite = INVALID_HANDLE_VALUE; SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION pi; STARTUPINFO si; LPTSTR pcmd = NULL; LPCTSTR pdir = NULL; LPVOID penvBlock = NULL; jlong *handles = NULL; jlong ret = 0; OSVERSIONINFO ver; jboolean onNT = JNI_FALSE; DWORD processFlag; ver.dwOSVersionInfoSize = sizeof(ver); GetVersionEx(&ver); if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) onNT = JNI_TRUE; assert(cmd != NULL); pcmd = (LPTSTR) JNU_GetStringPlatformChars(env, cmd, NULL); if (pcmd == NULL) goto Catch; if (dir != 0) { pdir = (LPCTSTR) JNU_GetStringPlatformChars(env, dir, NULL); if (pdir == NULL) goto Catch; pdir = (LPCTSTR) JVM_NativePath((char *)pdir); } if (envBlock != NULL) { penvBlock = onNT ? (LPVOID) ((*env)->GetStringChars(env, envBlock, NULL)) : (LPVOID) JNU_GetStringPlatformChars(env, envBlock, NULL); if (penvBlock == NULL) goto Catch; } assert(stdHandles != NULL); handles = (*env)->GetLongArrayElements(env, stdHandles, NULL); if (handles == NULL) goto Catch; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = 0; sa.bInheritHandle = TRUE; if (handles[0] != (jlong) -1) { si.hStdInput = (HANDLE) handles[0]; handles[0] = (jlong) -1; } else { if (! CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE)) { win32Error(env, "CreatePipe"); goto Catch; } si.hStdInput = inRead; SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE); handles[0] = (jlong) inWrite; } SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, TRUE); if (handles[1] != (jlong) -1) { si.hStdOutput = (HANDLE) handles[1]; handles[1] = (jlong) -1; } else { if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) { win32Error(env, "CreatePipe"); goto Catch; } si.hStdOutput = outWrite; SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE); handles[1] = (jlong) outRead; } SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, TRUE); if (redirectErrorStream) { si.hStdError = si.hStdOutput; handles[2] = (jlong) -1; } else if (handles[2] != (jlong) -1) { si.hStdError = (HANDLE) handles[2]; handles[2] = (jlong) -1; } else { if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) { win32Error(env, "CreatePipe"); goto Catch; } si.hStdError = errWrite; SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE); handles[2] = (jlong) errRead; } SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, TRUE); if (onNT) processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; else processFlag = selectProcessFlag(env, cmd); /* Java and Windows are both pure Unicode systems at heart. * Windows has both a legacy byte-based API and a 16-bit Unicode * "W" API. The Right Thing here is to call CreateProcessW, since * that will allow all process-related information like command * line arguments to be passed properly to the child. We don't do * that currently, since we would first have to have "W" versions * of JVM_NativePath and perhaps other functions. In the * meantime, we can call CreateProcess with the magic flag * CREATE_UNICODE_ENVIRONMENT, which passes only the environment * in "W" mode. We will fix this later. */ ret = CreateProcess(0, /* executable name */ pcmd, /* command line */ 0, /* process security attribute */ 0, /* thread security attribute */ TRUE, /* inherits system handles */ processFlag, /* selected based on exe type */ penvBlock, /* environment block */ pdir, /* change to the new current directory */ &si, /* (in) startup information */ &pi); /* (out) process information */ if (!ret) { win32Error(env, "CreateProcess"); goto Catch; } CloseHandle(pi.hThread); ret = (jlong)pi.hProcess; Finally: /* Always clean up the child's side of the pipes */ closeSafely(inRead); closeSafely(outWrite); closeSafely(errWrite); if (pcmd != NULL) JNU_ReleaseStringPlatformChars(env, cmd, (char *) pcmd); if (pdir != NULL) JNU_ReleaseStringPlatformChars(env, dir, (char *) pdir); if (penvBlock != NULL) { if (onNT) (*env)->ReleaseStringChars(env, envBlock, (jchar *) penvBlock); else JNU_ReleaseStringPlatformChars(env, dir, (char *) penvBlock); } if (handles != NULL) (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0); return ret; Catch: /* Clean up the parent's side of the pipes in case of failure only */ closeSafely(inWrite); closeSafely(outRead); closeSafely(errRead); goto Finally; }
/* Please, read about the MS inheritance problem http://support.microsoft.com/kb/315939 and critical section/synchronized block solution. */ static jlong processCreate( JNIEnv *env, const jchar *pcmd, const jchar *penvBlock, const jchar *pdir, jlong *handles, jboolean redirectErrorStream) { jlong ret = 0L; STARTUPINFOW si = {sizeof(si)}; /* Handles for which the inheritance flag must be restored. */ HANDLE stdIOE[HANDLE_STORAGE_SIZE] = { /* Current process standard IOE handles: JDK-7147084 */ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, /* Child process IOE handles: JDK-6921885 */ (HANDLE)handles[0], (HANDLE)handles[1], (HANDLE)handles[2]}; BOOL inherit[HANDLE_STORAGE_SIZE] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; { /* Extraction of current process standard IOE handles */ DWORD idsIOE[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; int i; for (i = 0; i < 3; ++i) /* Should not be closed by CloseHandle! */ stdIOE[i] = GetStdHandle(idsIOE[i]); } prepareIOEHandleState(stdIOE, inherit); { /* Input */ STDHOLDER holderIn = {{INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, OFFSET_READ}; if (initHolder(env, &handles[0], &holderIn, &si.hStdInput)) { /* Output */ STDHOLDER holderOut = {{INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, OFFSET_WRITE}; if (initHolder(env, &handles[1], &holderOut, &si.hStdOutput)) { /* Error */ STDHOLDER holderErr = {{INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, OFFSET_WRITE}; BOOL success; if (redirectErrorStream) { si.hStdError = si.hStdOutput; /* Here we set the error stream to [ProcessBuilder.NullInputStream.INSTANCE] value. That is in accordance with Java Doc for the redirection case. The Java file for the [ handles[2] ] will be closed in ANY case. It is not a handle leak. */ handles[2] = JAVA_INVALID_HANDLE_VALUE; success = TRUE; } else { success = initHolder(env, &handles[2], &holderErr, &si.hStdError); } if (success) { PROCESS_INFORMATION pi; DWORD processFlag = CREATE_UNICODE_ENVIRONMENT; /* Suppress popping-up of a console window for non-console applications */ if (GetConsoleWindow() == NULL) processFlag |= CREATE_NO_WINDOW; si.dwFlags = STARTF_USESTDHANDLES; if (!CreateProcessW( NULL, /* executable name */ (LPWSTR)pcmd, /* command line */ NULL, /* process security attribute */ NULL, /* thread security attribute */ TRUE, /* inherits system handles */ processFlag, /* selected based on exe type */ (LPVOID)penvBlock,/* environment block */ (LPCWSTR)pdir, /* change to the new current directory */ &si, /* (in) startup information */ &pi)) /* (out) process information */ { win32Error(env, L"CreateProcess"); } else { closeSafely(pi.hThread); ret = (jlong)pi.hProcess; } } releaseHolder(ret == 0, &holderErr); releaseHolder(ret == 0, &holderOut); } releaseHolder(ret == 0, &holderIn); } } restoreIOEHandleState(stdIOE, inherit); return ret; }