8078099: (process) ProcessHandle should uniquely identify processes
8078108: (process) ProcessHandle.isAlive should be robust
Summary: isAlive should check and confirm the start time
Reviewed-by: simonis, plevart
--- a/jdk/make/mapfiles/libjava/mapfile-vers Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/make/mapfiles/libjava/mapfile-vers Tue Jul 14 15:35:37 2015 -0400
@@ -166,11 +166,12 @@
Java_java_lang_Package_getSystemPackage0;
Java_java_lang_Package_getSystemPackages0;
Java_java_lang_ProcessEnvironment_environ;
+ Java_java_lang_ProcessHandleImpl_destroy0;
Java_java_lang_ProcessHandleImpl_getCurrentPid0;
- Java_java_lang_ProcessHandleImpl_parent0;
+ Java_java_lang_ProcessHandleImpl_getProcessPids0;
+ Java_java_lang_ProcessHandleImpl_initNative;
Java_java_lang_ProcessHandleImpl_isAlive0;
- Java_java_lang_ProcessHandleImpl_getProcessPids0;
- Java_java_lang_ProcessHandleImpl_destroy0;
+ Java_java_lang_ProcessHandleImpl_parent0;
Java_java_lang_ProcessHandleImpl_waitForProcessExit0;
Java_java_lang_ProcessHandleImpl_00024Info_initIDs;
Java_java_lang_ProcessHandleImpl_00024Info_info0;
--- a/jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c Tue Jul 14 15:35:37 2015 -0400
@@ -75,9 +75,8 @@
* Method: initIDs
* Signature: ()V
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
- (JNIEnv *env, jclass clazz) {
-
+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 =
@@ -88,7 +87,45 @@
(*env)->GetFieldID(env, clazz, "startTime", "J"));
CHECK_NULL(ProcessHandleImpl_Info_userID =
(*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
- clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+}
+/**************************************************************
+ * 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);
+ }
}
/*
@@ -98,8 +135,11 @@
* Method: parent0
* Signature: (J)J
*/
-JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
-(JNIEnv *env, jobject obj, jlong jpid) {
+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;
@@ -117,7 +157,14 @@
"java/lang/RuntimeException", "sysctl failed");
return -1;
}
- ppid = (bufSize > 0 && kp.kp_proc.p_pid == pid) ? kp.kp_eproc.e_ppid : -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;
}
@@ -136,15 +183,20 @@
* 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)
-{
- size_t count = 0;
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
+ jclass clazz,
+ jlong jpid,
+ jlongArray jarray,
+ jlongArray jparentArray,
+ jlongArray jstimesArray) {
jlong* pids = NULL;
jlong* ppids = NULL;
- size_t parentArraySize = 0;
- size_t arraySize = 0;
+ jlong* stimes = NULL;
+ jsize parentArraySize = 0;
+ jsize arraySize = 0;
+ jsize stimesSize = 0;
+ jsize count = 0;
size_t bufSize = 0;
pid_t pid = (pid_t) jpid;
@@ -159,6 +211,15 @@
return 0;
}
}
+ if (jstimesArray != NULL) {
+ stimesSize = (*env)->GetArrayLength(env, jstimesArray);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+
+ if (arraySize != stimesSize) {
+ JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+ return 0;
+ }
+ }
// Get buffer size needed to read all processes
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
@@ -198,6 +259,12 @@
break;
}
}
+ if (jstimesArray != NULL) {
+ stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
+ if (stimes == NULL) {
+ break;
+ }
+ }
// Process each entry in the buffer
for (i = nentries; --i >= 0; ++kp) {
@@ -209,6 +276,12 @@
// Store the parentPid
ppids[count] = (jlong) kp->kp_eproc.e_ppid;
}
+ if (stimes != NULL) {
+ // Store the process start time
+ jlong startTime = kp->kp_proc.p_starttime.tv_sec * 1000 +
+ kp->kp_proc.p_starttime.tv_usec / 1000;
+ stimes[count] = startTime;
+ }
}
count++; // Count to tabulate size needed
}
@@ -221,6 +294,9 @@
if (ppids != NULL) {
(*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
}
+ if (stimes != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
+ }
free(buffer);
// If more pids than array had size for; count will be greater than array size
@@ -238,8 +314,10 @@
* Method: info0
* Signature: (J)I
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
- (JNIEnv *env, jobject jinfo, jlong jpid) {
+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);
@@ -251,7 +329,7 @@
*/
static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) {
jlong totalTime; // nanoseconds
- unsigned long long startTime; // microseconds
+ unsigned long long startTime; // milliseconds
const pid_t pid = (pid_t) jpid;
struct kinfo_proc kp;
--- a/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java Tue Jul 14 15:35:37 2015 -0400
@@ -39,6 +39,7 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import sun.misc.InnocuousThread;
@@ -52,6 +53,24 @@
* @since 1.9
*/
final class ProcessHandleImpl implements ProcessHandle {
+ /**
+ * Cache the ProcessHandle of this process.
+ */
+ private static final ProcessHandleImpl current;
+
+ /**
+ * Map of pids to ExitCompletions.
+ */
+ private static final ConcurrentMap<Long, ExitCompletion>
+ completions = new ConcurrentHashMap<>();
+
+ static {
+ initNative();
+ long pid = getCurrentPid0();
+ current = new ProcessHandleImpl(pid, isAlive0(pid));
+ }
+
+ private static native void initNative();
/**
* The thread pool of "process reaper" daemon threads.
@@ -84,9 +103,6 @@
}
}
- private static final ConcurrentMap<Long, ExitCompletion>
- completions = new ConcurrentHashMap<>();
-
/**
* Returns a CompletableFuture that completes with process exit status when
* the process completes.
@@ -142,22 +158,33 @@
private static native int waitForProcessExit0(long pid, boolean reapvalue);
/**
- * Cache the ProcessHandle of this process.
- */
- private static final ProcessHandleImpl current =
- new ProcessHandleImpl(getCurrentPid0());
-
- /**
* The pid of this ProcessHandle.
*/
private final long pid;
/**
+ * The start time of this process.
+ * If STARTTIME_ANY, the start time of the process is not available from the os.
+ * If greater than zero, the start time of the process.
+ */
+ private final long startTime;
+
+ /* The start time should match any value.
+ * Typically, this is because the OS can not supply it.
+ * The process is known to exist but not the exact start time.
+ */
+ private final long STARTTIME_ANY = 0L;
+
+ /* The start time of a Process that does not exist. */
+ private final long STARTTIME_PROCESS_UNKNOWN = -1;
+
+ /**
* Private constructor. Instances are created by the {@code get(long)} factory.
* @param pid the pid for this instance
*/
- private ProcessHandleImpl(long pid) {
+ private ProcessHandleImpl(long pid, long startTime) {
this.pid = pid;
+ this.startTime = startTime;
}
/**
@@ -173,17 +200,21 @@
if (sm != null) {
sm.checkPermission(new RuntimePermission("manageProcess"));
}
- return Optional.ofNullable(isAlive0(pid) ? new ProcessHandleImpl(pid) : null);
+ long start = isAlive0(pid);
+ return (start >= 0)
+ ? Optional.of(new ProcessHandleImpl(pid, start))
+ : Optional.empty();
}
/**
- * Returns a ProcessHandle corresponding known to exist pid.
- * Called from ProcessImpl, it does not perform a security check or check if the process is alive.
+ * Returns a ProcessHandle for an existing native process known to be alive.
+ * The startTime of the process is retrieved and stored in the ProcessHandle.
+ * It does not perform a security check since it is called from ProcessImpl.
* @param pid of the known to exist process
* @return a ProcessHandle corresponding to an existing Process instance
*/
- static ProcessHandle getUnchecked(long pid) {
- return new ProcessHandleImpl(pid);
+ static ProcessHandleImpl getInternal(long pid) {
+ return new ProcessHandleImpl(pid, isAlive0(pid));
}
/**
@@ -227,12 +258,12 @@
* @throws SecurityException if permission is not granted by the
* security policy
*/
- static Optional<ProcessHandle> parent(long pid) {
+ public Optional<ProcessHandle> parent() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("manageProcess"));
}
- long ppid = parent0(pid);
+ long ppid = parent0(pid, startTime);
if (ppid <= 0) {
return Optional.empty();
}
@@ -242,9 +273,11 @@
/**
* Returns the parent of the native pid argument.
*
+ * @param pid the process id
+ * @param startTime the startTime of the process
* @return the parent of the native pid; if any, otherwise -1
*/
- private static native long parent0(long pid);
+ private static native long parent0(long pid, long startTime);
/**
* Returns the number of pids filled in to the array.
@@ -252,37 +285,46 @@
* otherwise only direct child process pids are returned
* @param pids an allocated long array to receive the pids
* @param ppids an allocated long array to receive the parent pids; may be null
+ * @param starttimes an allocated long array to receive the child start times; may be null
* @return if greater than or equals to zero is the number of pids in the array;
* if greater than the length of the arrays, the arrays are too small
*/
- private static native int getProcessPids0(long pid, long[] pids, long[] ppids);
+ private static native int getProcessPids0(long pid, long[] pids,
+ long[] ppids, long[] starttimes);
/**
* Destroy the process for this ProcessHandle.
- * @param pid the processs ID to destroy
+ * The native code checks the start time before sending the termination request.
+ *
* @param force {@code true} if the process should be terminated forcibly;
* else {@code false} for a normal termination
*/
- static void destroyProcess(long pid, boolean force) {
- destroy0(pid, force);
+ boolean destroyProcess(boolean force) {
+ if (this.equals(current)) {
+ throw new IllegalStateException("destroy of current process not allowed");
+ }
+ return destroy0(pid, startTime, force);
}
- private static native boolean destroy0(long pid, boolean forcibly);
+ /**
+ * Signal the process to terminate.
+ * The process is signaled only if its start time matches the known start time.
+ *
+ * @param pid process id to kill
+ * @param startTime the start time of the process
+ * @param forcibly true to forcibly terminate (SIGKILL vs SIGTERM)
+ * @return true if the process was signaled without error; false otherwise
+ */
+ private static native boolean destroy0(long pid, long startTime, boolean forcibly);
@Override
public boolean destroy() {
- if (this.equals(current)) {
- throw new IllegalStateException("destroy of current process not allowed");
- }
- return destroy0(getPid(), false);
+ return destroyProcess(false);
}
@Override
public boolean destroyForcibly() {
- if (this.equals(current)) {
- throw new IllegalStateException("destroy of current process not allowed");
- }
- return destroy0(getPid(), true);
+ return destroyProcess(true);
}
@@ -300,22 +342,20 @@
*/
@Override
public boolean isAlive() {
- return isAlive0(pid);
+ long start = isAlive0(pid);
+ return (start >= 0 && (start == startTime || start == 0 || startTime == 0));
}
/**
- * Returns true or false depending on whether the pid is alive.
- * This must not reap the exitValue like the isAlive method above.
+ * Returns the process start time depending on whether the pid is alive.
+ * This must not reap the exitValue.
*
* @param pid the pid to check
- * @return true or false
+ * @return the start time in milliseconds since 1970,
+ * 0 if the start time cannot be determined,
+ * -1 if the pid does not exist.
*/
- private static native boolean isAlive0(long pid);
-
- @Override
- public Optional<ProcessHandle> parent() {
- return parent(pid);
- }
+ private static native long isAlive0(long pid);
@Override
public Stream<ProcessHandle> children() {
@@ -336,11 +376,16 @@
}
int size = 100;
long[] childpids = null;
+ long[] starttimes = null;
while (childpids == null || size > childpids.length) {
childpids = new long[size];
- size = getProcessPids0(pid, childpids, null);
+ starttimes = new long[size];
+ size = getProcessPids0(pid, childpids, null, starttimes);
}
- return Arrays.stream(childpids, 0, size).mapToObj((id) -> new ProcessHandleImpl(id));
+
+ final long[] cpids = childpids;
+ final long[] stimes = starttimes;
+ return IntStream.range(0, size).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
}
@Override
@@ -352,10 +397,12 @@
int size = 100;
long[] pids = null;
long[] ppids = null;
+ long[] starttimes = null;
while (pids == null || size > pids.length) {
pids = new long[size];
ppids = new long[size];
- size = getProcessPids0(0, pids, ppids);
+ starttimes = new long[size];
+ size = getProcessPids0(0, pids, ppids, starttimes);
}
int next = 0; // index of next process to check
@@ -368,13 +415,16 @@
if (ppids[i] == ppid) {
swap(pids, i, next);
swap(ppids, i, next);
+ swap(starttimes, i, next);
next++;
}
}
ppid = pids[++count]; // pick up the next pid to scan for
} while (count < next);
- return Arrays.stream(pids, 0, count).mapToObj((id) -> new ProcessHandleImpl(id));
+ final long[] cpids = pids;
+ final long[] stimes = starttimes;
+ return IntStream.range(0, count).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
}
// Swap two elements in an array
@@ -386,7 +436,7 @@
@Override
public ProcessHandle.Info info() {
- return ProcessHandleImpl.Info.info(pid);
+ return ProcessHandleImpl.Info.info(pid, startTime);
}
@Override
@@ -406,8 +456,17 @@
@Override
public boolean equals(Object obj) {
- return (obj instanceof ProcessHandleImpl) &&
- (pid == ((ProcessHandleImpl) obj).pid);
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ProcessHandleImpl) {
+ ProcessHandleImpl other = (ProcessHandleImpl) obj;
+ return (pid == other.pid) &&
+ (startTime == other.startTime
+ || startTime == 0
+ || other.startTime == 0);
+ }
+ return false;
}
/**
@@ -453,14 +512,24 @@
/**
* Returns the Info object with the fields from the process.
* Whatever fields are provided by native are returned.
+ * If the startTime of the process does not match the provided
+ * startTime then an empty Info is returned.
*
* @param pid the native process identifier
+ * @param startTime the startTime of the process being queried
* @return ProcessHandle.Info non-null; individual fields may be null
* or -1 if not available.
*/
- public static ProcessHandle.Info info(long pid) {
+ public static ProcessHandle.Info info(long pid, long startTime) {
Info info = new Info();
info.info0(pid);
+ if (startTime != info.startTime) {
+ info.command = null;
+ info.arguments = null;
+ info.startTime = -1L;
+ info.totalTime = -1L;
+ info.user = null;
+ }
return info;
}
@@ -511,7 +580,7 @@
sb.append("args: ");
sb.append(Arrays.toString(arguments));
}
- if (startTime != -1) {
+ if (startTime > 0) {
if (sb.length() != 0) sb.append(", ");
sb.append("startTime: ");
sb.append(startInstant());
--- a/jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c Tue Jul 14 15:35:37 2015 -0400
@@ -38,6 +38,7 @@
#include <signal.h>
#include <stdlib.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
@@ -55,8 +56,9 @@
/*
* Signatures for internal OS specific functions.
*/
-static pid_t parentPid(JNIEnv *env, pid_t pid);
-static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+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);
@@ -86,9 +88,8 @@
* Method: initIDs
* Signature: ()V
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
- (JNIEnv *env, jclass clazz) {
-
+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,
@@ -99,25 +100,68 @@
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;
+ 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) {
+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 {
- ppid = parentPid(env, pid);
+ 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;
}
@@ -134,18 +178,24 @@
* 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)
-{
+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;
- size_t count = 0;
jlong* pids = NULL;
jlong* ppids = NULL;
- size_t parentArraySize = 0;
- size_t arraySize = 0;
+ 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);
@@ -158,6 +208,15 @@
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
@@ -180,9 +239,18 @@
break;
}
}
+ if (jstimesArray != NULL) {
+ stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
+ if (stimes == NULL) {
+ break;
+ }
+ }
while ((ptr = readdir(dir)) != NULL) {
- pid_t ppid;
+ 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);
@@ -190,20 +258,21 @@
continue;
}
- ppid = 0;
- if (pid != 0 || jparentArray != NULL) {
- // parentPid opens and reads /proc/pid/stat
- ppid = parentPid(env, childpid);
- }
- if (pid == 0 || ppid == pid) {
+ // 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 parentPid
+ // 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
}
@@ -216,45 +285,15 @@
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;
}
-/*
- * Returns the parent pid of a given pid, or -1 if not found
- */
-static pid_t parentPid(JNIEnv *env, pid_t pid) {
- FILE* fp;
- pstatus_t pstatus;
- int statlen;
- char fn[32];
- int i, p;
- char* s;
-
- /*
- * Try to open /proc/%d/status
- */
- snprintf(fn, sizeof fn, "/proc/%d/status", pid);
- fp = fopen(fn, "r");
- if (fp == NULL) {
- return -1;
- }
-
- /*
- * 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(&pstatus, 1, (sizeof pstatus), fp);
- fclose(fp);
- if (statlen < 0) {
- return -1;
- }
- return (pid_t) pstatus.pr_ppid;
-}
-
/**************************************************************
* Implementation of ProcessHandleImpl_Info native methods.
*/
@@ -266,93 +305,74 @@
* Method: info0
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
- (JNIEnv *env, jobject jinfo, jlong jpid) {
+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);
+ 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>/stat and fill in the fields of the Info object.
- * Gather the user and system times.
+ * 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 void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+static pid_t getStatInfo(JNIEnv *env, pid_t pid,
+ jlong *totalTime, jlong* startTime,
+ uid_t* uid) {
FILE* fp;
- pstatus_t pstatus;
- struct stat stat_buf;
- int ret;
+ psinfo_t psinfo;
char fn[32];
- int i, p;
- char* s;
- jlong totalTime;
+ int ret;
/*
* Try to open /proc/%d/status
*/
- snprintf(fn, sizeof fn, "/proc/%d/status", pid);
-
- if (stat(fn, &stat_buf) < 0) {
- return;
- }
-
+ snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
fp = fopen(fn, "r");
if (fp == NULL) {
- return;
+ return -1;
}
- ret = fread(&pstatus, 1, (sizeof pstatus), fp);
+ ret = fread(&psinfo, 1, (sizeof psinfo), fp);
fclose(fp);
- if (ret < 0) {
- return;
+ if (ret < (sizeof psinfo)) {
+ return -1;
}
- totalTime = pstatus.pr_utime.tv_sec * 1000000000L + pstatus.pr_utime.tv_nsec +
- pstatus.pr_stime.tv_sec * 1000000000L + pstatus.pr_stime.tv_nsec;
+ *totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec;
- (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
- JNU_CHECK_EXCEPTION(env);
+ *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) {
- FILE* fp;
- psinfo_t psinfo;
- int ret;
char fn[32];
char exePath[PATH_MAX];
- int i, p;
- jlong startTime;
- jobjectArray cmdArray;
jstring str = NULL;
-
- /*
- * try to open /proc/%d/psinfo
- */
- snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
- fp = fopen(fn, "r");
- if (fp == NULL) {
- return;
- }
-
- /*
- * 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.
- */
- ret = fread(&psinfo, 1, (sizeof psinfo), fp);
- fclose(fp);
- if (ret < 0) {
- return;
- }
-
- CHECK_NULL((str = uidToUser(env, psinfo.pr_uid)));
- (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, str);
- JNU_CHECK_EXCEPTION(env);
-
- startTime = (jlong)psinfo.pr_start.tv_sec * (jlong)1000 +
- (jlong)psinfo.pr_start.tv_nsec / 1000000;
- (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
- JNU_CHECK_EXCEPTION(env);
+ int ret;
/*
* The path to the executable command is the link in /proc/<pid>/paths/a.out.
--- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Tue Jul 14 15:35:37 2015 -0400
@@ -64,7 +64,7 @@
static final boolean SUPPORTS_NORMAL_TERMINATION = true;
private final int pid;
- private final ProcessHandle processHandle;
+ private final ProcessHandleImpl processHandle;
private int exitcode;
private boolean hasExited;
@@ -320,7 +320,7 @@
dir,
fds,
redirectErrorStream);
- processHandle = ProcessHandleImpl.getUnchecked(pid);
+ processHandle = ProcessHandleImpl.getInternal(pid);
try {
doPrivileged((PrivilegedExceptionAction<Void>) () -> {
@@ -505,7 +505,7 @@
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
- ProcessHandleImpl.destroyProcess(pid, force);
+ processHandle.destroyProcess(force);
}
try { stdin.close(); } catch (IOException ignored) {}
try { stdout.close(); } catch (IOException ignored) {}
@@ -521,7 +521,7 @@
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
- ProcessHandleImpl.destroyProcess(pid, force);
+ processHandle.destroyProcess(force);
try {
stdin.close();
if (stdout_inner_stream != null)
@@ -542,7 +542,7 @@
@Override
public CompletableFuture<Process> onExit() {
return ProcessHandleImpl.completion(pid, false)
- .handleAsync((exitStatus, unusedThrowable) -> {
+ .handleAsync((unusedExitStatus, unusedThrowable) -> {
boolean interrupted = false;
while (true) {
// Ensure that the concurrent task setting the exit status has completed
--- a/jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c Tue Jul 14 15:35:37 2015 -0400
@@ -53,7 +53,6 @@
* - destroy0(pid, force)
*/
-
#ifndef WIFEXITED
#define WIFEXITED(status) (((status)&0xFF) == 0)
#endif
@@ -82,15 +81,20 @@
} while((_result == NULL) && (errno == EINTR)); \
} while(0)
+#ifdef __solaris__
+ #define STAT_FILE "/proc/%d/status"
+#else
+ #define STAT_FILE "/proc/%d/stat"
+#endif
/* Block until a child process exits and return its exit code.
* Note, can only be called once for any given pid if reapStatus = true.
*/
JNIEXPORT jint JNICALL
Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
- jclass junk,
- jlong jpid,
- jboolean reapStatus)
+ jclass junk,
+ jlong jpid,
+ jboolean reapStatus)
{
pid_t pid = (pid_t)jpid;
errno = 0;
@@ -178,34 +182,32 @@
* Method: getCurrentPid0
* Signature: ()J
*/
-JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0
-(JNIEnv *env, jclass clazz) {
+JNIEXPORT jlong JNICALL
+Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) {
pid_t pid = getpid();
return (jlong) pid;
}
/*
* Class: java_lang_ProcessHandleImpl
- * Method: isAlive0
- * Signature: (J)Z
- */
-JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0
-(JNIEnv *env, jobject obj, jlong jpid) {
- pid_t pid = (pid_t) jpid;
- return (kill(pid, 0) < 0) ? JNI_FALSE : JNI_TRUE;
-}
-
-/*
- * Class: java_lang_ProcessHandleImpl
* Method: destroy0
* Signature: (Z)Z
*/
-JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0
-(JNIEnv *env, jobject obj, jlong jpid, jboolean force) {
+JNIEXPORT jboolean JNICALL
+Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
+ jobject obj,
+ jlong jpid,
+ jlong startTime,
+ jboolean force) {
pid_t pid = (pid_t) jpid;
int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
- return (kill(pid, sig) >= 0);
+ jlong start = Java_java_lang_ProcessHandleImpl_isAlive0(env, obj, jpid);
+ if (start == startTime || start == 0 || startTime == 0) {
+ return (kill(pid, sig) < 0) ? JNI_FALSE : JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
}
/**
@@ -260,11 +262,8 @@
/*
* Signatures for internal OS specific functions.
*/
-static pid_t parentPid(JNIEnv *env, pid_t pid);
-static jint getChildren(JNIEnv *env, jlong jpid,
- jlongArray array, jlongArray jparentArray);
-
-static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+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);
@@ -298,8 +297,8 @@
* Method: initIDs
* Signature: ()V
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
- (JNIEnv *env, jclass clazz) {
+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;"));
@@ -311,61 +310,98 @@
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);
bootTime_ms = getBoottime(env);
}
/*
+ * 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;
+ pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime);
+ return (ppid <= 0) ? -1 : startTime;
+}
+
+/*
* Returns the parent pid of the requested pid.
+ * The start time of the process must match (or be ANY).
*
* Class: java_lang_ProcessHandleImpl
* Method: parent0
* Signature: (J)J
*/
-JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
-(JNIEnv *env, jobject obj, jlong jpid) {
+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;
+ pid_t ppid;
pid_t mypid = getpid();
if (pid == mypid) {
ppid = getppid();
} else {
- ppid = parentPid(env, pid);
+ jlong start = 0L;;
+ jlong total = 0L; // unused
+ ppid = getStatInfo(env, pid, &total, &start);
+ if (start != startTime && start != 0 && startTime != 0) {
+ ppid = -1;
+ }
}
return (jlong) ppid;
}
/*
* Returns the children of the requested pid and optionally each parent.
- *
+ * 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
*/
-JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
-(JNIEnv *env, jclass clazz, jlong jpid,
- jlongArray jarray, jlongArray jparentArray) {
- return getChildren(env, jpid, jarray, jparentArray);
-}
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
+ jclass clazz,
+ jlong jpid,
+ jlongArray jarray,
+ jlongArray jparentArray,
+ jlongArray jstimesArray) {
-/*
- * 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.
- */
-static jint getChildren(JNIEnv *env, jlong jpid,
- jlongArray jarray, jlongArray jparentArray) {
DIR* dir;
struct dirent* ptr;
pid_t pid = (pid_t) jpid;
- pid_t ppid = 0;
- size_t count = 0;
jlong* pids = NULL;
jlong* ppids = NULL;
- size_t parentArraySize = 0;
- size_t arraySize = 0;
+ 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);
@@ -378,6 +414,15 @@
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
@@ -385,7 +430,7 @@
*/
if ((dir = opendir("/proc")) == NULL) {
JNU_ThrowByNameWithLastError(env,
- "java/lang/Runtime", "Unable to open /proc");
+ "java/lang/RuntimeException", "Unable to open /proc");
return -1;
}
@@ -400,20 +445,26 @@
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;
+
/* skip files that aren't numbers */
pid_t childpid = (pid_t) atoi(ptr->d_name);
if ((int) childpid <= 0) {
continue;
}
-
- ppid = 0;
- if (pid != 0 || jparentArray != NULL) {
- // parentPid opens and reads /proc/pid/stat
- ppid = parentPid(env, childpid);
- }
- if (pid == 0 || ppid == pid) {
+ // 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)) {
if (count < arraySize) {
// Only store if it fits
pids[count] = (jlong) childpid;
@@ -422,6 +473,10 @@
// Store the parentPid
ppids[count] = (jlong) ppid;
}
+ if (stimes != NULL) {
+ // Store the process start time
+ stimes[count] = startTime;
+ }
}
count++; // Count to tabulate size needed
}
@@ -434,56 +489,15 @@
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;
}
-/*
- * Returns the parent pid of a given pid, or -1 if not found
- */
-static pid_t parentPid(JNIEnv *env, pid_t pid) {
- char state;
- FILE* fp;
- char stat[2048];
- int statlen;
- char fn[32];
- int i, p;
- char* s;
-
- /*
- * try to open /proc/%d/stat
- */
- snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
- fp = fopen(fn, "r");
- if (fp == NULL) {
- return -1;
- }
-
- /*
- * 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(stat, 1, (sizeof stat - 1), fp);
- fclose(fp);
- if (statlen < 0) {
- return -1;
- }
-
- stat[statlen] = '\0';
- s = strrchr(stat, ')');
- if (s == NULL) {
- return -1;
- }
- do s++; while (isspace(*s));
- i = sscanf(s, "%c %d", &state, &p);
- if (i != 2) {
- return (pid_t)-1;
- }
- return (pid_t) p;
-}
/**************************************************************
* Implementation of ProcessHandleImpl_Info native methods.
@@ -496,49 +510,51 @@
* Method: info0
* Signature: (JLjava/lang/ProcessHandle/Info;)I
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
- (JNIEnv *env, jobject jinfo, jlong jpid) {
+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_t)pid);
- getCmdlineInfo(env, pid, jinfo);
+ 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);
+ }
}
/**
- * 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.
+ * Read /proc/<pid>/stat and return the ppid, total cputime and start time.
+ * -1 is fail; zero is unknown; > 0 is parent pid
*/
-static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
- char state;
+static pid_t getStatInfo(JNIEnv *env, pid_t pid,
+ jlong *totalTime, jlong* startTime) {
FILE* fp;
char buffer[2048];
- struct stat stat_buf;
int statlen;
char fn[32];
- int i, ppid = -2;
char* s;
- char *cmd;
- jstring name = NULL;
- unsigned long userTime = 0; // clock tics
- unsigned long totalTime = 0; // clock tics
- jlong total = 0; // nano seconds
- unsigned long long startTime = 0; // microseconds
+ 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);
-
- if (stat(fn, &stat_buf) < 0) {
- return;
- }
-
- CHECK_NULL((name = uidToUser(env, stat_buf.st_uid)));
- (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
- JNU_CHECK_EXCEPTION(env);
+ snprintf(fn, sizeof fn, STAT_FILE, pid);
fp = fopen(fn, "r");
if (fp == NULL) {
- return;
+ return -1; // fail, no such /proc/pid/stat
}
/*
@@ -549,38 +565,34 @@
statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
fclose(fp);
if (statlen < 0) {
- return;
+ return 0; // parent pid is not available
}
buffer[statlen] = '\0';
s = strchr(buffer, '(');
if (s == NULL) {
- return;
+ return 0; // parent pid is not available
}
// Found start of command, skip to end
s++;
s = strrchr(s, ')');
if (s == NULL) {
- return;
+ return 0; // parent pid is not available
}
s++;
// Scan the needed fields from status, retaining only ppid(4),
// utime (14), stime(15), starttime(22)
- i = sscanf(s, " %c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
- &state, &ppid, &userTime, &totalTime, &startTime);
- if (i != 5) {
- return; // not all values parsed; return error
+ 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
}
- total = (userTime + totalTime) * (jlong)(1000000000 / clock_ticks_per_second);
-
- startTime = bootTime_ms + ((startTime * 1000) / clock_ticks_per_second);
+ *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
- (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, total);
- JNU_CHECK_EXCEPTION(env);
- (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
- JNU_CHECK_EXCEPTION(env);
+ *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
+
+ return parentPid;
}
/**
@@ -632,6 +644,7 @@
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
@@ -679,6 +692,14 @@
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) {
--- a/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java Tue Jul 14 15:35:37 2015 -0400
@@ -34,7 +34,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.lang.Override;
import java.lang.ProcessBuilder.Redirect;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -391,7 +390,7 @@
handle = create(cmdstr, envblock, path,
stdHandles, redirectErrorStream);
- processHandle = ProcessHandleImpl.getUnchecked(getProcessId0(handle));
+ processHandle = ProcessHandleImpl.getInternal(getProcessId0(handle));
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
--- a/jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c Tue Jul 14 15:29:16 2015 -0400
+++ b/jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c Tue Jul 14 15:35:37 2015 -0400
@@ -64,8 +64,8 @@
* Method: initIDs
* Signature: ()V
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
- (JNIEnv *env, jclass clazz) {
+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;"));
@@ -78,15 +78,25 @@
CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
clazz, "user", "Ljava/lang/String;"));
}
+/**************************************************************
+ * Static method to initialize native.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: initNative
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
+}
/*
* Block until a child process exits and return its exit code.
*/
JNIEXPORT jint JNICALL
Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
- jclass junk,
- jlong jpid,
- jboolean reapStatus) {
+ jclass junk,
+ jlong jpid,
+ jboolean reapStatus) {
DWORD pid = (DWORD)jpid;
DWORD exitValue = -1;
HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION,
@@ -97,7 +107,7 @@
do {
if (!GetExitCodeProcess(handle, &exitValue)) {
JNU_ThrowByNameWithLastError(env,
- "java/lang/Runtime", "GetExitCodeProcess");
+ "java/lang/RuntimeException", "GetExitCodeProcess");
break;
}
if (exitValue == STILL_ACTIVE) {
@@ -110,7 +120,7 @@
INFINITE) /* Wait forever */
== WAIT_FAILED) {
JNU_ThrowByNameWithLastError(env,
- "java/lang/Runtime", "WaitForMultipleObjects");
+ "java/lang/RuntimeException", "WaitForMultipleObjects");
break;
}
}
@@ -126,8 +136,8 @@
* Method: getCurrentPid0
* Signature: ()J
*/
-JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0
-(JNIEnv *env, jclass clazz) {
+JNIEXPORT jlong JNICALL
+Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) {
DWORD pid = GetCurrentProcessId();
return (jlong)pid;
}
@@ -139,13 +149,21 @@
* Method: parent0
* Signature: (J)J
*/
-JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
-(JNIEnv *env, jclass clazz, jlong jpid) {
-
- DWORD ppid = -1;
+JNIEXPORT jlong JNICALL
+Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
+ jclass clazz,
+ jlong jpid,
+ jlong startTime) {
+ DWORD ppid = 0;
DWORD wpid = (DWORD)jpid;
PROCESSENTRY32 pe32;
HANDLE hProcessSnap;
+ jlong start;
+
+ start = Java_java_lang_ProcessHandleImpl_isAlive0(env, clazz, jpid);
+ if (start != startTime && start != 0 && startTime != 0) {
+ return -1;
+ }
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
@@ -181,18 +199,23 @@
* Method: getChildPids
* Signature: (J[J[J)I
*/
-JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
-(JNIEnv *env, jclass clazz, jlong jpid,
- jlongArray jarray, jlongArray jparentArray) {
-
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
+ jclass clazz,
+ jlong jpid,
+ jlongArray jarray,
+ jlongArray jparentArray,
+ jlongArray jstimesArray) {
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
DWORD ppid = (DWORD)jpid;
- size_t count = 0;
jlong* pids = NULL;
jlong* ppids = NULL;
- size_t parentArraySize = 0;
- size_t arraySize = 0;
+ 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);
@@ -205,6 +228,15 @@
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;
+ }
+ }
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
@@ -228,6 +260,12 @@
break;
}
}
+ if (jstimesArray != NULL) {
+ stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
+ if (stimes == NULL) {
+ break;
+ }
+ }
// Now walk the snapshot of processes, and
// save information about each process in turn
do {
@@ -236,11 +274,17 @@
&& (pe32.th32ParentProcessID == ppid))) {
if (count < arraySize) {
// Only store if it fits
- pids[count] = (jlong)pe32.th32ProcessID;
+ pids[count] = (jlong) pe32.th32ProcessID;
if (ppids != NULL) {
// Store the parentPid
ppids[count] = (jlong) pe32.th32ParentProcessID;
}
+ if (stimes != NULL) {
+ // Store the process start time
+ stimes[count] =
+ Java_java_lang_ProcessHandleImpl_isAlive0(env,
+ clazz, (jlong) pe32.th32ProcessID);
+ }
}
count++; // Count to tabulate size needed
}
@@ -253,6 +297,9 @@
if (ppids != NULL) {
(*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
}
+ if (stimes != NULL) {
+ (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
+ }
} else {
JNU_ThrowByName(env,
"java/lang/RuntimeException", "snapshot not available");
@@ -263,48 +310,6 @@
return (jint)count;
}
-/*
- * Destroy the process.
- *
- * Class: java_lang_ProcessHandleImpl
- * Method: destroy0
- * Signature: (Z)V
- */
-JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0
-(JNIEnv *env, jclass clazz, jlong jpid, jboolean force) {
- DWORD pid = (DWORD)jpid;
- HANDLE handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
- if (handle != NULL) {
- TerminateProcess(handle, 1);
- CloseHandle(handle); // Ignore return code
- return JNI_TRUE;
- }
- return JNI_FALSE;
-}
-
-/*
- * Class: java_lang_ProcessHandleImpl
- * Method: isAlive0
- * Signature: (J)Z
- */
-JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0
-(JNIEnv *env, jclass clazz, jlong jpid) {
- DWORD pid = (DWORD)jpid;
-
- jboolean ret = JNI_FALSE;
- HANDLE handle =
- OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
- FALSE, pid);
- if (handle != NULL) {
- DWORD dwExitStatus;
-
- GetExitCodeProcess(handle, &dwExitStatus);
- CloseHandle(handle); // Ignore return code
- ret = (dwExitStatus == STILL_ACTIVE);
- }
- return ret;
-}
-
/**
* Assemble a 64 bit value from two 32 bit values.
*/
@@ -315,14 +320,90 @@
}
/*
+ * Get the start time in ms from 1970 from the handle.
+ */
+static jlong getStartTime(HANDLE handle) {
+ FILETIME CreationTime, ExitTime, KernelTime, UserTime;
+ if (GetProcessTimes(handle, &CreationTime, &ExitTime, &KernelTime, &UserTime)) {
+ jlong start = jlong_from(CreationTime.dwHighDateTime,
+ CreationTime.dwLowDateTime) / 10000;
+ start -= 11644473600000L; // Rebase Epoch from 1601 to 1970
+ return start;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Destroy the process.
+ *
+ * Class: java_lang_ProcessHandleImpl
+ * Method: destroy0
+ * Signature: (Z)V
+ */
+JNIEXPORT jboolean JNICALL
+Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
+ jclass clazz,
+ jlong jpid,
+ jlong startTime,
+ jboolean force) {
+ DWORD pid = (DWORD)jpid;
+ jboolean ret = JNI_FALSE;
+ HANDLE handle = OpenProcess(PROCESS_TERMINATE | THREAD_QUERY_INFORMATION
+ | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
+ if (handle != NULL) {
+ jlong start = getStartTime(handle);
+ if (start == startTime || startTime == 0) {
+ ret = TerminateProcess(handle, 1) ? JNI_TRUE : JNI_FALSE;
+ }
+ CloseHandle(handle); // Ignore return code
+ }
+ return ret;
+}
+
+ /*
+ * 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, jclass clazz, jlong jpid) {
+ DWORD pid = (DWORD)jpid;
+
+ jlong ret = -1;
+ HANDLE handle =
+ OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
+ FALSE, pid);
+ if (handle != NULL) {
+ DWORD dwExitStatus;
+
+ GetExitCodeProcess(handle, &dwExitStatus);
+ if (dwExitStatus == STILL_ACTIVE) {
+ ret = getStartTime(handle);
+ } else {
+ ret = -1;
+ }
+ CloseHandle(handle); // Ignore return code
+ }
+ return ret;
+}
+
+/*
* Fill in the Info object from the OS information about the process.
*
* Class: java_lang_ProcessHandleImpl
* Method: info0
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
- (JNIEnv *env, jobject jinfo, jlong jpid) {
+JNIEXPORT void JNICALL
+Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
+ jobject jinfo,
+ jlong jpid) {
DWORD pid = (DWORD)jpid;
int ret = 0;
HANDLE handle =