1 | /* |
2 | * Copyright (c) 2014, 2018, 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 <stdio.h> |
35 | #include <errno.h> |
36 | #include <fcntl.h> |
37 | #include <signal.h> |
38 | #include <stdlib.h> |
39 | #include <unistd.h> |
40 | #include <string.h> |
41 | #include <dirent.h> |
42 | #include <ctype.h> |
43 | #include <limits.h> |
44 | #include <sys/types.h> |
45 | #include <sys/stat.h> |
46 | #include <sys/wait.h> |
47 | |
48 | /* For POSIX-compliant getpwuid_r on Solaris */ |
49 | #if defined(__solaris__) |
50 | #define _POSIX_PTHREAD_SEMANTICS |
51 | #endif |
52 | #include <pwd.h> |
53 | |
54 | #ifdef _AIX |
55 | #include <sys/procfs.h> |
56 | #endif |
57 | #ifdef __solaris__ |
58 | #include <procfs.h> |
59 | #endif |
60 | |
61 | #if defined(_AIX) |
62 | #define DIR DIR64 |
63 | #define dirent dirent64 |
64 | #define opendir opendir64 |
65 | #define readdir readdir64 |
66 | #define closedir closedir64 |
67 | #endif |
68 | |
69 | /** |
70 | * This file contains the implementation of the native ProcessHandleImpl |
71 | * functions which are common to all Unix variants. |
72 | * |
73 | * The currently supported Unix variants are Solaris, Linux, MaxOS X and AIX. |
74 | * The various similarities and differences between these systems make it hard |
75 | * to find a clear boundary between platform specific and shared code. |
76 | * |
77 | * In order to ease code sharing between the platforms while still keeping the |
78 | * code as clean as possible (i.e. free of preprocessor macros) we use the |
79 | * following source code layout (remember that ProcessHandleImpl_unix.c will |
80 | * be compiled on EVERY Unix platform while ProcessHandleImpl_<os>.c will be |
81 | * only compiled on the specific OS): |
82 | * |
83 | * - all the JNI wrappers for the ProcessHandleImpl functions go into this file |
84 | * - if their implementation is common on ALL the supported Unix platforms it |
85 | * goes right into the JNI wrappers |
86 | * - if the whole function or substantial parts of it are platform dependent, |
87 | * the implementation goes into os_<function_name> functions in |
88 | * ProcessHandleImpl_<os>.c |
89 | * - if at least two platforms implement an os_<function_name> function in the |
90 | * same way, this implementation is factored out into unix_<function_name>, |
91 | * placed into this file and called from the corresponding os_<function_name> |
92 | * function. |
93 | * - For convenience, all the os_ and unix_ functions are declared in |
94 | * ProcessHandleImpl_unix.h which is included into every |
95 | * ProcessHandleImpl_<os>.c file. |
96 | * |
97 | * Example 1: |
98 | * ---------- |
99 | * The implementation of Java_java_lang_ProcessHandleImpl_initNative() |
100 | * is the same on all platforms except on Linux where it initilizes one |
101 | * additional field. So we place the implementation right into |
102 | * Java_java_lang_ProcessHandleImpl_initNative() but add call to |
103 | * os_init() at the end of the function which is empty on all platforms |
104 | * except Linux where it performs the additionally initializations. |
105 | * |
106 | * Example 2: |
107 | * ---------- |
108 | * The implementation of Java_java_lang_ProcessHandleImpl_00024Info_info0 is the |
109 | * same on Solaris and AIX but different on Linux and MacOSX. We therefore simply |
110 | * call the helpers os_getParentPidAndTimings() and os_getCmdlineAndUserInfo(). |
111 | * The Linux and MaxOS X versions of these functions (in the corresponding files |
112 | * ProcessHandleImpl_linux.c and ProcessHandleImpl_macosx.c) directly contain |
113 | * the platform specific implementations while the Solaris and AIX |
114 | * implementations simply call back to unix_getParentPidAndTimings() and |
115 | * unix_getCmdlineAndUserInfo() which are implemented right in this file. |
116 | * |
117 | * The term "same implementation" is still a question of interpretation. It my |
118 | * be acceptable to have a few ifdef'ed lines if that allows the sharing of a |
119 | * huge function. On the other hand, if the platform specific code in a shared |
120 | * function grows over a certain limit, it may be better to refactor that |
121 | * functionality into corresponding, platform-specific os_ functions. |
122 | */ |
123 | |
124 | |
125 | #ifndef WIFEXITED |
126 | #define WIFEXITED(status) (((status)&0xFF) == 0) |
127 | #endif |
128 | |
129 | #ifndef WEXITSTATUS |
130 | #define WEXITSTATUS(status) (((status)>>8)&0xFF) |
131 | #endif |
132 | |
133 | #ifndef WIFSIGNALED |
134 | #define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0) |
135 | #endif |
136 | |
137 | #ifndef WTERMSIG |
138 | #define WTERMSIG(status) ((status)&0x7F) |
139 | #endif |
140 | |
141 | #ifdef __solaris__ |
142 | /* The child exited because of a signal. |
143 | * The best value to return is 0x80 + signal number, |
144 | * because that is what all Unix shells do, and because |
145 | * it allows callers to distinguish between process exit and |
146 | * process death by signal. |
147 | * Unfortunately, the historical behavior on Solaris is to return |
148 | * the signal number, and we preserve this for compatibility. */ |
149 | #define WTERMSIG_RETURN(status) WTERMSIG(status) |
150 | #else |
151 | #define WTERMSIG_RETURN(status) (WTERMSIG(status) + 0x80) |
152 | #endif |
153 | |
154 | #define RESTARTABLE(_cmd, _result) do { \ |
155 | do { \ |
156 | _result = _cmd; \ |
157 | } while((_result == -1) && (errno == EINTR)); \ |
158 | } while(0) |
159 | |
160 | #define RESTARTABLE_RETURN_PTR(_cmd, _result) do { \ |
161 | do { \ |
162 | _result = _cmd; \ |
163 | } while((_result == NULL) && (errno == EINTR)); \ |
164 | } while(0) |
165 | |
166 | |
167 | /* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ |
168 | jfieldID ProcessHandleImpl_Info_commandID; |
169 | |
170 | /* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */ |
171 | jfieldID ProcessHandleImpl_Info_commandLineID; |
172 | |
173 | /* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ |
174 | jfieldID ProcessHandleImpl_Info_argumentsID; |
175 | |
176 | /* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ |
177 | jfieldID ProcessHandleImpl_Info_totalTimeID; |
178 | |
179 | /* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ |
180 | jfieldID ProcessHandleImpl_Info_startTimeID; |
181 | |
182 | /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ |
183 | jfieldID ProcessHandleImpl_Info_userID; |
184 | |
185 | /* Size of password or group entry when not available via sysconf */ |
186 | #define ENT_BUF_SIZE 1024 |
187 | /* The value for the size of the buffer used by getpwuid_r(). The result of */ |
188 | /* sysconf(_SC_GETPW_R_SIZE_MAX) if available or ENT_BUF_SIZE otherwise. */ |
189 | static long getpw_buf_size; |
190 | |
191 | /************************************************************** |
192 | * Static method to initialize field IDs and the ticks per second rate. |
193 | * |
194 | * Class: java_lang_ProcessHandleImpl_Info |
195 | * Method: initIDs |
196 | * Signature: ()V |
197 | */ |
198 | JNIEXPORT void JNICALL |
199 | Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) { |
200 | |
201 | CHECK_NULL(ProcessHandleImpl_Info_commandID = |
202 | (*env)->GetFieldID(env, clazz, "command" , "Ljava/lang/String;" )); |
203 | CHECK_NULL(ProcessHandleImpl_Info_commandLineID = |
204 | (*env)->GetFieldID(env, clazz, "commandLine" , "Ljava/lang/String;" )); |
205 | CHECK_NULL(ProcessHandleImpl_Info_argumentsID = |
206 | (*env)->GetFieldID(env, clazz, "arguments" , "[Ljava/lang/String;" )); |
207 | CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = |
208 | (*env)->GetFieldID(env, clazz, "totalTime" , "J" )); |
209 | CHECK_NULL(ProcessHandleImpl_Info_startTimeID = |
210 | (*env)->GetFieldID(env, clazz, "startTime" , "J" )); |
211 | CHECK_NULL(ProcessHandleImpl_Info_userID = |
212 | (*env)->GetFieldID(env, clazz, "user" , "Ljava/lang/String;" )); |
213 | } |
214 | |
215 | /*********************************************************** |
216 | * Static method to initialize platform dependent constants. |
217 | * |
218 | * Class: java_lang_ProcessHandleImpl |
219 | * Method: initNative |
220 | * Signature: ()V |
221 | */ |
222 | JNIEXPORT void JNICALL |
223 | Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { |
224 | getpw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); |
225 | if (getpw_buf_size == -1) { |
226 | getpw_buf_size = ENT_BUF_SIZE; |
227 | } |
228 | os_initNative(env, clazz); |
229 | } |
230 | |
231 | /* Block until a child process exits and return its exit code. |
232 | * Note, can only be called once for any given pid if reapStatus = true. |
233 | * |
234 | * Class: java_lang_ProcessHandleImpl |
235 | * Method: waitForProcessExit0 |
236 | * Signature: (JZ)I |
237 | */ |
238 | JNIEXPORT jint JNICALL |
239 | Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env, |
240 | jclass junk, |
241 | jlong jpid, |
242 | jboolean reapStatus) { |
243 | pid_t pid = (pid_t)jpid; |
244 | errno = 0; |
245 | |
246 | if (reapStatus != JNI_FALSE) { |
247 | /* Wait for the child process to exit. |
248 | * waitpid() is standard, so use it on all POSIX platforms. |
249 | * It is known to work when blocking to wait for the pid |
250 | * This returns immediately if the child has already exited. |
251 | */ |
252 | int status; |
253 | while (waitpid(pid, &status, 0) < 0) { |
254 | switch (errno) { |
255 | case ECHILD: |
256 | return java_lang_ProcessHandleImpl_NOT_A_CHILD; // No child |
257 | case EINTR: break; |
258 | default: return -1; |
259 | } |
260 | } |
261 | |
262 | if (WIFEXITED(status)) { |
263 | return WEXITSTATUS(status); |
264 | } else if (WIFSIGNALED(status)) { |
265 | return WTERMSIG_RETURN(status); |
266 | } else { |
267 | return status; |
268 | } |
269 | } else { |
270 | /* |
271 | * Wait for the child process to exit without reaping the exitValue. |
272 | * waitid() is standard on all POSIX platforms. |
273 | * Note: waitid on Mac OS X 10.7 seems to be broken; |
274 | * it does not return the exit status consistently. |
275 | */ |
276 | siginfo_t siginfo; |
277 | int options = WEXITED | WNOWAIT; |
278 | memset(&siginfo, 0, sizeof siginfo); |
279 | while (waitid(P_PID, pid, &siginfo, options) < 0) { |
280 | switch (errno) { |
281 | case ECHILD: |
282 | return java_lang_ProcessHandleImpl_NOT_A_CHILD; // No child |
283 | case EINTR: break; |
284 | default: return -1; |
285 | } |
286 | } |
287 | |
288 | if (siginfo.si_code == CLD_EXITED) { |
289 | /* |
290 | * The child exited normally; get its exit code. |
291 | */ |
292 | return siginfo.si_status; |
293 | } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) { |
294 | return WTERMSIG_RETURN(siginfo.si_status); |
295 | } else { |
296 | /* |
297 | * Unknown exit code; pass it through. |
298 | */ |
299 | return siginfo.si_status; |
300 | } |
301 | } |
302 | } |
303 | |
304 | /* |
305 | * Class: java_lang_ProcessHandleImpl |
306 | * Method: getCurrentPid0 |
307 | * Signature: ()J |
308 | */ |
309 | JNIEXPORT jlong JNICALL |
310 | Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) { |
311 | pid_t pid = getpid(); |
312 | return (jlong) pid; |
313 | } |
314 | |
315 | /* |
316 | * Class: java_lang_ProcessHandleImpl |
317 | * Method: destroy0 |
318 | * Signature: (JJZ)Z |
319 | */ |
320 | JNIEXPORT jboolean JNICALL |
321 | Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env, |
322 | jobject obj, |
323 | jlong jpid, |
324 | jlong startTime, |
325 | jboolean force) { |
326 | pid_t pid = (pid_t) jpid; |
327 | int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; |
328 | jlong start = Java_java_lang_ProcessHandleImpl_isAlive0(env, obj, jpid); |
329 | |
330 | if (start == startTime || start == 0 || startTime == 0) { |
331 | return (kill(pid, sig) < 0) ? JNI_FALSE : JNI_TRUE; |
332 | } else { |
333 | return JNI_FALSE; |
334 | } |
335 | } |
336 | |
337 | /* |
338 | * Returns the children of the requested pid and optionally each parent and |
339 | * start time. |
340 | * Accumulates any process who parent pid matches. |
341 | * The resulting pids are stored into the array of longs. |
342 | * The number of pids is returned if they all fit. |
343 | * If the array is too short, the negative of the desired length is returned. |
344 | * Class: java_lang_ProcessHandleImpl |
345 | * Method: getProcessPids0 |
346 | * Signature: (J[J[J[J)I |
347 | */ |
348 | JNIEXPORT jint JNICALL |
349 | Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, |
350 | jclass clazz, |
351 | jlong jpid, |
352 | jlongArray jarray, |
353 | jlongArray jparentArray, |
354 | jlongArray jstimesArray) { |
355 | return os_getChildren(env, jpid, jarray, jparentArray, jstimesArray); |
356 | } |
357 | |
358 | /* |
359 | * Fill in the Info object from the OS information about the process. |
360 | * |
361 | * Class: java_lang_ProcessHandleImpl_Info |
362 | * Method: info0 |
363 | * Signature: (Ljava/lang/ProcessHandle/Info;J)I |
364 | */ |
365 | JNIEXPORT void JNICALL |
366 | Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, |
367 | jobject jinfo, |
368 | jlong jpid) { |
369 | pid_t pid = (pid_t) jpid; |
370 | pid_t ppid; |
371 | jlong totalTime = -1L; |
372 | jlong startTime = -1L; |
373 | |
374 | ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime); |
375 | if (ppid >= 0) { |
376 | (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); |
377 | JNU_CHECK_EXCEPTION(env); |
378 | |
379 | (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); |
380 | JNU_CHECK_EXCEPTION(env); |
381 | } |
382 | os_getCmdlineAndUserInfo(env, jinfo, pid); |
383 | } |
384 | |
385 | /* |
386 | * Check if a process is alive. |
387 | * Return the start time (ms since 1970) if it is available. |
388 | * If the start time is not available return 0. |
389 | * If the pid is invalid, return -1. |
390 | * |
391 | * Class: java_lang_ProcessHandleImpl |
392 | * Method: isAlive0 |
393 | * Signature: (J)J |
394 | */ |
395 | JNIEXPORT jlong JNICALL |
396 | Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) { |
397 | pid_t pid = (pid_t) jpid; |
398 | jlong startTime = 0L; |
399 | jlong totalTime = 0L; |
400 | pid_t ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime); |
401 | return (ppid < 0) ? -1 : startTime; |
402 | } |
403 | |
404 | /* |
405 | * Returns the parent pid of the requested pid. |
406 | * The start time of the process must match (or be ANY). |
407 | * |
408 | * Class: java_lang_ProcessHandleImpl |
409 | * Method: parent0 |
410 | * Signature: (JJ)J |
411 | */ |
412 | JNIEXPORT jlong JNICALL |
413 | Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, |
414 | jobject obj, |
415 | jlong jpid, |
416 | jlong startTime) { |
417 | pid_t pid = (pid_t) jpid; |
418 | pid_t ppid; |
419 | |
420 | if (pid == getpid()) { |
421 | ppid = getppid(); |
422 | } else { |
423 | jlong start = 0L; |
424 | jlong total = 0L; // unused |
425 | ppid = os_getParentPidAndTimings(env, pid, &total, &start); |
426 | if (start != startTime && start != 0 && startTime != 0) { |
427 | ppid = -1; |
428 | } |
429 | } |
430 | return (jlong) ppid; |
431 | } |
432 | |
433 | /** |
434 | * Construct the argument array by parsing the arguments from the sequence |
435 | * of arguments. |
436 | */ |
437 | void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, char *cp, |
438 | char *argsEnd, jstring cmdexe, char *cmdline) { |
439 | jobject argsArray; |
440 | int i; |
441 | |
442 | (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe); |
443 | JNU_CHECK_EXCEPTION(env); |
444 | |
445 | if (nargs >= 1) { |
446 | // Create a String array for nargs-1 elements |
447 | jclass clazzString = JNU_ClassString(env); |
448 | CHECK_NULL(clazzString); |
449 | argsArray = (*env)->NewObjectArray(env, nargs - 1, clazzString, NULL); |
450 | CHECK_NULL(argsArray); |
451 | |
452 | for (i = 0; i < nargs - 1; i++) { |
453 | jstring str = NULL; |
454 | |
455 | cp += strlen(cp) + 1; |
456 | if (cp > argsEnd || *cp == '\0') { |
457 | return; // Off the end pointer or an empty argument is an error |
458 | } |
459 | |
460 | CHECK_NULL((str = JNU_NewStringPlatform(env, cp))); |
461 | |
462 | (*env)->SetObjectArrayElement(env, argsArray, i, str); |
463 | JNU_CHECK_EXCEPTION(env); |
464 | } |
465 | (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); |
466 | JNU_CHECK_EXCEPTION(env); |
467 | } |
468 | if (cmdline != NULL) { |
469 | jstring commandLine = NULL; |
470 | CHECK_NULL((commandLine = JNU_NewStringPlatform(env, cmdline))); |
471 | (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandLineID, commandLine); |
472 | JNU_CHECK_EXCEPTION(env); |
473 | } |
474 | } |
475 | |
476 | void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) { |
477 | int result = 0; |
478 | char* pwbuf; |
479 | jstring name = NULL; |
480 | |
481 | /* allocate buffer for password record */ |
482 | pwbuf = (char*)malloc(getpw_buf_size); |
483 | if (pwbuf == NULL) { |
484 | JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent" ); |
485 | } else { |
486 | struct passwd pwent; |
487 | struct passwd* p = NULL; |
488 | RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)getpw_buf_size, &p), result); |
489 | |
490 | // Create the Java String if a name was found |
491 | if (result == 0 && p != NULL && |
492 | p->pw_name != NULL && *(p->pw_name) != '\0') { |
493 | name = JNU_NewStringPlatform(env, p->pw_name); |
494 | } |
495 | free(pwbuf); |
496 | } |
497 | if (name != NULL) { |
498 | (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); |
499 | } |
500 | } |
501 | |
502 | /* |
503 | * The following functions are common on Solaris, Linux and AIX. |
504 | */ |
505 | |
506 | #if defined(__solaris__) || defined (__linux__) || defined(_AIX) |
507 | |
508 | /* |
509 | * Returns the children of the requested pid and optionally each parent and |
510 | * start time. |
511 | * Reads /proc and accumulates any process who parent pid matches. |
512 | * The resulting pids are stored into the array of longs. |
513 | * The number of pids is returned if they all fit. |
514 | * If the array is too short, the negative of the desired length is returned. |
515 | */ |
516 | jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, |
517 | jlongArray jparentArray, jlongArray jstimesArray) { |
518 | DIR* dir; |
519 | struct dirent* ptr; |
520 | pid_t pid = (pid_t) jpid; |
521 | jlong* pids = NULL; |
522 | jlong* ppids = NULL; |
523 | jlong* stimes = NULL; |
524 | jsize parentArraySize = 0; |
525 | jsize arraySize = 0; |
526 | jsize stimesSize = 0; |
527 | jsize count = 0; |
528 | |
529 | arraySize = (*env)->GetArrayLength(env, jarray); |
530 | JNU_CHECK_EXCEPTION_RETURN(env, -1); |
531 | if (jparentArray != NULL) { |
532 | parentArraySize = (*env)->GetArrayLength(env, jparentArray); |
533 | JNU_CHECK_EXCEPTION_RETURN(env, -1); |
534 | |
535 | if (arraySize != parentArraySize) { |
536 | JNU_ThrowIllegalArgumentException(env, "array sizes not equal" ); |
537 | return 0; |
538 | } |
539 | } |
540 | if (jstimesArray != NULL) { |
541 | stimesSize = (*env)->GetArrayLength(env, jstimesArray); |
542 | JNU_CHECK_EXCEPTION_RETURN(env, -1); |
543 | |
544 | if (arraySize != stimesSize) { |
545 | JNU_ThrowIllegalArgumentException(env, "array sizes not equal" ); |
546 | return 0; |
547 | } |
548 | } |
549 | |
550 | /* |
551 | * To locate the children we scan /proc looking for files that have a |
552 | * position integer as a filename. |
553 | */ |
554 | if ((dir = opendir("/proc" )) == NULL) { |
555 | JNU_ThrowByNameWithLastError(env, |
556 | "java/lang/RuntimeException" , "Unable to open /proc" ); |
557 | return -1; |
558 | } |
559 | |
560 | do { // Block to break out of on Exception |
561 | pids = (*env)->GetLongArrayElements(env, jarray, NULL); |
562 | if (pids == NULL) { |
563 | break; |
564 | } |
565 | if (jparentArray != NULL) { |
566 | ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); |
567 | if (ppids == NULL) { |
568 | break; |
569 | } |
570 | } |
571 | if (jstimesArray != NULL) { |
572 | stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL); |
573 | if (stimes == NULL) { |
574 | break; |
575 | } |
576 | } |
577 | |
578 | while ((ptr = readdir(dir)) != NULL) { |
579 | pid_t ppid = 0; |
580 | jlong totalTime = 0L; |
581 | jlong startTime = 0L; |
582 | |
583 | /* skip files that aren't numbers */ |
584 | pid_t childpid = (pid_t) atoi(ptr->d_name); |
585 | if ((int) childpid <= 0) { |
586 | continue; |
587 | } |
588 | |
589 | // Get the parent pid, and start time |
590 | ppid = os_getParentPidAndTimings(env, childpid, &totalTime, &startTime); |
591 | if (ppid >= 0 && (pid == 0 || ppid == pid)) { |
592 | if (count < arraySize) { |
593 | // Only store if it fits |
594 | pids[count] = (jlong) childpid; |
595 | |
596 | if (ppids != NULL) { |
597 | // Store the parentPid |
598 | ppids[count] = (jlong) ppid; |
599 | } |
600 | if (stimes != NULL) { |
601 | // Store the process start time |
602 | stimes[count] = startTime; |
603 | } |
604 | } |
605 | count++; // Count to tabulate size needed |
606 | } |
607 | } |
608 | } while (0); |
609 | |
610 | if (pids != NULL) { |
611 | (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); |
612 | } |
613 | if (ppids != NULL) { |
614 | (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); |
615 | } |
616 | if (stimes != NULL) { |
617 | (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0); |
618 | } |
619 | |
620 | closedir(dir); |
621 | // If more pids than array had size for; count will be greater than array size |
622 | return count; |
623 | } |
624 | |
625 | #endif // defined(__solaris__) || defined (__linux__) || defined(_AIX) |
626 | |
627 | /* |
628 | * The following functions are common on Solaris and AIX. |
629 | */ |
630 | |
631 | #if defined(__solaris__) || defined(_AIX) |
632 | |
633 | /** |
634 | * Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo". |
635 | * Returns 0 on success and -1 on error. |
636 | */ |
637 | static int getPsinfo(pid_t pid, psinfo_t *psinfo) { |
638 | FILE* fp; |
639 | char fn[32]; |
640 | int ret; |
641 | |
642 | /* |
643 | * Try to open /proc/%d/psinfo |
644 | */ |
645 | snprintf(fn, sizeof fn, "/proc/%d/psinfo" , pid); |
646 | fp = fopen(fn, "r" ); |
647 | if (fp == NULL) { |
648 | return -1; |
649 | } |
650 | |
651 | ret = fread(psinfo, 1, sizeof(psinfo_t), fp); |
652 | fclose(fp); |
653 | if (ret < sizeof(psinfo_t)) { |
654 | return -1; |
655 | } |
656 | return 0; |
657 | } |
658 | |
659 | /** |
660 | * Read /proc/<pid>/psinfo and return the ppid, total cputime and start time. |
661 | * Return: -1 is fail; >= 0 is parent pid |
662 | * 'total' will contain the running time of 'pid' in nanoseconds. |
663 | * 'start' will contain the start time of 'pid' in milliseconds since epoch. |
664 | */ |
665 | pid_t unix_getParentPidAndTimings(JNIEnv *env, pid_t pid, |
666 | jlong *totalTime, jlong* startTime) { |
667 | psinfo_t psinfo; |
668 | |
669 | if (getPsinfo(pid, &psinfo) < 0) { |
670 | return -1; |
671 | } |
672 | |
673 | // Validate the pid before returning the info |
674 | if (kill(pid, 0) < 0) { |
675 | return -1; |
676 | } |
677 | |
678 | *totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec; |
679 | |
680 | *startTime = psinfo.pr_start.tv_sec * (jlong)1000 + |
681 | psinfo.pr_start.tv_nsec / 1000000; |
682 | |
683 | return (pid_t) psinfo.pr_ppid; |
684 | } |
685 | |
686 | void unix_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { |
687 | psinfo_t psinfo; |
688 | char fn[32]; |
689 | char exePath[PATH_MAX]; |
690 | char prargs[PRARGSZ + 1]; |
691 | jstring cmdexe = NULL; |
692 | int ret; |
693 | |
694 | /* |
695 | * On Solaris, the full path to the executable command is the link in |
696 | * /proc/<pid>/paths/a.out. But it is only readable for processes we own. |
697 | */ |
698 | #if defined(__solaris__) |
699 | snprintf(fn, sizeof fn, "/proc/%d/path/a.out" , pid); |
700 | if ((ret = readlink(fn, exePath, PATH_MAX - 1)) > 0) { |
701 | // null terminate and create String to store for command |
702 | exePath[ret] = '\0'; |
703 | CHECK_NULL(cmdexe = JNU_NewStringPlatform(env, exePath)); |
704 | } |
705 | #endif |
706 | |
707 | /* |
708 | * Now try to open /proc/%d/psinfo |
709 | */ |
710 | if (getPsinfo(pid, &psinfo) < 0) { |
711 | unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, NULL); |
712 | return; |
713 | } |
714 | |
715 | unix_getUserInfo(env, jinfo, psinfo.pr_uid); |
716 | |
717 | /* |
718 | * Now read psinfo.pr_psargs which contains the first PRARGSZ characters of the |
719 | * argument list (i.e. arg[0] arg[1] ...). Unfortunately, PRARGSZ is usually set |
720 | * to 80 characters only. Nevertheless it's better than nothing :) |
721 | */ |
722 | strncpy(prargs, psinfo.pr_psargs, PRARGSZ); |
723 | prargs[PRARGSZ] = '\0'; |
724 | if (prargs[0] == '\0') { |
725 | /* If psinfo.pr_psargs didn't contain any strings, use psinfo.pr_fname |
726 | * (which only contains the last component of exec()ed pathname) as a |
727 | * last resort. This is true for AIX kernel processes for example. |
728 | */ |
729 | strncpy(prargs, psinfo.pr_fname, PRARGSZ); |
730 | prargs[PRARGSZ] = '\0'; |
731 | } |
732 | unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, |
733 | prargs[0] == '\0' ? NULL : prargs); |
734 | } |
735 | |
736 | #endif // defined(__solaris__) || defined(_AIX) |
737 | |