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