1 /*
   2  * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright 2012, 2013 SAP AG. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 
  27 package java.lang;
  28 
  29 import java.io.*;
  30 
  31 import java.util.concurrent.TimeUnit;
  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     /* this is for the reaping thread */
  52     private native int waitForProcessExit(int pid);
  53 
  54     /**
  55      * Create a process using fork(2) and exec(2).
  56      *
  57      * @param std_fds array of file descriptors.  Indexes 0, 1, and
  58      *        2 correspond to standard input, standard output and
  59      *        standard error, respectively.  On input, a value of -1
  60      *        means to create a pipe to connect child and parent
  61      *        processes.  On output, a value which is not -1 is the
  62      *        parent pipe fd corresponding to the pipe which has
  63      *        been created.  An element of this array is -1 on input
  64      *        if and only if it is <em>not</em> -1 on output.
  65      * @return the pid of the subprocess
  66      */
  67     private native int forkAndExec(byte[] prog,
  68                                    byte[] argBlock, int argc,
  69                                    byte[] envBlock, int envc,
  70                                    byte[] dir,
  71                                    int[] std_fds,
  72                                    boolean redirectErrorStream)
  73         throws IOException;
  74 
  75     UNIXProcess(final byte[] prog,
  76                 final byte[] argBlock, int argc,
  77                 final byte[] envBlock, int envc,
  78                 final byte[] dir,
  79                 final int[] std_fds,
  80                 final boolean redirectErrorStream)
  81     throws IOException {
  82         pid = forkAndExec(prog,
  83                           argBlock, argc,
  84                           envBlock, envc,
  85                           dir,
  86                           std_fds,
  87                           redirectErrorStream);
  88 
  89         java.security.AccessController.doPrivileged(
  90         new java.security.PrivilegedAction<Void>() { public Void run() {
  91             if (std_fds[0] == -1)
  92                 stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE;
  93             else {
  94                 FileDescriptor stdin_fd = new FileDescriptor();
  95                 fdAccess.set(stdin_fd, std_fds[0]);
  96                 stdin_stream = new BufferedOutputStream(
  97                     new FileOutputStream(stdin_fd));
  98             }
  99 
 100             if (std_fds[1] == -1)
 101                 stdout_stream = ProcessBuilder.NullInputStream.INSTANCE;
 102             else {
 103                 FileDescriptor stdout_fd = new FileDescriptor();
 104                 fdAccess.set(stdout_fd, std_fds[1]);
 105                 stdout_inner_stream = new DeferredCloseInputStream(stdout_fd);
 106                 stdout_stream = new BufferedInputStream(stdout_inner_stream);
 107             }
 108 
 109             if (std_fds[2] == -1)
 110                 stderr_stream = ProcessBuilder.NullInputStream.INSTANCE;
 111             else {
 112                 FileDescriptor stderr_fd = new FileDescriptor();
 113                 fdAccess.set(stderr_fd, std_fds[2]);
 114                 stderr_stream = new DeferredCloseInputStream(stderr_fd);
 115             }
 116 
 117             return null; }});
 118 
 119         /*
 120          * For each subprocess forked a corresponding reaper thread
 121          * is started.  That thread is the only thread which waits
 122          * for the subprocess to terminate and it doesn't hold any
 123          * locks while doing so.  This design allows waitFor() and
 124          * exitStatus() to be safely executed in parallel (and they
 125          * need no native code).
 126          */
 127 
 128         java.security.AccessController.doPrivileged(
 129             new java.security.PrivilegedAction<Void>() { public Void run() {
 130                 Thread t = new Thread("process reaper") {
 131                     public void run() {
 132                         int res = waitForProcessExit(pid);
 133                         synchronized (UNIXProcess.this) {
 134                             hasExited = true;
 135                             exitcode = res;
 136                             UNIXProcess.this.notifyAll();
 137                         }
 138                     }
 139                 };
 140                 t.setDaemon(true);
 141                 t.start();
 142                 return null; }});
 143     }
 144 
 145     public OutputStream getOutputStream() {
 146         return stdin_stream;
 147     }
 148 
 149     public InputStream getInputStream() {
 150         return stdout_stream;
 151     }
 152 
 153     public InputStream getErrorStream() {
 154         return stderr_stream;
 155     }
 156 
 157     public synchronized int waitFor() throws InterruptedException {
 158         while (!hasExited) {
 159             wait();
 160         }
 161         return exitcode;
 162     }
 163 
 164     @Override
 165     public synchronized boolean waitFor(long timeout, TimeUnit unit)
 166         throws InterruptedException
 167     {
 168         if (hasExited) return true;
 169         if (timeout <= 0) return false;
 170 
 171         long timeoutAsNanos = unit.toNanos(timeout);
 172         long startTime = System.nanoTime();
 173         long rem = timeoutAsNanos;
 174 
 175         while (!hasExited && (rem > 0)) {
 176             wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
 177             rem = timeoutAsNanos - (System.nanoTime() - startTime);
 178         }
 179         return hasExited;
 180     }
 181 
 182     public synchronized int exitValue() {
 183         if (!hasExited) {
 184             throw new IllegalThreadStateException("process hasn't exited");
 185         }
 186         return exitcode;
 187     }
 188 
 189     private static native void destroyProcess(int pid, boolean force);
 190     public synchronized void destroy(boolean force) {
 191         // There is a risk that pid will be recycled, causing us to
 192         // kill the wrong process!  So we only terminate processes
 193         // that appear to still be running.  Even with this check,
 194         // there is an unavoidable race condition here, but the window
 195         // is very small, and OSes try hard to not recycle pids too
 196         // soon, so this is quite safe.
 197         if (!hasExited)
 198             destroyProcess(pid, force);
 199         try {
 200             stdin_stream.close();
 201             if (stdout_inner_stream != null)
 202                 stdout_inner_stream.closeDeferred(stdout_stream);
 203             if (stderr_stream instanceof DeferredCloseInputStream)
 204                 ((DeferredCloseInputStream) stderr_stream)
 205                     .closeDeferred(stderr_stream);
 206         } catch (IOException e) {
 207             // ignore
 208         }
 209     }
 210 
 211     public void destroy() {
 212         destroy(false);
 213     }
 214 
 215     @Override
 216     public Process destroyForcibly() {
 217         destroy(true);
 218         return this;
 219     }
 220 
 221     @Override
 222     public synchronized boolean isAlive() {
 223         return !hasExited;
 224     }
 225 
 226     // A FileInputStream that supports the deferment of the actual close
 227     // operation until the last pending I/O operation on the stream has
 228     // finished.  This is required on Solaris because we must close the stdin
 229     // and stdout streams in the destroy method in order to reclaim the
 230     // underlying file descriptors.  Doing so, however, causes any thread
 231     // currently blocked in a read on one of those streams to receive an
 232     // IOException("Bad file number"), which is incompatible with historical
 233     // behavior.  By deferring the close we allow any pending reads to see -1
 234     // (EOF) as they did before.
 235     //
 236     private static class DeferredCloseInputStream
 237         extends FileInputStream
 238     {
 239 
 240         private DeferredCloseInputStream(FileDescriptor fd) {
 241             super(fd);
 242         }
 243 
 244         private Object lock = new Object();     // For the following fields
 245         private boolean closePending = false;
 246         private int useCount = 0;
 247         private InputStream streamToClose;
 248 
 249         private void raise() {
 250             synchronized (lock) {
 251                 useCount++;
 252             }
 253         }
 254 
 255         private void lower() throws IOException {
 256             synchronized (lock) {
 257                 useCount--;
 258                 if (useCount == 0 && closePending) {
 259                     streamToClose.close();
 260                 }
 261             }
 262         }
 263 
 264         // stc is the actual stream to be closed; it might be this object, or
 265         // it might be an upstream object for which this object is downstream.
 266         //
 267         private void closeDeferred(InputStream stc) throws IOException {
 268             synchronized (lock) {
 269                 if (useCount == 0) {
 270                     stc.close();
 271                 } else {
 272                     closePending = true;
 273                     streamToClose = stc;
 274                 }
 275             }
 276         }
 277 
 278         public void close() throws IOException {
 279             synchronized (lock) {
 280                 useCount = 0;
 281                 closePending = false;
 282             }
 283             super.close();
 284         }
 285 
 286         public int read() throws IOException {
 287             raise();
 288             try {
 289                 return super.read();
 290             } finally {
 291                 lower();
 292             }
 293         }
 294 
 295         public int read(byte[] b) throws IOException {
 296             raise();
 297             try {
 298                 return super.read(b);
 299             } finally {
 300                 lower();
 301             }
 302         }
 303 
 304         public int read(byte[] b, int off, int len) throws IOException {
 305             raise();
 306             try {
 307                 return super.read(b, off, len);
 308             } finally {
 309                 lower();
 310             }
 311         }
 312 
 313         public long skip(long n) throws IOException {
 314             raise();
 315             try {
 316                 return super.skip(n);
 317             } finally {
 318                 lower();
 319             }
 320         }
 321 
 322         public int available() throws IOException {
 323             raise();
 324             try {
 325                 return super.available();
 326             } finally {
 327                 lower();
 328             }
 329         }
 330 
 331     }
 332 
 333     /* This routine initializes JNI field offsets for the class */
 334     private static native void init();
 335 
 336     static {
 337         init();
 338     }
 339 }