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