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 }