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