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;  >=  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     if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
 197         *startTime = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000 +
 198                               kp.kp_proc.p_starttime.tv_usec / 1000);
 199         ppid = kp.kp_eproc.e_ppid;
 200     }
 201 
 202     // Get cputime if for current process
 203     if (pid == getpid()) {
 204         struct rusage usage;
 205         if (getrusage(RUSAGE_SELF, &usage) == 0) {
 206           jlong microsecs =
 207               usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
 208               usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
 209           *totalTime = microsecs * 1000;
 210         }
 211     }
 212 
 213     return ppid;
 214 
 215 }
 216 
 217 /**
 218  * Return the uid of a process or -1 on error
 219  */
 220 static uid_t getUID(pid_t pid) {
 221     struct kinfo_proc kp;
 222     size_t bufSize = sizeof kp;
 223 
 224     // Read the process info for the specific pid
 225     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
 226 
 227     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) {
 228         if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
 229             return kp.kp_eproc.e_ucred.cr_uid;
 230         }
 231     }
 232     return (uid_t)-1;
 233 }
 234 
 235 /**
 236  * Retrieve the command and arguments for the process and store them
 237  * into the Info object.
 238  */
 239 void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
 240     int mib[3], maxargs, nargs, i;
 241     size_t size;
 242     char *args, *cp, *sp, *np;
 243 
 244     // Get the UID first. This is done here because it is cheap to do it here
 245     // on other platforms like Linux/Solaris/AIX where the uid comes from the
 246     // same source like the command line info.
 247     unix_getUserInfo(env, jinfo, getUID(pid));
 248 
 249     // Get the maximum size of the arguments
 250     mib[0] = CTL_KERN;
 251     mib[1] = KERN_ARGMAX;
 252     size = sizeof(maxargs);
 253     if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
 254             JNU_ThrowByNameWithLastError(env,
 255                 "java/lang/RuntimeException", "sysctl failed");
 256         return;
 257     }
 258 
 259     // Allocate an args buffer and get the arguments
 260     args = (char *)malloc(maxargs);
 261     if (args == NULL) {
 262         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 263         return;
 264     }
 265 
 266     do {            // a block to break out of on error
 267         char *argsEnd;
 268         jstring cmdexe = NULL;
 269 
 270         mib[0] = CTL_KERN;
 271         mib[1] = KERN_PROCARGS2;
 272         mib[2] = pid;
 273         size = (size_t) maxargs;
 274         if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
 275             if (errno != EINVAL) {
 276                 JNU_ThrowByNameWithLastError(env,
 277                     "java/lang/RuntimeException", "sysctl failed");
 278             }
 279             break;
 280         }
 281         memcpy(&nargs, args, sizeof(nargs));
 282 
 283         cp = &args[sizeof(nargs)];      // Strings start after nargs
 284         argsEnd = &args[size];
 285 
 286         // Store the command executable path
 287         if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {
 288             break;
 289         }
 290 
 291         // Skip trailing nulls after the executable path
 292         for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
 293             if (*cp != '\0') {
 294                 break;
 295             }
 296         }
 297 
 298         unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe, NULL);
 299     } while (0);
 300     // Free the arg buffer
 301     free(args);
 302 }
 303