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 }