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 */
168jfieldID ProcessHandleImpl_Info_commandID;
169
170/* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */
171jfieldID ProcessHandleImpl_Info_commandLineID;
172
173/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */
174jfieldID ProcessHandleImpl_Info_argumentsID;
175
176/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */
177jfieldID ProcessHandleImpl_Info_totalTimeID;
178
179/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */
180jfieldID ProcessHandleImpl_Info_startTimeID;
181
182/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
183jfieldID 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. */
189static 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 */
198JNIEXPORT void JNICALL
199Java_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 */
222JNIEXPORT void JNICALL
223Java_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 */
238JNIEXPORT jint JNICALL
239Java_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 */
309JNIEXPORT jlong JNICALL
310Java_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 */
320JNIEXPORT jboolean JNICALL
321Java_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 */
348JNIEXPORT jint JNICALL
349Java_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 */
365JNIEXPORT void JNICALL
366Java_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 */
395JNIEXPORT jlong JNICALL
396Java_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 */
412JNIEXPORT jlong JNICALL
413Java_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 */
437void 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
476void 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 */
516jint 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 */
637static 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 */
665pid_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
686void 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