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