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