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