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 }