1 /*
   2  * Copyright (c) 2003, 2018, 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 
  26 package java.lang;
  27 
  28 import java.lang.ProcessBuilder.Redirect;
  29 import java.io.BufferedInputStream;
  30 import java.io.BufferedOutputStream;
  31 import java.io.ByteArrayInputStream;
  32 import java.io.FileDescriptor;
  33 import java.io.FileInputStream;
  34 import java.io.FileOutputStream;
  35 import java.io.IOException;
  36 import java.io.InputStream;
  37 import java.io.OutputStream;
  38 import java.util.Arrays;
  39 import java.util.EnumSet;
  40 import java.util.Locale;
  41 import java.util.Set;
  42 import java.util.concurrent.CompletableFuture;
  43 import java.util.concurrent.TimeUnit;
  44 import java.security.AccessController;
  45 import static java.security.AccessController.doPrivileged;
  46 import java.security.PrivilegedAction;
  47 import java.security.PrivilegedActionException;
  48 import java.security.PrivilegedExceptionAction;
  49 import java.util.Properties;
  50 import jdk.internal.access.JavaIOFileDescriptorAccess;
  51 import jdk.internal.access.SharedSecrets;
  52 import jdk.internal.util.StaticProperty;
  53 import sun.security.action.GetPropertyAction;
  54 
  55 /**
  56  * java.lang.Process subclass in the UNIX environment.
  57  *
  58  * @author Mario Wolczko and Ross Knippel.
  59  * @author Konstantin Kladko (ported to Linux and Bsd)
  60  * @author Martin Buchholz
  61  * @author Volker Simonis (ported to AIX)
  62  * @since   1.5
  63  */
  64 final class ProcessImpl extends Process {
  65     private static final JavaIOFileDescriptorAccess fdAccess
  66         = SharedSecrets.getJavaIOFileDescriptorAccess();
  67 
  68     // Linux platforms support a normal (non-forcible) kill signal.
  69     static final boolean SUPPORTS_NORMAL_TERMINATION = true;
  70 
  71     private final int pid;
  72     private final ProcessHandleImpl processHandle;
  73     private int exitcode;
  74     private boolean hasExited;
  75 
  76     private /* final */ OutputStream stdin;
  77     private /* final */ InputStream  stdout;
  78     private /* final */ InputStream  stderr;
  79 
  80     // only used on Solaris
  81     private /* final */ DeferredCloseInputStream stdout_inner_stream;
  82 
  83     private static enum LaunchMechanism {
  84         // order IS important!
  85         FORK,
  86         POSIX_SPAWN,
  87         VFORK
  88     }
  89 
  90     private static enum Platform {
  91 
  92         LINUX(LaunchMechanism.VFORK, LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  93 
  94         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  95 
  96         SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  97 
  98         AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
  99 
 100         final LaunchMechanism defaultLaunchMechanism;
 101         final Set<LaunchMechanism> validLaunchMechanisms;
 102 
 103         Platform(LaunchMechanism ... launchMechanisms) {
 104             this.defaultLaunchMechanism = launchMechanisms[0];
 105             this.validLaunchMechanisms =
 106                 EnumSet.copyOf(Arrays.asList(launchMechanisms));
 107         }
 108 
 109         @SuppressWarnings("fallthrough")
 110         private String helperPath(String javahome, String osArch) {
 111             switch (this) {
 112                 case SOLARIS:
 113                     // fall through...
 114                 case LINUX:
 115                 case AIX:
 116                 case BSD:
 117                     return javahome + "/lib/jspawnhelper";
 118 
 119                 default:
 120                     throw new AssertionError("Unsupported platform: " + this);
 121             }
 122         }
 123 
 124         String helperPath() {
 125             Properties props = GetPropertyAction.privilegedGetProperties();
 126             return helperPath(StaticProperty.javaHome(),
 127                               props.getProperty("os.arch"));
 128         }
 129 
 130         LaunchMechanism launchMechanism() {
 131             return AccessController.doPrivileged(
 132                 (PrivilegedAction<LaunchMechanism>) () -> {
 133                     String s = System.getProperty(
 134                         "jdk.lang.Process.launchMechanism");
 135                     LaunchMechanism lm;
 136                     if (s == null) {
 137                         lm = defaultLaunchMechanism;
 138                         s = lm.name().toLowerCase(Locale.ENGLISH);
 139                     } else {
 140                         try {
 141                             lm = LaunchMechanism.valueOf(
 142                                 s.toUpperCase(Locale.ENGLISH));
 143                         } catch (IllegalArgumentException e) {
 144                             lm = null;
 145                         }
 146                     }
 147                     if (lm == null || !validLaunchMechanisms.contains(lm)) {
 148                         throw new Error(
 149                             s + " is not a supported " +
 150                             "process launch mechanism on this platform."
 151                         );
 152                     }
 153                     return lm;
 154                 }
 155             );
 156         }
 157 
 158         static Platform get() {
 159             String osName = GetPropertyAction.privilegedGetProperty("os.name");
 160 
 161             if (osName.equals("Linux")) { return LINUX; }
 162             if (osName.contains("OS X")) { return BSD; }
 163             if (osName.equals("SunOS")) { return SOLARIS; }
 164             if (osName.equals("AIX")) { return AIX; }
 165 
 166             throw new Error(osName + " is not a supported OS platform.");
 167         }
 168     }
 169 
 170     private static final Platform platform = Platform.get();
 171     private static final LaunchMechanism launchMechanism = platform.launchMechanism();
 172     private static final byte[] helperpath = toCString(platform.helperPath());
 173 
 174     private static byte[] toCString(String s) {
 175         if (s == null)
 176             return null;
 177         byte[] bytes = s.getBytes();
 178         byte[] result = new byte[bytes.length + 1];
 179         System.arraycopy(bytes, 0,
 180                          result, 0,
 181                          bytes.length);
 182         result[result.length-1] = (byte)0;
 183         return result;
 184     }
 185 
 186     // Only for use by ProcessBuilder.start()
 187     static Process start(String[] cmdarray,
 188                          java.util.Map<String,String> environment,
 189                          String dir,
 190                          ProcessBuilder.Redirect[] redirects,
 191                          boolean redirectErrorStream)
 192             throws IOException
 193     {
 194         assert cmdarray != null && cmdarray.length > 0;
 195 
 196         // Convert arguments to a contiguous block; it's easier to do
 197         // memory management in Java than in C.
 198         byte[][] args = new byte[cmdarray.length-1][];
 199         int size = args.length; // For added NUL bytes
 200         for (int i = 0; i < args.length; i++) {
 201             args[i] = cmdarray[i+1].getBytes();
 202             size += args[i].length;
 203         }
 204         byte[] argBlock = new byte[size];
 205         int i = 0;
 206         for (byte[] arg : args) {
 207             System.arraycopy(arg, 0, argBlock, i, arg.length);
 208             i += arg.length + 1;
 209             // No need to write NUL bytes explicitly
 210         }
 211 
 212         int[] envc = new int[1];
 213         byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
 214 
 215         int[] std_fds;
 216 
 217         FileInputStream  f0 = null;
 218         FileOutputStream f1 = null;
 219         FileOutputStream f2 = null;
 220 
 221         try {
 222             boolean forceNullOutputStream = false;
 223             if (redirects == null) {
 224                 std_fds = new int[] { -1, -1, -1 };
 225             } else {
 226                 std_fds = new int[3];
 227 
 228                 if (redirects[0] == Redirect.PIPE) {
 229                     std_fds[0] = -1;
 230                 } else if (redirects[0] == Redirect.INHERIT) {
 231                     std_fds[0] = 0;
 232                 } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
 233                     std_fds[0] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd());
 234                 } else {
 235                     f0 = new FileInputStream(redirects[0].file());
 236                     std_fds[0] = fdAccess.get(f0.getFD());
 237                 }
 238 
 239                 if (redirects[1] == Redirect.PIPE) {
 240                     std_fds[1] = -1;
 241                 } else if (redirects[1] == Redirect.INHERIT) {
 242                     std_fds[1] = 1;
 243                 } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
 244                     std_fds[1] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
 245                     // Force getInputStream to return a null stream,
 246                     // the fd is directly assigned to the next process.
 247                     forceNullOutputStream = true;
 248                 } else {
 249                     f1 = new FileOutputStream(redirects[1].file(),
 250                             redirects[1].append());
 251                     std_fds[1] = fdAccess.get(f1.getFD());
 252                 }
 253 
 254                 if (redirects[2] == Redirect.PIPE) {
 255                     std_fds[2] = -1;
 256                 } else if (redirects[2] == Redirect.INHERIT) {
 257                     std_fds[2] = 2;
 258                 } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
 259                     std_fds[2] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
 260                 } else {
 261                     f2 = new FileOutputStream(redirects[2].file(),
 262                             redirects[2].append());
 263                     std_fds[2] = fdAccess.get(f2.getFD());
 264                 }
 265             }
 266 
 267             Process p = new ProcessImpl
 268                     (toCString(cmdarray[0]),
 269                             argBlock, args.length,
 270                             envBlock, envc[0],
 271                             toCString(dir),
 272                             std_fds,
 273                             forceNullOutputStream,
 274                             redirectErrorStream);
 275             if (redirects != null) {
 276                 // Copy the fd's if they are to be redirected to another process
 277                 if (std_fds[0] >= 0 &&
 278                         redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
 279                     fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), std_fds[0]);
 280                 }
 281                 if (std_fds[1] >= 0 &&
 282                         redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
 283                     fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), std_fds[1]);
 284                 }
 285                 if (std_fds[2] >= 0 &&
 286                         redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
 287                     fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), std_fds[2]);
 288                 }
 289             }
 290             return p;
 291         } finally {
 292             // In theory, close() can throw IOException
 293             // (although it is rather unlikely to happen here)
 294             try { if (f0 != null) f0.close(); }
 295             finally {
 296                 try { if (f1 != null) f1.close(); }
 297                 finally { if (f2 != null) f2.close(); }
 298             }
 299         }
 300     }
 301 
 302 
 303     /**
 304      * Creates a process. Depending on the {@code mode} flag, this is done by
 305      * one of the following mechanisms:
 306      * <pre>
 307      *   1 - fork(2) and exec(2)
 308      *   2 - posix_spawn(3P)
 309      *   3 - vfork(2) and exec(2)
 310      * </pre>
 311      * @param fds an array of three file descriptors.
 312      *        Indexes 0, 1, and 2 correspond to standard input,
 313      *        standard output and standard error, respectively.  On
 314      *        input, a value of -1 means to create a pipe to connect
 315      *        child and parent processes.  On output, a value which
 316      *        is not -1 is the parent pipe fd corresponding to the
 317      *        pipe which has been created.  An element of this array
 318      *        is -1 on input if and only if it is <em>not</em> -1 on
 319      *        output.
 320      * @return the pid of the subprocess
 321      */
 322     private native int forkAndExec(int mode, byte[] helperpath,
 323                                    byte[] prog,
 324                                    byte[] argBlock, int argc,
 325                                    byte[] envBlock, int envc,
 326                                    byte[] dir,
 327                                    int[] fds,
 328                                    boolean redirectErrorStream)
 329         throws IOException;
 330 
 331     private ProcessImpl(final byte[] prog,
 332                 final byte[] argBlock, final int argc,
 333                 final byte[] envBlock, final int envc,
 334                 final byte[] dir,
 335                 final int[] fds,
 336                 final boolean forceNullOutputStream,
 337                 final boolean redirectErrorStream)
 338             throws IOException {
 339 
 340         pid = forkAndExec(launchMechanism.ordinal() + 1,
 341                           helperpath,
 342                           prog,
 343                           argBlock, argc,
 344                           envBlock, envc,
 345                           dir,
 346                           fds,
 347                           redirectErrorStream);
 348         processHandle = ProcessHandleImpl.getInternal(pid);
 349 
 350         try {
 351             doPrivileged((PrivilegedExceptionAction<Void>) () -> {
 352                 initStreams(fds, forceNullOutputStream);
 353                 return null;
 354             });
 355         } catch (PrivilegedActionException ex) {
 356             throw (IOException) ex.getException();
 357         }
 358     }
 359 
 360     static FileDescriptor newFileDescriptor(int fd) {
 361         FileDescriptor fileDescriptor = new FileDescriptor();
 362         fdAccess.set(fileDescriptor, fd);
 363         return fileDescriptor;
 364     }
 365 
 366     /**
 367      * Initialize the streams from the file descriptors.
 368      * @param fds array of stdin, stdout, stderr fds
 369      * @param forceNullOutputStream true if the stdout is being directed to
 370      *        a subsequent process. The stdout stream should be a null output stream .
 371      * @throws IOException
 372      */
 373     void initStreams(int[] fds, boolean forceNullOutputStream) throws IOException {
 374         switch (platform) {
 375             case LINUX:
 376             case BSD:
 377                 stdin = (fds[0] == -1) ?
 378                         ProcessBuilder.NullOutputStream.INSTANCE :
 379                         new ProcessPipeOutputStream(fds[0]);
 380 
 381                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
 382                          ProcessBuilder.NullInputStream.INSTANCE :
 383                          new ProcessPipeInputStream(fds[1]);
 384 
 385                 stderr = (fds[2] == -1) ?
 386                          ProcessBuilder.NullInputStream.INSTANCE :
 387                          new ProcessPipeInputStream(fds[2]);
 388 
 389                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 390                     synchronized (this) {
 391                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 392                         this.hasExited = true;
 393                         this.notifyAll();
 394                     }
 395 
 396                     if (stdout instanceof ProcessPipeInputStream)
 397                         ((ProcessPipeInputStream) stdout).processExited();
 398 
 399                     if (stderr instanceof ProcessPipeInputStream)
 400                         ((ProcessPipeInputStream) stderr).processExited();
 401 
 402                     if (stdin instanceof ProcessPipeOutputStream)
 403                         ((ProcessPipeOutputStream) stdin).processExited();
 404 
 405                     return null;
 406                 });
 407                 break;
 408 
 409             case SOLARIS:
 410                 stdin = (fds[0] == -1) ?
 411                         ProcessBuilder.NullOutputStream.INSTANCE :
 412                         new BufferedOutputStream(
 413                             new FileOutputStream(newFileDescriptor(fds[0])));
 414 
 415                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
 416                          ProcessBuilder.NullInputStream.INSTANCE :
 417                          new BufferedInputStream(
 418                              stdout_inner_stream =
 419                                  new DeferredCloseInputStream(
 420                                      newFileDescriptor(fds[1])));
 421 
 422                 stderr = (fds[2] == -1) ?
 423                          ProcessBuilder.NullInputStream.INSTANCE :
 424                          new DeferredCloseInputStream(newFileDescriptor(fds[2]));
 425 
 426                 /*
 427                  * For each subprocess forked a corresponding reaper task
 428                  * is submitted.  That task is the only thread which waits
 429                  * for the subprocess to terminate and it doesn't hold any
 430                  * locks while doing so.  This design allows waitFor() and
 431                  * exitStatus() to be safely executed in parallel (and they
 432                  * need no native code).
 433                  */
 434                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 435                     synchronized (this) {
 436                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 437                         this.hasExited = true;
 438                         this.notifyAll();
 439                     }
 440                     return null;
 441                 });
 442                 break;
 443 
 444             case AIX:
 445                 stdin = (fds[0] == -1) ?
 446                         ProcessBuilder.NullOutputStream.INSTANCE :
 447                         new ProcessPipeOutputStream(fds[0]);
 448 
 449                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
 450                          ProcessBuilder.NullInputStream.INSTANCE :
 451                          new DeferredCloseProcessPipeInputStream(fds[1]);
 452 
 453                 stderr = (fds[2] == -1) ?
 454                          ProcessBuilder.NullInputStream.INSTANCE :
 455                          new DeferredCloseProcessPipeInputStream(fds[2]);
 456 
 457                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 458                     synchronized (this) {
 459                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 460                         this.hasExited = true;
 461                         this.notifyAll();
 462                     }
 463 
 464                     if (stdout instanceof DeferredCloseProcessPipeInputStream)
 465                         ((DeferredCloseProcessPipeInputStream) stdout).processExited();
 466 
 467                     if (stderr instanceof DeferredCloseProcessPipeInputStream)
 468                         ((DeferredCloseProcessPipeInputStream) stderr).processExited();
 469 
 470                     if (stdin instanceof ProcessPipeOutputStream)
 471                         ((ProcessPipeOutputStream) stdin).processExited();
 472 
 473                     return null;
 474                 });
 475                 break;
 476 
 477             default: throw new AssertionError("Unsupported platform: " + platform);
 478         }
 479     }
 480 
 481     public OutputStream getOutputStream() {
 482         return stdin;
 483     }
 484 
 485     public InputStream getInputStream() {
 486         return stdout;
 487     }
 488 
 489     public InputStream getErrorStream() {
 490         return stderr;
 491     }
 492 
 493     public synchronized int waitFor() throws InterruptedException {
 494         while (!hasExited) {
 495             wait();
 496         }
 497         return exitcode;
 498     }
 499 
 500     @Override
 501     public synchronized boolean waitFor(long timeout, TimeUnit unit)
 502         throws InterruptedException
 503     {
 504         long remainingNanos = unit.toNanos(timeout);    // throw NPE before other conditions
 505         if (hasExited) return true;
 506         if (timeout <= 0) return false;
 507 
 508         long deadline = System.nanoTime() + remainingNanos;
 509         do {
 510             // Round up to next millisecond
 511             wait(TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L));
 512             if (hasExited) {
 513                 return true;
 514             }
 515             remainingNanos = deadline - System.nanoTime();
 516         } while (remainingNanos > 0);
 517         return hasExited;
 518     }
 519 
 520     public synchronized int exitValue() {
 521         if (!hasExited) {
 522             throw new IllegalThreadStateException("process hasn't exited");
 523         }
 524         return exitcode;
 525     }
 526 
 527     private void destroy(boolean force) {
 528         switch (platform) {
 529             case LINUX:
 530             case BSD:
 531             case AIX:
 532                 // There is a risk that pid will be recycled, causing us to
 533                 // kill the wrong process!  So we only terminate processes
 534                 // that appear to still be running.  Even with this check,
 535                 // there is an unavoidable race condition here, but the window
 536                 // is very small, and OSes try hard to not recycle pids too
 537                 // soon, so this is quite safe.
 538                 synchronized (this) {
 539                     if (!hasExited)
 540                         processHandle.destroyProcess(force);
 541                 }
 542                 try { stdin.close();  } catch (IOException ignored) {}
 543                 try { stdout.close(); } catch (IOException ignored) {}
 544                 try { stderr.close(); } catch (IOException ignored) {}
 545                 break;
 546 
 547             case SOLARIS:
 548                 // There is a risk that pid will be recycled, causing us to
 549                 // kill the wrong process!  So we only terminate processes
 550                 // that appear to still be running.  Even with this check,
 551                 // there is an unavoidable race condition here, but the window
 552                 // is very small, and OSes try hard to not recycle pids too
 553                 // soon, so this is quite safe.
 554                 synchronized (this) {
 555                     if (!hasExited)
 556                         processHandle.destroyProcess(force);
 557                     try {
 558                         stdin.close();
 559                         if (stdout_inner_stream != null)
 560                             stdout_inner_stream.closeDeferred(stdout);
 561                         if (stderr instanceof DeferredCloseInputStream)
 562                             ((DeferredCloseInputStream) stderr)
 563                                 .closeDeferred(stderr);
 564                     } catch (IOException e) {
 565                         // ignore
 566                     }
 567                 }
 568                 break;
 569 
 570             default: throw new AssertionError("Unsupported platform: " + platform);
 571         }
 572     }
 573 
 574     @Override
 575     public CompletableFuture<Process> onExit() {
 576         return ProcessHandleImpl.completion(pid, false)
 577                 .handleAsync((unusedExitStatus, unusedThrowable) -> {
 578                     boolean interrupted = false;
 579                     while (true) {
 580                         // Ensure that the concurrent task setting the exit status has completed
 581                         try {
 582                             waitFor();
 583                             break;
 584                         } catch (InterruptedException ie) {
 585                             interrupted = true;
 586                         }
 587                     }
 588                     if (interrupted) {
 589                         Thread.currentThread().interrupt();
 590                     }
 591                     return this;
 592                 });
 593     }
 594 
 595     @Override
 596     public ProcessHandle toHandle() {
 597         SecurityManager sm = System.getSecurityManager();
 598         if (sm != null) {
 599             sm.checkPermission(new RuntimePermission("manageProcess"));
 600         }
 601         return processHandle;
 602     }
 603 
 604     @Override
 605     public boolean supportsNormalTermination() {
 606         return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
 607     }
 608 
 609     @Override
 610     public void destroy() {
 611         destroy(false);
 612     }
 613 
 614     @Override
 615     public Process destroyForcibly() {
 616         destroy(true);
 617         return this;
 618     }
 619 
 620     @Override
 621     public long pid() {
 622         return pid;
 623     }
 624 
 625     @Override
 626     public synchronized boolean isAlive() {
 627         return !hasExited;
 628     }
 629 
 630     /**
 631      * The {@code toString} method returns a string consisting of
 632      * the native process ID of the process and the exit value of the process.
 633      *
 634      * @return a string representation of the object.
 635      */
 636     @Override
 637     public String toString() {
 638         return new StringBuilder("Process[pid=").append(pid)
 639                 .append(", exitValue=").append(hasExited ? exitcode : "\"not exited\"")
 640                 .append("]").toString();
 641     }
 642 
 643     private static native void init();
 644 
 645     static {
 646         init();
 647     }
 648 
 649     /**
 650      * A buffered input stream for a subprocess pipe file descriptor
 651      * that allows the underlying file descriptor to be reclaimed when
 652      * the process exits, via the processExited hook.
 653      *
 654      * This is tricky because we do not want the user-level InputStream to be
 655      * closed until the user invokes close(), and we need to continue to be
 656      * able to read any buffered data lingering in the OS pipe buffer.
 657      */
 658     private static class ProcessPipeInputStream extends BufferedInputStream {
 659         private final Object closeLock = new Object();
 660 
 661         ProcessPipeInputStream(int fd) {
 662             super(new PipeInputStream(newFileDescriptor(fd)));
 663         }
 664         private static byte[] drainInputStream(InputStream in)
 665                 throws IOException {
 666             int n = 0;
 667             int j;
 668             byte[] a = null;
 669             while ((j = in.available()) > 0) {
 670                 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
 671                 n += in.read(a, n, j);
 672             }
 673             return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
 674         }
 675 
 676         /** Called by the process reaper thread when the process exits. */
 677         synchronized void processExited() {
 678             synchronized (closeLock) {
 679                 try {
 680                     InputStream in = this.in;
 681                     // this stream is closed if and only if: in == null
 682                     if (in != null) {
 683                         byte[] stragglers = drainInputStream(in);
 684                         in.close();
 685                         this.in = (stragglers == null) ?
 686                             ProcessBuilder.NullInputStream.INSTANCE :
 687                             new ByteArrayInputStream(stragglers);
 688                     }
 689                 } catch (IOException ignored) {}
 690             }
 691         }
 692 
 693         @Override
 694         public void close() throws IOException {
 695             // BufferedInputStream#close() is not synchronized unlike most other
 696             // methods. Synchronizing helps avoid race with processExited().
 697             synchronized (closeLock) {
 698                 super.close();
 699             }
 700         }
 701     }
 702 
 703     /**
 704      * A buffered output stream for a subprocess pipe file descriptor
 705      * that allows the underlying file descriptor to be reclaimed when
 706      * the process exits, via the processExited hook.
 707      */
 708     private static class ProcessPipeOutputStream extends BufferedOutputStream {
 709         ProcessPipeOutputStream(int fd) {
 710             super(new FileOutputStream(newFileDescriptor(fd)));
 711         }
 712 
 713         /** Called by the process reaper thread when the process exits. */
 714         synchronized void processExited() {
 715             OutputStream out = this.out;
 716             if (out != null) {
 717                 try {
 718                     out.close();
 719                 } catch (IOException ignored) {
 720                     // We know of no reason to get an IOException, but if
 721                     // we do, there's nothing else to do but carry on.
 722                 }
 723                 this.out = ProcessBuilder.NullOutputStream.INSTANCE;
 724             }
 725         }
 726     }
 727 
 728     // A FileInputStream that supports the deferment of the actual close
 729     // operation until the last pending I/O operation on the stream has
 730     // finished.  This is required on Solaris because we must close the stdin
 731     // and stdout streams in the destroy method in order to reclaim the
 732     // underlying file descriptors.  Doing so, however, causes any thread
 733     // currently blocked in a read on one of those streams to receive an
 734     // IOException("Bad file number"), which is incompatible with historical
 735     // behavior.  By deferring the close we allow any pending reads to see -1
 736     // (EOF) as they did before.
 737     //
 738     private static class DeferredCloseInputStream extends PipeInputStream {
 739         DeferredCloseInputStream(FileDescriptor fd) {
 740             super(fd);
 741         }
 742 
 743         private Object lock = new Object();     // For the following fields
 744         private boolean closePending = false;
 745         private int useCount = 0;
 746         private InputStream streamToClose;
 747 
 748         private void raise() {
 749             synchronized (lock) {
 750                 useCount++;
 751             }
 752         }
 753 
 754         private void lower() throws IOException {
 755             synchronized (lock) {
 756                 useCount--;
 757                 if (useCount == 0 && closePending) {
 758                     streamToClose.close();
 759                 }
 760             }
 761         }
 762 
 763         // stc is the actual stream to be closed; it might be this object, or
 764         // it might be an upstream object for which this object is downstream.
 765         //
 766         private void closeDeferred(InputStream stc) throws IOException {
 767             synchronized (lock) {
 768                 if (useCount == 0) {
 769                     stc.close();
 770                 } else {
 771                     closePending = true;
 772                     streamToClose = stc;
 773                 }
 774             }
 775         }
 776 
 777         public void close() throws IOException {
 778             synchronized (lock) {
 779                 useCount = 0;
 780                 closePending = false;
 781             }
 782             super.close();
 783         }
 784 
 785         public int read() throws IOException {
 786             raise();
 787             try {
 788                 return super.read();
 789             } finally {
 790                 lower();
 791             }
 792         }
 793 
 794         public int read(byte[] b) throws IOException {
 795             raise();
 796             try {
 797                 return super.read(b);
 798             } finally {
 799                 lower();
 800             }
 801         }
 802 
 803         public int read(byte[] b, int off, int len) throws IOException {
 804             raise();
 805             try {
 806                 return super.read(b, off, len);
 807             } finally {
 808                 lower();
 809             }
 810         }
 811 
 812         public long skip(long n) throws IOException {
 813             raise();
 814             try {
 815                 return super.skip(n);
 816             } finally {
 817                 lower();
 818             }
 819         }
 820 
 821         public int available() throws IOException {
 822             raise();
 823             try {
 824                 return super.available();
 825             } finally {
 826                 lower();
 827             }
 828         }
 829     }
 830 
 831     /**
 832      * A buffered input stream for a subprocess pipe file descriptor
 833      * that allows the underlying file descriptor to be reclaimed when
 834      * the process exits, via the processExited hook.
 835      *
 836      * This is tricky because we do not want the user-level InputStream to be
 837      * closed until the user invokes close(), and we need to continue to be
 838      * able to read any buffered data lingering in the OS pipe buffer.
 839      *
 840      * On AIX this is especially tricky, because the 'close()' system call
 841      * will block if another thread is at the same time blocked in a file
 842      * operation (e.g. 'read()') on the same file descriptor. We therefore
 843      * combine 'ProcessPipeInputStream' approach used on Linux and Bsd
 844      * with the DeferredCloseInputStream approach used on Solaris. This means
 845      * that every potentially blocking operation on the file descriptor
 846      * increments a counter before it is executed and decrements it once it
 847      * finishes. The 'close()' operation will only be executed if there are
 848      * no pending operations. Otherwise it is deferred after the last pending
 849      * operation has finished.
 850      *
 851      */
 852     private static class DeferredCloseProcessPipeInputStream
 853         extends BufferedInputStream {
 854 
 855         private final Object closeLock = new Object();
 856         private int useCount = 0;
 857         private boolean closePending = false;
 858 
 859         DeferredCloseProcessPipeInputStream(int fd) {
 860             super(new PipeInputStream(newFileDescriptor(fd)));
 861         }
 862 
 863         private InputStream drainInputStream(InputStream in)
 864                 throws IOException {
 865             int n = 0;
 866             int j;
 867             byte[] a = null;
 868             synchronized (closeLock) {
 869                 if (buf == null) // asynchronous close()?
 870                     return null; // discard
 871                 j = in.available();
 872             }
 873             while (j > 0) {
 874                 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
 875                 synchronized (closeLock) {
 876                     if (buf == null) // asynchronous close()?
 877                         return null; // discard
 878                     n += in.read(a, n, j);
 879                     j = in.available();
 880                 }
 881             }
 882             return (a == null) ?
 883                     ProcessBuilder.NullInputStream.INSTANCE :
 884                     new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));
 885         }
 886 
 887         /** Called by the process reaper thread when the process exits. */
 888         synchronized void processExited() {
 889             try {
 890                 InputStream in = this.in;
 891                 if (in != null) {
 892                     InputStream stragglers = drainInputStream(in);
 893                     in.close();
 894                     this.in = stragglers;
 895                 }
 896             } catch (IOException ignored) { }
 897         }
 898 
 899         private void raise() {
 900             synchronized (closeLock) {
 901                 useCount++;
 902             }
 903         }
 904 
 905         private void lower() throws IOException {
 906             synchronized (closeLock) {
 907                 useCount--;
 908                 if (useCount == 0 && closePending) {
 909                     closePending = false;
 910                     super.close();
 911                 }
 912             }
 913         }
 914 
 915         @Override
 916         public int read() throws IOException {
 917             raise();
 918             try {
 919                 return super.read();
 920             } finally {
 921                 lower();
 922             }
 923         }
 924 
 925         @Override
 926         public int read(byte[] b) throws IOException {
 927             raise();
 928             try {
 929                 return super.read(b);
 930             } finally {
 931                 lower();
 932             }
 933         }
 934 
 935         @Override
 936         public int read(byte[] b, int off, int len) throws IOException {
 937             raise();
 938             try {
 939                 return super.read(b, off, len);
 940             } finally {
 941                 lower();
 942             }
 943         }
 944 
 945         @Override
 946         public long skip(long n) throws IOException {
 947             raise();
 948             try {
 949                 return super.skip(n);
 950             } finally {
 951                 lower();
 952             }
 953         }
 954 
 955         @Override
 956         public int available() throws IOException {
 957             raise();
 958             try {
 959                 return super.available();
 960             } finally {
 961                 lower();
 962             }
 963         }
 964 
 965         @Override
 966         public void close() throws IOException {
 967             // BufferedInputStream#close() is not synchronized unlike most other
 968             // methods. Synchronizing helps avoid racing with drainInputStream().
 969             synchronized (closeLock) {
 970                 if (useCount == 0) {
 971                     super.close();
 972                 }
 973                 else {
 974                     closePending = true;
 975                 }
 976             }
 977         }
 978     }
 979 }