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

Merge

...@@ -85,7 +85,6 @@ SUNWprivate_1.1 { ...@@ -85,7 +85,6 @@ SUNWprivate_1.1 {
Java_java_io_FileOutputStream_close0; Java_java_io_FileOutputStream_close0;
Java_java_io_FileOutputStream_initIDs; Java_java_io_FileOutputStream_initIDs;
Java_java_io_FileOutputStream_open; Java_java_io_FileOutputStream_open;
Java_java_io_FileOutputStream_openAppend;
Java_java_io_FileOutputStream_write; Java_java_io_FileOutputStream_write;
Java_java_io_FileOutputStream_writeBytes; Java_java_io_FileOutputStream_writeBytes;
Java_java_io_FileSystem_getFileSystem; Java_java_io_FileSystem_getFileSystem;
......
...@@ -48,15 +48,15 @@ public ...@@ -48,15 +48,15 @@ public
class FileInputStream extends InputStream class FileInputStream extends InputStream
{ {
/* File Descriptor - handle to the open file */ /* File Descriptor - handle to the open file */
private FileDescriptor fd; private final FileDescriptor fd;
private FileChannel channel = null; private FileChannel channel = null;
private Object closeLock = new Object(); private final Object closeLock = new Object();
private volatile boolean closed = false; private volatile boolean closed = false;
private static ThreadLocal<Boolean> runningFinalize = private static final ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<Boolean>(); new ThreadLocal<Boolean>();
private static boolean isRunningFinalize() { private static boolean isRunningFinalize() {
Boolean val; Boolean val;
...@@ -151,7 +151,7 @@ class FileInputStream extends InputStream ...@@ -151,7 +151,7 @@ class FileInputStream extends InputStream
* is thrown. * is thrown.
* <p> * <p>
* This constructor does not throw an exception if <code>fdObj</code> * 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 * However, if the methods are invoked on the resulting stream to attempt
* I/O on the stream, an <code>IOException</code> is thrown. * I/O on the stream, an <code>IOException</code> is thrown.
* *
...@@ -389,7 +389,7 @@ class FileInputStream extends InputStream ...@@ -389,7 +389,7 @@ class FileInputStream extends InputStream
* @see java.io.FileInputStream#close() * @see java.io.FileInputStream#close()
*/ */
protected void finalize() throws IOException { 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 * Finalizer should not release the FileDescriptor if another
......
...@@ -52,20 +52,16 @@ public ...@@ -52,20 +52,16 @@ public
class FileOutputStream extends OutputStream class FileOutputStream extends OutputStream
{ {
/** /**
* The system dependent file descriptor. The value is * The system dependent file descriptor.
* 1 more than actual file descriptor. This means that
* the default value 0 indicates that the file is not open.
*/ */
private FileDescriptor fd; private final FileDescriptor fd;
private FileChannel channel= null; private FileChannel channel= null;
private boolean append = false; private final Object closeLock = new Object();
private Object closeLock = new Object();
private volatile boolean closed = false; private volatile boolean closed = false;
private static ThreadLocal<Boolean> runningFinalize = private static final ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<Boolean>(); new ThreadLocal<Boolean>();
private static boolean isRunningFinalize() { private static boolean isRunningFinalize() {
Boolean val; Boolean val;
...@@ -75,7 +71,7 @@ class FileOutputStream extends OutputStream ...@@ -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 * specified name. A new <code>FileDescriptor</code> object is
* created to represent this file connection. * created to represent this file connection.
* <p> * <p>
...@@ -100,8 +96,8 @@ class FileOutputStream extends OutputStream ...@@ -100,8 +96,8 @@ class FileOutputStream extends OutputStream
} }
/** /**
* Creates an output file stream to write to the file with the specified * Creates a file output stream to write to the file with the specified
* <code>name</code>. If the second argument is <code>true</code>, then * name. If the second argument is <code>true</code>, then
* bytes will be written to the end of the file rather than the beginning. * 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 * A new <code>FileDescriptor</code> object is created to represent this
* file connection. * file connection.
...@@ -202,16 +198,11 @@ class FileOutputStream extends OutputStream ...@@ -202,16 +198,11 @@ class FileOutputStream extends OutputStream
} }
fd = new FileDescriptor(); fd = new FileDescriptor();
fd.incrementAndGetUseCount(); fd.incrementAndGetUseCount();
this.append = append; open(name, append);
if (append) {
openAppend(name);
} else {
open(name);
}
} }
/** /**
* 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 * descriptor, which represents an existing connection to an actual
* file in the file system. * file in the file system.
* <p> * <p>
...@@ -223,7 +214,7 @@ class FileOutputStream extends OutputStream ...@@ -223,7 +214,7 @@ class FileOutputStream extends OutputStream
* is thrown. * is thrown.
* <p> * <p>
* This constructor does not throw an exception if <code>fdObj</code> * 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 * However, if the methods are invoked on the resulting stream to attempt
* I/O on the stream, an <code>IOException</code> is thrown. * I/O on the stream, an <code>IOException</code> is thrown.
* *
...@@ -252,16 +243,12 @@ class FileOutputStream extends OutputStream ...@@ -252,16 +243,12 @@ class FileOutputStream extends OutputStream
} }
/** /**
* Opens a file, with the specified name, for writing. * Opens a file, with the specified name, for overwriting or appending.
* @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.
* @param name name of file to be opened * @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 * Writes the specified byte to this file output stream. Implements
...@@ -385,7 +372,7 @@ class FileOutputStream extends OutputStream ...@@ -385,7 +372,7 @@ class FileOutputStream extends OutputStream
public FileChannel getChannel() { public FileChannel getChannel() {
synchronized (this) { synchronized (this) {
if (channel == null) { 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() * Increment fd's use count. Invoking the channel's close()
...@@ -408,7 +395,7 @@ class FileOutputStream extends OutputStream ...@@ -408,7 +395,7 @@ class FileOutputStream extends OutputStream
*/ */
protected void finalize() throws IOException { protected void finalize() throws IOException {
if (fd != null) { if (fd != null) {
if (fd == fd.out || fd == fd.err) { if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
flush(); flush();
} else { } else {
......
...@@ -41,18 +41,24 @@ import java.io.*; ...@@ -41,18 +41,24 @@ import java.io.*;
* <p>The methods that create processes may not work well for special * <p>The methods that create processes may not work well for special
* processes on certain native platforms, such as native windowing * processes on certain native platforms, such as native windowing
* processes, daemon processes, Win16/DOS processes on Microsoft * processes, daemon processes, Win16/DOS processes on Microsoft
* Windows, or shell scripts. The created subprocess does not have * Windows, or shell scripts.
* its own terminal or console. All its standard I/O (i.e. stdin, *
* stdout, stderr) operations will be redirected to the parent process * <p>By default, the created subprocess does not have its own terminal
* through three streams * or console. All its standard I/O (i.e. stdin, stdout, stderr)
* ({@link #getOutputStream()}, * operations will be redirected to the parent process, where they can
* {@link #getInputStream()}, * be accessed via the streams obtained using the methods
* {@link #getErrorStream()}). * {@link #getOutputStream()},
* {@link #getInputStream()}, and
* {@link #getErrorStream()}.
* The parent process uses these streams to feed input to and get output * The parent process uses these streams to feed input to and get output
* from the subprocess. Because some native platforms only provide * from the subprocess. Because some native platforms only provide
* limited buffer size for standard input and output streams, failure * limited buffer size for standard input and output streams, failure
* to promptly write the input stream or read the output stream of * 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 * <p>The subprocess is not killed when there are no more references to
* the {@code Process} object, but rather the subprocess * the {@code Process} object, but rather the subprocess
...@@ -62,16 +68,22 @@ import java.io.*; ...@@ -62,16 +68,22 @@ import java.io.*;
* Process} object execute asynchronously or concurrently with respect * Process} object execute asynchronously or concurrently with respect
* to the Java process that owns the {@code Process} object. * to the Java process that owns the {@code Process} object.
* *
* @author unascribed * <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way
* @see ProcessBuilder * to create a {@code Process}.
*
* @since JDK1.0 * @since JDK1.0
*/ */
public abstract class Process { public abstract class Process {
/** /**
* Returns the output stream connected to the normal input of the * Returns the output stream connected to the normal input of the
* subprocess. Output to the stream is piped into the standard * subprocess. Output to the stream is piped into the standard
* input stream of the process represented by this {@code Process} * input of the process represented by this {@code Process} object.
* 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 * <p>Implementation note: It is a good idea for the returned
* output stream to be buffered. * output stream to be buffered.
...@@ -84,30 +96,47 @@ public abstract class Process { ...@@ -84,30 +96,47 @@ public abstract class Process {
/** /**
* Returns the input stream connected to the normal output of the * Returns the input stream connected to the normal output of the
* subprocess. The stream obtains data piped from the standard * subprocess. The stream obtains data piped from the standard
* output stream of the process represented by this {@code * output of the process represented by this {@code Process} object.
* 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 * <p>Implementation note: It is a good idea for the returned
* input stream to be buffered. * input stream to be buffered.
* *
* @return the input stream connected to the normal output of the * @return the input stream connected to the normal output of the
* subprocess * subprocess
* @see ProcessBuilder#redirectErrorStream()
*/ */
abstract public InputStream getInputStream(); abstract public InputStream getInputStream();
/** /**
* Returns the input stream connected to the error output stream of * Returns the input stream connected to the error output of the
* the subprocess. The stream obtains data piped from the error * subprocess. The stream obtains data piped from the error output
* output stream of the process represented by this {@code Process} * of the process represented by this {@code Process} object.
* 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 * <p>Implementation note: It is a good idea for the returned
* input stream to be buffered. * 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 * the subprocess
* @see ProcessBuilder#redirectErrorStream()
*/ */
abstract public InputStream getErrorStream(); abstract public InputStream getErrorStream();
......
...@@ -33,4 +33,8 @@ import java.io.FileDescriptor; ...@@ -33,4 +33,8 @@ import java.io.FileDescriptor;
public interface JavaIOFileDescriptorAccess { public interface JavaIOFileDescriptorAccess {
public void set(FileDescriptor obj, int fd); public void set(FileDescriptor obj, int fd);
public int get(FileDescriptor 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 ...@@ -52,39 +52,37 @@ public class FileChannelImpl
{ {
// Used to make native read and write calls // Used to make native read and write calls
private static NativeDispatcher nd; private static final NativeDispatcher nd;
// Memory allocation size for mapping buffers // Memory allocation size for mapping buffers
private static long allocationGranularity; private static final long allocationGranularity;
// Cached field for MappedByteBuffer.isAMappedBuffer // Cached field for MappedByteBuffer.isAMappedBuffer
private static Field isAMappedBufferField; private static final Field isAMappedBufferField;
// File descriptor // File descriptor
private FileDescriptor fd; private final FileDescriptor fd;
// File access mode (immutable) // File access mode (immutable)
private boolean writable; private final boolean writable;
private boolean readable; private final boolean readable;
private boolean appending;
// Required to prevent finalization of creating stream (immutable) // 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 // 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 // Lock for operations involving position and size
private Object positionLock = new Object(); private final Object positionLock = new Object();
private FileChannelImpl(FileDescriptor fd, boolean readable, private FileChannelImpl(FileDescriptor fd, boolean readable,
boolean writable, Object parent, boolean append) boolean writable, Object parent)
{ {
this.fd = fd; this.fd = fd;
this.readable = readable; this.readable = readable;
this.writable = writable; this.writable = writable;
this.parent = parent; this.parent = parent;
this.appending = append;
} }
// Invoked by getChannel() methods // Invoked by getChannel() methods
...@@ -94,14 +92,7 @@ public class FileChannelImpl ...@@ -94,14 +92,7 @@ public class FileChannelImpl
boolean readable, boolean writable, boolean readable, boolean writable,
Object parent) Object parent)
{ {
return new FileChannelImpl(fd, readable, writable, parent, false); return new FileChannelImpl(fd, readable, writable, parent);
}
public static FileChannel open(FileDescriptor fd,
boolean readable, boolean writable,
Object parent, boolean append)
{
return new FileChannelImpl(fd, readable, writable, parent, append);
} }
private void ensureOpen() throws IOException { private void ensureOpen() throws IOException {
...@@ -134,15 +125,7 @@ public class FileChannelImpl ...@@ -134,15 +125,7 @@ public class FileChannelImpl
// superclass AbstractInterruptibleChannel, but the isOpen logic in // superclass AbstractInterruptibleChannel, but the isOpen logic in
// that method will prevent this method from being reinvoked. // that method will prevent this method from being reinvoked.
// //
if (parent instanceof FileInputStream) ((java.io.Closeable)parent).close();
((FileInputStream)parent).close();
else if (parent instanceof FileOutputStream)
((FileOutputStream)parent).close();
else if (parent instanceof RandomAccessFile)
((RandomAccessFile)parent).close();
else
assert false;
} else { } else {
nd.close(fd); nd.close(fd);
} }
...@@ -218,8 +201,6 @@ public class FileChannelImpl ...@@ -218,8 +201,6 @@ public class FileChannelImpl
if (!isOpen()) if (!isOpen())
return 0; return 0;
ti = threads.add(); ti = threads.add();
if (appending)
position(size());
do { do {
n = IOUtil.write(fd, src, -1, nd, positionLock); n = IOUtil.write(fd, src, -1, nd, positionLock);
} while ((n == IOStatus.INTERRUPTED) && isOpen()); } while ((n == IOStatus.INTERRUPTED) && isOpen());
...@@ -244,8 +225,6 @@ public class FileChannelImpl ...@@ -244,8 +225,6 @@ public class FileChannelImpl
if (!isOpen()) if (!isOpen())
return 0; return 0;
ti = threads.add(); ti = threads.add();
if (appending)
position(size());
do { do {
n = IOUtil.write(fd, srcs, nd); n = IOUtil.write(fd, srcs, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen()); } while ((n == IOStatus.INTERRUPTED) && isOpen());
...@@ -1051,7 +1030,7 @@ public class FileChannelImpl ...@@ -1051,7 +1030,7 @@ public class FileChannelImpl
private FileKey fileKey; private FileKey fileKey;
FileLockReference(FileLock referent, FileLockReference(FileLock referent,
ReferenceQueue queue, ReferenceQueue<FileLock> queue,
FileKey key) { FileKey key) {
super(referent, queue); super(referent, queue);
this.fileKey = key; this.fileKey = key;
...@@ -1073,7 +1052,7 @@ public class FileChannelImpl ...@@ -1073,7 +1052,7 @@ public class FileChannelImpl
new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>(); new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
// reference queue for cleared refs // reference queue for cleared refs
private static ReferenceQueue queue = new ReferenceQueue(); private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
// the enclosing file channel // the enclosing file channel
private FileChannelImpl fci; private FileChannelImpl fci;
......
...@@ -40,7 +40,7 @@ readSingle(JNIEnv *env, jobject this, jfieldID fid) { ...@@ -40,7 +40,7 @@ readSingle(JNIEnv *env, jobject this, jfieldID fid) {
char ret; char ret;
FD fd = GET_FD(this, fid); FD fd = GET_FD(this, fid);
if (fd == -1) { if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed"); JNU_ThrowIOException(env, "Stream Closed");
return -1; return -1;
} }
nread = IO_Read(fd, &ret, 1); nread = IO_Read(fd, &ret, 1);
...@@ -94,8 +94,8 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes, ...@@ -94,8 +94,8 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
fd = GET_FD(this, fid); fd = GET_FD(this, fid);
if (fd == -1) { if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed"); JNU_ThrowIOException(env, "Stream Closed");
return -1; return -1;
} }
nread = IO_Read(fd, buf, len); nread = IO_Read(fd, buf, len);
...@@ -121,7 +121,7 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) { ...@@ -121,7 +121,7 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
int n; int n;
FD fd = GET_FD(this, fid); FD fd = GET_FD(this, fid);
if (fd == -1) { if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed"); JNU_ThrowIOException(env, "Stream Closed");
return; return;
} }
n = IO_Write(fd, &c, 1); n = IO_Write(fd, &c, 1);
...@@ -172,8 +172,8 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, ...@@ -172,8 +172,8 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
while (len > 0) { while (len > 0) {
fd = GET_FD(this, fid); fd = GET_FD(this, fid);
if (fd == -1) { if (fd == -1) {
JNU_ThrowIOException (env, "Stream Closed"); JNU_ThrowIOException(env, "Stream Closed");
return; break;
} }
n = IO_Write(fd, buf+off, len); n = IO_Write(fd, buf+off, len);
if (n == JVM_IO_ERR) { if (n == JVM_IO_ERR) {
......
...@@ -152,11 +152,19 @@ public final class FileDescriptor { ...@@ -152,11 +152,19 @@ public final class FileDescriptor {
public int get(FileDescriptor obj) { public int get(FileDescriptor obj) {
return obj.fd; 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() { int incrementAndGetUseCount() {
return useCount.incrementAndGet(); return useCount.incrementAndGet();
......
...@@ -26,7 +26,10 @@ ...@@ -26,7 +26,10 @@
package java.lang; package java.lang;
import java.io.IOException; 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 * This class is for the exclusive use of ProcessBuilder.start() to
...@@ -36,6 +39,9 @@ import java.lang.Process; ...@@ -36,6 +39,9 @@ import java.lang.Process;
* @since 1.5 * @since 1.5
*/ */
final class ProcessImpl { final class ProcessImpl {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
private ProcessImpl() {} // Not instantiable private ProcessImpl() {} // Not instantiable
private static byte[] toCString(String s) { private static byte[] toCString(String s) {
...@@ -54,6 +60,7 @@ final class ProcessImpl { ...@@ -54,6 +60,7 @@ final class ProcessImpl {
static Process start(String[] cmdarray, static Process start(String[] cmdarray,
java.util.Map<String,String> environment, java.util.Map<String,String> environment,
String dir, String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream) boolean redirectErrorStream)
throws IOException throws IOException
{ {
...@@ -78,11 +85,61 @@ final class ProcessImpl { ...@@ -78,11 +85,61 @@ final class ProcessImpl {
int[] envc = new int[1]; int[] envc = new int[1];
byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc); 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 return new UNIXProcess
(toCString(cmdarray[0]), (toCString(cmdarray[0]),
argBlock, args.length, argBlock, args.length,
envBlock, envc[0], envBlock, envc[0],
toCString(dir), toCString(dir),
std_fds,
redirectErrorStream); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -34,9 +34,9 @@ import java.io.*; ...@@ -34,9 +34,9 @@ import java.io.*;
*/ */
final class UNIXProcess extends Process { final class UNIXProcess extends Process {
private FileDescriptor stdin_fd; private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
private FileDescriptor stdout_fd; = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
private FileDescriptor stderr_fd;
private int pid; private int pid;
private int exitcode; private int exitcode;
private boolean hasExited; private boolean hasExited;
...@@ -48,15 +48,26 @@ final class UNIXProcess extends Process { ...@@ -48,15 +48,26 @@ final class UNIXProcess extends Process {
/* this is for the reaping thread */ /* this is for the reaping thread */
private native int waitForProcessExit(int pid); 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, private native int forkAndExec(byte[] prog,
byte[] argBlock, int argc, byte[] argBlock, int argc,
byte[] envBlock, int envc, byte[] envBlock, int envc,
byte[] dir, byte[] dir,
boolean redirectErrorStream, int[] std_fds,
FileDescriptor stdin_fd, boolean redirectErrorStream)
FileDescriptor stdout_fd, throws IOException;
FileDescriptor stderr_fd)
throws IOException;
/* In the process constructor we wait on this gate until the process */ /* In the process constructor we wait on this gate until the process */
/* has been created. Then we return from the constructor. */ /* has been created. Then we return from the constructor. */
...@@ -97,67 +108,82 @@ final class UNIXProcess extends Process { ...@@ -97,67 +108,82 @@ final class UNIXProcess extends Process {
} }
UNIXProcess(final byte[] prog, UNIXProcess(final byte[] prog,
final byte[] argBlock, final int argc, final byte[] argBlock, final int argc,
final byte[] envBlock, final int envc, final byte[] envBlock, final int envc,
final byte[] dir, final byte[] dir,
final boolean redirectErrorStream) final int[] std_fds,
final boolean redirectErrorStream)
throws IOException { throws IOException {
stdin_fd = new FileDescriptor();
stdout_fd = new FileDescriptor();
stderr_fd = new FileDescriptor();
final Gate gate = new Gate(); final Gate gate = new Gate();
/* /*
* For each subprocess forked a corresponding reaper thread * For each subprocess forked a corresponding reaper thread
* is started. That thread is the only thread which waits * is started. That thread is the only thread which waits
* for the subprocess to terminate and it doesn't hold any * for the subprocess to terminate and it doesn't hold any
* locks while doing so. This design allows waitFor() and * locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they * exitStatus() to be safely executed in parallel (and they
* need no native code). * need no native code).
*/ */
java.security.AccessController.doPrivileged( java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() { new java.security.PrivilegedAction<Void>() {
public Object run() { public Void run() {
Thread t = new Thread("process reaper") { Thread t = new Thread("process reaper") {
public void run() { public void run() {
try { try {
pid = forkAndExec(prog, pid = forkAndExec(prog,
argBlock, argc, argBlock, argc,
envBlock, envc, envBlock, envc,
dir, dir,
redirectErrorStream, std_fds,
stdin_fd, stdout_fd, stderr_fd); redirectErrorStream);
} catch (IOException e) { } catch (IOException e) {
gate.setException(e); /*remember to rethrow later*/ gate.setException(e); /*remember to rethrow later*/
gate.exit(); gate.exit();
return; return;
} }
java.security.AccessController.doPrivileged( java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() { new java.security.PrivilegedAction<Void>() {
public Object run() { public Void run() {
stdin_stream = new BufferedOutputStream(new if (std_fds[0] == -1)
FileOutputStream(stdin_fd)); stdin_stream = new ProcessBuilder.NullOutputStream();
stdout_stream = new BufferedInputStream(new else {
FileInputStream(stdout_fd)); 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); stderr_stream = new FileInputStream(stderr_fd);
return null;
} }
});
return null; }});
gate.exit(); /* exit from constructor */ gate.exit(); /* exit from constructor */
int res = waitForProcessExit(pid); int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) { synchronized (UNIXProcess.this) {
hasExited = true; hasExited = true;
exitcode = res; exitcode = res;
UNIXProcess.this.notifyAll(); UNIXProcess.this.notifyAll();
} }
} }
}; };
t.setDaemon(true); t.setDaemon(true);
t.start(); t.start();
return null; return null; }});
}
});
gate.waitForExit(); gate.waitForExit();
IOException e = gate.getException(); IOException e = gate.getException();
if (e != null) if (e != null)
...@@ -165,43 +191,43 @@ final class UNIXProcess extends Process { ...@@ -165,43 +191,43 @@ final class UNIXProcess extends Process {
} }
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
return stdin_stream; return stdin_stream;
} }
public InputStream getInputStream() { public InputStream getInputStream() {
return stdout_stream; return stdout_stream;
} }
public InputStream getErrorStream() { public InputStream getErrorStream() {
return stderr_stream; return stderr_stream;
} }
public synchronized int waitFor() throws InterruptedException { public synchronized int waitFor() throws InterruptedException {
while (!hasExited) { while (!hasExited) {
wait(); wait();
} }
return exitcode; return exitcode;
} }
public synchronized int exitValue() { public synchronized int exitValue() {
if (!hasExited) { if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited"); throw new IllegalThreadStateException("process hasn't exited");
} }
return exitcode; return exitcode;
} }
private static native void destroyProcess(int pid); private static native void destroyProcess(int pid);
public void destroy() { public void destroy() {
// There is a risk that pid will be recycled, causing us to // There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes // kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check, // that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window // there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too // is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe. // soon, so this is quite safe.
synchronized (this) { synchronized (this) {
if (!hasExited) if (!hasExited)
destroyProcess(pid); destroyProcess(pid);
} }
try { try {
stdin_stream.close(); stdin_stream.close();
stdout_stream.close(); stdout_stream.close();
...@@ -215,6 +241,6 @@ final class UNIXProcess extends Process { ...@@ -215,6 +241,6 @@ final class UNIXProcess extends Process {
private static native void initIDs(); private static native void initIDs();
static { 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -33,129 +33,155 @@ import java.io.*; ...@@ -33,129 +33,155 @@ import java.io.*;
*/ */
final class UNIXProcess extends Process { final class UNIXProcess extends Process {
private FileDescriptor stdin_fd; private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
private FileDescriptor stdout_fd; = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
private FileDescriptor stderr_fd;
private int pid; private final int pid;
private int exitcode; private int exitcode;
private boolean hasExited; private boolean hasExited;
private OutputStream stdin_stream; private OutputStream stdin_stream;
private BufferedInputStream stdout_stream; private InputStream stdout_stream;
private DeferredCloseInputStream stdout_inner_stream; private DeferredCloseInputStream stdout_inner_stream;
private DeferredCloseInputStream stderr_stream; private InputStream stderr_stream;
/* this is for the reaping thread */ /* this is for the reaping thread */
private native int waitForProcessExit(int pid); 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, private native int forkAndExec(byte[] prog,
byte[] argBlock, int argc, byte[] argBlock, int argc,
byte[] envBlock, int envc, byte[] envBlock, int envc,
byte[] dir, byte[] dir,
boolean redirectErrorStream, int[] std_fds,
FileDescriptor stdin_fd, boolean redirectErrorStream)
FileDescriptor stdout_fd, throws IOException;
FileDescriptor stderr_fd)
throws IOException;
UNIXProcess(final byte[] prog, UNIXProcess(final byte[] prog,
final byte[] argBlock, int argc, final byte[] argBlock, int argc,
final byte[] envBlock, int envc, final byte[] envBlock, int envc,
final byte[] dir, final byte[] dir,
final boolean redirectErrorStream) final int[] std_fds,
final boolean redirectErrorStream)
throws IOException { throws IOException {
stdin_fd = new FileDescriptor(); pid = forkAndExec(prog,
stdout_fd = new FileDescriptor(); argBlock, argc,
stderr_fd = new FileDescriptor(); envBlock, envc,
dir,
pid = forkAndExec(prog, std_fds,
argBlock, argc, redirectErrorStream);
envBlock, envc,
dir, java.security.AccessController.doPrivileged(
redirectErrorStream, new java.security.PrivilegedAction<Void>() { public Void run() {
stdin_fd, stdout_fd, stderr_fd); if (std_fds[0] == -1)
stdin_stream = new ProcessBuilder.NullOutputStream();
java.security.AccessController.doPrivileged( else {
new java.security.PrivilegedAction() { FileDescriptor stdin_fd = new FileDescriptor();
public Object run() { fdAccess.set(stdin_fd, std_fds[0]);
stdin_stream stdin_stream = new BufferedOutputStream(
= new BufferedOutputStream(new FileOutputStream(stdin_fd)); new FileOutputStream(stdin_fd));
stdout_inner_stream = new DeferredCloseInputStream(stdout_fd); }
stdout_stream = new BufferedInputStream(stdout_inner_stream);
stderr_stream = new DeferredCloseInputStream(stderr_fd); if (std_fds[1] == -1)
return null; 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);
* For each subprocess forked a corresponding reaper thread stdout_stream = new BufferedInputStream(stdout_inner_stream);
* 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 if (std_fds[2] == -1)
* exitStatus() to be safely executed in parallel (and they stderr_stream = new ProcessBuilder.NullInputStream();
* need no native code). else {
*/ FileDescriptor stderr_fd = new FileDescriptor();
fdAccess.set(stderr_fd, std_fds[2]);
java.security.AccessController.doPrivileged( stderr_stream = new DeferredCloseInputStream(stderr_fd);
new java.security.PrivilegedAction() { }
public Object run() {
Thread t = new Thread("process reaper") { return null; }});
public void run() {
int res = waitForProcessExit(pid); /*
synchronized (UNIXProcess.this) { * For each subprocess forked a corresponding reaper thread
hasExited = true; * is started. That thread is the only thread which waits
exitcode = res; * for the subprocess to terminate and it doesn't hold any
UNIXProcess.this.notifyAll(); * locks while doing so. This design allows waitFor() and
} * exitStatus() to be safely executed in parallel (and they
} * need no native code).
}; */
t.setDaemon(true);
t.start(); java.security.AccessController.doPrivileged(
return null; 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() { public OutputStream getOutputStream() {
return stdin_stream; return stdin_stream;
} }
public InputStream getInputStream() { public InputStream getInputStream() {
return stdout_stream; return stdout_stream;
} }
public InputStream getErrorStream() { public InputStream getErrorStream() {
return stderr_stream; return stderr_stream;
} }
public synchronized int waitFor() throws InterruptedException { public synchronized int waitFor() throws InterruptedException {
while (!hasExited) { while (!hasExited) {
wait(); wait();
} }
return exitcode; return exitcode;
} }
public synchronized int exitValue() { public synchronized int exitValue() {
if (!hasExited) { if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited"); throw new IllegalThreadStateException("process hasn't exited");
} }
return exitcode; return exitcode;
} }
private static native void destroyProcess(int pid); private static native void destroyProcess(int pid);
public synchronized void destroy() { public synchronized void destroy() {
// There is a risk that pid will be recycled, causing us to // There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes // kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check, // that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window // there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too // is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe. // soon, so this is quite safe.
if (!hasExited) if (!hasExited)
destroyProcess(pid); destroyProcess(pid);
try { try {
stdin_stream.close(); stdin_stream.close();
stdout_inner_stream.closeDeferred(stdout_stream); if (stdout_inner_stream != null)
stderr_stream.closeDeferred(stderr_stream); stdout_inner_stream.closeDeferred(stdout_stream);
if (stderr_stream instanceof DeferredCloseInputStream)
((DeferredCloseInputStream) stderr_stream)
.closeDeferred(stderr_stream);
} catch (IOException e) { } catch (IOException e) {
// ignore // ignore
} }
...@@ -172,99 +198,99 @@ final class UNIXProcess extends Process { ...@@ -172,99 +198,99 @@ final class UNIXProcess extends Process {
// (EOF) as they did before. // (EOF) as they did before.
// //
private static class DeferredCloseInputStream private static class DeferredCloseInputStream
extends FileInputStream extends FileInputStream
{ {
private DeferredCloseInputStream(FileDescriptor fd) { private DeferredCloseInputStream(FileDescriptor fd) {
super(fd); super(fd);
} }
private Object lock = new Object(); // For the following fields private Object lock = new Object(); // For the following fields
private boolean closePending = false; private boolean closePending = false;
private int useCount = 0; private int useCount = 0;
private InputStream streamToClose; private InputStream streamToClose;
private void raise() { private void raise() {
synchronized (lock) { synchronized (lock) {
useCount++; useCount++;
} }
} }
private void lower() throws IOException { private void lower() throws IOException {
synchronized (lock) { synchronized (lock) {
useCount--; useCount--;
if (useCount == 0 && closePending) { if (useCount == 0 && closePending) {
streamToClose.close(); streamToClose.close();
} }
} }
} }
// stc is the actual stream to be closed; it might be this object, or // 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. // it might be an upstream object for which this object is downstream.
// //
private void closeDeferred(InputStream stc) throws IOException { private void closeDeferred(InputStream stc) throws IOException {
synchronized (lock) { synchronized (lock) {
if (useCount == 0) { if (useCount == 0) {
stc.close(); stc.close();
} else { } else {
closePending = true; closePending = true;
streamToClose = stc; streamToClose = stc;
} }
} }
} }
public void close() throws IOException { public void close() throws IOException {
synchronized (lock) { synchronized (lock) {
useCount = 0; useCount = 0;
closePending = false; closePending = false;
} }
super.close(); super.close();
} }
public int read() throws IOException { public int read() throws IOException {
raise(); raise();
try { try {
return super.read(); return super.read();
} finally { } finally {
lower(); lower();
} }
} }
public int read(byte[] b) throws IOException { public int read(byte[] b) throws IOException {
raise(); raise();
try { try {
return super.read(b); return super.read(b);
} finally { } finally {
lower(); lower();
} }
} }
public int read(byte[] b, int off, int len) throws IOException { public int read(byte[] b, int off, int len) throws IOException {
raise(); raise();
try { try {
return super.read(b, off, len); return super.read(b, off, len);
} finally { } finally {
lower(); lower();
} }
} }
public long skip(long n) throws IOException { public long skip(long n) throws IOException {
raise(); raise();
try { try {
return super.skip(n); return super.skip(n);
} finally { } finally {
lower(); lower();
} }
} }
public int available() throws IOException { public int available() throws IOException {
raise(); raise();
try { try {
return super.available(); return super.available();
} finally { } finally {
lower(); lower();
} }
} }
} }
...@@ -272,6 +298,6 @@ final class UNIXProcess extends Process { ...@@ -272,6 +298,6 @@ final class UNIXProcess extends Process {
private static native void initIDs(); private static native void initIDs();
static { static {
initIDs(); initIDs();
} }
} }
...@@ -53,13 +53,10 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fdClass) { ...@@ -53,13 +53,10 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fdClass) {
*/ */
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) { Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC); 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_openAppend(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
} }
JNIEXPORT void JNICALL 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -479,6 +479,37 @@ closeSafely(int fd) ...@@ -479,6 +479,37 @@ closeSafely(int fd)
close(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__ #ifndef __solaris__
#undef fork1 #undef fork1
#define fork1() fork() #define fork1() fork()
...@@ -491,10 +522,8 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, ...@@ -491,10 +522,8 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
jbyteArray argBlock, jint argc, jbyteArray argBlock, jint argc,
jbyteArray envBlock, jint envc, jbyteArray envBlock, jint envc,
jbyteArray dir, jbyteArray dir,
jboolean redirectErrorStream, jintArray std_fds,
jobject stdin_fd, jboolean redirectErrorStream)
jobject stdout_fd,
jobject stderr_fd)
{ {
int errnum; int errnum;
int resultPid = -1; int resultPid = -1;
...@@ -505,6 +534,7 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, ...@@ -505,6 +534,7 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
const char *pargBlock = getBytes(env, argBlock); const char *pargBlock = getBytes(env, argBlock);
const char *penvBlock = getBytes(env, envBlock); const char *penvBlock = getBytes(env, envBlock);
const char *pdir = getBytes(env, dir); 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; 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, ...@@ -527,9 +557,13 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
initVectorFromBlock(envv, penvBlock, envc); initVectorFromBlock(envv, penvBlock, envc);
} }
if ((pipe(in) < 0) || assert(std_fds != NULL);
(pipe(out) < 0) || fds = (*env)->GetIntArrayElements(env, std_fds, NULL);
(pipe(err) < 0) || 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)) { (pipe(fail) < 0)) {
throwIOException(env, errno, "Bad file descriptor"); throwIOException(env, errno, "Bad file descriptor");
goto Catch; goto Catch;
...@@ -544,23 +578,26 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, ...@@ -544,23 +578,26 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
if (resultPid == 0) { if (resultPid == 0) {
/* Child process */ /* Child process */
/* Close the parent sides of the pipe. /* Close the parent sides of the pipes.
Give the child sides of the pipes the right fileno's.
Closing pipe fds here is redundant, since closeDescriptors() Closing pipe fds here is redundant, since closeDescriptors()
would do it anyways, but a little paranoia is a good thing. */ 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 */ /* Note: it is possible for in[0] == 0 */
close(in[1]); moveDescriptor(in[0] != -1 ? in[0] : fds[0], STDIN_FILENO);
moveDescriptor(in[0], STDIN_FILENO); moveDescriptor(out[1]!= -1 ? out[1] : fds[1], STDOUT_FILENO);
close(out[0]);
moveDescriptor(out[1], STDOUT_FILENO);
close(err[0]);
if (redirectErrorStream) { if (redirectErrorStream) {
close(err[1]); closeSafely(err[1]);
dup2(STDOUT_FILENO, STDERR_FILENO); dup2(STDOUT_FILENO, STDERR_FILENO);
} else { } else {
moveDescriptor(err[1], STDERR_FILENO); moveDescriptor(err[1] != -1 ? err[1] : fds[2], STDERR_FILENO);
} }
close(fail[0]);
moveDescriptor(fail[1], FAIL_FILENO); moveDescriptor(fail[1], FAIL_FILENO);
/* close everything */ /* close everything */
...@@ -600,15 +637,21 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, ...@@ -600,15 +637,21 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
/* parent process */ /* parent process */
close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec */ 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); waitpid(resultPid, NULL, 0);
throwIOException(env, errnum, "Exec failed"); throwIOException(env, errnum, "Exec failed");
goto Catch; goto Catch;
default:
throwIOException(env, errno, "Read failed");
goto Catch;
} }
(*env)->SetIntField(env, stdin_fd, IO_fd_fdID, in [1]); fds[0] = (in [1] != -1) ? in [1] : -1;
(*env)->SetIntField(env, stdout_fd, IO_fd_fdID, out[0]); fds[1] = (out[0] != -1) ? out[0] : -1;
(*env)->SetIntField(env, stderr_fd, IO_fd_fdID, err[0]); fds[2] = (err[0] != -1) ? err[0] : -1;
Finally: Finally:
/* Always clean up the child's side of the pipes */ /* Always clean up the child's side of the pipes */
...@@ -628,6 +671,9 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, ...@@ -628,6 +671,9 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
releaseBytes(env, envBlock, penvBlock); releaseBytes(env, envBlock, penvBlock);
releaseBytes(env, dir, pdir); releaseBytes(env, dir, pdir);
if (fds != NULL)
(*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);
return resultPid; return resultPid;
Catch: Catch:
......
...@@ -29,17 +29,14 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -29,17 +29,14 @@ import java.util.concurrent.atomic.AtomicInteger;
/** /**
* Instances of the file descriptor class serve as an opaque handle * Instances of the file descriptor class serve as an opaque handle
* to the underlying machine-specific structure representing an open * to the underlying machine-specific structure representing an
* file, an open socket, or another source or sink of bytes. The * open file, an open socket, or another source or sink of bytes.
* main practical use for a file descriptor is to create a * The main practical use for a file descriptor is to create a
* <code>FileInputStream</code> or <code>FileOutputStream</code> to * {@link FileInputStream} or {@link FileOutputStream} to contain it.
* contain it. *
* <p> * <p>Applications should not create their own file descriptors.
* Applications should not create their own file descriptors.
* *
* @author Pavani Diwanji * @author Pavani Diwanji
* @see java.io.FileInputStream
* @see java.io.FileOutputStream
* @since JDK1.0 * @since JDK1.0
*/ */
public final class FileDescriptor { public final class FileDescriptor {
...@@ -81,6 +78,14 @@ public final class FileDescriptor { ...@@ -81,6 +78,14 @@ public final class FileDescriptor {
public int get(FileDescriptor obj) { public int get(FileDescriptor obj) {
return obj.fd; 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 { ...@@ -88,7 +93,7 @@ public final class FileDescriptor {
/** /**
* A handle to the standard input stream. Usually, this file * A handle to the standard input stream. Usually, this file
* descriptor is not used directly, but rather via the input stream * 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 * @see java.lang.System#in
*/ */
...@@ -97,7 +102,7 @@ public final class FileDescriptor { ...@@ -97,7 +102,7 @@ public final class FileDescriptor {
/** /**
* A handle to the standard output stream. Usually, this file * A handle to the standard output stream. Usually, this file
* descriptor is not used directly, but rather via the output stream * 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 * @see java.lang.System#out
*/ */
public static final FileDescriptor out = standardStream(1); public static final FileDescriptor out = standardStream(1);
...@@ -105,7 +110,7 @@ public final class FileDescriptor { ...@@ -105,7 +110,7 @@ public final class FileDescriptor {
/** /**
* A handle to the standard error stream. Usually, this file * A handle to the standard error stream. Usually, this file
* descriptor is not used directly, but rather via the output stream * 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 * @see java.lang.System#err
*/ */
...@@ -114,9 +119,9 @@ public final class FileDescriptor { ...@@ -114,9 +119,9 @@ public final class FileDescriptor {
/** /**
* Tests if this file descriptor object is valid. * 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; * valid, open file, socket, or other active I/O connection;
* <code>false</code> otherwise. * {@code false} otherwise.
*/ */
public boolean valid() { public boolean valid() {
return ((handle != -1) || (fd != -1)); return ((handle != -1) || (fd != -1));
......
...@@ -25,7 +25,16 @@ ...@@ -25,7 +25,16 @@
package java.lang; 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 /* This class is for the exclusive use of ProcessBuilder.start() to
* create new processes. * create new processes.
...@@ -35,30 +44,82 @@ import java.io.*; ...@@ -35,30 +44,82 @@ import java.io.*;
*/ */
final class ProcessImpl extends Process { final class ProcessImpl extends Process {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
// System-dependent portion of ProcessBuilder.start() // System-dependent portion of ProcessBuilder.start()
static Process start(String cmdarray[], static Process start(String cmdarray[],
java.util.Map<String,String> environment, java.util.Map<String,String> environment,
String dir, String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream) boolean redirectErrorStream)
throws IOException throws IOException
{ {
String envblock = ProcessEnvironment.toEnvironmentBlock(environment); 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 long handle = 0;
private FileDescriptor stdin_fd;
private FileDescriptor stdout_fd;
private FileDescriptor stderr_fd;
private OutputStream stdin_stream; private OutputStream stdin_stream;
private InputStream stdout_stream; private InputStream stdout_stream;
private InputStream stderr_stream; private InputStream stderr_stream;
private ProcessImpl(String cmd[], private ProcessImpl(final String cmd[],
String envblock, final String envblock,
String path, final String path,
boolean redirectErrorStream) final long[] stdHandles,
final boolean redirectErrorStream)
throws IOException throws IOException
{ {
// Win32 CreateProcess requires cmd[0] to be normalized // Win32 CreateProcess requires cmd[0] to be normalized
...@@ -91,25 +152,39 @@ final class ProcessImpl extends Process { ...@@ -91,25 +152,39 @@ final class ProcessImpl extends Process {
} }
String cmdstr = cmdbuf.toString(); String cmdstr = cmdbuf.toString();
stdin_fd = new FileDescriptor(); handle = create(cmdstr, envblock, path,
stdout_fd = new FileDescriptor(); stdHandles, redirectErrorStream);
stderr_fd = new FileDescriptor();
handle = create(cmdstr, envblock, path, redirectErrorStream,
stdin_fd, stdout_fd, stderr_fd);
java.security.AccessController.doPrivileged( java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() { new java.security.PrivilegedAction<Void>() {
public Object run() { public Void run() {
stdin_stream = if (stdHandles[0] == -1L)
new BufferedOutputStream(new FileOutputStream(stdin_fd)); stdin_stream = new ProcessBuilder.NullOutputStream();
stdout_stream = else {
new BufferedInputStream(new FileInputStream(stdout_fd)); FileDescriptor stdin_fd = new FileDescriptor();
stderr_stream = fdAccess.setHandle(stdin_fd, stdHandles[0]);
new FileInputStream(stderr_fd); stdin_stream = new BufferedOutputStream(
return null; 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() { public OutputStream getOutputStream() {
...@@ -150,13 +225,30 @@ final class ProcessImpl extends Process { ...@@ -150,13 +225,30 @@ final class ProcessImpl extends Process {
public void destroy() { terminateProcess(handle); } public void destroy() { terminateProcess(handle); }
private static native void terminateProcess(long 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, private static native long create(String cmdstr,
String envblock, String envblock,
String dir, String dir,
boolean redirectErrorStream, long[] stdHandles,
FileDescriptor in_fd, boolean redirectErrorStream)
FileDescriptor out_fd,
FileDescriptor err_fd)
throws IOException; throws IOException;
private static native boolean closeHandle(long handle); private static native boolean closeHandle(long handle);
......
...@@ -39,8 +39,6 @@ ...@@ -39,8 +39,6 @@
jfieldID fos_fd; /* id for jobject 'fd' in java.io.FileOutputStream */ jfieldID fos_fd; /* id for jobject 'fd' in java.io.FileOutputStream */
jfieldID fos_append;
/************************************************************** /**************************************************************
* static methods to store field ID's in initializers * static methods to store field ID's in initializers
*/ */
...@@ -49,7 +47,6 @@ JNIEXPORT void JNICALL ...@@ -49,7 +47,6 @@ JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) { Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
fos_fd = fos_fd =
(*env)->GetFieldID(env, fosClass, "fd", "Ljava/io/FileDescriptor;"); (*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) { ...@@ -57,45 +54,20 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
*/ */
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) { Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC); 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_openAppend(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) { 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); writeSingle(env, this, byte, fos_fd);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env, Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) { 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); writeBytes(env, this, bytes, off, len, fos_fd);
} }
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
extern jboolean onNT = JNI_FALSE; extern jboolean onNT = JNI_FALSE;
static int MAX_INPUT_EVENTS = 2000; static DWORD MAX_INPUT_EVENTS = 2000;
void void
initializeWindowsVersion() { initializeWindowsVersion() {
...@@ -190,9 +190,16 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) { ...@@ -190,9 +190,16 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
jlong jlong
winFileHandleOpen(JNIEnv *env, jstring path, int flags) 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 = 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_WRONLY) ? GENERIC_WRITE :
(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :
GENERIC_READ; GENERIC_READ;
const DWORD sharing = const DWORD sharing =
FILE_SHARE_READ | FILE_SHARE_WRITE; FILE_SHARE_READ | FILE_SHARE_WRITE;
...@@ -444,24 +451,6 @@ handleSetLength(jlong fd, jlong length) { ...@@ -444,24 +451,6 @@ handleSetLength(jlong fd, jlong length) {
return 0; 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 JNIEXPORT
size_t size_t
handleRead(jlong fd, void *buf, jint len) handleRead(jlong fd, void *buf, jint len)
...@@ -513,7 +502,7 @@ handleClose(JNIEnv *env, jobject this, jfieldID fid) ...@@ -513,7 +502,7 @@ handleClose(JNIEnv *env, jobject this, jfieldID fid)
FD fd = GET_FD(this, fid); FD fd = GET_FD(this, fid);
HANDLE h = (HANDLE)fd; HANDLE h = (HANDLE)fd;
if (fd == INVALID_HANDLE_VALUE) { if (h == INVALID_HANDLE_VALUE) {
return 0; return 0;
} }
......
...@@ -38,7 +38,6 @@ void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags); ...@@ -38,7 +38,6 @@ void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags);
int handleAvailable(jlong fd, jlong *pbytes); int handleAvailable(jlong fd, jlong *pbytes);
JNIEXPORT int handleSync(jlong fd); JNIEXPORT int handleSync(jlong fd);
int handleSetLength(jlong fd, jlong length); 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 handleRead(jlong fd, void *buf, jint len);
JNIEXPORT size_t handleWrite(jlong fd, const void *buf, jint len); JNIEXPORT size_t handleWrite(jlong fd, const void *buf, jint len);
jint handleClose(JNIEnv *env, jobject this, jfieldID fid); jint handleClose(JNIEnv *env, jobject this, jfieldID fid);
......
...@@ -33,7 +33,12 @@ ...@@ -33,7 +33,12 @@
#include <windows.h> #include <windows.h>
#include <io.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 * char *
extractExecutablePath(JNIEnv *env, char *source) extractExecutablePath(JNIEnv *env, char *source)
...@@ -120,7 +125,7 @@ win32Error(JNIEnv *env, const char *functionName) ...@@ -120,7 +125,7 @@ win32Error(JNIEnv *env, const char *functionName)
static void static void
closeSafely(HANDLE handle) closeSafely(HANDLE handle)
{ {
if (handle) if (handle != INVALID_HANDLE_VALUE)
CloseHandle(handle); CloseHandle(handle);
} }
...@@ -129,23 +134,22 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, ...@@ -129,23 +134,22 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
jstring cmd, jstring cmd,
jstring envBlock, jstring envBlock,
jstring dir, jstring dir,
jboolean redirectErrorStream, jlongArray stdHandles,
jobject in_fd, jboolean redirectErrorStream)
jobject out_fd,
jobject err_fd)
{ {
HANDLE inRead = 0; HANDLE inRead = INVALID_HANDLE_VALUE;
HANDLE inWrite = 0; HANDLE inWrite = INVALID_HANDLE_VALUE;
HANDLE outRead = 0; HANDLE outRead = INVALID_HANDLE_VALUE;
HANDLE outWrite = 0; HANDLE outWrite = INVALID_HANDLE_VALUE;
HANDLE errRead = 0; HANDLE errRead = INVALID_HANDLE_VALUE;
HANDLE errWrite = 0; HANDLE errWrite = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
STARTUPINFO si; STARTUPINFO si;
LPTSTR pcmd = NULL; LPTSTR pcmd = NULL;
LPCTSTR pdir = NULL; LPCTSTR pdir = NULL;
LPVOID penvBlock = NULL; LPVOID penvBlock = NULL;
jlong *handles = NULL;
jlong ret = 0; jlong ret = 0;
OSVERSIONINFO ver; OSVERSIONINFO ver;
jboolean onNT = JNI_FALSE; jboolean onNT = JNI_FALSE;
...@@ -156,17 +160,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, ...@@ -156,17 +160,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
onNT = JNI_TRUE; 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); assert(cmd != NULL);
pcmd = (LPTSTR) JNU_GetStringPlatformChars(env, cmd, NULL); pcmd = (LPTSTR) JNU_GetStringPlatformChars(env, cmd, NULL);
if (pcmd == NULL) goto Catch; if (pcmd == NULL) goto Catch;
...@@ -184,19 +177,62 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, ...@@ -184,19 +177,62 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
if (penvBlock == NULL) goto Catch; if (penvBlock == NULL) goto Catch;
} }
assert(stdHandles != NULL);
handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);
if (handles == NULL) goto Catch;
memset(&si, 0, sizeof(si)); memset(&si, 0, sizeof(si));
si.cb = sizeof(si); si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES; si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = inRead;
si.hStdOutput = outWrite;
si.hStdError = redirectErrorStream ? outWrite : errWrite;
SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE); sa.nLength = sizeof(sa);
SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE); sa.lpSecurityDescriptor = 0;
SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE); sa.bInheritHandle = TRUE;
if (redirectErrorStream) if (handles[0] != (jlong) -1) {
SetHandleInformation(errWrite, HANDLE_FLAG_INHERIT, FALSE); 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) if (onNT)
processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
...@@ -232,9 +268,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, ...@@ -232,9 +268,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
ret = (jlong)pi.hProcess; 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: Finally:
/* Always clean up the child's side of the pipes */ /* Always clean up the child's side of the pipes */
...@@ -252,6 +285,9 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored, ...@@ -252,6 +285,9 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
else else
JNU_ReleaseStringPlatformChars(env, dir, (char *) penvBlock); JNU_ReleaseStringPlatformChars(env, dir, (char *) penvBlock);
} }
if (handles != NULL)
(*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
return ret; return ret;
Catch: Catch:
......
...@@ -673,13 +673,13 @@ GetJavaProperties(JNIEnv* env) ...@@ -673,13 +673,13 @@ GetJavaProperties(JNIEnv* env)
/* OS properties */ /* OS properties */
{ {
char buf[100]; char buf[100];
OSVERSIONINFO ver; OSVERSIONINFOEX ver;
ver.dwOSVersionInfoSize = sizeof(ver); ver.dwOSVersionInfoSize = sizeof(ver);
GetVersionEx(&ver); GetVersionEx((OSVERSIONINFO *) &ver);
/* /*
* From msdn page on OSVERSIONINFOEX, current as of this * 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 * Operating system dwMajorVersion dwMinorVersion
* ================== ============== ============== * ================== ============== ==============
...@@ -692,7 +692,7 @@ GetJavaProperties(JNIEnv* env) ...@@ -692,7 +692,7 @@ GetJavaProperties(JNIEnv* env)
* Windows 2000 5 0 * Windows 2000 5 0
* Windows XP 5 1 * Windows XP 5 1
* Windows Server 2003 family 5 2 * Windows Server 2003 family 5 2
* Windows Vista 6 0 * Windows Vista family 6 0
* *
* This mapping will presumably be augmented as new Windows * This mapping will presumably be augmented as new Windows
* versions are released. * versions are released.
...@@ -724,7 +724,20 @@ GetJavaProperties(JNIEnv* env) ...@@ -724,7 +724,20 @@ GetJavaProperties(JNIEnv* env)
default: sprops.os_name = "Windows NT (unknown)"; break; default: sprops.os_name = "Windows NT (unknown)"; break;
} }
} else if (ver.dwMajorVersion == 6) { } 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 { } else {
sprops.os_name = "Windows NT (unknown)"; 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 @@ ...@@ -25,12 +25,15 @@
* @test * @test
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689 * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 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 * @summary Basic tests for Process and Environment Variable code
* @run main/othervm Basic * @run main/othervm Basic
* @author Martin Buchholz * @author Martin Buchholz
*/ */
import java.lang.ProcessBuilder.Redirect;
import static java.lang.ProcessBuilder.Redirect.*;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.security.*; import java.security.*;
...@@ -257,7 +260,29 @@ public class Basic { ...@@ -257,7 +260,29 @@ public class Basic {
public static class JavaChild { public static class JavaChild {
public static void main(String args[]) throws Throwable { public static void main(String args[]) throws Throwable {
String action = args[0]; 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]); String val = System.getenv(args[1]);
printUTF8(val == null ? "null" : val); printUTF8(val == null ? "null" : val);
} else if (action.equals("System.getenv(\\u1234)")) { } else if (action.equals("System.getenv(\\u1234)")) {
...@@ -599,6 +624,333 @@ public class Basic { ...@@ -599,6 +624,333 @@ public class Basic {
} catch (Throwable t) { unexpected(t); } } 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 { private static void realMain(String[] args) throws Throwable {
if (Windows.is()) if (Windows.is())
System.out.println("This appears to be a Windows system."); System.out.println("This appears to be a Windows system.");
...@@ -607,6 +959,9 @@ public class Basic { ...@@ -607,6 +959,9 @@ public class Basic {
if (UnicodeOS.is()) if (UnicodeOS.is())
System.out.println("This appears to be a Unicode-based OS."); 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 // Basic tests for setting, replacing and deleting envvars
//---------------------------------------------------------------- //----------------------------------------------------------------
...@@ -1354,7 +1709,8 @@ public class Basic { ...@@ -1354,7 +1709,8 @@ public class Basic {
execPermission); execPermission);
ProcessBuilder pb = new ProcessBuilder("env"); ProcessBuilder pb = new ProcessBuilder("env");
pb.environment().put("foo","bar"); pb.environment().put("foo","bar");
pb.start(); Process p = pb.start();
closeStreams(p);
} catch (IOException e) { // OK } catch (IOException e) { // OK
} catch (Throwable t) { unexpected(t); } } catch (Throwable t) { unexpected(t); }
...@@ -1378,6 +1734,14 @@ public class Basic { ...@@ -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. // A Policy class designed to make permissions fiddling very easy.
//---------------------------------------------------------------- //----------------------------------------------------------------
...@@ -1432,10 +1796,19 @@ public class Basic { ...@@ -1432,10 +1796,19 @@ public class Basic {
} }
} catch (Throwable t) { } catch (Throwable t) {
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) { private static ProcessResults run(Process p) {
Throwable throwable = null; Throwable throwable = null;
int exitValue = -1; int exitValue = -1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册