1 /* 2 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <assert.h> 27 #include "java_lang_ProcessImpl.h" 28 29 #include "jni.h" 30 #include "jvm.h" 31 #include "jni_util.h" 32 #include "io_util.h" 33 #include <windows.h> 34 #include <io.h> 35 36 /* We try to make sure that we can read and write 4095 bytes (the 37 * fixed limit on Linux) to the pipe on all operating systems without 38 * deadlock. Windows 2000 inexplicably appears to need an extra 24 39 * bytes of slop to avoid deadlock. 40 */ 41 #define PIPE_SIZE (4096+24) 42 43 char * 44 extractExecutablePath(JNIEnv *env, char *source) 45 { 46 char *p, *r; 47 48 /* If no spaces, then use entire thing */ 49 if ((p = strchr(source, ' ')) == NULL) 50 return source; 51 52 /* If no quotes, or quotes after space, return up to space */ 53 if (((r = strchr(source, '"')) == NULL) || (r > p)) { 54 *p = 0; 55 return source; 56 } 57 58 /* Quotes before space, return up to space after next quotes */ 59 p = strchr(r, '"'); 60 if ((p = strchr(p, ' ')) == NULL) 61 return source; 62 *p = 0; 63 return source; 64 } 65 66 DWORD 67 selectProcessFlag(JNIEnv *env, jstring cmd0) 68 { 69 char buf[MAX_PATH]; 70 DWORD newFlag = 0; 71 char *exe, *p, *name; 72 unsigned char buffer[2]; 73 long headerLoc = 0; 74 int fd = 0; 75 76 exe = (char *)JNU_GetStringPlatformChars(env, cmd0, 0); 77 exe = extractExecutablePath(env, exe); 78 79 if (exe != NULL) { 80 if ((p = strchr(exe, '\\')) == NULL) { 81 SearchPath(NULL, exe, ".exe", MAX_PATH, buf, &name); 82 } else { 83 p = strrchr(exe, '\\'); 84 *p = 0; 85 p++; 86 SearchPath(exe, p, ".exe", MAX_PATH, buf, &name); 87 } 88 } 89 90 fd = _open(buf, _O_RDONLY); 91 if (fd > 0) { 92 _read(fd, buffer, 2); 93 if (buffer[0] == 'M' && buffer[1] == 'Z') { 94 _lseek(fd, 60L, SEEK_SET); 95 _read(fd, buffer, 2); 96 headerLoc = (long)buffer[1] << 8 | (long)buffer[0]; 97 _lseek(fd, headerLoc, SEEK_SET); 98 _read(fd, buffer, 2); 99 if (buffer[0] == 'P' && buffer[1] == 'E') { 100 newFlag = DETACHED_PROCESS; 101 } 102 } 103 _close(fd); 104 } 105 JNU_ReleaseStringPlatformChars(env, cmd0, exe); 106 return newFlag; 107 } 108 109 static void 110 win32Error(JNIEnv *env, const char *functionName) 111 { 112 static const char * const format = "%s error=%d, %s"; 113 static const char * const fallbackFormat = "%s failed, error=%d"; 114 char buf[256]; 115 char errmsg[sizeof(buf) + 100]; 116 const int errnum = GetLastError(); 117 const int n = JVM_GetLastErrorString(buf, sizeof(buf)); 118 if (n > 0) 119 sprintf(errmsg, format, functionName, errnum, buf); 120 else 121 sprintf(errmsg, fallbackFormat, functionName, errnum); 122 JNU_ThrowIOException(env, errmsg); 123 } 124 125 static void 126 closeSafely(HANDLE handle) 127 { 128 if (handle != INVALID_HANDLE_VALUE) 129 CloseHandle(handle); 130 } 131 132 JNIEXPORT jlong JNICALL 133 Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, 134 jstring cmd, 135 jstring envBlock, 136 jstring dir, 137 jlongArray stdHandles, 138 jboolean redirectErrorStream) 139 { 140 HANDLE inRead = INVALID_HANDLE_VALUE; 141 HANDLE inWrite = INVALID_HANDLE_VALUE; 142 HANDLE outRead = INVALID_HANDLE_VALUE; 143 HANDLE outWrite = INVALID_HANDLE_VALUE; 144 HANDLE errRead = INVALID_HANDLE_VALUE; 145 HANDLE errWrite = INVALID_HANDLE_VALUE; 146 SECURITY_ATTRIBUTES sa; 147 PROCESS_INFORMATION pi; 148 STARTUPINFOW si; 149 const jchar* pcmd = NULL; 150 const jchar* pdir = NULL; 151 const jchar* penvBlock = NULL; 152 jlong *handles = NULL; 153 jlong ret = 0; 154 OSVERSIONINFO ver; 155 jboolean onNT = JNI_FALSE; 156 DWORD processFlag; 157 158 ver.dwOSVersionInfoSize = sizeof(ver); 159 GetVersionEx(&ver); 160 if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) 161 onNT = JNI_TRUE; 162 163 assert(cmd != NULL); 164 pcmd = (*env)->GetStringChars(env, cmd, NULL); 165 if (pcmd == NULL) goto Catch; 166 167 if (dir != 0) { 168 pdir = (*env)->GetStringChars(env, dir, NULL); 169 if (pdir == NULL) goto Catch; 170 } 171 if (envBlock != NULL) { 172 penvBlock = ((*env)->GetStringChars(env, envBlock, NULL)); 173 if (penvBlock == NULL) goto Catch; 174 } 175 assert(stdHandles != NULL); 176 handles = (*env)->GetLongArrayElements(env, stdHandles, NULL); 177 if (handles == NULL) goto Catch; 178 179 memset(&si, 0, sizeof(si)); 180 si.cb = sizeof(si); 181 si.dwFlags = STARTF_USESTDHANDLES; 182 183 sa.nLength = sizeof(sa); 184 sa.lpSecurityDescriptor = 0; 185 sa.bInheritHandle = TRUE; 186 187 if (handles[0] != (jlong) -1) { 188 si.hStdInput = (HANDLE) handles[0]; 189 handles[0] = (jlong) -1; 190 } else { 191 if (! CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE)) { 192 win32Error(env, "CreatePipe"); 193 goto Catch; 194 } 195 si.hStdInput = inRead; 196 SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE); 197 handles[0] = (jlong) inWrite; 198 } 199 SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, TRUE); 200 201 if (handles[1] != (jlong) -1) { 202 si.hStdOutput = (HANDLE) handles[1]; 203 handles[1] = (jlong) -1; 204 } else { 205 if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) { 206 win32Error(env, "CreatePipe"); 207 goto Catch; 208 } 209 si.hStdOutput = outWrite; 210 SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE); 211 handles[1] = (jlong) outRead; 212 } 213 SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, TRUE); 214 215 if (redirectErrorStream) { 216 si.hStdError = si.hStdOutput; 217 handles[2] = (jlong) -1; 218 } else if (handles[2] != (jlong) -1) { 219 si.hStdError = (HANDLE) handles[2]; 220 handles[2] = (jlong) -1; 221 } else { 222 if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) { 223 win32Error(env, "CreatePipe"); 224 goto Catch; 225 } 226 si.hStdError = errWrite; 227 SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE); 228 handles[2] = (jlong) errRead; 229 } 230 SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, TRUE); 231 232 if (onNT) 233 processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; 234 else 235 processFlag = selectProcessFlag(env, cmd) | CREATE_UNICODE_ENVIRONMENT; 236 ret = CreateProcessW(0, /* executable name */ 237 (LPWSTR)pcmd, /* command line */ 238 0, /* process security attribute */ 239 0, /* thread security attribute */ 240 TRUE, /* inherits system handles */ 241 processFlag, /* selected based on exe type */ 242 (LPVOID)penvBlock,/* environment block */ 243 (LPCWSTR)pdir, /* change to the new current directory */ 244 &si, /* (in) startup information */ 245 &pi); /* (out) process information */ 246 if (!ret) { 247 win32Error(env, "CreateProcess"); 248 goto Catch; 249 } 250 251 CloseHandle(pi.hThread); 252 ret = (jlong)pi.hProcess; 253 254 Finally: 255 /* Always clean up the child's side of the pipes */ 256 closeSafely(inRead); 257 closeSafely(outWrite); 258 closeSafely(errWrite); 259 260 if (pcmd != NULL) 261 (*env)->ReleaseStringChars(env, cmd, pcmd); 262 if (pdir != NULL) 263 (*env)->ReleaseStringChars(env, dir, pdir); 264 if (penvBlock != NULL) 265 (*env)->ReleaseStringChars(env, envBlock, penvBlock); 266 if (handles != NULL) 267 (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0); 268 return ret; 269 270 Catch: 271 /* Clean up the parent's side of the pipes in case of failure only */ 272 closeSafely(inWrite); 273 closeSafely(outRead); 274 closeSafely(errRead); 275 goto Finally; 276 } 277 278 JNIEXPORT jint JNICALL 279 Java_java_lang_ProcessImpl_getExitCodeProcess(JNIEnv *env, jclass ignored, jlong handle) 280 { 281 DWORD exit_code; 282 if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0) 283 win32Error(env, "GetExitCodeProcess"); 284 return exit_code; 285 } 286 287 JNIEXPORT jint JNICALL 288 Java_java_lang_ProcessImpl_getStillActive(JNIEnv *env, jclass ignored) 289 { 290 return STILL_ACTIVE; 291 } 292 293 JNIEXPORT void JNICALL 294 Java_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlong handle) 295 { 296 HANDLE events[2]; 297 events[0] = (HANDLE) handle; 298 events[1] = JVM_GetThreadInterruptEvent(); 299 300 if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events, 301 FALSE, /* Wait for ANY event */ 302 INFINITE) /* Wait forever */ 303 == WAIT_FAILED) 304 win32Error(env, "WaitForMultipleObjects"); 305 } 306 307 JNIEXPORT void JNICALL 308 Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle) 309 { 310 TerminateProcess((HANDLE) handle, 1); 311 } 312 313 JNIEXPORT jboolean JNICALL 314 Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle) 315 { 316 return CloseHandle((HANDLE) handle); 317 } 318 319 /** 320 * Returns a copy of the Unicode characters of a string. Fow now this 321 * function doesn't handle long path names and other issues. 322 */ 323 static WCHAR* getPath(JNIEnv *env, jstring ps) { 324 WCHAR *pathbuf = NULL; 325 const jchar *chars = (*(env))->GetStringChars(env, ps, NULL); 326 if (chars != NULL) { 327 size_t pathlen = wcslen(chars); 328 pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR)); 329 if (pathbuf == NULL) { 330 JNU_ThrowOutOfMemoryError(env, NULL); 331 } else { 332 wcscpy(pathbuf, chars); 333 } 334 (*env)->ReleaseStringChars(env, ps, chars); 335 } 336 return pathbuf; 337 } 338 339 JNIEXPORT jlong JNICALL 340 Java_java_lang_ProcessImpl_openForAtomicAppend(JNIEnv *env, jclass ignored, jstring path) 341 { 342 const DWORD access = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); 343 const DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; 344 const DWORD disposition = OPEN_ALWAYS; 345 const DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL; 346 HANDLE h; 347 WCHAR *pathbuf = getPath(env, path); 348 if (pathbuf == NULL) { 349 /* Exception already pending */ 350 return -1; 351 } 352 h = CreateFileW( 353 pathbuf, /* Wide char path name */ 354 access, /* Read and/or write permission */ 355 sharing, /* File sharing flags */ 356 NULL, /* Security attributes */ 357 disposition, /* creation disposition */ 358 flagsAndAttributes, /* flags and attributes */ 359 NULL); 360 free(pathbuf); 361 if (h == INVALID_HANDLE_VALUE) { 362 JNU_ThrowIOExceptionWithLastError(env, "CreateFileW"); 363 } 364 return ptr_to_jlong(h); 365 }