blob: 5f9a546f4bd0994dc531467de79c3fa97ad5cd72 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
Roger Riggsaa6b19f2015-01-26 10:55:27 -05002 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
J. Duke319a3b92007-12-01 00:00:00 +00003 * 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
Kelly O'Hairfe008ae2010-05-25 15:58:33 -07007 * published by the Free Software Foundation. Oracle designates this
J. Duke319a3b92007-12-01 00:00:00 +00008 * particular file as subject to the "Classpath" exception as provided
Kelly O'Hairfe008ae2010-05-25 15:58:33 -07009 * by Oracle in the LICENSE file that accompanied this code.
J. Duke319a3b92007-12-01 00:00:00 +000010 *
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 *
Kelly O'Hairfe008ae2010-05-25 15:58:33 -070021 * 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.
J. Duke319a3b92007-12-01 00:00:00 +000024 */
25
26package java.lang;
27
Roger Riggsaa6b19f2015-01-26 10:55:27 -050028import java.lang.ProcessBuilder.Redirect;
29import java.io.BufferedInputStream;
30import java.io.BufferedOutputStream;
31import java.io.ByteArrayInputStream;
32import java.io.FileDescriptor;
Martin Buchholzabde1242008-03-10 14:32:51 -070033import java.io.FileInputStream;
34import java.io.FileOutputStream;
Roger Riggsaa6b19f2015-01-26 10:55:27 -050035import java.io.IOException;
36import java.io.InputStream;
37import java.io.OutputStream;
38import java.util.Arrays;
39import java.util.EnumSet;
40import java.util.Locale;
41import java.util.Set;
Roger Riggs103d99b2015-05-29 14:04:12 -040042import java.util.concurrent.CompletableFuture;
Roger Riggsaa6b19f2015-01-26 10:55:27 -050043import java.util.concurrent.TimeUnit;
44import java.security.AccessController;
45import static java.security.AccessController.doPrivileged;
46import java.security.PrivilegedAction;
47import java.security.PrivilegedActionException;
48import java.security.PrivilegedExceptionAction;
Chris Hegarty0cc24c22015-09-28 13:39:27 +010049import jdk.internal.misc.JavaIOFileDescriptorAccess;
50import jdk.internal.misc.SharedSecrets;
J. Duke319a3b92007-12-01 00:00:00 +000051
52/**
Roger Riggs103d99b2015-05-29 14:04:12 -040053 * java.lang.Process subclass in the UNIX environment.
J. Duke319a3b92007-12-01 00:00:00 +000054 *
Roger Riggsaa6b19f2015-01-26 10:55:27 -050055 * @author Mario Wolczko and Ross Knippel.
56 * @author Konstantin Kladko (ported to Linux and Bsd)
J. Duke319a3b92007-12-01 00:00:00 +000057 * @author Martin Buchholz
Roger Riggsaa6b19f2015-01-26 10:55:27 -050058 * @author Volker Simonis (ported to AIX)
J. Duke319a3b92007-12-01 00:00:00 +000059 * @since 1.5
60 */
Roger Riggsaa6b19f2015-01-26 10:55:27 -050061final class ProcessImpl extends Process {
Chris Hegarty0cc24c22015-09-28 13:39:27 +010062 private static final JavaIOFileDescriptorAccess fdAccess
63 = SharedSecrets.getJavaIOFileDescriptorAccess();
Martin Buchholzabde1242008-03-10 14:32:51 -070064
Roger Riggs103d99b2015-05-29 14:04:12 -040065 // Linux platforms support a normal (non-forcible) kill signal.
66 static final boolean SUPPORTS_NORMAL_TERMINATION = true;
67
Roger Riggsaa6b19f2015-01-26 10:55:27 -050068 private final int pid;
Roger Riggs8477d882015-07-14 15:35:37 -040069 private final ProcessHandleImpl processHandle;
Roger Riggsaa6b19f2015-01-26 10:55:27 -050070 private int exitcode;
71 private boolean hasExited;
72
73 private /* final */ OutputStream stdin;
Roger Riggs103d99b2015-05-29 14:04:12 -040074 private /* final */ InputStream stdout;
Roger Riggsaa6b19f2015-01-26 10:55:27 -050075 private /* final */ InputStream stderr;
76
77 // only used on Solaris
78 private /* final */ DeferredCloseInputStream stdout_inner_stream;
79
80 private static enum LaunchMechanism {
81 // order IS important!
82 FORK,
83 POSIX_SPAWN,
84 VFORK
85 }
86
87 private static enum Platform {
88
89 LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK),
90
91 BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
92
93 SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
94
95 AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
96
97 final LaunchMechanism defaultLaunchMechanism;
98 final Set<LaunchMechanism> validLaunchMechanisms;
99
100 Platform(LaunchMechanism ... launchMechanisms) {
101 this.defaultLaunchMechanism = launchMechanisms[0];
102 this.validLaunchMechanisms =
Roger Riggs103d99b2015-05-29 14:04:12 -0400103 EnumSet.copyOf(Arrays.asList(launchMechanisms));
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500104 }
105
106 @SuppressWarnings("fallthrough")
107 private String helperPath(String javahome, String osArch) {
108 switch (this) {
109 case SOLARIS:
110 if (osArch.equals("x86")) { osArch = "i386"; }
111 else if (osArch.equals("x86_64")) { osArch = "amd64"; }
112 // fall through...
113 case LINUX:
114 case AIX:
115 return javahome + "/lib/" + osArch + "/jspawnhelper";
116
117 case BSD:
118 return javahome + "/lib/jspawnhelper";
119
120 default:
121 throw new AssertionError("Unsupported platform: " + this);
122 }
123 }
124
125 String helperPath() {
126 return AccessController.doPrivileged(
Roger Riggs103d99b2015-05-29 14:04:12 -0400127 (PrivilegedAction<String>) () ->
128 helperPath(System.getProperty("java.home"),
129 System.getProperty("os.arch"))
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500130 );
131 }
132
133 LaunchMechanism launchMechanism() {
134 return AccessController.doPrivileged(
Roger Riggs103d99b2015-05-29 14:04:12 -0400135 (PrivilegedAction<LaunchMechanism>) () -> {
136 String s = System.getProperty(
137 "jdk.lang.Process.launchMechanism");
138 LaunchMechanism lm;
139 if (s == null) {
140 lm = defaultLaunchMechanism;
141 s = lm.name().toLowerCase(Locale.ENGLISH);
142 } else {
143 try {
144 lm = LaunchMechanism.valueOf(
145 s.toUpperCase(Locale.ENGLISH));
146 } catch (IllegalArgumentException e) {
147 lm = null;
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500148 }
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500149 }
Roger Riggs103d99b2015-05-29 14:04:12 -0400150 if (lm == null || !validLaunchMechanisms.contains(lm)) {
151 throw new Error(
152 s + " is not a supported " +
153 "process launch mechanism on this platform."
154 );
155 }
156 return lm;
157 }
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500158 );
159 }
160
161 static Platform get() {
162 String osName = AccessController.doPrivileged(
Roger Riggs103d99b2015-05-29 14:04:12 -0400163 (PrivilegedAction<String>) () -> System.getProperty("os.name")
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500164 );
165
166 if (osName.equals("Linux")) { return LINUX; }
167 if (osName.contains("OS X")) { return BSD; }
168 if (osName.equals("SunOS")) { return SOLARIS; }
169 if (osName.equals("AIX")) { return AIX; }
170
171 throw new Error(osName + " is not a supported OS platform.");
172 }
173 }
174
175 private static final Platform platform = Platform.get();
176 private static final LaunchMechanism launchMechanism = platform.launchMechanism();
177 private static final byte[] helperpath = toCString(platform.helperPath());
178
J. Duke319a3b92007-12-01 00:00:00 +0000179 private static byte[] toCString(String s) {
180 if (s == null)
181 return null;
182 byte[] bytes = s.getBytes();
183 byte[] result = new byte[bytes.length + 1];
184 System.arraycopy(bytes, 0,
Roger Riggs103d99b2015-05-29 14:04:12 -0400185 result, 0,
186 bytes.length);
J. Duke319a3b92007-12-01 00:00:00 +0000187 result[result.length-1] = (byte)0;
188 return result;
189 }
190
191 // Only for use by ProcessBuilder.start()
192 static Process start(String[] cmdarray,
193 java.util.Map<String,String> environment,
194 String dir,
Martin Buchholzabde1242008-03-10 14:32:51 -0700195 ProcessBuilder.Redirect[] redirects,
J. Duke319a3b92007-12-01 00:00:00 +0000196 boolean redirectErrorStream)
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500197 throws IOException
J. Duke319a3b92007-12-01 00:00:00 +0000198 {
199 assert cmdarray != null && cmdarray.length > 0;
200
201 // Convert arguments to a contiguous block; it's easier to do
202 // memory management in Java than in C.
203 byte[][] args = new byte[cmdarray.length-1][];
204 int size = args.length; // For added NUL bytes
205 for (int i = 0; i < args.length; i++) {
206 args[i] = cmdarray[i+1].getBytes();
207 size += args[i].length;
208 }
209 byte[] argBlock = new byte[size];
210 int i = 0;
211 for (byte[] arg : args) {
212 System.arraycopy(arg, 0, argBlock, i, arg.length);
213 i += arg.length + 1;
214 // No need to write NUL bytes explicitly
215 }
216
217 int[] envc = new int[1];
218 byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
219
Martin Buchholzabde1242008-03-10 14:32:51 -0700220 int[] std_fds;
221
222 FileInputStream f0 = null;
223 FileOutputStream f1 = null;
224 FileOutputStream f2 = null;
225
226 try {
Roger Riggs2714bda2015-11-13 15:48:59 -0500227 boolean forceNullOutputStream = false;
Martin Buchholzabde1242008-03-10 14:32:51 -0700228 if (redirects == null) {
229 std_fds = new int[] { -1, -1, -1 };
230 } else {
231 std_fds = new int[3];
232
Roger Riggs2714bda2015-11-13 15:48:59 -0500233 if (redirects[0] == Redirect.PIPE) {
Martin Buchholzabde1242008-03-10 14:32:51 -0700234 std_fds[0] = -1;
Roger Riggs2714bda2015-11-13 15:48:59 -0500235 } else if (redirects[0] == Redirect.INHERIT) {
Martin Buchholzabde1242008-03-10 14:32:51 -0700236 std_fds[0] = 0;
Roger Riggs2714bda2015-11-13 15:48:59 -0500237 } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
238 std_fds[0] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd());
239 } else {
Martin Buchholzabde1242008-03-10 14:32:51 -0700240 f0 = new FileInputStream(redirects[0].file());
241 std_fds[0] = fdAccess.get(f0.getFD());
242 }
243
Roger Riggs2714bda2015-11-13 15:48:59 -0500244 if (redirects[1] == Redirect.PIPE) {
Martin Buchholzabde1242008-03-10 14:32:51 -0700245 std_fds[1] = -1;
Roger Riggs2714bda2015-11-13 15:48:59 -0500246 } else if (redirects[1] == Redirect.INHERIT) {
Martin Buchholzabde1242008-03-10 14:32:51 -0700247 std_fds[1] = 1;
Roger Riggs2714bda2015-11-13 15:48:59 -0500248 } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
249 std_fds[1] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
250 // Force getInputStream to return a null stream,
251 // the fd is directly assigned to the next process.
252 forceNullOutputStream = true;
253 } else {
Alan Bateman3111dfa2010-12-01 13:49:02 +0000254 f1 = new FileOutputStream(redirects[1].file(),
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500255 redirects[1].append());
Martin Buchholzabde1242008-03-10 14:32:51 -0700256 std_fds[1] = fdAccess.get(f1.getFD());
257 }
258
Roger Riggs2714bda2015-11-13 15:48:59 -0500259 if (redirects[2] == Redirect.PIPE) {
Martin Buchholzabde1242008-03-10 14:32:51 -0700260 std_fds[2] = -1;
Roger Riggs2714bda2015-11-13 15:48:59 -0500261 } else if (redirects[2] == Redirect.INHERIT) {
Martin Buchholzabde1242008-03-10 14:32:51 -0700262 std_fds[2] = 2;
Roger Riggs2714bda2015-11-13 15:48:59 -0500263 } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
264 std_fds[2] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
265 } else {
Alan Bateman3111dfa2010-12-01 13:49:02 +0000266 f2 = new FileOutputStream(redirects[2].file(),
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500267 redirects[2].append());
Martin Buchholzabde1242008-03-10 14:32:51 -0700268 std_fds[2] = fdAccess.get(f2.getFD());
269 }
270 }
271
Roger Riggs2714bda2015-11-13 15:48:59 -0500272 Process p = new ProcessImpl
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500273 (toCString(cmdarray[0]),
274 argBlock, args.length,
275 envBlock, envc[0],
276 toCString(dir),
277 std_fds,
Roger Riggs2714bda2015-11-13 15:48:59 -0500278 forceNullOutputStream,
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500279 redirectErrorStream);
Roger Riggs2714bda2015-11-13 15:48:59 -0500280 if (redirects != null) {
281 // Copy the fd's if they are to be redirected to another process
282 if (std_fds[0] >= 0 &&
283 redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
284 fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), std_fds[0]);
285 }
286 if (std_fds[1] >= 0 &&
287 redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
288 fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), std_fds[1]);
289 }
290 if (std_fds[2] >= 0 &&
291 redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
292 fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), std_fds[2]);
293 }
294 }
295 return p;
Martin Buchholzabde1242008-03-10 14:32:51 -0700296 } finally {
297 // In theory, close() can throw IOException
298 // (although it is rather unlikely to happen here)
299 try { if (f0 != null) f0.close(); }
300 finally {
301 try { if (f1 != null) f1.close(); }
302 finally { if (f2 != null) f2.close(); }
303 }
304 }
J. Duke319a3b92007-12-01 00:00:00 +0000305 }
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500306
307
308 /**
309 * Creates a process. Depending on the {@code mode} flag, this is done by
310 * one of the following mechanisms:
311 * <pre>
312 * 1 - fork(2) and exec(2)
313 * 2 - posix_spawn(3P)
314 * 3 - vfork(2) and exec(2)
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500315 * </pre>
316 * @param fds an array of three file descriptors.
317 * Indexes 0, 1, and 2 correspond to standard input,
318 * standard output and standard error, respectively. On
319 * input, a value of -1 means to create a pipe to connect
320 * child and parent processes. On output, a value which
321 * is not -1 is the parent pipe fd corresponding to the
322 * pipe which has been created. An element of this array
323 * is -1 on input if and only if it is <em>not</em> -1 on
324 * output.
325 * @return the pid of the subprocess
326 */
327 private native int forkAndExec(int mode, byte[] helperpath,
328 byte[] prog,
329 byte[] argBlock, int argc,
330 byte[] envBlock, int envc,
331 byte[] dir,
332 int[] fds,
333 boolean redirectErrorStream)
Roger Riggs103d99b2015-05-29 14:04:12 -0400334 throws IOException;
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500335
336 private ProcessImpl(final byte[] prog,
337 final byte[] argBlock, final int argc,
338 final byte[] envBlock, final int envc,
339 final byte[] dir,
340 final int[] fds,
Roger Riggs2714bda2015-11-13 15:48:59 -0500341 final boolean forceNullOutputStream,
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500342 final boolean redirectErrorStream)
343 throws IOException {
344
345 pid = forkAndExec(launchMechanism.ordinal() + 1,
Roger Riggs103d99b2015-05-29 14:04:12 -0400346 helperpath,
347 prog,
348 argBlock, argc,
349 envBlock, envc,
350 dir,
351 fds,
352 redirectErrorStream);
Roger Riggs8477d882015-07-14 15:35:37 -0400353 processHandle = ProcessHandleImpl.getInternal(pid);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500354
355 try {
356 doPrivileged((PrivilegedExceptionAction<Void>) () -> {
Roger Riggs2714bda2015-11-13 15:48:59 -0500357 initStreams(fds, forceNullOutputStream);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500358 return null;
359 });
360 } catch (PrivilegedActionException ex) {
361 throw (IOException) ex.getException();
362 }
363 }
364
365 static FileDescriptor newFileDescriptor(int fd) {
366 FileDescriptor fileDescriptor = new FileDescriptor();
367 fdAccess.set(fileDescriptor, fd);
368 return fileDescriptor;
369 }
370
Roger Riggs2714bda2015-11-13 15:48:59 -0500371 /**
372 * Initialize the streams from the file descriptors.
373 * @param fds array of stdin, stdout, stderr fds
374 * @param forceNullOutputStream true if the stdout is being directed to
375 * a subsequent process. The stdout stream should be a null output stream .
376 * @throws IOException
377 */
378 void initStreams(int[] fds, boolean forceNullOutputStream) throws IOException {
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500379 switch (platform) {
380 case LINUX:
381 case BSD:
382 stdin = (fds[0] == -1) ?
383 ProcessBuilder.NullOutputStream.INSTANCE :
384 new ProcessPipeOutputStream(fds[0]);
385
Roger Riggs2714bda2015-11-13 15:48:59 -0500386 stdout = (fds[1] == -1 || forceNullOutputStream) ?
Roger Riggs103d99b2015-05-29 14:04:12 -0400387 ProcessBuilder.NullInputStream.INSTANCE :
388 new ProcessPipeInputStream(fds[1]);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500389
390 stderr = (fds[2] == -1) ?
Roger Riggs103d99b2015-05-29 14:04:12 -0400391 ProcessBuilder.NullInputStream.INSTANCE :
392 new ProcessPipeInputStream(fds[2]);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500393
Roger Riggs103d99b2015-05-29 14:04:12 -0400394 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500395 synchronized (this) {
Roger Riggs103d99b2015-05-29 14:04:12 -0400396 this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500397 this.hasExited = true;
398 this.notifyAll();
399 }
400
401 if (stdout instanceof ProcessPipeInputStream)
402 ((ProcessPipeInputStream) stdout).processExited();
403
404 if (stderr instanceof ProcessPipeInputStream)
405 ((ProcessPipeInputStream) stderr).processExited();
406
407 if (stdin instanceof ProcessPipeOutputStream)
408 ((ProcessPipeOutputStream) stdin).processExited();
Roger Riggs103d99b2015-05-29 14:04:12 -0400409
410 return null;
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500411 });
412 break;
413
414 case SOLARIS:
415 stdin = (fds[0] == -1) ?
416 ProcessBuilder.NullOutputStream.INSTANCE :
417 new BufferedOutputStream(
Roger Riggs103d99b2015-05-29 14:04:12 -0400418 new FileOutputStream(newFileDescriptor(fds[0])));
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500419
420 stdout = (fds[1] == -1) ?
Roger Riggs103d99b2015-05-29 14:04:12 -0400421 ProcessBuilder.NullInputStream.INSTANCE :
422 new BufferedInputStream(
423 stdout_inner_stream =
424 new DeferredCloseInputStream(
425 newFileDescriptor(fds[1])));
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500426
427 stderr = (fds[2] == -1) ?
Roger Riggs103d99b2015-05-29 14:04:12 -0400428 ProcessBuilder.NullInputStream.INSTANCE :
429 new DeferredCloseInputStream(newFileDescriptor(fds[2]));
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500430
431 /*
432 * For each subprocess forked a corresponding reaper task
433 * is submitted. That task is the only thread which waits
434 * for the subprocess to terminate and it doesn't hold any
435 * locks while doing so. This design allows waitFor() and
436 * exitStatus() to be safely executed in parallel (and they
437 * need no native code).
438 */
Roger Riggs103d99b2015-05-29 14:04:12 -0400439 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500440 synchronized (this) {
Roger Riggs103d99b2015-05-29 14:04:12 -0400441 this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500442 this.hasExited = true;
443 this.notifyAll();
444 }
Roger Riggs103d99b2015-05-29 14:04:12 -0400445 return null;
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500446 });
447 break;
448
449 case AIX:
450 stdin = (fds[0] == -1) ?
451 ProcessBuilder.NullOutputStream.INSTANCE :
452 new ProcessPipeOutputStream(fds[0]);
453
454 stdout = (fds[1] == -1) ?
Roger Riggs103d99b2015-05-29 14:04:12 -0400455 ProcessBuilder.NullInputStream.INSTANCE :
456 new DeferredCloseProcessPipeInputStream(fds[1]);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500457
458 stderr = (fds[2] == -1) ?
Roger Riggs103d99b2015-05-29 14:04:12 -0400459 ProcessBuilder.NullInputStream.INSTANCE :
460 new DeferredCloseProcessPipeInputStream(fds[2]);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500461
Roger Riggs103d99b2015-05-29 14:04:12 -0400462 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500463 synchronized (this) {
Roger Riggs103d99b2015-05-29 14:04:12 -0400464 this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500465 this.hasExited = true;
466 this.notifyAll();
467 }
468
469 if (stdout instanceof DeferredCloseProcessPipeInputStream)
470 ((DeferredCloseProcessPipeInputStream) stdout).processExited();
471
472 if (stderr instanceof DeferredCloseProcessPipeInputStream)
473 ((DeferredCloseProcessPipeInputStream) stderr).processExited();
474
475 if (stdin instanceof ProcessPipeOutputStream)
476 ((ProcessPipeOutputStream) stdin).processExited();
Roger Riggs103d99b2015-05-29 14:04:12 -0400477
478 return null;
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500479 });
480 break;
481
482 default: throw new AssertionError("Unsupported platform: " + platform);
483 }
484 }
485
486 public OutputStream getOutputStream() {
487 return stdin;
488 }
489
490 public InputStream getInputStream() {
491 return stdout;
492 }
493
494 public InputStream getErrorStream() {
495 return stderr;
496 }
497
498 public synchronized int waitFor() throws InterruptedException {
499 while (!hasExited) {
500 wait();
501 }
502 return exitcode;
503 }
504
505 @Override
506 public synchronized boolean waitFor(long timeout, TimeUnit unit)
Roger Riggs103d99b2015-05-29 14:04:12 -0400507 throws InterruptedException
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500508 {
Roger Riggsa0564572015-03-23 10:13:32 -0400509 long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500510 if (hasExited) return true;
511 if (timeout <= 0) return false;
512
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500513 long deadline = System.nanoTime() + remainingNanos;
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500514 do {
515 // Round up to next millisecond
516 wait(TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L));
517 if (hasExited) {
518 return true;
519 }
520 remainingNanos = deadline - System.nanoTime();
521 } while (remainingNanos > 0);
522 return hasExited;
523 }
524
525 public synchronized int exitValue() {
526 if (!hasExited) {
527 throw new IllegalThreadStateException("process hasn't exited");
528 }
529 return exitcode;
530 }
531
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500532 private void destroy(boolean force) {
533 switch (platform) {
534 case LINUX:
535 case BSD:
536 case AIX:
537 // There is a risk that pid will be recycled, causing us to
538 // kill the wrong process! So we only terminate processes
539 // that appear to still be running. Even with this check,
540 // there is an unavoidable race condition here, but the window
541 // is very small, and OSes try hard to not recycle pids too
542 // soon, so this is quite safe.
543 synchronized (this) {
544 if (!hasExited)
Roger Riggs8477d882015-07-14 15:35:37 -0400545 processHandle.destroyProcess(force);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500546 }
547 try { stdin.close(); } catch (IOException ignored) {}
548 try { stdout.close(); } catch (IOException ignored) {}
549 try { stderr.close(); } catch (IOException ignored) {}
550 break;
551
552 case SOLARIS:
553 // There is a risk that pid will be recycled, causing us to
554 // kill the wrong process! So we only terminate processes
555 // that appear to still be running. Even with this check,
556 // there is an unavoidable race condition here, but the window
557 // is very small, and OSes try hard to not recycle pids too
558 // soon, so this is quite safe.
559 synchronized (this) {
560 if (!hasExited)
Roger Riggs8477d882015-07-14 15:35:37 -0400561 processHandle.destroyProcess(force);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500562 try {
563 stdin.close();
564 if (stdout_inner_stream != null)
565 stdout_inner_stream.closeDeferred(stdout);
566 if (stderr instanceof DeferredCloseInputStream)
567 ((DeferredCloseInputStream) stderr)
Roger Riggs103d99b2015-05-29 14:04:12 -0400568 .closeDeferred(stderr);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500569 } catch (IOException e) {
570 // ignore
571 }
572 }
573 break;
574
575 default: throw new AssertionError("Unsupported platform: " + platform);
576 }
577 }
578
Roger Riggs103d99b2015-05-29 14:04:12 -0400579 @Override
580 public CompletableFuture<Process> onExit() {
581 return ProcessHandleImpl.completion(pid, false)
Roger Riggs8477d882015-07-14 15:35:37 -0400582 .handleAsync((unusedExitStatus, unusedThrowable) -> {
Roger Riggscacb7302015-06-17 16:03:49 -0400583 boolean interrupted = false;
584 while (true) {
585 // Ensure that the concurrent task setting the exit status has completed
586 try {
587 waitFor();
588 break;
589 } catch (InterruptedException ie) {
590 interrupted = true;
591 }
592 }
593 if (interrupted) {
594 Thread.currentThread().interrupt();
595 }
596 return this;
597 });
Roger Riggs103d99b2015-05-29 14:04:12 -0400598 }
599
600 @Override
601 public ProcessHandle toHandle() {
602 SecurityManager sm = System.getSecurityManager();
603 if (sm != null) {
604 sm.checkPermission(new RuntimePermission("manageProcess"));
605 }
606 return processHandle;
607 }
608
609 @Override
610 public boolean supportsNormalTermination() {
611 return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
612 }
613
614 @Override
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500615 public void destroy() {
616 destroy(false);
617 }
618
619 @Override
620 public Process destroyForcibly() {
621 destroy(true);
622 return this;
623 }
624
625 @Override
626 public long getPid() {
627 return pid;
628 }
629
630 @Override
631 public synchronized boolean isAlive() {
632 return !hasExited;
633 }
634
635 private static native void init();
636
637 static {
638 init();
639 }
640
641 /**
642 * A buffered input stream for a subprocess pipe file descriptor
643 * that allows the underlying file descriptor to be reclaimed when
644 * the process exits, via the processExited hook.
645 *
646 * This is tricky because we do not want the user-level InputStream to be
647 * closed until the user invokes close(), and we need to continue to be
648 * able to read any buffered data lingering in the OS pipe buffer.
649 */
650 private static class ProcessPipeInputStream extends BufferedInputStream {
651 private final Object closeLock = new Object();
652
653 ProcessPipeInputStream(int fd) {
654 super(new FileInputStream(newFileDescriptor(fd)));
655 }
656 private static byte[] drainInputStream(InputStream in)
657 throws IOException {
658 int n = 0;
659 int j;
660 byte[] a = null;
661 while ((j = in.available()) > 0) {
662 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
663 n += in.read(a, n, j);
664 }
665 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
666 }
667
668 /** Called by the process reaper thread when the process exits. */
669 synchronized void processExited() {
670 synchronized (closeLock) {
671 try {
672 InputStream in = this.in;
673 // this stream is closed if and only if: in == null
674 if (in != null) {
675 byte[] stragglers = drainInputStream(in);
676 in.close();
677 this.in = (stragglers == null) ?
Roger Riggs103d99b2015-05-29 14:04:12 -0400678 ProcessBuilder.NullInputStream.INSTANCE :
679 new ByteArrayInputStream(stragglers);
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500680 }
681 } catch (IOException ignored) {}
682 }
683 }
684
685 @Override
686 public void close() throws IOException {
687 // BufferedInputStream#close() is not synchronized unlike most other
688 // methods. Synchronizing helps avoid race with processExited().
689 synchronized (closeLock) {
690 super.close();
691 }
692 }
693 }
694
695 /**
696 * A buffered output stream for a subprocess pipe file descriptor
697 * that allows the underlying file descriptor to be reclaimed when
698 * the process exits, via the processExited hook.
699 */
700 private static class ProcessPipeOutputStream extends BufferedOutputStream {
701 ProcessPipeOutputStream(int fd) {
702 super(new FileOutputStream(newFileDescriptor(fd)));
703 }
704
705 /** Called by the process reaper thread when the process exits. */
706 synchronized void processExited() {
707 OutputStream out = this.out;
708 if (out != null) {
709 try {
710 out.close();
711 } catch (IOException ignored) {
712 // We know of no reason to get an IOException, but if
713 // we do, there's nothing else to do but carry on.
714 }
715 this.out = ProcessBuilder.NullOutputStream.INSTANCE;
716 }
717 }
718 }
719
720 // A FileInputStream that supports the deferment of the actual close
721 // operation until the last pending I/O operation on the stream has
722 // finished. This is required on Solaris because we must close the stdin
723 // and stdout streams in the destroy method in order to reclaim the
724 // underlying file descriptors. Doing so, however, causes any thread
725 // currently blocked in a read on one of those streams to receive an
726 // IOException("Bad file number"), which is incompatible with historical
727 // behavior. By deferring the close we allow any pending reads to see -1
728 // (EOF) as they did before.
729 //
730 private static class DeferredCloseInputStream extends FileInputStream
731 {
732 DeferredCloseInputStream(FileDescriptor fd) {
733 super(fd);
734 }
735
736 private Object lock = new Object(); // For the following fields
737 private boolean closePending = false;
738 private int useCount = 0;
739 private InputStream streamToClose;
740
741 private void raise() {
742 synchronized (lock) {
743 useCount++;
744 }
745 }
746
747 private void lower() throws IOException {
748 synchronized (lock) {
749 useCount--;
750 if (useCount == 0 && closePending) {
751 streamToClose.close();
752 }
753 }
754 }
755
756 // stc is the actual stream to be closed; it might be this object, or
757 // it might be an upstream object for which this object is downstream.
758 //
759 private void closeDeferred(InputStream stc) throws IOException {
760 synchronized (lock) {
761 if (useCount == 0) {
762 stc.close();
763 } else {
764 closePending = true;
765 streamToClose = stc;
766 }
767 }
768 }
769
770 public void close() throws IOException {
771 synchronized (lock) {
772 useCount = 0;
773 closePending = false;
774 }
775 super.close();
776 }
777
778 public int read() throws IOException {
779 raise();
780 try {
781 return super.read();
782 } finally {
783 lower();
784 }
785 }
786
787 public int read(byte[] b) throws IOException {
788 raise();
789 try {
790 return super.read(b);
791 } finally {
792 lower();
793 }
794 }
795
796 public int read(byte[] b, int off, int len) throws IOException {
797 raise();
798 try {
799 return super.read(b, off, len);
800 } finally {
801 lower();
802 }
803 }
804
805 public long skip(long n) throws IOException {
806 raise();
807 try {
808 return super.skip(n);
809 } finally {
810 lower();
811 }
812 }
813
814 public int available() throws IOException {
815 raise();
816 try {
817 return super.available();
818 } finally {
819 lower();
820 }
821 }
822 }
823
824 /**
825 * A buffered input stream for a subprocess pipe file descriptor
826 * that allows the underlying file descriptor to be reclaimed when
827 * the process exits, via the processExited hook.
828 *
829 * This is tricky because we do not want the user-level InputStream to be
830 * closed until the user invokes close(), and we need to continue to be
831 * able to read any buffered data lingering in the OS pipe buffer.
832 *
833 * On AIX this is especially tricky, because the 'close()' system call
834 * will block if another thread is at the same time blocked in a file
835 * operation (e.g. 'read()') on the same file descriptor. We therefore
836 * combine 'ProcessPipeInputStream' approach used on Linux and Bsd
837 * with the DeferredCloseInputStream approach used on Solaris. This means
838 * that every potentially blocking operation on the file descriptor
839 * increments a counter before it is executed and decrements it once it
840 * finishes. The 'close()' operation will only be executed if there are
841 * no pending operations. Otherwise it is deferred after the last pending
842 * operation has finished.
843 *
844 */
845 private static class DeferredCloseProcessPipeInputStream
Roger Riggs103d99b2015-05-29 14:04:12 -0400846 extends BufferedInputStream {
Roger Riggsaa6b19f2015-01-26 10:55:27 -0500847
848 private final Object closeLock = new Object();
849 private int useCount = 0;
850 private boolean closePending = false;
851
852 DeferredCloseProcessPipeInputStream(int fd) {
853 super(new FileInputStream(newFileDescriptor(fd)));
854 }
855
856 private InputStream drainInputStream(InputStream in)
857 throws IOException {
858 int n = 0;
859 int j;
860 byte[] a = null;
861 synchronized (closeLock) {
862 if (buf == null) // asynchronous close()?
863 return null; // discard
864 j = in.available();
865 }
866 while (j > 0) {
867 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
868 synchronized (closeLock) {
869 if (buf == null) // asynchronous close()?
870 return null; // discard
871 n += in.read(a, n, j);
872 j = in.available();
873 }
874 }
875 return (a == null) ?
876 ProcessBuilder.NullInputStream.INSTANCE :
877 new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));
878 }
879
880 /** Called by the process reaper thread when the process exits. */
881 synchronized void processExited() {
882 try {
883 InputStream in = this.in;
884 if (in != null) {
885 InputStream stragglers = drainInputStream(in);
886 in.close();
887 this.in = stragglers;
888 }
889 } catch (IOException ignored) { }
890 }
891
892 private void raise() {
893 synchronized (closeLock) {
894 useCount++;
895 }
896 }
897
898 private void lower() throws IOException {
899 synchronized (closeLock) {
900 useCount--;
901 if (useCount == 0 && closePending) {
902 closePending = false;
903 super.close();
904 }
905 }
906 }
907
908 @Override
909 public int read() throws IOException {
910 raise();
911 try {
912 return super.read();
913 } finally {
914 lower();
915 }
916 }
917
918 @Override
919 public int read(byte[] b) throws IOException {
920 raise();
921 try {
922 return super.read(b);
923 } finally {
924 lower();
925 }
926 }
927
928 @Override
929 public int read(byte[] b, int off, int len) throws IOException {
930 raise();
931 try {
932 return super.read(b, off, len);
933 } finally {
934 lower();
935 }
936 }
937
938 @Override
939 public long skip(long n) throws IOException {
940 raise();
941 try {
942 return super.skip(n);
943 } finally {
944 lower();
945 }
946 }
947
948 @Override
949 public int available() throws IOException {
950 raise();
951 try {
952 return super.available();
953 } finally {
954 lower();
955 }
956 }
957
958 @Override
959 public void close() throws IOException {
960 // BufferedInputStream#close() is not synchronized unlike most other
961 // methods. Synchronizing helps avoid racing with drainInputStream().
962 synchronized (closeLock) {
963 if (useCount == 0) {
964 super.close();
965 }
966 else {
967 closePending = true;
968 }
969 }
970 }
971 }
J. Duke319a3b92007-12-01 00:00:00 +0000972}