< prev index next >

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

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

*** 26,60 **** #include "jni.h" #include "jni_util.h" #include "java_lang_ProcessHandleImpl.h" #include "java_lang_ProcessHandleImpl_Info.h" - #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <pwd.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> ! #include <string.h> ! #include <dirent.h> ! #include <ctype.h> /** ! * Implementations of ProcessHandleImpl functions that are common to all ! * Unix variants: ! * - waitForProcessExit0(pid, reap) ! * - getCurrentPid0() ! * - destroy0(pid, force) */ #ifndef WIFEXITED #define WIFEXITED(status) (((status)&0xFF) == 0) #endif #ifndef WEXITSTATUS --- 26,116 ---- #include "jni.h" #include "jni_util.h" #include "java_lang_ProcessHandleImpl.h" #include "java_lang_ProcessHandleImpl_Info.h" + #include "ProcessHandleImpl_unix.h" + #include <stdio.h> #include <errno.h> #include <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> ! #ifdef _AIX ! #include <sys/procfs.h> ! #endif ! #ifdef __solaris__ ! #include <procfs.h> ! #endif /** ! * 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 #ifndef WEXITSTATUS
*** 67,76 **** --- 123,145 ---- #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)
*** 79,103 **** do { \ _result = _cmd; \ } 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) ! { pid_t pid = (pid_t)jpid; errno = 0; if (reapStatus != JNI_FALSE) { /* Wait for the child process to exit. --- 148,223 ---- do { \ _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;")); ! } ! ! /************************************************************** ! * 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); ! 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) { pid_t pid = (pid_t)jpid; errno = 0; if (reapStatus != JNI_FALSE) { /* Wait for the child process to exit.
*** 115,136 **** } 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 } else { return status; } } else { /* --- 235,245 ---- } if (WIFEXITED(status)) { return WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { ! return WTERMSIG_RETURN(status); } else { return status; } } else { /*
*** 154,175 **** /* * 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 } else { /* * Unknown exit code; pass it through. */ return siginfo.si_status; --- 263,273 ---- /* * The child exited normally; get its exit code. */ return siginfo.si_status; } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) { ! return WTERMSIG_RETURN(siginfo.si_status); } else { /* * Unknown exit code; pass it through. */ return siginfo.si_status;
*** 189,199 **** } /* * Class: java_lang_ProcessHandleImpl * Method: destroy0 ! * Signature: (Z)Z */ JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env, jobject obj, jlong jpid, --- 287,297 ---- } /* * Class: java_lang_ProcessHandleImpl * Method: destroy0 ! * Signature: (JJZ)Z */ JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env, jobject obj, jlong jpid,
*** 208,330 **** } else { return JNI_FALSE; } } ! /** ! * 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) { ! 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; } - /** - * 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. * * 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); ! bootTime_ms = getBoottime(env); } /* * Check if a process is alive. * Return the start time (ms since 1970) if it is available. --- 306,361 ---- } else { return JNI_FALSE; } } ! /* ! * 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 */ ! 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); } /* ! * 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; ! pid_t ppid; ! jlong totalTime = -1L; ! jlong startTime = -1L; ! ppid = os_getParentPidAndTimings(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); ! } ! os_getCmdlineAndUserInfo(env, jinfo, pid); } /* * Check if a process is alive. * Return the start time (ms since 1970) if it is available.
*** 338,358 **** 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, --- 369,389 ---- 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 = os_getParentPidAndTimings(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: (JJ)J */ JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, jobject obj, jlong jpid,
*** 362,399 **** pid_t mypid = getpid(); if (pid == mypid) { ppid = getppid(); } else { ! 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, ! jlongArray jstimesArray) { ! DIR* dir; struct dirent* ptr; pid_t pid = (pid_t) jpid; jlong* pids = NULL; jlong* ppids = NULL; --- 393,507 ---- pid_t mypid = getpid(); if (pid == mypid) { ppid = getppid(); } else { ! jlong start = 0L; jlong total = 0L; // unused ! ppid = os_getParentPidAndTimings(env, pid, &total, &start); if (start != startTime && start != 0 && startTime != 0) { ppid = -1; } } return (jlong) ppid; } + /** + * 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. + */ + void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, + char *cp, char *argsEnd, jstring cmdexe) { + jobject argsArray; + int i; + + if (cmdexe == NULL && nargs > 0) { + // Create a string from arg[0] + CHECK_NULL((cmdexe = JNU_NewStringPlatform(env, cp))); + } + (*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); + } + } + + /* Size of password or group entry when not available via sysconf */ + #define ENT_BUF_SIZE 1024 + + void unix_getUserInfo(JNIEnv* env, jobject jinfo, 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 + + // 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); + } + } + + /* + * The following fuctions 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. */ ! jint unix_getChildren(JNIEnv *env, 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;
*** 460,471 **** /* 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); if (ppid > 0 && (pid == 0 || ppid == pid)) { if (count < arraySize) { // Only store if it fits pids[count] = (jlong) childpid; --- 568,580 ---- /* skip files that aren't numbers */ pid_t childpid = (pid_t) atoi(ptr->d_name); if ((int) childpid <= 0) { continue; } ! ! // 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;
*** 496,790 **** 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: (JLjava/lang/ProcessHandle/Info;)I */ - 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); ! } ! } /** ! * Read /proc/<pid>/stat and return the ppid, total cputime and start time. ! * -1 is fail; zero is unknown; > 0 is parent pid */ ! static pid_t getStatInfo(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, STAT_FILE, 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; ! } ! } ! free(line); ! if (fp != 0) { ! fclose(fp); } - - 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; } } ! --- 605,730 ---- closedir(dir); // If more pids than array had size for; count will be greater than array size return count; } ! #endif // defined(__solaris__) || defined (__linux__) || defined(_AIX) /* ! * The following fuctions are common on Solaris and AIX. */ ! #if defined(__solaris__) || defined(_AIX) /** ! * Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo". ! * Returns 0 on success and -1 on error. */ ! static int getPsinfo(pid_t pid, psinfo_t *psinfo) { FILE* fp; char fn[32]; ! int ret; /* ! * Try to open /proc/%d/psinfo */ ! snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid); fp = fopen(fn, "r"); if (fp == NULL) { ! return -1; } ! ret = fread(psinfo, 1, sizeof(psinfo_t), fp); fclose(fp); ! if (ret < sizeof(psinfo_t)) { ! return -1; } ! return 0; } /** ! * Read /proc/<pid>/psinfo and return the ppid, total cputime and start time. ! * Return: -1 is fail; zero is unknown; > 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; ! if (getPsinfo(pid, &psinfo) < 0) { ! 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; ! 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]; ! jstring str = NULL, 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 /* ! * Now try to open /proc/%d/psinfo */ ! if (getPsinfo(pid, &psinfo) < 0) { ! unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe); ! 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 :) ! */ ! { ! 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 separated 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++; } } } ! unix_fillArgArray(env, jinfo, nargs, prargs, argsEnd, cmdexe); } } ! #endif // defined(__solaris__) || defined(_AIX)
< prev index next >