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