提交 73d65ed1 编写于 作者: L lmalvent

Merge

......@@ -85,7 +85,6 @@ SUNWprivate_1.1 {
Java_java_io_FileOutputStream_close0;
Java_java_io_FileOutputStream_initIDs;
Java_java_io_FileOutputStream_open;
Java_java_io_FileOutputStream_openAppend;
Java_java_io_FileOutputStream_write;
Java_java_io_FileOutputStream_writeBytes;
Java_java_io_FileSystem_getFileSystem;
......
......@@ -48,15 +48,15 @@ public
class FileInputStream extends InputStream
{
/* File Descriptor - handle to the open file */
private FileDescriptor fd;
private final FileDescriptor fd;
private FileChannel channel = null;
private Object closeLock = new Object();
private final Object closeLock = new Object();
private volatile boolean closed = false;
private static ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<Boolean>();
private static final ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<Boolean>();
private static boolean isRunningFinalize() {
Boolean val;
......@@ -151,7 +151,7 @@ class FileInputStream extends InputStream
* is thrown.
* <p>
* This constructor does not throw an exception if <code>fdObj</code>
* is {link java.io.FileDescriptor#valid() invalid}.
* is {@link java.io.FileDescriptor#valid() invalid}.
* However, if the methods are invoked on the resulting stream to attempt
* I/O on the stream, an <code>IOException</code> is thrown.
*
......@@ -389,7 +389,7 @@ class FileInputStream extends InputStream
* @see java.io.FileInputStream#close()
*/
protected void finalize() throws IOException {
if ((fd != null) && (fd != fd.in)) {
if ((fd != null) && (fd != FileDescriptor.in)) {
/*
* Finalizer should not release the FileDescriptor if another
......
......@@ -52,20 +52,16 @@ public
class FileOutputStream extends OutputStream
{
/**
* The system dependent file descriptor. The value is
* 1 more than actual file descriptor. This means that
* the default value 0 indicates that the file is not open.
* The system dependent file descriptor.
*/
private FileDescriptor fd;
private final FileDescriptor fd;
private FileChannel channel= null;
private boolean append = false;
private Object closeLock = new Object();
private final Object closeLock = new Object();
private volatile boolean closed = false;
private static ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<Boolean>();
private static final ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<Boolean>();
private static boolean isRunningFinalize() {
Boolean val;
......@@ -75,7 +71,7 @@ class FileOutputStream extends OutputStream
}
/**
* Creates an output file stream to write to the file with the
* Creates a file output stream to write to the file with the
* specified name. A new <code>FileDescriptor</code> object is
* created to represent this file connection.
* <p>
......@@ -100,8 +96,8 @@ class FileOutputStream extends OutputStream
}
/**
* Creates an output file stream to write to the file with the specified
* <code>name</code>. If the second argument is <code>true</code>, then
* Creates a file output stream to write to the file with the specified
* name. If the second argument is <code>true</code>, then
* bytes will be written to the end of the file rather than the beginning.
* A new <code>FileDescriptor</code> object is created to represent this
* file connection.
......@@ -202,16 +198,11 @@ class FileOutputStream extends OutputStream
}
fd = new FileDescriptor();
fd.incrementAndGetUseCount();
this.append = append;
if (append) {
openAppend(name);
} else {
open(name);
}
open(name, append);
}
/**
* Creates an output file stream to write to the specified file
* Creates a file output stream to write to the specified file
* descriptor, which represents an existing connection to an actual
* file in the file system.
* <p>
......@@ -223,7 +214,7 @@ class FileOutputStream extends OutputStream
* is thrown.
* <p>
* This constructor does not throw an exception if <code>fdObj</code>
* is {link java.io.FileDescriptor#valid() invalid}.
* is {@link java.io.FileDescriptor#valid() invalid}.
* However, if the methods are invoked on the resulting stream to attempt
* I/O on the stream, an <code>IOException</code> is thrown.
*
......@@ -252,16 +243,12 @@ class FileOutputStream extends OutputStream
}
/**
* Opens a file, with the specified name, for writing.
* @param name name of file to be opened
*/
private native void open(String name) throws FileNotFoundException;
/**
* Opens a file, with the specified name, for appending.
* Opens a file, with the specified name, for overwriting or appending.
* @param name name of file to be opened
* @param append whether the file is to be opened in append mode
*/
private native void openAppend(String name) throws FileNotFoundException;
private native void open(String name, boolean append)
throws FileNotFoundException;
/**
* Writes the specified byte to this file output stream. Implements
......@@ -385,7 +372,7 @@ class FileOutputStream extends OutputStream
public FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, false, true, this, append);
channel = FileChannelImpl.open(fd, false, true, this);
/*
* Increment fd's use count. Invoking the channel's close()
......@@ -408,7 +395,7 @@ class FileOutputStream extends OutputStream
*/
protected void finalize() throws IOException {
if (fd != null) {
if (fd == fd.out || fd == fd.err) {
if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
flush();
} else {
......
......@@ -41,18 +41,24 @@ import java.io.*;
* <p>The methods that create processes may not work well for special
* processes on certain native platforms, such as native windowing
* processes, daemon processes, Win16/DOS processes on Microsoft
* Windows, or shell scripts. The created subprocess does not have
* its own terminal or console. All its standard I/O (i.e. stdin,
* stdout, stderr) operations will be redirected to the parent process
* through three streams
* ({@link #getOutputStream()},
* {@link #getInputStream()},
* {@link #getErrorStream()}).
* Windows, or shell scripts.
*
* <p>By default, the created subprocess does not have its own terminal
* or console. All its standard I/O (i.e. stdin, stdout, stderr)
* operations will be redirected to the parent process, where they can
* be accessed via the streams obtained using the methods
* {@link #getOutputStream()},
* {@link #getInputStream()}, and
* {@link #getErrorStream()}.
* The parent process uses these streams to feed input to and get output
* from the subprocess. Because some native platforms only provide
* limited buffer size for standard input and output streams, failure
* to promptly write the input stream or read the output stream of
* the subprocess may cause the subprocess to block, and even deadlock.
* the subprocess may cause the subprocess to block, or even deadlock.
*
* <p>Where desired, <a href="ProcessBuilder.html#redirect-input">
* subprocess I/O can also be redirected</a>
* using methods of the {@link ProcessBuilder} class.
*
* <p>The subprocess is not killed when there are no more references to
* the {@code Process} object, but rather the subprocess
......@@ -62,16 +68,22 @@ import java.io.*;
* Process} object execute asynchronously or concurrently with respect
* to the Java process that owns the {@code Process} object.
*
* @author unascribed
* @see ProcessBuilder
* <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way
* to create a {@code Process}.
*
* @since JDK1.0
*/
public abstract class Process {
/**
* Returns the output stream connected to the normal input of the
* subprocess. Output to the stream is piped into the standard
* input stream of the process represented by this {@code Process}
* object.
* input of the process represented by this {@code Process} object.
*
* <p>If the standard input of the subprocess has been redirected using
* {@link ProcessBuilder#redirectInput(Redirect)
* ProcessBuilder.redirectInput}
* then this method will return a
* <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
*
* <p>Implementation note: It is a good idea for the returned
* output stream to be buffered.
......@@ -84,30 +96,47 @@ public abstract class Process {
/**
* Returns the input stream connected to the normal output of the
* subprocess. The stream obtains data piped from the standard
* output stream of the process represented by this {@code
* Process} object.
* output of the process represented by this {@code Process} object.
*
* <p>If the standard output of the subprocess has been redirected using
* {@link ProcessBuilder#redirectOutput(Redirect)
* ProcessBuilder.redirectOutput}
* then this method will return a
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
*
* <p>Otherwise, if the standard error of the subprocess has been
* redirected using
* {@link ProcessBuilder#redirectErrorStream(boolean)
* ProcessBuilder.redirectErrorStream}
* then the input stream returned by this method will receive the
* merged standard output and the standard error of the subprocess.
*
* <p>Implementation note: It is a good idea for the returned
* input stream to be buffered.
*
* @return the input stream connected to the normal output of the
* subprocess
* @see ProcessBuilder#redirectErrorStream()
*/
abstract public InputStream getInputStream();
/**
* Returns the input stream connected to the error output stream of
* the subprocess. The stream obtains data piped from the error
* output stream of the process represented by this {@code Process}
* object.
* Returns the input stream connected to the error output of the
* subprocess. The stream obtains data piped from the error output
* of the process represented by this {@code Process} object.
*
* <p>If the standard error of the subprocess has been redirected using
* {@link ProcessBuilder#redirectError(Redirect)
* ProcessBuilder.redirectError} or
* {@link ProcessBuilder#redirectErrorStream(boolean)
* ProcessBuilder.redirectErrorStream}
* then this method will return a
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
*
* <p>Implementation note: It is a good idea for the returned
* input stream to be buffered.
*
* @return the input stream connected to the error output stream of
* @return the input stream connected to the error output of
* the subprocess
* @see ProcessBuilder#redirectErrorStream()
*/
abstract public InputStream getErrorStream();
......
......@@ -33,4 +33,8 @@ import java.io.FileDescriptor;
public interface JavaIOFileDescriptorAccess {
public void set(FileDescriptor obj, int fd);
public int get(FileDescriptor fd);
// Only valid on Windows
public void setHandle(FileDescriptor obj, long handle);
public long getHandle(FileDescriptor obj);
}
......@@ -52,39 +52,37 @@ public class FileChannelImpl
{
// Used to make native read and write calls
private static NativeDispatcher nd;
private static final NativeDispatcher nd;
// Memory allocation size for mapping buffers
private static long allocationGranularity;
private static final long allocationGranularity;
// Cached field for MappedByteBuffer.isAMappedBuffer
private static Field isAMappedBufferField;
private static final Field isAMappedBufferField;
// File descriptor
private FileDescriptor fd;
private final FileDescriptor fd;
// File access mode (immutable)
private boolean writable;
private boolean readable;
private boolean appending;
private final boolean writable;
private final boolean readable;
// Required to prevent finalization of creating stream (immutable)
private Object parent;
private final Object parent;
// Thread-safe set of IDs of native threads, for signalling
private NativeThreadSet threads = new NativeThreadSet(2);
private final NativeThreadSet threads = new NativeThreadSet(2);
// Lock for operations involving position and size
private Object positionLock = new Object();
private final Object positionLock = new Object();
private FileChannelImpl(FileDescriptor fd, boolean readable,
boolean writable, Object parent, boolean append)
boolean writable, Object parent)
{
this.fd = fd;
this.readable = readable;
this.writable = writable;
this.parent = parent;
this.appending = append;
}
// Invoked by getChannel() methods
......@@ -94,14 +92,7 @@ public class FileChannelImpl
boolean readable, boolean writable,
Object parent)
{
return new FileChannelImpl(fd, readable, writable, parent, false);
}
public static FileChannel open(FileDescriptor fd,
boolean readable, boolean writable,
Object parent, boolean append)
{
return new FileChannelImpl(fd, readable, writable, parent, append);
return new FileChannelImpl(fd, readable, writable, parent);
}
private void ensureOpen() throws IOException {
......@@ -134,15 +125,7 @@ public class FileChannelImpl
// superclass AbstractInterruptibleChannel, but the isOpen logic in
// that method will prevent this method from being reinvoked.
//
if (parent instanceof FileInputStream)
((FileInputStream)parent).close();
else if (parent instanceof FileOutputStream)
((FileOutputStream)parent).close();
else if (parent instanceof RandomAccessFile)
((RandomAccessFile)parent).close();
else
assert false;
((java.io.Closeable)parent).close();
} else {
nd.close(fd);
}
......@@ -218,8 +201,6 @@ public class FileChannelImpl
if (!isOpen())
return 0;
ti = threads.add();
if (appending)
position(size());
do {
n = IOUtil.write(fd, src, -1, nd, positionLock);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
......@@ -244,8 +225,6 @@ public class FileChannelImpl
if (!isOpen())
return 0;
ti = threads.add();
if (appending)
position(size());
do {
n = IOUtil.write(fd, srcs, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
......@@ -1051,7 +1030,7 @@ public class FileChannelImpl
private FileKey fileKey;
FileLockReference(FileLock referent,
ReferenceQueue queue,
ReferenceQueue<FileLock> queue,
FileKey key) {
super(referent, queue);
this.fileKey = key;
......@@ -1073,7 +1052,7 @@ public class FileChannelImpl
new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
// reference queue for cleared refs
private static ReferenceQueue queue = new ReferenceQueue();
private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
// the enclosing file channel
private FileChannelImpl fci;
......
......@@ -40,7 +40,7 @@ readSingle(JNIEnv *env, jobject this, jfieldID fid) {
char ret;
FD fd = GET_FD(this, fid);
if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed");
JNU_ThrowIOException(env, "Stream Closed");
return -1;
}
nread = IO_Read(fd, &ret, 1);
......@@ -94,8 +94,8 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
fd = GET_FD(this, fid);
if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed");
return -1;
JNU_ThrowIOException(env, "Stream Closed");
return -1;
}
nread = IO_Read(fd, buf, len);
......@@ -121,7 +121,7 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
int n;
FD fd = GET_FD(this, fid);
if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed");
JNU_ThrowIOException(env, "Stream Closed");
return;
}
n = IO_Write(fd, &c, 1);
......@@ -172,8 +172,8 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
while (len > 0) {
fd = GET_FD(this, fid);
if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed");
return;
JNU_ThrowIOException(env, "Stream Closed");
break;
}
n = IO_Write(fd, buf+off, len);
if (n == JVM_IO_ERR) {
......
......@@ -152,11 +152,19 @@ public final class FileDescriptor {
public int get(FileDescriptor obj) {
return obj.fd;
}
public void setHandle(FileDescriptor obj, long handle) {
throw new UnsupportedOperationException();
}
public long getHandle(FileDescriptor obj) {
throw new UnsupportedOperationException();
}
}
);
}
// pacakge private methods used by FIS,FOS and RAF
// package private methods used by FIS, FOS and RAF
int incrementAndGetUseCount() {
return useCount.incrementAndGet();
......
......@@ -26,7 +26,10 @@
package java.lang;
import java.io.IOException;
import java.lang.Process;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.ProcessBuilder.Redirect;
import java.lang.ProcessBuilder.Redirect;
/**
* This class is for the exclusive use of ProcessBuilder.start() to
......@@ -36,6 +39,9 @@ import java.lang.Process;
* @since 1.5
*/
final class ProcessImpl {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
private ProcessImpl() {} // Not instantiable
private static byte[] toCString(String s) {
......@@ -54,6 +60,7 @@ final class ProcessImpl {
static Process start(String[] cmdarray,
java.util.Map<String,String> environment,
String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream)
throws IOException
{
......@@ -78,11 +85,61 @@ final class ProcessImpl {
int[] envc = new int[1];
byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
int[] std_fds;
FileInputStream f0 = null;
FileOutputStream f1 = null;
FileOutputStream f2 = null;
try {
if (redirects == null) {
std_fds = new int[] { -1, -1, -1 };
} else {
std_fds = new int[3];
if (redirects[0] == Redirect.PIPE)
std_fds[0] = -1;
else if (redirects[0] == Redirect.INHERIT)
std_fds[0] = 0;
else {
f0 = new FileInputStream(redirects[0].file());
std_fds[0] = fdAccess.get(f0.getFD());
}
if (redirects[1] == Redirect.PIPE)
std_fds[1] = -1;
else if (redirects[1] == Redirect.INHERIT)
std_fds[1] = 1;
else {
f1 = redirects[1].toFileOutputStream();
std_fds[1] = fdAccess.get(f1.getFD());
}
if (redirects[2] == Redirect.PIPE)
std_fds[2] = -1;
else if (redirects[2] == Redirect.INHERIT)
std_fds[2] = 2;
else {
f2 = redirects[2].toFileOutputStream();
std_fds[2] = fdAccess.get(f2.getFD());
}
}
return new UNIXProcess
(toCString(cmdarray[0]),
argBlock, args.length,
envBlock, envc[0],
toCString(dir),
std_fds,
redirectErrorStream);
} finally {
// In theory, close() can throw IOException
// (although it is rather unlikely to happen here)
try { if (f0 != null) f0.close(); }
finally {
try { if (f1 != null) f1.close(); }
finally { if (f2 != null) f2.close(); }
}
}
}
}
/*
* Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
/*
* Copyright 1995-2008 Sun Microsystems, Inc. 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
......@@ -34,9 +34,9 @@ import java.io.*;
*/
final class UNIXProcess extends Process {
private FileDescriptor stdin_fd;
private FileDescriptor stdout_fd;
private FileDescriptor stderr_fd;
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
private int pid;
private int exitcode;
private boolean hasExited;
......@@ -48,15 +48,26 @@ final class UNIXProcess extends Process {
/* 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,
boolean redirectErrorStream,
FileDescriptor stdin_fd,
FileDescriptor stdout_fd,
FileDescriptor stderr_fd)
throws IOException;
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. */
......@@ -97,67 +108,82 @@ final class UNIXProcess extends Process {
}
UNIXProcess(final byte[] prog,
final byte[] argBlock, final int argc,
final byte[] envBlock, final int envc,
final byte[] dir,
final boolean redirectErrorStream)
final byte[] argBlock, final int argc,
final byte[] envBlock, final int envc,
final byte[] dir,
final int[] std_fds,
final boolean redirectErrorStream)
throws IOException {
stdin_fd = new FileDescriptor();
stdout_fd = new FileDescriptor();
stderr_fd = new FileDescriptor();
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() {
public Object run() {
Thread t = new Thread("process reaper") {
public void run() {
/*
* 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,
redirectErrorStream,
stdin_fd, stdout_fd, stderr_fd);
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() {
public Object run() {
stdin_stream = new BufferedOutputStream(new
FileOutputStream(stdin_fd));
stdout_stream = new BufferedInputStream(new
FileInputStream(stdout_fd));
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;
}
});
return null; }});
gate.exit(); /* exit from constructor */
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
t.setDaemon(true);
t.start();
return null;
}
});
return null; }});
gate.waitForExit();
IOException e = gate.getException();
if (e != null)
......@@ -165,43 +191,43 @@ final class UNIXProcess extends Process {
}
public OutputStream getOutputStream() {
return stdin_stream;
return stdin_stream;
}
public InputStream getInputStream() {
return stdout_stream;
return stdout_stream;
}
public InputStream getErrorStream() {
return stderr_stream;
return stderr_stream;
}
public synchronized int waitFor() throws InterruptedException {
while (!hasExited) {
wait();
}
return exitcode;
wait();
}
return exitcode;
}
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited");
}
return exitcode;
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);
}
// 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();
......@@ -215,6 +241,6 @@ final class UNIXProcess extends Process {
private static native void initIDs();
static {
initIDs();
initIDs();
}
}
/*
* Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
/*
* Copyright 1995-2008 Sun Microsystems, Inc. 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
......@@ -33,129 +33,155 @@ import java.io.*;
*/
final class UNIXProcess extends Process {
private FileDescriptor stdin_fd;
private FileDescriptor stdout_fd;
private FileDescriptor stderr_fd;
private int pid;
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
private final int pid;
private int exitcode;
private boolean hasExited;
private OutputStream stdin_stream;
private BufferedInputStream stdout_stream;
private InputStream stdout_stream;
private DeferredCloseInputStream stdout_inner_stream;
private DeferredCloseInputStream stderr_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,
boolean redirectErrorStream,
FileDescriptor stdin_fd,
FileDescriptor stdout_fd,
FileDescriptor stderr_fd)
throws IOException;
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir,
int[] std_fds,
boolean redirectErrorStream)
throws IOException;
UNIXProcess(final byte[] prog,
final byte[] argBlock, int argc,
final byte[] envBlock, int envc,
final byte[] dir,
final boolean redirectErrorStream)
final byte[] argBlock, int argc,
final byte[] envBlock, int envc,
final byte[] dir,
final int[] std_fds,
final boolean redirectErrorStream)
throws IOException {
stdin_fd = new FileDescriptor();
stdout_fd = new FileDescriptor();
stderr_fd = new FileDescriptor();
pid = forkAndExec(prog,
argBlock, argc,
envBlock, envc,
dir,
redirectErrorStream,
stdin_fd, stdout_fd, stderr_fd);
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
stdin_stream
= new BufferedOutputStream(new FileOutputStream(stdin_fd));
stdout_inner_stream = new DeferredCloseInputStream(stdout_fd);
stdout_stream = new BufferedInputStream(stdout_inner_stream);
stderr_stream = new DeferredCloseInputStream(stderr_fd);
return null;
}
});
/*
* 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() {
public Object run() {
Thread t = new Thread("process reaper") {
public void run() {
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
t.setDaemon(true);
t.start();
return null;
}
});
pid = forkAndExec(prog,
argBlock, argc,
envBlock, envc,
dir,
std_fds,
redirectErrorStream);
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_inner_stream = new DeferredCloseInputStream(stdout_fd);
stdout_stream = new BufferedInputStream(stdout_inner_stream);
}
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 DeferredCloseInputStream(stderr_fd);
}
return null; }});
/*
* 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() {
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
t.setDaemon(true);
t.start();
return null; }});
}
public OutputStream getOutputStream() {
return stdin_stream;
return stdin_stream;
}
public InputStream getInputStream() {
return stdout_stream;
return stdout_stream;
}
public InputStream getErrorStream() {
return stderr_stream;
return stderr_stream;
}
public synchronized int waitFor() throws InterruptedException {
while (!hasExited) {
wait();
}
return exitcode;
wait();
}
return exitcode;
}
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited");
}
return exitcode;
if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited");
}
return exitcode;
}
private static native void destroyProcess(int pid);
public synchronized 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.
if (!hasExited)
destroyProcess(pid);
try {
// 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.
if (!hasExited)
destroyProcess(pid);
try {
stdin_stream.close();
stdout_inner_stream.closeDeferred(stdout_stream);
stderr_stream.closeDeferred(stderr_stream);
if (stdout_inner_stream != null)
stdout_inner_stream.closeDeferred(stdout_stream);
if (stderr_stream instanceof DeferredCloseInputStream)
((DeferredCloseInputStream) stderr_stream)
.closeDeferred(stderr_stream);
} catch (IOException e) {
// ignore
}
......@@ -172,99 +198,99 @@ final class UNIXProcess extends Process {
// (EOF) as they did before.
//
private static class DeferredCloseInputStream
extends FileInputStream
extends FileInputStream
{
private DeferredCloseInputStream(FileDescriptor fd) {
super(fd);
}
private Object lock = new Object(); // For the following fields
private boolean closePending = false;
private int useCount = 0;
private InputStream streamToClose;
private void raise() {
synchronized (lock) {
useCount++;
}
}
private void lower() throws IOException {
synchronized (lock) {
useCount--;
if (useCount == 0 && closePending) {
streamToClose.close();
}
}
}
// stc is the actual stream to be closed; it might be this object, or
// it might be an upstream object for which this object is downstream.
//
private void closeDeferred(InputStream stc) throws IOException {
synchronized (lock) {
if (useCount == 0) {
stc.close();
} else {
closePending = true;
streamToClose = stc;
}
}
}
public void close() throws IOException {
synchronized (lock) {
useCount = 0;
closePending = false;
}
super.close();
}
public int read() throws IOException {
raise();
try {
return super.read();
} finally {
lower();
}
}
public int read(byte[] b) throws IOException {
raise();
try {
return super.read(b);
} finally {
lower();
}
}
public int read(byte[] b, int off, int len) throws IOException {
raise();
try {
return super.read(b, off, len);
} finally {
lower();
}
}
public long skip(long n) throws IOException {
raise();
try {
return super.skip(n);
} finally {
lower();
}
}
public int available() throws IOException {
raise();
try {
return super.available();
} finally {
lower();
}
}
private DeferredCloseInputStream(FileDescriptor fd) {
super(fd);
}
private Object lock = new Object(); // For the following fields
private boolean closePending = false;
private int useCount = 0;
private InputStream streamToClose;
private void raise() {
synchronized (lock) {
useCount++;
}
}
private void lower() throws IOException {
synchronized (lock) {
useCount--;
if (useCount == 0 && closePending) {
streamToClose.close();
}
}
}
// stc is the actual stream to be closed; it might be this object, or
// it might be an upstream object for which this object is downstream.
//
private void closeDeferred(InputStream stc) throws IOException {
synchronized (lock) {
if (useCount == 0) {
stc.close();
} else {
closePending = true;
streamToClose = stc;
}
}
}
public void close() throws IOException {
synchronized (lock) {
useCount = 0;
closePending = false;
}
super.close();
}
public int read() throws IOException {
raise();
try {
return super.read();
} finally {
lower();
}
}
public int read(byte[] b) throws IOException {
raise();
try {
return super.read(b);
} finally {
lower();
}
}
public int read(byte[] b, int off, int len) throws IOException {
raise();
try {
return super.read(b, off, len);
} finally {
lower();
}
}
public long skip(long n) throws IOException {
raise();
try {
return super.skip(n);
} finally {
lower();
}
}
public int available() throws IOException {
raise();
try {
return super.available();
} finally {
lower();
}
}
}
......@@ -272,6 +298,6 @@ final class UNIXProcess extends Process {
private static native void initIDs();
static {
initIDs();
initIDs();
}
}
......@@ -53,13 +53,10 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fdClass) {
*/
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC);
}
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_openAppend(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
jstring path, jboolean append) {
fileOpen(env, this, path, fos_fd,
O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
}
JNIEXPORT void JNICALL
......
/*
* Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1995-2008 Sun Microsystems, Inc. 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
......@@ -479,6 +479,37 @@ closeSafely(int fd)
close(fd);
}
/*
* Reads nbyte bytes from file descriptor fd into buf,
* The read operation is retried in case of EINTR or partial reads.
*
* Returns number of bytes read (normally nbyte, but may be less in
* case of EOF). In case of read errors, returns -1 and sets errno.
*/
static ssize_t
readFully(int fd, void *buf, size_t nbyte)
{
ssize_t remaining = nbyte;
for (;;) {
ssize_t n = read(fd, buf, remaining);
if (n == 0) {
return nbyte - remaining;
} else if (n > 0) {
remaining -= n;
if (remaining <= 0)
return nbyte;
/* We were interrupted in the middle of reading the bytes.
* Unlikely, but possible. */
buf = (void *) (((char *)buf) + n);
} else if (errno == EINTR) {
/* Strange signals like SIGJVM1 are possible at any time.
* See http://www.dreamsongs.com/WorseIsBetter.html */
} else {
return -1;
}
}
}
#ifndef __solaris__
#undef fork1
#define fork1() fork()
......@@ -491,10 +522,8 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
jbyteArray argBlock, jint argc,
jbyteArray envBlock, jint envc,
jbyteArray dir,
jboolean redirectErrorStream,
jobject stdin_fd,
jobject stdout_fd,
jobject stderr_fd)
jintArray std_fds,
jboolean redirectErrorStream)
{
int errnum;
int resultPid = -1;
......@@ -505,6 +534,7 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
const char *pargBlock = getBytes(env, argBlock);
const char *penvBlock = getBytes(env, envBlock);
const char *pdir = getBytes(env, dir);
jint *fds = NULL;
in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1;
......@@ -527,9 +557,13 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
initVectorFromBlock(envv, penvBlock, envc);
}
if ((pipe(in) < 0) ||
(pipe(out) < 0) ||
(pipe(err) < 0) ||
assert(std_fds != NULL);
fds = (*env)->GetIntArrayElements(env, std_fds, NULL);
if (fds == NULL) goto Catch;
if ((fds[0] == -1 && pipe(in) < 0) ||
(fds[1] == -1 && pipe(out) < 0) ||
(fds[2] == -1 && pipe(err) < 0) ||
(pipe(fail) < 0)) {
throwIOException(env, errno, "Bad file descriptor");
goto Catch;
......@@ -544,23 +578,26 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
if (resultPid == 0) {
/* Child process */
/* Close the parent sides of the pipe.
Give the child sides of the pipes the right fileno's.
/* Close the parent sides of the pipes.
Closing pipe fds here is redundant, since closeDescriptors()
would do it anyways, but a little paranoia is a good thing. */
closeSafely(in[1]);
closeSafely(out[0]);
closeSafely(err[0]);
closeSafely(fail[0]);
/* Give the child sides of the pipes the right fileno's. */
/* Note: it is possible for in[0] == 0 */
close(in[1]);
moveDescriptor(in[0], STDIN_FILENO);
close(out[0]);
moveDescriptor(out[1], STDOUT_FILENO);
close(err[0]);
moveDescriptor(in[0] != -1 ? in[0] : fds[0], STDIN_FILENO);
moveDescriptor(out[1]!= -1 ? out[1] : fds[1], STDOUT_FILENO);
if (redirectErrorStream) {
close(err[1]);
closeSafely(err[1]);
dup2(STDOUT_FILENO, STDERR_FILENO);
} else {
moveDescriptor(err[1], STDERR_FILENO);
moveDescriptor(err[1] != -1 ? err[1] : fds[2], STDERR_FILENO);
}
close(fail[0]);
moveDescriptor(fail[1], FAIL_FILENO);
/* close everything */
......@@ -600,15 +637,21 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
/* parent process */
close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec */
if (read(fail[0], &errnum, sizeof(errnum)) != 0) {
switch (readFully(fail[0], &errnum, sizeof(errnum))) {
case 0: break; /* Exec succeeded */
case sizeof(errnum):
waitpid(resultPid, NULL, 0);
throwIOException(env, errnum, "Exec failed");
goto Catch;
default:
throwIOException(env, errno, "Read failed");
goto Catch;
}
(*env)->SetIntField(env, stdin_fd, IO_fd_fdID, in [1]);
(*env)->SetIntField(env, stdout_fd, IO_fd_fdID, out[0]);
(*env)->SetIntField(env, stderr_fd, IO_fd_fdID, err[0]);
fds[0] = (in [1] != -1) ? in [1] : -1;
fds[1] = (out[0] != -1) ? out[0] : -1;
fds[2] = (err[0] != -1) ? err[0] : -1;
Finally:
/* Always clean up the child's side of the pipes */
......@@ -628,6 +671,9 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
releaseBytes(env, envBlock, penvBlock);
releaseBytes(env, dir, pdir);
if (fds != NULL)
(*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);
return resultPid;
Catch:
......
......@@ -29,17 +29,14 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Instances of the file descriptor class serve as an opaque handle
* to the underlying machine-specific structure representing an open
* file, an open socket, or another source or sink of bytes. The
* main practical use for a file descriptor is to create a
* <code>FileInputStream</code> or <code>FileOutputStream</code> to
* contain it.
* <p>
* Applications should not create their own file descriptors.
* to the underlying machine-specific structure representing an
* open file, an open socket, or another source or sink of bytes.
* The main practical use for a file descriptor is to create a
* {@link FileInputStream} or {@link FileOutputStream} to contain it.
*
* <p>Applications should not create their own file descriptors.
*
* @author Pavani Diwanji
* @see java.io.FileInputStream
* @see java.io.FileOutputStream
* @since JDK1.0
*/
public final class FileDescriptor {
......@@ -81,6 +78,14 @@ public final class FileDescriptor {
public int get(FileDescriptor obj) {
return obj.fd;
}
public void setHandle(FileDescriptor obj, long handle) {
obj.handle = handle;
}
public long getHandle(FileDescriptor obj) {
return obj.handle;
}
}
);
}
......@@ -88,7 +93,7 @@ public final class FileDescriptor {
/**
* A handle to the standard input stream. Usually, this file
* descriptor is not used directly, but rather via the input stream
* known as <code>System.in</code>.
* known as {@code System.in}.
*
* @see java.lang.System#in
*/
......@@ -97,7 +102,7 @@ public final class FileDescriptor {
/**
* A handle to the standard output stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as <code>System.out</code>.
* known as {@code System.out}.
* @see java.lang.System#out
*/
public static final FileDescriptor out = standardStream(1);
......@@ -105,7 +110,7 @@ public final class FileDescriptor {
/**
* A handle to the standard error stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as <code>System.err</code>.
* known as {@code System.err}.
*
* @see java.lang.System#err
*/
......@@ -114,9 +119,9 @@ public final class FileDescriptor {
/**
* Tests if this file descriptor object is valid.
*
* @return <code>true</code> if the file descriptor object represents a
* @return {@code true} if the file descriptor object represents a
* valid, open file, socket, or other active I/O connection;
* <code>false</code> otherwise.
* {@code false} otherwise.
*/
public boolean valid() {
return ((handle != -1) || (fd != -1));
......
......@@ -25,7 +25,16 @@
package java.lang;
import java.io.*;
import java.io.IOException;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileDescriptor;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.lang.ProcessBuilder.Redirect;
/* This class is for the exclusive use of ProcessBuilder.start() to
* create new processes.
......@@ -35,30 +44,82 @@ import java.io.*;
*/
final class ProcessImpl extends Process {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
// System-dependent portion of ProcessBuilder.start()
static Process start(String cmdarray[],
java.util.Map<String,String> environment,
String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream)
throws IOException
{
String envblock = ProcessEnvironment.toEnvironmentBlock(environment);
return new ProcessImpl(cmdarray, envblock, dir, redirectErrorStream);
FileInputStream f0 = null;
FileOutputStream f1 = null;
FileOutputStream f2 = null;
try {
long[] stdHandles;
if (redirects == null) {
stdHandles = new long[] { -1L, -1L, -1L };
} else {
stdHandles = new long[3];
if (redirects[0] == Redirect.PIPE)
stdHandles[0] = -1L;
else if (redirects[0] == Redirect.INHERIT)
stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
else {
f0 = new FileInputStream(redirects[0].file());
stdHandles[0] = fdAccess.getHandle(f0.getFD());
}
if (redirects[1] == Redirect.PIPE)
stdHandles[1] = -1L;
else if (redirects[1] == Redirect.INHERIT)
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
else {
f1 = redirects[1].toFileOutputStream();
stdHandles[1] = fdAccess.getHandle(f1.getFD());
}
if (redirects[2] == Redirect.PIPE)
stdHandles[2] = -1L;
else if (redirects[2] == Redirect.INHERIT)
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
else {
f2 = redirects[2].toFileOutputStream();
stdHandles[2] = fdAccess.getHandle(f2.getFD());
}
}
return new ProcessImpl(cmdarray, envblock, dir,
stdHandles, redirectErrorStream);
} finally {
// In theory, close() can throw IOException
// (although it is rather unlikely to happen here)
try { if (f0 != null) f0.close(); }
finally {
try { if (f1 != null) f1.close(); }
finally { if (f2 != null) f2.close(); }
}
}
}
private long handle = 0;
private FileDescriptor stdin_fd;
private FileDescriptor stdout_fd;
private FileDescriptor stderr_fd;
private OutputStream stdin_stream;
private InputStream stdout_stream;
private InputStream stderr_stream;
private ProcessImpl(String cmd[],
String envblock,
String path,
boolean redirectErrorStream)
private ProcessImpl(final String cmd[],
final String envblock,
final String path,
final long[] stdHandles,
final boolean redirectErrorStream)
throws IOException
{
// Win32 CreateProcess requires cmd[0] to be normalized
......@@ -91,25 +152,39 @@ final class ProcessImpl extends Process {
}
String cmdstr = cmdbuf.toString();
stdin_fd = new FileDescriptor();
stdout_fd = new FileDescriptor();
stderr_fd = new FileDescriptor();
handle = create(cmdstr, envblock, path, redirectErrorStream,
stdin_fd, stdout_fd, stderr_fd);
handle = create(cmdstr, envblock, path,
stdHandles, redirectErrorStream);
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
stdin_stream =
new BufferedOutputStream(new FileOutputStream(stdin_fd));
stdout_stream =
new BufferedInputStream(new FileInputStream(stdout_fd));
stderr_stream =
new FileInputStream(stderr_fd);
return null;
new java.security.PrivilegedAction<Void>() {
public Void run() {
if (stdHandles[0] == -1L)
stdin_stream = new ProcessBuilder.NullOutputStream();
else {
FileDescriptor stdin_fd = new FileDescriptor();
fdAccess.setHandle(stdin_fd, stdHandles[0]);
stdin_stream = new BufferedOutputStream(
new FileOutputStream(stdin_fd));
}
});
if (stdHandles[1] == -1L)
stdout_stream = new ProcessBuilder.NullInputStream();
else {
FileDescriptor stdout_fd = new FileDescriptor();
fdAccess.setHandle(stdout_fd, stdHandles[1]);
stdout_stream = new BufferedInputStream(
new FileInputStream(stdout_fd));
}
if (stdHandles[2] == -1L)
stderr_stream = new ProcessBuilder.NullInputStream();
else {
FileDescriptor stderr_fd = new FileDescriptor();
fdAccess.setHandle(stderr_fd, stdHandles[2]);
stderr_stream = new FileInputStream(stderr_fd);
}
return null; }});
}
public OutputStream getOutputStream() {
......@@ -150,13 +225,30 @@ final class ProcessImpl extends Process {
public void destroy() { terminateProcess(handle); }
private static native void terminateProcess(long handle);
/**
* Create a process using the win32 function CreateProcess.
*
* @param cmdstr the Windows commandline
* @param envblock NUL-separated, double-NUL-terminated list of
* environment strings in VAR=VALUE form
* @param dir the working directory of the process, or null if
* inheriting the current directory from the parent process
* @param stdHandles array of windows HANDLEs. 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 handle 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.
* @param redirectErrorStream redirectErrorStream attribute
* @return the native subprocess HANDLE returned by CreateProcess
*/
private static native long create(String cmdstr,
String envblock,
String dir,
boolean redirectErrorStream,
FileDescriptor in_fd,
FileDescriptor out_fd,
FileDescriptor err_fd)
long[] stdHandles,
boolean redirectErrorStream)
throws IOException;
private static native boolean closeHandle(long handle);
......
......@@ -39,8 +39,6 @@
jfieldID fos_fd; /* id for jobject 'fd' in java.io.FileOutputStream */
jfieldID fos_append;
/**************************************************************
* static methods to store field ID's in initializers
*/
......@@ -49,7 +47,6 @@ JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
fos_fd =
(*env)->GetFieldID(env, fosClass, "fd", "Ljava/io/FileDescriptor;");
fos_append = (*env)->GetFieldID(env, fosClass, "append", "Z");
}
/**************************************************************
......@@ -57,45 +54,20 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
*/
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC);
}
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_openAppend(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
jstring path, jboolean append) {
fileOpen(env, this, path, fos_fd,
O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
}
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
jboolean append = (*env)->GetBooleanField(env, this, fos_append);
FD fd = GET_FD(this, fos_fd);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
return;
}
if (append == JNI_TRUE) {
if (IO_Lseek(fd, 0L, SEEK_END) == -1) {
JNU_ThrowIOExceptionWithLastError(env, "Append failed");
}
}
writeSingle(env, this, byte, fos_fd);
}
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) {
jboolean append = (*env)->GetBooleanField(env, this, fos_append);
FD fd = GET_FD(this, fos_fd);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
return;
}
if (append == JNI_TRUE) {
if (IO_Lseek(fd, 0L, SEEK_END) == -1) {
JNU_ThrowIOExceptionWithLastError(env, "Append failed");
}
}
writeBytes(env, this, bytes, off, len, fos_fd);
}
......
......@@ -42,7 +42,7 @@
extern jboolean onNT = JNI_FALSE;
static int MAX_INPUT_EVENTS = 2000;
static DWORD MAX_INPUT_EVENTS = 2000;
void
initializeWindowsVersion() {
......@@ -190,9 +190,16 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
jlong
winFileHandleOpen(JNIEnv *env, jstring path, int flags)
{
/* To implement O_APPEND, we use the strategy from
http://msdn2.microsoft.com/en-us/library/aa363858.aspx
"You can get atomic append by opening a file with
FILE_APPEND_DATA access and _without_ FILE_WRITE_DATA access.
If you do this then all writes will ignore the current file
pointer and be done at the end-of file." */
const DWORD access =
(flags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) :
(flags & O_APPEND) ? (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) :
(flags & O_WRONLY) ? GENERIC_WRITE :
(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :
GENERIC_READ;
const DWORD sharing =
FILE_SHARE_READ | FILE_SHARE_WRITE;
......@@ -444,24 +451,6 @@ handleSetLength(jlong fd, jlong length) {
return 0;
}
int
handleFileSizeFD(jlong fd, jlong *size)
{
DWORD sizeLow = 0;
DWORD sizeHigh = 0;
HANDLE h = (HANDLE)fd;
if (h == INVALID_HANDLE_VALUE) {
return -1;
}
sizeLow = GetFileSize(h, &sizeHigh);
if (sizeLow == ((DWORD)-1)) {
if (GetLastError() != ERROR_SUCCESS) {
return -1;
}
}
return (((jlong)sizeHigh) << 32) | sizeLow;
}
JNIEXPORT
size_t
handleRead(jlong fd, void *buf, jint len)
......@@ -513,7 +502,7 @@ handleClose(JNIEnv *env, jobject this, jfieldID fid)
FD fd = GET_FD(this, fid);
HANDLE h = (HANDLE)fd;
if (fd == INVALID_HANDLE_VALUE) {
if (h == INVALID_HANDLE_VALUE) {
return 0;
}
......
......@@ -38,7 +38,6 @@ void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags);
int handleAvailable(jlong fd, jlong *pbytes);
JNIEXPORT int handleSync(jlong fd);
int handleSetLength(jlong fd, jlong length);
int handleFileSizeFD(jlong fd, jlong *size);
JNIEXPORT size_t handleRead(jlong fd, void *buf, jint len);
JNIEXPORT size_t handleWrite(jlong fd, const void *buf, jint len);
jint handleClose(JNIEnv *env, jobject this, jfieldID fid);
......
......@@ -33,7 +33,12 @@
#include <windows.h>
#include <io.h>
#define PIPE_SIZE 4096
/* We try to make sure that we can read and write 4095 bytes (the
* fixed limit on Linux) to the pipe on all operating systems without
* deadlock. Windows 2000 inexplicably appears to need an extra 24
* bytes of slop to avoid deadlock.
*/
#define PIPE_SIZE (4096+24)
char *
extractExecutablePath(JNIEnv *env, char *source)
......@@ -120,7 +125,7 @@ win32Error(JNIEnv *env, const char *functionName)
static void
closeSafely(HANDLE handle)
{
if (handle)
if (handle != INVALID_HANDLE_VALUE)
CloseHandle(handle);
}
......@@ -129,23 +134,22 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
jstring cmd,
jstring envBlock,
jstring dir,
jboolean redirectErrorStream,
jobject in_fd,
jobject out_fd,
jobject err_fd)
jlongArray stdHandles,
jboolean redirectErrorStream)
{
HANDLE inRead = 0;
HANDLE inWrite = 0;
HANDLE outRead = 0;
HANDLE outWrite = 0;
HANDLE errRead = 0;
HANDLE errWrite = 0;
HANDLE inRead = INVALID_HANDLE_VALUE;
HANDLE inWrite = INVALID_HANDLE_VALUE;
HANDLE outRead = INVALID_HANDLE_VALUE;
HANDLE outWrite = INVALID_HANDLE_VALUE;
HANDLE errRead = INVALID_HANDLE_VALUE;
HANDLE errWrite = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pi;
STARTUPINFO si;
LPTSTR pcmd = NULL;
LPCTSTR pdir = NULL;
LPVOID penvBlock = NULL;
jlong *handles = NULL;
jlong ret = 0;
OSVERSIONINFO ver;
jboolean onNT = JNI_FALSE;
......@@ -156,17 +160,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
onNT = JNI_TRUE;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = TRUE;
if (!(CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE) &&
CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE) &&
CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE))) {
win32Error(env, "CreatePipe");
goto Catch;
}
assert(cmd != NULL);
pcmd = (LPTSTR) JNU_GetStringPlatformChars(env, cmd, NULL);
if (pcmd == NULL) goto Catch;
......@@ -184,19 +177,62 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
if (penvBlock == NULL) goto Catch;
}
assert(stdHandles != NULL);
handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);
if (handles == NULL) goto Catch;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = inRead;
si.hStdOutput = outWrite;
si.hStdError = redirectErrorStream ? outWrite : errWrite;
SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE);
SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE);
SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE);
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = TRUE;
if (redirectErrorStream)
SetHandleInformation(errWrite, HANDLE_FLAG_INHERIT, FALSE);
if (handles[0] != (jlong) -1) {
si.hStdInput = (HANDLE) handles[0];
handles[0] = (jlong) -1;
} else {
if (! CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE)) {
win32Error(env, "CreatePipe");
goto Catch;
}
si.hStdInput = inRead;
SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE);
handles[0] = (jlong) inWrite;
}
SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, TRUE);
if (handles[1] != (jlong) -1) {
si.hStdOutput = (HANDLE) handles[1];
handles[1] = (jlong) -1;
} else {
if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) {
win32Error(env, "CreatePipe");
goto Catch;
}
si.hStdOutput = outWrite;
SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE);
handles[1] = (jlong) outRead;
}
SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, TRUE);
if (redirectErrorStream) {
si.hStdError = si.hStdOutput;
handles[2] = (jlong) -1;
} else if (handles[2] != (jlong) -1) {
si.hStdError = (HANDLE) handles[2];
handles[2] = (jlong) -1;
} else {
if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) {
win32Error(env, "CreatePipe");
goto Catch;
}
si.hStdError = errWrite;
SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE);
handles[2] = (jlong) errRead;
}
SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, TRUE);
if (onNT)
processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
......@@ -232,9 +268,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
CloseHandle(pi.hThread);
ret = (jlong)pi.hProcess;
(*env)->SetLongField(env, in_fd, IO_handle_fdID, (jlong)inWrite);
(*env)->SetLongField(env, out_fd, IO_handle_fdID, (jlong)outRead);
(*env)->SetLongField(env, err_fd, IO_handle_fdID, (jlong)errRead);
Finally:
/* Always clean up the child's side of the pipes */
......@@ -252,6 +285,9 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
else
JNU_ReleaseStringPlatformChars(env, dir, (char *) penvBlock);
}
if (handles != NULL)
(*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
return ret;
Catch:
......
......@@ -673,13 +673,13 @@ GetJavaProperties(JNIEnv* env)
/* OS properties */
{
char buf[100];
OSVERSIONINFO ver;
OSVERSIONINFOEX ver;
ver.dwOSVersionInfoSize = sizeof(ver);
GetVersionEx(&ver);
GetVersionEx((OSVERSIONINFO *) &ver);
/*
* From msdn page on OSVERSIONINFOEX, current as of this
* writing decoding of dwMajorVersion and dwMinorVersion.
* writing, decoding of dwMajorVersion and dwMinorVersion.
*
* Operating system dwMajorVersion dwMinorVersion
* ================== ============== ==============
......@@ -692,7 +692,7 @@ GetJavaProperties(JNIEnv* env)
* Windows 2000 5 0
* Windows XP 5 1
* Windows Server 2003 family 5 2
* Windows Vista 6 0
* Windows Vista family 6 0
*
* This mapping will presumably be augmented as new Windows
* versions are released.
......@@ -724,7 +724,20 @@ GetJavaProperties(JNIEnv* env)
default: sprops.os_name = "Windows NT (unknown)"; break;
}
} else if (ver.dwMajorVersion == 6) {
sprops.os_name = "Windows Vista";
/*
* From MSDN OSVERSIONINFOEX documentation:
*
* "Because the version numbers for Windows Server 2008
* and Windows Vista are identical, you must also test
* whether the wProductType member is VER_NT_WORKSTATION.
* If wProductType is VER_NT_WORKSTATION, the operating
* system is Windows Vista; otherwise, it is Windows
* Server 2008."
*/
if (ver.wProductType == VER_NT_WORKSTATION)
sprops.os_name = "Windows Vista";
else
sprops.os_name = "Windows Server 2008";
} else {
sprops.os_name = "Windows NT (unknown)";
}
......
/*
* Copyright 2007 Sun Microsystems, Inc. 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.
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
* @test
* @bug 6631352
* @summary Check that appends are atomic
*/
import java.io.File;
import java.io.FileOutputStream;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class AtomicAppend {
// Before the fix for
// 6631352: Implement atomic append mode using FILE_APPEND_DATA (win)
// this would fail intermittently on windows
void test(String[] args) throws Throwable {
final int nThreads = 10;
final int writes = 1000;
final File file = new File("foo");
file.delete();
try {
final ExecutorService es = Executors.newFixedThreadPool(nThreads);
for (int i = 0; i < nThreads; i++)
es.execute(new Runnable() { public void run() {
try {
FileOutputStream s = new FileOutputStream(file, true);
for (int j = 0; j < 1000; j++) {
s.write((int) 'x');
s.flush();
}
s.close();
} catch (Throwable t) { unexpected(t); }}});
es.shutdown();
es.awaitTermination(10L, TimeUnit.MINUTES);
equal(file.length(), (long) (nThreads * writes));
} finally {
file.delete();
}
}
//--------------------- Infrastructure ---------------------------
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
new AtomicAppend().instanceMain(args);}
void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}
......@@ -25,12 +25,15 @@
* @test
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
* 6464154 6523983 6206031
* 6464154 6523983 6206031 4960438 6631352 6631966
* @summary Basic tests for Process and Environment Variable code
* @run main/othervm Basic
* @author Martin Buchholz
*/
import java.lang.ProcessBuilder.Redirect;
import static java.lang.ProcessBuilder.Redirect.*;
import java.io.*;
import java.util.*;
import java.security.*;
......@@ -257,7 +260,29 @@ public class Basic {
public static class JavaChild {
public static void main(String args[]) throws Throwable {
String action = args[0];
if (action.equals("System.getenv(String)")) {
if (action.equals("testIO")) {
String expected = "standard input";
char[] buf = new char[expected.length()+1];
int n = new InputStreamReader(System.in).read(buf,0,buf.length);
if (n != expected.length())
System.exit(5);
if (! new String(buf,0,n).equals(expected))
System.exit(5);
System.err.print("standard error");
System.out.print("standard output");
} else if (action.equals("testInheritIO")) {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("testIO");
ProcessBuilder pb = new ProcessBuilder(childArgs);
pb.inheritIO();
ProcessResults r = run(pb);
if (! r.out().equals(""))
System.exit(7);
if (! r.err().equals(""))
System.exit(8);
if (r.exitValue() != 0)
System.exit(9);
} else if (action.equals("System.getenv(String)")) {
String val = System.getenv(args[1]);
printUTF8(val == null ? "null" : val);
} else if (action.equals("System.getenv(\\u1234)")) {
......@@ -599,6 +624,333 @@ public class Basic {
} catch (Throwable t) { unexpected(t); }
}
static void checkRedirects(ProcessBuilder pb,
Redirect in, Redirect out, Redirect err) {
equal(pb.redirectInput(), in);
equal(pb.redirectOutput(), out);
equal(pb.redirectError(), err);
}
static void redirectIO(ProcessBuilder pb,
Redirect in, Redirect out, Redirect err) {
pb.redirectInput(in);
pb.redirectOutput(out);
pb.redirectError(err);
}
static void setFileContents(File file, String contents) {
try {
Writer w = new FileWriter(file);
w.write(contents);
w.close();
} catch (Throwable t) { unexpected(t); }
}
static String fileContents(File file) {
try {
Reader r = new FileReader(file);
StringBuilder sb = new StringBuilder();
char[] buffer = new char[1024];
int n;
while ((n = r.read(buffer)) != -1)
sb.append(buffer,0,n);
r.close();
return new String(sb);
} catch (Throwable t) { unexpected(t); return ""; }
}
static void testIORedirection() throws Throwable {
final File ifile = new File("ifile");
final File ofile = new File("ofile");
final File efile = new File("efile");
ifile.delete();
ofile.delete();
efile.delete();
//----------------------------------------------------------------
// Check mutual inequality of different types of Redirect
//----------------------------------------------------------------
Redirect[] redirects =
{ PIPE,
INHERIT,
Redirect.from(ifile),
Redirect.to(ifile),
Redirect.appendTo(ifile),
Redirect.from(ofile),
Redirect.to(ofile),
Redirect.appendTo(ofile),
};
for (int i = 0; i < redirects.length; i++)
for (int j = 0; j < redirects.length; j++)
equal(redirects[i].equals(redirects[j]), (i == j));
//----------------------------------------------------------------
// Check basic properties of different types of Redirect
//----------------------------------------------------------------
equal(PIPE.type(), Redirect.Type.PIPE);
equal(PIPE.toString(), "PIPE");
equal(PIPE.file(), null);
equal(INHERIT.type(), Redirect.Type.INHERIT);
equal(INHERIT.toString(), "INHERIT");
equal(INHERIT.file(), null);
equal(Redirect.from(ifile).type(), Redirect.Type.READ);
equal(Redirect.from(ifile).toString(),
"redirect to read from file \"ifile\"");
equal(Redirect.from(ifile).file(), ifile);
equal(Redirect.from(ifile),
Redirect.from(ifile));
equal(Redirect.from(ifile).hashCode(),
Redirect.from(ifile).hashCode());
equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
equal(Redirect.to(ofile).toString(),
"redirect to write to file \"ofile\"");
equal(Redirect.to(ofile).file(), ofile);
equal(Redirect.to(ofile),
Redirect.to(ofile));
equal(Redirect.to(ofile).hashCode(),
Redirect.to(ofile).hashCode());
equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
equal(Redirect.appendTo(efile).toString(),
"redirect to append to file \"efile\"");
equal(Redirect.appendTo(efile).file(), efile);
equal(Redirect.appendTo(efile),
Redirect.appendTo(efile));
equal(Redirect.appendTo(efile).hashCode(),
Redirect.appendTo(efile).hashCode());
//----------------------------------------------------------------
// Check initial values of redirects
//----------------------------------------------------------------
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("testIO");
final ProcessBuilder pb = new ProcessBuilder(childArgs);
checkRedirects(pb, PIPE, PIPE, PIPE);
//----------------------------------------------------------------
// Check inheritIO
//----------------------------------------------------------------
pb.inheritIO();
checkRedirects(pb, INHERIT, INHERIT, INHERIT);
//----------------------------------------------------------------
// Check setters and getters agree
//----------------------------------------------------------------
pb.redirectInput(ifile);
equal(pb.redirectInput().file(), ifile);
equal(pb.redirectInput(), Redirect.from(ifile));
pb.redirectOutput(ofile);
equal(pb.redirectOutput().file(), ofile);
equal(pb.redirectOutput(), Redirect.to(ofile));
pb.redirectError(efile);
equal(pb.redirectError().file(), efile);
equal(pb.redirectError(), Redirect.to(efile));
THROWS(IllegalArgumentException.class,
new Fun(){void f() {
pb.redirectInput(Redirect.to(ofile)); }},
new Fun(){void f() {
pb.redirectInput(Redirect.appendTo(ofile)); }},
new Fun(){void f() {
pb.redirectOutput(Redirect.from(ifile)); }},
new Fun(){void f() {
pb.redirectError(Redirect.from(ifile)); }});
THROWS(IOException.class,
// Input file does not exist
new Fun(){void f() throws Throwable { pb.start(); }});
setFileContents(ifile, "standard input");
//----------------------------------------------------------------
// Writing to non-existent files
//----------------------------------------------------------------
{
ProcessResults r = run(pb);
equal(r.exitValue(), 0);
equal(fileContents(ofile), "standard output");
equal(fileContents(efile), "standard error");
equal(r.out(), "");
equal(r.err(), "");
ofile.delete();
efile.delete();
}
//----------------------------------------------------------------
// Both redirectErrorStream + redirectError
//----------------------------------------------------------------
{
pb.redirectErrorStream(true);
ProcessResults r = run(pb);
equal(r.exitValue(), 0);
equal(fileContents(ofile),
"standard error" + "standard output");
equal(fileContents(efile), "");
equal(r.out(), "");
equal(r.err(), "");
ofile.delete();
efile.delete();
}
//----------------------------------------------------------------
// Appending to existing files
//----------------------------------------------------------------
{
setFileContents(ofile, "ofile-contents");
setFileContents(efile, "efile-contents");
pb.redirectOutput(Redirect.appendTo(ofile));
pb.redirectError(Redirect.appendTo(efile));
pb.redirectErrorStream(false);
ProcessResults r = run(pb);
equal(r.exitValue(), 0);
equal(fileContents(ofile),
"ofile-contents" + "standard output");
equal(fileContents(efile),
"efile-contents" + "standard error");
equal(r.out(), "");
equal(r.err(), "");
ofile.delete();
efile.delete();
}
//----------------------------------------------------------------
// Replacing existing files
//----------------------------------------------------------------
{
setFileContents(ofile, "ofile-contents");
setFileContents(efile, "efile-contents");
pb.redirectOutput(ofile);
pb.redirectError(Redirect.to(efile));
ProcessResults r = run(pb);
equal(r.exitValue(), 0);
equal(fileContents(ofile), "standard output");
equal(fileContents(efile), "standard error");
equal(r.out(), "");
equal(r.err(), "");
ofile.delete();
efile.delete();
}
//----------------------------------------------------------------
// Appending twice to the same file?
//----------------------------------------------------------------
{
setFileContents(ofile, "ofile-contents");
setFileContents(efile, "efile-contents");
Redirect appender = Redirect.appendTo(ofile);
pb.redirectOutput(appender);
pb.redirectError(appender);
ProcessResults r = run(pb);
equal(r.exitValue(), 0);
equal(fileContents(ofile),
"ofile-contents" +
"standard error" +
"standard output");
equal(fileContents(efile), "efile-contents");
equal(r.out(), "");
equal(r.err(), "");
ifile.delete();
ofile.delete();
efile.delete();
}
//----------------------------------------------------------------
// Testing INHERIT is harder.
// Note that this requires __FOUR__ nested JVMs involved in one test,
// if you count the harness JVM.
//----------------------------------------------------------------
{
redirectIO(pb, PIPE, PIPE, PIPE);
List<String> command = pb.command();
command.set(command.size() - 1, "testInheritIO");
Process p = pb.start();
new PrintStream(p.getOutputStream()).print("standard input");
p.getOutputStream().close();
ProcessResults r = run(p);
equal(r.exitValue(), 0);
equal(r.out(), "standard output");
equal(r.err(), "standard error");
}
//----------------------------------------------------------------
// Test security implications of I/O redirection
//----------------------------------------------------------------
// Read access to current directory is always granted;
// So create a tmpfile for input instead.
final File tmpFile = File.createTempFile("Basic", "tmp");
setFileContents(tmpFile, "standard input");
final Policy policy = new Policy();
Policy.setPolicy(policy);
System.setSecurityManager(new SecurityManager());
try {
final Permission xPermission
= new FilePermission("<<ALL FILES>>", "execute");
final Permission rxPermission
= new FilePermission("<<ALL FILES>>", "read,execute");
final Permission wxPermission
= new FilePermission("<<ALL FILES>>", "write,execute");
final Permission rwxPermission
= new FilePermission("<<ALL FILES>>", "read,write,execute");
THROWS(SecurityException.class,
new Fun() { void f() throws IOException {
policy.setPermissions(xPermission);
redirectIO(pb, from(tmpFile), PIPE, PIPE);
pb.start();}},
new Fun() { void f() throws IOException {
policy.setPermissions(rxPermission);
redirectIO(pb, PIPE, to(ofile), PIPE);
pb.start();}},
new Fun() { void f() throws IOException {
policy.setPermissions(rxPermission);
redirectIO(pb, PIPE, PIPE, to(efile));
pb.start();}});
{
policy.setPermissions(rxPermission);
redirectIO(pb, from(tmpFile), PIPE, PIPE);
ProcessResults r = run(pb);
equal(r.out(), "standard output");
equal(r.err(), "standard error");
}
{
policy.setPermissions(wxPermission);
redirectIO(pb, PIPE, to(ofile), to(efile));
Process p = pb.start();
new PrintStream(p.getOutputStream()).print("standard input");
p.getOutputStream().close();
ProcessResults r = run(p);
policy.setPermissions(rwxPermission);
equal(fileContents(ofile), "standard output");
equal(fileContents(efile), "standard error");
}
{
policy.setPermissions(rwxPermission);
redirectIO(pb, from(tmpFile), to(ofile), to(efile));
ProcessResults r = run(pb);
policy.setPermissions(rwxPermission);
equal(fileContents(ofile), "standard output");
equal(fileContents(efile), "standard error");
}
} finally {
policy.setPermissions(new RuntimePermission("setSecurityManager"));
System.setSecurityManager(null);
tmpFile.delete();
ifile.delete();
ofile.delete();
efile.delete();
}
}
private static void realMain(String[] args) throws Throwable {
if (Windows.is())
System.out.println("This appears to be a Windows system.");
......@@ -607,6 +959,9 @@ public class Basic {
if (UnicodeOS.is())
System.out.println("This appears to be a Unicode-based OS.");
try { testIORedirection(); }
catch (Throwable t) { unexpected(t); }
//----------------------------------------------------------------
// Basic tests for setting, replacing and deleting envvars
//----------------------------------------------------------------
......@@ -1354,7 +1709,8 @@ public class Basic {
execPermission);
ProcessBuilder pb = new ProcessBuilder("env");
pb.environment().put("foo","bar");
pb.start();
Process p = pb.start();
closeStreams(p);
} catch (IOException e) { // OK
} catch (Throwable t) { unexpected(t); }
......@@ -1378,6 +1734,14 @@ public class Basic {
}
static void closeStreams(Process p) {
try {
p.getOutputStream().close();
p.getInputStream().close();
p.getErrorStream().close();
} catch (Throwable t) { unexpected(t); }
}
//----------------------------------------------------------------
// A Policy class designed to make permissions fiddling very easy.
//----------------------------------------------------------------
......@@ -1432,10 +1796,19 @@ public class Basic {
}
} catch (Throwable t) {
throwable = t;
} finally {
try { is.close(); }
catch (Throwable t) { throwable = t; }
}
}
}
static ProcessResults run(ProcessBuilder pb) {
try {
return run(pb.start());
} catch (Throwable t) { unexpected(t); return null; }
}
private static ProcessResults run(Process p) {
Throwable throwable = null;
int exitValue = -1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册