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