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
37static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
38static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
39static 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 */
46static jfieldID ProcessHandleImpl_Info_commandID;
47
48/* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */
49static jfieldID ProcessHandleImpl_Info_commandLineID;
50
51/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
52static jfieldID ProcessHandleImpl_Info_argumentsID;
53
54/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
55static jfieldID ProcessHandleImpl_Info_totalTimeID;
56
57/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
58static jfieldID ProcessHandleImpl_Info_startTimeID;
59
60/* Field id for jString 'accountName' in java.lang.ProcessHandleImpl.UserPrincipal */
61static 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 */
70JNIEXPORT void JNICALL
71Java_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 */
93JNIEXPORT void JNICALL
94Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
95}
96
97/*
98 * Block until a child process exits and return its exit code.
99 */
100JNIEXPORT jint JNICALL
101Java_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 */
144JNIEXPORT jlong JNICALL
145Java_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 */
157JNIEXPORT jlong JNICALL
158Java_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 */
214JNIEXPORT jint JNICALL
215Java_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 */
328static 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 */
337static 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 */
356JNIEXPORT jboolean JNICALL
357Java_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 */
386JNIEXPORT jlong JNICALL
387Java_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 */
415JNIEXPORT void JNICALL
416Java_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 */
438static 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
471static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
472    char exeName[1024];
473    int bufsize = sizeof exeName;
474    jstring commandObj;
475
476    if (QueryFullProcessImageName(handle, 0,  exeName, &bufsize)) {
477        commandObj = (*env)->NewStringUTF(env, exeName);
478        CHECK_NULL(commandObj);
479        (*env)->SetObjectField(env, jinfo,
480                               ProcessHandleImpl_Info_commandID, commandObj);
481    }
482}
483
484static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo) {
485#define TOKEN_LEN 256
486    DWORD token_len = TOKEN_LEN;
487    char token_buf[TOKEN_LEN];
488    TOKEN_USER *token_user = (TOKEN_USER*)token_buf;
489    HANDLE tokenHandle;
490    WCHAR domain[255 + 1 + 255 + 1];    // large enough to concat with '/' and name
491    WCHAR name[255 + 1];
492    DWORD domainLen = sizeof(domain) - sizeof(name);
493    DWORD nameLen = sizeof(name);
494    SID_NAME_USE use;
495    jstring s;
496    int ret;
497
498    if (!OpenProcessToken(handle, TOKEN_READ, &tokenHandle)) {
499        return;
500    }
501
502    ret = GetTokenInformation(tokenHandle, TokenUser, token_user,
503                              token_len, &token_len);
504    CloseHandle(tokenHandle);           // always close handle
505    if (!ret) {
506        JNU_ThrowByNameWithLastError(env,
507            "java/lang/RuntimeException", "GetTokenInformation");
508        return;
509    }
510
511    if (LookupAccountSidW(NULL, token_user->User.Sid, &name[0], &nameLen,
512                          &domain[0], &domainLen, &use) == 0) {
513        // Name not available, convert to a String
514        LPWSTR str;
515        if (ConvertSidToStringSidW(token_user->User.Sid, &str) == 0) {
516            return;
517        }
518        s = (*env)->NewString(env, (const jchar *)str, (jsize)wcslen(str));
519        LocalFree(str);
520    } else {
521        wcscat(domain, L"\\");
522        wcscat(domain, name);
523        s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain));
524    }
525    CHECK_NULL(s);
526    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, s);
527}
528