1 /*
   2  * Copyright (c) 1995, 2012, 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.io.*;
  29 import java.util.concurrent.TimeUnit;
  30 import java.security.AccessController;
  31 import java.security.PrivilegedAction;
  32 
  33 /* java.lang.Process subclass in the UNIX environment.
  34  *
  35  * @author Mario Wolczko and Ross Knippel.
  36  */
  37 
  38 final class UNIXProcess extends Process {
  39     private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
  40         = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
  41 
  42     private final int pid;
  43     private int exitcode;
  44     private boolean hasExited;
  45 
  46     private OutputStream stdin_stream;
  47     private InputStream stdout_stream;
  48     private DeferredCloseInputStream stdout_inner_stream;
  49     private InputStream stderr_stream;
  50 
  51     private static String javahome;
  52     private static String arch;
  53     private static String helperpath;
  54 
  55     enum LaunchMechanism {
  56         fork(1),
  57         posix_spawn(2);
  58 
  59         private int value;
  60         LaunchMechanism(int x) {value = x;}
  61     };
  62 
  63     /* On Solaris, the default is to spawn */
  64     private static LaunchMechanism launchMechanism;
  65 
  66     static {
  67         AccessController.doPrivileged(new PrivilegedAction<Void>() {
  68             public Void run() {
  69                 String javahome = System.getProperty("java.home");
  70                 String osArch = System.getProperty("os.arch");
  71                 if (osArch.equals("x86")) {
  72                     osArch = "i386";
  73                 } else if (osArch.equals("x86_64")) {
  74                     osArch = "amd64";
  75                 }
  76 
  77                 helperpath = javahome + "/lib/" + osArch + "/jspawnhelper";
  78                 String s = System.getProperty(
  79                     "jdk.lang.Process.launchMechanism", "posix_spawn");
  80 
  81                 try {
  82                     launchMechanism = LaunchMechanism.valueOf(s);
  83                 } catch (IllegalArgumentException e) {
  84                     throw new Error(s + " is not a supported " +
  85                         "process launch mechanism on this platform.");
  86                 }
  87                 return null;
  88             }
  89         });
  90     }
  91 
  92     /* this is for the reaping thread */
  93     private native int waitForProcessExit(int pid);
  94 
  95     /**
  96      * Create a process. Depending on the mode flag, this is done by
  97      * one of the following mechanisms.
  98      * - fork(2) and exec(2)
  99      * - vfork(2) and exec(2)
 100      * - posix_spawn(2)
 101      *
 102      * @param std_fds array of file descriptors.  Indexes 0, 1, and
 103      *        2 correspond to standard input, standard output and
 104      *        standard error, respectively.  On input, a value of -1
 105      *        means to create a pipe to connect child and parent
 106      *        processes.  On output, a value which is not -1 is the
 107      *        parent pipe fd corresponding to the pipe which has
 108      *        been created.  An element of this array is -1 on input
 109      *        if and only if it is <em>not</em> -1 on output.
 110      * @return the pid of the subprocess
 111      */
 112     private native int forkAndExec(int mode, byte[] prog,
 113                                    byte[] argBlock, int argc,
 114                                    byte[] envBlock, int envc,
 115                                    byte[] dir,
 116                                    int[] std_fds,
 117                                    boolean redirectErrorStream)
 118         throws IOException;
 119 
 120     UNIXProcess(final byte[] prog,
 121                 final byte[] argBlock, int argc,
 122                 final byte[] envBlock, int envc,
 123                 final byte[] dir,
 124                 final int[] std_fds,
 125                 final boolean redirectErrorStream)
 126     throws IOException {
 127         pid = forkAndExec(launchMechanism.value,
 128                           prog,
 129                           argBlock, argc,
 130                           envBlock, envc,
 131                           dir,
 132                           std_fds,
 133                           redirectErrorStream);
 134 
 135         java.security.AccessController.doPrivileged(
 136         new java.security.PrivilegedAction<Void>() { public Void run() {
 137             if (std_fds[0] == -1)
 138                 stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE;
 139             else {
 140                 FileDescriptor stdin_fd = new FileDescriptor();
 141                 fdAccess.set(stdin_fd, std_fds[0]);
 142                 stdin_stream = new BufferedOutputStream(
 143                     new FileOutputStream(stdin_fd));
 144             }
 145 
 146             if (std_fds[1] == -1)
 147                 stdout_stream = ProcessBuilder.NullInputStream.INSTANCE;
 148             else {
 149                 FileDescriptor stdout_fd = new FileDescriptor();
 150                 fdAccess.set(stdout_fd, std_fds[1]);
 151                 stdout_inner_stream = new DeferredCloseInputStream(stdout_fd);
 152                 stdout_stream = new BufferedInputStream(stdout_inner_stream);
 153             }
 154 
 155             if (std_fds[2] == -1)
 156                 stderr_stream = ProcessBuilder.NullInputStream.INSTANCE;
 157             else {
 158                 FileDescriptor stderr_fd = new FileDescriptor();
 159                 fdAccess.set(stderr_fd, std_fds[2]);
 160                 stderr_stream = new DeferredCloseInputStream(stderr_fd);
 161             }
 162 
 163             return null; }});
 164 
 165         /*
 166          * For each subprocess forked a corresponding reaper thread
 167          * is started.  That thread is the only thread which waits
 168          * for the subprocess to terminate and it doesn't hold any
 169          * locks while doing so.  This design allows waitFor() and
 170          * exitStatus() to be safely executed in parallel (and they
 171          * need no native code).
 172          */
 173 
 174         java.security.AccessController.doPrivileged(
 175             new java.security.PrivilegedAction<Void>() { public Void run() {
 176                 Thread t = new Thread("process reaper") {
 177                     public void run() {
 178                         int res = waitForProcessExit(pid);
 179                         synchronized (UNIXProcess.this) {
 180                             hasExited = true;
 181                             exitcode = res;
 182                             UNIXProcess.this.notifyAll();
 183                         }
 184                     }
 185                 };
 186                 t.setDaemon(true);
 187                 t.start();
 188                 return null; }});
 189     }
 190 
 191     public OutputStream getOutputStream() {
 192         return stdin_stream;
 193     }
 194 
 195     public InputStream getInputStream() {
 196         return stdout_stream;
 197     }
 198 
 199     public InputStream getErrorStream() {
 200         return stderr_stream;
 201     }
 202 
 203     public synchronized int waitFor() throws InterruptedException {
 204         while (!hasExited) {
 205             wait();
 206         }
 207         return exitcode;
 208     }
 209 
 210     @Override
 211     public synchronized boolean waitFor(long timeout, TimeUnit unit)
 212         throws InterruptedException
 213     {
 214         if (hasExited) return true;
 215         if (timeout <= 0) return false;
 216 
 217         long timeoutAsNanos = unit.toNanos(timeout);
 218         long startTime = System.nanoTime();
 219         long rem = timeoutAsNanos;
 220 
 221         while (!hasExited && (rem > 0)) {
 222             wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
 223             rem = timeoutAsNanos - (System.nanoTime() - startTime);
 224         }
 225         return hasExited;
 226     }
 227 
 228     public synchronized int exitValue() {
 229         if (!hasExited) {
 230             throw new IllegalThreadStateException("process hasn't exited");
 231         }
 232         return exitcode;
 233     }
 234 
 235     private static native void destroyProcess(int pid, boolean force);
 236     private synchronized void destroy(boolean force) {
 237         // There is a risk that pid will be recycled, causing us to
 238         // kill the wrong process!  So we only terminate processes
 239         // that appear to still be running.  Even with this check,
 240         // there is an unavoidable race condition here, but the window
 241         // is very small, and OSes try hard to not recycle pids too
 242         // soon, so this is quite safe.
 243         if (!hasExited)
 244             destroyProcess(pid, force);
 245         try {
 246             stdin_stream.close();
 247             if (stdout_inner_stream != null)
 248                 stdout_inner_stream.closeDeferred(stdout_stream);
 249             if (stderr_stream instanceof DeferredCloseInputStream)
 250                 ((DeferredCloseInputStream) stderr_stream)
 251                     .closeDeferred(stderr_stream);
 252         } catch (IOException e) {
 253             // ignore
 254         }
 255     }
 256 
 257     public void destroy() {
 258         destroy(false);
 259     }
 260 
 261     @Override
 262     public Process destroyForcibly() {
 263         destroy(true);
 264         return this;
 265     }
 266 
 267     @Override
 268     public synchronized boolean isAlive() {
 269         return !hasExited;
 270     }
 271 
 272     // A FileInputStream that supports the deferment of the actual close
 273     // operation until the last pending I/O operation on the stream has
 274     // finished.  This is required on Solaris because we must close the stdin
 275     // and stdout streams in the destroy method in order to reclaim the
 276     // underlying file descriptors.  Doing so, however, causes any thread
 277     // currently blocked in a read on one of those streams to receive an
 278     // IOException("Bad file number"), which is incompatible with historical
 279     // behavior.  By deferring the close we allow any pending reads to see -1
 280     // (EOF) as they did before.
 281     //
 282     private static class DeferredCloseInputStream
 283         extends FileInputStream
 284     {
 285 
 286         private DeferredCloseInputStream(FileDescriptor fd) {
 287             super(fd);
 288         }
 289 
 290         private Object lock = new Object();     // For the following fields
 291         private boolean closePending = false;
 292         private int useCount = 0;
 293         private InputStream streamToClose;
 294 
 295         private void raise() {
 296             synchronized (lock) {
 297                 useCount++;
 298             }
 299         }
 300 
 301         private void lower() throws IOException {
 302             synchronized (lock) {
 303                 useCount--;
 304                 if (useCount == 0 && closePending) {
 305                     streamToClose.close();
 306                 }
 307             }
 308         }
 309 
 310         // stc is the actual stream to be closed; it might be this object, or
 311         // it might be an upstream object for which this object is downstream.
 312         //
 313         private void closeDeferred(InputStream stc) throws IOException {
 314             synchronized (lock) {
 315                 if (useCount == 0) {
 316                     stc.close();
 317                 } else {
 318                     closePending = true;
 319                     streamToClose = stc;
 320                 }
 321             }
 322         }
 323 
 324         public void close() throws IOException {
 325             synchronized (lock) {
 326                 useCount = 0;
 327                 closePending = false;
 328             }
 329             super.close();
 330         }
 331 
 332         public int read() throws IOException {
 333             raise();
 334             try {
 335                 return super.read();
 336             } finally {
 337                 lower();
 338             }
 339         }
 340 
 341         public int read(byte[] b) throws IOException {
 342             raise();
 343             try {
 344                 return super.read(b);
 345             } finally {
 346                 lower();
 347             }
 348         }
 349 
 350         public int read(byte[] b, int off, int len) throws IOException {
 351             raise();
 352             try {
 353                 return super.read(b, off, len);
 354             } finally {
 355                 lower();
 356             }
 357         }
 358 
 359         public long skip(long n) throws IOException {
 360             raise();
 361             try {
 362                 return super.skip(n);
 363             } finally {
 364                 lower();
 365             }
 366         }
 367 
 368         public int available() throws IOException {
 369             raise();
 370             try {
 371                 return super.available();
 372             } finally {
 373                 lower();
 374             }
 375         }
 376 
 377     }
 378 
 379     private static native void init();
 380 
 381     static {
 382         init();
 383     }
 384 }