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