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.lang.ProcessBuilder.Redirect; 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.EnumSet; 40 import java.util.Locale; 41 import java.util.Set; 42 import java.util.concurrent.CompletableFuture; 43 import java.util.concurrent.TimeUnit; 44 import java.security.AccessController; 45 import static java.security.AccessController.doPrivileged; 46 import java.security.PrivilegedAction; 47 import java.security.PrivilegedActionException; 48 import java.security.PrivilegedExceptionAction; 49 50 /** 51 * java.lang.Process subclass in the UNIX environment. 52 * 53 * @author Mario Wolczko and Ross Knippel. 54 * @author Konstantin Kladko (ported to Linux and Bsd) 55 * @author Martin Buchholz 56 * @author Volker Simonis (ported to AIX) 57 * @since 1.5 58 */ 59 final class ProcessImpl extends Process { 60 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess 61 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); 62 63 // Linux platforms support a normal (non-forcible) kill signal. 64 static final boolean SUPPORTS_DESTROY_FORCIBLY = false; 65 66 private final int pid; 67 private final ProcessHandle processHandle; 68 private int exitcode; 69 private boolean hasExited; 70 71 private /* final */ OutputStream stdin; 72 private /* final */ InputStream stdout; 73 private /* final */ InputStream stderr; 74 75 // only used on Solaris 76 private /* final */ DeferredCloseInputStream stdout_inner_stream; 77 78 private static enum LaunchMechanism { 79 // order IS important! 80 FORK, 81 POSIX_SPAWN, 82 VFORK 83 } 84 85 private static enum Platform { 86 87 LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK), 88 89 BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), 90 91 SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), 92 93 AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK); 94 95 final LaunchMechanism defaultLaunchMechanism; 96 final Set<LaunchMechanism> validLaunchMechanisms; 97 204 args[i] = cmdarray[i+1].getBytes(); 205 size += args[i].length; 206 } 207 byte[] argBlock = new byte[size]; 208 int i = 0; 209 for (byte[] arg : args) { 210 System.arraycopy(arg, 0, argBlock, i, arg.length); 211 i += arg.length + 1; 212 // No need to write NUL bytes explicitly 213 } 214 215 int[] envc = new int[1]; 216 byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc); 217 218 int[] std_fds; 219 220 FileInputStream f0 = null; 221 FileOutputStream f1 = null; 222 FileOutputStream f2 = null; 223 224 try { 225 if (redirects == null) { 226 std_fds = new int[] { -1, -1, -1 }; 227 } else { 228 std_fds = new int[3]; 229 230 if (redirects[0] == Redirect.PIPE) 231 std_fds[0] = -1; 232 else if (redirects[0] == Redirect.INHERIT) 233 std_fds[0] = 0; 234 else { 235 f0 = new FileInputStream(redirects[0].file()); 236 std_fds[0] = fdAccess.get(f0.getFD()); 237 } 238 239 if (redirects[1] == Redirect.PIPE) 240 std_fds[1] = -1; 241 else if (redirects[1] == Redirect.INHERIT) 242 std_fds[1] = 1; 243 else { 244 f1 = new FileOutputStream(redirects[1].file(), 245 redirects[1].append()); 246 std_fds[1] = fdAccess.get(f1.getFD()); 247 } 248 249 if (redirects[2] == Redirect.PIPE) 250 std_fds[2] = -1; 251 else if (redirects[2] == Redirect.INHERIT) 252 std_fds[2] = 2; 253 else { 254 f2 = new FileOutputStream(redirects[2].file(), 255 redirects[2].append()); 256 std_fds[2] = fdAccess.get(f2.getFD()); 257 } 258 } 259 260 return new ProcessImpl 261 (toCString(cmdarray[0]), 262 argBlock, args.length, 263 envBlock, envc[0], 264 toCString(dir), 265 std_fds, 266 redirectErrorStream); 267 } finally { 268 // In theory, close() can throw IOException 269 // (although it is rather unlikely to happen here) 270 try { if (f0 != null) f0.close(); } 271 finally { 272 try { if (f1 != null) f1.close(); } 273 finally { if (f2 != null) f2.close(); } 274 } 275 } 276 } 277 278 279 /** 280 * Creates a process. Depending on the {@code mode} flag, this is done by 281 * one of the following mechanisms: 282 * <pre> 283 * 1 - fork(2) and exec(2) 284 * 2 - posix_spawn(3P) 285 * 3 - vfork(2) and exec(2) 286 * </pre> 287 * @param fds an array of three file descriptors. 288 * Indexes 0, 1, and 2 correspond to standard input, 289 * standard output and standard error, respectively. On 290 * input, a value of -1 means to create a pipe to connect 291 * child and parent processes. On output, a value which 292 * is not -1 is the parent pipe fd corresponding to the 293 * pipe which has been created. An element of this array 294 * is -1 on input if and only if it is <em>not</em> -1 on 295 * output. 296 * @return the pid of the subprocess 297 */ 298 private native int forkAndExec(int mode, byte[] helperpath, 299 byte[] prog, 300 byte[] argBlock, int argc, 301 byte[] envBlock, int envc, 302 byte[] dir, 303 int[] fds, 304 boolean redirectErrorStream) 305 throws IOException; 306 307 private ProcessImpl(final byte[] prog, 308 final byte[] argBlock, final int argc, 309 final byte[] envBlock, final int envc, 310 final byte[] dir, 311 final int[] fds, 312 final boolean redirectErrorStream) 313 throws IOException { 314 315 pid = forkAndExec(launchMechanism.ordinal() + 1, 316 helperpath, 317 prog, 318 argBlock, argc, 319 envBlock, envc, 320 dir, 321 fds, 322 redirectErrorStream); 323 processHandle = ProcessHandleImpl.getUnchecked(pid); 324 325 try { 326 doPrivileged((PrivilegedExceptionAction<Void>) () -> { 327 initStreams(fds); 328 return null; 329 }); 330 } catch (PrivilegedActionException ex) { 331 throw (IOException) ex.getException(); 332 } 333 } 334 335 static FileDescriptor newFileDescriptor(int fd) { 336 FileDescriptor fileDescriptor = new FileDescriptor(); 337 fdAccess.set(fileDescriptor, fd); 338 return fileDescriptor; 339 } 340 341 void initStreams(int[] fds) throws IOException { 342 switch (platform) { 343 case LINUX: 344 case BSD: 345 stdin = (fds[0] == -1) ? 346 ProcessBuilder.NullOutputStream.INSTANCE : 347 new ProcessPipeOutputStream(fds[0]); 348 349 stdout = (fds[1] == -1) ? 350 ProcessBuilder.NullInputStream.INSTANCE : 351 new ProcessPipeInputStream(fds[1]); 352 439 ((ProcessPipeOutputStream) stdin).processExited(); 440 441 return null; 442 }); 443 break; 444 445 default: throw new AssertionError("Unsupported platform: " + platform); 446 } 447 } 448 449 public OutputStream getOutputStream() { 450 return stdin; 451 } 452 453 public InputStream getInputStream() { 454 return stdout; 455 } 456 457 public InputStream getErrorStream() { 458 return stderr; 459 } 460 461 public synchronized int waitFor() throws InterruptedException { 462 while (!hasExited) { 463 wait(); 464 } 465 return exitcode; 466 } 467 468 @Override 469 public synchronized boolean waitFor(long timeout, TimeUnit unit) 470 throws InterruptedException 471 { 472 long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions 473 if (hasExited) return true; 474 if (timeout <= 0) return false; 475 476 long deadline = System.nanoTime() + remainingNanos; 477 do { 478 // Round up to next millisecond | 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 sun.nio.ch.SelChImpl; 29 30 import java.io.BufferedInputStream; 31 import java.io.BufferedOutputStream; 32 import java.io.ByteArrayInputStream; 33 import java.io.Closeable; 34 import java.io.FileDescriptor; 35 import java.io.FileInputStream; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.OutputStream; 40 import java.lang.ProcessBuilder.Redirect; 41 import java.nio.channels.Pipe; 42 import java.security.AccessController; 43 import java.security.PrivilegedAction; 44 import java.security.PrivilegedActionException; 45 import java.security.PrivilegedExceptionAction; 46 import java.util.Arrays; 47 import java.util.EnumSet; 48 import java.util.Locale; 49 import java.util.Set; 50 import java.util.concurrent.CompletableFuture; 51 import java.util.concurrent.TimeUnit; 52 53 import static java.security.AccessController.doPrivileged; 54 55 /** 56 * java.lang.Process subclass in the UNIX environment. 57 * 58 * @author Mario Wolczko and Ross Knippel. 59 * @author Konstantin Kladko (ported to Linux and Bsd) 60 * @author Martin Buchholz 61 * @author Volker Simonis (ported to AIX) 62 * @since 1.5 63 */ 64 final class ProcessImpl extends Process { 65 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess 66 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); 67 68 // Linux platforms support a normal (non-forcible) kill signal. 69 static final boolean SUPPORTS_DESTROY_FORCIBLY = false; 70 71 private final int pid; 72 private final ProcessHandle processHandle; 73 private int exitcode; 74 private boolean hasExited; 75 76 private /* final */ OutputStream stdin; 77 private /* final */ InputStream stdout; 78 private /* final */ InputStream stderr; 79 80 // only used on Solaris 81 private /* final */ DeferredCloseInputStream stdout_inner_stream; 82 83 private final Pipe.SinkChannel stdinChannel; 84 private final Pipe.SourceChannel stdoutChannel; 85 private final Pipe.SourceChannel stderrChannel; 86 87 private static enum LaunchMechanism { 88 // order IS important! 89 FORK, 90 POSIX_SPAWN, 91 VFORK 92 } 93 94 private static enum Platform { 95 96 LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK), 97 98 BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), 99 100 SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), 101 102 AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK); 103 104 final LaunchMechanism defaultLaunchMechanism; 105 final Set<LaunchMechanism> validLaunchMechanisms; 106 213 args[i] = cmdarray[i+1].getBytes(); 214 size += args[i].length; 215 } 216 byte[] argBlock = new byte[size]; 217 int i = 0; 218 for (byte[] arg : args) { 219 System.arraycopy(arg, 0, argBlock, i, arg.length); 220 i += arg.length + 1; 221 // No need to write NUL bytes explicitly 222 } 223 224 int[] envc = new int[1]; 225 byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc); 226 227 int[] std_fds; 228 229 FileInputStream f0 = null; 230 FileOutputStream f1 = null; 231 FileOutputStream f2 = null; 232 233 Pipe p0 = null; 234 Pipe p1 = null; 235 Pipe p2 = null; 236 237 try { 238 if (redirects == null) { 239 std_fds = new int[] { -1, -1, -1 }; 240 } else { 241 std_fds = new int[3]; 242 243 if (redirects[0] == Redirect.PIPE) { 244 std_fds[0] = -1; 245 } else if (redirects[0] == Redirect.PIPE_CHANNEL) { 246 p0 = Pipe.open(); 247 if (!(p0.source() instanceof SelChImpl)) { 248 throw new UnsupportedOperationException("Unsupported SelectorProvider"); 249 } 250 std_fds[0] = ((SelChImpl) p0.source()).getFDVal(); 251 } else if (redirects[0] == Redirect.INHERIT) { 252 std_fds[0] = 0; 253 } else { 254 f0 = new FileInputStream(redirects[0].file()); 255 std_fds[0] = fdAccess.get(f0.getFD()); 256 } 257 258 if (redirects[1] == Redirect.PIPE) { 259 std_fds[1] = -1; 260 } else if (redirects[1] == Redirect.PIPE_CHANNEL) { 261 p1 = Pipe.open(); 262 if (!(p1.sink() instanceof SelChImpl)) { 263 throw new UnsupportedOperationException("Unsupported SelectorProvider"); 264 } 265 std_fds[1] = ((SelChImpl) p1.sink()).getFDVal(); 266 } else if (redirects[1] == Redirect.INHERIT) { 267 std_fds[1] = 1; 268 } else { 269 f1 = new FileOutputStream(redirects[1].file(), 270 redirects[1].append()); 271 std_fds[1] = fdAccess.get(f1.getFD()); 272 } 273 274 if (redirects[2] == Redirect.PIPE) { 275 std_fds[2] = -1; 276 } else if (redirects[2] == Redirect.PIPE_CHANNEL) { 277 if (redirectErrorStream) { 278 std_fds[2] = -1; 279 } else { 280 p2 = Pipe.open(); 281 if (!(p2.sink() instanceof SelChImpl)) { 282 throw new UnsupportedOperationException("Unsupported SelectorProvider"); 283 } 284 std_fds[2] = ((SelChImpl) p2.sink()).getFDVal(); 285 } 286 } else if (redirects[2] == Redirect.INHERIT) { 287 std_fds[2] = 2; 288 } else { 289 f2 = new FileOutputStream(redirects[2].file(), 290 redirects[2].append()); 291 std_fds[2] = fdAccess.get(f2.getFD()); 292 } 293 } 294 295 return new ProcessImpl 296 (toCString(cmdarray[0]), 297 argBlock, args.length, 298 envBlock, envc[0], 299 toCString(dir), 300 std_fds, 301 new Pipe[] { p0, p1, p2 }, 302 redirectErrorStream); 303 } catch (Exception e) { 304 // close unused pipe ends 305 if (p0 != null) close(p0.sink()); 306 if (p1 != null) close(p1.source()); 307 if (p2 != null) close(p2.source()); 308 // re-throw 309 throw e; 310 } finally { 311 // close dup-ed file handles 312 close(f0); 313 close(f1); 314 close(f2); 315 // close dup-ed pipe ends 316 if (p0 != null) close(p0.source()); 317 if (p1 != null) close(p1.sink()); 318 if (p2 != null) close(p2.sink()); 319 } 320 } 321 322 private static IOException close(Closeable closeable) { 323 if (closeable != null) { 324 try { 325 closeable.close(); 326 } catch (IOException e) { 327 return e; 328 } 329 } 330 return null; 331 } 332 333 334 /** 335 * Creates a process. Depending on the {@code mode} flag, this is done by 336 * one of the following mechanisms: 337 * <pre> 338 * 1 - fork(2) and exec(2) 339 * 2 - posix_spawn(3P) 340 * 3 - vfork(2) and exec(2) 341 * </pre> 342 * @param fds an array of three file descriptors. 343 * Indexes 0, 1, and 2 correspond to standard input, 344 * standard output and standard error, respectively. On 345 * input, a value of -1 means to create a pipe to connect 346 * child and parent processes. On output, a value which 347 * is not -1 is the parent pipe fd corresponding to the 348 * pipe which has been created. An element of this array 349 * is -1 on input if and only if it is <em>not</em> -1 on 350 * output. 351 * @return the pid of the subprocess 352 */ 353 private native int forkAndExec(int mode, byte[] helperpath, 354 byte[] prog, 355 byte[] argBlock, int argc, 356 byte[] envBlock, int envc, 357 byte[] dir, 358 int[] fds, 359 boolean redirectErrorStream) 360 throws IOException; 361 362 private ProcessImpl(final byte[] prog, 363 final byte[] argBlock, final int argc, 364 final byte[] envBlock, final int envc, 365 final byte[] dir, 366 final int[] fds, 367 final Pipe[] pipes, 368 final boolean redirectErrorStream) 369 throws IOException { 370 371 pid = forkAndExec(launchMechanism.ordinal() + 1, 372 helperpath, 373 prog, 374 argBlock, argc, 375 envBlock, envc, 376 dir, 377 fds, 378 redirectErrorStream); 379 processHandle = ProcessHandleImpl.getUnchecked(pid); 380 381 try { 382 doPrivileged((PrivilegedExceptionAction<Void>) () -> { 383 initStreams(fds); 384 return null; 385 }); 386 } catch (PrivilegedActionException ex) { 387 throw (IOException) ex.getException(); 388 } 389 390 stdinChannel = (pipes[0] == null) ? null : pipes[0].sink(); 391 stdoutChannel = (pipes[1] == null) ? null : pipes[1].source(); 392 stderrChannel = (pipes[2] == null) ? null : pipes[2].source(); 393 } 394 395 static FileDescriptor newFileDescriptor(int fd) { 396 FileDescriptor fileDescriptor = new FileDescriptor(); 397 fdAccess.set(fileDescriptor, fd); 398 return fileDescriptor; 399 } 400 401 void initStreams(int[] fds) throws IOException { 402 switch (platform) { 403 case LINUX: 404 case BSD: 405 stdin = (fds[0] == -1) ? 406 ProcessBuilder.NullOutputStream.INSTANCE : 407 new ProcessPipeOutputStream(fds[0]); 408 409 stdout = (fds[1] == -1) ? 410 ProcessBuilder.NullInputStream.INSTANCE : 411 new ProcessPipeInputStream(fds[1]); 412 499 ((ProcessPipeOutputStream) stdin).processExited(); 500 501 return null; 502 }); 503 break; 504 505 default: throw new AssertionError("Unsupported platform: " + platform); 506 } 507 } 508 509 public OutputStream getOutputStream() { 510 return stdin; 511 } 512 513 public InputStream getInputStream() { 514 return stdout; 515 } 516 517 public InputStream getErrorStream() { 518 return stderr; 519 } 520 521 public Pipe.SinkChannel getOutputChannel() { 522 return stdinChannel; 523 } 524 525 public Pipe.SourceChannel getInputChannel() { 526 return stdoutChannel; 527 } 528 529 public Pipe.SourceChannel getErrorChannel() { 530 return stderrChannel; 531 } 532 533 public synchronized int waitFor() throws InterruptedException { 534 while (!hasExited) { 535 wait(); 536 } 537 return exitcode; 538 } 539 540 @Override 541 public synchronized boolean waitFor(long timeout, TimeUnit unit) 542 throws InterruptedException 543 { 544 long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions 545 if (hasExited) return true; 546 if (timeout <= 0) return false; 547 548 long deadline = System.nanoTime() + remainingNanos; 549 do { 550 // Round up to next millisecond |