1 /*
   2  * Copyright (c) 2014, 2015, 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 
  27 #include "jni.h"
  28 #include "jvm.h"
  29 #include "jni_util.h"
  30 #include "java_lang_ProcessHandleImpl.h"
  31 #include "java_lang_ProcessHandleImpl_Info.h"
  32 
  33 #include <windows.h>
  34 #include <tlhelp32.h>
  35 #include <sddl.h>
  36 
  37 static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
  38 static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
  39 static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo);
  40 
  41 /**************************************************************
  42  * Implementation of ProcessHandleImpl_Info native methods.
  43  */
  44 
  45 /* Field id for jString 'command' in java.lang.ProcessHandle.Info */
  46 static jfieldID ProcessHandleImpl_Info_commandID;
  47 
  48 /* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */
  49 static jfieldID ProcessHandleImpl_Info_commandLineID;
  50 
  51 /* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
  52 static jfieldID ProcessHandleImpl_Info_argumentsID;
  53 
  54 /* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
  55 static jfieldID ProcessHandleImpl_Info_totalTimeID;
  56 
  57 /* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
  58 static jfieldID ProcessHandleImpl_Info_startTimeID;
  59 
  60 /* Field id for jString 'accountName' in java.lang.ProcessHandleImpl.UserPrincipal */
  61 static jfieldID ProcessHandleImpl_Info_userID;
  62 
  63 /**************************************************************
  64  * Static method to initialize field IDs.
  65  *
  66  * Class:     java_lang_ProcessHandleImpl_Info
  67  * Method:    initIDs
  68  * Signature: ()V
  69  */
  70 JNIEXPORT void JNICALL
  71 Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
  72 
  73     CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
  74         clazz, "command", "Ljava/lang/String;"));
  75     CHECK_NULL(ProcessHandleImpl_Info_commandLineID = (*env)->GetFieldID(env,
  76         clazz, "commandLine", "Ljava/lang/String;"));
  77     CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
  78         clazz, "arguments", "[Ljava/lang/String;"));
  79     CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
  80         clazz, "totalTime", "J"));
  81     CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
  82         clazz, "startTime", "J"));
  83     CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
  84         clazz, "user", "Ljava/lang/String;"));
  85 }
  86 /**************************************************************
  87  * Static method to initialize native.
  88  *
  89  * Class:     java_lang_ProcessHandleImpl
  90  * Method:    initNative
  91  * Signature: ()V
  92  */
  93 JNIEXPORT void JNICALL
  94 Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
  95 }
  96 
  97 /*
  98  * Block until a child process exits and return its exit code.
  99  */
 100 JNIEXPORT jint JNICALL
 101 Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
 102                                                      jclass junk,
 103                                                      jlong jpid,
 104                                                      jboolean reapStatus) {
 105     DWORD pid = (DWORD)jpid;
 106     DWORD exitValue = -1;
 107     HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION,
 108                                 FALSE, pid);
 109     if (handle == NULL) {
 110         return exitValue;          // No process with that pid is alive
 111     }
 112     do {
 113         if (!GetExitCodeProcess(handle, &exitValue)) {
 114             JNU_ThrowByNameWithLastError(env,
 115                 "java/lang/RuntimeException", "GetExitCodeProcess");
 116             break;
 117         }
 118         if (exitValue == STILL_ACTIVE) {
 119             HANDLE events[2];
 120             events[0] = handle;
 121             events[1] = JVM_GetThreadInterruptEvent();
 122 
 123             if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
 124                                        FALSE,    /* Wait for ANY event */
 125                                        INFINITE) /* Wait forever */
 126                 == WAIT_FAILED) {
 127                 JNU_ThrowByNameWithLastError(env,
 128                     "java/lang/RuntimeException", "WaitForMultipleObjects");
 129                 break;
 130             }
 131         }
 132     } while (exitValue == STILL_ACTIVE);
 133     CloseHandle(handle);         // Ignore return code
 134     return exitValue;
 135 }
 136 
 137 /*
 138  * Returns the pid of the caller.
 139  *
 140  * Class:     java_lang_ProcessHandleImpl
 141  * Method:    getCurrentPid0
 142  * Signature: ()J
 143  */
 144 JNIEXPORT jlong JNICALL
 145 Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) {
 146     DWORD  pid = GetCurrentProcessId();
 147     return (jlong)pid;
 148 }
 149 
 150 /*
 151  * Returns the parent pid of the requested pid.
 152  *
 153  * Class:     java_lang_ProcessHandleImpl
 154  * Method:    parent0
 155  * Signature: (J)J
 156  */
 157 JNIEXPORT jlong JNICALL
 158 Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
 159                                          jclass clazz,
 160                                          jlong jpid,
 161                                          jlong startTime) {
 162     DWORD ppid = 0;
 163     DWORD wpid = (DWORD)jpid;
 164     PROCESSENTRY32 pe32;
 165     HANDLE hProcessSnap;
 166     jlong start;
 167 
 168     start = Java_java_lang_ProcessHandleImpl_isAlive0(env, clazz, jpid);
 169     if (start != startTime && start != 0 && startTime != 0) {
 170         return -1;
 171     }
 172 
 173     // Take a snapshot of all processes in the system.
 174     hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 175     if (hProcessSnap == INVALID_HANDLE_VALUE) {
 176         JNU_ThrowByName(env,
 177             "java/lang/RuntimeException", "snapshot not available");
 178         return -1;
 179     }
 180 
 181     // Retrieve information about the first process,
 182     pe32.dwSize = sizeof (PROCESSENTRY32);
 183     if (Process32First(hProcessSnap, &pe32)) {
 184         // Now walk the snapshot of processes, and
 185         do {
 186             if (wpid == pe32.th32ProcessID) {
 187                 // The parent PID may be stale if that process has exited
 188                 // and may have been reused.
 189                 // A valid parent's start time is the same or before the child's
 190                 jlong ppStartTime = Java_java_lang_ProcessHandleImpl_isAlive0(env,
 191                         clazz, pe32.th32ParentProcessID);
 192                 if (ppStartTime > 0 && ppStartTime <= startTime) {
 193                     ppid = pe32.th32ParentProcessID;
 194                 }
 195                 break;
 196             }
 197         } while (Process32Next(hProcessSnap, &pe32));
 198     } else {
 199         JNU_ThrowByName(env,
 200             "java/lang/RuntimeException", "snapshot not available");
 201         ppid = (DWORD)-1;
 202     }
 203     CloseHandle(hProcessSnap); // Ignore return code
 204     return (jlong)ppid;
 205 }
 206 
 207 /*
 208  * Returns the children of the requested pid and optionally each parent.
 209  *
 210  * Class:     java_lang_ProcessHandleImpl
 211  * Method:    getChildPids
 212  * Signature: (J[J[J)I
 213  */
 214 JNIEXPORT jint JNICALL
 215 Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
 216                                                  jclass clazz,
 217                                                  jlong jpid,
 218                                                  jlongArray jarray,
 219                                                  jlongArray jparentArray,
 220                                                  jlongArray jstimesArray) {
 221     HANDLE hProcessSnap;
 222     PROCESSENTRY32 pe32;
 223     DWORD ppid = (DWORD)jpid;
 224     jlong* pids = NULL;
 225     jlong* ppids = NULL;
 226     jlong* stimes = NULL;
 227     jsize parentArraySize = 0;
 228     jsize arraySize = 0;
 229     jsize stimesSize = 0;
 230     jsize count = 0;
 231 
 232     arraySize = (*env)->GetArrayLength(env, jarray);
 233     JNU_CHECK_EXCEPTION_RETURN(env, -1);
 234     if (jparentArray != NULL) {
 235         parentArraySize = (*env)->GetArrayLength(env, jparentArray);
 236         JNU_CHECK_EXCEPTION_RETURN(env, -1);
 237 
 238         if (arraySize != parentArraySize) {
 239             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
 240             return 0;
 241         }
 242     }
 243     if (jstimesArray != NULL) {
 244         stimesSize = (*env)->GetArrayLength(env, jstimesArray);
 245         JNU_CHECK_EXCEPTION_RETURN(env, -1);
 246 
 247         if (arraySize != stimesSize) {
 248             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
 249             return 0;
 250         }
 251     }
 252 
 253     // Take a snapshot of all processes in the system.
 254     hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 255     if (hProcessSnap == INVALID_HANDLE_VALUE) {
 256         JNU_ThrowByName(env,
 257             "java/lang/RuntimeException", "snapshot not available");
 258         return 0;
 259     }
 260 
 261     // Retrieve information about the first process,
 262     pe32.dwSize = sizeof (PROCESSENTRY32);
 263     if (Process32First(hProcessSnap, &pe32)) {
 264         do { // Block to break out of on Exception
 265             pids = (*env)->GetLongArrayElements(env, jarray, NULL);
 266             if (pids == NULL) {
 267                 break;
 268             }
 269             if (jparentArray != NULL) {
 270                 ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
 271                 if (ppids == NULL) {
 272                     break;
 273                 }
 274             }
 275             if (jstimesArray != NULL) {
 276                 stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
 277                 if (stimes == NULL) {
 278                     break;
 279                 }
 280             }
 281             // Now walk the snapshot of processes, and
 282             // save information about each process in turn
 283             do {
 284                 if (ppid == 0 ||
 285                     (pe32.th32ParentProcessID > 0
 286                     && (pe32.th32ParentProcessID == ppid))) {
 287                     if (count < arraySize) {
 288                         // Only store if it fits
 289                         pids[count] = (jlong) pe32.th32ProcessID;
 290                         if (ppids != NULL) {
 291                             // Store the parentPid
 292                             ppids[count] = (jlong) pe32.th32ParentProcessID;
 293                         }
 294                         if (stimes != NULL) {
 295                             // Store the process start time
 296                             stimes[count] =
 297                                     Java_java_lang_ProcessHandleImpl_isAlive0(env,
 298                                             clazz, (jlong) pe32.th32ProcessID);
 299                         }
 300                     }
 301                     count++;    // Count to tabulate size needed
 302                 }
 303             } while (Process32Next(hProcessSnap, &pe32));
 304         } while (0);
 305 
 306         if (pids != NULL) {
 307             (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
 308         }
 309         if (ppids != NULL) {
 310             (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
 311         }
 312         if (stimes != NULL) {
 313             (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
 314         }
 315     } else {
 316         JNU_ThrowByName(env,
 317             "java/lang/RuntimeException", "snapshot not available");
 318         count = 0;
 319     }
 320     CloseHandle(hProcessSnap);
 321     // If more pids than array had size for;  count will be greater than array size
 322     return (jint)count;
 323 }
 324 
 325 /**
 326  * Assemble a 64 bit value from two 32 bit values.
 327  */
 328 static jlong jlong_from(jint high, jint low) {
 329     jlong result = 0;
 330     result = ((jlong)high << 32) | ((0x000000000ffffffff) & (jlong)low);
 331     return result;
 332 }
 333 
 334 /*
 335  * Get the start time in ms from 1970 from the handle.
 336  */
 337 static jlong getStartTime(HANDLE handle) {
 338     FILETIME CreationTime, ExitTime, KernelTime, UserTime;
 339     if (GetProcessTimes(handle, &CreationTime, &ExitTime, &KernelTime, &UserTime)) {
 340         jlong start = jlong_from(CreationTime.dwHighDateTime,
 341                                  CreationTime.dwLowDateTime) / 10000;
 342         start -= 11644473600000L; // Rebase Epoch from 1601 to 1970
 343         return start;
 344     } else {
 345         return 0;
 346     }
 347 }
 348 
 349 /*
 350  * Destroy the process.
 351  *
 352  * Class:     java_lang_ProcessHandleImpl
 353  * Method:    destroy0
 354  * Signature: (Z)V
 355  */
 356 JNIEXPORT jboolean JNICALL
 357 Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
 358                                           jclass clazz,
 359                                           jlong jpid,
 360                                           jlong startTime,
 361                                           jboolean force) {
 362     DWORD pid = (DWORD)jpid;
 363     jboolean ret = JNI_FALSE;
 364     HANDLE handle = OpenProcess(PROCESS_TERMINATE | THREAD_QUERY_INFORMATION
 365                                 | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
 366     if (handle != NULL) {
 367         jlong start = getStartTime(handle);
 368         if (start == startTime || startTime == 0) {
 369             ret = TerminateProcess(handle, 1) ? JNI_TRUE : JNI_FALSE;
 370         }
 371         CloseHandle(handle);         // Ignore return code
 372     }
 373     return ret;
 374 }
 375 
 376  /*
 377  * Check if a process is alive.
 378  * Return the start time (ms since 1970) if it is available.
 379  * If the start time is not available return 0.
 380  * If the pid is invalid, return -1.
 381  *
 382  * Class:     java_lang_ProcessHandleImpl
 383  * Method:    isAlive0
 384  * Signature: (J)J
 385  */
 386 JNIEXPORT jlong JNICALL
 387 Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jclass clazz, jlong jpid) {
 388     DWORD pid = (DWORD)jpid;
 389 
 390     jlong ret = -1;
 391     HANDLE handle =
 392         OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
 393                     FALSE, pid);
 394     if (handle != NULL) {
 395         DWORD dwExitStatus;
 396 
 397         GetExitCodeProcess(handle, &dwExitStatus);
 398         if (dwExitStatus == STILL_ACTIVE) {
 399             ret = getStartTime(handle);
 400         } else {
 401             ret = -1;
 402         }
 403         CloseHandle(handle); // Ignore return code
 404    }
 405    return ret;
 406 }
 407 
 408 /*
 409  * Fill in the Info object from the OS information about the process.
 410  *
 411  * Class:     java_lang_ProcessHandleImpl
 412  * Method:    info0
 413  * Signature: (J)V
 414  */
 415 JNIEXPORT void JNICALL
 416 Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
 417                                                  jobject jinfo,
 418                                                  jlong jpid) {
 419     DWORD pid = (DWORD)jpid;
 420     int ret = 0;
 421     HANDLE handle =
 422         OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
 423                     FALSE, pid);
 424     if (handle == NULL) {
 425         return;
 426     }
 427     getStatInfo(env, handle, jinfo);
 428     getCmdlineInfo(env, handle, jinfo);
 429     procToUser(env, handle, jinfo);
 430 
 431     CloseHandle(handle);                // Ignore return code
 432 }
 433 
 434 /**
 435  * Read /proc/<pid>/stat and fill in the fields of the Info object.
 436  * The executable name, plus the user, system, and start times are gathered.
 437  */
 438 static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
 439     FILETIME CreationTime;
 440     FILETIME ExitTime;
 441     FILETIME KernelTime;
 442     FILETIME UserTime;
 443     jlong userTime;             // nanoseconds
 444     jlong totalTime;            // nanoseconds
 445     jlong startTime;            // nanoseconds
 446     UserTime.dwHighDateTime = 0;
 447     UserTime.dwLowDateTime = 0;
 448     KernelTime.dwHighDateTime = 0;
 449     KernelTime.dwLowDateTime = 0;
 450     CreationTime.dwHighDateTime = 0;
 451     CreationTime.dwLowDateTime = 0;
 452 
 453     if (GetProcessTimes(handle, &CreationTime, &ExitTime, &KernelTime, &UserTime)) {
 454         userTime = jlong_from(UserTime.dwHighDateTime, UserTime.dwLowDateTime);
 455         totalTime = jlong_from( KernelTime.dwHighDateTime, KernelTime.dwLowDateTime);
 456         totalTime = (totalTime + userTime) * 100;  // convert sum to nano-seconds
 457 
 458         startTime = jlong_from(CreationTime.dwHighDateTime,
 459                                CreationTime.dwLowDateTime) / 10000;
 460         startTime -= 11644473600000L; // Rebase Epoch from 1601 to 1970
 461 
 462         (*env)->SetLongField(env, jinfo,
 463                              ProcessHandleImpl_Info_totalTimeID, totalTime);
 464         JNU_CHECK_EXCEPTION(env);
 465         (*env)->SetLongField(env, jinfo,
 466                              ProcessHandleImpl_Info_startTimeID, startTime);
 467         JNU_CHECK_EXCEPTION(env);
 468     }
 469 }
 470 
 471 static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
 472     WCHAR exeName[1024];
 473     DWORD bufsize = sizeof(exeName)/sizeof(WCHAR);
 474     jstring commandObj;
 475 
 476     if (QueryFullProcessImageNameW(handle, 0,  exeName, &bufsize)) {
 477         commandObj = (*env)->NewString(env, (const jchar *)exeName,
 478                                        (jsize)wcslen(exeName));
 479         CHECK_NULL(commandObj);
 480         (*env)->SetObjectField(env, jinfo,
 481                                ProcessHandleImpl_Info_commandID, commandObj);
 482     }
 483 }
 484 
 485 static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo) {
 486 #define TOKEN_LEN 256
 487     DWORD token_len = TOKEN_LEN;
 488     char token_buf[TOKEN_LEN];
 489     TOKEN_USER *token_user = (TOKEN_USER*)token_buf;
 490     HANDLE tokenHandle;
 491     WCHAR domain[255 + 1 + 255 + 1];    // large enough to concat with '/' and name
 492     WCHAR name[255 + 1];
 493     DWORD domainLen = sizeof(domain) - sizeof(name);
 494     DWORD nameLen = sizeof(name);
 495     SID_NAME_USE use;
 496     jstring s;
 497     int ret;
 498 
 499     if (!OpenProcessToken(handle, TOKEN_READ, &tokenHandle)) {
 500         return;
 501     }
 502 
 503     ret = GetTokenInformation(tokenHandle, TokenUser, token_user,
 504                               token_len, &token_len);
 505     CloseHandle(tokenHandle);           // always close handle
 506     if (!ret) {
 507         JNU_ThrowByNameWithLastError(env,
 508             "java/lang/RuntimeException", "GetTokenInformation");
 509         return;
 510     }
 511 
 512     if (LookupAccountSidW(NULL, token_user->User.Sid, &name[0], &nameLen,
 513                           &domain[0], &domainLen, &use) == 0) {
 514         // Name not available, convert to a String
 515         LPWSTR str;
 516         if (ConvertSidToStringSidW(token_user->User.Sid, &str) == 0) {
 517             return;
 518         }
 519         s = (*env)->NewString(env, (const jchar *)str, (jsize)wcslen(str));
 520         LocalFree(str);
 521     } else {
 522         wcscat(domain, L"\\");
 523         wcscat(domain, name);
 524         s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain));
 525     }
 526     CHECK_NULL(s);
 527     (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, s);
 528 }