1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. 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.BufferedInputStream; 30 import java.io.BufferedOutputStream; 31 import java.io.ByteArrayInputStream; 32 import java.io.FileDescriptor; 33 import java.io.FileInputStream; 34 import java.io.FileOutputStream; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.OutputStream; 38 import java.util.Arrays; 39 import java.util.concurrent.Executors; 40 import java.util.concurrent.Executor; 41 import java.util.concurrent.ThreadFactory; 42 import java.security.AccessController; 43 import static java.security.AccessController.doPrivileged; 44 import java.security.PrivilegedAction; 45 import java.security.PrivilegedActionException; 46 import java.security.PrivilegedExceptionAction; 47 48 /** 49 * java.lang.Process subclass in the UNIX environment. 50 * 51 * @author Mario Wolczko and Ross Knippel. 52 * @author Konstantin Kladko (ported to Linux) 53 * @author Martin Buchholz 54 */ 55 final class UNIXProcess extends Process { 56 private final int pid; 57 private int exitcode; 58 private boolean hasExited; 59 60 private /* final */ OutputStream stdin; 61 private /* final */ InputStream stdout; 62 private /* final */ InputStream stderr; 63 64 /* this is for the reaping thread */ waitForProcessExit(int pid)65 private native int waitForProcessExit(int pid); 66 67 /** 68 * Create a process using fork(2) and exec(2). 69 * 70 * @param fds an array of three file descriptors. 71 * Indexes 0, 1, and 2 correspond to standard input, 72 * standard output and standard error, respectively. On 73 * input, a value of -1 means to create a pipe to connect 74 * child and parent processes. On output, a value which 75 * is not -1 is the parent pipe fd corresponding to the 76 * pipe which has been created. An element of this array 77 * is -1 on input if and only if it is <em>not</em> -1 on 78 * output. 79 * @return the pid of the subprocess 80 */ forkAndExec(byte[] prog, byte[] argBlock, int argc, byte[] envBlock, int envc, byte[] dir, int[] fds, boolean redirectErrorStream)81 private native int forkAndExec(byte[] prog, 82 byte[] argBlock, int argc, 83 byte[] envBlock, int envc, 84 byte[] dir, 85 int[] fds, 86 boolean redirectErrorStream) 87 throws IOException; 88 89 /** 90 * The thread factory used to create "process reaper" daemon threads. 91 */ 92 private static class ProcessReaperThreadFactory implements ThreadFactory { 93 private final static ThreadGroup group = getRootThreadGroup(); 94 getRootThreadGroup()95 private static ThreadGroup getRootThreadGroup() { 96 return doPrivileged(new PrivilegedAction<ThreadGroup> () { 97 public ThreadGroup run() { 98 ThreadGroup root = Thread.currentThread().getThreadGroup(); 99 while (root.getParent() != null) 100 root = root.getParent(); 101 return root; 102 }}); 103 } 104 newThread(Runnable grimReaper)105 public Thread newThread(Runnable grimReaper) { 106 // Our thread stack requirement is quite modest. 107 Thread t = new Thread(group, grimReaper, "process reaper", 32768); 108 t.setDaemon(true); 109 // A small attempt (probably futile) to avoid priority inversion 110 t.setPriority(Thread.MAX_PRIORITY); 111 return t; 112 } 113 } 114 115 /** 116 * The thread pool of "process reaper" daemon threads. 117 */ 118 private static final Executor processReaperExecutor = 119 doPrivileged(new PrivilegedAction<Executor>() { 120 public Executor run() { 121 return Executors.newCachedThreadPool 122 (new ProcessReaperThreadFactory()); 123 }}); 124 125 UNIXProcess(final byte[] prog, 126 final byte[] argBlock, final int argc, 127 final byte[] envBlock, final int envc, 128 final byte[] dir, 129 final int[] fds, 130 final boolean redirectErrorStream) 131 throws IOException { 132 133 pid = forkAndExec(prog, 134 argBlock, argc, 135 envBlock, envc, 136 dir, 137 fds, 138 redirectErrorStream); 139 140 try { 141 doPrivileged(new PrivilegedExceptionAction<Void>() { 142 public Void run() throws IOException { 143 initStreams(fds); 144 return null; 145 }}); 146 } catch (PrivilegedActionException ex) { 147 throw (IOException) ex.getException(); 148 } 149 } 150 151 static FileDescriptor newFileDescriptor(int fd) { 152 FileDescriptor fileDescriptor = new FileDescriptor(); 153 fileDescriptor.setInt$(fd); 154 return fileDescriptor; 155 } 156 157 void initStreams(int[] fds) throws IOException { 158 stdin = (fds[0] == -1) ? 159 ProcessBuilder.NullOutputStream.INSTANCE : 160 new ProcessPipeOutputStream(fds[0]); 161 162 stdout = (fds[1] == -1) ? 163 ProcessBuilder.NullInputStream.INSTANCE : 164 new ProcessPipeInputStream(fds[1]); 165 166 stderr = (fds[2] == -1) ? 167 ProcessBuilder.NullInputStream.INSTANCE : 168 new ProcessPipeInputStream(fds[2]); 169 170 processReaperExecutor.execute(new Runnable() { 171 public void run() { 172 int exitcode = waitForProcessExit(pid); 173 UNIXProcess.this.processExited(exitcode); 174 }}); 175 } 176 177 void processExited(int exitcode) { 178 synchronized (this) { 179 this.exitcode = exitcode; 180 hasExited = true; 181 notifyAll(); 182 } 183 184 if (stdout instanceof ProcessPipeInputStream) 185 ((ProcessPipeInputStream) stdout).processExited(); 186 187 if (stderr instanceof ProcessPipeInputStream) 188 ((ProcessPipeInputStream) stderr).processExited(); 189 190 if (stdin instanceof ProcessPipeOutputStream) 191 ((ProcessPipeOutputStream) stdin).processExited(); 192 } 193 194 public OutputStream getOutputStream() { 195 return stdin; 196 } 197 198 public InputStream getInputStream() { 199 return stdout; 200 } 201 202 public InputStream getErrorStream() { 203 return stderr; 204 } 205 206 public synchronized int waitFor() throws InterruptedException { 207 while (!hasExited) { 208 wait(); 209 } 210 return exitcode; 211 } 212 213 public synchronized int exitValue() { 214 if (!hasExited) { 215 throw new IllegalThreadStateException("process hasn't exited"); 216 } 217 return exitcode; 218 } 219 220 private static native void destroyProcess(int pid); 221 public void destroy() { 222 // There is a risk that pid will be recycled, causing us to 223 // kill the wrong process! So we only terminate processes 224 // that appear to still be running. Even with this check, 225 // there is an unavoidable race condition here, but the window 226 // is very small, and OSes try hard to not recycle pids too 227 // soon, so this is quite safe. 228 synchronized (this) { 229 if (!hasExited) 230 destroyProcess(pid); 231 } 232 try { stdin.close(); } catch (IOException ignored) {} 233 try { stdout.close(); } catch (IOException ignored) {} 234 try { stderr.close(); } catch (IOException ignored) {} 235 } 236 237 @Override 238 public String toString() { 239 StringBuilder sb = new StringBuilder("Process[pid="); 240 sb.append(pid); 241 if (hasExited) { 242 sb.append(" ,hasExited=true, exitcode="); 243 sb.append(exitcode); 244 sb.append("]"); 245 } else { 246 sb.append(", hasExited=false]"); 247 } 248 249 return sb.toString(); 250 } 251 252 /* This routine initializes JNI field offsets for the class */ 253 private static native void initIDs(); 254 255 static { 256 initIDs(); 257 } 258 259 /** 260 * A buffered input stream for a subprocess pipe file descriptor 261 * that allows the underlying file descriptor to be reclaimed when 262 * the process exits, via the processExited hook. 263 * 264 * This is tricky because we do not want the user-level InputStream to be 265 * closed until the user invokes close(), and we need to continue to be 266 * able to read any buffered data lingering in the OS pipe buffer. 267 */ 268 static class ProcessPipeInputStream extends BufferedInputStream { 269 ProcessPipeInputStream(int fd) { 270 super(new FileInputStream(newFileDescriptor(fd), true /* isFdOwner */)); 271 } 272 273 private static byte[] drainInputStream(InputStream in) 274 throws IOException { 275 if (in == null) return null; 276 int n = 0; 277 int j; 278 byte[] a = null; 279 while ((j = in.available()) > 0) { 280 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); 281 n += in.read(a, n, j); 282 } 283 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n); 284 } 285 286 /** Called by the process reaper thread when the process exits. */ 287 synchronized void processExited() { 288 // Most BufferedInputStream methods are synchronized, but close() 289 // is not, and so we have to handle concurrent racing close(). 290 try { 291 InputStream in = this.in; 292 if (in != null) { 293 byte[] stragglers = drainInputStream(in); 294 in.close(); 295 this.in = (stragglers == null) ? 296 ProcessBuilder.NullInputStream.INSTANCE : 297 new ByteArrayInputStream(stragglers); 298 if (buf == null) // asynchronous close()? 299 this.in = null; 300 } 301 } catch (IOException ignored) { 302 // probably an asynchronous close(). 303 } 304 } 305 } 306 307 /** 308 * A buffered output stream for a subprocess pipe file descriptor 309 * that allows the underlying file descriptor to be reclaimed when 310 * the process exits, via the processExited hook. 311 */ 312 static class ProcessPipeOutputStream extends BufferedOutputStream { 313 ProcessPipeOutputStream(int fd) { 314 super(new FileOutputStream(newFileDescriptor(fd), true /* isFdOwner */)); 315 } 316 317 /** Called by the process reaper thread when the process exits. */ 318 synchronized void processExited() { 319 OutputStream out = this.out; 320 if (out != null) { 321 try { 322 out.close(); 323 } catch (IOException ignored) { 324 // We know of no reason to get an IOException, but if 325 // we do, there's nothing else to do but carry on. 326 } 327 this.out = ProcessBuilder.NullOutputStream.INSTANCE; 328 } 329 } 330 } 331 } 332