annotate src/windows/native/java/lang/ProcessImpl_md.c @ 20:1d12b16c7df9

6631966: (process) Raise Windows pipe buffer size an extra 24 bytes (win) Reviewed-by: alanb, iris
author martin
date Mon, 10 Mar 2008 14:32:50 -0700
parents 37a05a11f281
children b5a587dd5af3
rev   line source
duke@0 1 /*
duke@0 2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
duke@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@0 4 *
duke@0 5 * This code is free software; you can redistribute it and/or modify it
duke@0 6 * under the terms of the GNU General Public License version 2 only, as
duke@0 7 * published by the Free Software Foundation. Sun designates this
duke@0 8 * particular file as subject to the "Classpath" exception as provided
duke@0 9 * by Sun in the LICENSE file that accompanied this code.
duke@0 10 *
duke@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@0 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@0 15 * accompanied this code).
duke@0 16 *
duke@0 17 * You should have received a copy of the GNU General Public License version
duke@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@0 20 *
duke@0 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@0 22 * CA 95054 USA or visit www.sun.com if you need additional information or
duke@0 23 * have any questions.
duke@0 24 */
duke@0 25
duke@0 26 #include <assert.h>
duke@0 27 #include "java_lang_ProcessImpl.h"
duke@0 28
duke@0 29 #include "jni.h"
duke@0 30 #include "jvm.h"
duke@0 31 #include "jni_util.h"
duke@0 32 #include "io_util.h"
duke@0 33 #include <windows.h>
duke@0 34 #include <io.h>
duke@0 35
martin@20 36 /* We try to make sure that we can read and write 4095 bytes (the
martin@20 37 * fixed limit on Linux) to the pipe on all operating systems without
martin@20 38 * deadlock. Windows 2000 inexplicably appears to need an extra 24
martin@20 39 * bytes of slop to avoid deadlock.
martin@20 40 */
martin@20 41 #define PIPE_SIZE (4096+24)
duke@0 42
duke@0 43 char *
duke@0 44 extractExecutablePath(JNIEnv *env, char *source)
duke@0 45 {
duke@0 46 char *p, *r;
duke@0 47
duke@0 48 /* If no spaces, then use entire thing */
duke@0 49 if ((p = strchr(source, ' ')) == NULL)
duke@0 50 return source;
duke@0 51
duke@0 52 /* If no quotes, or quotes after space, return up to space */
duke@0 53 if (((r = strchr(source, '"')) == NULL) || (r > p)) {
duke@0 54 *p = 0;
duke@0 55 return source;
duke@0 56 }
duke@0 57
duke@0 58 /* Quotes before space, return up to space after next quotes */
duke@0 59 p = strchr(r, '"');
duke@0 60 if ((p = strchr(p, ' ')) == NULL)
duke@0 61 return source;
duke@0 62 *p = 0;
duke@0 63 return source;
duke@0 64 }
duke@0 65
duke@0 66 DWORD
duke@0 67 selectProcessFlag(JNIEnv *env, jstring cmd0)
duke@0 68 {
duke@0 69 char buf[MAX_PATH];
duke@0 70 DWORD newFlag = 0;
duke@0 71 char *exe, *p, *name;
duke@0 72 unsigned char buffer[2];
duke@0 73 long headerLoc = 0;
duke@0 74 int fd = 0;
duke@0 75
duke@0 76 exe = (char *)JNU_GetStringPlatformChars(env, cmd0, 0);
duke@0 77 exe = extractExecutablePath(env, exe);
duke@0 78
duke@0 79 if (exe != NULL) {
duke@0 80 if ((p = strchr(exe, '\\')) == NULL) {
duke@0 81 SearchPath(NULL, exe, ".exe", MAX_PATH, buf, &name);
duke@0 82 } else {
duke@0 83 p = strrchr(exe, '\\');
duke@0 84 *p = 0;
duke@0 85 p++;
duke@0 86 SearchPath(exe, p, ".exe", MAX_PATH, buf, &name);
duke@0 87 }
duke@0 88 }
duke@0 89
duke@0 90 fd = _open(buf, _O_RDONLY);
duke@0 91 if (fd > 0) {
duke@0 92 _read(fd, buffer, 2);
duke@0 93 if (buffer[0] == 'M' && buffer[1] == 'Z') {
duke@0 94 _lseek(fd, 60L, SEEK_SET);
duke@0 95 _read(fd, buffer, 2);
duke@0 96 headerLoc = (long)buffer[1] << 8 | (long)buffer[0];
duke@0 97 _lseek(fd, headerLoc, SEEK_SET);
duke@0 98 _read(fd, buffer, 2);
duke@0 99 if (buffer[0] == 'P' && buffer[1] == 'E') {
duke@0 100 newFlag = DETACHED_PROCESS;
duke@0 101 }
duke@0 102 }
duke@0 103 _close(fd);
duke@0 104 }
duke@0 105 JNU_ReleaseStringPlatformChars(env, cmd0, exe);
duke@0 106 return newFlag;
duke@0 107 }
duke@0 108
duke@0 109 static void
duke@0 110 win32Error(JNIEnv *env, const char *functionName)
duke@0 111 {
duke@0 112 static const char * const format = "%s error=%d, %s";
duke@0 113 static const char * const fallbackFormat = "%s failed, error=%d";
duke@0 114 char buf[256];
duke@0 115 char errmsg[sizeof(buf) + 100];
duke@0 116 const int errnum = GetLastError();
duke@0 117 const int n = JVM_GetLastErrorString(buf, sizeof(buf));
duke@0 118 if (n > 0)
duke@0 119 sprintf(errmsg, format, functionName, errnum, buf);
duke@0 120 else
duke@0 121 sprintf(errmsg, fallbackFormat, functionName, errnum);
duke@0 122 JNU_ThrowIOException(env, errmsg);
duke@0 123 }
duke@0 124
duke@0 125 static void
duke@0 126 closeSafely(HANDLE handle)
duke@0 127 {
duke@0 128 if (handle)
duke@0 129 CloseHandle(handle);
duke@0 130 }
duke@0 131
duke@0 132 JNIEXPORT jlong JNICALL
duke@0 133 Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
duke@0 134 jstring cmd,
duke@0 135 jstring envBlock,
duke@0 136 jstring dir,
duke@0 137 jboolean redirectErrorStream,
duke@0 138 jobject in_fd,
duke@0 139 jobject out_fd,
duke@0 140 jobject err_fd)
duke@0 141 {
duke@0 142 HANDLE inRead = 0;
duke@0 143 HANDLE inWrite = 0;
duke@0 144 HANDLE outRead = 0;
duke@0 145 HANDLE outWrite = 0;
duke@0 146 HANDLE errRead = 0;
duke@0 147 HANDLE errWrite = 0;
duke@0 148 SECURITY_ATTRIBUTES sa;
duke@0 149 PROCESS_INFORMATION pi;
duke@0 150 STARTUPINFO si;
duke@0 151 LPTSTR pcmd = NULL;
duke@0 152 LPCTSTR pdir = NULL;
duke@0 153 LPVOID penvBlock = NULL;
duke@0 154 jlong ret = 0;
duke@0 155 OSVERSIONINFO ver;
duke@0 156 jboolean onNT = JNI_FALSE;
duke@0 157 DWORD processFlag;
duke@0 158
duke@0 159 ver.dwOSVersionInfoSize = sizeof(ver);
duke@0 160 GetVersionEx(&ver);
duke@0 161 if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
duke@0 162 onNT = JNI_TRUE;
duke@0 163
duke@0 164 sa.nLength = sizeof(sa);
duke@0 165 sa.lpSecurityDescriptor = 0;
duke@0 166 sa.bInheritHandle = TRUE;
duke@0 167
duke@0 168 if (!(CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE) &&
duke@0 169 CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE) &&
duke@0 170 CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE))) {
duke@0 171 win32Error(env, "CreatePipe");
duke@0 172 goto Catch;
duke@0 173 }
duke@0 174
duke@0 175 assert(cmd != NULL);
duke@0 176 pcmd = (LPTSTR) JNU_GetStringPlatformChars(env, cmd, NULL);
duke@0 177 if (pcmd == NULL) goto Catch;
duke@0 178
duke@0 179 if (dir != 0) {
duke@0 180 pdir = (LPCTSTR) JNU_GetStringPlatformChars(env, dir, NULL);
duke@0 181 if (pdir == NULL) goto Catch;
duke@0 182 pdir = (LPCTSTR) JVM_NativePath((char *)pdir);
duke@0 183 }
duke@0 184
duke@0 185 if (envBlock != NULL) {
duke@0 186 penvBlock = onNT
duke@0 187 ? (LPVOID) ((*env)->GetStringChars(env, envBlock, NULL))
duke@0 188 : (LPVOID) JNU_GetStringPlatformChars(env, envBlock, NULL);
duke@0 189 if (penvBlock == NULL) goto Catch;
duke@0 190 }
duke@0 191
duke@0 192 memset(&si, 0, sizeof(si));
duke@0 193 si.cb = sizeof(si);
duke@0 194 si.dwFlags = STARTF_USESTDHANDLES;
duke@0 195 si.hStdInput = inRead;
duke@0 196 si.hStdOutput = outWrite;
duke@0 197 si.hStdError = redirectErrorStream ? outWrite : errWrite;
duke@0 198
duke@0 199 SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE);
duke@0 200 SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE);
duke@0 201 SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE);
duke@0 202
duke@0 203 if (redirectErrorStream)
duke@0 204 SetHandleInformation(errWrite, HANDLE_FLAG_INHERIT, FALSE);
duke@0 205
duke@0 206 if (onNT)
duke@0 207 processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
duke@0 208 else
duke@0 209 processFlag = selectProcessFlag(env, cmd);
duke@0 210
duke@0 211 /* Java and Windows are both pure Unicode systems at heart.
duke@0 212 * Windows has both a legacy byte-based API and a 16-bit Unicode
duke@0 213 * "W" API. The Right Thing here is to call CreateProcessW, since
duke@0 214 * that will allow all process-related information like command
duke@0 215 * line arguments to be passed properly to the child. We don't do
duke@0 216 * that currently, since we would first have to have "W" versions
duke@0 217 * of JVM_NativePath and perhaps other functions. In the
duke@0 218 * meantime, we can call CreateProcess with the magic flag
duke@0 219 * CREATE_UNICODE_ENVIRONMENT, which passes only the environment
duke@0 220 * in "W" mode. We will fix this later. */
duke@0 221
duke@0 222 ret = CreateProcess(0, /* executable name */
duke@0 223 pcmd, /* command line */
duke@0 224 0, /* process security attribute */
duke@0 225 0, /* thread security attribute */
duke@0 226 TRUE, /* inherits system handles */
duke@0 227 processFlag, /* selected based on exe type */
duke@0 228 penvBlock, /* environment block */
duke@0 229 pdir, /* change to the new current directory */
duke@0 230 &si, /* (in) startup information */
duke@0 231 &pi); /* (out) process information */
duke@0 232
duke@0 233 if (!ret) {
duke@0 234 win32Error(env, "CreateProcess");
duke@0 235 goto Catch;
duke@0 236 }
duke@0 237
duke@0 238 CloseHandle(pi.hThread);
duke@0 239 ret = (jlong)pi.hProcess;
duke@0 240 (*env)->SetLongField(env, in_fd, IO_handle_fdID, (jlong)inWrite);
duke@0 241 (*env)->SetLongField(env, out_fd, IO_handle_fdID, (jlong)outRead);
duke@0 242 (*env)->SetLongField(env, err_fd, IO_handle_fdID, (jlong)errRead);
duke@0 243
duke@0 244 Finally:
duke@0 245 /* Always clean up the child's side of the pipes */
duke@0 246 closeSafely(inRead);
duke@0 247 closeSafely(outWrite);
duke@0 248 closeSafely(errWrite);
duke@0 249
duke@0 250 if (pcmd != NULL)
duke@0 251 JNU_ReleaseStringPlatformChars(env, cmd, (char *) pcmd);
duke@0 252 if (pdir != NULL)
duke@0 253 JNU_ReleaseStringPlatformChars(env, dir, (char *) pdir);
duke@0 254 if (penvBlock != NULL) {
duke@0 255 if (onNT)
duke@0 256 (*env)->ReleaseStringChars(env, envBlock, (jchar *) penvBlock);
duke@0 257 else
duke@0 258 JNU_ReleaseStringPlatformChars(env, dir, (char *) penvBlock);
duke@0 259 }
duke@0 260 return ret;
duke@0 261
duke@0 262 Catch:
duke@0 263 /* Clean up the parent's side of the pipes in case of failure only */
duke@0 264 closeSafely(inWrite);
duke@0 265 closeSafely(outRead);
duke@0 266 closeSafely(errRead);
duke@0 267 goto Finally;
duke@0 268 }
duke@0 269
duke@0 270 JNIEXPORT jint JNICALL
duke@0 271 Java_java_lang_ProcessImpl_getExitCodeProcess(JNIEnv *env, jclass ignored, jlong handle)
duke@0 272 {
duke@0 273 DWORD exit_code;
duke@0 274 if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0)
duke@0 275 win32Error(env, "GetExitCodeProcess");
duke@0 276 return exit_code;
duke@0 277 }
duke@0 278
duke@0 279 JNIEXPORT jint JNICALL
duke@0 280 Java_java_lang_ProcessImpl_getStillActive(JNIEnv *env, jclass ignored)
duke@0 281 {
duke@0 282 return STILL_ACTIVE;
duke@0 283 }
duke@0 284
duke@0 285 JNIEXPORT void JNICALL
duke@0 286 Java_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlong handle)
duke@0 287 {
duke@0 288 HANDLE events[2];
duke@0 289 events[0] = (HANDLE) handle;
duke@0 290 events[1] = JVM_GetThreadInterruptEvent();
duke@0 291
duke@0 292 if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
duke@0 293 FALSE, /* Wait for ANY event */
duke@0 294 INFINITE) /* Wait forever */
duke@0 295 == WAIT_FAILED)
duke@0 296 win32Error(env, "WaitForMultipleObjects");
duke@0 297 }
duke@0 298
duke@0 299 JNIEXPORT void JNICALL
duke@0 300 Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle)
duke@0 301 {
duke@0 302 TerminateProcess((HANDLE) handle, 1);
duke@0 303 }
duke@0 304
duke@0 305 JNIEXPORT jboolean JNICALL
duke@0 306 Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle)
duke@0 307 {
duke@0 308 return CloseHandle((HANDLE) handle);
duke@0 309 }