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