src/windows/native/java/lang/ProcessImpl_md.c

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 133,292 **** { if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); } ! 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; ! STARTUPINFOW si; ! const jchar* pcmd = NULL; ! const jchar* pdir = NULL; ! const jchar* 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 = (*env)->GetStringChars(env, cmd, NULL); ! if (pcmd == NULL) goto Catch; ! ! if (dir != 0) { ! pdir = (*env)->GetStringChars(env, dir, NULL); ! if (pdir == NULL) goto Catch; } ! if (envBlock != NULL) { ! penvBlock = ((*env)->GetStringChars(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, 0); - handles[0] = (jlong) inWrite; } ! SetHandleInformation(si.hStdInput, ! HANDLE_FLAG_INHERIT, ! HANDLE_FLAG_INHERIT); ! 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, 0); ! handles[1] = (jlong) outRead; } ! SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, ! HANDLE_FLAG_INHERIT); 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, 0); - handles[2] = (jlong) errRead; - } - SetHandleInformation(si.hStdError, - HANDLE_FLAG_INHERIT, - HANDLE_FLAG_INHERIT); ! if (onNT) ! processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; ! else ! processFlag = selectProcessFlag(env, cmd) | CREATE_UNICODE_ENVIRONMENT; ! ret = CreateProcessW(0, /* executable name */ (LPWSTR)pcmd, /* command line */ ! 0, /* process security attribute */ ! 0, /* 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 */ ! 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) ! (*env)->ReleaseStringChars(env, cmd, pcmd); if (pdir != NULL) (*env)->ReleaseStringChars(env, dir, pdir); if (penvBlock != NULL) (*env)->ReleaseStringChars(env, envBlock, 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; } JNIEXPORT jint JNICALL Java_java_lang_ProcessImpl_getExitCodeProcess(JNIEnv *env, jclass ignored, jlong handle) { --- 133,406 ---- { if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); } ! static BOOL hasInheritFlag(HANDLE handle) { ! DWORD mask; ! if (GetHandleInformation(handle, &mask)) { ! return mask & HANDLE_FLAG_INHERIT; } ! return FALSE; ! } ! #define HANDLE_STORAGE_SIZE 6 ! #define OFFSET_READ 0 ! #define OFFSET_WRITE 1 ! //long signed version of INVALID_HANDLE_VALUE ! #define JAVA_INVALID_HANDLE_VALUE ((jlong) -1) ! #define OPPOSITE_END(offset) (offset==OFFSET_READ ? OFFSET_WRITE : OFFSET_READ) ! /* Pipe holder structure */ ! typedef struct _STDHOLDER { ! HANDLE pipe[2]; ! int offset; ! } STDHOLDER; ! /* Responsible for correct initialization of the [pHolder] structure ! (that is used for handles recycling) if needs, ! and appropriate setup of IOE handle [phStd] for child process based ! on created pipe or Java handle. */ ! static BOOL initHolder( ! JNIEnv *env, ! jlong *pjhandles, /* IN OUT - the handle form Java, ! that can be a file, console or undefined */ ! STDHOLDER *pHolder, /* OUT - initialized structure that holds pipe ! handles */ ! HANDLE *phStd /* OUT - initialized handle for child process */ ! ) { ! /* Here we test the value from Java against invalid ! handle value. We are not using INVALID_HANDLE_VALUE macro ! due to double signed/unsigned and 32/64bit ambiguity. ! Otherwise it will be easy to get the wrong ! value 0x00000000FFFFFFFF ! instead 0xFFFFFFFFFFFFFFFF. */ ! if (*pjhandles != JAVA_INVALID_HANDLE_VALUE) { ! /* Java file or console redirection */ ! *phStd = (HANDLE) *pjhandles; ! /* Here we set the related Java stream (Process.getXXXXStream()) ! to [ProcessBuilder.NullXXXXStream.INSTANCE] value. ! The initial Java handle [*pjhandles] will be closed in ! ANY case. It is not a handle leak. */ ! *pjhandles = JAVA_INVALID_HANDLE_VALUE; } else { ! /* Creation of parent-child pipe */ ! if (!CreatePipe( ! &pHolder->pipe[OFFSET_READ], ! &pHolder->pipe[OFFSET_WRITE], ! NULL, /* we would like to inherit ! default process access, ! instead of 'Everybody' access */ ! PIPE_SIZE)) ! { win32Error(env, "CreatePipe"); ! return FALSE; ! } else { ! /* [thisProcessEnd] has no the inherit flag because ! the [lpPipeAttributes] param of [CreatePipe] ! had the NULL value. */ ! HANDLE thisProcessEnd = pHolder->pipe[OPPOSITE_END(pHolder->offset)]; ! *phStd = pHolder->pipe[pHolder->offset]; ! *pjhandles = (jlong) thisProcessEnd; } } ! /* Pipe handle will be closed in the [releaseHolder] call, ! file handle will be closed in Java. ! The long-live handle need to restore the inherit flag, ! we do it later in the [prepareIOEHandleState] call. */ ! SetHandleInformation( ! *phStd, ! HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); ! return TRUE; ! } ! /* 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)]); } ! } ! ! /* Stores and drops the inherit flag of handles that should not ! be shared with the child process by default, but can hold the ! inherit flag due to MS process birth specific. */ ! static void prepareIOEHandleState( ! HANDLE *stdIOE, ! BOOL *inherit) ! { ! int i; ! for (i = 0; i < HANDLE_STORAGE_SIZE; ++i) { ! HANDLE hstd = stdIOE[i]; ! if (INVALID_HANDLE_VALUE != hstd && hasInheritFlag(hstd)) { ! /* FALSE by default */ ! inherit[i] = TRUE; ! /* Java does not need implicit inheritance for IOE handles, ! so we drop inherit flag that probably was installed by ! previous CreateProcess call that launched current process. ! We will return the handle state back after CreateProcess call. ! By clearing inherit flag we prevent "greedy grandchild" birth. ! The explicit inheritance for child process IOE handles is ! implemented in the [initHolder] call. */ ! SetHandleInformation(hstd, HANDLE_FLAG_INHERIT, 0); } ! } ! } ! ! /* Restores the inheritance flag of handles from stored values. */ ! static void restoreIOEHandleState( ! const HANDLE *stdIOE, ! const BOOL *inherit) ! { ! /* The set of current process standard IOE handles and ! the set of child process IOE handles can intersect. ! To restore the inherit flag right, we use backward ! array iteration. */ ! int i; ! for (i = HANDLE_STORAGE_SIZE - 1; i >= 0; --i) ! if (INVALID_HANDLE_VALUE != stdIOE[i]) { ! /* Restore inherit flag for any case. ! The handle can be changed by explicit inheritance.*/ ! SetHandleInformation(stdIOE[i], HANDLE_FLAG_INHERIT, ! inherit[i] ? HANDLE_FLAG_INHERIT : 0); ! } ! } + /* 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_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; ! ! 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, "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; ! } ! JNIEXPORT jlong JNICALL ! Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, ! jstring cmd, ! jstring envBlock, ! jstring dir, ! jlongArray stdHandles, ! jboolean redirectErrorStream) ! { ! jlong ret = 0; ! if (cmd != NULL && stdHandles != NULL) { ! const jchar *pcmd = (*env)->GetStringChars(env, cmd, NULL); ! if (pcmd != NULL) { ! const jchar *penvBlock = (envBlock != NULL) ! ? (*env)->GetStringChars(env, envBlock, NULL) ! : NULL; ! const jchar *pdir = (dir != NULL) ! ? (*env)->GetStringChars(env, dir, NULL) ! : NULL; ! jlong *handles = (*env)->GetLongArrayElements(env, stdHandles, NULL); ! if (handles != NULL) { ! ret = processCreate( ! env, ! pcmd, ! penvBlock, ! pdir, ! handles, ! redirectErrorStream); ! (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0); ! } if (pdir != NULL) (*env)->ReleaseStringChars(env, dir, pdir); if (penvBlock != NULL) (*env)->ReleaseStringChars(env, envBlock, penvBlock); ! (*env)->ReleaseStringChars(env, cmd, pcmd); ! } ! } return ret; } JNIEXPORT jint JNICALL Java_java_lang_ProcessImpl_getExitCodeProcess(JNIEnv *env, jclass ignored, jlong handle) {