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
os_initNative(JNIEnv * env,jclass clazz)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 */
os_getChildren(JNIEnv * env,jlong jpid,jlongArray jarray,jlongArray jparentArray,jlongArray jstimesArray)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; >= 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 */
os_getParentPidAndTimings(JNIEnv * env,pid_t jpid,jlong * totalTime,jlong * startTime)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 if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
197 *startTime = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000 +
198 kp.kp_proc.p_starttime.tv_usec / 1000);
199 ppid = kp.kp_eproc.e_ppid;
200 }
201
202 // Get cputime if for current process
203 if (pid == getpid()) {
204 struct rusage usage;
205 if (getrusage(RUSAGE_SELF, &usage) == 0) {
206 jlong microsecs =
207 usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
208 usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
209 *totalTime = microsecs * 1000;
210 }
211 }
212
213 return ppid;
214
215 }
216
217 /**
218 * Return the uid of a process or -1 on error
219 */
getUID(pid_t pid)220 static uid_t getUID(pid_t pid) {
221 struct kinfo_proc kp;
222 size_t bufSize = sizeof kp;
223
224 // Read the process info for the specific pid
225 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
226
227 if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) {
228 if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
229 return kp.kp_eproc.e_ucred.cr_uid;
230 }
231 }
232 return (uid_t)-1;
233 }
234
235 /**
236 * Retrieve the command and arguments for the process and store them
237 * into the Info object.
238 */
os_getCmdlineAndUserInfo(JNIEnv * env,jobject jinfo,pid_t pid)239 void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
240 int mib[3], maxargs, nargs, i;
241 size_t size;
242 char *args, *cp, *sp, *np;
243
244 // Get the UID first. This is done here because it is cheap to do it here
245 // on other platforms like Linux/Solaris/AIX where the uid comes from the
246 // same source like the command line info.
247 unix_getUserInfo(env, jinfo, getUID(pid));
248
249 // Get the maximum size of the arguments
250 mib[0] = CTL_KERN;
251 mib[1] = KERN_ARGMAX;
252 size = sizeof(maxargs);
253 if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
254 JNU_ThrowByNameWithLastError(env,
255 "java/lang/RuntimeException", "sysctl failed");
256 return;
257 }
258
259 // Allocate an args buffer and get the arguments
260 args = (char *)malloc(maxargs);
261 if (args == NULL) {
262 JNU_ThrowOutOfMemoryError(env, "malloc failed");
263 return;
264 }
265
266 do { // a block to break out of on error
267 char *argsEnd;
268 jstring cmdexe = NULL;
269
270 mib[0] = CTL_KERN;
271 mib[1] = KERN_PROCARGS2;
272 mib[2] = pid;
273 size = (size_t) maxargs;
274 if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
275 if (errno != EINVAL) {
276 JNU_ThrowByNameWithLastError(env,
277 "java/lang/RuntimeException", "sysctl failed");
278 }
279 break;
280 }
281 memcpy(&nargs, args, sizeof(nargs));
282
283 cp = &args[sizeof(nargs)]; // Strings start after nargs
284 argsEnd = &args[size];
285
286 // Store the command executable path
287 if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {
288 break;
289 }
290
291 // Skip trailing nulls after the executable path
292 for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
293 if (*cp != '\0') {
294 break;
295 }
296 }
297
298 unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe, NULL);
299 } while (0);
300 // Free the arg buffer
301 free(args);
302 }
303
304