Path: blob/master/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c
41119 views
/*1* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "jni.h"26#include "jni_util.h"27#include "java_lang_ProcessHandleImpl.h"28#include "java_lang_ProcessHandleImpl_Info.h"2930#include "ProcessHandleImpl_unix.h"3132#include <stdio.h>33#include <errno.h>34#include <signal.h>35#include <stdlib.h>36#include <unistd.h>37#include <string.h>3839#include <sys/sysctl.h>4041/**42* Implementation of native ProcessHandleImpl functions for MAC OS X.43* See ProcessHandleImpl_unix.c for more details.44*/4546void os_initNative(JNIEnv *env, jclass clazz) {}4748/*49* Returns the children of the requested pid and optionally each parent.50*51* Use sysctl to accumulate any process whose parent pid is zero or matches.52* The resulting pids are stored into the array of longs.53* The number of pids is returned if they all fit.54* If the parentArray is non-null, store the parent pid.55* If the array is too short, excess pids are not stored and56* the desired length is returned.57*/58jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,59jlongArray jparentArray, jlongArray jstimesArray) {60jlong* pids = NULL;61jlong* ppids = NULL;62jlong* stimes = NULL;63jsize parentArraySize = 0;64jsize arraySize = 0;65jsize stimesSize = 0;66jsize count = 0;67size_t bufSize = 0;68pid_t pid = (pid_t) jpid;6970arraySize = (*env)->GetArrayLength(env, jarray);71JNU_CHECK_EXCEPTION_RETURN(env, -1);72if (jparentArray != NULL) {73parentArraySize = (*env)->GetArrayLength(env, jparentArray);74JNU_CHECK_EXCEPTION_RETURN(env, -1);7576if (arraySize != parentArraySize) {77JNU_ThrowIllegalArgumentException(env, "array sizes not equal");78return 0;79}80}81if (jstimesArray != NULL) {82stimesSize = (*env)->GetArrayLength(env, jstimesArray);83JNU_CHECK_EXCEPTION_RETURN(env, -1);8485if (arraySize != stimesSize) {86JNU_ThrowIllegalArgumentException(env, "array sizes not equal");87return 0;88}89}9091// Get buffer size needed to read all processes92int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};93if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {94JNU_ThrowByNameWithLastError(env,95"java/lang/RuntimeException", "sysctl failed");96return -1;97}9899// Allocate buffer big enough for all processes100void *buffer = malloc(bufSize);101if (buffer == NULL) {102JNU_ThrowOutOfMemoryError(env, "malloc failed");103return -1;104}105106// Read process info for all processes107if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {108JNU_ThrowByNameWithLastError(env,109"java/lang/RuntimeException", "sysctl failed");110free(buffer);111return -1;112}113114do { // Block to break out of on Exception115struct kinfo_proc *kp = (struct kinfo_proc *) buffer;116unsigned long nentries = bufSize / sizeof (struct kinfo_proc);117long i;118119pids = (*env)->GetLongArrayElements(env, jarray, NULL);120if (pids == NULL) {121break;122}123if (jparentArray != NULL) {124ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);125if (ppids == NULL) {126break;127}128}129if (jstimesArray != NULL) {130stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);131if (stimes == NULL) {132break;133}134}135136// Process each entry in the buffer137for (i = nentries; --i >= 0; ++kp) {138if (pid == 0 || kp->kp_eproc.e_ppid == pid) {139if (count < arraySize) {140// Only store if it fits141pids[count] = (jlong) kp->kp_proc.p_pid;142if (ppids != NULL) {143// Store the parentPid144ppids[count] = (jlong) kp->kp_eproc.e_ppid;145}146if (stimes != NULL) {147// Store the process start time148jlong startTime = kp->kp_proc.p_starttime.tv_sec * 1000 +149kp->kp_proc.p_starttime.tv_usec / 1000;150stimes[count] = startTime;151}152}153count++; // Count to tabulate size needed154}155}156} while (0);157158if (pids != NULL) {159(*env)->ReleaseLongArrayElements(env, jarray, pids, 0);160}161if (ppids != NULL) {162(*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);163}164if (stimes != NULL) {165(*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);166}167168free(buffer);169// If more pids than array had size for; count will be greater than array size170return count;171}172173/**174* Use sysctl and return the ppid, total cputime and start time.175* Return: -1 is fail; >= 0 is parent pid176* 'total' will contain the running time of 'pid' in nanoseconds.177* 'start' will contain the start time of 'pid' in milliseconds since epoch.178*/179pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t jpid,180jlong *totalTime, jlong *startTime) {181182const pid_t pid = (pid_t) jpid;183pid_t ppid = -1;184struct kinfo_proc kp;185size_t bufSize = sizeof kp;186187// Read the process info for the specific pid188int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};189190if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {191JNU_ThrowByNameWithLastError(env,192"java/lang/RuntimeException", "sysctl failed");193return -1;194}195if (bufSize > 0 && kp.kp_proc.p_pid == pid) {196*startTime = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000 +197kp.kp_proc.p_starttime.tv_usec / 1000);198ppid = kp.kp_eproc.e_ppid;199}200201// Get cputime if for current process202if (pid == getpid()) {203struct rusage usage;204if (getrusage(RUSAGE_SELF, &usage) == 0) {205jlong microsecs =206usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +207usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;208*totalTime = microsecs * 1000;209}210}211212return ppid;213214}215216/**217* Return the uid of a process or -1 on error218*/219static uid_t getUID(pid_t pid) {220struct kinfo_proc kp;221size_t bufSize = sizeof kp;222223// Read the process info for the specific pid224int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};225226if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) {227if (bufSize > 0 && kp.kp_proc.p_pid == pid) {228return kp.kp_eproc.e_ucred.cr_uid;229}230}231return (uid_t)-1;232}233234/**235* Retrieve the command and arguments for the process and store them236* into the Info object.237*/238void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {239int mib[3], maxargs, nargs, i;240size_t size;241char *args, *cp, *sp, *np;242243// Get the UID first. This is done here because it is cheap to do it here244// on other platforms like Linux/Solaris/AIX where the uid comes from the245// same source like the command line info.246unix_getUserInfo(env, jinfo, getUID(pid));247248// Get the maximum size of the arguments249mib[0] = CTL_KERN;250mib[1] = KERN_ARGMAX;251size = sizeof(maxargs);252if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {253JNU_ThrowByNameWithLastError(env,254"java/lang/RuntimeException", "sysctl failed");255return;256}257258// Allocate an args buffer and get the arguments259args = (char *)malloc(maxargs);260if (args == NULL) {261JNU_ThrowOutOfMemoryError(env, "malloc failed");262return;263}264265do { // a block to break out of on error266char *argsEnd;267jstring cmdexe = NULL;268269mib[0] = CTL_KERN;270mib[1] = KERN_PROCARGS2;271mib[2] = pid;272size = (size_t) maxargs;273if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {274if (errno != EINVAL) {275JNU_ThrowByNameWithLastError(env,276"java/lang/RuntimeException", "sysctl failed");277}278break;279}280memcpy(&nargs, args, sizeof(nargs));281282cp = &args[sizeof(nargs)]; // Strings start after nargs283argsEnd = &args[size];284285// Store the command executable path286if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {287break;288}289290// Skip trailing nulls after the executable path291for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {292if (*cp != '\0') {293break;294}295}296297unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe, NULL);298} while (0);299// Free the arg buffer300free(args);301}302303304305