0N/A/*
3261N/A * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include <assert.h>
0N/A#include "java_lang_ProcessImpl.h"
0N/A
0N/A#include "jni.h"
0N/A#include "jvm.h"
0N/A#include "jni_util.h"
0N/A#include "io_util.h"
0N/A#include <windows.h>
0N/A#include <io.h>
0N/A
20N/A/* We try to make sure that we can read and write 4095 bytes (the
20N/A * fixed limit on Linux) to the pipe on all operating systems without
20N/A * deadlock. Windows 2000 inexplicably appears to need an extra 24
20N/A * bytes of slop to avoid deadlock.
20N/A */
20N/A#define PIPE_SIZE (4096+24)
0N/A
0N/Achar *
0N/AextractExecutablePath(JNIEnv *env, char *source)
0N/A{
0N/A char *p, *r;
0N/A
0N/A /* If no spaces, then use entire thing */
0N/A if ((p = strchr(source, ' ')) == NULL)
0N/A return source;
0N/A
0N/A /* If no quotes, or quotes after space, return up to space */
0N/A if (((r = strchr(source, '"')) == NULL) || (r > p)) {
0N/A *p = 0;
0N/A return source;
0N/A }
0N/A
0N/A /* Quotes before space, return up to space after next quotes */
0N/A p = strchr(r, '"');
0N/A if ((p = strchr(p, ' ')) == NULL)
0N/A return source;
0N/A *p = 0;
0N/A return source;
0N/A}
0N/A
0N/ADWORD
0N/AselectProcessFlag(JNIEnv *env, jstring cmd0)
0N/A{
0N/A char buf[MAX_PATH];
0N/A DWORD newFlag = 0;
0N/A char *exe, *p, *name;
0N/A unsigned char buffer[2];
0N/A long headerLoc = 0;
0N/A int fd = 0;
0N/A
0N/A exe = (char *)JNU_GetStringPlatformChars(env, cmd0, 0);
0N/A exe = extractExecutablePath(env, exe);
0N/A
0N/A if (exe != NULL) {
0N/A if ((p = strchr(exe, '\\')) == NULL) {
0N/A SearchPath(NULL, exe, ".exe", MAX_PATH, buf, &name);
0N/A } else {
0N/A p = strrchr(exe, '\\');
0N/A *p = 0;
0N/A p++;
0N/A SearchPath(exe, p, ".exe", MAX_PATH, buf, &name);
0N/A }
0N/A }
0N/A
0N/A fd = _open(buf, _O_RDONLY);
0N/A if (fd > 0) {
0N/A _read(fd, buffer, 2);
0N/A if (buffer[0] == 'M' && buffer[1] == 'Z') {
0N/A _lseek(fd, 60L, SEEK_SET);
0N/A _read(fd, buffer, 2);
0N/A headerLoc = (long)buffer[1] << 8 | (long)buffer[0];
0N/A _lseek(fd, headerLoc, SEEK_SET);
0N/A _read(fd, buffer, 2);
0N/A if (buffer[0] == 'P' && buffer[1] == 'E') {
0N/A newFlag = DETACHED_PROCESS;
0N/A }
0N/A }
0N/A _close(fd);
0N/A }
0N/A JNU_ReleaseStringPlatformChars(env, cmd0, exe);
0N/A return newFlag;
0N/A}
0N/A
0N/Astatic void
0N/Awin32Error(JNIEnv *env, const char *functionName)
0N/A{
0N/A static const char * const format = "%s error=%d, %s";
0N/A static const char * const fallbackFormat = "%s failed, error=%d";
0N/A char buf[256];
0N/A char errmsg[sizeof(buf) + 100];
0N/A const int errnum = GetLastError();
0N/A const int n = JVM_GetLastErrorString(buf, sizeof(buf));
0N/A if (n > 0)
0N/A sprintf(errmsg, format, functionName, errnum, buf);
0N/A else
0N/A sprintf(errmsg, fallbackFormat, functionName, errnum);
0N/A JNU_ThrowIOException(env, errmsg);
0N/A}
0N/A
0N/Astatic void
0N/AcloseSafely(HANDLE handle)
0N/A{
25N/A if (handle != INVALID_HANDLE_VALUE)
0N/A CloseHandle(handle);
0N/A}
0N/A
0N/AJNIEXPORT jlong JNICALL
0N/AJava_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
0N/A jstring cmd,
0N/A jstring envBlock,
0N/A jstring dir,
25N/A jlongArray stdHandles,
25N/A jboolean redirectErrorStream)
0N/A{
25N/A HANDLE inRead = INVALID_HANDLE_VALUE;
25N/A HANDLE inWrite = INVALID_HANDLE_VALUE;
25N/A HANDLE outRead = INVALID_HANDLE_VALUE;
25N/A HANDLE outWrite = INVALID_HANDLE_VALUE;
25N/A HANDLE errRead = INVALID_HANDLE_VALUE;
25N/A HANDLE errWrite = INVALID_HANDLE_VALUE;
0N/A SECURITY_ATTRIBUTES sa;
0N/A PROCESS_INFORMATION pi;
2247N/A STARTUPINFOW si;
2247N/A const jchar* pcmd = NULL;
2247N/A const jchar* pdir = NULL;
2247N/A const jchar* penvBlock = NULL;
2247N/A jlong *handles = NULL;
0N/A jlong ret = 0;
0N/A OSVERSIONINFO ver;
0N/A jboolean onNT = JNI_FALSE;
0N/A DWORD processFlag;
0N/A
0N/A ver.dwOSVersionInfoSize = sizeof(ver);
0N/A GetVersionEx(&ver);
0N/A if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
0N/A onNT = JNI_TRUE;
0N/A
0N/A assert(cmd != NULL);
2247N/A pcmd = (*env)->GetStringChars(env, cmd, NULL);
0N/A if (pcmd == NULL) goto Catch;
0N/A
0N/A if (dir != 0) {
2247N/A pdir = (*env)->GetStringChars(env, dir, NULL);
0N/A if (pdir == NULL) goto Catch;
0N/A }
0N/A if (envBlock != NULL) {
2247N/A penvBlock = ((*env)->GetStringChars(env, envBlock, NULL));
0N/A if (penvBlock == NULL) goto Catch;
0N/A }
25N/A assert(stdHandles != NULL);
25N/A handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);
25N/A if (handles == NULL) goto Catch;
25N/A
0N/A memset(&si, 0, sizeof(si));
0N/A si.cb = sizeof(si);
0N/A si.dwFlags = STARTF_USESTDHANDLES;
25N/A
25N/A sa.nLength = sizeof(sa);
25N/A sa.lpSecurityDescriptor = 0;
25N/A sa.bInheritHandle = TRUE;
25N/A
25N/A if (handles[0] != (jlong) -1) {
25N/A si.hStdInput = (HANDLE) handles[0];
25N/A handles[0] = (jlong) -1;
25N/A } else {
25N/A if (! CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE)) {
25N/A win32Error(env, "CreatePipe");
25N/A goto Catch;
25N/A }
25N/A si.hStdInput = inRead;
25N/A SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE);
25N/A handles[0] = (jlong) inWrite;
25N/A }
25N/A SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, TRUE);
0N/A
25N/A if (handles[1] != (jlong) -1) {
25N/A si.hStdOutput = (HANDLE) handles[1];
25N/A handles[1] = (jlong) -1;
25N/A } else {
25N/A if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) {
25N/A win32Error(env, "CreatePipe");
25N/A goto Catch;
25N/A }
25N/A si.hStdOutput = outWrite;
25N/A SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE);
25N/A handles[1] = (jlong) outRead;
25N/A }
25N/A SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, TRUE);
0N/A
25N/A if (redirectErrorStream) {
25N/A si.hStdError = si.hStdOutput;
25N/A handles[2] = (jlong) -1;
25N/A } else if (handles[2] != (jlong) -1) {
25N/A si.hStdError = (HANDLE) handles[2];
25N/A handles[2] = (jlong) -1;
25N/A } else {
25N/A if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) {
25N/A win32Error(env, "CreatePipe");
25N/A goto Catch;
25N/A }
25N/A si.hStdError = errWrite;
25N/A SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE);
25N/A handles[2] = (jlong) errRead;
25N/A }
25N/A SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, TRUE);
0N/A
0N/A if (onNT)
0N/A processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
0N/A else
2247N/A processFlag = selectProcessFlag(env, cmd) | CREATE_UNICODE_ENVIRONMENT;
2247N/A ret = CreateProcessW(0, /* executable name */
2247N/A (LPWSTR)pcmd, /* command line */
2247N/A 0, /* process security attribute */
2247N/A 0, /* thread security attribute */
2247N/A TRUE, /* inherits system handles */
2247N/A processFlag, /* selected based on exe type */
2247N/A (LPVOID)penvBlock,/* environment block */
2247N/A (LPCWSTR)pdir, /* change to the new current directory */
2247N/A &si, /* (in) startup information */
2247N/A &pi); /* (out) process information */
0N/A if (!ret) {
0N/A win32Error(env, "CreateProcess");
0N/A goto Catch;
0N/A }
0N/A
0N/A CloseHandle(pi.hThread);
0N/A ret = (jlong)pi.hProcess;
0N/A
0N/A Finally:
0N/A /* Always clean up the child's side of the pipes */
0N/A closeSafely(inRead);
0N/A closeSafely(outWrite);
0N/A closeSafely(errWrite);
0N/A
0N/A if (pcmd != NULL)
2247N/A (*env)->ReleaseStringChars(env, cmd, pcmd);
0N/A if (pdir != NULL)
2247N/A (*env)->ReleaseStringChars(env, dir, pdir);
2247N/A if (penvBlock != NULL)
2247N/A (*env)->ReleaseStringChars(env, envBlock, penvBlock);
25N/A if (handles != NULL)
25N/A (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
0N/A return ret;
0N/A
0N/A Catch:
0N/A /* Clean up the parent's side of the pipes in case of failure only */
0N/A closeSafely(inWrite);
0N/A closeSafely(outRead);
0N/A closeSafely(errRead);
0N/A goto Finally;
0N/A}
0N/A
0N/AJNIEXPORT jint JNICALL
0N/AJava_java_lang_ProcessImpl_getExitCodeProcess(JNIEnv *env, jclass ignored, jlong handle)
0N/A{
0N/A DWORD exit_code;
0N/A if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0)
0N/A win32Error(env, "GetExitCodeProcess");
0N/A return exit_code;
0N/A}
0N/A
0N/AJNIEXPORT jint JNICALL
0N/AJava_java_lang_ProcessImpl_getStillActive(JNIEnv *env, jclass ignored)
0N/A{
0N/A return STILL_ACTIVE;
0N/A}
0N/A
0N/AJNIEXPORT void JNICALL
0N/AJava_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlong handle)
0N/A{
0N/A HANDLE events[2];
0N/A events[0] = (HANDLE) handle;
0N/A events[1] = JVM_GetThreadInterruptEvent();
0N/A
0N/A if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
0N/A FALSE, /* Wait for ANY event */
0N/A INFINITE) /* Wait forever */
0N/A == WAIT_FAILED)
0N/A win32Error(env, "WaitForMultipleObjects");
0N/A}
0N/A
0N/AJNIEXPORT void JNICALL
0N/AJava_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle)
0N/A{
0N/A TerminateProcess((HANDLE) handle, 1);
0N/A}
0N/A
0N/AJNIEXPORT jboolean JNICALL
0N/AJava_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle)
0N/A{
0N/A return CloseHandle((HANDLE) handle);
0N/A}
3200N/A
3200N/A/**
3200N/A * Returns a copy of the Unicode characters of a string. Fow now this
3200N/A * function doesn't handle long path names and other issues.
3200N/A */
3200N/Astatic WCHAR* getPath(JNIEnv *env, jstring ps) {
3200N/A WCHAR *pathbuf = NULL;
3200N/A const jchar *chars = (*(env))->GetStringChars(env, ps, NULL);
3200N/A if (chars != NULL) {
3200N/A size_t pathlen = wcslen(chars);
3200N/A pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
3200N/A if (pathbuf == NULL) {
3200N/A JNU_ThrowOutOfMemoryError(env, NULL);
3200N/A } else {
3200N/A wcscpy(pathbuf, chars);
3200N/A }
3200N/A (*env)->ReleaseStringChars(env, ps, chars);
3200N/A }
3200N/A return pathbuf;
3200N/A}
3200N/A
3200N/AJNIEXPORT jlong JNICALL
3200N/AJava_java_lang_ProcessImpl_openForAtomicAppend(JNIEnv *env, jclass ignored, jstring path)
3200N/A{
3200N/A const DWORD access = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA);
3200N/A const DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
3200N/A const DWORD disposition = OPEN_ALWAYS;
3200N/A const DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
3200N/A HANDLE h;
3200N/A WCHAR *pathbuf = getPath(env, path);
3200N/A if (pathbuf == NULL) {
3200N/A /* Exception already pending */
3200N/A return -1;
3200N/A }
3200N/A h = CreateFileW(
3200N/A pathbuf, /* Wide char path name */
3200N/A access, /* Read and/or write permission */
3200N/A sharing, /* File sharing flags */
3200N/A NULL, /* Security attributes */
3200N/A disposition, /* creation disposition */
3200N/A flagsAndAttributes, /* flags and attributes */
3200N/A NULL);
3200N/A free(pathbuf);
3200N/A if (h == INVALID_HANDLE_VALUE) {
3200N/A JNU_ThrowIOExceptionWithLastError(env, "CreateFileW");
3200N/A }
3200N/A return ptr_to_jlong(h);
3200N/A}