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  * Return the uid of a process or -1 on error
  51  */
  52 uid_t os_getUID(pid_t pid) {
  53     struct kinfo_proc kp;
  54     size_t bufSize = sizeof kp;
  55 
  56     // Read the process info for the specific pid
  57     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
  58 
  59     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) {
  60         if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
  61             return kp.kp_eproc.e_ucred.cr_uid;
  62         }
  63     }
  64     return (uid_t)-1;
  65 }
  66 
  67 /*
  68  * Returns the children of the requested pid and optionally each parent.
  69  *
  70  * Use sysctl to accumulate any process whose parent pid is zero or matches.
  71  * The resulting pids are stored into the array of longs.
  72  * The number of pids is returned if they all fit.
  73  * If the parentArray is non-null, store the parent pid.
  74  * If the array is too short, excess pids are not stored and
  75  * the desired length is returned.
  76  */
  77 jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
  78                     jlongArray jparentArray, jlongArray jstimesArray) {
  79     jlong* pids = NULL;
  80     jlong* ppids = NULL;
  81     jlong* stimes = NULL;
  82     jsize parentArraySize = 0;
  83     jsize arraySize = 0;
  84     jsize stimesSize = 0;
  85     jsize count = 0;
  86     size_t bufSize = 0;
  87     pid_t pid = (pid_t) jpid;
  88 
  89     arraySize = (*env)->GetArrayLength(env, jarray);
  90     JNU_CHECK_EXCEPTION_RETURN(env, -1);
  91     if (jparentArray != NULL) {
  92         parentArraySize = (*env)->GetArrayLength(env, jparentArray);
  93         JNU_CHECK_EXCEPTION_RETURN(env, -1);
  94 
  95         if (arraySize != parentArraySize) {
  96             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
  97             return 0;
  98         }
  99     }
 100     if (jstimesArray != NULL) {
 101         stimesSize = (*env)->GetArrayLength(env, jstimesArray);
 102         JNU_CHECK_EXCEPTION_RETURN(env, -1);
 103 
 104         if (arraySize != stimesSize) {
 105             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
 106             return 0;
 107         }
 108     }
 109 
 110     // Get buffer size needed to read all processes
 111     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
 112     if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {
 113         JNU_ThrowByNameWithLastError(env,
 114             "java/lang/RuntimeException", "sysctl failed");
 115         return -1;
 116     }
 117 
 118     // Allocate buffer big enough for all processes
 119     void *buffer = malloc(bufSize);
 120     if (buffer == NULL) {
 121         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 122         return -1;
 123     }
 124 
 125     // Read process info for all processes
 126     if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {
 127         JNU_ThrowByNameWithLastError(env,
 128             "java/lang/RuntimeException", "sysctl failed");
 129         free(buffer);
 130         return -1;
 131     }
 132 
 133     do { // Block to break out of on Exception
 134         struct kinfo_proc *kp = (struct kinfo_proc *) buffer;
 135         unsigned long nentries = bufSize / sizeof (struct kinfo_proc);
 136         long i;
 137 
 138         pids = (*env)->GetLongArrayElements(env, jarray, NULL);
 139         if (pids == NULL) {
 140             break;
 141         }
 142         if (jparentArray != NULL) {
 143             ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
 144             if (ppids == NULL) {
 145                 break;
 146             }
 147         }
 148         if (jstimesArray != NULL) {
 149             stimes  = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
 150             if (stimes == NULL) {
 151                 break;
 152             }
 153         }
 154 
 155         // Process each entry in the buffer
 156         for (i = nentries; --i >= 0; ++kp) {
 157             if (pid == 0 || kp->kp_eproc.e_ppid == pid) {
 158                 if (count < arraySize) {
 159                     // Only store if it fits
 160                     pids[count] = (jlong) kp->kp_proc.p_pid;
 161                     if (ppids != NULL) {
 162                         // Store the parentPid
 163                         ppids[count] = (jlong) kp->kp_eproc.e_ppid;
 164                     }
 165                     if (stimes != NULL) {
 166                         // Store the process start time
 167                         jlong startTime = kp->kp_proc.p_starttime.tv_sec * 1000 +
 168                                           kp->kp_proc.p_starttime.tv_usec / 1000;
 169                         stimes[count] = startTime;
 170                     }
 171                 }
 172                 count++; // Count to tabulate size needed
 173             }
 174         }
 175     } while (0);
 176 
 177     if (pids != NULL) {
 178         (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
 179     }
 180     if (ppids != NULL) {
 181         (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
 182     }
 183     if (stimes != NULL) {
 184         (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
 185     }
 186 
 187     free(buffer);
 188     // If more pids than array had size for; count will be greater than array size
 189     return count;
 190 }
 191 
 192 /**
 193  * Use sysctl and return the ppid, total cputime and start time.
 194  * Return: -1 is fail;  zero is unknown; >  0 is parent pid
 195  */
 196 pid_t os_getParentPid(JNIEnv *env, pid_t jpid, jlong *totalTime, jlong *startTime) {
 197 
 198     const pid_t pid = (pid_t) jpid;
 199     struct kinfo_proc kp;
 200     size_t bufSize = sizeof kp;
 201 
 202     // Read the process info for the specific pid
 203     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
 204 
 205     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
 206         JNU_ThrowByNameWithLastError(env,
 207             "java/lang/RuntimeException", "sysctl failed");
 208         return -1;
 209     }
 210 
 211     if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
 212       *startTime = kp.kp_proc.p_starttime.tv_sec * 1000 +
 213                    kp.kp_proc.p_starttime.tv_usec / 1000;
 214     }
 215 
 216     // Get cputime if for current process
 217     if (pid == getpid()) {
 218         struct rusage usage;
 219         if (getrusage(RUSAGE_SELF, &usage) == 0) {
 220           jlong microsecs =
 221               usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
 222               usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
 223           *totalTime = microsecs * 1000;
 224         }
 225     }
 226 
 227     return (bufSize > 0 && kp.kp_proc.p_pid == pid) ? kp.kp_eproc.e_ppid : -1;
 228 
 229 }
 230 
 231 /**
 232  * Retrieve the command and arguments for the process and store them
 233  * into the Info object.
 234  */
 235 void os_getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
 236     int mib[3], maxargs, nargs, i;
 237     size_t size;
 238     char *args, *cp, *sp, *np;
 239 
 240     // Get the maximum size of the arguments
 241     mib[0] = CTL_KERN;
 242     mib[1] = KERN_ARGMAX;
 243     size = sizeof(maxargs);
 244     if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
 245             JNU_ThrowByNameWithLastError(env,
 246                     "java/lang/RuntimeException", "sysctl failed");
 247         return;
 248     }
 249 
 250     // Allocate an args buffer and get the arguments
 251     args = (char *)malloc(maxargs);
 252     if (args == NULL) {
 253         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 254         return;
 255     }
 256 
 257     do {            // a block to break out of on error
 258         char *argsEnd;
 259         jstring cmdexe = NULL;
 260 
 261         mib[0] = CTL_KERN;
 262         mib[1] = KERN_PROCARGS2;
 263         mib[2] = pid;
 264         size = (size_t) maxargs;
 265         if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
 266             if (errno != EINVAL) {
 267             JNU_ThrowByNameWithLastError(env,
 268                     "java/lang/RuntimeException", "sysctl failed");
 269             }
 270             break;
 271         }
 272         memcpy(&nargs, args, sizeof(nargs));
 273 
 274         cp = &args[sizeof(nargs)];      // Strings start after nargs
 275         argsEnd = &args[size];
 276 
 277         // Store the command executable path
 278         if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {
 279             break;
 280         }
 281 
 282         // Skip trailing nulls after the executable path
 283         for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
 284             if (*cp != '\0') {
 285                 break;
 286             }
 287         }
 288 
 289         unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe);
 290     } while (0);
 291     // Free the arg buffer
 292     free(args);
 293 }
 294