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