001: /*
002: * @(#)UNIXProcess.java 1.39 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package java.lang;
028:
029: import java.io.*;
030:
031: /* java.lang.Process subclass in the UNIX environment.
032: *
033: * @author Mario Wolczko and Ross Knippel.
034: */
035:
036: class UNIXProcess extends Process {
037: private FileDescriptor stdin_fd;
038: private FileDescriptor stdout_fd;
039: private FileDescriptor stderr_fd;
040: private int pid;
041: private int exitcode;
042: private boolean hasExited;
043:
044: private OutputStream stdin_stream;
045: private BufferedInputStream stdout_stream;
046: private DeferredCloseInputStream stdout_inner_stream;
047: private DeferredCloseInputStream stderr_stream;
048:
049: /* this is for the reaping thread */
050: private native int waitForProcessExit(int pid);
051:
052: private UNIXProcess() {
053: }
054:
055: private native int forkAndExec(String cmd[], String env[],
056: String path, FileDescriptor stdin_fd,
057: FileDescriptor stdout_fd, FileDescriptor stderr_fd)
058: throws java.io.IOException;
059:
060: UNIXProcess(String cmdarray[], String env[])
061: throws java.io.IOException {
062: this (cmdarray, env, null);
063: }
064:
065: UNIXProcess(String cmdarray[], String env[], String path)
066: throws java.io.IOException {
067:
068: stdin_fd = new FileDescriptor();
069: stdout_fd = new FileDescriptor();
070: stderr_fd = new FileDescriptor();
071:
072: pid = forkAndExec(cmdarray, env, path, stdin_fd, stdout_fd,
073: stderr_fd);
074:
075: java.security.AccessController
076: .doPrivileged(new java.security.PrivilegedAction() {
077: public Object run() {
078: stdin_stream = new BufferedOutputStream(
079: new FileOutputStream(stdin_fd));
080: stdout_inner_stream = new DeferredCloseInputStream(
081: stdout_fd);
082: stdout_stream = new BufferedInputStream(
083: stdout_inner_stream);
084: stderr_stream = new DeferredCloseInputStream(
085: stderr_fd);
086: return null;
087: }
088: });
089:
090: /*
091: * For each subprocess forked a corresponding reaper thread
092: * is started. That thread is the only thread which waits
093: * for the subprocess to terminate and it doesn't hold any
094: * locks while doing so. This design allows waitFor() and
095: * exitStatus() to be safely executed in parallel (and they
096: * need no native code).
097: */
098:
099: java.security.AccessController
100: .doPrivileged(new java.security.PrivilegedAction() {
101: public Object run() {
102: Thread t = new Thread("process reaper") {
103: public void run() {
104: int res = waitForProcessExit(pid);
105: synchronized (UNIXProcess.this ) {
106: hasExited = true;
107: exitcode = res;
108: UNIXProcess.this .notifyAll();
109: }
110: }
111: };
112: t.setDaemon(true);
113: t.start();
114: return null;
115: }
116: });
117: }
118:
119: public OutputStream getOutputStream() {
120: return stdin_stream;
121: }
122:
123: public InputStream getInputStream() {
124: return stdout_stream;
125: }
126:
127: public InputStream getErrorStream() {
128: return stderr_stream;
129: }
130:
131: public synchronized int waitFor() throws InterruptedException {
132: while (!hasExited) {
133: wait();
134: }
135: return exitcode;
136: }
137:
138: public synchronized int exitValue() {
139: if (!hasExited) {
140: throw new IllegalThreadStateException(
141: "process hasn't exited");
142: }
143: return exitcode;
144: }
145:
146: private static native void destroyProcess(int pid);
147:
148: public synchronized void destroy() {
149: destroyProcess(pid);
150: try {
151: stdin_stream.close();
152: stdout_inner_stream.closeDeferred(stdout_stream);
153: stderr_stream.closeDeferred(stderr_stream);
154: } catch (IOException e) {
155: // ignore
156: }
157: }
158:
159: // A FileInputStream that supports the deferment of the actual close
160: // operation until the last pending I/O operation on the stream has
161: // finished. This is required on Solaris because we must close the stdin
162: // and stdout streams in the destroy method in order to reclaim the
163: // underlying file descriptors. Doing so, however, causes any thread
164: // currently blocked in a read on one of those streams to receive an
165: // IOException("Bad file number"), which is incompatible with historical
166: // behavior. By deferring the close we allow any pending reads to see -1
167: // (EOF) as they did before.
168: //
169: private static class DeferredCloseInputStream extends
170: FileInputStream {
171:
172: private DeferredCloseInputStream(FileDescriptor fd) {
173: super (fd);
174: }
175:
176: private Object lock = new Object(); // For the following fields
177: private boolean closePending = false;
178: private int useCount = 0;
179: private InputStream streamToClose;
180:
181: private void raise() {
182: synchronized (lock) {
183: useCount++;
184: }
185: }
186:
187: private void lower() throws IOException {
188: synchronized (lock) {
189: useCount--;
190: if (useCount == 0 && closePending) {
191: streamToClose.close();
192: }
193: }
194: }
195:
196: // stc is the actual stream to be closed; it might be this object, or
197: // it might be an upstream object for which this object is downstream.
198: //
199: private void closeDeferred(InputStream stc) throws IOException {
200: synchronized (lock) {
201: if (useCount == 0) {
202: stc.close();
203: } else {
204: closePending = true;
205: streamToClose = stc;
206: }
207: }
208: }
209:
210: public void close() throws IOException {
211: synchronized (lock) {
212: useCount = 0;
213: closePending = false;
214: }
215: super .close();
216: }
217:
218: public int read() throws IOException {
219: raise();
220: try {
221: return super .read();
222: } finally {
223: lower();
224: }
225: }
226:
227: public int read(byte[] b) throws IOException {
228: raise();
229: try {
230: return super .read(b);
231: } finally {
232: lower();
233: }
234: }
235:
236: public int read(byte[] b, int off, int len) throws IOException {
237: raise();
238: try {
239: return super .read(b, off, len);
240: } finally {
241: lower();
242: }
243: }
244:
245: public long skip(long n) throws IOException {
246: raise();
247: try {
248: return super .skip(n);
249: } finally {
250: lower();
251: }
252: }
253:
254: public int available() throws IOException {
255: raise();
256: try {
257: return super.available();
258: } finally {
259: lower();
260: }
261: }
262:
263: }
264:
265: }
|