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