Path: blob/master/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c
41119 views
/*1* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/242526#include "jni.h"27#include "jvm.h"28#include "jni_util.h"29#include "java_lang_ProcessHandleImpl.h"30#include "java_lang_ProcessHandleImpl_Info.h"3132#include <windows.h>33#include <tlhelp32.h>34#include <sddl.h>3536static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo);37static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo);38static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo);3940/**************************************************************41* Implementation of ProcessHandleImpl_Info native methods.42*/4344/* Field id for jString 'command' in java.lang.ProcessHandle.Info */45static jfieldID ProcessHandleImpl_Info_commandID;4647/* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */48static jfieldID ProcessHandleImpl_Info_commandLineID;4950/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */51static jfieldID ProcessHandleImpl_Info_argumentsID;5253/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */54static jfieldID ProcessHandleImpl_Info_totalTimeID;5556/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */57static jfieldID ProcessHandleImpl_Info_startTimeID;5859/* Field id for jString 'accountName' in java.lang.ProcessHandleImpl.UserPrincipal */60static jfieldID ProcessHandleImpl_Info_userID;6162/**************************************************************63* Static method to initialize field IDs.64*65* Class: java_lang_ProcessHandleImpl_Info66* Method: initIDs67* Signature: ()V68*/69JNIEXPORT void JNICALL70Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {7172CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,73clazz, "command", "Ljava/lang/String;"));74CHECK_NULL(ProcessHandleImpl_Info_commandLineID = (*env)->GetFieldID(env,75clazz, "commandLine", "Ljava/lang/String;"));76CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,77clazz, "arguments", "[Ljava/lang/String;"));78CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,79clazz, "totalTime", "J"));80CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,81clazz, "startTime", "J"));82CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,83clazz, "user", "Ljava/lang/String;"));84}85/**************************************************************86* Static method to initialize native.87*88* Class: java_lang_ProcessHandleImpl89* Method: initNative90* Signature: ()V91*/92JNIEXPORT void JNICALL93Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {94}9596/*97* Block until a child process exits and return its exit code.98*/99JNIEXPORT jint JNICALL100Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,101jclass junk,102jlong jpid,103jboolean reapStatus) {104DWORD pid = (DWORD)jpid;105DWORD exitValue = -1;106HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION,107FALSE, pid);108if (handle == NULL) {109return exitValue; // No process with that pid is alive110}111do {112if (!GetExitCodeProcess(handle, &exitValue)) {113JNU_ThrowByNameWithLastError(env,114"java/lang/RuntimeException", "GetExitCodeProcess");115break;116}117if (exitValue == STILL_ACTIVE) {118HANDLE events[2];119events[0] = handle;120events[1] = JVM_GetThreadInterruptEvent();121122if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,123FALSE, /* Wait for ANY event */124INFINITE) /* Wait forever */125== WAIT_FAILED) {126JNU_ThrowByNameWithLastError(env,127"java/lang/RuntimeException", "WaitForMultipleObjects");128break;129}130}131} while (exitValue == STILL_ACTIVE);132CloseHandle(handle); // Ignore return code133return exitValue;134}135136/*137* Returns the pid of the caller.138*139* Class: java_lang_ProcessHandleImpl140* Method: getCurrentPid0141* Signature: ()J142*/143JNIEXPORT jlong JNICALL144Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) {145DWORD pid = GetCurrentProcessId();146return (jlong)pid;147}148149/*150* Returns the parent pid of the requested pid.151*152* Class: java_lang_ProcessHandleImpl153* Method: parent0154* Signature: (J)J155*/156JNIEXPORT jlong JNICALL157Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,158jclass clazz,159jlong jpid,160jlong startTime) {161DWORD ppid = 0;162DWORD wpid = (DWORD)jpid;163PROCESSENTRY32 pe32;164HANDLE hProcessSnap;165jlong start;166167start = Java_java_lang_ProcessHandleImpl_isAlive0(env, clazz, jpid);168if (start != startTime && start != 0 && startTime != 0) {169return -1;170}171172// Take a snapshot of all processes in the system.173hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);174if (hProcessSnap == INVALID_HANDLE_VALUE) {175JNU_ThrowByName(env,176"java/lang/RuntimeException", "snapshot not available");177return -1;178}179180// Retrieve information about the first process,181pe32.dwSize = sizeof (PROCESSENTRY32);182if (Process32First(hProcessSnap, &pe32)) {183// Now walk the snapshot of processes, and184do {185if (wpid == pe32.th32ProcessID) {186// The parent PID may be stale if that process has exited187// and may have been reused.188// A valid parent's start time is the same or before the child's189jlong ppStartTime = Java_java_lang_ProcessHandleImpl_isAlive0(env,190clazz, pe32.th32ParentProcessID);191if (ppStartTime > 0 && ppStartTime <= startTime) {192ppid = pe32.th32ParentProcessID;193}194break;195}196} while (Process32Next(hProcessSnap, &pe32));197} else {198JNU_ThrowByName(env,199"java/lang/RuntimeException", "snapshot not available");200ppid = (DWORD)-1;201}202CloseHandle(hProcessSnap); // Ignore return code203return (jlong)ppid;204}205206/*207* Returns the children of the requested pid and optionally each parent.208*209* Class: java_lang_ProcessHandleImpl210* Method: getChildPids211* Signature: (J[J[J)I212*/213JNIEXPORT jint JNICALL214Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,215jclass clazz,216jlong jpid,217jlongArray jarray,218jlongArray jparentArray,219jlongArray jstimesArray) {220HANDLE hProcessSnap;221PROCESSENTRY32 pe32;222DWORD ppid = (DWORD)jpid;223jlong* pids = NULL;224jlong* ppids = NULL;225jlong* stimes = NULL;226jsize parentArraySize = 0;227jsize arraySize = 0;228jsize stimesSize = 0;229jsize count = 0;230231arraySize = (*env)->GetArrayLength(env, jarray);232JNU_CHECK_EXCEPTION_RETURN(env, -1);233if (jparentArray != NULL) {234parentArraySize = (*env)->GetArrayLength(env, jparentArray);235JNU_CHECK_EXCEPTION_RETURN(env, -1);236237if (arraySize != parentArraySize) {238JNU_ThrowIllegalArgumentException(env, "array sizes not equal");239return 0;240}241}242if (jstimesArray != NULL) {243stimesSize = (*env)->GetArrayLength(env, jstimesArray);244JNU_CHECK_EXCEPTION_RETURN(env, -1);245246if (arraySize != stimesSize) {247JNU_ThrowIllegalArgumentException(env, "array sizes not equal");248return 0;249}250}251252// Take a snapshot of all processes in the system.253hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);254if (hProcessSnap == INVALID_HANDLE_VALUE) {255JNU_ThrowByName(env,256"java/lang/RuntimeException", "snapshot not available");257return 0;258}259260// Retrieve information about the first process,261pe32.dwSize = sizeof (PROCESSENTRY32);262if (Process32First(hProcessSnap, &pe32)) {263do { // Block to break out of on Exception264pids = (*env)->GetLongArrayElements(env, jarray, NULL);265if (pids == NULL) {266break;267}268if (jparentArray != NULL) {269ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);270if (ppids == NULL) {271break;272}273}274if (jstimesArray != NULL) {275stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);276if (stimes == NULL) {277break;278}279}280// Now walk the snapshot of processes, and281// save information about each process in turn282do {283if (ppid == 0 ||284(pe32.th32ParentProcessID > 0285&& (pe32.th32ParentProcessID == ppid))) {286if (count < arraySize) {287// Only store if it fits288pids[count] = (jlong) pe32.th32ProcessID;289if (ppids != NULL) {290// Store the parentPid291ppids[count] = (jlong) pe32.th32ParentProcessID;292}293if (stimes != NULL) {294// Store the process start time295stimes[count] =296Java_java_lang_ProcessHandleImpl_isAlive0(env,297clazz, (jlong) pe32.th32ProcessID);298}299}300count++; // Count to tabulate size needed301}302} while (Process32Next(hProcessSnap, &pe32));303} while (0);304305if (pids != NULL) {306(*env)->ReleaseLongArrayElements(env, jarray, pids, 0);307}308if (ppids != NULL) {309(*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);310}311if (stimes != NULL) {312(*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);313}314} else {315JNU_ThrowByName(env,316"java/lang/RuntimeException", "snapshot not available");317count = 0;318}319CloseHandle(hProcessSnap);320// If more pids than array had size for; count will be greater than array size321return (jint)count;322}323324/**325* Assemble a 64 bit value from two 32 bit values.326*/327static jlong jlong_from(jint high, jint low) {328jlong result = 0;329result = ((jlong)high << 32) | ((0x000000000ffffffff) & (jlong)low);330return result;331}332333/*334* Get the start time in ms from 1970 from the handle.335*/336static jlong getStartTime(HANDLE handle) {337FILETIME CreationTime, ExitTime, KernelTime, UserTime;338if (GetProcessTimes(handle, &CreationTime, &ExitTime, &KernelTime, &UserTime)) {339jlong start = jlong_from(CreationTime.dwHighDateTime,340CreationTime.dwLowDateTime) / 10000;341start -= 11644473600000L; // Rebase Epoch from 1601 to 1970342return start;343} else {344return 0;345}346}347348/*349* Destroy the process.350*351* Class: java_lang_ProcessHandleImpl352* Method: destroy0353* Signature: (Z)V354*/355JNIEXPORT jboolean JNICALL356Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,357jclass clazz,358jlong jpid,359jlong startTime,360jboolean force) {361DWORD pid = (DWORD)jpid;362jboolean ret = JNI_FALSE;363HANDLE handle = OpenProcess(PROCESS_TERMINATE | THREAD_QUERY_INFORMATION364| PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);365if (handle != NULL) {366jlong start = getStartTime(handle);367if (start == startTime || startTime == 0) {368ret = TerminateProcess(handle, 1) ? JNI_TRUE : JNI_FALSE;369}370CloseHandle(handle); // Ignore return code371}372return ret;373}374375/*376* Check if a process is alive.377* Return the start time (ms since 1970) if it is available.378* If the start time is not available return 0.379* If the pid is invalid, return -1.380*381* Class: java_lang_ProcessHandleImpl382* Method: isAlive0383* Signature: (J)J384*/385JNIEXPORT jlong JNICALL386Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jclass clazz, jlong jpid) {387DWORD pid = (DWORD)jpid;388389jlong ret = -1;390HANDLE handle =391OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,392FALSE, pid);393if (handle != NULL) {394DWORD dwExitStatus;395396GetExitCodeProcess(handle, &dwExitStatus);397if (dwExitStatus == STILL_ACTIVE) {398ret = getStartTime(handle);399} else {400ret = -1;401}402CloseHandle(handle); // Ignore return code403}404return ret;405}406407/*408* Fill in the Info object from the OS information about the process.409*410* Class: java_lang_ProcessHandleImpl411* Method: info0412* Signature: (J)V413*/414JNIEXPORT void JNICALL415Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,416jobject jinfo,417jlong jpid) {418DWORD pid = (DWORD)jpid;419int ret = 0;420HANDLE handle =421OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,422FALSE, pid);423if (handle == NULL) {424return;425}426getStatInfo(env, handle, jinfo);427getCmdlineInfo(env, handle, jinfo);428procToUser(env, handle, jinfo);429430CloseHandle(handle); // Ignore return code431}432433/**434* Read /proc/<pid>/stat and fill in the fields of the Info object.435* The executable name, plus the user, system, and start times are gathered.436*/437static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {438FILETIME CreationTime;439FILETIME ExitTime;440FILETIME KernelTime;441FILETIME UserTime;442jlong userTime; // nanoseconds443jlong totalTime; // nanoseconds444jlong startTime; // nanoseconds445UserTime.dwHighDateTime = 0;446UserTime.dwLowDateTime = 0;447KernelTime.dwHighDateTime = 0;448KernelTime.dwLowDateTime = 0;449CreationTime.dwHighDateTime = 0;450CreationTime.dwLowDateTime = 0;451452if (GetProcessTimes(handle, &CreationTime, &ExitTime, &KernelTime, &UserTime)) {453userTime = jlong_from(UserTime.dwHighDateTime, UserTime.dwLowDateTime);454totalTime = jlong_from( KernelTime.dwHighDateTime, KernelTime.dwLowDateTime);455totalTime = (totalTime + userTime) * 100; // convert sum to nano-seconds456457startTime = jlong_from(CreationTime.dwHighDateTime,458CreationTime.dwLowDateTime) / 10000;459startTime -= 11644473600000L; // Rebase Epoch from 1601 to 1970460461(*env)->SetLongField(env, jinfo,462ProcessHandleImpl_Info_totalTimeID, totalTime);463JNU_CHECK_EXCEPTION(env);464(*env)->SetLongField(env, jinfo,465ProcessHandleImpl_Info_startTimeID, startTime);466JNU_CHECK_EXCEPTION(env);467}468}469470static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {471WCHAR exeName[1024];472WCHAR *longPath;473DWORD bufsize = sizeof(exeName)/sizeof(WCHAR);474jstring commandObj = NULL;475476if (QueryFullProcessImageNameW(handle, 0, exeName, &bufsize)) {477commandObj = (*env)->NewString(env, (const jchar *)exeName,478(jsize)wcslen(exeName));479} else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {480bufsize = 32768;481longPath = (WCHAR*)malloc(bufsize * sizeof(WCHAR));482if (longPath != NULL) {483if (QueryFullProcessImageNameW(handle, 0, longPath, &bufsize)) {484commandObj = (*env)->NewString(env, (const jchar *)longPath,485(jsize)wcslen(longPath));486}487free(longPath);488}489}490CHECK_NULL(commandObj);491(*env)->SetObjectField(env, jinfo,492ProcessHandleImpl_Info_commandID, commandObj);493}494495static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo) {496#define TOKEN_LEN 256497DWORD token_len = TOKEN_LEN;498char token_buf[TOKEN_LEN];499TOKEN_USER *token_user = (TOKEN_USER*)token_buf;500HANDLE tokenHandle;501WCHAR domain[255 + 1 + 255 + 1]; // large enough to concat with '/' and name502WCHAR name[255 + 1];503DWORD domainLen = sizeof(domain) - sizeof(name);504DWORD nameLen = sizeof(name);505SID_NAME_USE use;506jstring s;507int ret;508509if (!OpenProcessToken(handle, TOKEN_READ, &tokenHandle)) {510return;511}512513ret = GetTokenInformation(tokenHandle, TokenUser, token_user,514token_len, &token_len);515CloseHandle(tokenHandle); // always close handle516if (!ret) {517JNU_ThrowByNameWithLastError(env,518"java/lang/RuntimeException", "GetTokenInformation");519return;520}521522if (LookupAccountSidW(NULL, token_user->User.Sid, &name[0], &nameLen,523&domain[0], &domainLen, &use) == 0) {524// Name not available, convert to a String525LPWSTR str;526if (ConvertSidToStringSidW(token_user->User.Sid, &str) == 0) {527return;528}529s = (*env)->NewString(env, (const jchar *)str, (jsize)wcslen(str));530LocalFree(str);531} else {532wcscat(domain, L"\\");533wcscat(domain, name);534s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain));535}536CHECK_NULL(s);537(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, s);538}539540541