8131168: Refactor ProcessHandleImpl_*.c and add implememtation for AIX
Reviewed-by: rriggs, smarks
This commit is contained in:
parent
d9b43a9e2c
commit
1b8b82463e
50
jdk/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c
Normal file
50
jdk/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 "ProcessHandleImpl_unix.h"
|
||||
|
||||
#include <sys/procfs.h>
|
||||
|
||||
/*
|
||||
* Implementation of native ProcessHandleImpl functions for AIX.
|
||||
* See ProcessHandleImpl_unix.c for more details.
|
||||
*/
|
||||
|
||||
void os_initNative(JNIEnv *env, jclass clazz) {}
|
||||
|
||||
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
|
||||
jlongArray jparentArray, jlongArray jstimesArray) {
|
||||
return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
|
||||
}
|
||||
|
||||
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) {
|
||||
return unix_getParentPidAndTimings(env, pid, total, start);
|
||||
}
|
||||
|
||||
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
unix_getCmdlineAndUserInfo(env, jinfo, pid);
|
||||
}
|
266
jdk/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c
Normal file
266
jdk/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* Implementation of native ProcessHandleImpl functions for Linux.
|
||||
* See ProcessHandleImpl_unix.c for more details.
|
||||
*/
|
||||
|
||||
/* Signatures for internal OS specific functions. */
|
||||
static long long getBoottime(JNIEnv *env);
|
||||
|
||||
/* A static offset in milliseconds since boot. */
|
||||
static long long bootTime_ms;
|
||||
static long clock_ticks_per_second;
|
||||
static int pageSize;
|
||||
|
||||
void os_initNative(JNIEnv *env, jclass clazz) {
|
||||
bootTime_ms = getBoottime(env);
|
||||
clock_ticks_per_second = sysconf(_SC_CLK_TCK);
|
||||
pageSize = sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
|
||||
jlongArray jparentArray, jlongArray jstimesArray) {
|
||||
return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read /proc/<pid>/stat and return the ppid, total cputime and start time.
|
||||
* -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 pid,
|
||||
jlong *totalTime, jlong* startTime) {
|
||||
FILE* fp;
|
||||
char buffer[2048];
|
||||
int statlen;
|
||||
char fn[32];
|
||||
char* s;
|
||||
int parentPid;
|
||||
long unsigned int utime = 0; // clock tics
|
||||
long unsigned int stime = 0; // clock tics
|
||||
long long unsigned int start = 0; // microseconds
|
||||
|
||||
/*
|
||||
* Try to stat and then open /proc/%d/stat
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
|
||||
|
||||
fp = fopen(fn, "r");
|
||||
if (fp == NULL) {
|
||||
return -1; // fail, no such /proc/pid/stat
|
||||
}
|
||||
|
||||
/*
|
||||
* The format is: pid (command) state ppid ...
|
||||
* As the command could be anything we must find the right most
|
||||
* ")" and then skip the white spaces that follow it.
|
||||
*/
|
||||
statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
|
||||
fclose(fp);
|
||||
if (statlen < 0) {
|
||||
return -1; // parent pid is not available
|
||||
}
|
||||
|
||||
buffer[statlen] = '\0';
|
||||
s = strchr(buffer, '(');
|
||||
if (s == NULL) {
|
||||
return -1; // parent pid is not available
|
||||
}
|
||||
// Found start of command, skip to end
|
||||
s++;
|
||||
s = strrchr(s, ')');
|
||||
if (s == NULL) {
|
||||
return -1; // parent pid is not available
|
||||
}
|
||||
s++;
|
||||
|
||||
// Scan the needed fields from status, retaining only ppid(4),
|
||||
// utime (14), stime(15), starttime(22)
|
||||
if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
|
||||
&parentPid, &utime, &stime, &start)) {
|
||||
return 0; // not all values parsed; return error
|
||||
}
|
||||
|
||||
*totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
|
||||
|
||||
*startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
|
||||
|
||||
return parentPid;
|
||||
}
|
||||
|
||||
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
int fd;
|
||||
int cmdlen = 0;
|
||||
char *cmdline = NULL, *cmdEnd = NULL; // used for command line args and exe
|
||||
char *args = NULL;
|
||||
jstring cmdexe = NULL;
|
||||
char fn[32];
|
||||
struct stat stat_buf;
|
||||
|
||||
/*
|
||||
* Try to open /proc/<pid>/cmdline
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
|
||||
if ((fd = open(fn, O_RDONLY)) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fstat(fd, &stat_buf) == 0) {
|
||||
unix_getUserInfo(env, jinfo, stat_buf.st_uid);
|
||||
}
|
||||
|
||||
do { // Block to break out of on errors
|
||||
int i, truncated = 0;
|
||||
int count;
|
||||
char *s;
|
||||
|
||||
/*
|
||||
* The path name read by readlink() is limited to PATH_MAX characters.
|
||||
* The content of /proc/<pid>/cmdline is limited to PAGE_SIZE characters.
|
||||
*/
|
||||
cmdline = (char*)malloc((PATH_MAX > pageSize ? PATH_MAX : pageSize) + 1);
|
||||
if (cmdline == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* On Linux, the full path to the executable command is the link in
|
||||
* /proc/<pid>/exe. But it is only readable for processes we own.
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
|
||||
if ((cmdlen = readlink(fn, cmdline, PATH_MAX)) > 0) {
|
||||
// null terminate and create String to store for command
|
||||
cmdline[cmdlen] = '\0';
|
||||
cmdexe = JNU_NewStringPlatform(env, cmdline);
|
||||
(*env)->ExceptionClear(env); // unconditionally clear any exception
|
||||
}
|
||||
|
||||
/*
|
||||
* The command-line arguments appear as a set of strings separated by
|
||||
* null bytes ('\0'), with a further null byte after the last
|
||||
* string. The last string is only null terminated if the whole command
|
||||
* line is not exceeding (PAGE_SIZE - 1) characters.
|
||||
*/
|
||||
cmdlen = 0;
|
||||
s = cmdline;
|
||||
while ((count = read(fd, s, pageSize - cmdlen)) > 0) {
|
||||
cmdlen += count;
|
||||
s += count;
|
||||
}
|
||||
if (count < 0) {
|
||||
break;
|
||||
}
|
||||
// We have to null-terminate because the process may have changed argv[]
|
||||
// or because the content in /proc/<pid>/cmdline is truncated.
|
||||
cmdline[cmdlen] = '\0';
|
||||
if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') {
|
||||
truncated = 1;
|
||||
} else if (cmdlen == 0) {
|
||||
// /proc/<pid>/cmdline was empty. This usually happens for kernel processes
|
||||
// like '[kthreadd]'. We could try to read /proc/<pid>/comm in the future.
|
||||
}
|
||||
if (cmdlen > 0 && (cmdexe == NULL || truncated)) {
|
||||
// We have no exact command or the arguments are truncated.
|
||||
// In this case we save the command line from /proc/<pid>/cmdline.
|
||||
args = (char*)malloc(pageSize + 1);
|
||||
if (args != NULL) {
|
||||
memcpy(args, cmdline, cmdlen + 1);
|
||||
for (i = 0; i < cmdlen; i++) {
|
||||
if (args[i] == '\0') {
|
||||
args[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
if (!truncated) {
|
||||
// Count the arguments
|
||||
cmdEnd = &cmdline[cmdlen];
|
||||
for (s = cmdline; *s != '\0' && (s < cmdEnd); i++) {
|
||||
s += strnlen(s, (cmdEnd - s)) + 1;
|
||||
}
|
||||
}
|
||||
unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe, args);
|
||||
} while (0);
|
||||
|
||||
if (cmdline != NULL) {
|
||||
free(cmdline);
|
||||
}
|
||||
if (args != NULL) {
|
||||
free(args);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the boottime from /proc/stat.
|
||||
*/
|
||||
static long long getBoottime(JNIEnv *env) {
|
||||
FILE *fp;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
long long bootTime = 0;
|
||||
|
||||
fp = fopen("/proc/stat", "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (getline(&line, &len, fp) != -1) {
|
||||
if (sscanf(line, "btime %llu", &bootTime) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
|
||||
if (fp != 0) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return bootTime * 1000;
|
||||
}
|
@ -28,6 +28,8 @@
|
||||
#include "java_lang_ProcessHandleImpl.h"
|
||||
#include "java_lang_ProcessHandleImpl_Info.h"
|
||||
|
||||
#include "ProcessHandleImpl_unix.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
@ -38,144 +40,15 @@
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
/**
|
||||
* Implementations of ProcessHandleImpl functions for MAC OS X;
|
||||
* are NOT common to all Unix variants.
|
||||
* Implementation of native ProcessHandleImpl functions for MAC OS X.
|
||||
* See ProcessHandleImpl_unix.c for more details.
|
||||
*/
|
||||
|
||||
static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
|
||||
static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
|
||||
|
||||
/*
|
||||
* Common Unix function to lookup the uid and return the user name.
|
||||
*/
|
||||
extern jstring uidToUser(JNIEnv* env, uid_t uid);
|
||||
|
||||
/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_commandID;
|
||||
|
||||
/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_argumentsID;
|
||||
|
||||
/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_totalTimeID;
|
||||
|
||||
/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_startTimeID;
|
||||
|
||||
/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_userID;
|
||||
|
||||
/* static value for clock ticks per second. */
|
||||
static long clock_ticks_per_second;
|
||||
|
||||
/**************************************************************
|
||||
* Static method to initialize field IDs and the ticks per second rate.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl_Info
|
||||
* Method: initIDs
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
|
||||
CHECK_NULL(ProcessHandleImpl_Info_commandID =
|
||||
(*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_argumentsID =
|
||||
(*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_totalTimeID =
|
||||
(*env)->GetFieldID(env, clazz, "totalTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_startTimeID =
|
||||
(*env)->GetFieldID(env, clazz, "startTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_userID =
|
||||
(*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
|
||||
}
|
||||
/**************************************************************
|
||||
* Static method to initialize the ticks per second rate.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: initNative
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
|
||||
clock_ticks_per_second = sysconf(_SC_CLK_TCK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a process is alive.
|
||||
* Return the start time (ms since 1970) if it is available.
|
||||
* If the start time is not available return 0.
|
||||
* If the pid is invalid, return -1.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: isAlive0
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
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) {
|
||||
return (errno == EINVAL) ? -1 : 0;
|
||||
} else {
|
||||
return (bufSize == 0) ? -1 :
|
||||
(jlong) (kp.kp_proc.p_starttime.tv_sec * 1000
|
||||
+ kp.kp_proc.p_starttime.tv_usec / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the parent pid of the requested pid.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: parent0
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
|
||||
jobject obj,
|
||||
jlong jpid,
|
||||
jlong startTime) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
pid_t ppid = -1;
|
||||
|
||||
if (pid == getpid()) {
|
||||
ppid = getppid();
|
||||
} else {
|
||||
const pid_t pid = (pid_t) jpid;
|
||||
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 the buffer is full and for the pid requested then check the start
|
||||
if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
|
||||
jlong start = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000
|
||||
+ kp.kp_proc.p_starttime.tv_usec / 1000);
|
||||
if (start == startTime || start == 0 || startTime == 0) {
|
||||
ppid = kp.kp_eproc.e_ppid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (jlong) ppid;
|
||||
}
|
||||
void os_initNative(JNIEnv *env, jclass clazz) {}
|
||||
|
||||
/*
|
||||
* Returns the children of the requested pid and optionally each parent.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: getProcessPids0
|
||||
* Signature: (J[J[J)I
|
||||
*
|
||||
* 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.
|
||||
@ -183,13 +56,8 @@ Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
|
||||
* If the array is too short, excess pids are not stored and
|
||||
* the desired length is returned.
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
|
||||
jclass clazz,
|
||||
jlong jpid,
|
||||
jlongArray jarray,
|
||||
jlongArray jparentArray,
|
||||
jlongArray jstimesArray) {
|
||||
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
|
||||
jlongArray jparentArray, jlongArray jstimesArray) {
|
||||
jlong* pids = NULL;
|
||||
jlong* ppids = NULL;
|
||||
jlong* stimes = NULL;
|
||||
@ -303,35 +171,17 @@ Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
|
||||
return count;
|
||||
}
|
||||
|
||||
/**************************************************************
|
||||
* Implementation of ProcessHandleImpl_Info native methods.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fill in the Info object from the OS information about the process.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: info0
|
||||
* Signature: (J)I
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
|
||||
jobject jinfo,
|
||||
jlong jpid) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
getStatInfo(env, jinfo, pid);
|
||||
getCmdlineInfo(env, jinfo, pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read /proc/<pid>/stat and fill in the fields of the Info object.
|
||||
* The executable name, plus the user, system, and start times are gathered.
|
||||
* 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.
|
||||
*/
|
||||
static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) {
|
||||
jlong totalTime; // nanoseconds
|
||||
unsigned long long startTime; // milliseconds
|
||||
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;
|
||||
|
||||
@ -339,92 +189,70 @@ static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) {
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
||||
|
||||
if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
|
||||
if (errno == EINVAL) {
|
||||
return;
|
||||
} else {
|
||||
JNU_ThrowByNameWithLastError(env,
|
||||
"java/lang/RuntimeException", "sysctl failed");
|
||||
}
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
// Convert the UID to the username
|
||||
jstring name = NULL;
|
||||
CHECK_NULL((name = uidToUser(env, kp.kp_eproc.e_ucred.cr_uid)));
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
startTime = kp.kp_proc.p_starttime.tv_sec * 1000 +
|
||||
kp.kp_proc.p_starttime.tv_usec / 1000;
|
||||
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
// Get cputime if for current process
|
||||
if (pid == getpid()) {
|
||||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage) != 0) {
|
||||
return;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
|
||||
return ppid;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the argument array by parsing the arguments from the sequence of arguments.
|
||||
* Return the uid of a process or -1 on error
|
||||
*/
|
||||
static int fillArgArray(JNIEnv *env, jobject jinfo, int nargs,
|
||||
const char *cp, const char *argsEnd) {
|
||||
jstring str = NULL;
|
||||
jobject argsArray;
|
||||
int i;
|
||||
static uid_t getUID(pid_t pid) {
|
||||
struct kinfo_proc kp;
|
||||
size_t bufSize = sizeof kp;
|
||||
|
||||
if (nargs < 1) {
|
||||
return 0;
|
||||
}
|
||||
// Create a String array for nargs-1 elements
|
||||
CHECK_NULL_RETURN((argsArray = (*env)->NewObjectArray(env,
|
||||
nargs - 1, JNU_ClassString(env), NULL)), -1);
|
||||
// Read the process info for the specific pid
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
||||
|
||||
for (i = 0; i < nargs - 1; i++) {
|
||||
// skip to the next argument; omits arg[0]
|
||||
cp += strnlen(cp, (argsEnd - cp)) + 1;
|
||||
|
||||
if (cp > argsEnd || *cp == '\0') {
|
||||
return -2; // Off the end pointer or an empty argument is an error
|
||||
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;
|
||||
}
|
||||
|
||||
CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
|
||||
|
||||
(*env)->SetObjectArrayElement(env, argsArray, i, str);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, -3);
|
||||
}
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, -4);
|
||||
return 0;
|
||||
return (uid_t)-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the command and arguments for the process and store them
|
||||
* into the Info object.
|
||||
*/
|
||||
static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
int mib[3], maxargs, nargs, i;
|
||||
size_t size;
|
||||
char *args, *cp, *sp, *np;
|
||||
|
||||
// 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");
|
||||
"java/lang/RuntimeException", "sysctl failed");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -437,7 +265,7 @@ static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
|
||||
do { // a block to break out of on error
|
||||
char *argsEnd;
|
||||
jstring str = NULL;
|
||||
jstring cmdexe = NULL;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROCARGS2;
|
||||
@ -445,7 +273,7 @@ static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
size = (size_t) maxargs;
|
||||
if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
|
||||
if (errno != EINVAL) {
|
||||
JNU_ThrowByNameWithLastError(env,
|
||||
JNU_ThrowByNameWithLastError(env,
|
||||
"java/lang/RuntimeException", "sysctl failed");
|
||||
}
|
||||
break;
|
||||
@ -456,11 +284,7 @@ static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
argsEnd = &args[size];
|
||||
|
||||
// Store the command executable path
|
||||
if ((str = JNU_NewStringPlatform(env, cp)) == NULL) {
|
||||
break;
|
||||
}
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -471,7 +295,7 @@ static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
}
|
||||
}
|
||||
|
||||
fillArgArray(env, jinfo, nargs, cp, argsEnd);
|
||||
unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe, NULL);
|
||||
} while (0);
|
||||
// Free the arg buffer
|
||||
free(args);
|
||||
|
@ -224,9 +224,35 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
|
||||
*/
|
||||
public Optional<String> command();
|
||||
|
||||
/**
|
||||
* Returns the command line of the process.
|
||||
* <p>
|
||||
* If {@link #command command()} and {@link #arguments arguments()} return
|
||||
* non-empty optionals, this is simply a convenience method which concatenates
|
||||
* the values of the two functions separated by spaces. Otherwise it will return a
|
||||
* best-effort, platform dependent representation of the command line.
|
||||
*
|
||||
* @apiNote Note that the returned executable pathname and the
|
||||
* arguments may be truncated on some platforms due to system
|
||||
* limitations.
|
||||
* <p>
|
||||
* The executable pathname may contain only the
|
||||
* name of the executable without the full path information.
|
||||
* It is undecideable whether white space separates different
|
||||
* arguments or is part of a single argument.
|
||||
*
|
||||
* @return an {@code Optional<String>} of the command line
|
||||
* of the process
|
||||
*/
|
||||
public Optional<String> commandLine();
|
||||
|
||||
/**
|
||||
* Returns an array of Strings of the arguments of the process.
|
||||
*
|
||||
* @apiNote On some platforms, native applications are free to change
|
||||
* the arguments array after startup and this method may only
|
||||
* show the changed values.
|
||||
*
|
||||
* @return an {@code Optional<String[]>} of the arguments of the process
|
||||
*/
|
||||
public Optional<String[]> arguments();
|
||||
|
@ -472,7 +472,7 @@ final class ProcessHandleImpl implements ProcessHandle {
|
||||
/**
|
||||
* Implementation of ProcessHandle.Info.
|
||||
* Information snapshot about a process.
|
||||
* The attributes of a process vary by operating system and not available
|
||||
* The attributes of a process vary by operating system and are not available
|
||||
* in all implementations. Additionally, information about other processes
|
||||
* is limited by the operating system privileges of the process making the request.
|
||||
* If a value is not available, either a {@code null} or {@code -1} is stored.
|
||||
@ -496,6 +496,7 @@ final class ProcessHandleImpl implements ProcessHandle {
|
||||
private native void info0(long pid);
|
||||
|
||||
String command;
|
||||
String commandLine;
|
||||
String[] arguments;
|
||||
long startTime;
|
||||
long totalTime;
|
||||
@ -503,6 +504,7 @@ final class ProcessHandleImpl implements ProcessHandle {
|
||||
|
||||
Info() {
|
||||
command = null;
|
||||
commandLine = null;
|
||||
arguments = null;
|
||||
startTime = -1L;
|
||||
totalTime = -1L;
|
||||
@ -538,6 +540,15 @@ final class ProcessHandleImpl implements ProcessHandle {
|
||||
return Optional.ofNullable(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> commandLine() {
|
||||
if (command != null && arguments != null) {
|
||||
return Optional.of(command + " " + String.join(" ", arguments));
|
||||
} else {
|
||||
return Optional.ofNullable(commandLine);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String[]> arguments() {
|
||||
return Optional.ofNullable(arguments);
|
||||
@ -580,6 +591,11 @@ final class ProcessHandleImpl implements ProcessHandle {
|
||||
sb.append("args: ");
|
||||
sb.append(Arrays.toString(arguments));
|
||||
}
|
||||
if (commandLine != null) {
|
||||
if (sb.length() != 0) sb.append(", ");
|
||||
sb.append("cmdLine: ");
|
||||
sb.append(commandLine);
|
||||
}
|
||||
if (startTime > 0) {
|
||||
if (sb.length() != 0) sb.append(", ");
|
||||
sb.append("startTime: ");
|
||||
|
@ -24,368 +24,28 @@
|
||||
*/
|
||||
|
||||
#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 <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <procfs.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
/**
|
||||
* Implementations of ProcessHandleImpl functions that are
|
||||
* NOT common to all Unix variants:
|
||||
* - getProcessPids0(pid, pidArray)
|
||||
*
|
||||
* Implementations of ProcessHandleImpl_Info
|
||||
* - totalTime, startTime
|
||||
* - Command
|
||||
* - Arguments
|
||||
*/
|
||||
|
||||
/*
|
||||
* Signatures for internal OS specific functions.
|
||||
* Implementation of native ProcessHandleImpl functions for Solaris.
|
||||
* See ProcessHandleImpl_unix.c for more details.
|
||||
*/
|
||||
static pid_t getStatInfo(JNIEnv *env, pid_t pid,
|
||||
jlong *totalTime, jlong* startTime,
|
||||
uid_t *uid);
|
||||
static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
|
||||
|
||||
extern jstring uidToUser(JNIEnv* env, uid_t uid);
|
||||
void os_initNative(JNIEnv *env, jclass clazz) {}
|
||||
|
||||
/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_commandID;
|
||||
|
||||
/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_argumentsID;
|
||||
|
||||
/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_totalTimeID;
|
||||
|
||||
/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_startTimeID;
|
||||
|
||||
/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_userID;
|
||||
|
||||
/* static value for clock ticks per second. */
|
||||
static long clock_ticks_per_second;
|
||||
|
||||
/**************************************************************
|
||||
* Static method to initialize field IDs and the ticks per second rate.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl_Info
|
||||
* Method: initIDs
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
|
||||
CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
|
||||
clazz, "command", "Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
|
||||
clazz, "arguments", "[Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
|
||||
clazz, "totalTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
|
||||
clazz, "startTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
|
||||
clazz, "user", "Ljava/lang/String;"));
|
||||
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
|
||||
jlongArray jparentArray, jlongArray jstimesArray) {
|
||||
return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
|
||||
}
|
||||
|
||||
/**************************************************************
|
||||
* Static method to initialize the ticks per second rate.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: initNative
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
|
||||
clock_ticks_per_second = sysconf(_SC_CLK_TCK);
|
||||
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) {
|
||||
return unix_getParentPidAndTimings(env, pid, total, start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a process is alive.
|
||||
* Return the start time (ms since 1970) if it is available.
|
||||
* If the start time is not available return 0.
|
||||
* If the pid is invalid, return -1.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: isAlive0
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
jlong startTime = 0L;
|
||||
jlong totalTime = 0L;
|
||||
uid_t uid = -1;
|
||||
pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime, &uid);
|
||||
return (ppid < 0) ? -1 : startTime;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the parent pid of the requested pid.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: parent0
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
|
||||
jobject obj,
|
||||
jlong jpid,
|
||||
jlong startTime) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
pid_t ppid = -1;
|
||||
|
||||
if (pid == getpid()) {
|
||||
ppid = getppid();
|
||||
} else {
|
||||
jlong start = 0L;
|
||||
jlong total = 0L;
|
||||
uid_t uid = -1;
|
||||
|
||||
pid_t ppid = getStatInfo(env, pid, &total, &start, &uid);
|
||||
if (start != startTime
|
||||
&& start != 0
|
||||
&& startTime != 0) {
|
||||
ppid = -1;
|
||||
}
|
||||
}
|
||||
return (jlong) ppid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the children of the requested pid and optionally each parent.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: getChildPids
|
||||
* Signature: (J[J)I
|
||||
*
|
||||
* Reads /proc and accumulates any process who parent pid matches.
|
||||
* The resulting pids are stored into the array of longs.
|
||||
* The number of pids is returned if they all fit.
|
||||
* If the array is too short, the desired length is returned.
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
|
||||
jclass clazz,
|
||||
jlong jpid,
|
||||
jlongArray jarray,
|
||||
jlongArray jparentArray,
|
||||
jlongArray jstimesArray) {
|
||||
DIR* dir;
|
||||
struct dirent* ptr;
|
||||
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;
|
||||
char procname[32];
|
||||
|
||||
arraySize = (*env)->GetArrayLength(env, jarray);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, 0);
|
||||
if (jparentArray != NULL) {
|
||||
parentArraySize = (*env)->GetArrayLength(env, jparentArray);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, 0);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* To locate the children we scan /proc looking for files that have a
|
||||
* positive integer as a filename.
|
||||
*/
|
||||
if ((dir = opendir("/proc")) == NULL) {
|
||||
JNU_ThrowByNameWithLastError(env,
|
||||
"java/lang/Runtime", "Unable to open /proc");
|
||||
return 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 ((ptr = readdir(dir)) != NULL) {
|
||||
pid_t ppid = 0;
|
||||
jlong totalTime = 0L;
|
||||
jlong startTime = 0L;
|
||||
uid_t uid; // value unused
|
||||
|
||||
/* skip files that aren't numbers */
|
||||
pid_t childpid = (pid_t) atoi(ptr->d_name);
|
||||
if ((int) childpid <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read /proc/pid/stat and get the parent pid, and start time
|
||||
ppid = getStatInfo(env, childpid, &totalTime, &startTime, &uid);
|
||||
if (ppid >= 0 && (pid == 0 || ppid == pid)) {
|
||||
if (count < arraySize) {
|
||||
// Only store if it fits
|
||||
pids[count] = (jlong) childpid;
|
||||
|
||||
if (ppids != NULL) {
|
||||
// Store the parent Pid
|
||||
ppids[count] = (jlong) ppid;
|
||||
}
|
||||
if (stimes != NULL) {
|
||||
// Store the process start time
|
||||
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);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
// If more pids than array had size for; count will be greater than array size
|
||||
return count;
|
||||
}
|
||||
|
||||
/**************************************************************
|
||||
* Implementation of ProcessHandleImpl_Info native methods.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fill in the Info object from the OS information about the process.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl_Info
|
||||
* Method: info0
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
|
||||
jobject jinfo,
|
||||
jlong jpid) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
jlong startTime = 0L;
|
||||
jlong totalTime = 0L;
|
||||
uid_t uid = -1;
|
||||
pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime, &uid);
|
||||
|
||||
getCmdlineInfo(env, jinfo, pid);
|
||||
|
||||
if (ppid > 0) {
|
||||
jstring str;
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
CHECK_NULL((str = uidToUser(env, uid)));
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, str);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read /proc/<pid>/status and return the ppid, total cputime and start time.
|
||||
* Return: -1 is fail; zero is unknown; > 0 is parent pid
|
||||
*/
|
||||
static pid_t getStatInfo(JNIEnv *env, pid_t pid,
|
||||
jlong *totalTime, jlong* startTime,
|
||||
uid_t* uid) {
|
||||
FILE* fp;
|
||||
psinfo_t psinfo;
|
||||
char fn[32];
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Try to open /proc/%d/status
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
|
||||
fp = fopen(fn, "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = fread(&psinfo, 1, (sizeof psinfo), fp);
|
||||
fclose(fp);
|
||||
if (ret < (sizeof psinfo)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec;
|
||||
|
||||
*startTime = psinfo.pr_start.tv_sec * (jlong)1000 +
|
||||
psinfo.pr_start.tv_nsec / 1000000;
|
||||
|
||||
*uid = psinfo.pr_uid;
|
||||
|
||||
return (pid_t) psinfo.pr_ppid;
|
||||
}
|
||||
|
||||
static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
char fn[32];
|
||||
char exePath[PATH_MAX];
|
||||
jstring str = NULL;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The path to the executable command is the link in /proc/<pid>/paths/a.out.
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid);
|
||||
if ((ret = readlink(fn, exePath, PATH_MAX - 1)) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// null terminate and create String to store for command
|
||||
exePath[ret] = '\0';
|
||||
CHECK_NULL(str = JNU_NewStringPlatform(env, exePath));
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
unix_getCmdlineAndUserInfo(env, jinfo, pid);
|
||||
}
|
||||
|
||||
|
@ -28,31 +28,87 @@
|
||||
#include "java_lang_ProcessHandleImpl.h"
|
||||
#include "java_lang_ProcessHandleImpl_Info.h"
|
||||
|
||||
#include "ProcessHandleImpl_unix.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#ifdef _AIX
|
||||
#include <sys/procfs.h>
|
||||
#endif
|
||||
#ifdef __solaris__
|
||||
#include <procfs.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementations of ProcessHandleImpl functions that are common to all
|
||||
* Unix variants:
|
||||
* - waitForProcessExit0(pid, reap)
|
||||
* - getCurrentPid0()
|
||||
* - destroy0(pid, force)
|
||||
* This file contains the implementation of the native ProcessHandleImpl
|
||||
* functions which are common to all Unix variants.
|
||||
*
|
||||
* The currently supported Unix variants are Solaris, Linux, MaxOS X and AIX.
|
||||
* The various similarities and differences between these systems make it hard
|
||||
* to find a clear boundary between platform specific and shared code.
|
||||
*
|
||||
* In order to ease code sharing between the platforms while still keeping the
|
||||
* code as clean as possible (i.e. free of preprocessor macros) we use the
|
||||
* following source code layout (remember that ProcessHandleImpl_unix.c will
|
||||
* be compiled on EVERY Unix platform while ProcessHandleImpl_<os>.c will be
|
||||
* only compiled on the specific OS):
|
||||
*
|
||||
* - all the JNI wrappers for the ProcessHandleImpl functions go into this file
|
||||
* - if their implementation is common on ALL the supported Unix platforms it
|
||||
* goes right into the JNI wrappers
|
||||
* - if the whole function or substantial parts of it are platform dependent,
|
||||
* the implementation goes into os_<function_name> functions in
|
||||
* ProcessHandleImpl_<os>.c
|
||||
* - if at least two platforms implement an os_<function_name> function in the
|
||||
* same way, this implementation is factored out into unix_<function_name>,
|
||||
* placed into this file and called from the corresponding os_<function_name>
|
||||
* function.
|
||||
* - For convenience, all the os_ and unix_ functions are declared in
|
||||
* ProcessHandleImpl_unix.h which is included into every
|
||||
* ProcessHandleImpl_<os>.c file.
|
||||
*
|
||||
* Example 1:
|
||||
* ----------
|
||||
* The implementation of Java_java_lang_ProcessHandleImpl_initNative()
|
||||
* is the same on all platforms except on Linux where it initilizes one
|
||||
* additional field. So we place the implementation right into
|
||||
* Java_java_lang_ProcessHandleImpl_initNative() but add call to
|
||||
* os_init() at the end of the function which is empty on all platforms
|
||||
* except Linux where it performs the additionally initializations.
|
||||
*
|
||||
* Example 2:
|
||||
* ----------
|
||||
* The implementation of Java_java_lang_ProcessHandleImpl_00024Info_info0 is the
|
||||
* same on Solaris and AIX but different on Linux and MacOSX. We therefore simply
|
||||
* call the helpers os_getParentPidAndTimings() and os_getCmdlineAndUserInfo().
|
||||
* The Linux and MaxOS X versions of these functions (in the corresponding files
|
||||
* ProcessHandleImpl_linux.c and ProcessHandleImpl_macosx.c) directly contain
|
||||
* the platform specific implementations while the Solaris and AIX
|
||||
* implementations simply call back to unix_getParentPidAndTimings() and
|
||||
* unix_getCmdlineAndUserInfo() which are implemented right in this file.
|
||||
*
|
||||
* The term "same implementation" is still a question of interpretation. It my
|
||||
* be acceptable to have a few ifdef'ed lines if that allows the sharing of a
|
||||
* huge function. On the other hand, if the platform specific code in a shared
|
||||
* function grows over a certain limit, it may be better to refactor that
|
||||
* functionality into corresponding, platform-specific os_ functions.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WIFEXITED
|
||||
#define WIFEXITED(status) (((status)&0xFF) == 0)
|
||||
#endif
|
||||
@ -69,6 +125,19 @@
|
||||
#define WTERMSIG(status) ((status)&0x7F)
|
||||
#endif
|
||||
|
||||
#ifdef __solaris__
|
||||
/* The child exited because of a signal.
|
||||
* The best value to return is 0x80 + signal number,
|
||||
* because that is what all Unix shells do, and because
|
||||
* it allows callers to distinguish between process exit and
|
||||
* process death by signal.
|
||||
* Unfortunately, the historical behavior on Solaris is to return
|
||||
* the signal number, and we preserve this for compatibility. */
|
||||
#define WTERMSIG_RETURN(status) WTERMSIG(status)
|
||||
#else
|
||||
#define WTERMSIG_RETURN(status) (WTERMSIG(status) + 0x80)
|
||||
#endif
|
||||
|
||||
#define RESTARTABLE(_cmd, _result) do { \
|
||||
do { \
|
||||
_result = _cmd; \
|
||||
@ -81,21 +150,83 @@
|
||||
} while((_result == NULL) && (errno == EINTR)); \
|
||||
} while(0)
|
||||
|
||||
#ifdef __solaris__
|
||||
#define STAT_FILE "/proc/%d/status"
|
||||
#else
|
||||
#define STAT_FILE "/proc/%d/stat"
|
||||
#endif
|
||||
|
||||
/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */
|
||||
jfieldID ProcessHandleImpl_Info_commandID;
|
||||
|
||||
/* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */
|
||||
jfieldID ProcessHandleImpl_Info_commandLineID;
|
||||
|
||||
/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */
|
||||
jfieldID ProcessHandleImpl_Info_argumentsID;
|
||||
|
||||
/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */
|
||||
jfieldID ProcessHandleImpl_Info_totalTimeID;
|
||||
|
||||
/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */
|
||||
jfieldID ProcessHandleImpl_Info_startTimeID;
|
||||
|
||||
/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
|
||||
jfieldID ProcessHandleImpl_Info_userID;
|
||||
|
||||
/* Size of password or group entry when not available via sysconf */
|
||||
#define ENT_BUF_SIZE 1024
|
||||
/* The value for the size of the buffer used by getpwuid_r(). The result of */
|
||||
/* sysconf(_SC_GETPW_R_SIZE_MAX) if available or ENT_BUF_SIZE otherwise. */
|
||||
static long getpw_buf_size;
|
||||
|
||||
/**************************************************************
|
||||
* Static method to initialize field IDs and the ticks per second rate.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl_Info
|
||||
* Method: initIDs
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
|
||||
|
||||
CHECK_NULL(ProcessHandleImpl_Info_commandID =
|
||||
(*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_commandLineID =
|
||||
(*env)->GetFieldID(env, clazz, "commandLine", "Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_argumentsID =
|
||||
(*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_totalTimeID =
|
||||
(*env)->GetFieldID(env, clazz, "totalTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_startTimeID =
|
||||
(*env)->GetFieldID(env, clazz, "startTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_userID =
|
||||
(*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
* Static method to initialize platform dependent constants.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: initNative
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
|
||||
getpw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (getpw_buf_size == -1) {
|
||||
getpw_buf_size = ENT_BUF_SIZE;
|
||||
}
|
||||
os_initNative(env, clazz);
|
||||
}
|
||||
|
||||
/* Block until a child process exits and return its exit code.
|
||||
* Note, can only be called once for any given pid if reapStatus = true.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: waitForProcessExit0
|
||||
* Signature: (JZ)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
|
||||
jclass junk,
|
||||
jlong jpid,
|
||||
jboolean reapStatus)
|
||||
{
|
||||
jboolean reapStatus) {
|
||||
pid_t pid = (pid_t)jpid;
|
||||
errno = 0;
|
||||
|
||||
@ -117,18 +248,7 @@ Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
|
||||
if (WIFEXITED(status)) {
|
||||
return WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
/* The child exited because of a signal.
|
||||
* The best value to return is 0x80 + signal number,
|
||||
* because that is what all Unix shells do, and because
|
||||
* it allows callers to distinguish between process exit and
|
||||
* process death by signal.
|
||||
* Unfortunately, the historical behavior on Solaris is to return
|
||||
* the signal number, and we preserve this for compatibility. */
|
||||
#ifdef __solaris__
|
||||
return WTERMSIG(status);
|
||||
#else
|
||||
return 0x80 + WTERMSIG(status);
|
||||
#endif
|
||||
return WTERMSIG_RETURN(status);
|
||||
} else {
|
||||
return status;
|
||||
}
|
||||
@ -156,18 +276,7 @@ Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
|
||||
*/
|
||||
return siginfo.si_status;
|
||||
} else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) {
|
||||
/* The child exited because of a signal.
|
||||
* The best value to return is 0x80 + signal number,
|
||||
* because that is what all Unix shells do, and because
|
||||
* it allows callers to distinguish between process exit and
|
||||
* process death by signal.
|
||||
* Unfortunately, the historical behavior on Solaris is to return
|
||||
* the signal number, and we preserve this for compatibility. */
|
||||
#ifdef __solaris__
|
||||
return WTERMSIG(siginfo.si_status);
|
||||
#else
|
||||
return 0x80 + WTERMSIG(siginfo.si_status);
|
||||
#endif
|
||||
return WTERMSIG_RETURN(siginfo.si_status);
|
||||
} else {
|
||||
/*
|
||||
* Unknown exit code; pass it through.
|
||||
@ -191,7 +300,7 @@ Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) {
|
||||
/*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: destroy0
|
||||
* Signature: (Z)Z
|
||||
* Signature: (JJZ)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
|
||||
@ -210,119 +319,52 @@ Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of password or group entry when not available via sysconf
|
||||
/*
|
||||
* Returns the children of the requested pid and optionally each parent and
|
||||
* start time.
|
||||
* Accumulates any process who parent pid matches.
|
||||
* The resulting pids are stored into the array of longs.
|
||||
* The number of pids is returned if they all fit.
|
||||
* If the array is too short, the negative of the desired length is returned.
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: getProcessPids0
|
||||
* Signature: (J[J[J[J)I
|
||||
*/
|
||||
#define ENT_BUF_SIZE 1024
|
||||
|
||||
/**
|
||||
* Return a strong username for the uid_t or null.
|
||||
*/
|
||||
jstring uidToUser(JNIEnv* env, uid_t uid) {
|
||||
int result = 0;
|
||||
int buflen;
|
||||
char* pwbuf;
|
||||
jstring name = NULL;
|
||||
|
||||
/* allocate buffer for password record */
|
||||
buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (buflen == -1)
|
||||
buflen = ENT_BUF_SIZE;
|
||||
pwbuf = (char*)malloc(buflen);
|
||||
if (pwbuf == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent");
|
||||
} else {
|
||||
struct passwd pwent;
|
||||
struct passwd* p = NULL;
|
||||
|
||||
#ifdef __solaris__
|
||||
RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p);
|
||||
#else
|
||||
RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result);
|
||||
#endif
|
||||
|
||||
// Return the Java String if a name was found
|
||||
if (result == 0 && p != NULL &&
|
||||
p->pw_name != NULL && *(p->pw_name) != '\0') {
|
||||
name = JNU_NewStringPlatform(env, p->pw_name);
|
||||
}
|
||||
free(pwbuf);
|
||||
}
|
||||
return name;
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
|
||||
jclass clazz,
|
||||
jlong jpid,
|
||||
jlongArray jarray,
|
||||
jlongArray jparentArray,
|
||||
jlongArray jstimesArray) {
|
||||
return os_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementations of ProcessHandleImpl functions that are common to
|
||||
* (some) Unix variants:
|
||||
* - getProcessPids0(pid, pidArray, parentArray)
|
||||
*/
|
||||
|
||||
#if defined(__linux__) || defined(__AIX__)
|
||||
|
||||
/*
|
||||
* Signatures for internal OS specific functions.
|
||||
*/
|
||||
static pid_t getStatInfo(JNIEnv *env, pid_t pid,
|
||||
jlong *totalTime, jlong* startTime);
|
||||
static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo);
|
||||
static long long getBoottime(JNIEnv *env);
|
||||
|
||||
jstring uidToUser(JNIEnv* env, uid_t uid);
|
||||
|
||||
/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_commandID;
|
||||
|
||||
/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_argumentsID;
|
||||
|
||||
/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_totalTimeID;
|
||||
|
||||
/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_startTimeID;
|
||||
|
||||
/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_userID;
|
||||
|
||||
/* static value for clock ticks per second. */
|
||||
static long clock_ticks_per_second;
|
||||
|
||||
/* A static offset in milliseconds since boot. */
|
||||
static long long bootTime_ms;
|
||||
|
||||
/**************************************************************
|
||||
* Static method to initialize field IDs and the ticks per second rate.
|
||||
* Fill in the Info object from the OS information about the process.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl_Info
|
||||
* Method: initIDs
|
||||
* Signature: ()V
|
||||
* Method: info0
|
||||
* Signature: (Ljava/lang/ProcessHandle/Info;J)I
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
|
||||
jobject jinfo,
|
||||
jlong jpid) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
pid_t ppid;
|
||||
jlong totalTime = -1L;
|
||||
jlong startTime = -1L;
|
||||
|
||||
CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
|
||||
clazz, "command", "Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
|
||||
clazz, "arguments", "[Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
|
||||
clazz, "totalTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
|
||||
clazz, "startTime", "J"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
|
||||
clazz, "user", "Ljava/lang/String;"));
|
||||
}
|
||||
ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime);
|
||||
if (ppid >= 0) {
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
/**************************************************************
|
||||
* Static method to initialize the ticks per second rate.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: initNative
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
|
||||
clock_ticks_per_second = sysconf(_SC_CLK_TCK);
|
||||
bootTime_ms = getBoottime(env);
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
os_getCmdlineAndUserInfo(env, jinfo, pid);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -340,8 +382,8 @@ Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid)
|
||||
pid_t pid = (pid_t) jpid;
|
||||
jlong startTime = 0L;
|
||||
jlong totalTime = 0L;
|
||||
pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime);
|
||||
return (ppid <= 0) ? -1 : startTime;
|
||||
pid_t ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime);
|
||||
return (ppid < 0) ? -1 : startTime;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -350,7 +392,7 @@ Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid)
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: parent0
|
||||
* Signature: (J)J
|
||||
* Signature: (JJ)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
|
||||
@ -360,13 +402,12 @@ Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
|
||||
pid_t pid = (pid_t) jpid;
|
||||
pid_t ppid;
|
||||
|
||||
pid_t mypid = getpid();
|
||||
if (pid == mypid) {
|
||||
if (pid == getpid()) {
|
||||
ppid = getppid();
|
||||
} else {
|
||||
jlong start = 0L;;
|
||||
jlong start = 0L;
|
||||
jlong total = 0L; // unused
|
||||
ppid = getStatInfo(env, pid, &total, &start);
|
||||
ppid = os_getParentPidAndTimings(env, pid, &total, &start);
|
||||
if (start != startTime && start != 0 && startTime != 0) {
|
||||
ppid = -1;
|
||||
}
|
||||
@ -374,24 +415,94 @@ Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
|
||||
return (jlong) ppid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the argument array by parsing the arguments from the sequence
|
||||
* of arguments.
|
||||
*/
|
||||
void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, char *cp,
|
||||
char *argsEnd, jstring cmdexe, char *cmdline) {
|
||||
jobject argsArray;
|
||||
int i;
|
||||
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
if (nargs >= 1) {
|
||||
// Create a String array for nargs-1 elements
|
||||
argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL);
|
||||
CHECK_NULL(argsArray);
|
||||
|
||||
for (i = 0; i < nargs - 1; i++) {
|
||||
jstring str = NULL;
|
||||
|
||||
cp += strlen(cp) + 1;
|
||||
if (cp > argsEnd || *cp == '\0') {
|
||||
return; // Off the end pointer or an empty argument is an error
|
||||
}
|
||||
|
||||
CHECK_NULL((str = JNU_NewStringPlatform(env, cp)));
|
||||
|
||||
(*env)->SetObjectArrayElement(env, argsArray, i, str);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
if (cmdline != NULL) {
|
||||
jstring commandLine = NULL;
|
||||
CHECK_NULL((commandLine = JNU_NewStringPlatform(env, cmdline)));
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandLineID, commandLine);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
}
|
||||
|
||||
void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) {
|
||||
int result = 0;
|
||||
char* pwbuf;
|
||||
jstring name = NULL;
|
||||
|
||||
/* allocate buffer for password record */
|
||||
pwbuf = (char*)malloc(getpw_buf_size);
|
||||
if (pwbuf == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent");
|
||||
} else {
|
||||
struct passwd pwent;
|
||||
struct passwd* p = NULL;
|
||||
|
||||
#ifdef __solaris__
|
||||
RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)getpw_buf_size), p);
|
||||
#else
|
||||
RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)getpw_buf_size, &p), result);
|
||||
#endif
|
||||
|
||||
// Create the Java String if a name was found
|
||||
if (result == 0 && p != NULL &&
|
||||
p->pw_name != NULL && *(p->pw_name) != '\0') {
|
||||
name = JNU_NewStringPlatform(env, p->pw_name);
|
||||
}
|
||||
free(pwbuf);
|
||||
}
|
||||
if (name != NULL) {
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the children of the requested pid and optionally each parent.
|
||||
* The following functions are common on Solaris, Linux and AIX.
|
||||
*/
|
||||
|
||||
#if defined(__solaris__) || defined (__linux__) || defined(_AIX)
|
||||
|
||||
/*
|
||||
* Returns the children of the requested pid and optionally each parent and
|
||||
* start time.
|
||||
* Reads /proc and accumulates any process who parent pid matches.
|
||||
* The resulting pids are stored into the array of longs.
|
||||
* The number of pids is returned if they all fit.
|
||||
* If the array is too short, the negative of the desired length is returned. *
|
||||
* Class: java_lang_ProcessHandleImpl
|
||||
* Method: getChildPids
|
||||
* Signature: (J[J[J)I
|
||||
* If the array is too short, the negative of the desired length is returned.
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
|
||||
jclass clazz,
|
||||
jlong jpid,
|
||||
jlongArray jarray,
|
||||
jlongArray jparentArray,
|
||||
jlongArray jstimesArray) {
|
||||
|
||||
jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
|
||||
jlongArray jparentArray, jlongArray jstimesArray) {
|
||||
DIR* dir;
|
||||
struct dirent* ptr;
|
||||
pid_t pid = (pid_t) jpid;
|
||||
@ -462,9 +573,10 @@ Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
|
||||
if ((int) childpid <= 0) {
|
||||
continue;
|
||||
}
|
||||
// Read /proc/pid/stat and get the parent pid, and start time
|
||||
ppid = getStatInfo(env, childpid, &totalTime, &startTime);
|
||||
if (ppid > 0 && (pid == 0 || ppid == pid)) {
|
||||
|
||||
// Get the parent pid, and start time
|
||||
ppid = os_getParentPidAndTimings(env, childpid, &totalTime, &startTime);
|
||||
if (ppid >= 0 && (pid == 0 || ppid == pid)) {
|
||||
if (count < arraySize) {
|
||||
// Only store if it fits
|
||||
pids[count] = (jlong) childpid;
|
||||
@ -498,293 +610,110 @@ Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************
|
||||
* Implementation of ProcessHandleImpl_Info native methods.
|
||||
*/
|
||||
#endif // defined(__solaris__) || defined (__linux__) || defined(_AIX)
|
||||
|
||||
/*
|
||||
* Fill in the Info object from the OS information about the process.
|
||||
*
|
||||
* Class: java_lang_ProcessHandleImpl_Info
|
||||
* Method: info0
|
||||
* Signature: (JLjava/lang/ProcessHandle/Info;)I
|
||||
* The following functions are common on Solaris and AIX.
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
|
||||
jobject jinfo,
|
||||
jlong jpid) {
|
||||
pid_t pid = (pid_t) jpid;
|
||||
pid_t ppid;
|
||||
jlong totalTime = 0L;
|
||||
jlong startTime = -1L;
|
||||
|
||||
ppid = getStatInfo(env, pid, &totalTime, &startTime);
|
||||
if (ppid > 0) {
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
(*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
|
||||
getCmdlineInfo(env, pid, jinfo);
|
||||
}
|
||||
}
|
||||
#if defined(__solaris__) || defined(_AIX)
|
||||
|
||||
/**
|
||||
* Read /proc/<pid>/stat and return the ppid, total cputime and start time.
|
||||
* -1 is fail; zero is unknown; > 0 is parent pid
|
||||
* Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo".
|
||||
* Returns 0 on success and -1 on error.
|
||||
*/
|
||||
static pid_t getStatInfo(JNIEnv *env, pid_t pid,
|
||||
jlong *totalTime, jlong* startTime) {
|
||||
static int getPsinfo(pid_t pid, psinfo_t *psinfo) {
|
||||
FILE* fp;
|
||||
char buffer[2048];
|
||||
int statlen;
|
||||
char fn[32];
|
||||
char* s;
|
||||
int parentPid;
|
||||
long unsigned int utime = 0; // clock tics
|
||||
long unsigned int stime = 0; // clock tics
|
||||
long long unsigned int start = 0; // microseconds
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Try to stat and then open /proc/%d/stat
|
||||
* Try to open /proc/%d/psinfo
|
||||
*/
|
||||
snprintf(fn, sizeof fn, STAT_FILE, pid);
|
||||
|
||||
snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
|
||||
fp = fopen(fn, "r");
|
||||
if (fp == NULL) {
|
||||
return -1; // fail, no such /proc/pid/stat
|
||||
}
|
||||
|
||||
/*
|
||||
* The format is: pid (command) state ppid ...
|
||||
* As the command could be anything we must find the right most
|
||||
* ")" and then skip the white spaces that follow it.
|
||||
*/
|
||||
statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
|
||||
fclose(fp);
|
||||
if (statlen < 0) {
|
||||
return 0; // parent pid is not available
|
||||
}
|
||||
|
||||
buffer[statlen] = '\0';
|
||||
s = strchr(buffer, '(');
|
||||
if (s == NULL) {
|
||||
return 0; // parent pid is not available
|
||||
}
|
||||
// Found start of command, skip to end
|
||||
s++;
|
||||
s = strrchr(s, ')');
|
||||
if (s == NULL) {
|
||||
return 0; // parent pid is not available
|
||||
}
|
||||
s++;
|
||||
|
||||
// Scan the needed fields from status, retaining only ppid(4),
|
||||
// utime (14), stime(15), starttime(22)
|
||||
if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
|
||||
&parentPid, &utime, &stime, &start)) {
|
||||
return 0; // not all values parsed; return error
|
||||
}
|
||||
|
||||
*totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
|
||||
|
||||
*startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
|
||||
|
||||
return parentPid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the argument array by parsing the arguments from the sequence
|
||||
* of arguments. The zero'th arg is the command executable
|
||||
*/
|
||||
static int fillArgArray(JNIEnv *env, jobject jinfo,
|
||||
int nargs, char *cp, char *argsEnd, jstring cmdexe) {
|
||||
jobject argsArray;
|
||||
int i;
|
||||
|
||||
if (nargs < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmdexe == NULL) {
|
||||
// Create a string from arg[0]
|
||||
CHECK_NULL_RETURN((cmdexe = JNU_NewStringPlatform(env, cp)), -1);
|
||||
}
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, -3);
|
||||
|
||||
// Create a String array for nargs-1 elements
|
||||
argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL);
|
||||
CHECK_NULL_RETURN(argsArray, -1);
|
||||
|
||||
for (i = 0; i < nargs - 1; i++) {
|
||||
jstring str = NULL;
|
||||
|
||||
cp += strnlen(cp, (argsEnd - cp)) + 1;
|
||||
if (cp > argsEnd || *cp == '\0') {
|
||||
return -2; // Off the end pointer or an empty argument is an error
|
||||
}
|
||||
|
||||
CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
|
||||
|
||||
(*env)->SetObjectArrayElement(env, argsArray, i, str);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, -3);
|
||||
}
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, -4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo) {
|
||||
int fd;
|
||||
int cmdlen = 0;
|
||||
char *cmdline = NULL, *cmdEnd; // used for command line args and exe
|
||||
jstring cmdexe = NULL;
|
||||
char fn[32];
|
||||
struct stat stat_buf;
|
||||
|
||||
/*
|
||||
* Try to open /proc/%d/cmdline
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
|
||||
if ((fd = open(fn, O_RDONLY)) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
do { // Block to break out of on errors
|
||||
int i;
|
||||
char *s;
|
||||
|
||||
cmdline = (char*)malloc(PATH_MAX);
|
||||
if (cmdline == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The path to the executable command is the link in /proc/<pid>/exe.
|
||||
*/
|
||||
snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
|
||||
if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) {
|
||||
// null terminate and create String to store for command
|
||||
cmdline[cmdlen] = '\0';
|
||||
cmdexe = JNU_NewStringPlatform(env, cmdline);
|
||||
(*env)->ExceptionClear(env); // unconditionally clear any exception
|
||||
}
|
||||
|
||||
/*
|
||||
* The buffer format is the arguments nul terminated with an extra nul.
|
||||
*/
|
||||
cmdlen = read(fd, cmdline, PATH_MAX-1);
|
||||
if (cmdlen < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Terminate the buffer and count the arguments
|
||||
cmdline[cmdlen] = '\0';
|
||||
cmdEnd = &cmdline[cmdlen + 1];
|
||||
for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) {
|
||||
s += strnlen(s, (cmdEnd - s)) + 1;
|
||||
}
|
||||
|
||||
if (fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe) < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Get and store the user name
|
||||
if (fstat(fd, &stat_buf) == 0) {
|
||||
jstring name = uidToUser(env, stat_buf.st_uid);
|
||||
if (name != NULL) {
|
||||
(*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (cmdline != NULL) {
|
||||
free(cmdline);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the boottime from /proc/stat.
|
||||
*/
|
||||
static long long getBoottime(JNIEnv *env) {
|
||||
FILE *fp;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
long long bootTime = 0;
|
||||
|
||||
fp = fopen("/proc/stat", "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (getline(&line, &len, fp) != -1) {
|
||||
if (sscanf(line, "btime %llu", &bootTime) == 1) {
|
||||
break;
|
||||
}
|
||||
ret = fread(psinfo, 1, sizeof(psinfo_t), fp);
|
||||
fclose(fp);
|
||||
if (ret < sizeof(psinfo_t)) {
|
||||
return -1;
|
||||
}
|
||||
free(line);
|
||||
|
||||
if (fp != 0) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return bootTime * 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // defined(__linux__) || defined(__AIX__)
|
||||
/**
|
||||
* Read /proc/<pid>/psinfo 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 unix_getParentPidAndTimings(JNIEnv *env, pid_t pid,
|
||||
jlong *totalTime, jlong* startTime) {
|
||||
psinfo_t psinfo;
|
||||
|
||||
|
||||
/* Block until a child process exits and return its exit code.
|
||||
Note, can only be called once for any given pid. */
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env,
|
||||
jobject junk,
|
||||
jint pid)
|
||||
{
|
||||
/* We used to use waitid() on Solaris, waitpid() on Linux, but
|
||||
* waitpid() is more standard, so use it on all POSIX platforms. */
|
||||
int status;
|
||||
/* Wait for the child process to exit. This returns immediately if
|
||||
the child has already exited. */
|
||||
while (waitpid(pid, &status, 0) < 0) {
|
||||
switch (errno) {
|
||||
case ECHILD: return 0;
|
||||
case EINTR: break;
|
||||
default: return -1;
|
||||
}
|
||||
if (getPsinfo(pid, &psinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
/*
|
||||
* The child exited normally; get its exit code.
|
||||
*/
|
||||
return WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
/* The child exited because of a signal.
|
||||
* The best value to return is 0x80 + signal number,
|
||||
* because that is what all Unix shells do, and because
|
||||
* it allows callers to distinguish between process exit and
|
||||
* process death by signal.
|
||||
* Unfortunately, the historical behavior on Solaris is to return
|
||||
* the signal number, and we preserve this for compatibility. */
|
||||
#ifdef __solaris__
|
||||
return WTERMSIG(status);
|
||||
#else
|
||||
return 0x80 + WTERMSIG(status);
|
||||
*totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec;
|
||||
|
||||
*startTime = psinfo.pr_start.tv_sec * (jlong)1000 +
|
||||
psinfo.pr_start.tv_nsec / 1000000;
|
||||
|
||||
return (pid_t) psinfo.pr_ppid;
|
||||
}
|
||||
|
||||
void unix_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
|
||||
psinfo_t psinfo;
|
||||
char fn[32];
|
||||
char exePath[PATH_MAX];
|
||||
char prargs[PRARGSZ + 1];
|
||||
jstring cmdexe = NULL;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* On Solaris, the full path to the executable command is the link in
|
||||
* /proc/<pid>/paths/a.out. But it is only readable for processes we own.
|
||||
*/
|
||||
#if defined(__solaris__)
|
||||
snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid);
|
||||
if ((ret = readlink(fn, exePath, PATH_MAX - 1)) > 0) {
|
||||
// null terminate and create String to store for command
|
||||
exePath[ret] = '\0';
|
||||
CHECK_NULL(cmdexe = JNU_NewStringPlatform(env, exePath));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* Unknown exit code; pass it through.
|
||||
*/
|
||||
return status;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
#endif // defined(__solaris__) || defined(_AIX)
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 <sys/types.h>
|
||||
|
||||
/*
|
||||
* Declaration of ProcessHandleImpl functions common on all Unix platforms.
|
||||
* 'unix_' functions have a single implementation in ProcessHandleImpl_unix.c
|
||||
* 'os_' prefixed functions have different, os-specific implementations in the
|
||||
* various ProcessHandleImpl_{linux,macosx,solaris,aix}.c files.
|
||||
* See ProcessHandleImpl_unix.c for more details.
|
||||
*/
|
||||
|
||||
/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */
|
||||
extern jfieldID ProcessHandleImpl_Info_commandID;
|
||||
|
||||
/* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */
|
||||
extern jfieldID ProcessHandleImpl_Info_commandLineID;
|
||||
|
||||
/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */
|
||||
extern jfieldID ProcessHandleImpl_Info_argumentsID;
|
||||
|
||||
/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */
|
||||
extern jfieldID ProcessHandleImpl_Info_totalTimeID;
|
||||
|
||||
/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */
|
||||
extern jfieldID ProcessHandleImpl_Info_startTimeID;
|
||||
|
||||
/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
|
||||
extern jfieldID ProcessHandleImpl_Info_userID;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
extern pid_t unix_getParentPidAndTimings(JNIEnv *env, pid_t pid,
|
||||
jlong *total, jlong *start);
|
||||
extern pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid,
|
||||
jlong *total, jlong *start);
|
||||
|
||||
extern void unix_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid);
|
||||
extern void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid);
|
||||
|
||||
extern jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray array,
|
||||
jlongArray jparentArray, jlongArray jstimesArray);
|
||||
extern jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray array,
|
||||
jlongArray jparentArray, jlongArray jstimesArray);
|
||||
|
||||
extern void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid);
|
||||
extern void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, char *cp,
|
||||
char *argsEnd, jstring cmdexe, char *cmdline);
|
||||
|
||||
extern void os_initNative(JNIEnv *env, jclass clazz);
|
@ -45,6 +45,9 @@ static void procToUser(JNIEnv *env, HANDLE handle, jobject jinfo);
|
||||
/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_commandID;
|
||||
|
||||
/* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_commandLineID;
|
||||
|
||||
/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
|
||||
static jfieldID ProcessHandleImpl_Info_argumentsID;
|
||||
|
||||
@ -69,6 +72,8 @@ Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
|
||||
|
||||
CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
|
||||
clazz, "command", "Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_commandLineID = (*env)->GetFieldID(env,
|
||||
clazz, "commandLine", "Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
|
||||
clazz, "arguments", "[Ljava/lang/String;"));
|
||||
CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
|
||||
|
@ -136,7 +136,17 @@ public class InfoTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Platform.isAix()) {
|
||||
// Unfortunately, on AIX the usr/sys times reported through
|
||||
// /proc/<pid>/psinfo which are used by ProcessHandle.Info
|
||||
// are running slow compared to the corresponding times reported
|
||||
// by the times()/getrusage() system calls which are used by
|
||||
// OperatingSystemMXBean.getProcessCpuTime() and returned by
|
||||
// the JavaChild for the "cputime" command.
|
||||
// This is because /proc/<pid>/status is only updated once a second.
|
||||
// So we better wait a little bit to get plausible values here.
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
ProcessHandle.Info info = p1.info();
|
||||
System.out.printf(" info: %s%n", info);
|
||||
|
||||
@ -160,19 +170,10 @@ public class InfoTest {
|
||||
if (info.arguments().isPresent()) {
|
||||
String[] args = info.arguments().get();
|
||||
|
||||
if (Platform.isLinux() || Platform.isOSX()) {
|
||||
int offset = args.length - extraArgs.length;
|
||||
for (int i = 0; i < extraArgs.length; i++) {
|
||||
Assert.assertEquals(args[offset + i], extraArgs[i],
|
||||
"Actual argument mismatch, index: " + i);
|
||||
}
|
||||
} else if (Platform.isSolaris()) {
|
||||
Assert.assertEquals(args.length, 1,
|
||||
"Expected argument list length: 1");
|
||||
Assert.assertNotNull(args[0],
|
||||
"Expected an argument");
|
||||
} else {
|
||||
System.out.printf("No argument test for OS: %s%n", Platform.getOsName());
|
||||
int offset = args.length - extraArgs.length;
|
||||
for (int i = 0; i < extraArgs.length; i++) {
|
||||
Assert.assertEquals(args[offset + i], extraArgs[i],
|
||||
"Actual argument mismatch, index: " + i);
|
||||
}
|
||||
|
||||
// Now check that the first argument is not the same as the executed command
|
||||
@ -183,6 +184,46 @@ public class InfoTest {
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isPresent() && info.arguments().isPresent()) {
|
||||
// If both, 'command' and 'arguments' are present,
|
||||
// 'commandLine' is just the concatenation of the two.
|
||||
Assert.assertTrue(info.commandLine().isPresent(),
|
||||
"commandLine() must be available");
|
||||
|
||||
String javaExe = System.getProperty("test.jdk") +
|
||||
File.separator + "bin" + File.separator + "java";
|
||||
String expected = Platform.isWindows() ? javaExe + ".exe" : javaExe;
|
||||
Path expectedPath = Paths.get(expected);
|
||||
String commandLine = info.commandLine().get();
|
||||
String commandLineCmd = commandLine.split(" ")[0];
|
||||
Path commandLineCmdPath = Paths.get(commandLineCmd);
|
||||
Assert.assertTrue(Files.isSameFile(commandLineCmdPath, expectedPath),
|
||||
"commandLine() should start with: " + expectedPath +
|
||||
" but starts with " + commandLineCmdPath);
|
||||
|
||||
List<String> allArgs = p1.getArgs();
|
||||
for (int i = 0; i < allArgs.size(); i++) {
|
||||
Assert.assertTrue(commandLine.contains(allArgs.get(i)),
|
||||
"commandLine() must contain argument: " + allArgs.get(i));
|
||||
}
|
||||
} else if (info.commandLine().isPresent()) {
|
||||
// If we only have the commandLine() we can only do some basic checks...
|
||||
String commandLine = info.commandLine().get();
|
||||
String javaExe = "java" + (Platform.isWindows() ? ".exe": "");
|
||||
int pos = commandLine.indexOf(javaExe);
|
||||
Assert.assertTrue(pos > 0, "commandLine() should at least contain 'java'");
|
||||
|
||||
pos += javaExe.length() + 1; // +1 for the space after the command
|
||||
List<String> allArgs = p1.getArgs();
|
||||
// First argument is the command - skip it here as we've already checked that.
|
||||
for (int i = 1; (i < allArgs.size()) &&
|
||||
(pos + allArgs.get(i).length() < commandLine.length()); i++) {
|
||||
Assert.assertTrue(commandLine.contains(allArgs.get(i)),
|
||||
"commandLine() must contain argument: " + allArgs.get(i));
|
||||
pos += allArgs.get(i).length() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.totalCpuDuration().isPresent()) {
|
||||
Duration totalCPU = info.totalCpuDuration().get();
|
||||
Duration epsilon = Duration.ofMillis(200L);
|
||||
@ -269,10 +310,27 @@ public class InfoTest {
|
||||
public static void test4() {
|
||||
Duration myCputime1 = ProcessUtil.MXBeanCpuTime();
|
||||
|
||||
if (Platform.isAix()) {
|
||||
// Unfortunately, on AIX the usr/sys times reported through
|
||||
// /proc/<pid>/psinfo which are used by ProcessHandle.Info
|
||||
// are running slow compared to the corresponding times reported
|
||||
// by the times()/getrusage() system calls which are used by
|
||||
// OperatingSystemMXBean.getProcessCpuTime() and returned by
|
||||
// the JavaChild for the "cputime" command.
|
||||
// So we better wait a little bit to get plausible values here.
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ex) {}
|
||||
}
|
||||
Optional<Duration> dur1 = ProcessHandle.current().info().totalCpuDuration();
|
||||
|
||||
Duration myCputime2 = ProcessUtil.MXBeanCpuTime();
|
||||
|
||||
if (Platform.isAix()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ex) {}
|
||||
}
|
||||
Optional<Duration> dur2 = ProcessHandle.current().info().totalCpuDuration();
|
||||
|
||||
if (dur1.isPresent() && dur2.isPresent()) {
|
||||
|
@ -68,8 +68,9 @@ private static volatile int commandSeq = 0; // Command sequence number
|
||||
* {@link #forEachOutputLine} can be used to process output from the child
|
||||
* @param delegate the process to delegate and send commands to and get responses from
|
||||
*/
|
||||
private JavaChild(Process delegate) {
|
||||
this.delegate = delegate;
|
||||
private JavaChild(ProcessBuilder pb) throws IOException {
|
||||
allArgs = pb.command();
|
||||
delegate = pb.start();
|
||||
// Initialize PrintWriter with autoflush (on println)
|
||||
inputWriter = new PrintWriter(delegate.getOutputStream(), true);
|
||||
outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream()));
|
||||
@ -119,6 +120,10 @@ private static volatile int commandSeq = 0; // Command sequence number
|
||||
return "delegate: " + delegate.toString();
|
||||
}
|
||||
|
||||
public List<String> getArgs() {
|
||||
return allArgs;
|
||||
}
|
||||
|
||||
public CompletableFuture<JavaChild> onJavaChildExit() {
|
||||
return onExit().thenApply(ph -> this);
|
||||
}
|
||||
@ -187,7 +192,7 @@ private static volatile int commandSeq = 0; // Command sequence number
|
||||
}
|
||||
ProcessBuilder pb = build(stringArgs);
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
return new JavaChild(pb.start());
|
||||
return new JavaChild(pb);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,6 +241,9 @@ private static volatile int commandSeq = 0; // Command sequence number
|
||||
"-classpath", absolutifyPath(classpath),
|
||||
"JavaChild");
|
||||
|
||||
// Will hold the complete list of arguments which was given to Processbuilder.command()
|
||||
private List<String> allArgs;
|
||||
|
||||
private static String absolutifyPath(String path) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String file : path.split(File.pathSeparator)) {
|
||||
|
@ -126,6 +126,11 @@ public class OnExitTest extends ProcessUtil {
|
||||
} while (processes.size() < expected &&
|
||||
Instant.now().isBefore(endTimeout));
|
||||
|
||||
if (processes.size() < expected) {
|
||||
printf("WARNING: not all children have been started. Can't complete test.%n");
|
||||
printf(" You can try to increase the timeout or%n");
|
||||
printf(" you can try to use a faster VM (i.e. not a debug version).%n");
|
||||
}
|
||||
children = getAllChildren(procHandle);
|
||||
|
||||
ConcurrentHashMap<ProcessHandle, CompletableFuture<ProcessHandle>> completions =
|
||||
|
Loading…
Reference in New Issue
Block a user