blob: 00eef09d43111f1b86efd47cac2de9f9cda84924 [file] [log] [blame]
/*
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.io.*;
/* java.lang.Process subclass in the UNIX environment.
*
* @author Mario Wolczko and Ross Knippel.
* @author Konstantin Kladko (ported to Linux)
*/
final class UNIXProcess extends Process {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
private int pid;
private int exitcode;
private boolean hasExited;
private OutputStream stdin_stream;
private InputStream stdout_stream;
private InputStream stderr_stream;
/* this is for the reaping thread */
private native int waitForProcessExit(int pid);
/**
* Create a process using fork(2) and exec(2).
*
* @param std_fds array of file descriptors. Indexes 0, 1, and
* 2 correspond to standard input, standard output and
* standard error, respectively. On input, a value of -1
* means to create a pipe to connect child and parent
* processes. On output, a value which is not -1 is the
* parent pipe fd corresponding to the pipe which has
* been created. An element of this array is -1 on input
* if and only if it is <em>not</em> -1 on output.
* @return the pid of the subprocess
*/
private native int forkAndExec(byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir,
int[] std_fds,
boolean redirectErrorStream)
throws IOException;
/* In the process constructor we wait on this gate until the process */
/* has been created. Then we return from the constructor. */
/* fork() is called by the same thread which later waits for the process */
/* to terminate */
private static class Gate {
private boolean exited = false;
private IOException savedException;
synchronized void exit() { /* Opens the gate */
exited = true;
this.notify();
}
synchronized void waitForExit() { /* wait until the gate is open */
boolean interrupted = false;
while (!exited) {
try {
this.wait();
} catch (InterruptedException e) {
interrupted = true;
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
}
void setException (IOException e) {
savedException = e;
}
IOException getException() {
return savedException;
}
}
UNIXProcess(final byte[] prog,
final byte[] argBlock, final int argc,
final byte[] envBlock, final int envc,
final byte[] dir,
final int[] std_fds,
final boolean redirectErrorStream)
throws IOException {
final Gate gate = new Gate();
/*
* For each subprocess forked a corresponding reaper thread
* is started. That thread is the only thread which waits
* for the subprocess to terminate and it doesn't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
Thread t = new Thread("process reaper") {
public void run() {
try {
pid = forkAndExec(prog,
argBlock, argc,
envBlock, envc,
dir,
std_fds,
redirectErrorStream);
} catch (IOException e) {
gate.setException(e); /*remember to rethrow later*/
gate.exit();
return;
}
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
if (std_fds[0] == -1)
stdin_stream = new ProcessBuilder.NullOutputStream();
else {
FileDescriptor stdin_fd = new FileDescriptor();
fdAccess.set(stdin_fd, std_fds[0]);
stdin_stream = new BufferedOutputStream(
new FileOutputStream(stdin_fd));
}
if (std_fds[1] == -1)
stdout_stream = new ProcessBuilder.NullInputStream();
else {
FileDescriptor stdout_fd = new FileDescriptor();
fdAccess.set(stdout_fd, std_fds[1]);
stdout_stream = new BufferedInputStream(
new FileInputStream(stdout_fd));
}
if (std_fds[2] == -1)
stderr_stream = new ProcessBuilder.NullInputStream();
else {
FileDescriptor stderr_fd = new FileDescriptor();
fdAccess.set(stderr_fd, std_fds[2]);
stderr_stream = new FileInputStream(stderr_fd);
}
return null; }});
gate.exit(); /* exit from constructor */
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
t.setDaemon(true);
t.start();
return null; }});
gate.waitForExit();
IOException e = gate.getException();
if (e != null)
throw new IOException(e.toString());
}
public OutputStream getOutputStream() {
return stdin_stream;
}
public InputStream getInputStream() {
return stdout_stream;
}
public InputStream getErrorStream() {
return stderr_stream;
}
public synchronized int waitFor() throws InterruptedException {
while (!hasExited) {
wait();
}
return exitcode;
}
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited");
}
return exitcode;
}
private static native void destroyProcess(int pid);
public void destroy() {
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
destroyProcess(pid);
}
try {
stdin_stream.close();
stdout_stream.close();
stderr_stream.close();
} catch (IOException e) {
// ignore
}
}
/* This routine initializes JNI field offsets for the class */
private static native void initIDs();
static {
initIDs();
}
}