blob: eff634acbde007163f3cd9d1b742076eb9492250 [file]
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025 SAP SE. 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 "ProcessHandleImpl_unix.h"
#include <sys/procfs.h>
#include <procinfo.h>
#include <string.h>
/*
* Implementation of native ProcessHandleImpl functions for AIX.
* See ProcessHandleImpl_unix.c for more details.
*/
void os_initNative(JNIEnv *env, jclass clazz) {}
/*
* Return pids of active processes, and optionally parent pids and
* start times for each process.
* For a specific non-zero pid, only the direct children are returned.
* If the pid is zero, all active processes are returned.
* Use getprocs64 to accumulate any process following the rules above.
* The resulting pids are stored into an array of longs named jarray.
* The number of pids is returned if they all fit.
* If the parentArray is non-null, store also the parent pid.
* In this case the parentArray must have the same length as the result pid array.
* Of course in the case of a given non-zero pid all entries in the parentArray
* will contain this pid, so this array does only make sense in the case of a given
* zero pid.
* If the jstimesArray is non-null, store also the start time of the pid.
* In this case the jstimesArray must have the same length as the result pid array.
* If the array(s) (is|are) 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) {
pid_t pid = (pid_t) jpid;
jlong* pids = NULL;
jlong* ppids = NULL;
jlong* stimes = NULL;
jsize parentArraySize = 0;
jsize arraySize = 0;
jsize stimesSize = 0;
jsize count = 0;
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;
}
}
const int chunk = 100;
struct procentry64 ProcessBuffer[chunk];
pid_t idxptr = 0;
int i, num = 0;
do { // Block to break out of on Exception
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;
}
}
while ((num = getprocs64(ProcessBuffer, sizeof(struct procentry64), NULL,
sizeof(struct fdsinfo64), &idxptr, chunk)) != -1) {
for (i = 0; i < num; i++) {
pid_t childpid = (pid_t) ProcessBuffer[i].pi_pid;
pid_t ppid = (pid_t) ProcessBuffer[i].pi_ppid;
// Get the parent pid, and start time
if (pid == 0 || ppid == pid) {
if (count < arraySize) {
// Only store if it fits
pids[count] = (jlong) childpid;
if (ppids != NULL) {
// Store the parentPid
ppids[count] = (jlong) ppid;
}
if (stimes != NULL) {
// Store the process start time
stimes[count] = ((jlong) ProcessBuffer[i].pi_start) * 1000;;
}
}
count++; // Count to tabulate size needed
}
}
if (num < chunk) {
break;
}
}
} 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);
}
if (num == -1) {
JNU_ThrowByNameWithLastError(env,
"java/lang/RuntimeException", "Unable to retrieve Process info");
return -1;
}
// If more pids than array had size for; count will be greater than array size
return count;
}
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) {
pid_t the_pid = pid;
struct procentry64 ProcessBuffer;
if (getprocs64(&ProcessBuffer, sizeof(ProcessBuffer), NULL, sizeof(struct fdsinfo64), &the_pid, 1) <= 0) {
return -1;
}
// Validate the pid before returning the info
if (kill(pid, 0) < 0) {
return -1;
}
*total = ((ProcessBuffer.pi_ru.ru_utime.tv_sec + ProcessBuffer.pi_ru.ru_stime.tv_sec) * 1000000000L) +
((ProcessBuffer.pi_ru.ru_utime.tv_usec + ProcessBuffer.pi_ru.ru_stime.tv_usec));
*start = ProcessBuffer.pi_start * (jlong)1000;
return (pid_t) ProcessBuffer.pi_ppid;
}
/**
* Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo".
* Returns 0 on success and -1 on error.
*/
static int getPsinfo(pid_t pid, psinfo_t *psinfo) {
FILE* fp;
char fn[32];
size_t ret;
/*
* Try to open /proc/%d/psinfo
*/
snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
fp = fopen(fn, "r");
if (fp == NULL) {
return -1;
}
ret = fread(psinfo, 1, sizeof(psinfo_t), fp);
fclose(fp);
if (ret < sizeof(psinfo_t)) {
return -1;
}
return 0;
}
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
psinfo_t psinfo;
char prargs[PRARGSZ + 1];
jstring cmdexe = NULL;
/*
* Now try to open /proc/%d/psinfo
*/
if (getPsinfo(pid, &psinfo) < 0) {
unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, NULL);
return;
}
unix_getUserInfo(env, jinfo, psinfo.pr_uid);
/*
* Now read psinfo.pr_psargs which contains the first PRARGSZ characters of the
* argument list (i.e. arg[0] arg[1] ...). Unfortunately, PRARGSZ is usually set
* to 80 characters only. Nevertheless it's better than nothing :)
*/
strncpy(prargs, psinfo.pr_psargs, PRARGSZ);
prargs[PRARGSZ] = '\0';
if (prargs[0] == '\0') {
/* If psinfo.pr_psargs didn't contain any strings, use psinfo.pr_fname
* (which only contains the last component of exec()ed pathname) as a
* last resort. This is true for AIX kernel processes for example.
*/
strncpy(prargs, psinfo.pr_fname, PRARGSZ);
prargs[PRARGSZ] = '\0';
}
unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe,
prargs[0] == '\0' ? NULL : prargs);
}