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