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 }