1 /*
2 * Copyright (c) 1995, 2013, 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.util.Arrays;
38 import java.util.concurrent.Executors;
39 import java.util.concurrent.Executor;
40 import java.util.concurrent.ThreadFactory;
41 import java.util.concurrent.TimeUnit;
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 static final sun.misc.JavaIOFileDescriptorAccess fdAccess
57 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
58
59 private final int pid;
60 private int exitcode;
61 private boolean hasExited;
62
63 private /* final */ OutputStream stdin;
64 private /* final */ InputStream stdout;
65 private /* final */ InputStream stderr;
66
67 private static enum LaunchMechanism {
68 FORK(1),
69 VFORK(3);
70
71 private int value;
72 LaunchMechanism(int x) {value = x;}
73 };
74
75 /* default is VFORK on Linux */
76 private static final LaunchMechanism launchMechanism;
77 private static byte[] helperpath;
78
79 private static byte[] toCString(String s) {
80 if (s == null)
81 return null;
82 byte[] bytes = s.getBytes();
83 byte[] result = new byte[bytes.length + 1];
84 System.arraycopy(bytes, 0,
85 result, 0,
86 bytes.length);
87 result[result.length-1] = (byte)0;
88 return result;
89 }
90
91 static {
92 launchMechanism = AccessController.doPrivileged(
93 new PrivilegedAction<LaunchMechanism>()
94 {
95 public LaunchMechanism run() {
96 String javahome = System.getProperty("java.home");
97 String osArch = System.getProperty("os.arch");
98
99 helperpath = toCString(javahome + "/lib/" + osArch + "/jspawnhelper");
100 String s = System.getProperty(
101 "jdk.lang.Process.launchMechanism", "vfork");
102
103 try {
104 return LaunchMechanism.valueOf(s.toUpperCase());
105 } catch (IllegalArgumentException e) {
106 throw new Error(s + " is not a supported " +
107 "process launch mechanism on this platform.");
108 }
109 }
110 });
111 }
112
113 /* this is for the reaping thread */
114 private native int waitForProcessExit(int pid);
115
116 /**
117 * Create a process. Depending on the mode flag, this is done by
118 * one of the following mechanisms.
119 * - fork(2) and exec(2)
120 * - clone(2) and exec(2)
121 * - vfork(2) and exec(2)
122 *
123 * @param fds an array of three file descriptors.
124 * Indexes 0, 1, and 2 correspond to standard input,
125 * standard output and standard error, respectively. On
126 * input, a value of -1 means to create a pipe to connect
127 * child and parent processes. On output, a value which
128 * is not -1 is the parent pipe fd corresponding to the
129 * pipe which has been created. An element of this array
130 * is -1 on input if and only if it is <em>not</em> -1 on
131 * output.
132 * @return the pid of the subprocess
133 */
134 private native int forkAndExec(int mode, byte[] helperpath,
152 ThreadGroup root = Thread.currentThread().getThreadGroup();
153 while (root.getParent() != null)
154 root = root.getParent();
155 return root;
156 }});
157 }
158
159 public Thread newThread(Runnable grimReaper) {
160 // Our thread stack requirement is quite modest.
161 Thread t = new Thread(group, grimReaper, "process reaper", 32768);
162 t.setDaemon(true);
163 // A small attempt (probably futile) to avoid priority inversion
164 t.setPriority(Thread.MAX_PRIORITY);
165 return t;
166 }
167 }
168
169 /**
170 * The thread pool of "process reaper" daemon threads.
171 */
172 private static final Executor processReaperExecutor =
173 doPrivileged(new PrivilegedAction<Executor>() {
174 public Executor run() {
175 return Executors.newCachedThreadPool
176 (new ProcessReaperThreadFactory());
177 }});
178
179 UNIXProcess(final byte[] prog,
180 final byte[] argBlock, final int argc,
181 final byte[] envBlock, final int envc,
182 final byte[] dir,
183 final int[] fds,
184 final boolean redirectErrorStream)
185 throws IOException {
186
187 pid = forkAndExec(launchMechanism.value,
188 helperpath,
189 prog,
190 argBlock, argc,
191 envBlock, envc,
192 dir,
193 fds,
194 redirectErrorStream);
195
196 try {
197 doPrivileged(new PrivilegedExceptionAction<Void>() {
198 public Void run() throws IOException {
199 initStreams(fds);
200 return null;
201 }});
202 } catch (PrivilegedActionException ex) {
203 throw (IOException) ex.getException();
204 }
205 }
206
207 static FileDescriptor newFileDescriptor(int fd) {
208 FileDescriptor fileDescriptor = new FileDescriptor();
209 fdAccess.set(fileDescriptor, fd);
210 return fileDescriptor;
211 }
212
213 void initStreams(int[] fds) throws IOException {
214 stdin = (fds[0] == -1) ?
215 ProcessBuilder.NullOutputStream.INSTANCE :
216 new ProcessPipeOutputStream(fds[0]);
217
218 stdout = (fds[1] == -1) ?
219 ProcessBuilder.NullInputStream.INSTANCE :
220 new ProcessPipeInputStream(fds[1]);
221
222 stderr = (fds[2] == -1) ?
223 ProcessBuilder.NullInputStream.INSTANCE :
224 new ProcessPipeInputStream(fds[2]);
225
226 processReaperExecutor.execute(new Runnable() {
227 public void run() {
228 int exitcode = waitForProcessExit(pid);
229 UNIXProcess.this.processExited(exitcode);
230 }});
231 }
232
233 void processExited(int exitcode) {
234 synchronized (this) {
235 this.exitcode = exitcode;
236 hasExited = true;
237 notifyAll();
238 }
239
240 if (stdout instanceof ProcessPipeInputStream)
241 ((ProcessPipeInputStream) stdout).processExited();
242
243 if (stderr instanceof ProcessPipeInputStream)
244 ((ProcessPipeInputStream) stderr).processExited();
245
246 if (stdin instanceof ProcessPipeOutputStream)
247 ((ProcessPipeOutputStream) stdin).processExited();
248 }
249
250 public OutputStream getOutputStream() {
251 return stdin;
252 }
253
254 public InputStream getInputStream() {
255 return stdout;
256 }
257
258 public InputStream getErrorStream() {
259 return stderr;
260 }
261
262 public synchronized int waitFor() throws InterruptedException {
263 while (!hasExited) {
264 wait();
265 }
266 return exitcode;
267 }
268
274 if (timeout <= 0) return false;
275
276 long timeoutAsNanos = unit.toNanos(timeout);
277 long startTime = System.nanoTime();
278 long rem = timeoutAsNanos;
279
280 while (!hasExited && (rem > 0)) {
281 wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
282 rem = timeoutAsNanos - (System.nanoTime() - startTime);
283 }
284 return hasExited;
285 }
286
287 public synchronized int exitValue() {
288 if (!hasExited) {
289 throw new IllegalThreadStateException("process hasn't exited");
290 }
291 return exitcode;
292 }
293
294 private static native void destroyProcess(int pid, boolean force);
295 private void destroy(boolean force) {
296 // There is a risk that pid will be recycled, causing us to
297 // kill the wrong process! So we only terminate processes
298 // that appear to still be running. Even with this check,
299 // there is an unavoidable race condition here, but the window
300 // is very small, and OSes try hard to not recycle pids too
301 // soon, so this is quite safe.
302 synchronized (this) {
303 if (!hasExited)
304 destroyProcess(pid, force);
305 }
306 try { stdin.close(); } catch (IOException ignored) {}
307 try { stdout.close(); } catch (IOException ignored) {}
308 try { stderr.close(); } catch (IOException ignored) {}
309 }
310
311 public void destroy() {
312 destroy(false);
313 }
314
315 @Override
316 public Process destroyForcibly() {
317 destroy(true);
318 return this;
319 }
320
321 @Override
322 public synchronized boolean isAlive() {
323 return !hasExited;
324 }
325
326 private static native void init();
327
328 static {
329 init();
330 }
331
332 /**
333 * A buffered input stream for a subprocess pipe file descriptor
334 * that allows the underlying file descriptor to be reclaimed when
335 * the process exits, via the processExited hook.
336 *
337 * This is tricky because we do not want the user-level InputStream to be
338 * closed until the user invokes close(), and we need to continue to be
339 * able to read any buffered data lingering in the OS pipe buffer.
340 */
341 static class ProcessPipeInputStream extends BufferedInputStream {
342 private final Object closeLock = new Object();
343
344 ProcessPipeInputStream(int fd) {
345 super(new FileInputStream(newFileDescriptor(fd)));
346 }
347 private static byte[] drainInputStream(InputStream in)
348 throws IOException {
349 int n = 0;
350 int j;
351 byte[] a = null;
352 while ((j = in.available()) > 0) {
353 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
354 n += in.read(a, n, j);
355 }
356 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
357 }
358
359 /** Called by the process reaper thread when the process exits. */
360 synchronized void processExited() {
361 synchronized (closeLock) {
371 }
372 } catch (IOException ignored) {}
373 }
374 }
375
376 @Override
377 public void close() throws IOException {
378 // BufferedInputStream#close() is not synchronized unlike most other methods.
379 // Synchronizing helps avoid race with processExited().
380 synchronized (closeLock) {
381 super.close();
382 }
383 }
384 }
385
386 /**
387 * A buffered output stream for a subprocess pipe file descriptor
388 * that allows the underlying file descriptor to be reclaimed when
389 * the process exits, via the processExited hook.
390 */
391 static class ProcessPipeOutputStream extends BufferedOutputStream {
392 ProcessPipeOutputStream(int fd) {
393 super(new FileOutputStream(newFileDescriptor(fd)));
394 }
395
396 /** Called by the process reaper thread when the process exits. */
397 synchronized void processExited() {
398 OutputStream out = this.out;
399 if (out != null) {
400 try {
401 out.close();
402 } catch (IOException ignored) {
403 // We know of no reason to get an IOException, but if
404 // we do, there's nothing else to do but carry on.
405 }
406 this.out = ProcessBuilder.NullOutputStream.INSTANCE;
407 }
408 }
409 }
410 }
|
1 /*
2 * Copyright (c) 1995, 2014, 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.util.Arrays;
38 import java.util.EnumSet;
39 import java.util.concurrent.Executors;
40 import java.util.concurrent.Executor;
41 import java.util.concurrent.ThreadFactory;
42 import java.util.concurrent.TimeUnit;
43 import java.security.AccessController;
44 import static java.security.AccessController.doPrivileged;
45 import java.security.PrivilegedAction;
46 import java.security.PrivilegedActionException;
47 import java.security.PrivilegedExceptionAction;
48
49 /**
50 * java.lang.Process subclass in the UNIX environment.
51 *
52 * @author Mario Wolczko and Ross Knippel.
53 * @author Konstantin Kladko (ported to Linux and Bsd)
54 * @author Martin Buchholz
55 * @author Volker Simonis (ported to AIX)
56 * @author Peter Levart (merged UNIX variants into common source)
57 */
58 abstract class UNIXProcess extends Process {
59 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
60 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
61
62 final int pid;
63 int exitcode;
64 boolean hasExited;
65
66 /* final */ OutputStream stdin;
67 /* final */ InputStream stdout;
68 /* final */ InputStream stderr;
69
70 private static enum Os {
71
72 LINUX {
73 @Override
74 LaunchMechanism defaultLaunchMechanism() {
75 return LaunchMechanism.VFORK;
76 }
77
78 @Override
79 String helperPath(String javahome, String osArch) {
80 return javahome + "/lib/" + osArch + "/jspawnhelper";
81 }
82
83 @Override
84 UNIXProcess newUNIXProcess(byte[] prog,
85 byte[] argBlock, final int argc,
86 byte[] envBlock, final int envc,
87 byte[] dir,
88 int[] fds,
89 boolean redirectErrorStream)
90 throws IOException {
91
92 return new LinuxOrBsdProcess(prog,
93 argBlock, argc,
94 envBlock, envc,
95 dir,
96 fds,
97 redirectErrorStream);
98 }
99 },
100
101 BSD {
102 @Override
103 LaunchMechanism defaultLaunchMechanism() {
104 return LaunchMechanism.POSIX_SPAWN;
105 }
106
107 @Override
108 String helperPath(String javahome, String osArch) {
109 return javahome + "/lib/jspawnhelper";
110 }
111
112 @Override
113 UNIXProcess newUNIXProcess(byte[] prog,
114 byte[] argBlock, final int argc,
115 byte[] envBlock, final int envc,
116 byte[] dir,
117 int[] fds,
118 boolean redirectErrorStream)
119 throws IOException {
120
121 return new LinuxOrBsdProcess(prog,
122 argBlock, argc,
123 envBlock, envc,
124 dir,
125 fds,
126 redirectErrorStream);
127 }
128 },
129
130 SOLARIS {
131 @Override
132 LaunchMechanism defaultLaunchMechanism() {
133 return LaunchMechanism.POSIX_SPAWN;
134 }
135
136 @Override
137 String helperPath(String javahome, String osArch) {
138 if (osArch.equals("x86")) {
139 osArch = "i386";
140 } else if (osArch.equals("x86_64")) {
141 osArch = "amd64";
142 }
143 return javahome + "/lib/" + osArch + "/jspawnhelper";
144 }
145
146 @Override
147 UNIXProcess newUNIXProcess(byte[] prog,
148 byte[] argBlock, final int argc,
149 byte[] envBlock, final int envc,
150 byte[] dir,
151 int[] fds,
152 boolean redirectErrorStream)
153 throws IOException {
154
155 return new SolarisProcess(prog,
156 argBlock, argc,
157 envBlock, envc,
158 dir,
159 fds,
160 redirectErrorStream);
161 }
162 },
163
164 AIX {
165 @Override
166 LaunchMechanism defaultLaunchMechanism() {
167 return LaunchMechanism.POSIX_SPAWN;
168 }
169
170 @Override
171 String helperPath(String javahome, String osArch) {
172 return javahome + "/lib/" + osArch + "/jspawnhelper";
173 }
174
175 @Override
176 UNIXProcess newUNIXProcess(byte[] prog,
177 byte[] argBlock, final int argc,
178 byte[] envBlock, final int envc,
179 byte[] dir,
180 int[] fds,
181 boolean redirectErrorStream)
182 throws IOException {
183
184 return new AixProcess(prog,
185 argBlock, argc,
186 envBlock, envc,
187 dir,
188 fds,
189 redirectErrorStream);
190 }
191 };
192
193 abstract LaunchMechanism defaultLaunchMechanism();
194
195 abstract String helperPath(String javahome, String osArch);
196
197 abstract UNIXProcess newUNIXProcess(byte[] prog,
198 byte[] argBlock, final int argc,
199 byte[] envBlock, final int envc,
200 byte[] dir,
201 int[] fds,
202 boolean redirectErrorStream) throws IOException;
203
204 String helperPath() {
205 return AccessController.doPrivileged(
206 new PrivilegedAction<String>() {
207 @Override
208 public String run() {
209 return helperPath(System.getProperty("java.home"), System.getProperty("os.arch"));
210 }
211 }
212 );
213 }
214
215 LaunchMechanism launchMechanism() {
216 return AccessController.doPrivileged(
217 new PrivilegedAction<LaunchMechanism>() {
218 @Override
219 public LaunchMechanism run() {
220 String s = System.getProperty("jdk.lang.Process.launchMechanism");
221 LaunchMechanism lm;
222 if (s == null) {
223 lm = defaultLaunchMechanism();
224 s = lm.name().toLowerCase();
225 } else {
226 try {
227 lm = LaunchMechanism.valueOf(s.toUpperCase());
228 } catch (IllegalArgumentException e) {
229 lm = null;
230 }
231 }
232 if (lm == null || !lm.validOnOses.contains(Os.this)) {
233 throw new Error(
234 s + " is not a supported " +
235 "process launch mechanism on this platform."
236 );
237 }
238 return lm;
239 }
240 }
241 );
242 }
243
244 static Os get() {
245 String osName = AccessController.doPrivileged(
246 new PrivilegedAction<String>() {
247 public String run() { return System.getProperty("os.name"); }
248 }
249 );
250
251 if (osName.equals("Linux")) { return LINUX; }
252 if (osName.startsWith("Mac")) { return BSD; }
253 if (osName.equals("Solaris") || osName.equals("SunOS")) { return SOLARIS; }
254 if (osName.equals("AIX")) { return AIX; }
255
256 throw new Error(osName + " is not a supported OS platform.");
257 }
258 }
259
260 private static enum LaunchMechanism {
261 FORK(1, EnumSet.of(Os.LINUX, Os.BSD, Os.SOLARIS, Os.AIX)),
262 POSIX_SPAWN(2, EnumSet.of(Os.BSD, Os.SOLARIS, Os.AIX)),
263 VFORK(3, EnumSet.of(Os.LINUX));
264
265 final int value;
266 final EnumSet<Os> validOnOses;
267
268 LaunchMechanism(int value, EnumSet<Os> validOnOses) {
269 this.value = value;
270 this.validOnOses = validOnOses;
271 }
272 }
273
274 private static final Os OS = Os.get();
275 private static final LaunchMechanism launchMechanism = OS.launchMechanism();
276 private static final byte[] helperpath = toCString(OS.helperPath());
277
278 private static byte[] toCString(String s) {
279 if (s == null)
280 return null;
281 byte[] bytes = s.getBytes();
282 byte[] result = new byte[bytes.length + 1];
283 System.arraycopy(bytes, 0,
284 result, 0,
285 bytes.length);
286 result[result.length-1] = (byte)0;
287 return result;
288 }
289
290 /* this is for the reaping thread */
291 native int waitForProcessExit(int pid);
292
293 /**
294 * Create a process. Depending on the mode flag, this is done by
295 * one of the following mechanisms.
296 * - fork(2) and exec(2)
297 * - clone(2) and exec(2)
298 * - vfork(2) and exec(2)
299 *
300 * @param fds an array of three file descriptors.
301 * Indexes 0, 1, and 2 correspond to standard input,
302 * standard output and standard error, respectively. On
303 * input, a value of -1 means to create a pipe to connect
304 * child and parent processes. On output, a value which
305 * is not -1 is the parent pipe fd corresponding to the
306 * pipe which has been created. An element of this array
307 * is -1 on input if and only if it is <em>not</em> -1 on
308 * output.
309 * @return the pid of the subprocess
310 */
311 private native int forkAndExec(int mode, byte[] helperpath,
329 ThreadGroup root = Thread.currentThread().getThreadGroup();
330 while (root.getParent() != null)
331 root = root.getParent();
332 return root;
333 }});
334 }
335
336 public Thread newThread(Runnable grimReaper) {
337 // Our thread stack requirement is quite modest.
338 Thread t = new Thread(group, grimReaper, "process reaper", 32768);
339 t.setDaemon(true);
340 // A small attempt (probably futile) to avoid priority inversion
341 t.setPriority(Thread.MAX_PRIORITY);
342 return t;
343 }
344 }
345
346 /**
347 * The thread pool of "process reaper" daemon threads.
348 */
349 static final Executor processReaperExecutor =
350 doPrivileged(new PrivilegedAction<Executor>() {
351 public Executor run() {
352 return Executors.newCachedThreadPool
353 (new ProcessReaperThreadFactory());
354 }});
355
356 static UNIXProcess newInstance(byte[] prog,
357 byte[] argBlock, final int argc,
358 byte[] envBlock, final int envc,
359 byte[] dir,
360 int[] fds,
361 boolean redirectErrorStream)
362 throws IOException {
363
364 return OS.newUNIXProcess(prog,
365 argBlock, argc,
366 envBlock, envc,
367 dir,
368 fds,
369 redirectErrorStream);
370 }
371
372 UNIXProcess(final byte[] prog,
373 final byte[] argBlock, final int argc,
374 final byte[] envBlock, final int envc,
375 final byte[] dir,
376 final int[] fds,
377 final boolean redirectErrorStream)
378 throws IOException {
379
380 pid = forkAndExec(launchMechanism.value,
381 helperpath,
382 prog,
383 argBlock, argc,
384 envBlock, envc,
385 dir,
386 fds,
387 redirectErrorStream);
388
389 try {
390 doPrivileged(new PrivilegedExceptionAction<Void>() {
391 public Void run() throws IOException {
392 initStreams(fds);
393 return null;
394 }});
395 } catch (PrivilegedActionException ex) {
396 throw (IOException) ex.getException();
397 }
398 }
399
400 static FileDescriptor newFileDescriptor(int fd) {
401 FileDescriptor fileDescriptor = new FileDescriptor();
402 fdAccess.set(fileDescriptor, fd);
403 return fileDescriptor;
404 }
405
406 abstract void initStreams(int[] fds) throws IOException;
407
408 public OutputStream getOutputStream() {
409 return stdin;
410 }
411
412 public InputStream getInputStream() {
413 return stdout;
414 }
415
416 public InputStream getErrorStream() {
417 return stderr;
418 }
419
420 public synchronized int waitFor() throws InterruptedException {
421 while (!hasExited) {
422 wait();
423 }
424 return exitcode;
425 }
426
432 if (timeout <= 0) return false;
433
434 long timeoutAsNanos = unit.toNanos(timeout);
435 long startTime = System.nanoTime();
436 long rem = timeoutAsNanos;
437
438 while (!hasExited && (rem > 0)) {
439 wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
440 rem = timeoutAsNanos - (System.nanoTime() - startTime);
441 }
442 return hasExited;
443 }
444
445 public synchronized int exitValue() {
446 if (!hasExited) {
447 throw new IllegalThreadStateException("process hasn't exited");
448 }
449 return exitcode;
450 }
451
452 static native void destroyProcess(int pid, boolean force);
453
454 abstract void destroy(boolean force);
455
456 public void destroy() {
457 destroy(false);
458 }
459
460 @Override
461 public Process destroyForcibly() {
462 destroy(true);
463 return this;
464 }
465
466 @Override
467 public synchronized boolean isAlive() {
468 return !hasExited;
469 }
470
471 private static native void init();
472
473 static {
474 init();
475 }
476
477 /**
478 * A buffered input stream for a subprocess pipe file descriptor
479 * that allows the underlying file descriptor to be reclaimed when
480 * the process exits, via the processExited hook.
481 *
482 * This is tricky because we do not want the user-level InputStream to be
483 * closed until the user invokes close(), and we need to continue to be
484 * able to read any buffered data lingering in the OS pipe buffer.
485 */
486 private static class ProcessPipeInputStream extends BufferedInputStream {
487 private final Object closeLock = new Object();
488
489 ProcessPipeInputStream(int fd) {
490 super(new FileInputStream(newFileDescriptor(fd)));
491 }
492 private static byte[] drainInputStream(InputStream in)
493 throws IOException {
494 int n = 0;
495 int j;
496 byte[] a = null;
497 while ((j = in.available()) > 0) {
498 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
499 n += in.read(a, n, j);
500 }
501 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
502 }
503
504 /** Called by the process reaper thread when the process exits. */
505 synchronized void processExited() {
506 synchronized (closeLock) {
516 }
517 } catch (IOException ignored) {}
518 }
519 }
520
521 @Override
522 public void close() throws IOException {
523 // BufferedInputStream#close() is not synchronized unlike most other methods.
524 // Synchronizing helps avoid race with processExited().
525 synchronized (closeLock) {
526 super.close();
527 }
528 }
529 }
530
531 /**
532 * A buffered output stream for a subprocess pipe file descriptor
533 * that allows the underlying file descriptor to be reclaimed when
534 * the process exits, via the processExited hook.
535 */
536 private static class ProcessPipeOutputStream extends BufferedOutputStream {
537 ProcessPipeOutputStream(int fd) {
538 super(new FileOutputStream(newFileDescriptor(fd)));
539 }
540
541 /** Called by the process reaper thread when the process exits. */
542 synchronized void processExited() {
543 OutputStream out = this.out;
544 if (out != null) {
545 try {
546 out.close();
547 } catch (IOException ignored) {
548 // We know of no reason to get an IOException, but if
549 // we do, there's nothing else to do but carry on.
550 }
551 this.out = ProcessBuilder.NullOutputStream.INSTANCE;
552 }
553 }
554 }
555
556 // A FileInputStream that supports the deferment of the actual close
557 // operation until the last pending I/O operation on the stream has
558 // finished. This is required on Solaris because we must close the stdin
559 // and stdout streams in the destroy method in order to reclaim the
560 // underlying file descriptors. Doing so, however, causes any thread
561 // currently blocked in a read on one of those streams to receive an
562 // IOException("Bad file number"), which is incompatible with historical
563 // behavior. By deferring the close we allow any pending reads to see -1
564 // (EOF) as they did before.
565 //
566 private static class DeferredCloseInputStream extends FileInputStream
567 {
568 DeferredCloseInputStream(FileDescriptor fd) {
569 super(fd);
570 }
571
572 private Object lock = new Object(); // For the following fields
573 private boolean closePending = false;
574 private int useCount = 0;
575 private InputStream streamToClose;
576
577 private void raise() {
578 synchronized (lock) {
579 useCount++;
580 }
581 }
582
583 private void lower() throws IOException {
584 synchronized (lock) {
585 useCount--;
586 if (useCount == 0 && closePending) {
587 streamToClose.close();
588 }
589 }
590 }
591
592 // stc is the actual stream to be closed; it might be this object, or
593 // it might be an upstream object for which this object is downstream.
594 //
595 private void closeDeferred(InputStream stc) throws IOException {
596 synchronized (lock) {
597 if (useCount == 0) {
598 stc.close();
599 } else {
600 closePending = true;
601 streamToClose = stc;
602 }
603 }
604 }
605
606 public void close() throws IOException {
607 synchronized (lock) {
608 useCount = 0;
609 closePending = false;
610 }
611 super.close();
612 }
613
614 public int read() throws IOException {
615 raise();
616 try {
617 return super.read();
618 } finally {
619 lower();
620 }
621 }
622
623 public int read(byte[] b) throws IOException {
624 raise();
625 try {
626 return super.read(b);
627 } finally {
628 lower();
629 }
630 }
631
632 public int read(byte[] b, int off, int len) throws IOException {
633 raise();
634 try {
635 return super.read(b, off, len);
636 } finally {
637 lower();
638 }
639 }
640
641 public long skip(long n) throws IOException {
642 raise();
643 try {
644 return super.skip(n);
645 } finally {
646 lower();
647 }
648 }
649
650 public int available() throws IOException {
651 raise();
652 try {
653 return super.available();
654 } finally {
655 lower();
656 }
657 }
658 }
659
660 /**
661 * A buffered input stream for a subprocess pipe file descriptor
662 * that allows the underlying file descriptor to be reclaimed when
663 * the process exits, via the processExited hook.
664 *
665 * This is tricky because we do not want the user-level InputStream to be
666 * closed until the user invokes close(), and we need to continue to be
667 * able to read any buffered data lingering in the OS pipe buffer.
668 *
669 * On AIX this is especially tricky, because the 'close()' system call
670 * will block if another thread is at the same time blocked in a file
671 * operation (e.g. 'read()') on the same file descriptor. We therefore
672 * combine 'ProcessPipeInputStream' approach used on Linux and Bsd
673 * with the DeferredCloseInputStream approach used on Solaris. This means
674 * that every potentially blocking operation on the file descriptor
675 * increments a counter before it is executed and decrements it once it
676 * finishes. The 'close()' operation will only be executed if there are
677 * no pending operations. Otherwise it is deferred after the last pending
678 * operation has finished.
679 *
680 */
681 private static class DeferredCloseProcessPipeInputStream extends BufferedInputStream {
682 private final Object closeLock = new Object();
683 private int useCount = 0;
684 private boolean closePending = false;
685
686 DeferredCloseProcessPipeInputStream(int fd) {
687 super(new FileInputStream(newFileDescriptor(fd)));
688 }
689
690 private InputStream drainInputStream(InputStream in)
691 throws IOException {
692 int n = 0;
693 int j;
694 byte[] a = null;
695 synchronized (closeLock) {
696 if (buf == null) // asynchronous close()?
697 return null; // discard
698 j = in.available();
699 }
700 while (j > 0) {
701 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
702 synchronized (closeLock) {
703 if (buf == null) // asynchronous close()?
704 return null; // discard
705 n += in.read(a, n, j);
706 j = in.available();
707 }
708 }
709 return (a == null) ?
710 ProcessBuilder.NullInputStream.INSTANCE :
711 new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));
712 }
713
714 /** Called by the process reaper thread when the process exits. */
715 synchronized void processExited() {
716 try {
717 InputStream in = this.in;
718 if (in != null) {
719 InputStream stragglers = drainInputStream(in);
720 in.close();
721 this.in = stragglers;
722 }
723 } catch (IOException ignored) { }
724 }
725
726 private void raise() {
727 synchronized (closeLock) {
728 useCount++;
729 }
730 }
731
732 private void lower() throws IOException {
733 synchronized (closeLock) {
734 useCount--;
735 if (useCount == 0 && closePending) {
736 closePending = false;
737 super.close();
738 }
739 }
740 }
741
742 @Override
743 public int read() throws IOException {
744 raise();
745 try {
746 return super.read();
747 } finally {
748 lower();
749 }
750 }
751
752 @Override
753 public int read(byte[] b) throws IOException {
754 raise();
755 try {
756 return super.read(b);
757 } finally {
758 lower();
759 }
760 }
761
762 @Override
763 public int read(byte[] b, int off, int len) throws IOException {
764 raise();
765 try {
766 return super.read(b, off, len);
767 } finally {
768 lower();
769 }
770 }
771
772 @Override
773 public long skip(long n) throws IOException {
774 raise();
775 try {
776 return super.skip(n);
777 } finally {
778 lower();
779 }
780 }
781
782 @Override
783 public int available() throws IOException {
784 raise();
785 try {
786 return super.available();
787 } finally {
788 lower();
789 }
790 }
791
792 @Override
793 public void close() throws IOException {
794 // BufferedInputStream#close() is not synchronized unlike most other methods.
795 // Synchronizing helps avoid racing with drainInputStream().
796 synchronized (closeLock) {
797 if (useCount == 0) {
798 super.close();
799 }
800 else {
801 closePending = true;
802 }
803 }
804 }
805 }
806
807 // specific variants of UNIXProcess as subclasses...
808
809 private static final class LinuxOrBsdProcess extends UNIXProcess {
810
811 LinuxOrBsdProcess(
812 byte[] prog,
813 byte[] argBlock, int argc,
814 byte[] envBlock, int envc,
815 byte[] dir,
816 int[] fds,
817 boolean redirectErrorStream
818 )
819 throws IOException {
820
821 super(prog,
822 argBlock, argc,
823 envBlock, envc,
824 dir,
825 fds,
826 redirectErrorStream);
827 }
828
829 @Override
830 void initStreams(int[] fds) throws IOException {
831 stdin = (fds[0] == -1) ?
832 ProcessBuilder.NullOutputStream.INSTANCE :
833 new ProcessPipeOutputStream(fds[0]);
834
835 stdout = (fds[1] == -1) ?
836 ProcessBuilder.NullInputStream.INSTANCE :
837 new ProcessPipeInputStream(fds[1]);
838
839 stderr = (fds[2] == -1) ?
840 ProcessBuilder.NullInputStream.INSTANCE :
841 new ProcessPipeInputStream(fds[2]);
842
843 processReaperExecutor.execute(new Runnable() {
844 public void run() {
845 int exitcode = waitForProcessExit(pid);
846
847 synchronized (LinuxOrBsdProcess.this) {
848 LinuxOrBsdProcess.this.exitcode = exitcode;
849 LinuxOrBsdProcess.this.hasExited = true;
850 LinuxOrBsdProcess.this.notifyAll();
851 }
852
853 if (stdout instanceof ProcessPipeInputStream)
854 ((ProcessPipeInputStream) stdout).processExited();
855
856 if (stderr instanceof ProcessPipeInputStream)
857 ((ProcessPipeInputStream) stderr).processExited();
858
859 if (stdin instanceof ProcessPipeOutputStream)
860 ((ProcessPipeOutputStream) stdin).processExited();
861 }});
862 }
863
864 @Override
865 void destroy(boolean force) {
866 // There is a risk that pid will be recycled, causing us to
867 // kill the wrong process! So we only terminate processes
868 // that appear to still be running. Even with this check,
869 // there is an unavoidable race condition here, but the window
870 // is very small, and OSes try hard to not recycle pids too
871 // soon, so this is quite safe.
872 synchronized (this) {
873 if (!hasExited)
874 destroyProcess(pid, force);
875 }
876 try { stdin.close(); } catch (IOException ignored) {}
877 try { stdout.close(); } catch (IOException ignored) {}
878 try { stderr.close(); } catch (IOException ignored) {}
879 }
880 }
881
882 private static final class SolarisProcess extends UNIXProcess {
883
884 private /* final */ DeferredCloseInputStream stdout_inner_stream;
885
886 SolarisProcess(
887 byte[] prog,
888 byte[] argBlock, int argc,
889 byte[] envBlock, int envc,
890 byte[] dir,
891 int[] fds,
892 boolean redirectErrorStream
893 )
894 throws IOException {
895
896 super(prog,
897 argBlock, argc,
898 envBlock, envc,
899 dir,
900 fds,
901 redirectErrorStream);
902 }
903
904 @Override
905 void initStreams(int[] fds) throws IOException {
906 stdin = (fds[0] == -1) ?
907 ProcessBuilder.NullOutputStream.INSTANCE :
908 new BufferedOutputStream(
909 new FileOutputStream(newFileDescriptor(fds[0])));
910
911 stdout = (fds[1] == -1) ?
912 ProcessBuilder.NullInputStream.INSTANCE :
913 new BufferedInputStream(
914 stdout_inner_stream =
915 new DeferredCloseInputStream(newFileDescriptor(fds[1])));
916
917 stderr = (fds[2] == -1) ?
918 ProcessBuilder.NullInputStream.INSTANCE :
919 new DeferredCloseInputStream(newFileDescriptor(fds[2]));
920
921 /*
922 * For each subprocess forked a corresponding reaper task
923 * is submitted. That task is the only thread which waits
924 * for the subprocess to terminate and it doesn't hold any
925 * locks while doing so. This design allows waitFor() and
926 * exitStatus() to be safely executed in parallel (and they
927 * need no native code).
928 */
929 processReaperExecutor.execute(new Runnable() {
930 public void run() {
931 int exitcode = waitForProcessExit(pid);
932
933 synchronized (SolarisProcess.this) {
934 SolarisProcess.this.exitcode = exitcode;
935 SolarisProcess.this.hasExited = true;
936 SolarisProcess.this.notifyAll();
937 }
938 }});
939 }
940
941 @Override
942 synchronized void destroy(boolean force) {
943 // There is a risk that pid will be recycled, causing us to
944 // kill the wrong process! So we only terminate processes
945 // that appear to still be running. Even with this check,
946 // there is an unavoidable race condition here, but the window
947 // is very small, and OSes try hard to not recycle pids too
948 // soon, so this is quite safe.
949 if (!hasExited)
950 destroyProcess(pid, force);
951 try {
952 stdin.close();
953 if (stdout_inner_stream != null)
954 stdout_inner_stream.closeDeferred(stdout);
955 if (stderr instanceof DeferredCloseInputStream)
956 ((DeferredCloseInputStream) stderr)
957 .closeDeferred(stderr);
958 } catch (IOException e) {
959 // ignore
960 }
961 }
962 }
963
964 static final class AixProcess extends UNIXProcess {
965 AixProcess(
966 byte[] prog,
967 byte[] argBlock, int argc,
968 byte[] envBlock, int envc,
969 byte[] dir,
970 int[] fds,
971 boolean redirectErrorStream
972 )
973 throws IOException {
974
975 super(prog,
976 argBlock, argc,
977 envBlock, envc,
978 dir,
979 fds,
980 redirectErrorStream);
981 }
982
983 @Override
984 void initStreams(int[] fds) throws IOException {
985 stdin = (fds[0] == -1) ?
986 ProcessBuilder.NullOutputStream.INSTANCE :
987 new ProcessPipeOutputStream(fds[0]);
988
989 stdout = (fds[1] == -1) ?
990 ProcessBuilder.NullInputStream.INSTANCE :
991 new DeferredCloseProcessPipeInputStream(fds[1]);
992
993 stderr = (fds[2] == -1) ?
994 ProcessBuilder.NullInputStream.INSTANCE :
995 new DeferredCloseProcessPipeInputStream(fds[2]);
996
997 processReaperExecutor.execute(new Runnable() {
998 public void run() {
999 int exitcode = waitForProcessExit(pid);
1000
1001 synchronized (AixProcess.this) {
1002 AixProcess.this.exitcode = exitcode;
1003 AixProcess.this.hasExited = true;
1004 AixProcess.this.notifyAll();
1005 }
1006
1007 if (stdout instanceof DeferredCloseProcessPipeInputStream)
1008 ((DeferredCloseProcessPipeInputStream) stdout).processExited();
1009
1010 if (stderr instanceof DeferredCloseProcessPipeInputStream)
1011 ((DeferredCloseProcessPipeInputStream) stderr).processExited();
1012
1013 if (stdin instanceof ProcessPipeOutputStream)
1014 ((ProcessPipeOutputStream) stdin).processExited();
1015 }});
1016 }
1017
1018 @Override
1019 void destroy(boolean force) {
1020 // There is a risk that pid will be recycled, causing us to
1021 // kill the wrong process! So we only terminate processes
1022 // that appear to still be running. Even with this check,
1023 // there is an unavoidable race condition here, but the window
1024 // is very small, and OSes try hard to not recycle pids too
1025 // soon, so this is quite safe.
1026 synchronized (this) {
1027 if (!hasExited)
1028 destroyProcess(pid, force);
1029 }
1030 try { stdin.close(); } catch (IOException ignored) {}
1031 try { stdout.close(); } catch (IOException ignored) {}
1032 try { stderr.close(); } catch (IOException ignored) {}
1033 }
1034 }
1035 }
|