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