1 /*
2 * Copyright (c) 2014, 2020, 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
Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv * env,jclass clazz)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
Java_java_lang_ProcessHandleImpl_initNative(JNIEnv * env,jclass clazz)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
Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv * env,jclass junk,jlong jpid,jboolean reapStatus)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
Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv * env,jclass clazz)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
Java_java_lang_ProcessHandleImpl_parent0(JNIEnv * env,jclass clazz,jlong jpid,jlong startTime)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 return -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
Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv * env,jclass clazz,jlong jpid,jlongArray jarray,jlongArray jparentArray,jlongArray jstimesArray)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 return 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 */
jlong_from(jint high,jint low)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 */
getStartTime(HANDLE handle)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
Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv * env,jclass clazz,jlong jpid,jlong startTime,jboolean force)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
Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv * env,jclass clazz,jlong jpid)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
Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv * env,jobject jinfo,jlong jpid)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 */
getStatInfo(JNIEnv * env,HANDLE handle,jobject jinfo)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
getCmdlineInfo(JNIEnv * env,HANDLE handle,jobject jinfo)471 static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
472 WCHAR exeName[1024];
473 WCHAR *longPath;
474 DWORD bufsize = sizeof(exeName)/sizeof(WCHAR);
475 jstring commandObj = NULL;
476
477 if (QueryFullProcessImageNameW(handle, 0, exeName, &bufsize)) {
478 commandObj = (*env)->NewString(env, (const jchar *)exeName,
479 (jsize)wcslen(exeName));
480 } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
481 bufsize = 32768;
482 longPath = (WCHAR*)malloc(bufsize * sizeof(WCHAR));
483 if (longPath != NULL) {
484 if (QueryFullProcessImageNameW(handle, 0, longPath, &bufsize)) {
485 commandObj = (*env)->NewString(env, (const jchar *)longPath,
486 (jsize)wcslen(longPath));
487 }
488 free(longPath);
489 }
490 }
491 CHECK_NULL(commandObj);
492 (*env)->SetObjectField(env, jinfo,
493 ProcessHandleImpl_Info_commandID, commandObj);
494 }
495
procToUser(JNIEnv * env,HANDLE handle,jobject jinfo)496 static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo) {
497 #define TOKEN_LEN 256
498 DWORD token_len = TOKEN_LEN;
499 char token_buf[TOKEN_LEN];
500 TOKEN_USER *token_user = (TOKEN_USER*)token_buf;
501 HANDLE tokenHandle;
502 WCHAR domain[255 + 1 + 255 + 1]; // large enough to concat with '/' and name
503 WCHAR name[255 + 1];
504 DWORD domainLen = sizeof(domain) - sizeof(name);
505 DWORD nameLen = sizeof(name);
506 SID_NAME_USE use;
507 jstring s;
508 int ret;
509
510 if (!OpenProcessToken(handle, TOKEN_READ, &tokenHandle)) {
511 return;
512 }
513
514 ret = GetTokenInformation(tokenHandle, TokenUser, token_user,
515 token_len, &token_len);
516 CloseHandle(tokenHandle); // always close handle
517 if (!ret) {
518 JNU_ThrowByNameWithLastError(env,
519 "java/lang/RuntimeException", "GetTokenInformation");
520 return;
521 }
522
523 if (LookupAccountSidW(NULL, token_user->User.Sid, &name[0], &nameLen,
524 &domain[0], &domainLen, &use) == 0) {
525 // Name not available, convert to a String
526 LPWSTR str;
527 if (ConvertSidToStringSidW(token_user->User.Sid, &str) == 0) {
528 return;
529 }
530 s = (*env)->NewString(env, (const jchar *)str, (jsize)wcslen(str));
531 LocalFree(str);
532 } else {
533 wcscat(domain, L"\\");
534 wcscat(domain, name);
535 s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain));
536 }
537 CHECK_NULL(s);
538 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, s);
539 }
540