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[] 'arguments' in java.lang.ProcessHandleImpl.Info */
158 jfieldID ProcessHandleImpl_Info_argumentsID;
159
160 /* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */
161 jfieldID ProcessHandleImpl_Info_totalTimeID;
162
163 /* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */
164 jfieldID ProcessHandleImpl_Info_startTimeID;
165
166 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
167 jfieldID ProcessHandleImpl_Info_userID;
168
169 /* static value for clock ticks per second. */
170 long clock_ticks_per_second;
171
172 /**************************************************************
173 * Static method to initialize field IDs and the ticks per second rate.
174 *
175 * Class: java_lang_ProcessHandleImpl_Info
176 * Method: initIDs
177 * Signature: ()V
178 */
179 JNIEXPORT void JNICALL
180 Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
181
182 CHECK_NULL(ProcessHandleImpl_Info_commandID =
183 (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;"));
184 CHECK_NULL(ProcessHandleImpl_Info_argumentsID =
185 (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;"));
186 CHECK_NULL(ProcessHandleImpl_Info_totalTimeID =
187 (*env)->GetFieldID(env, clazz, "totalTime", "J"));
188 CHECK_NULL(ProcessHandleImpl_Info_startTimeID =
189 (*env)->GetFieldID(env, clazz, "startTime", "J"));
190 CHECK_NULL(ProcessHandleImpl_Info_userID =
191 (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
192 }
193
194 /**************************************************************
195 * Static method to initialize the ticks per second rate.
196 *
197 * Class: java_lang_ProcessHandleImpl
198 * Method: initNative
199 * Signature: ()V
200 */
201 JNIEXPORT void JNICALL
202 Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
203 clock_ticks_per_second = sysconf(_SC_CLK_TCK);
204 os_initNative(env, clazz);
205 }
206
207 /* Block until a child process exits and return its exit code.
208 * Note, can only be called once for any given pid if reapStatus = true.
209 *
210 * Class: java_lang_ProcessHandleImpl
211 * Method: waitForProcessExit0
212 * Signature: (JZ)I
213 */
214 JNIEXPORT jint JNICALL
215 Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
216 jclass junk,
217 jlong jpid,
218 jboolean reapStatus) {
219 pid_t pid = (pid_t)jpid;
220 errno = 0;
221
222 if (reapStatus != JNI_FALSE) {
223 /* Wait for the child process to exit.
224 * waitpid() is standard, so use it on all POSIX platforms.
225 * It is known to work when blocking to wait for the pid
226 * This returns immediately if the child has already exited.
227 */
228 int status;
229 while (waitpid(pid, &status, 0) < 0) {
230 switch (errno) {
231 case ECHILD: return 0;
232 case EINTR: break;
233 default: return -1;
234 }
235 }
236
237 if (WIFEXITED(status)) {
238 return WEXITSTATUS(status);
239 } else if (WIFSIGNALED(status)) {
240 return WTERMSIG_RETURN(status);
241 } else {
242 return status;
243 }
244 } else {
245 /*
246 * Wait for the child process to exit without reaping the exitValue.
247 * waitid() is standard on all POSIX platforms.
248 * Note: waitid on Mac OS X 10.7 seems to be broken;
249 * it does not return the exit status consistently.
250 */
251 siginfo_t siginfo;
252 int options = WEXITED | WNOWAIT;
253 memset(&siginfo, 0, sizeof siginfo);
254 while (waitid(P_PID, pid, &siginfo, options) < 0) {
255 switch (errno) {
256 case ECHILD: return 0;
257 case EINTR: break;
258 default: return -1;
259 }
260 }
261
262 if (siginfo.si_code == CLD_EXITED) {
263 /*
264 * The child exited normally; get its exit code.
265 */
266 return siginfo.si_status;
267 } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) {
268 return WTERMSIG_RETURN(siginfo.si_status);
269 } else {
270 /*
271 * Unknown exit code; pass it through.
272 */
273 return siginfo.si_status;
274 }
275 }
276 }
277
278 /*
279 * Class: java_lang_ProcessHandleImpl
280 * Method: getCurrentPid0
281 * Signature: ()J
282 */
283 JNIEXPORT jlong JNICALL
284 Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) {
285 pid_t pid = getpid();
286 return (jlong) pid;
287 }
288
289 /*
290 * Class: java_lang_ProcessHandleImpl
291 * Method: destroy0
292 * Signature: (JJZ)Z
293 */
294 JNIEXPORT jboolean JNICALL
295 Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
296 jobject obj,
297 jlong jpid,
298 jlong startTime,
299 jboolean force) {
300 pid_t pid = (pid_t) jpid;
301 int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
302 jlong start = Java_java_lang_ProcessHandleImpl_isAlive0(env, obj, jpid);
303
304 if (start == startTime || start == 0 || startTime == 0) {
305 return (kill(pid, sig) < 0) ? JNI_FALSE : JNI_TRUE;
306 } else {
307 return JNI_FALSE;
308 }
309 }
310
311 /*
312 * Returns the children of the requested pid and optionally each parent and
313 * start time.
314 * Accumulates any process who parent pid matches.
315 * The resulting pids are stored into the array of longs.
316 * The number of pids is returned if they all fit.
317 * If the array is too short, the negative of the desired length is returned.
318 * Class: java_lang_ProcessHandleImpl
319 * Method: getProcessPids0
320 * Signature: (J[J[J[J)I
321 */
322 JNIEXPORT jint JNICALL
323 Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
324 jclass clazz,
325 jlong jpid,
326 jlongArray jarray,
327 jlongArray jparentArray,
328 jlongArray jstimesArray) {
329 return os_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
330 }
331
332 /*
333 * Fill in the Info object from the OS information about the process.
334 *
335 * Class: java_lang_ProcessHandleImpl_Info
336 * Method: info0
337 * Signature: (Ljava/lang/ProcessHandle/Info;J)I
338 */
339 JNIEXPORT void JNICALL
340 Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
341 jobject jinfo,
342 jlong jpid) {
343 pid_t pid = (pid_t) jpid;
344 pid_t ppid;
345 jlong totalTime = -1L;
346 jlong startTime = -1L;
347
348 ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime);
349 if (ppid > 0) {
350 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
351 JNU_CHECK_EXCEPTION(env);
352
353 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
354 JNU_CHECK_EXCEPTION(env);
355 }
356 os_getCmdlineAndUserInfo(env, jinfo, pid);
357 }
358
359 /*
360 * Check if a process is alive.
361 * Return the start time (ms since 1970) if it is available.
362 * If the start time is not available return 0.
363 * If the pid is invalid, return -1.
364 *
365 * Class: java_lang_ProcessHandleImpl
366 * Method: isAlive0
367 * Signature: (J)J
368 */
369 JNIEXPORT jlong JNICALL
370 Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) {
371 pid_t pid = (pid_t) jpid;
372 jlong startTime = 0L;
373 jlong totalTime = 0L;
374 pid_t ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime);
375 return (ppid <= 0) ? -1 : startTime;
376 }
377
378 /*
379 * Returns the parent pid of the requested pid.
380 * The start time of the process must match (or be ANY).
381 *
382 * Class: java_lang_ProcessHandleImpl
383 * Method: parent0
384 * Signature: (JJ)J
385 */
386 JNIEXPORT jlong JNICALL
387 Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
388 jobject obj,
389 jlong jpid,
390 jlong startTime) {
391 pid_t pid = (pid_t) jpid;
392 pid_t ppid;
393
394 pid_t mypid = getpid();
395 if (pid == mypid) {
396 ppid = getppid();
397 } else {
398 jlong start = 0L;
399 jlong total = 0L; // unused
400 ppid = os_getParentPidAndTimings(env, pid, &total, &start);
401 if (start != startTime && start != 0 && startTime != 0) {
402 ppid = -1;
403 }
404 }
405 return (jlong) ppid;
406 }
407
408 /**
409 * Construct the argument array by parsing the arguments from the sequence
410 * of arguments. The zero'th arg is the command executable but it will only
411 * be used if the 'cmdexe' argument is NULL.
412 */
413 void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs,
414 char *cp, char *argsEnd, jstring cmdexe) {
415 jobject argsArray;
416 int i;
417
418 if (cmdexe == NULL && nargs > 0) {
419 // Create a string from arg[0]
420 CHECK_NULL((cmdexe = JNU_NewStringPlatform(env, cp)));
421 }
422 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
423 JNU_CHECK_EXCEPTION(env);
424
425 if (nargs > 1) {
426 // Create a String array for nargs-1 elements
427 argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL);
428 CHECK_NULL(argsArray);
429
430 for (i = 0; i < nargs - 1; i++) {
431 jstring str = NULL;
432
433 cp += strlen(cp) + 1;
434 if (cp > argsEnd || *cp == '\0') {
435 return; // Off the end pointer or an empty argument is an error
436 }
437
438 CHECK_NULL((str = JNU_NewStringPlatform(env, cp)));
439
440 (*env)->SetObjectArrayElement(env, argsArray, i, str);
441 JNU_CHECK_EXCEPTION(env);
442 }
443 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
444 JNU_CHECK_EXCEPTION(env);
445 }
446 }
447
448 /* Size of password or group entry when not available via sysconf */
449 #define ENT_BUF_SIZE 1024
450
451 void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) {
452 int result = 0;
453 int buflen;
454 char* pwbuf;
455 jstring name = NULL;
456
457 /* allocate buffer for password record */
458 buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
459 if (buflen == -1) {
460 buflen = ENT_BUF_SIZE;
461 }
462 pwbuf = (char*)malloc(buflen);
463 if (pwbuf == NULL) {
464 JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent");
465 } else {
466 struct passwd pwent;
467 struct passwd* p = NULL;
468
469 #ifdef __solaris__
470 RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p);
471 #else
472 RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result);
473 #endif
474
475 // Create the Java String if a name was found
476 if (result == 0 && p != NULL &&
477 p->pw_name != NULL && *(p->pw_name) != '\0') {
478 name = JNU_NewStringPlatform(env, p->pw_name);
479 }
480 free(pwbuf);
481 }
482 if (name != NULL) {
483 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
484 }
485 }
486
487 /*
488 * The following fuctions are common on Solaris, Linux and AIX.
489 */
490
491 #if defined(__solaris__) || defined (__linux__) || defined(_AIX)
492
493 /*
494 * Returns the children of the requested pid and optionally each parent and
495 * start time.
496 * Reads /proc and accumulates any process who parent pid matches.
497 * The resulting pids are stored into the array of longs.
498 * The number of pids is returned if they all fit.
499 * If the array is too short, the negative of the desired length is returned.
500 */
501 jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
502 jlongArray jparentArray, jlongArray jstimesArray) {
503 DIR* dir;
504 struct dirent* ptr;
505 pid_t pid = (pid_t) jpid;
506 jlong* pids = NULL;
507 jlong* ppids = NULL;
508 jlong* stimes = NULL;
509 jsize parentArraySize = 0;
510 jsize arraySize = 0;
511 jsize stimesSize = 0;
512 jsize count = 0;
513
514 arraySize = (*env)->GetArrayLength(env, jarray);
515 JNU_CHECK_EXCEPTION_RETURN(env, -1);
516 if (jparentArray != NULL) {
517 parentArraySize = (*env)->GetArrayLength(env, jparentArray);
518 JNU_CHECK_EXCEPTION_RETURN(env, -1);
519
520 if (arraySize != parentArraySize) {
521 JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
522 return 0;
553 break;
554 }
555 }
556 if (jstimesArray != NULL) {
557 stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
558 if (stimes == NULL) {
559 break;
560 }
561 }
562
563 while ((ptr = readdir(dir)) != NULL) {
564 pid_t ppid = 0;
565 jlong totalTime = 0L;
566 jlong startTime = 0L;
567
568 /* skip files that aren't numbers */
569 pid_t childpid = (pid_t) atoi(ptr->d_name);
570 if ((int) childpid <= 0) {
571 continue;
572 }
573
574 // Get the parent pid, and start time
575 ppid = os_getParentPidAndTimings(env, childpid, &totalTime, &startTime);
576 if (ppid > 0 && (pid == 0 || ppid == pid)) {
577 if (count < arraySize) {
578 // Only store if it fits
579 pids[count] = (jlong) childpid;
580
581 if (ppids != NULL) {
582 // Store the parentPid
583 ppids[count] = (jlong) ppid;
584 }
585 if (stimes != NULL) {
586 // Store the process start time
587 stimes[count] = startTime;
588 }
589 }
590 count++; // Count to tabulate size needed
591 }
592 }
593 } while (0);
594
595 if (pids != NULL) {
596 (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
597 }
598 if (ppids != NULL) {
599 (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
600 }
601 if (stimes != NULL) {
602 (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
603 }
604
605 closedir(dir);
606 // If more pids than array had size for; count will be greater than array size
607 return count;
608 }
609
610 #endif // defined(__solaris__) || defined (__linux__) || defined(_AIX)
611
612 /*
613 * The following fuctions are common on Solaris and AIX.
614 */
615
616 #if defined(__solaris__) || defined(_AIX)
617
618 /**
619 * Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo".
620 * Returns 0 on success and -1 on error.
621 */
622 static int getPsinfo(pid_t pid, psinfo_t *psinfo) {
623 FILE* fp;
624 char fn[32];
625 int ret;
626
627 /*
628 * Try to open /proc/%d/psinfo
629 */
630 snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
631 fp = fopen(fn, "r");
632 if (fp == NULL) {
633 return -1;
634 }
635
636 ret = fread(psinfo, 1, sizeof(psinfo_t), fp);
637 fclose(fp);
638 if (ret < sizeof(psinfo_t)) {
639 return -1;
640 }
641 return 0;
642 }
643
644 /**
645 * Read /proc/<pid>/psinfo and return the ppid, total cputime and start time.
646 * Return: -1 is fail; zero is unknown; > 0 is parent pid
647 * 'total' will contain the running time of 'pid' in nanoseconds.
648 * 'start' will contain the start time of 'pid' in milliseconds since epoch.
649 */
650 pid_t unix_getParentPidAndTimings(JNIEnv *env, pid_t pid,
651 jlong *totalTime, jlong* startTime) {
652 psinfo_t psinfo;
653
654 if (getPsinfo(pid, &psinfo) < 0) {
655 return -1;
656 }
657
658 *totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec;
659
660 *startTime = psinfo.pr_start.tv_sec * (jlong)1000 +
661 psinfo.pr_start.tv_nsec / 1000000;
662
663 return (pid_t) psinfo.pr_ppid;
664 }
665
666 void unix_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
667 psinfo_t psinfo;
668 char fn[32];
669 char exePath[PATH_MAX];
670 jstring str = NULL, cmdexe = NULL;
671 int ret;
672
673 /*
674 * On Solaris, the full path to the executable command is the link in
675 * /proc/<pid>/paths/a.out. But it is only readable for processes we own.
676 */
677 #if defined(__solaris__)
678 snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid);
679 if ((ret = readlink(fn, exePath, PATH_MAX - 1)) > 0) {
680 // null terminate and create String to store for command
681 exePath[ret] = '\0';
682 CHECK_NULL(cmdexe = JNU_NewStringPlatform(env, exePath));
683 }
684 #endif
685
686 /*
687 * Now try to open /proc/%d/psinfo
688 */
689 if (getPsinfo(pid, &psinfo) < 0) {
690 unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe);
691 return;
692 }
693
694 unix_getUserInfo(env, jinfo, psinfo.pr_uid);
695
696 /*
697 * Now read psinfo.pr_psargs which contains the first PRARGSZ characters of the
698 * argument list (i.e. arg[0] arg[1] ...). Unfortunately, PRARGSZ is usually set
699 * to 80 characters only. Nevertheless it's better than nothing :)
700 */
701 {
702 char prargs[PRARGSZ + 1];
703 char *cp, *argsEnd;
704 int nargs = 1;
705 strncpy(prargs, psinfo.pr_psargs, PRARGSZ);
706 prargs[PRARGSZ] = '\0';
707 argsEnd = &prargs[strlen(prargs)];
708 if (argsEnd == prargs) {
709 /* If psinfo.pr_psargs didn't contain any strings, use psinfo.pr_fname
710 * (which only contains the last component of exec()ed pathname) as a
711 * last resort. This is true for AIX kernel processes for example.
712 */
713 strncpy(prargs, psinfo.pr_fname, PRARGSZ);
714 prargs[PRARGSZ] = '\0';
715 argsEnd = &prargs[strlen(prargs)];
716 } else {
717 /* The arguments are separated by a single whitespace character but we want
718 * them to be distinct, null-terminated strings. */
719 for (cp = prargs; *cp != '\0'; cp++) {
720 if (*cp == ' ') {
721 *cp = '\0';
722 nargs++;
723 }
724 }
725 }
726 unix_fillArgArray(env, jinfo, nargs, prargs, argsEnd, cmdexe);
727 }
728 }
729
730 #endif // defined(__solaris__) || defined(_AIX)
|