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 32 #include <stdio.h> 33 #include <ctype.h> 34 #include <dirent.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <procfs.h> 38 #include <signal.h> 39 #include <stdlib.h> 40 #include <sys/stat.h> 41 #include <unistd.h> 42 #include <limits.h> 43 44 /** 45 * Implementations of ProcessHandleImpl functions that are 46 * NOT common to all Unix variants: 47 * - getProcessPids0(pid, pidArray) 48 * 49 * Implementations of ProcessHandleImpl_Info 50 * - totalTime, startTime 51 * - Command 52 * - Arguments 53 */ 54 55 /* 56 * Signatures for internal OS specific functions. 57 */ 58 static pid_t parentPid(JNIEnv *env, pid_t pid); 59 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid); 60 static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid); 61 62 extern jstring uidToUser(JNIEnv* env, uid_t uid); 63 64 /* Field id for jString 'command' in java.lang.ProcessHandle.Info */ 65 static jfieldID ProcessHandleImpl_Info_commandID; 66 67 /* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */ 68 static jfieldID ProcessHandleImpl_Info_argumentsID; 69 70 /* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */ 71 static jfieldID ProcessHandleImpl_Info_totalTimeID; 72 73 /* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */ 74 static jfieldID ProcessHandleImpl_Info_startTimeID; 75 76 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ 77 static jfieldID ProcessHandleImpl_Info_userID; 78 79 /* static value for clock ticks per second. */ 80 static long clock_ticks_per_second; 81 82 /************************************************************** 83 * Static method to initialize field IDs and the ticks per second rate. 84 * 85 * Class: java_lang_ProcessHandleImpl_Info 86 * Method: initIDs 87 * Signature: ()V 88 */ 89 JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs 90 (JNIEnv *env, jclass clazz) { 91 92 CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env, 93 clazz, "command", "Ljava/lang/String;")); 94 CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env, 95 clazz, "arguments", "[Ljava/lang/String;")); 96 CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env, 97 clazz, "totalTime", "J")); 98 CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env, 99 clazz, "startTime", "J")); 100 CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env, 101 clazz, "user", "Ljava/lang/String;")); 102 clock_ticks_per_second = sysconf(_SC_CLK_TCK); 103 } 104 105 /* 106 * Returns the parent pid of the requested pid. 107 * 108 * Class: java_lang_ProcessHandleImpl 109 * Method: parent0 110 * Signature: (J)J 111 */ 112 JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0 113 (JNIEnv *env, jobject obj, jlong jpid) { 114 pid_t pid = (pid_t) jpid; 115 pid_t ppid = -1; 116 117 if (pid == getpid()) { 118 ppid = getppid(); 119 } else { 120 ppid = parentPid(env, pid); 121 } 122 return (jlong) ppid; 123 } 124 125 /* 126 * Returns the children of the requested pid and optionally each parent. 127 * 128 * Class: java_lang_ProcessHandleImpl 129 * Method: getChildPids 130 * Signature: (J[J)I 131 * 132 * Reads /proc and accumulates any process who parent pid matches. 133 * The resulting pids are stored into the array of longs. 134 * The number of pids is returned if they all fit. 135 * If the array is too short, the desired length is returned. 136 */ 137 JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0 138 (JNIEnv *env, jclass clazz, jlong jpid, 139 jlongArray jarray, jlongArray jparentArray) 140 { 141 DIR* dir; 142 struct dirent* ptr; 143 pid_t pid = (pid_t) jpid; 144 size_t count = 0; 145 jlong* pids = NULL; 146 jlong* ppids = NULL; 147 size_t parentArraySize = 0; 148 size_t arraySize = 0; 149 150 arraySize = (*env)->GetArrayLength(env, jarray); 151 JNU_CHECK_EXCEPTION_RETURN(env, 0); 152 if (jparentArray != NULL) { 153 parentArraySize = (*env)->GetArrayLength(env, jparentArray); 154 JNU_CHECK_EXCEPTION_RETURN(env, 0); 155 156 if (arraySize != parentArraySize) { 157 JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); 158 return 0; 159 } 160 } 161 162 /* 163 * To locate the children we scan /proc looking for files that have a 164 * positive integer as a filename. 165 */ 166 if ((dir = opendir("/proc")) == NULL) { 167 JNU_ThrowByNameWithLastError(env, 168 "java/lang/Runtime", "Unable to open /proc"); 169 return 0; 170 } 171 172 do { // Block to break out of on Exception 173 pids = (*env)->GetLongArrayElements(env, jarray, NULL); 174 if (pids == NULL) { 175 break; 176 } 177 if (jparentArray != NULL) { 178 ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); 179 if (ppids == NULL) { 180 break; 181 } 182 } 183 184 while ((ptr = readdir(dir)) != NULL) { 185 pid_t ppid; 186 187 /* skip files that aren't numbers */ 188 pid_t childpid = (pid_t) atoi(ptr->d_name); 189 if ((int) childpid <= 0) { 190 continue; 191 } 192 193 ppid = 0; 194 if (pid != 0 || jparentArray != NULL) { 195 // parentPid opens and reads /proc/pid/stat 196 ppid = parentPid(env, childpid); 197 } 198 if (pid == 0 || ppid == pid) { 199 if (count < arraySize) { 200 // Only store if it fits 201 pids[count] = (jlong) childpid; 202 203 if (ppids != NULL) { 204 // Store the parentPid 205 ppids[count] = (jlong) ppid; 206 } 207 } 208 count++; // Count to tabulate size needed 209 } 210 } 211 } while (0); 212 213 if (pids != NULL) { 214 (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); 215 } 216 if (ppids != NULL) { 217 (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); 218 } 219 220 closedir(dir); 221 // If more pids than array had size for; count will be greater than array size 222 return count; 223 } 224 225 /* 226 * Returns the parent pid of a given pid, or -1 if not found 227 */ 228 static pid_t parentPid(JNIEnv *env, pid_t pid) { 229 FILE* fp; 230 pstatus_t pstatus; 231 int statlen; 232 char fn[32]; 233 int i, p; 234 char* s; 235 236 /* 237 * Try to open /proc/%d/status 238 */ 239 snprintf(fn, sizeof fn, "/proc/%d/status", pid); 240 fp = fopen(fn, "r"); 241 if (fp == NULL) { 242 return -1; 243 } 244 245 /* 246 * The format is: pid (command) state ppid ... 247 * As the command could be anything we must find the right most 248 * ")" and then skip the white spaces that follow it. 249 */ 250 statlen = fread(&pstatus, 1, (sizeof pstatus), fp); 251 fclose(fp); 252 if (statlen < 0) { 253 return -1; 254 } 255 return (pid_t) pstatus.pr_ppid; 256 } 257 258 /************************************************************** 259 * Implementation of ProcessHandleImpl_Info native methods. 260 */ 261 262 /* 263 * Fill in the Info object from the OS information about the process. 264 * 265 * Class: java_lang_ProcessHandleImpl_Info 266 * Method: info0 267 * Signature: (J)V 268 */ 269 JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0 270 (JNIEnv *env, jobject jinfo, jlong jpid) { 271 pid_t pid = (pid_t) jpid; 272 getStatInfo(env, jinfo, pid); 273 getCmdlineInfo(env, jinfo, pid); 274 } 275 276 /** 277 * Read /proc/<pid>/stat and fill in the fields of the Info object. 278 * Gather the user and system times. 279 */ 280 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) { 281 FILE* fp; 282 pstatus_t pstatus; 283 struct stat stat_buf; 284 int ret; 285 char fn[32]; 286 int i, p; 287 char* s; 288 jlong totalTime; 289 290 /* 291 * Try to open /proc/%d/status 292 */ 293 snprintf(fn, sizeof fn, "/proc/%d/status", pid); 294 295 if (stat(fn, &stat_buf) < 0) { 296 return; 297 } 298 299 fp = fopen(fn, "r"); 300 if (fp == NULL) { 301 return; 302 } 303 304 ret = fread(&pstatus, 1, (sizeof pstatus), fp); 305 fclose(fp); 306 if (ret < 0) { 307 return; 308 } 309 310 totalTime = pstatus.pr_utime.tv_sec * 1000000000L + pstatus.pr_utime.tv_nsec + 311 pstatus.pr_stime.tv_sec * 1000000000L + pstatus.pr_stime.tv_nsec; 312 313 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); 314 JNU_CHECK_EXCEPTION(env); 315 } 316 317 static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) { 318 FILE* fp; 319 psinfo_t psinfo; 320 int ret; 321 char fn[32]; 322 char exePath[PATH_MAX]; 323 int i, p; 324 jlong startTime; 325 jobjectArray cmdArray; 326 jstring str = NULL; 327 328 /* 329 * try to open /proc/%d/psinfo 330 */ 331 snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid); 332 fp = fopen(fn, "r"); 333 if (fp == NULL) { 334 return; 335 } 336 337 /* 338 * The format is: pid (command) state ppid ... 339 * As the command could be anything we must find the right most 340 * ")" and then skip the white spaces that follow it. 341 */ 342 ret = fread(&psinfo, 1, (sizeof psinfo), fp); 343 fclose(fp); 344 if (ret < 0) { 345 return; 346 } 347 348 CHECK_NULL((str = uidToUser(env, psinfo.pr_uid))); 349 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, str); 350 JNU_CHECK_EXCEPTION(env); 351 352 startTime = (jlong)psinfo.pr_start.tv_sec * (jlong)1000 + 353 (jlong)psinfo.pr_start.tv_nsec / 1000000; 354 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); 355 JNU_CHECK_EXCEPTION(env); 356 357 /* 358 * The path to the executable command is the link in /proc/<pid>/paths/a.out. 359 */ 360 snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid); 361 if ((ret = readlink(fn, exePath, PATH_MAX - 1)) < 0) { 362 return; 363 } 364 365 // null terminate and create String to store for command 366 exePath[ret] = '\0'; 367 CHECK_NULL(str = JNU_NewStringPlatform(env, exePath)); 368 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str); 369 JNU_CHECK_EXCEPTION(env); 370 } 371