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

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * 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,160 +133,274 @@
 {
     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)
+static BOOL hasInheritFlag(HANDLE handle)
 {
-    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;
+    DWORD mask;
+    if (GetHandleInformation(handle, &mask)) {
+        return mask & HANDLE_FLAG_INHERIT;
     }
-    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;
+    return FALSE;
+}
 
-    memset(&si, 0, sizeof(si));
-    si.cb = sizeof(si);
-    si.dwFlags = STARTF_USESTDHANDLES;
+#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)
 
-    sa.nLength = sizeof(sa);
-    sa.lpSecurityDescriptor = 0;
-    sa.bInheritHandle = TRUE;
+/* Pipe holder structure */
+typedef struct _STDHOLDER {
+    HANDLE  pipe[2];
+    int     offset;
+} STDHOLDER;
 
-    if (handles[0] != (jlong) -1) {
-        si.hStdInput = (HANDLE) handles[0];
-        handles[0] = (jlong) -1;
+/* 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 {
-        if (! CreatePipe(&inRead,  &inWrite,  &sa, PIPE_SIZE)) {
+        /* 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");
-            goto Catch;
+            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;
         }
-        si.hStdInput = inRead;
-        SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, 0);
-        handles[0] = (jlong) inWrite;
     }
-    SetHandleInformation(si.hStdInput,
-        HANDLE_FLAG_INHERIT,
-        HANDLE_FLAG_INHERIT);
+    /* 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;
+}
 
-    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;
+/* 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)]);
         }
-        si.hStdOutput = outWrite;
-        SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, 0);
-        handles[1] = (jlong) outRead;
+}
+
+/* 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);
     }
-    SetHandleInformation(si.hStdOutput,
+    }
+}
+
+/* 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,
-        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;
-        handles[2] = (jlong) -1;
-    } else if (handles[2] != (jlong) -1) {
-        si.hStdError = (HANDLE) handles[2];
-        handles[2] = (jlong) -1;
+                    /* 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 {
-        if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) {
-            win32Error(env, "CreatePipe");
-            goto Catch;
+                    success = initHolder(env, &handles[2], &holderErr, &si.hStdError);
         }
-        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 */
+                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 */
-                         0,                /* process security attribute */
-                         0,                /* thread security attribute */
+                        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 */
-    if (!ret) {
+                        &pi))             /* (out) process information */
+                    {
         win32Error(env, "CreateProcess");
-        goto Catch;
+                    } else {
+                        closeSafely(pi.hThread);
+                        ret = (jlong)pi.hProcess;
     }
+                }
+                releaseHolder(ret == 0, &holderErr);
+                releaseHolder(ret == 0, &holderOut);
+            }
+            releaseHolder(ret == 0, &holderIn);
+        }
+    }
+    restoreIOEHandleState(stdIOE, inherit);
 
-    CloseHandle(pi.hThread);
-    ret = (jlong)pi.hProcess;
+    return ret;
+}
 
- 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);
+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);
-    if (handles != NULL)
-        (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
+            (*env)->ReleaseStringChars(env, cmd, pcmd);
+        }
+    }
     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)
 {