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