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 <stdio.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37
38 #include <sys/sysctl.h>
39
40 /**
41 * Implementations of ProcessHandleImpl functions for MAC OS X;
42 * are NOT common to all Unix variants.
43 */
44
45 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
46 static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
47
48 /*
49 * Common Unix function to lookup the uid and return the user name.
50 */
51 extern jstring uidToUser(JNIEnv* env, uid_t uid);
52
53 /* Field id for jString 'command' in java.lang.ProcessHandle.Info */
54 static jfieldID ProcessHandleImpl_Info_commandID;
55
56 /* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
57 static jfieldID ProcessHandleImpl_Info_argumentsID;
58
59 /* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
60 static jfieldID ProcessHandleImpl_Info_totalTimeID;
61
62 /* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
63 static jfieldID ProcessHandleImpl_Info_startTimeID;
64
65 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
66 static jfieldID ProcessHandleImpl_Info_userID;
67
68 /* static value for clock ticks per second. */
69 static long clock_ticks_per_second;
70
71 /**************************************************************
72 * Static method to initialize field IDs and the ticks per second rate.
73 *
74 * Class: java_lang_ProcessHandleImpl_Info
75 * Method: initIDs
76 * Signature: ()V
77 */
78 JNIEXPORT void JNICALL
79 Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
80 CHECK_NULL(ProcessHandleImpl_Info_commandID =
81 (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;"));
82 CHECK_NULL(ProcessHandleImpl_Info_argumentsID =
83 (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;"));
84 CHECK_NULL(ProcessHandleImpl_Info_totalTimeID =
85 (*env)->GetFieldID(env, clazz, "totalTime", "J"));
86 CHECK_NULL(ProcessHandleImpl_Info_startTimeID =
87 (*env)->GetFieldID(env, clazz, "startTime", "J"));
88 CHECK_NULL(ProcessHandleImpl_Info_userID =
89 (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
90 }
91 /**************************************************************
92 * Static method to initialize the ticks per second rate.
93 *
94 * Class: java_lang_ProcessHandleImpl
95 * Method: initNative
96 * Signature: ()V
97 */
98 JNIEXPORT void JNICALL
99 Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
100 clock_ticks_per_second = sysconf(_SC_CLK_TCK);
101 }
102
103 /*
104 * Check if a process is alive.
105 * Return the start time (ms since 1970) if it is available.
106 * If the start time is not available return 0.
107 * If the pid is invalid, return -1.
108 *
109 * Class: java_lang_ProcessHandleImpl
110 * Method: isAlive0
111 * Signature: (J)J
112 */
113 JNIEXPORT jlong JNICALL
114 Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) {
115 pid_t pid = (pid_t) jpid;
116 struct kinfo_proc kp;
117 size_t bufSize = sizeof kp;
118
119 // Read the process info for the specific pid
120 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
121
122 if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
123 return (errno == EINVAL) ? -1 : 0;
124 } else {
125 return (bufSize == 0) ? -1 :
126 (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000
127 + kp.kp_proc.p_starttime.tv_usec / 1000);
128 }
129 }
130
131 /*
132 * Returns the parent pid of the requested pid.
133 *
134 * Class: java_lang_ProcessHandleImpl
135 * Method: parent0
136 * Signature: (J)J
137 */
138 JNIEXPORT jlong JNICALL
139 Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
140 jobject obj,
141 jlong jpid,
142 jlong startTime) {
143 pid_t pid = (pid_t) jpid;
144 pid_t ppid = -1;
145
146 if (pid == getpid()) {
147 ppid = getppid();
148 } else {
149 const pid_t pid = (pid_t) jpid;
150 struct kinfo_proc kp;
151 size_t bufSize = sizeof kp;
152
153 // Read the process info for the specific pid
154 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
155 if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
156 JNU_ThrowByNameWithLastError(env,
157 "java/lang/RuntimeException", "sysctl failed");
158 return -1;
159 }
160 // If the buffer is full and for the pid requested then check the start
161 if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
162 jlong start = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000
163 + kp.kp_proc.p_starttime.tv_usec / 1000);
164 if (start == startTime || start == 0 || startTime == 0) {
165 ppid = kp.kp_eproc.e_ppid;
166 }
167 }
168 }
169 return (jlong) ppid;
170 }
171
172 /*
173 * Returns the children of the requested pid and optionally each parent.
174 *
175 * Class: java_lang_ProcessHandleImpl
176 * Method: getProcessPids0
177 * Signature: (J[J[J)I
178 *
179 * Use sysctl to accumulate any process whose parent pid is zero or matches.
180 * The resulting pids are stored into the array of longs.
181 * The number of pids is returned if they all fit.
182 * If the parentArray is non-null, store the parent pid.
183 * If the array is too short, excess pids are not stored and
184 * the desired length is returned.
185 */
186 JNIEXPORT jint JNICALL
187 Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
188 jclass clazz,
189 jlong jpid,
190 jlongArray jarray,
191 jlongArray jparentArray,
192 jlongArray jstimesArray) {
193 jlong* pids = NULL;
194 jlong* ppids = NULL;
195 jlong* stimes = NULL;
196 jsize parentArraySize = 0;
197 jsize arraySize = 0;
198 jsize stimesSize = 0;
199 jsize count = 0;
200 size_t bufSize = 0;
201 pid_t pid = (pid_t) jpid;
202
203 arraySize = (*env)->GetArrayLength(env, jarray);
204 JNU_CHECK_EXCEPTION_RETURN(env, -1);
205 if (jparentArray != NULL) {
206 parentArraySize = (*env)->GetArrayLength(env, jparentArray);
207 JNU_CHECK_EXCEPTION_RETURN(env, -1);
208
209 if (arraySize != parentArraySize) {
210 JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
211 return 0;
212 }
286 count++; // Count to tabulate size needed
287 }
288 }
289 } while (0);
290
291 if (pids != NULL) {
292 (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
293 }
294 if (ppids != NULL) {
295 (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
296 }
297 if (stimes != NULL) {
298 (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
299 }
300
301 free(buffer);
302 // If more pids than array had size for; count will be greater than array size
303 return count;
304 }
305
306 /**************************************************************
307 * Implementation of ProcessHandleImpl_Info native methods.
308 */
309
310 /*
311 * Fill in the Info object from the OS information about the process.
312 *
313 * Class: java_lang_ProcessHandleImpl
314 * Method: info0
315 * Signature: (J)I
316 */
317 JNIEXPORT void JNICALL
318 Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
319 jobject jinfo,
320 jlong jpid) {
321 pid_t pid = (pid_t) jpid;
322 getStatInfo(env, jinfo, pid);
323 getCmdlineInfo(env, jinfo, pid);
324 }
325
326 /**
327 * Read /proc/<pid>/stat and fill in the fields of the Info object.
328 * The executable name, plus the user, system, and start times are gathered.
329 */
330 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) {
331 jlong totalTime; // nanoseconds
332 unsigned long long startTime; // milliseconds
333
334 const pid_t pid = (pid_t) jpid;
335 struct kinfo_proc kp;
336 size_t bufSize = sizeof kp;
337
338 // Read the process info for the specific pid
339 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
340
341 if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
342 if (errno == EINVAL) {
343 return;
344 } else {
345 JNU_ThrowByNameWithLastError(env,
346 "java/lang/RuntimeException", "sysctl failed");
347 }
348 return;
349 }
350
351 // Convert the UID to the username
352 jstring name = NULL;
353 CHECK_NULL((name = uidToUser(env, kp.kp_eproc.e_ucred.cr_uid)));
354 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
355 JNU_CHECK_EXCEPTION(env);
356
357 startTime = kp.kp_proc.p_starttime.tv_sec * 1000 +
358 kp.kp_proc.p_starttime.tv_usec / 1000;
359
360 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
361 JNU_CHECK_EXCEPTION(env);
362
363 // Get cputime if for current process
364 if (pid == getpid()) {
365 struct rusage usage;
366 if (getrusage(RUSAGE_SELF, &usage) != 0) {
367 return;
368 }
369 jlong microsecs =
370 usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
371 usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
372 totalTime = microsecs * 1000;
373 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
374 JNU_CHECK_EXCEPTION(env);
375 }
376 }
377
378 /**
379 * Construct the argument array by parsing the arguments from the sequence of arguments.
380 */
381 static int fillArgArray(JNIEnv *env, jobject jinfo, int nargs,
382 const char *cp, const char *argsEnd) {
383 jstring str = NULL;
384 jobject argsArray;
385 int i;
386
387 if (nargs < 1) {
388 return 0;
389 }
390 // Create a String array for nargs-1 elements
391 CHECK_NULL_RETURN((argsArray = (*env)->NewObjectArray(env,
392 nargs - 1, JNU_ClassString(env), NULL)), -1);
393
394 for (i = 0; i < nargs - 1; i++) {
395 // skip to the next argument; omits arg[0]
396 cp += strnlen(cp, (argsEnd - cp)) + 1;
397
398 if (cp > argsEnd || *cp == '\0') {
399 return -2; // Off the end pointer or an empty argument is an error
400 }
401
402 CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
403
404 (*env)->SetObjectArrayElement(env, argsArray, i, str);
405 JNU_CHECK_EXCEPTION_RETURN(env, -3);
406 }
407 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
408 JNU_CHECK_EXCEPTION_RETURN(env, -4);
409 return 0;
410 }
411
412 /**
413 * Retrieve the command and arguments for the process and store them
414 * into the Info object.
415 */
416 static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
417 int mib[3], maxargs, nargs, i;
418 size_t size;
419 char *args, *cp, *sp, *np;
420
421 // Get the maximum size of the arguments
422 mib[0] = CTL_KERN;
423 mib[1] = KERN_ARGMAX;
424 size = sizeof(maxargs);
425 if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
426 JNU_ThrowByNameWithLastError(env,
427 "java/lang/RuntimeException", "sysctl failed");
428 return;
429 }
430
431 // Allocate an args buffer and get the arguments
432 args = (char *)malloc(maxargs);
433 if (args == NULL) {
434 JNU_ThrowOutOfMemoryError(env, "malloc failed");
435 return;
436 }
437
438 do { // a block to break out of on error
439 char *argsEnd;
440 jstring str = NULL;
441
442 mib[0] = CTL_KERN;
443 mib[1] = KERN_PROCARGS2;
444 mib[2] = pid;
445 size = (size_t) maxargs;
446 if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
447 if (errno != EINVAL) {
448 JNU_ThrowByNameWithLastError(env,
449 "java/lang/RuntimeException", "sysctl failed");
450 }
451 break;
452 }
453 memcpy(&nargs, args, sizeof(nargs));
454
455 cp = &args[sizeof(nargs)]; // Strings start after nargs
456 argsEnd = &args[size];
457
458 // Store the command executable path
459 if ((str = JNU_NewStringPlatform(env, cp)) == NULL) {
460 break;
461 }
462 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
463 if ((*env)->ExceptionCheck(env)) {
464 break;
465 }
466
467 // Skip trailing nulls after the executable path
468 for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
469 if (*cp != '\0') {
470 break;
471 }
472 }
473
474 fillArgArray(env, jinfo, nargs, cp, argsEnd);
475 } while (0);
476 // Free the arg buffer
477 free(args);
478 }
479
|
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 }
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
|