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