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 34 #include <fcntl.h> 35 #include <limits.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 41 #include <string.h> 42 #include <ctype.h> 43 44 /* 45 * Implementation of native ProcessHandleImpl functions for Linux. 46 * See ProcessHandleImpl_unix.c for more details. 47 */ 48 49 /* Signatures for internal OS specific functions. */ 50 static long long getBoottime(JNIEnv *env); 51 52 /* A static offset in milliseconds since boot. */ 53 static long long bootTime_ms; 54 55 void os_init(JNIEnv *env, jclass clazz) { 56 bootTime_ms = getBoottime(env); 57 } 58 59 jint os_getChildren(JNIEnv *env, jlong jpid, 60 jlongArray jarray, jlongArray jparentArray) { 61 return unix_getChildren(env, jpid, jarray, jparentArray); 62 } 63 64 /* 65 * Returns the parent pid of a given pid, or -1 if not found 66 */ 67 pid_t os_parentPid(JNIEnv *env, pid_t pid) { 68 char state; 69 FILE* fp; 70 char stat[2048]; 71 int statlen; 72 char fn[32]; 73 int i, p; 74 char* s; 75 76 /* 77 * try to open /proc/%d/stat 78 */ 79 snprintf(fn, sizeof fn, "/proc/%d/stat", pid); 80 fp = fopen(fn, "r"); 81 if (fp == NULL) { 82 return -1; 83 } 84 85 /* 86 * The format is: pid (command) state ppid ... 87 * As the command could be anything we must find the right most 88 * ")" and then skip the white spaces that follow it. 89 */ 90 statlen = fread(stat, 1, (sizeof stat - 1), fp); 91 fclose(fp); 92 if (statlen < 0) { 93 return -1; 94 } 95 96 stat[statlen] = '\0'; 97 s = strrchr(stat, ')'); 98 if (s == NULL) { 99 return -1; 100 } 101 do s++; while (isspace(*s)); 102 i = sscanf(s, "%c %d", &state, &p); 103 if (i != 2) { 104 return (pid_t)-1; 105 } 106 return (pid_t) p; 107 } 108 109 /** 110 * Read /proc/<pid>/stat and fill in the fields of the Info object. 111 * The executable name, plus the user, system, and start times are gathered. 112 */ 113 void os_getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) { 114 char state; 115 FILE* fp; 116 char buffer[2048]; 117 struct stat stat_buf; 118 int statlen; 119 char fn[32]; 120 int i, ppid = -2; 121 char* s; 122 char *cmd; 123 jstring name = NULL; 124 unsigned long userTime = 0; // clock tics 125 unsigned long totalTime = 0; // clock tics 126 jlong total = 0; // nano seconds 127 unsigned long long startTime = 0; // microseconds 128 129 /* 130 * Try to stat and then open /proc/%d/stat 131 */ 132 snprintf(fn, sizeof fn, "/proc/%d/stat", pid); 133 134 if (stat(fn, &stat_buf) < 0) { 135 return; 136 } 137 138 CHECK_NULL((name = unix_uidToUser(env, stat_buf.st_uid))); 139 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); 140 JNU_CHECK_EXCEPTION(env); 141 142 fp = fopen(fn, "r"); 143 if (fp == NULL) { 144 return; 145 } 146 147 /* 148 * The format is: pid (command) state ppid ... 149 * As the command could be anything we must find the right most 150 * ")" and then skip the white spaces that follow it. 151 */ 152 statlen = fread(buffer, 1, (sizeof buffer - 1), fp); 153 fclose(fp); 154 if (statlen < 0) { 155 return; 156 } 157 158 buffer[statlen] = '\0'; 159 s = strchr(buffer, '('); 160 if (s == NULL) { 161 return; 162 } 163 // Found start of command, skip to end 164 s++; 165 s = strrchr(s, ')'); 166 if (s == NULL) { 167 return; 168 } 169 s++; 170 171 // Scan the needed fields from status, retaining only ppid(4), 172 // utime (14), stime(15), starttime(22) 173 i = sscanf(s, " %c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu", 174 &state, &ppid, &userTime, &totalTime, &startTime); 175 if (i != 5) { 176 return; // not all values parsed; return error 177 } 178 179 total = (userTime + totalTime) * (jlong)(1000000000 / clock_ticks_per_second); 180 181 startTime = bootTime_ms + ((startTime * 1000) / clock_ticks_per_second); 182 183 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, total); 184 JNU_CHECK_EXCEPTION(env); 185 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); 186 JNU_CHECK_EXCEPTION(env); 187 } 188 189 void os_getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) { 190 int fd; 191 int cmdlen = 0; 192 char *cmdline = NULL, *cmdEnd; // used for command line args and exe 193 jstring cmdexe = NULL; 194 char fn[32]; 195 196 /* 197 * Try to open /proc/%d/cmdline 198 */ 199 snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid); 200 if ((fd = open(fn, O_RDONLY)) < 0) { 201 return; 202 } 203 204 do { // Block to break out of on errors 205 int i; 206 char *s; 207 208 cmdline = (char*)malloc(PATH_MAX); 209 if (cmdline == NULL) { 210 break; 211 } 212 213 /* 214 * The path to the executable command is the link in /proc/<pid>/exe. 215 */ 216 snprintf(fn, sizeof fn, "/proc/%d/exe", pid); 217 if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) { 218 // null terminate and create String to store for command 219 cmdline[cmdlen] = '\0'; 220 cmdexe = JNU_NewStringPlatform(env, cmdline); 221 (*env)->ExceptionClear(env); // unconditionally clear any exception 222 } 223 224 /* 225 * The buffer format is the arguments nul terminated with an extra nul. 226 */ 227 cmdlen = read(fd, cmdline, PATH_MAX-1); 228 if (cmdlen < 0) { 229 break; 230 } 231 232 // Terminate the buffer and count the arguments 233 cmdline[cmdlen] = '\0'; 234 cmdEnd = &cmdline[cmdlen + 1]; 235 for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) { 236 s += strnlen(s, (cmdEnd - s)) + 1; 237 } 238 239 if (unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe) < 0) { 240 break; 241 } 242 } while (0); 243 244 if (cmdline != NULL) { 245 free(cmdline); 246 } 247 if (fd >= 0) { 248 close(fd); 249 } 250 } 251 252 /** 253 * Read the boottime from /proc/stat. 254 */ 255 static long long getBoottime(JNIEnv *env) { 256 FILE *fp; 257 char *line = NULL; 258 size_t len = 0; 259 long long bootTime = 0; 260 261 fp = fopen("/proc/stat", "r"); 262 if (fp == NULL) { 263 return -1; 264 } 265 266 while (getline(&line, &len, fp) != -1) { 267 if (sscanf(line, "btime %llu", &bootTime) == 1) { 268 break; 269 } 270 } 271 free(line); 272 273 if (fp != 0) { 274 fclose(fp); 275 } 276 277 return bootTime * 1000; 278 }