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