1 /* 2 * Copyright (c) 2014, 2021, 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.AccessController; 29 import java.security.PrivilegedAction; 30 import java.time.Duration; 31 import java.time.Instant; 32 import java.util.Arrays; 33 import java.util.Optional; 34 import java.util.concurrent.CompletableFuture; 35 import java.util.concurrent.ConcurrentHashMap; 36 import java.util.concurrent.ConcurrentMap; 37 import java.util.concurrent.Executor; 38 import java.util.concurrent.Executors; 39 import java.util.concurrent.ThreadFactory; 40 import java.util.concurrent.ThreadLocalRandom; 41 import java.util.stream.IntStream; 42 import java.util.stream.Stream; 43 44 /** 45 * ProcessHandleImpl is the implementation of ProcessHandle. 46 * 47 * @see Process 48 * @since 9 49 */ 50 @jdk.internal.ValueBased 51 final class ProcessHandleImpl implements ProcessHandle { 52 /** 53 * Default size of stack for reaper processes. 54 */ 55 private static long REAPER_DEFAULT_STACKSIZE = 128 * 1024; 56 57 /** 58 * Return value from waitForProcessExit0 indicating the process is not a child. 59 */ 60 @Native 61 private static final int NOT_A_CHILD = -2; 62 63 /** 64 * Cache the ProcessHandle of this process. 65 */ 66 private static final ProcessHandleImpl current; 67 68 /** 69 * Map of pids to ExitCompletions. 70 */ 71 private static final ConcurrentMap<Long, ExitCompletion> 72 completions = new ConcurrentHashMap<>(); 73 74 static { initNative()75 initNative(); 76 long pid = getCurrentPid0(); 77 current = new ProcessHandleImpl(pid, isAlive0(pid)); 78 } 79 initNative()80 private static native void initNative(); 81 82 /** 83 * The thread pool of "process reaper" daemon threads. 84 */ 85 @SuppressWarnings("removal") 86 private static final Executor processReaperExecutor = 87 AccessController.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 @SuppressWarnings("removal") 242 SecurityManager sm = System.getSecurityManager(); 243 if (sm != null) { 244 sm.checkPermission(new RuntimePermission("manageProcess")); 245 } 246 long start = isAlive0(pid); 247 return (start >= 0) 248 ? Optional.of(new ProcessHandleImpl(pid, start)) 249 : Optional.empty(); 250 } 251 252 /** 253 * Returns a ProcessHandle for an existing native process known to be alive. 254 * The startTime of the process is retrieved and stored in the ProcessHandle. 255 * It does not perform a security check since it is called from ProcessImpl. 256 * @param pid of the known to exist process 257 * @return a ProcessHandle corresponding to an existing Process instance 258 */ getInternal(long pid)259 static ProcessHandleImpl getInternal(long pid) { 260 return new ProcessHandleImpl(pid, isAlive0(pid)); 261 } 262 263 /** 264 * Returns the native process ID. 265 * A {@code long} is used to be able to fit the system specific binary values 266 * for the process. 267 * 268 * @return the native process ID 269 */ 270 @Override pid()271 public long pid() { 272 return pid; 273 } 274 275 /** 276 * Returns the ProcessHandle for the current native process. 277 * 278 * @return The ProcessHandle for the OS process. 279 * @throws SecurityException if RuntimePermission("manageProcess") is not granted 280 */ current()281 public static ProcessHandleImpl current() { 282 @SuppressWarnings("removal") 283 SecurityManager sm = System.getSecurityManager(); 284 if (sm != null) { 285 sm.checkPermission(new RuntimePermission("manageProcess")); 286 } 287 return current; 288 } 289 290 /** 291 * Return the pid of the current process. 292 * 293 * @return the pid of the current process 294 */ getCurrentPid0()295 private static native long getCurrentPid0(); 296 297 /** 298 * Returns a ProcessHandle for the parent process. 299 * 300 * @return a ProcessHandle of the parent process; {@code null} is returned 301 * if the child process does not have a parent 302 * @throws SecurityException if permission is not granted by the 303 * security policy 304 */ parent()305 public Optional<ProcessHandle> parent() { 306 @SuppressWarnings("removal") 307 SecurityManager sm = System.getSecurityManager(); 308 if (sm != null) { 309 sm.checkPermission(new RuntimePermission("manageProcess")); 310 } 311 long ppid = parent0(pid, startTime); 312 if (ppid <= 0) { 313 return Optional.empty(); 314 } 315 return get(ppid); 316 } 317 318 /** 319 * Returns the parent of the native pid argument. 320 * 321 * @param pid the process id 322 * @param startTime the startTime of the process 323 * @return the parent of the native pid; if any, otherwise -1 324 */ parent0(long pid, long startTime)325 private static native long parent0(long pid, long startTime); 326 327 /** 328 * Returns the number of pids filled in to the array. 329 * @param pid if {@code pid} equals zero, then all known processes are returned; 330 * otherwise only direct child process pids are returned 331 * @param pids an allocated long array to receive the pids 332 * @param ppids an allocated long array to receive the parent pids; may be null 333 * @param starttimes an allocated long array to receive the child start times; may be null 334 * @return if greater than or equal to zero is the number of pids in the array; 335 * if greater than the length of the arrays, the arrays are too small 336 */ getProcessPids0(long pid, long[] pids, long[] ppids, long[] starttimes)337 private static native int getProcessPids0(long pid, long[] pids, 338 long[] ppids, long[] starttimes); 339 340 /** 341 * Destroy the process for this ProcessHandle. 342 * The native code checks the start time before sending the termination request. 343 * 344 * @param force {@code true} if the process should be terminated forcibly; 345 * else {@code false} for a normal termination 346 */ destroyProcess(boolean force)347 boolean destroyProcess(boolean force) { 348 if (this.equals(current)) { 349 throw new IllegalStateException("destroy of current process not allowed"); 350 } 351 return destroy0(pid, startTime, force); 352 } 353 354 /** 355 * Signal the process to terminate. 356 * The process is signaled only if its start time matches the known start time. 357 * 358 * @param pid process id to kill 359 * @param startTime the start time of the process 360 * @param forcibly true to forcibly terminate (SIGKILL vs SIGTERM) 361 * @return true if the process was signaled without error; false otherwise 362 */ destroy0(long pid, long startTime, boolean forcibly)363 private static native boolean destroy0(long pid, long startTime, boolean forcibly); 364 365 @Override destroy()366 public boolean destroy() { 367 return destroyProcess(false); 368 } 369 370 @Override destroyForcibly()371 public boolean destroyForcibly() { 372 return destroyProcess(true); 373 } 374 375 376 @Override supportsNormalTermination()377 public boolean supportsNormalTermination() { 378 return ProcessImpl.SUPPORTS_NORMAL_TERMINATION; 379 } 380 381 /** 382 * Tests whether the process represented by this {@code ProcessHandle} is alive. 383 * 384 * @return {@code true} if the process represented by this 385 * {@code ProcessHandle} object has not yet terminated. 386 * @since 9 387 */ 388 @Override isAlive()389 public boolean isAlive() { 390 long start = isAlive0(pid); 391 return (start >= 0 && (start == startTime || start == 0 || startTime == 0)); 392 } 393 394 /** 395 * Returns the process start time depending on whether the pid is alive. 396 * This must not reap the exitValue. 397 * 398 * @param pid the pid to check 399 * @return the start time in milliseconds since 1970, 400 * 0 if the start time cannot be determined, 401 * -1 if the pid does not exist. 402 */ isAlive0(long pid)403 private static native long isAlive0(long pid); 404 405 @Override children()406 public Stream<ProcessHandle> children() { 407 // The native OS code selects based on matching the requested parent pid. 408 // If the original parent exits, the pid may have been re-used for 409 // this newer process. 410 // Processes started by the original parent (now dead) will all have 411 // start times less than the start of this newer parent. 412 // Processes started by this newer parent will have start times equal 413 // or after this parent. 414 return children(pid).filter(ph -> startTime <= ((ProcessHandleImpl)ph).startTime); 415 } 416 417 /** 418 * Returns a Stream of the children of a process or all processes. 419 * 420 * @param pid the pid of the process for which to find the children; 421 * 0 for all processes 422 * @return a stream of ProcessHandles 423 */ children(long pid)424 static Stream<ProcessHandle> children(long pid) { 425 @SuppressWarnings("removal") 426 SecurityManager sm = System.getSecurityManager(); 427 if (sm != null) { 428 sm.checkPermission(new RuntimePermission("manageProcess")); 429 } 430 int size = 100; 431 long[] childpids = null; 432 long[] starttimes = null; 433 while (childpids == null || size > childpids.length) { 434 childpids = new long[size]; 435 starttimes = new long[size]; 436 size = getProcessPids0(pid, childpids, null, starttimes); 437 } 438 439 final long[] cpids = childpids; 440 final long[] stimes = starttimes; 441 return IntStream.range(0, size).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i])); 442 } 443 444 @Override descendants()445 public Stream<ProcessHandle> descendants() { 446 @SuppressWarnings("removal") 447 SecurityManager sm = System.getSecurityManager(); 448 if (sm != null) { 449 sm.checkPermission(new RuntimePermission("manageProcess")); 450 } 451 int size = 100; 452 long[] pids = null; 453 long[] ppids = null; 454 long[] starttimes = null; 455 while (pids == null || size > pids.length) { 456 pids = new long[size]; 457 ppids = new long[size]; 458 starttimes = new long[size]; 459 size = getProcessPids0(0, pids, ppids, starttimes); 460 } 461 462 int next = 0; // index of next process to check 463 int count = -1; // count of subprocesses scanned 464 long ppid = pid; // start looking for this parent 465 long ppStart = 0; 466 // Find the start time of the parent 467 for (int i = 0; i < size; i++) { 468 if (pids[i] == ppid) { 469 ppStart = starttimes[i]; 470 break; 471 } 472 } 473 do { 474 // Scan from next to size looking for ppid with child start time 475 // the same or later than the parent. 476 // If found, exchange it with index next 477 for (int i = next; i < size; i++) { 478 if (ppids[i] == ppid && 479 ppStart <= starttimes[i]) { 480 swap(pids, i, next); 481 swap(ppids, i, next); 482 swap(starttimes, i, next); 483 next++; 484 } 485 } 486 ppid = pids[++count]; // pick up the next pid to scan for 487 ppStart = starttimes[count]; // and its start time 488 } while (count < next); 489 490 final long[] cpids = pids; 491 final long[] stimes = starttimes; 492 return IntStream.range(0, count).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i])); 493 } 494 495 // Swap two elements in an array swap(long[] array, int x, int y)496 private static void swap(long[] array, int x, int y) { 497 long v = array[x]; 498 array[x] = array[y]; 499 array[y] = v; 500 } 501 502 @Override info()503 public ProcessHandle.Info info() { 504 return ProcessHandleImpl.Info.info(pid, startTime); 505 } 506 507 @Override compareTo(ProcessHandle other)508 public int compareTo(ProcessHandle other) { 509 return Long.compare(pid, ((ProcessHandleImpl) other).pid); 510 } 511 512 @Override toString()513 public String toString() { 514 return Long.toString(pid); 515 } 516 517 @Override hashCode()518 public int hashCode() { 519 return Long.hashCode(pid); 520 } 521 522 @Override equals(Object obj)523 public boolean equals(Object obj) { 524 if (this == obj) { 525 return true; 526 } 527 return (obj instanceof ProcessHandleImpl other) 528 && (pid == other.pid) 529 && (startTime == other.startTime || startTime == 0 || other.startTime == 0); 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