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