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 1.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 1.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         return children(pid);
 363     }
 364 
 365     /**
 366      * Returns a Stream of the children of a process or all processes.
 367      *
 368      * @param pid the pid of the process for which to find the children;
 369      *            0 for all processes
 370      * @return a stream of ProcessHandles
 371      */
 372     static Stream<ProcessHandle> children(long pid) {
 373         SecurityManager sm = System.getSecurityManager();
 374         if (sm != null) {
 375             sm.checkPermission(new RuntimePermission("manageProcess"));
 376         }
 377         int size = 100;
 378         long[] childpids = null;
 379         long[] starttimes = null;
 380         while (childpids == null || size > childpids.length) {
 381             childpids = new long[size];
 382             starttimes = new long[size];
 383             size = getProcessPids0(pid, childpids, null, starttimes);
 384         }
 385 
 386         final long[] cpids = childpids;
 387         final long[] stimes = starttimes;
 388         return IntStream.range(0, size).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
 389     }
 390 
 391     @Override
 392     public Stream<ProcessHandle> allChildren() {
 393         SecurityManager sm = System.getSecurityManager();
 394         if (sm != null) {
 395             sm.checkPermission(new RuntimePermission("manageProcess"));
 396         }
 397         int size = 100;
 398         long[] pids = null;
 399         long[] ppids = null;
 400         long[] starttimes = null;
 401         while (pids == null || size > pids.length) {
 402             pids = new long[size];
 403             ppids = new long[size];
 404             starttimes = new long[size];
 405             size = getProcessPids0(0, pids, ppids, starttimes);
 406         }
 407 
 408         int next = 0;       // index of next process to check
 409         int count = -1;     // count of subprocesses scanned
 410         long ppid = pid;    // start looking for this parent
 411         do {
 412             // Scan from next to size looking for ppid
 413             // if found, exchange it to index next
 414             for (int i = next; i < size; i++) {
 415                 if (ppids[i] == ppid) {
 416                     swap(pids, i, next);
 417                     swap(ppids, i, next);
 418                     swap(starttimes, i, next);
 419                     next++;
 420                 }
 421             }
 422             ppid = pids[++count];   // pick up the next pid to scan for
 423         } while (count < next);
 424 
 425         final long[] cpids = pids;
 426         final long[] stimes = starttimes;
 427         return IntStream.range(0, count).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
 428     }
 429 
 430     // Swap two elements in an array
 431     private static void swap(long[] array, int x, int y) {
 432         long v = array[x];
 433         array[x] = array[y];
 434         array[y] = v;
 435     }
 436 
 437     @Override
 438     public ProcessHandle.Info info() {
 439         return ProcessHandleImpl.Info.info(pid, startTime);
 440     }
 441 
 442     @Override
 443     public int compareTo(ProcessHandle other) {
 444         return Long.compare(pid, ((ProcessHandleImpl) other).pid);
 445     }
 446 
 447     @Override
 448     public String toString() {
 449         return Long.toString(pid);
 450     }
 451 
 452     @Override
 453     public int hashCode() {
 454         return Long.hashCode(pid);
 455     }
 456 
 457     @Override
 458     public boolean equals(Object obj) {
 459         if (this == obj) {
 460             return true;
 461         }
 462         if (obj instanceof ProcessHandleImpl) {
 463             ProcessHandleImpl other = (ProcessHandleImpl) obj;
 464             return (pid == other.pid) &&
 465                     (startTime == other.startTime
 466                         || startTime == 0
 467                         || other.startTime == 0);
 468         }
 469         return false;
 470     }
 471 
 472     /**
 473      * Implementation of ProcessHandle.Info.
 474      * Information snapshot about a process.
 475      * The attributes of a process vary by operating system and not available
 476      * in all implementations.  Additionally, information about other processes
 477      * is limited by the operating system privileges of the process making the request.
 478      * If a value is not available, either a {@code null} or {@code -1} is stored.
 479      * The accessor methods return {@code null} if the value is not available.
 480      */
 481     static class Info implements ProcessHandle.Info {
 482         static {
 483             initIDs();
 484         }
 485 
 486         /**
 487          * Initialization of JNI fieldIDs.
 488          */
 489         private static native void initIDs();
 490 
 491         /**
 492          * Fill in this Info instance with information about the native process.
 493          * If values are not available the native code does not modify the field.
 494          * @param pid  of the native process
 495          */
 496         private native void info0(long pid);
 497 
 498         String command;
 499         String[] arguments;
 500         long startTime;
 501         long totalTime;
 502         String user;
 503 
 504         Info() {
 505             command = null;
 506             arguments = null;
 507             startTime = -1L;
 508             totalTime = -1L;
 509             user = null;
 510         }
 511 
 512         /**
 513          * Returns the Info object with the fields from the process.
 514          * Whatever fields are provided by native are returned.
 515          * If the startTime of the process does not match the provided
 516          * startTime then an empty Info is returned.
 517          *
 518          * @param pid the native process identifier
 519          * @param startTime the startTime of the process being queried
 520          * @return ProcessHandle.Info non-null; individual fields may be null
 521          *          or -1 if not available.
 522          */
 523         public static ProcessHandle.Info info(long pid, long startTime) {
 524             Info info = new Info();
 525             info.info0(pid);
 526             if (startTime != info.startTime) {
 527                 info.command = null;
 528                 info.arguments = null;
 529                 info.startTime = -1L;
 530                 info.totalTime = -1L;
 531                 info.user = null;
 532             }
 533             return info;
 534         }
 535 
 536         @Override
 537         public Optional<String> command() {
 538             return Optional.ofNullable(command);
 539         }
 540 
 541         @Override
 542         public Optional<String[]> arguments() {
 543             return Optional.ofNullable(arguments);
 544         }
 545 
 546         @Override
 547         public Optional<Instant> startInstant() {
 548             return (startTime > 0)
 549                     ? Optional.of(Instant.ofEpochMilli(startTime))
 550                     : Optional.empty();
 551         }
 552 
 553         @Override
 554         public Optional<Duration> totalCpuDuration() {
 555             return (totalTime != -1)
 556                     ? Optional.of(Duration.ofNanos(totalTime))
 557                     : Optional.empty();
 558         }
 559 
 560         @Override
 561         public Optional<String> user() {
 562             return Optional.ofNullable(user);
 563         }
 564 
 565         @Override
 566         public String toString() {
 567             StringBuilder sb = new StringBuilder(60);
 568             sb.append('[');
 569             if (user != null) {
 570                 sb.append("user: ");
 571                 sb.append(user());
 572             }
 573             if (command != null) {
 574                 if (sb.length() != 0) sb.append(", ");
 575                 sb.append("cmd: ");
 576                 sb.append(command);
 577             }
 578             if (arguments != null && arguments.length > 0) {
 579                 if (sb.length() != 0) sb.append(", ");
 580                 sb.append("args: ");
 581                 sb.append(Arrays.toString(arguments));
 582             }
 583             if (startTime > 0) {
 584                 if (sb.length() != 0) sb.append(", ");
 585                 sb.append("startTime: ");
 586                 sb.append(startInstant());
 587             }
 588             if (totalTime != -1) {
 589                 if (sb.length() != 0) sb.append(", ");
 590                 sb.append("totalTime: ");
 591                 sb.append(totalCpuDuration().toString());
 592             }
 593             sb.append(']');
 594             return sb.toString();
 595         }
 596     }
 597 }