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