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