/* * Copyright 1997-2008 Sun Microsystems, Inc. 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. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ #include #include "java_lang_ProcessImpl.h" #include "jni.h" #include "jvm.h" #include "jni_util.h" #include "io_util.h" #include #include /* We try to make sure that we can read and write 4095 bytes (the * fixed limit on Linux) to the pipe on all operating systems without * deadlock. Windows 2000 inexplicably appears to need an extra 24 * bytes of slop to avoid deadlock. */ #define PIPE_SIZE (4096+24) static void win32Error(JNIEnv *env, const char *functionName) { static const char * const format = "%s error=%d, %s"; static const char * const fallbackFormat = "%s failed, error=%d"; char buf[256]; char errmsg[sizeof(buf) + 100]; const int errnum = GetLastError(); const int n = JVM_GetLastErrorString(buf, sizeof(buf)); if (n > 0) sprintf(errmsg, format, functionName, errnum, buf); else sprintf(errmsg, fallbackFormat, functionName, errnum); JNU_ThrowIOException(env, errmsg); } static void closeSafely(HANDLE handle) { if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); } /* This is a unicode adapted version of JVM_NativePath from path_md.c. * Convert a pathname to native format. On win32, this involves forcing all * separators to be '\\' rather than '/' and removing redundant separators. * This procedure modifies the given path in place, as the result is never * longer than the original. There is no error return; this operation always * succeeds. */ static LPWSTR getNativePath(LPWSTR path) { LPWSTR src = path; LPWSTR dst = path; LPWSTR end = path; /* If a drive specifier is found, this will * point to the colon following the drive letter */ LPWSTR colon = NULL; /* Check for leading separators */ while ((*src) == L'/' || (*src) == L'\\') src++; if (iswalpha(*src) && src[1] == L':') { /* Remove leading separators if followed by drive specifier. This * hack is necessary to support file URLs containing drive * specifiers (e.g., "file://c:/path"). As a side effect, * "/c:/path" can be used as an alternative to "c:/path". */ *dst++ = *src++; colon = dst; *dst++ = ':'; src++; } else { src = path; if (((src[0]) == L'/' || (src[0]) == L'\\') && ((src[1]) == L'/' || (src[1]) == L'\\')) { /* UNC pathname: Retain first separator; leave src pointed at * second separator so that further separators will be collapsed * into the second separator. The result will be a pathname * beginning with "\\\\" followed (most likely) by a host name. */ src = dst = path + 1; path[0] = L'\\'; /* Force first separator to '\\' */ } } end = dst; /* Remove redundant separators from remainder of path, forcing all * separators to be '\\' rather than '/'. Also, single byte space * characters are removed from the end of the path because those * are not legal ending characters on this operating system. */ while (*src != L'\0') { if ((*src) == L'/' || (*src) == L'\\') { *dst++ = L'\\'; src++; while ((*src) == L'/' || (*src) == L'\\') src++; if (*src == L'\0') { /* Check for trailing separator */ end = dst; if (colon == dst - 2) break; /* "z:\\" */ if (dst == path + 1) break; /* "\\" */ if (dst == path + 2 && (*path) == L'/' || (*path) == L'\\') { /* "\\\\" is not collapsed to "\\" because "\\\\" marks the * beginning of a UNC pathname. Even though it is not, by * itself, a valid UNC pathname, we leave it as is in order * to be consistent with the path canonicalizer as well * as the win32 APIs, which treat this case as an invalid * UNC pathname rather than as an alias for the root * directory of the current drive. */ break; } end = --dst; /* Path does not denote a root directory, so remove trailing separator */ break; } end = dst; } else { WCHAR c = *src++; *dst++ = c; /* Space is not a legal ending character */ if (c != L' ') end = dst; } } *end = L'\0'; return path; } static LPWSTR getStringCopy(JNIEnv *env, jstring s) { int len; LPWSTR res; const jchar * chars; len = (*env)->GetStringLength(env, s); res = malloc((len + 1) * sizeof(WCHAR)); if (res) { chars = (*env)->GetStringChars(env, s, NULL); if (!chars) { free(res); return NULL; } memcpy(res, chars, len * sizeof(WCHAR)); res[len] = L'\0'; return res; } return NULL; } 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; LPWSTR pcmd = NULL; LPWSTR pdir = NULL; LPVOID penvBlock = NULL; jlong *handles = NULL; jlong ret = 0; assert(cmd != NULL); /* CreateProcessW needs this parameter to be writeable, so we need a null terminated copy of the string */ pcmd = getStringCopy(env, cmd); if (pcmd == NULL) goto Catch; if (dir != 0) { /* Since getNativePath also performs inplace modifications, we need to make a a null terminated copy of the string */ pdir = getStringCopy(env, dir); if (pdir == NULL) goto Catch; pdir = getNativePath(pdir); } if (envBlock != NULL) { /* ProcessEnvironment.toEnvironmentBlock() takes care of proper string termination here */ penvBlock = (LPVOID)((*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, 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); ret = CreateProcessW(0, /* executable name */ pcmd, /* command line */ 0, /* process security attribute */ 0, /* thread security attribute */ TRUE, /* inherits system handles */ CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, /* creation flags */ 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) free(pcmd); if (pdir != NULL) free(pdir); if (penvBlock != NULL) (*env)->ReleaseStringChars(env, envBlock, (jchar *) 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) { DWORD exit_code; if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0) win32Error(env, "GetExitCodeProcess"); return exit_code; } JNIEXPORT jint JNICALL Java_java_lang_ProcessImpl_getStillActive(JNIEnv *env, jclass ignored) { return STILL_ACTIVE; } JNIEXPORT void JNICALL Java_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlong handle) { HANDLE events[2]; events[0] = (HANDLE) handle; events[1] = JVM_GetThreadInterruptEvent(); if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events, FALSE, /* Wait for ANY event */ INFINITE) /* Wait forever */ == WAIT_FAILED) win32Error(env, "WaitForMultipleObjects"); } JNIEXPORT void JNICALL Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle) { TerminateProcess((HANDLE) handle, 1); } JNIEXPORT jboolean JNICALL Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle) { return CloseHandle((HANDLE) handle); }