1 /*
   2  * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "jni.h"
  27 #include "jni_util.h"
  28 #include "java_lang_ProcessHandleImpl.h"
  29 #include "java_lang_ProcessHandleImpl_Info.h"
  30 
  31 #include "ProcessHandleImpl_unix.h"
  32 
  33 #include <stdio.h>
  34 #include <errno.h>
  35 #include <signal.h>
  36 #include <stdlib.h>
  37 #include <unistd.h>
  38 #include <string.h>
  39 
  40 #include <sys/sysctl.h>
  41 
  42 /**
  43  * Implementation of native ProcessHandleImpl functions for MAC OS X.
  44  * See ProcessHandleImpl_unix.c for more details.
  45  */
  46 
  47 void os_initNative(JNIEnv *env, jclass clazz) {}
  48 
  49 /*
  50  * Returns the children of the requested pid and optionally each parent.
  51  *
  52  * Use sysctl to accumulate any process whose parent pid is zero or matches.
  53  * The resulting pids are stored into the array of longs.
  54  * The number of pids is returned if they all fit.
  55  * If the parentArray is non-null, store the parent pid.
  56  * If the array is too short, excess pids are not stored and
  57  * the desired length is returned.
  58  */
  59 jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
  60                     jlongArray jparentArray, jlongArray jstimesArray) {
  61     jlong* pids = NULL;
  62     jlong* ppids = NULL;
  63     jlong* stimes = NULL;
  64     jsize parentArraySize = 0;
  65     jsize arraySize = 0;
  66     jsize stimesSize = 0;
  67     jsize count = 0;
  68     size_t bufSize = 0;
  69     pid_t pid = (pid_t) jpid;
  70 
  71     arraySize = (*env)->GetArrayLength(env, jarray);
  72     JNU_CHECK_EXCEPTION_RETURN(env, -1);
  73     if (jparentArray != NULL) {
  74         parentArraySize = (*env)->GetArrayLength(env, jparentArray);
  75         JNU_CHECK_EXCEPTION_RETURN(env, -1);
  76 
  77         if (arraySize != parentArraySize) {
  78             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
  79             return 0;
  80         }
  81     }
  82     if (jstimesArray != NULL) {
  83         stimesSize = (*env)->GetArrayLength(env, jstimesArray);
  84         JNU_CHECK_EXCEPTION_RETURN(env, -1);
  85 
  86         if (arraySize != stimesSize) {
  87             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
  88             return 0;
  89         }
  90     }
  91 
  92     // Get buffer size needed to read all processes
  93     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
  94     if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {
  95         JNU_ThrowByNameWithLastError(env,
  96             "java/lang/RuntimeException", "sysctl failed");
  97         return -1;
  98     }
  99 
 100     // Allocate buffer big enough for all processes
 101     void *buffer = malloc(bufSize);
 102     if (buffer == NULL) {
 103         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 104         return -1;
 105     }
 106 
 107     // Read process info for all processes
 108     if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {
 109         JNU_ThrowByNameWithLastError(env,
 110             "java/lang/RuntimeException", "sysctl failed");
 111         free(buffer);
 112         return -1;
 113     }
 114 
 115     do { // Block to break out of on Exception
 116         struct kinfo_proc *kp = (struct kinfo_proc *) buffer;
 117         unsigned long nentries = bufSize / sizeof (struct kinfo_proc);
 118         long i;
 119 
 120         pids = (*env)->GetLongArrayElements(env, jarray, NULL);
 121         if (pids == NULL) {
 122             break;
 123         }
 124         if (jparentArray != NULL) {
 125             ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
 126             if (ppids == NULL) {
 127                 break;
 128             }
 129         }
 130         if (jstimesArray != NULL) {
 131             stimes  = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
 132             if (stimes == NULL) {
 133                 break;
 134             }
 135         }
 136 
 137         // Process each entry in the buffer
 138         for (i = nentries; --i >= 0; ++kp) {
 139             if (pid == 0 || kp->kp_eproc.e_ppid == pid) {
 140                 if (count < arraySize) {
 141                     // Only store if it fits
 142                     pids[count] = (jlong) kp->kp_proc.p_pid;
 143                     if (ppids != NULL) {
 144                         // Store the parentPid
 145                         ppids[count] = (jlong) kp->kp_eproc.e_ppid;
 146                     }
 147                     if (stimes != NULL) {
 148                         // Store the process start time
 149                         jlong startTime = kp->kp_proc.p_starttime.tv_sec * 1000 +
 150                                           kp->kp_proc.p_starttime.tv_usec / 1000;
 151                         stimes[count] = startTime;
 152                     }
 153                 }
 154                 count++; // Count to tabulate size needed
 155             }
 156         }
 157     } while (0);
 158 
 159     if (pids != NULL) {
 160         (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
 161     }
 162     if (ppids != NULL) {
 163         (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
 164     }
 165     if (stimes != NULL) {
 166         (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
 167     }
 168 
 169     free(buffer);
 170     // If more pids than array had size for; count will be greater than array size
 171     return count;
 172 }
 173 
 174 /**
 175  * Use sysctl and return the ppid, total cputime and start time.
 176  * Return: -1 is fail;  zero is unknown; >  0 is parent pid
 177  * 'total' will contain the running time of 'pid' in nanoseconds.
 178  * 'start' will contain the start time of 'pid' in milliseconds since epoch.
 179  */
 180 pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t jpid,
 181                                 jlong *totalTime, jlong *startTime) {
 182 
 183     const pid_t pid = (pid_t) jpid;
 184     pid_t ppid = -1;
 185     struct kinfo_proc kp;
 186     size_t bufSize = sizeof kp;
 187 
 188     // Read the process info for the specific pid
 189     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
 190 
 191     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
 192         JNU_ThrowByNameWithLastError(env,
 193             "java/lang/RuntimeException", "sysctl failed");
 194         return -1;
 195     }
 196 
 197     if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
 198         *startTime = kp.kp_proc.p_starttime.tv_sec * 1000 +
 199                      kp.kp_proc.p_starttime.tv_usec / 1000;
 200         ppid = kp.kp_eproc.e_ppid;
 201     }
 202 
 203     // Get cputime if for current process
 204     if (pid == getpid()) {
 205         struct rusage usage;
 206         if (getrusage(RUSAGE_SELF, &usage) == 0) {
 207           jlong microsecs =
 208               usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
 209               usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
 210           *totalTime = microsecs * 1000;
 211         }
 212     }
 213 
 214     return ppid;
 215 
 216 }
 217 
 218 /**
 219  * Return the uid of a process or -1 on error
 220  */
 221 static uid_t getUID(pid_t pid) {
 222     struct kinfo_proc kp;
 223     size_t bufSize = sizeof kp;
 224 
 225     // Read the process info for the specific pid
 226     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
 227 
 228     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) {
 229         if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
 230             return kp.kp_eproc.e_ucred.cr_uid;
 231         }
 232     }
 233     return (uid_t)-1;
 234 }
 235 
 236 /**
 237  * Retrieve the command and arguments for the process and store them
 238  * into the Info object.
 239  */
 240 void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
 241     int mib[3], maxargs, nargs, i;
 242     size_t size;
 243     char *args, *cp, *sp, *np;
 244 
 245     // Get the UID first. This is done here because it is cheap to do it here
 246     // on other platforms like Linux/Solaris/AIX where the uid comes from the
 247     // same source like the command line info.
 248     unix_getUserInfo(env, jinfo, getUID(pid));
 249 
 250     // Get the maximum size of the arguments
 251     mib[0] = CTL_KERN;
 252     mib[1] = KERN_ARGMAX;
 253     size = sizeof(maxargs);
 254     if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
 255             JNU_ThrowByNameWithLastError(env,
 256                     "java/lang/RuntimeException", "sysctl failed");
 257         return;
 258     }
 259 
 260     // Allocate an args buffer and get the arguments
 261     args = (char *)malloc(maxargs);
 262     if (args == NULL) {
 263         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 264         return;
 265     }
 266 
 267     do {            // a block to break out of on error
 268         char *argsEnd;
 269         jstring cmdexe = NULL;
 270 
 271         mib[0] = CTL_KERN;
 272         mib[1] = KERN_PROCARGS2;
 273         mib[2] = pid;
 274         size = (size_t) maxargs;
 275         if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
 276             if (errno != EINVAL) {
 277             JNU_ThrowByNameWithLastError(env,
 278                     "java/lang/RuntimeException", "sysctl failed");
 279             }
 280             break;
 281         }
 282         memcpy(&nargs, args, sizeof(nargs));
 283 
 284         cp = &args[sizeof(nargs)];      // Strings start after nargs
 285         argsEnd = &args[size];
 286 
 287         // Store the command executable path
 288         if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {
 289             break;
 290         }
 291 
 292         // Skip trailing nulls after the executable path
 293         for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
 294             if (*cp != '\0') {
 295                 break;
 296             }
 297         }
 298 
 299         unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe);
 300     } while (0);
 301     // Free the arg buffer
 302     free(args);
 303 }
 304