--- a/jdk/src/windows/native/java/lang/ProcessImpl_md.c Thu Aug 08 13:51:55 2013 +0900
+++ b/jdk/src/windows/native/java/lang/ProcessImpl_md.c Thu Aug 08 09:16:16 2013 +0400
@@ -113,6 +113,233 @@
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, L"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, 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;
+}
+
JNIEXPORT jlong JNICALL
Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
jstring cmd,
@@ -121,138 +348,35 @@
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;
- DWORD processFlag;
-
- 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, L"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, L"CreatePipe");
- goto Catch;
+ 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);
}
- 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, L"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);
-
- processFlag = CREATE_NO_WINDOW | 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, L"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