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