< prev index next >

src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c

Print this page
rev 12414 : 8131168: Refactor ProcessHandleImpl_*.c and add implememtation for AIX

@@ -26,33 +26,88 @@
 #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 <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 pltforms 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 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_00024Info_initIDs()
+ * 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_00024Info_initIDs() 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 two helpers os_getStatInfo() and os_getCmdlineInfo(). 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_getStatInfo() and
+ * unix_getCmdlineInfo() 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)

@@ -68,10 +123,23 @@
 
 #ifndef WTERMSIG
 #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; \
   } while((_result == -1) && (errno == EINTR)); \
 } while(0)

@@ -81,19 +149,62 @@
     _result = _cmd; \
   } while((_result == NULL) && (errno == EINTR)); \
 } while(0)
 
 
+/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */
+jfieldID ProcessHandleImpl_Info_commandID;
+
+/* 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;
+
+/* static value for clock ticks per second. */
+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;"));
+    clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+
+    os_init(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)
-{
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_waitForProcessExit0
+(JNIEnv* env, jclass junk, jlong jpid, jboolean reapStatus) {
     pid_t pid = (pid_t)jpid;
     errno = 0;
 
     if (reapStatus != JNI_FALSE) {
         /* Wait for the child process to exit.

@@ -111,22 +222,11 @@
         }
 
         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;
         }
      } else {
         /*

@@ -150,22 +250,11 @@
              /*
               * The child exited normally; get its exit code.
               */
              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.
               */
              return siginfo.si_status;

@@ -196,29 +285,118 @@
 }
 
 /*
  * Class:     java_lang_ProcessHandleImpl
  * Method:    destroy0
- * Signature: (Z)Z
+ * Signature: (JZ)Z
  */
 JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0
 (JNIEnv *env, jobject obj, jlong jpid, jboolean force) {
     pid_t pid = (pid_t) jpid;
     int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
     return (kill(pid, sig) >= 0);
 
 }
 
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * 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 os_getChildren(env, jpid, jarray, jparentArray);
+}
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class:     java_lang_ProcessHandleImpl_Info
+ * Method:    info0
+ * Signature: (Ljava/lang/ProcessHandle/Info;J)I
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+(JNIEnv *env, jobject jinfo, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    os_getStatInfo(env, jinfo, pid);
+    os_getCmdlineInfo(env, jinfo, pid);
+}
+
+/*
+ * 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) {
+    pid_t pid = (pid_t) jpid;
+    pid_t ppid = -1;
+
+    pid_t mypid = getpid();
+    if (pid == mypid) {
+        ppid = getppid();
+    } else {
+        ppid = os_parentPid(env, pid);
+    }
+    return (jlong) ppid;
+}
+
 /**
- * Size of password or group entry when not available via sysconf
+ * Construct the argument array by parsing the arguments from the sequence
+ * of arguments. The zero'th arg is the command executable but it will only
+ * be used if the 'cmdexe' argument is NULL.
  */
+int unix_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 += strlen(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;
+}
+
+/* Size of password or group entry when not available via sysconf */
 #define ENT_BUF_SIZE   1024
 
 /**
  * Return a strong username for the uid_t or null.
  */
-jstring uidToUser(JNIEnv* env, uid_t uid) {
+jstring unix_uidToUser(JNIEnv* env, uid_t uid) {
     int result = 0;
     int buflen;
     char* pwbuf;
     jstring name = NULL;
 

@@ -247,117 +425,23 @@
         free(pwbuf);
     }
     return name;
 }
 
-/**
- * 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 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 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.
- *
- * 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;"));
-    clock_ticks_per_second = sysconf(_SC_CLK_TCK);
-    bootTime_ms = getBoottime(env);
-}
-
 /*
- * Returns the parent pid of the requested pid.
- *
- * Class:     java_lang_ProcessHandleImpl
- * Method:    parent0
- * Signature: (J)J
+ * The following fuctions are common on Solaris, Linux and AIX.
  */
-JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
-(JNIEnv *env, jobject obj, jlong jpid) {
-    pid_t pid = (pid_t) jpid;
-    pid_t ppid = -1;
-
-    pid_t mypid = getpid();
-    if (pid == mypid) {
-        ppid = getppid();
-    } else {
-        ppid = parentPid(env, pid);
-    }
-    return (jlong) ppid;
-}
 
-/*
- * Returns the children of the requested pid and optionally each parent.
- *
- * 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);
-}
+#if defined(__solaris__) || defined (__linux__) || defined(_AIX)
 
 /*
  * 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,
+jint unix_getChildren(JNIEnv *env, jlong jpid,
     jlongArray jarray, jlongArray jparentArray) {
     DIR* dir;
     struct dirent* ptr;
     pid_t pid = (pid_t) jpid;
     pid_t ppid = 0;

@@ -408,12 +492,12 @@
                 continue;
             }
 
             ppid = 0;
             if (pid != 0 || jparentArray != NULL) {
-                // parentPid opens and reads /proc/pid/stat
-                ppid = parentPid(env, childpid);
+                // os_parentPid opens and reads /proc/pid/stat
+                ppid = os_parentPid(env, childpid);
             }
             if (pid == 0 || ppid == pid) {
                 if (count < arraySize) {
                     // Only store if it fits
                     pids[count] = (jlong) childpid;

@@ -438,332 +522,134 @@
     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.
- */
+#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 fuctions 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;
-    getStatInfo(env, jinfo, (pid_t)pid);
-    getCmdlineInfo(env, pid, jinfo);
-}
+
+#if defined(__solaris__) || defined(_AIX)
 
 /**
  * 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.
+ * Gather the user and system times.
  */
