// Processes.cpp //#include #if defined(_WIN32) || defined(_WIN64) // C4290 - the compiler ignores exception specifications #pragma warning(disable: 4290) #define WIN32_LEAN_AND_MEAN #include #include "SafeHandle.hpp" #define TIMEOUT_PERIOD 1000 #elif defined(__unix) || defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) #include #include #include #include #define INITIAL_TIMEOUT 50000 #define TIMEOUT_LIMIT 5 #endif // _WIN32 || _WIN64 #include #include "SystemError.hpp" #include "ErrorHandling.hpp" #include "cipres-jni.h" namespace Cipres { namespace JNI { #if defined(_WIN32) || defined(_WIN64) // SendWindowClose static BOOL CALLBACK SendWindowClose(HWND handle, LPARAM param) { unsigned long pid; ::GetWindowThreadProcessId(handle, &pid); if(pid == static_cast(param)) ::PostMessage(handle, WM_CLOSE, NULL, NULL); return TRUE; } #elif defined(__unix) || defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) // ProcessExists static inline bool ProcessExists(int pid) throw() { return ::kill(pid, 0) == 0 || errno == EPERM; } #endif } // namespace JNI } // namespace Cipres using namespace Cipres; using namespace Cipres::JNI; // public interface // Java_org_cipres_jni_Processes_getProcessID JNIEXPORT jint JNICALL Java_org_cipres_jni_Processes_getProcessID(JNIEnv *env, jclass obj) { #if defined(_WIN32) || defined(_WIN64) return ::GetCurrentProcessId(); #elif defined(__unix) || defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) return ::getpid(); #endif } // Java_org_cipres_jni_Processes_processExists JNIEXPORT jboolean JNICALL Java_org_cipres_jni_Processes_processExists(JNIEnv *env, jclass, jint pid) { #if defined(_WIN32) || defined(_WIN64) // the System Idle process always exists if(pid == 0) return true; HANDLE process = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if(process != NULL) { ::CloseHandle(process); return true; } else if(::GetLastError() == ERROR_ACCESS_DENIED) return true; else return false; #elif defined(__unix) || defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) return ProcessExists(pid); #endif } // Java_org_cipres_jni_Processes_killProcess JNIEXPORT void JNICALL Java_org_cipres_jni_Processes_killProcess(JNIEnv *env, jclass obj, jint pid, jboolean force) { try { #if defined(_WIN32) || defined(_WIN64) SafeHandle process(::OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, pid)); if(process == NULL) { int error_code = ::GetLastError(); if(error_code == ERROR_INVALID_PARAMETER) throw std::invalid_argument("No such process"); else if(error_code != ERROR_ACCESS_DENIED) throw SystemError("killProcess", "OpenProcess", error_code); // in Windows, a user's account isn't automatically granted all the privileges that // it can potentially claim from group memberships (e.g. the Administrators group.) // If we're initially denied access to the process, then, we can try to increase // our privileges and try again SafeHandle token; // try to get the access token for the current thread, in case it's impersonating // the security credentials of another user. If the thread isn't running as anyone // else, it won't have any token set, and we'll have to get the access token for // the process if(::OpenThreadToken(::GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &token) == FALSE) { error_code = ::GetLastError(); if(error_code != ERROR_NO_TOKEN) throw SystemError("killProcess", "OpenThreadToken", error_code); if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token) == FALSE) throw SystemError("killProcess", "OpenProcessToken", ::GetLastError()); } TOKEN_PRIVILEGES new_privs; new_privs.PrivilegeCount = 1; if(::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &new_privs.Privileges[0].Luid) == FALSE) throw SystemError("killProcess", "LookupPrivilegeValue", ::GetLastError()); new_privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; TOKEN_PRIVILEGES old_privs; unsigned long buffer_size = sizeof(old_privs); if(::AdjustTokenPrivileges(token.Get(), FALSE, &new_privs, sizeof(new_privs), &old_privs, &buffer_size) == FALSE) throw SystemError("killProcess", "AdjustTokenPrivileges", ::GetLastError()); process.Reset(::OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, pid)); if(process == NULL) throw SystemError("killProcess", "OpenProcess", ::GetLastError()); // reinstate the original set of privileges, so that we can confine the // escalation to the scope of this function if(::AdjustTokenPrivileges(token.Get(), FALSE, &old_privs, sizeof(old_privs), NULL, NULL) == FALSE) throw SystemError("killProcess", "AdjustTokenPrivileges", ::GetLastError()); } ::EnumWindows(SendWindowClose, pid); if(!force) return; if(::WaitForSingleObject(process.Get(), TIMEOUT_PERIOD) != WAIT_OBJECT_0) ::TerminateProcess(process.Get(), -1); #elif defined(__unix) || defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) if(::kill(pid, SIGTERM) != 0) { int error_code = errno; if(error_code == ESRCH) throw std::invalid_argument("No such process"); else throw SystemError("killProcess", "kill", error_code); } if(!force) return; unsigned int timeout_period = INITIAL_TIMEOUT; for(int i = 0 ; i < TIMEOUT_LIMIT ; i++) { ::usleep(timeout_period); if(!ProcessExists(pid)) return; timeout_period *= 2; } ::kill(pid, SIGKILL); #endif } catch(const std::invalid_argument &arg_err) { ThrowError(env, JAVA_CLASS_ILLEGAL_ARG, arg_err.what()); } catch(const std::runtime_error &run_err) { ThrowError(env, JAVA_CLASS_RUNTIME_EXCEPT, run_err.what()); } }