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