-static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
-    char state;
+void unix_getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
     FILE* fp;
-    char buffer[2048];
+    pstatus_t pstatus;
     struct stat stat_buf;
-    int statlen;
+    int ret;
     char fn[32];
-    int i, ppid = -2;
+    int i, p;
     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
+    jlong totalTime;
 
     /*
-     * Try to stat and then open /proc/%d/stat
+     * Try to open /proc/%d/status
      */
-    snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
+    snprintf(fn, sizeof fn, "/proc/%d/status", 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);
-
     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.
-     */
-    statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
+    ret = fread(&pstatus, 1, (sizeof pstatus), fp);
     fclose(fp);
-    if (statlen < 0) {
-        return;
-    }
-
-    buffer[statlen] = '\0';
-    s = strchr(buffer, '(');
-    if (s == NULL) {
-        return;
-    }
-    // Found start of command, skip to end
-    s++;
-    s = strrchr(s, ')');
-    if (s == NULL) {
+    if (ret < 0) {
         return;
     }
-    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
-    }
 
-    total = (userTime + totalTime) * (jlong)(1000000000 / clock_ticks_per_second);
+    totalTime = pstatus.pr_utime.tv_sec * 1000000000L + pstatus.pr_utime.tv_nsec +
+                pstatus.pr_stime.tv_sec * 1000000000L + pstatus.pr_stime.tv_nsec;
 
-    startTime = bootTime_ms + ((startTime * 1000) / clock_ticks_per_second);
-
-    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, total);
-    JNU_CHECK_EXCEPTION(env);
-    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
+    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
     JNU_CHECK_EXCEPTION(env);
 }
 
-/**
- * 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;
+void unix_getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+    FILE* fp;
+    psinfo_t psinfo;
+    int ret;
     char fn[32];
+    char exePath[PATH_MAX];
+    jlong startTime;
+    jstring str = NULL, cmdexe = NULL;
 
     /*
-     * Try to open /proc/%d/cmdline
+     * try to open /proc/%d/psinfo
      */
-    snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
-    if ((fd = open(fn, O_RDONLY)) < 0) {
+    snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
+    fp = fopen(fn, "r");
+    if (fp == NULL) {
         return;
     }
 
-    do {                // Block to break out of on errors
-        int i;
-        char *s;
-
-        cmdline = (char*)malloc(PATH_MAX);
-        if (cmdline == NULL) {
-            break;
+    ret = fread(&psinfo, 1, (sizeof psinfo), fp);
+    fclose(fp);
+    if (ret < 0) {
+        return;
         }
 
+    CHECK_NULL((str = unix_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);
+
         /*
-         * The path to the executable command is the link in /proc/<pid>/exe.
+     * On Solris, 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.
+     * On AIX, the readlink() call will simply fail because the link doesn't exist.
          */
-        snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
-        if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) {
+    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
-            cmdline[cmdlen] = '\0';
-            cmdexe = JNU_NewStringPlatform(env, cmdline);
-            (*env)->ExceptionClear(env);        // unconditionally clear any exception
+        exePath[ret] = '\0';
+        CHECK_NULL(cmdexe = JNU_NewStringPlatform(env, exePath));
         }
 
         /*
-         * 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;
-        }
-    } 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;
-        }
-    }
-    free(line);
-
-    if (fp != 0) {
-        fclose(fp);
+     * 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 :)
+     */
+    {
+        char prargs[PRARGSZ + 1];
+        char *cp, *argsEnd;
+        int nargs = 1;
+        strncpy(prargs, psinfo.pr_psargs, PRARGSZ);
+        prargs[PRARGSZ] = '\0';
+        argsEnd = &prargs[strlen(prargs)];
+        if (argsEnd == prargs) {
+            /* 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';
+            argsEnd = &prargs[strlen(prargs)];
+        } else {
+            /* The arguments are separeted by a single whitespace character but we want
+             * them to be distinct, null-terminated strings. */
+            for (cp = prargs; *cp != '\0'; cp++) {
+                if (*cp == ' ') {
+                    *cp = '\0';
+                    nargs++;
     }
-
-    return bootTime * 1000;
-}
-
-#endif  //  defined(__linux__) || defined(__AIX__)
-
-
-/* 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 (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);
-#endif
-    } else {
-        /*
-         * Unknown exit code; pass it through.
-         */
-        return status;
+        unix_fillArgArray(env, jinfo, nargs, prargs, argsEnd, cmdexe);
     }
 }
 
-
+#endif // defined(__solaris__) || defined(_AIX)
< prev index next >