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