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 package java.lang; 26 27 import java.lang.annotation.Native; 28 import java.security.PrivilegedAction; 29 import java.time.Duration; 30 import java.time.Instant; 31 import java.util.Arrays; 32 import java.util.Optional; 33 import java.util.concurrent.CompletableFuture; 34 import java.util.concurrent.ConcurrentHashMap; 35 import java.util.concurrent.ConcurrentMap; 36 import java.util.concurrent.Executor; 37 import java.util.concurrent.Executors; 38 import java.util.concurrent.ThreadFactory; 39 import java.util.concurrent.ThreadLocalRandom; 40 import java.util.stream.IntStream; 41 import java.util.stream.Stream; 42 43 import static java.security.AccessController.doPrivileged; 44 45 /** 46 * ProcessHandleImpl is the implementation of ProcessHandle. 47 * 48 * @see Process 49 * @since 9 50 */ 51 @jdk.internal.ValueBased 52 final class ProcessHandleImpl implements ProcessHandle { 53 /** 54 * Default size of stack for reaper processes. 55 */ 56 private static long REAPER_DEFAULT_STACKSIZE = 128 * 1024; 57 58 /** 59 * Return value from waitForProcessExit0 indicating the process is not a child. 60 */ 61 @Native 62 private static final int NOT_A_CHILD = -2; 63 64 /** 65 * Cache the ProcessHandle of this process. 66 */ 67 private static final ProcessHandleImpl current; 68 69 /** 70 * Map of pids to ExitCompletions. 71 */ 72 private static final ConcurrentMap<Long, ExitCompletion> 73 completions = new ConcurrentHashMap<>(); 74 75 static { initNative()76 initNative(); 77 long pid = getCurrentPid0(); 78 current = new ProcessHandleImpl(pid, isAlive0(pid)); 79 } 80 initNative()81 private static native void initNative(); 82 83 /** 84 * The thread pool of "process reaper" daemon threads. 85 */ 86 private static final Executor processReaperExecutor = 87 doPrivileged((PrivilegedAction<Executor>) () -> { 88 // Initialize ThreadLocalRandom now to avoid using the smaller stack 89 // of the processReaper threads. 90 ThreadLocalRandom.current(); 91 92 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 93 while (tg.getParent() != null) tg = tg.getParent(); 94 ThreadGroup systemThreadGroup = tg; 95 96 // For a debug build, the stack shadow zone is larger; 97 // Increase the total stack size to avoid potential stack overflow. 98 int debugDelta = "release".equals(System.getProperty("jdk.debug")) ? 0 : (4*4096); 99 final long stackSize = Boolean.getBoolean("jdk.lang.processReaperUseDefaultStackSize") 100 ? 0 : REAPER_DEFAULT_STACKSIZE + debugDelta; 101 102 ThreadFactory threadFactory = grimReaper -> { 103 Thread t = new Thread(systemThreadGroup, grimReaper, 104 "process reaper", stackSize, false); 105 t.setDaemon(true); 106 // A small attempt (probably futile) to avoid priority inversion 107 t.setPriority(Thread.MAX_PRIORITY); 108 return t; 109 }; 110 111 return Executors.newCachedThreadPool(threadFactory); 112 }); 113 114 private static class ExitCompletion extends CompletableFuture<Integer> { 115 final boolean isReaping; 116 ExitCompletion(boolean isReaping)117 ExitCompletion(boolean isReaping) { 118 this.isReaping = isReaping; 119 } 120 } 121 122 /** 123 * Returns a CompletableFuture that completes with process exit status when 124 * the process completes. 125 * 126 * @param shouldReap true if the exit value should be reaped 127 */ completion(long pid, boolean shouldReap)128 static CompletableFuture<Integer> completion(long pid, boolean shouldReap) { 129 // check canonicalizing cache 1st 130 ExitCompletion completion = completions.get(pid); 131 // re-try until we get a completion that shouldReap => isReaping 132 while (completion == null || (shouldReap && !completion.isReaping)) { 133 ExitCompletion newCompletion = new ExitCompletion(shouldReap); 134 if (completion == null) { 135 completion = completions.putIfAbsent(pid, newCompletion); 136 } else { 137 completion = completions.replace(pid, completion, newCompletion) 138 ? null : completions.get(pid); 139 } 140 if (completion == null) { 141 // newCompletion has just been installed successfully 142 completion = newCompletion; 143 // spawn a thread to wait for and deliver the exit value 144 processReaperExecutor.execute(new Runnable() { 145 // Use inner class to avoid lambda stack overhead 146 public void run() { 147 int exitValue = waitForProcessExit0(pid, shouldReap); 148 if (exitValue == NOT_A_CHILD) { 149 // pid not alive or not a child of this process 150 // If it is alive wait for it to terminate 151 long sleep = 300; // initial milliseconds to sleep 152 int incr = 30; // increment to the sleep time 153 154 long startTime = isAlive0(pid); 155 long origStart = startTime; 156 while (startTime >= 0) { 157 try { 158 Thread.sleep(Math.min(sleep, 5000L)); // no more than 5 sec 159 sleep += incr; 160 } catch (InterruptedException ie) { 161 // ignore and retry 162 } 163 startTime = isAlive0(pid); // recheck if it is alive 164 if (startTime > 0 && origStart > 0 && startTime != origStart) { 165 // start time changed (and is not zero), pid is not the same process 166 break; 167 } 168 } 169 exitValue = 0; 170 } 171 newCompletion.complete(exitValue); 172 // remove from cache afterwards 173 completions.remove(pid, newCompletion); 174 } 175 }); 176 } 177 } 178 return completion; 179 } 180 181 @Override onExit()182 public CompletableFuture<ProcessHandle> onExit() { 183 if (this.equals(current)) { 184 throw new IllegalStateException("onExit for current process not allowed"); 185 } 186 187 return ProcessHandleImpl.completion(pid(), false) 188 .handleAsync((exitStatus, unusedThrowable) -> this); 189 } 190 191 /** 192 * Wait for the process to exit, return the value. 193 * Conditionally reap the value if requested 194 * @param pid the processId 195 * @param reapvalue if true, the value is retrieved, 196 * else return the value and leave the process waitable 197 * 198 * @return the value or -1 if an error occurs 199 */ waitForProcessExit0(long pid, boolean reapvalue)200 private static native int waitForProcessExit0(long pid, boolean reapvalue); 201 202 /** 203 * The pid of this ProcessHandle. 204 */ 205 private final long pid; 206 207 /** 208 * The start time of this process. 209 * If STARTTIME_ANY, the start time of the process is not available from the os. 210 * If greater than zero, the start time of the process. 211 */ 212 private final long startTime; 213 214 /* The start time should match any value. 215 * Typically, this is because the OS can not supply it. 216 * The process is known to exist but not the exact start time. 217 */ 218 private final long STARTTIME_ANY = 0L; 219 220 /* The start time of a Process that does not exist. */ 221 private final long STARTTIME_PROCESS_UNKNOWN = -1; 222 223 /** 224 * Private constructor. Instances are created by the {@code get(long)} factory. 225 * @param pid the pid for this instance 226 */ ProcessHandleImpl(long pid, long startTime)227 private ProcessHandleImpl(long pid, long startTime) { 228 this.pid = pid; 229 this.startTime = startTime; 230 } 231 232 /** 233 * Returns a ProcessHandle for an existing native process. 234 * 235 * @param pid the native process identifier 236 * @return The ProcessHandle for the pid if the process is alive; 237 * or {@code null} if the process ID does not exist in the native system. 238 * @throws SecurityException if RuntimePermission("manageProcess") is not granted 239 */ get(long pid)240 static Optional<ProcessHandle> get(long pid) { 241 SecurityManager sm = System.getSecurityManager(); 242 if (sm != null) { 243 sm.checkPermission(new RuntimePermission("manageProcess")); 244 } 245 long start = isAlive0(pid); 246 return (start >= 0) 247 ? Optional.of(new ProcessHandleImpl(pid, start)) 248 : Optional.empty(); 249 } 250 251 /** 252 * Returns a ProcessHandle for an existing native process known to be alive. 253 * The startTime of the process is retrieved and stored in the ProcessHandle. 254 * It does not perform a security check since it is called from ProcessImpl. 255 * @param pid of the known to exist process 256 * @return a ProcessHandle corresponding to an existing Process instance 257 */ getInternal(long pid)258 static ProcessHandleImpl getInternal(long pid) { 259 return new ProcessHandleImpl(pid, isAlive0(pid)); 260 } 261 262 /** 263 * Returns the native process ID. 264 * A {@code long} is used to be able to fit the system specific binary values 265 * for the process. 266 * 267 * @return the native process ID 268 */ 269 @Override pid()270 public long pid() { 271 return pid; 272 } 273 274 /** 275 * Returns the ProcessHandle for the current native process. 276 * 277 * @return The ProcessHandle for the OS process. 278 * @throws SecurityException if RuntimePermission("manageProcess") is not granted 279 */ current()280 public static ProcessHandleImpl current() { 281 SecurityManager sm = System.getSecurityManager(); 282 if (sm != null) { 283 sm.checkPermission(new RuntimePermission("manageProcess")); 284 } 285 return current; 286 } 287 288 /** 289 * Return the pid of the current process. 290 * 291 * @return the pid of the current process 292 */ getCurrentPid0()293 private static native long getCurrentPid0(); 294 295 /** 296 * Returns a ProcessHandle for the parent process. 297 * 298 * @return a ProcessHandle of the parent process; {@code null} is returned 299 * if the child process does not have a parent 300 * @throws SecurityException if permission is not granted by the 301 * security policy 302 */ parent()303 public Optional<ProcessHandle> parent() { 304 SecurityManager sm = System.getSecurityManager(); 305 if (sm != null) { 306 sm.checkPermission(new RuntimePermission("manageProcess")); 307 } 308 long ppid = parent0(pid, startTime); 309 if (ppid <= 0) { 310 return Optional.empty(); 311 } 312 return get(ppid); 313 } 314 315 /** 316 * Returns the parent of the native pid argument. 317 * 318 * @param pid the process id 319 * @param startTime the startTime of the process 320 * @return the parent of the native pid; if any, otherwise -1 321 */ parent0(long pid, long startTime)322 private static native long parent0(long pid, long startTime); 323 324 /** 325 * Returns the number of pids filled in to the array. 326 * @param pid if {@code pid} equals zero, then all known processes are returned; 327 * otherwise only direct child process pids are returned 328 * @param pids an allocated long array to receive the pids 329 * @param ppids an allocated long array to receive the parent pids; may be null 330 * @param starttimes an allocated long array to receive the child start times; may be null 331 * @return if greater than or equal to zero is the number of pids in the array; 332 * if greater than the length of the arrays, the arrays are too small 333 */ getProcessPids0(long pid, long[] pids, long[] ppids, long[] starttimes)334 private static native int getProcessPids0(long pid, long[] pids, 335 long[] ppids, long[] starttimes); 336 337 /** 338 * Destroy the process for this ProcessHandle. 339 * The native code checks the start time before sending the termination request. 340 * 341 * @param force {@code true} if the process should be terminated forcibly; 342 * else {@code false} for a normal termination 343 */ destroyProcess(boolean force)344 boolean destroyProcess(boolean force) { 345 if (this.equals(current)) { 346 throw new IllegalStateException("destroy of current process not allowed"); 347 } 348 return destroy0(pid, startTime, force); 349 } 350 351 /** 352 * Signal the process to terminate. 353 * The process is signaled only if its start time matches the known start time. 354 * 355 * @param pid process id to kill 356 * @param startTime the start time of the process 357 * @param forcibly true to forcibly terminate (SIGKILL vs SIGTERM) 358 * @return true if the process was signaled without error; false otherwise 359 */ destroy0(long pid, long startTime, boolean forcibly)360 private static native boolean destroy0(long pid, long startTime, boolean forcibly); 361 362 @Override destroy()363 public boolean destroy() { 364 return destroyProcess(false); 365 } 366 367 @Override destroyForcibly()368 public boolean destroyForcibly() { 369 return destroyProcess(true); 370 } 371 372 373 @Override supportsNormalTermination()374 public boolean supportsNormalTermination() { 375 return ProcessImpl.SUPPORTS_NORMAL_TERMINATION; 376 } 377 378 /** 379 * Tests whether the process represented by this {@code ProcessHandle} is alive. 380 * 381 * @return {@code true} if the process represented by this 382 * {@code ProcessHandle} object has not yet terminated. 383 * @since 9 384 */ 385 @Override isAlive()386 public boolean isAlive() { 387 long start = isAlive0(pid); 388 return (start >= 0 && (start == startTime || start == 0 || startTime == 0)); 389 } 390 391 /** 392 * Returns the process start time depending on whether the pid is alive. 393 * This must not reap the exitValue. 394 * 395 * @param pid the pid to check 396 * @return the start time in milliseconds since 1970, 397 * 0 if the start time cannot be determined, 398 * -1 if the pid does not exist. 399 */ isAlive0(long pid)400 private static native long isAlive0(long pid); 401 402 @Override children()403 public Stream<ProcessHandle> children() { 404 // The native OS code selects based on matching the requested parent pid. 405 // If the original parent exits, the pid may have been re-used for 406 // this newer process. 407 // Processes started by the original parent (now dead) will all have 408 // start times less than the start of this newer parent. 409 // Processes started by this newer parent will have start times equal 410 // or after this parent. 411 return children(pid).filter(ph -> startTime <= ((ProcessHandleImpl)ph).startTime); 412 } 413 414 /** 415 * Returns a Stream of the children of a process or all processes. 416 * 417 * @param pid the pid of the process for which to find the children; 418 * 0 for all processes 419 * @return a stream of ProcessHandles 420 */ children(long pid)421 static Stream<ProcessHandle> children(long pid) { 422 SecurityManager sm = System.getSecurityManager(); 423 if (sm != null) { 424 sm.checkPermission(new RuntimePermission("manageProcess")); 425 } 426 int size = 100; 427 long[] childpids = null; 428 long[] starttimes = null; 429 while (childpids == null || size > childpids.length) { 430 childpids = new long[size]; 431 starttimes = new long[size]; 432 size = getProcessPids0(pid, childpids, null, starttimes); 433 } 434 435 final long[] cpids = childpids; 436 final long[] stimes = starttimes; 437 return IntStream.range(0, size).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i])); 438 } 439 440 @Override descendants()441 public Stream<ProcessHandle> descendants() { 442 SecurityManager sm = System.getSecurityManager(); 443 if (sm != null) { 444 sm.checkPermission(new RuntimePermission("manageProcess")); 445 } 446 int size = 100; 447 long[] pids = null; 448 long[] ppids = null; 449 long[] starttimes = null; 450 while (pids == null || size > pids.length) { 451 pids = new long[size]; 452 ppids = new long[size]; 453 starttimes = new long[size]; 454 size = getProcessPids0(0, pids, ppids, starttimes); 455 } 456 457 int next = 0; // index of next process to check 458 int count = -1; // count of subprocesses scanned 459 long ppid = pid; // start looking for this parent 460 long ppStart = 0; 461 // Find the start time of the parent 462 for (int i = 0; i < size; i++) { 463 if (pids[i] == ppid) { 464 ppStart = starttimes[i]; 465 break; 466 } 467 } 468 do { 469 // Scan from next to size looking for ppid with child start time 470 // the same or later than the parent. 471 // If found, exchange it with index next 472 for (int i = next; i < size; i++) { 473 if (ppids[i] == ppid && 474 ppStart <= starttimes[i]) { 475 swap(pids, i, next); 476 swap(ppids, i, next); 477 swap(starttimes, i, next); 478 next++; 479 } 480 } 481 ppid = pids[++count]; // pick up the next pid to scan for 482 ppStart = starttimes[count]; // and its start time 483 } while (count < next); 484 485 final long[] cpids = pids; 486 final long[] stimes = starttimes; 487 return IntStream.range(0, count).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i])); 488 } 489 490 // Swap two elements in an array swap(long[] array, int x, int y)491 private static void swap(long[] array, int x, int y) { 492 long v = array[x]; 493 array[x] = array[y]; 494 array[y] = v; 495 } 496 497 @Override info()498 public ProcessHandle.Info info() { 499 return ProcessHandleImpl.Info.info(pid, startTime); 500 } 501 502 @Override compareTo(ProcessHandle other)503 public int compareTo(ProcessHandle other) { 504 return Long.compare(pid, ((ProcessHandleImpl) other).pid); 505 } 506 507 @Override toString()508 public String toString() { 509 return Long.toString(pid); 510 } 511 512 @Override hashCode()513 public int hashCode() { 514 return Long.hashCode(pid); 515 } 516 517 @Override equals(Object obj)518 public boolean equals(Object obj) { 519 if (this == obj) { 520 return true; 521 } 522 if (obj instanceof ProcessHandleImpl) { 523 ProcessHandleImpl other = (ProcessHandleImpl) obj; 524 return (pid == other.pid) && 525 (startTime == other.startTime 526 || startTime == 0 527 || other.startTime == 0); 528 } 529 return false; 530 } 531 532 /** 533 * Implementation of ProcessHandle.Info. 534 * Information snapshot about a process. 535 * The attributes of a process vary by operating system and are not available 536 * in all implementations. Additionally, information about other processes 537 * is limited by the operating system privileges of the process making the request. 538 * If a value is not available, either a {@code null} or {@code -1} is stored. 539 * The accessor methods return {@code null} if the value is not available. 540 */ 541 static class Info implements ProcessHandle.Info { 542 static { initIDs()543 initIDs(); 544 } 545 546 /** 547 * Initialization of JNI fieldIDs. 548 */ initIDs()549 private static native void initIDs(); 550 551 /** 552 * Fill in this Info instance with information about the native process. 553 * If values are not available the native code does not modify the field. 554 * @param pid of the native process 555 */ info0(long pid)556 private native void info0(long pid); 557 558 String command; 559 String commandLine; 560 String[] arguments; 561 long startTime; 562 long totalTime; 563 String user; 564 Info()565 Info() { 566 command = null; 567 commandLine = null; 568 arguments = null; 569 startTime = -1L; 570 totalTime = -1L; 571 user = null; 572 } 573 574 /** 575 * Returns the Info object with the fields from the process. 576 * Whatever fields are provided by native are returned. 577 * If the startTime of the process does not match the provided 578 * startTime then an empty Info is returned. 579 * 580 * @param pid the native process identifier 581 * @param startTime the startTime of the process being queried 582 * @return ProcessHandle.Info non-null; individual fields may be null 583 * or -1 if not available. 584 */ info(long pid, long startTime)585 public static ProcessHandle.Info info(long pid, long startTime) { 586 Info info = new Info(); 587 info.info0(pid); 588 if (startTime != info.startTime) { 589 info.command = null; 590 info.arguments = null; 591 info.startTime = -1L; 592 info.totalTime = -1L; 593 info.user = null; 594 } 595 return info; 596 } 597 598 @Override command()599 public Optional<String> command() { 600 return Optional.ofNullable(command); 601 } 602 603 @Override commandLine()604 public Optional<String> commandLine() { 605 if (command != null && arguments != null) { 606 return Optional.of(command + " " + String.join(" ", arguments)); 607 } else { 608 return Optional.ofNullable(commandLine); 609 } 610 } 611 612 @Override arguments()613 public Optional<String[]> arguments() { 614 return Optional.ofNullable(arguments); 615 } 616 617 @Override startInstant()618 public Optional<Instant> startInstant() { 619 return (startTime > 0) 620 ? Optional.of(Instant.ofEpochMilli(startTime)) 621 : Optional.empty(); 622 } 623 624 @Override totalCpuDuration()625 public Optional<Duration> totalCpuDuration() { 626 return (totalTime != -1) 627 ? Optional.of(Duration.ofNanos(totalTime)) 628 : Optional.empty(); 629 } 630 631 @Override user()632 public Optional<String> user() { 633 return Optional.ofNullable(user); 634 } 635 636 @Override toString()637 public String toString() { 638 StringBuilder sb = new StringBuilder(60); 639 sb.append('['); 640 if (user != null) { 641 sb.append("user: "); 642 sb.append(user()); 643 } 644 if (command != null) { 645 if (sb.length() != 0) sb.append(", "); 646 sb.append("cmd: "); 647 sb.append(command); 648 } 649 if (arguments != null && arguments.length > 0) { 650 if (sb.length() != 0) sb.append(", "); 651 sb.append("args: "); 652 sb.append(Arrays.toString(arguments)); 653 } 654 if (commandLine != null) { 655 if (sb.length() != 0) sb.append(", "); 656 sb.append("cmdLine: "); 657 sb.append(commandLine); 658 } 659 if (startTime > 0) { 660 if (sb.length() != 0) sb.append(", "); 661 sb.append("startTime: "); 662 sb.append(startInstant()); 663 } 664 if (totalTime != -1) { 665 if (sb.length() != 0) sb.append(", "); 666 sb.append("totalTime: "); 667 sb.append(totalCpuDuration().toString()); 668 } 669 sb.append(']'); 670 return sb.toString(); 671 } 672 } 673 } 674