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