mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
303 lines
9.9 KiB
C
303 lines
9.9 KiB
C
/*
|
|
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
#include "jni.h"
|
|
#include "jni_util.h"
|
|
#include "java_lang_ProcessHandleImpl.h"
|
|
#include "java_lang_ProcessHandleImpl_Info.h"
|
|
|
|
#include "ProcessHandleImpl_unix.h"
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
/**
|
|
* Implementation of native ProcessHandleImpl functions for MAC OS X.
|
|
* See ProcessHandleImpl_unix.c for more details.
|
|
*/
|
|
|
|
void os_initNative(JNIEnv *env, jclass clazz) {}
|
|
|
|
/*
|
|
* Returns the children of the requested pid and optionally each parent.
|
|
*
|
|
* Use sysctl to accumulate any process whose parent pid is zero or matches.
|
|
* The resulting pids are stored into the array of longs.
|
|
* The number of pids is returned if they all fit.
|
|
* If the parentArray is non-null, store the parent pid.
|
|
* If the array is too short, excess pids are not stored and
|
|
* the desired length is returned.
|
|
*/
|
|
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
|
|
jlongArray jparentArray, jlongArray jstimesArray) {
|
|
jlong* pids = NULL;
|
|
jlong* ppids = NULL;
|
|
jlong* stimes = NULL;
|
|
jsize parentArraySize = 0;
|
|
jsize arraySize = 0;
|
|
jsize stimesSize = 0;
|
|
jsize count = 0;
|
|
size_t bufSize = 0;
|
|
pid_t pid = (pid_t) jpid;
|
|
|
|
arraySize = (*env)->GetArrayLength(env, jarray);
|
|
JNU_CHECK_EXCEPTION_RETURN(env, -1);
|
|
if (jparentArray != NULL) {
|
|
parentArraySize = (*env)->GetArrayLength(env, jparentArray);
|
|
JNU_CHECK_EXCEPTION_RETURN(env, -1);
|
|
|
|
if (arraySize != parentArraySize) {
|
|
JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
|
|
return 0;
|
|
}
|
|
}
|
|
if (jstimesArray != NULL) {
|
|
stimesSize = (*env)->GetArrayLength(env, jstimesArray);
|
|
JNU_CHECK_EXCEPTION_RETURN(env, -1);
|
|
|
|
if (arraySize != stimesSize) {
|
|
JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Get buffer size needed to read all processes
|
|
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
|
|
if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {
|
|
JNU_ThrowByNameWithLastError(env,
|
|
"java/lang/RuntimeException", "sysctl failed");
|
|
return -1;
|
|
}
|
|
|
|
// Allocate buffer big enough for all processes
|
|
void *buffer = malloc(bufSize);
|
|
if (buffer == NULL) {
|
|
JNU_ThrowOutOfMemoryError(env, "malloc failed");
|
|
return -1;
|
|
}
|
|
|
|
// Read process info for all processes
|
|
if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {
|
|
JNU_ThrowByNameWithLastError(env,
|
|
"java/lang/RuntimeException", "sysctl failed");
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
do { // Block to break out of on Exception
|
|
struct kinfo_proc *kp = (struct kinfo_proc *) buffer;
|
|
unsigned long nentries = bufSize / sizeof (struct kinfo_proc);
|
|
long i;
|
|
|
|
pids = (*env)->GetLongArrayElements(env, jarray, NULL);
|
|
if (pids == NULL) {
|
|
break;
|
|
}
|
|
if (jparentArray != NULL) {
|
|
ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
|
|
if (ppids == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
if (jstimesArray != NULL) {
|
|
stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
|
|
if (stimes == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Process each entry in the buffer
|
|
for (i = nentries; --i >= 0; ++kp) {
|
|
if (pid == 0 || kp->kp_eproc.e_ppid == pid) {
|
|
if (count < arraySize) {
|
|
// Only store if it fits
|
|
pids[count] = (jlong) kp->kp_proc.p_pid;
|
|
if (ppids != NULL) {
|
|
// Store the parentPid
|
|
ppids[count] = (jlong) kp->kp_eproc.e_ppid;
|
|
}
|
|
if (stimes != NULL) {
|
|
// Store the process start time
|
|
jlong startTime = kp->kp_proc.p_starttime.tv_sec * 1000 +
|
|
kp->kp_proc.p_starttime.tv_usec / 1000;
|
|
stimes[count] = startTime;
|
|
}
|
|
}
|
|
count++; // Count to tabulate size needed
|
|
}
|
|
}
|
|
} while (0);
|
|
|
|
if (pids != NULL) {
|
|
(*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
|
|
}
|
|
if (ppids != NULL) {
|
|
(*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
|
|
}
|
|
if (stimes != NULL) {
|
|
(*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
|
|
}
|
|
|
|
free(buffer);
|
|
// If more pids than array had size for; count will be greater than array size
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Use sysctl and return the ppid, total cputime and start time.
|
|
* Return: -1 is fail; >= 0 is parent pid
|
|
* 'total' will contain the running time of 'pid' in nanoseconds.
|
|
* 'start' will contain the start time of 'pid' in milliseconds since epoch.
|
|
*/
|
|
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t jpid,
|
|
jlong *totalTime, jlong *startTime) {
|
|
|
|
const pid_t pid = (pid_t) jpid;
|
|
pid_t ppid = -1;
|
|
struct kinfo_proc kp;
|
|
size_t bufSize = sizeof kp;
|
|
|
|
// Read the process info for the specific pid
|
|
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
|
|
|
if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
|
|
JNU_ThrowByNameWithLastError(env,
|
|
"java/lang/RuntimeException", "sysctl failed");
|
|
return -1;
|
|
}
|
|
if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
|
|
*startTime = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000 +
|
|
kp.kp_proc.p_starttime.tv_usec / 1000);
|
|
ppid = kp.kp_eproc.e_ppid;
|
|
}
|
|
|
|
// Get cputime if for current process
|
|
if (pid == getpid()) {
|
|
struct rusage usage;
|
|
if (getrusage(RUSAGE_SELF, &usage) == 0) {
|
|
jlong microsecs =
|
|
usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
|
|
usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
|
|
*totalTime = microsecs * 1000;
|
|
}
|
|
}
|
|
|
|
return ppid;
|
|
|
|
}
|
|
|
|
/**
|
|
* Return the uid of a process or -1 on error
|
|
*/
|
|
static uid_t getUID(pid_t pid) {
|
|
struct kinfo_proc kp;
|
|
size_t bufSize = sizeof kp;
|
|
|
|
// Read the process info for the specific pid
|
|
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
|
|
|
if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) {
|
|
if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
|
|
return kp.kp_eproc.e_ucred.cr_uid;
|
|
}
|
|
}
|
|
return (uid_t)-1;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the command and arguments for the process and store them
|
|
* into the Info object.
|
|
*/
|
|
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
|
int mib[3], maxargs, nargs;
|
|
size_t size;
|
|
char *args, *cp;
|
|
|
|
// Get the UID first. This is done here because it is cheap to do it here
|
|
// on other platforms like Linux/Solaris/AIX where the uid comes from the
|
|
// same source like the command line info.
|
|
unix_getUserInfo(env, jinfo, getUID(pid));
|
|
|
|
// Get the maximum size of the arguments
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_ARGMAX;
|
|
size = sizeof(maxargs);
|
|
if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
|
|
JNU_ThrowByNameWithLastError(env,
|
|
"java/lang/RuntimeException", "sysctl failed");
|
|
return;
|
|
}
|
|
|
|
// Allocate an args buffer and get the arguments
|
|
args = (char *)malloc(maxargs);
|
|
if (args == NULL) {
|
|
JNU_ThrowOutOfMemoryError(env, "malloc failed");
|
|
return;
|
|
}
|
|
|
|
do { // a block to break out of on error
|
|
char *argsEnd;
|
|
jstring cmdexe = NULL;
|
|
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_PROCARGS2;
|
|
mib[2] = pid;
|
|
size = (size_t) maxargs;
|
|
if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
|
|
if (errno != EINVAL && errno != EIO) {
|
|
// If the pid is invalid, the information returned is empty and no exception
|
|
JNU_ThrowByNameWithLastError(env,
|
|
"java/lang/RuntimeException", "sysctl failed");
|
|
}
|
|
break;
|
|
}
|
|
memcpy(&nargs, args, sizeof(nargs));
|
|
|
|
cp = &args[sizeof(nargs)]; // Strings start after nargs
|
|
argsEnd = &args[size];
|
|
|
|
// Store the command executable path
|
|
if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {
|
|
break;
|
|
}
|
|
|
|
// Skip trailing nulls after the executable path
|
|
for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
|
|
if (*cp != '\0') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe, NULL);
|
|
} while (0);
|
|
// Free the arg buffer
|
|
free(args);
|
|
}
|