1 /*
   2  * Copyright (c) 1997, 2010, 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 static const char EXE_EXT[] = ".exe";
  67 
  68 DWORD
  69 selectProcessFlag(JNIEnv *env, jstring cmd0)
  70 {
  71     DWORD newFlag = 0;
  72     char *exe = (char *)JNU_GetStringPlatformChars(env, cmd0, 0);
  73     if (exe != NULL) {
  74         char buf[MAX_PATH];
  75         char *name;
  76         DWORD len;
  77         exe = extractExecutablePath(env, exe);
  78         if (exe != NULL) {
  79             /* We are here for Win9x/Me, so the [/] is not the path sep */
  80             char *p = strrchr(exe, '\\');
  81             if (p == NULL) {
  82                 len = SearchPath(NULL, exe, EXE_EXT, MAX_PATH, buf, &name);
  83             } else {
  84                 *p = 0;
  85                 len = SearchPath(exe, p + 1, EXE_EXT, MAX_PATH, buf, &name);
  86             }
  87         }
  88 
  89         if (len > 0 && len < MAX_PATH) {
  90             /* Here the [buf] path is valid and null terminated */
  91             int fd = _open(buf, _O_RDONLY);
  92             if (fd != -1) {
  93                 unsigned char buffer[2];
  94                 if (_read(fd, buffer, 2) == 2
  95                     && buffer[0] == 'M' && buffer[1] == 'Z'
  96                     && _lseek(fd, 60L, SEEK_SET) == 60L
  97                     && _read(fd, buffer, 2) == 2)
  98                 {
  99                     long headerLoc = (long)buffer[1] << 8 | (long)buffer[0];
 100                     if (_lseek(fd, headerLoc, SEEK_SET) == headerLoc
 101                         && _read(fd, buffer, 2) == 2
 102                         && buffer[0] == 'P' && buffer[1] == 'E')
 103                     {
 104                         newFlag = DETACHED_PROCESS;
 105                     }
 106                 }
 107                 _close(fd);
 108             }
 109         }
 110         JNU_ReleaseStringPlatformChars(env, cmd0, exe);
 111     }
 112     return newFlag;
 113 }
 114 
 115 static void
 116 win32Error(JNIEnv *env, const char *functionName)
 117 {
 118     static const char * const format = "%s error=%d, %s";
 119     static const char * const fallbackFormat = "%s failed, error=%d";
 120     char buf[256];
 121     char errmsg[sizeof(buf) + 100];
 122     const int errnum = GetLastError();
 123     const int n = JVM_GetLastErrorString(buf, sizeof(buf));
 124     if (n > 0)
 125         sprintf(errmsg, format, functionName, errnum, buf);
 126     else
 127         sprintf(errmsg, fallbackFormat, functionName, errnum);
 128     JNU_ThrowIOException(env, errmsg);
 129 }
 130 
 131 static void
 132 closeSafely(HANDLE handle)
 133 {
 134     if (handle != INVALID_HANDLE_VALUE)
 135         CloseHandle(handle);
 136 }
 137 
 138 JNIEXPORT jlong JNICALL
 139 Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
 140                                   jstring cmd,
 141                                   jstring envBlock,
 142                                   jstring dir,
 143                                   jlongArray stdHandles,
 144                                   jboolean redirectErrorStream)
 145 {
 146     HANDLE inRead   = INVALID_HANDLE_VALUE;
 147     HANDLE inWrite  = INVALID_HANDLE_VALUE;
 148     HANDLE outRead  = INVALID_HANDLE_VALUE;
 149     HANDLE outWrite = INVALID_HANDLE_VALUE;
 150     HANDLE errRead  = INVALID_HANDLE_VALUE;
 151     HANDLE errWrite = INVALID_HANDLE_VALUE;
 152     SECURITY_ATTRIBUTES sa;
 153     PROCESS_INFORMATION pi;
 154     STARTUPINFOW si;
 155     const jchar*  pcmd = NULL;
 156     const jchar*  pdir = NULL;
 157     const jchar*  penvBlock = NULL;
 158     jlong  *handles = NULL;
 159     jlong ret = 0;
 160     OSVERSIONINFO ver;
 161     jboolean onNT = JNI_FALSE;
 162     DWORD processFlag;
 163 
 164     ver.dwOSVersionInfoSize = sizeof(ver);
 165     GetVersionEx(&ver);
 166     if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
 167         onNT = JNI_TRUE;
 168 
 169     assert(cmd != NULL);
 170     pcmd = (*env)->GetStringChars(env, cmd, NULL);
 171     if (pcmd == NULL) goto Catch;
 172 
 173     if (dir != 0) {
 174         pdir = (*env)->GetStringChars(env, dir, NULL);
 175         if (pdir == NULL) goto Catch;
 176     }
 177     if (envBlock != NULL) {
 178         penvBlock = ((*env)->GetStringChars(env, envBlock, NULL));
 179         if (penvBlock == NULL) goto Catch;
 180     }
 181     assert(stdHandles != NULL);
 182     handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);
 183     if (handles == NULL) goto Catch;
 184 
 185     memset(&si, 0, sizeof(si));
 186     si.cb = sizeof(si);
 187     si.dwFlags = STARTF_USESTDHANDLES;
 188 
 189     sa.nLength = sizeof(sa);
 190     sa.lpSecurityDescriptor = 0;
 191     sa.bInheritHandle = TRUE;
 192 
 193     if (handles[0] != (jlong) -1) {
 194         si.hStdInput = (HANDLE) handles[0];
 195         handles[0] = (jlong) -1;
 196     } else {
 197         if (! CreatePipe(&inRead,  &inWrite,  &sa, PIPE_SIZE)) {
 198             win32Error(env, "CreatePipe");
 199             goto Catch;
 200         }
 201         si.hStdInput = inRead;
 202         SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, 0);
 203         handles[0] = (jlong) inWrite;
 204     }
 205     SetHandleInformation(si.hStdInput,
 206         HANDLE_FLAG_INHERIT,
 207         HANDLE_FLAG_INHERIT);
 208 
 209     if (handles[1] != (jlong) -1) {
 210         si.hStdOutput = (HANDLE) handles[1];
 211         handles[1] = (jlong) -1;
 212     } else {
 213         if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) {
 214             win32Error(env, "CreatePipe");
 215             goto Catch;
 216         }
 217         si.hStdOutput = outWrite;
 218         SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, 0);
 219         handles[1] = (jlong) outRead;
 220     }
 221     SetHandleInformation(si.hStdOutput,
 222         HANDLE_FLAG_INHERIT,
 223         HANDLE_FLAG_INHERIT);
 224 
 225     if (redirectErrorStream) {
 226         si.hStdError = si.hStdOutput;
 227         handles[2] = (jlong) -1;
 228     } else if (handles[2] != (jlong) -1) {
 229         si.hStdError = (HANDLE) handles[2];
 230         handles[2] = (jlong) -1;
 231     } else {
 232         if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) {
 233             win32Error(env, "CreatePipe");
 234             goto Catch;
 235         }
 236         si.hStdError = errWrite;
 237         SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, 0);
 238         handles[2] = (jlong) errRead;
 239     }
 240     SetHandleInformation(si.hStdError,
 241         HANDLE_FLAG_INHERIT,
 242         HANDLE_FLAG_INHERIT);
 243 
 244     if (onNT)
 245         processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
 246     else
 247         processFlag = selectProcessFlag(env, cmd) | CREATE_UNICODE_ENVIRONMENT;
 248     ret = CreateProcessW(0,                /* executable name */
 249                          (LPWSTR)pcmd,     /* command line */
 250                          0,                /* process security attribute */
 251                          0,                /* thread security attribute */
 252                          TRUE,             /* inherits system handles */
 253                          processFlag,      /* selected based on exe type */
 254                          (LPVOID)penvBlock,/* environment block */
 255                          (LPCWSTR)pdir,    /* change to the new current directory */
 256                          &si,              /* (in)  startup information */
 257                          &pi);             /* (out) process information */
 258     if (!ret) {
 259         win32Error(env, "CreateProcess");
 260         goto Catch;
 261     }
 262 
 263     CloseHandle(pi.hThread);
 264     ret = (jlong)pi.hProcess;
 265 
 266  Finally:
 267     /* Always clean up the child's side of the pipes */
 268     closeSafely(inRead);
 269     closeSafely(outWrite);
 270     closeSafely(errWrite);
 271 
 272     if (pcmd != NULL)
 273         (*env)->ReleaseStringChars(env, cmd, pcmd);
 274     if (pdir != NULL)
 275         (*env)->ReleaseStringChars(env, dir, pdir);
 276     if (penvBlock != NULL)
 277         (*env)->ReleaseStringChars(env, envBlock, penvBlock);
 278     if (handles != NULL)
 279         (*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
 280     return ret;
 281 
 282  Catch:
 283     /* Clean up the parent's side of the pipes in case of failure only */
 284     closeSafely(inWrite);
 285     closeSafely(outRead);
 286     closeSafely(errRead);
 287     goto Finally;
 288 }
 289 
 290 JNIEXPORT jint JNICALL
 291 Java_java_lang_ProcessImpl_getExitCodeProcess(JNIEnv *env, jclass ignored, jlong handle)
 292 {
 293     DWORD exit_code;
 294     if (GetExitCodeProcess((HANDLE) handle, &exit_code) == 0)
 295         win32Error(env, "GetExitCodeProcess");
 296     return exit_code;
 297 }
 298 
 299 JNIEXPORT jint JNICALL
 300 Java_java_lang_ProcessImpl_getStillActive(JNIEnv *env, jclass ignored)
 301 {
 302     return STILL_ACTIVE;
 303 }
 304 
 305 JNIEXPORT void JNICALL
 306 Java_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlong handle)
 307 {
 308     HANDLE events[2];
 309     events[0] = (HANDLE) handle;
 310     events[1] = JVM_GetThreadInterruptEvent();
 311 
 312     if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
 313                                FALSE,    /* Wait for ANY event */
 314                                INFINITE) /* Wait forever */
 315         == WAIT_FAILED)
 316         win32Error(env, "WaitForMultipleObjects");
 317 }
 318 
 319 JNIEXPORT void JNICALL
 320 Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle)
 321 {
 322     TerminateProcess((HANDLE) handle, 1);
 323 }
 324 
 325 JNIEXPORT jboolean JNICALL
 326 Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle)
 327 {
 328     return CloseHandle((HANDLE) handle);
 329 }
 330 
 331 /**
 332  * Returns a copy of the Unicode characters of a string. Fow now this
 333  * function doesn't handle long path names and other issues.
 334  */
 335 static WCHAR* getPath(JNIEnv *env, jstring ps) {
 336     WCHAR *pathbuf = NULL;
 337     const jchar *chars = (*(env))->GetStringChars(env, ps, NULL);
 338     if (chars != NULL) {
 339         size_t pathlen = wcslen(chars);
 340         pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
 341         if (pathbuf == NULL) {
 342             JNU_ThrowOutOfMemoryError(env, NULL);
 343         } else {
 344             wcscpy(pathbuf, chars);
 345         }
 346         (*env)->ReleaseStringChars(env, ps, chars);
 347     }
 348     return pathbuf;
 349 }
 350 
 351 JNIEXPORT jlong JNICALL
 352 Java_java_lang_ProcessImpl_openForAtomicAppend(JNIEnv *env, jclass ignored, jstring path)
 353 {
 354     const DWORD access = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA);
 355     const DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
 356     const DWORD disposition = OPEN_ALWAYS;
 357     const DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
 358     HANDLE h;
 359     WCHAR *pathbuf = getPath(env, path);
 360     if (pathbuf == NULL) {
 361         /* Exception already pending */
 362         return -1;
 363     }
 364     h = CreateFileW(
 365         pathbuf,            /* Wide char path name */
 366         access,             /* Read and/or write permission */
 367         sharing,            /* File sharing flags */
 368         NULL,               /* Security attributes */
 369         disposition,        /* creation disposition */
 370         flagsAndAttributes, /* flags and attributes */
 371         NULL);
 372     free(pathbuf);
 373     if (h == INVALID_HANDLE_VALUE) {
 374         JNU_ThrowIOExceptionWithLastError(env, "CreateFileW");
 375     }
 376     return ptr_to_jlong(h);
 377 }