/* 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)]);
    }
}
Esempio n. 2
0
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;
}