Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_jdk
提交
095ce48d
D
dragonwell8_jdk
项目概览
openanolis
/
dragonwell8_jdk
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_jdk
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
095ce48d
编写于
3月 10, 2008
作者:
M
martin
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
4960438: (process) Need IO redirection API for subprocesses
Reviewed-by: alanb, iris
上级
6d73e05f
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
1680 addition
and
476 deletion
+1680
-476
src/share/classes/java/lang/Process.java
src/share/classes/java/lang/Process.java
+50
-21
src/share/classes/java/lang/ProcessBuilder.java
src/share/classes/java/lang/ProcessBuilder.java
+635
-91
src/share/classes/sun/misc/JavaIOFileDescriptorAccess.java
src/share/classes/sun/misc/JavaIOFileDescriptorAccess.java
+4
-0
src/solaris/classes/java/io/FileDescriptor.java
src/solaris/classes/java/io/FileDescriptor.java
+9
-1
src/solaris/classes/java/lang/ProcessImpl.java
src/solaris/classes/java/lang/ProcessImpl.java
+58
-1
src/solaris/classes/java/lang/UNIXProcess.java.linux
src/solaris/classes/java/lang/UNIXProcess.java.linux
+105
-79
src/solaris/classes/java/lang/UNIXProcess.java.solaris
src/solaris/classes/java/lang/UNIXProcess.java.solaris
+209
-183
src/solaris/native/java/lang/UNIXProcess_md.c
src/solaris/native/java/lang/UNIXProcess_md.c
+30
-21
src/windows/classes/java/io/FileDescriptor.java
src/windows/classes/java/io/FileDescriptor.java
+19
-14
src/windows/classes/java/lang/ProcessImpl.java
src/windows/classes/java/lang/ProcessImpl.java
+121
-29
src/windows/native/java/lang/ProcessImpl_md.c
src/windows/native/java/lang/ProcessImpl_md.c
+64
-33
test/java/lang/ProcessBuilder/Basic.java
test/java/lang/ProcessBuilder/Basic.java
+376
-3
未找到文件。
src/share/classes/java/lang/Process.java
浏览文件 @
095ce48d
...
...
@@ -41,18 +41,24 @@ import java.io.*;
* <p>The methods that create processes may not work well for special
* processes on certain native platforms, such as native windowing
* processes, daemon processes, Win16/DOS processes on Microsoft
* Windows, or shell scripts. The created subprocess does not have
* its own terminal or console. All its standard I/O (i.e. stdin,
* stdout, stderr) operations will be redirected to the parent process
* through three streams
* ({@link #getOutputStream()},
* {@link #getInputStream()},
* {@link #getErrorStream()}).
* Windows, or shell scripts.
*
* <p>By default, the created subprocess does not have its own terminal
* or console. All its standard I/O (i.e. stdin, stdout, stderr)
* operations will be redirected to the parent process, where they can
* be accessed via the streams obtained using the methods
* {@link #getOutputStream()},
* {@link #getInputStream()}, and
* {@link #getErrorStream()}.
* The parent process uses these streams to feed input to and get output
* from the subprocess. Because some native platforms only provide
* limited buffer size for standard input and output streams, failure
* to promptly write the input stream or read the output stream of
* the subprocess may cause the subprocess to block, and even deadlock.
* the subprocess may cause the subprocess to block, or even deadlock.
*
* <p>Where desired, <a href="ProcessBuilder.html#redirect-input">
* subprocess I/O can also be redirected</a>
* using methods of the {@link ProcessBuilder} class.
*
* <p>The subprocess is not killed when there are no more references to
* the {@code Process} object, but rather the subprocess
...
...
@@ -62,16 +68,22 @@ import java.io.*;
* Process} object execute asynchronously or concurrently with respect
* to the Java process that owns the {@code Process} object.
*
* @author unascribed
* @see ProcessBuilder
* <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way
* to create a {@code Process}.
*
* @since JDK1.0
*/
public
abstract
class
Process
{
/**
* Returns the output stream connected to the normal input of the
* subprocess. Output to the stream is piped into the standard
* input stream of the process represented by this {@code Process}
* object.
* input of the process represented by this {@code Process} object.
*
* <p>If the standard input of the subprocess has been redirected using
* {@link ProcessBuilder#redirectInput(Redirect)
* ProcessBuilder.redirectInput}
* then this method will return a
* <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
*
* <p>Implementation note: It is a good idea for the returned
* output stream to be buffered.
...
...
@@ -84,30 +96,47 @@ public abstract class Process {
/**
* Returns the input stream connected to the normal output of the
* subprocess. The stream obtains data piped from the standard
* output stream of the process represented by this {@code
* Process} object.
* output of the process represented by this {@code Process} object.
*
* <p>If the standard output of the subprocess has been redirected using
* {@link ProcessBuilder#redirectOutput(Redirect)
* ProcessBuilder.redirectOutput}
* then this method will return a
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
*
* <p>Otherwise, if the standard error of the subprocess has been
* redirected using
* {@link ProcessBuilder#redirectErrorStream(boolean)
* ProcessBuilder.redirectErrorStream}
* then the input stream returned by this method will receive the
* merged standard output and the standard error of the subprocess.
*
* <p>Implementation note: It is a good idea for the returned
* input stream to be buffered.
*
* @return the input stream connected to the normal output of the
* subprocess
* @see ProcessBuilder#redirectErrorStream()
*/
abstract
public
InputStream
getInputStream
();
/**
* Returns the input stream connected to the error output stream of
* the subprocess. The stream obtains data piped from the error
* output stream of the process represented by this {@code Process}
* object.
* Returns the input stream connected to the error output of the
* subprocess. The stream obtains data piped from the error output
* of the process represented by this {@code Process} object.
*
* <p>If the standard error of the subprocess has been redirected using
* {@link ProcessBuilder#redirectError(Redirect)
* ProcessBuilder.redirectError} or
* {@link ProcessBuilder#redirectErrorStream(boolean)
* ProcessBuilder.redirectErrorStream}
* then this method will return a
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
*
* <p>Implementation note: It is a good idea for the returned
* input stream to be buffered.
*
* @return the input stream connected to the error output
stream
of
* @return the input stream connected to the error output of
* the subprocess
* @see ProcessBuilder#redirectErrorStream()
*/
abstract
public
InputStream
getErrorStream
();
...
...
src/share/classes/java/lang/ProcessBuilder.java
浏览文件 @
095ce48d
/*
* Copyright 2003-200
6
Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2003-200
8
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
...
...
@@ -27,6 +27,10 @@ package java.lang;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.io.FileOutputStream
;
import
java.util.Arrays
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
...
...
@@ -34,7 +38,7 @@ import java.util.Map;
/**
* This class is used to create operating system processes.
*
* <p>Each
<code>ProcessBuilder</code>
instance manages a collection
* <p>Each
{@code ProcessBuilder}
instance manages a collection
* of process attributes. The {@link #start()} method creates a new
* {@link Process} instance with those attributes. The {@link
* #start()} method can be invoked repeatedly from the same instance
...
...
@@ -59,19 +63,64 @@ import java.util.Map;
*
* <li>a <i>working directory</i>. The default value is the current
* working directory of the current process, usually the directory
* named by the system property <code>user.dir</code>.
* named by the system property {@code user.dir}.
*
* <li><a name="redirect-input">a source of <i>standard input</i>.
* By default, the subprocess reads input from a pipe. Java code
* can access this pipe via the output stream returned by
* {@link Process#getOutputStream()}. However, standard input may
* be redirected to another source using
* {@link #redirectInput(Redirect) redirectInput}.
* In this case, {@link Process#getOutputStream()} will return a
* <i>null output stream</i>, for which:
*
* <ul>
* <li>the {@link OutputStream#write(int) write} methods always
* throw {@code IOException}
* <li>the {@link OutputStream#close() close} method does nothing
* </ul>
*
* <li><a name="redirect-output">a destination for <i>standard output</i>
* and <i>standard error</i>. By default, the subprocess writes standard
* output and standard error to pipes. Java code can access these pipes
* via the input streams returned by {@link Process#getInputStream()} and
* {@link Process#getErrorStream()}. However, standard output and
* standard error may be redirected to other destinations using
* {@link #redirectOutput(Redirect) redirectOutput} and
* {@link #redirectError(Redirect) redirectError}.
* In this case, {@link Process#getInputStream()} and/or
* {@link Process#getErrorStream()} will return a <i>null input
* stream</i>, for which:
*
* <ul>
* <li>the {@link InputStream#read() read} methods always return
* {@code -1}
* <li>the {@link InputStream#available() available} method always returns
* {@code 0}
* <li>the {@link InputStream#close() close} method does nothing
* </ul>
*
* <li>a <i>redirectErrorStream</i> property. Initially, this property
* is
<code>false</code>
, meaning that the standard output and error
* is
{@code false}
, meaning that the standard output and error
* output of a subprocess are sent to two separate streams, which can
* be accessed using the {@link Process#getInputStream()} and {@link
* Process#getErrorStream()} methods. If the value is set to
* <code>true</code>, the standard error is merged with the standard
* output. This makes it easier to correlate error messages with the
* corresponding output. In this case, the merged data can be read
* from the stream returned by {@link Process#getInputStream()}, while
* reading from the stream returned by {@link
* Process#getErrorStream()} will get an immediate end of file.
* Process#getErrorStream()} methods.
*
* <p>If the value is set to {@code true}, then:
*
* <ul>
* <li>standard error is merged with the standard output and always sent
* to the same destination (this makes it easier to correlate error
* messages with the corresponding output)
* <li>the common destination of standard error and standard output can be
* redirected using
* {@link #redirectOutput(Redirect) redirectOutput}
* <li>any redirection set by the
* {@link #redirectError(Redirect) redirectError}
* method is ignored when creating a subprocess
* <li>the stream returned from {@link Process#getErrorStream()} will
* always be a <a href="#redirect-output">null input stream</a>
* </ul>
*
* </ul>
*
...
...
@@ -87,34 +136,43 @@ import java.util.Map;
* is invoked.
*
* <p><strong>Note that this class is not synchronized.</strong>
* If multiple threads access a
<code>ProcessBuilder</code>
instance
* If multiple threads access a
{@code ProcessBuilder}
instance
* concurrently, and at least one of the threads modifies one of the
* attributes structurally, it <i>must</i> be synchronized externally.
*
* <p>Starting a new process which uses the default working directory
* and environment is easy:
*
* <
blockquote><pre>
* <
pre> {@code
* Process p = new ProcessBuilder("myCommand", "myArg").start();
*
</pre></blockquot
e>
*
}</pr
e>
*
* <p>Here is an example that starts a process with a modified working
* directory and environment:
* directory and environment, and redirects standard output and error
* to be appended to a log file:
*
* <blockquote><pre>
* ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
* Map<String, String> env = pb.environment();
* <pre> {@code
* ProcessBuilder pb =
* new ProcessBuilder("myCommand", "myArg1", "myArg2");
* Map<String, String> env = pb.environment();
* env.put("VAR1", "myValue");
* env.remove("OTHERVAR");
* env.put("VAR2", env.get("VAR1") + "suffix");
* pb.directory(new File("myDir"));
* File log = new File("log");
* pb.redirectErrorStream(true);
* pb.redirectOutput(Redirect.appendTo(log));
* Process p = pb.start();
* </pre></blockquote>
* assert pb.redirectInput() == Redirect.PIPE;
* assert pb.redirectOutput().file() == log;
* assert p.getInputStream().read() == -1;
* }</pre>
*
* <p>To start a process with an explicit set of environment
* variables, first call {@link java.util.Map#clear() Map.clear()}
* before adding environment variables.
*
* @author Martin Buchholz
* @since 1.5
*/
...
...
@@ -124,20 +182,19 @@ public final class ProcessBuilder
private
File
directory
;
private
Map
<
String
,
String
>
environment
;
private
boolean
redirectErrorStream
;
private
Redirect
[]
redirects
;
/**
* Constructs a process builder with the specified operating
* system program and arguments. This constructor does <i>not</i>
* make a copy of the
<code>command</code>
list. Subsequent
* make a copy of the
{@code command}
list. Subsequent
* updates to the list will be reflected in the state of the
* process builder. It is not checked whether
* <code>command</code> corresponds to a valid operating system
* command.</p>
*
* @param command The list containing the program and its arguments
* {@code command} corresponds to a valid operating system
* command.
*
* @
throws NullPointerException
*
If the argument is <code>null</code>
* @
param command the list containing the program and its arguments
*
@throws NullPointerException if the argument is null
*/
public
ProcessBuilder
(
List
<
String
>
command
)
{
if
(
command
==
null
)
...
...
@@ -149,12 +206,12 @@ public final class ProcessBuilder
* Constructs a process builder with the specified operating
* system program and arguments. This is a convenience
* constructor that sets the process builder's command to a string
* list containing the same strings as the
<code>command</code>
* list containing the same strings as the
{@code command}
* array, in the same order. It is not checked whether
*
<code>command</code>
corresponds to a valid operating system
* command.
</p>
*
{@code command}
corresponds to a valid operating system
* command.
*
* @param
command A
string array containing the program and its arguments
* @param
command a
string array containing the program and its arguments
*/
public
ProcessBuilder
(
String
...
command
)
{
this
.
command
=
new
ArrayList
<
String
>(
command
.
length
);
...
...
@@ -165,16 +222,15 @@ public final class ProcessBuilder
/**
* Sets this process builder's operating system program and
* arguments. This method does <i>not</i> make a copy of the
*
<code>command</code>
list. Subsequent updates to the list will
*
{@code command}
list. Subsequent updates to the list will
* be reflected in the state of the process builder. It is not
* checked whether
<code>command</code>
corresponds to a valid
* operating system command.
</p>
* checked whether
{@code command}
corresponds to a valid
* operating system command.
*
* @param
command T
he list containing the program and its arguments
* @return
T
his process builder
* @param
command t
he list containing the program and its arguments
* @return
t
his process builder
*
* @throws NullPointerException
* If the argument is <code>null</code>
* @throws NullPointerException if the argument is null
*/
public
ProcessBuilder
command
(
List
<
String
>
command
)
{
if
(
command
==
null
)
...
...
@@ -187,12 +243,12 @@ public final class ProcessBuilder
* Sets this process builder's operating system program and
* arguments. This is a convenience method that sets the command
* to a string list containing the same strings as the
*
<code>command</code>
array, in the same order. It is not
* checked whether
<code>command</code>
corresponds to a valid
* operating system command.
</p>
*
{@code command}
array, in the same order. It is not
* checked whether
{@code command}
corresponds to a valid
* operating system command.
*
* @param
command A
string array containing the program and its arguments
* @return
T
his process builder
* @param
command a
string array containing the program and its arguments
* @return
t
his process builder
*/
public
ProcessBuilder
command
(
String
...
command
)
{
this
.
command
=
new
ArrayList
<
String
>(
command
.
length
);
...
...
@@ -205,9 +261,9 @@ public final class ProcessBuilder
* Returns this process builder's operating system program and
* arguments. The returned list is <i>not</i> a copy. Subsequent
* updates to the list will be reflected in the state of this
* process builder.
</p>
* process builder.
*
* @return
T
his process builder's program and its arguments
* @return
t
his process builder's program and its arguments
*/
public
List
<
String
>
command
()
{
return
command
;
...
...
@@ -225,10 +281,10 @@ public final class ProcessBuilder
* <p>The returned object may be modified using ordinary {@link
* java.util.Map Map} operations. These modifications will be
* visible to subprocesses started via the {@link #start()}
* method. Two
<code>ProcessBuilder</code>
instances always
* method. Two
{@code ProcessBuilder}
instances always
* contain independent process environments, so changes to the
* returned map will never be reflected in any other
*
<code>ProcessBuilder</code>
instance or the values returned by
*
{@code ProcessBuilder}
instance or the values returned by
* {@link System#getenv System.getenv}.
*
* <p>If the system does not support environment variables, an
...
...
@@ -262,25 +318,24 @@ public final class ProcessBuilder
* <p>The returned map is typically case-sensitive on all platforms.
*
* <p>If a security manager exists, its
* {@link SecurityManager#checkPermission checkPermission}
* method is called with a
* <code>{@link RuntimePermission}("getenv.*")</code>
* permission. This may result in a {@link SecurityException} being
* thrown.
* {@link SecurityManager#checkPermission checkPermission} method
* is called with a
* {@link RuntimePermission}{@code ("getenv.*")} permission.
* This may result in a {@link SecurityException} being thrown.
*
* <p>When passing information to a Java subprocess,
* <a href=System.html#EnvironmentVSSystemProperties>system properties</a>
* are generally preferred over environment variables.
</p>
* are generally preferred over environment variables.
*
* @return
T
his process builder's environment
* @return
t
his process builder's environment
*
* @throws
SecurityException
*
I
f a security manager exists and its
*
{@link SecurityManager#checkPermission checkPermission}
*
method doesn't allow access to the process environment
* @throws SecurityException
*
i
f a security manager exists and its
* {@link SecurityManager#checkPermission checkPermission}
* method doesn't allow access to the process environment
*
* @see
Runtime#exec(String[],String[],java.io.File)
* @see
System#getenv()
* @see Runtime#exec(String[],String[],java.io.File)
* @see System#getenv()
*/
public
Map
<
String
,
String
>
environment
()
{
SecurityManager
security
=
System
.
getSecurityManager
();
...
...
@@ -328,12 +383,12 @@ public final class ProcessBuilder
*
* Subprocesses subsequently started by this object's {@link
* #start()} method will use this as their working directory.
* The returned value may be
<code>null</code>
-- this means to use
* The returned value may be
{@code null}
-- this means to use
* the working directory of the current Java process, usually the
* directory named by the system property
<code>user.dir</code>
,
* as the working directory of the child process.
</p>
* directory named by the system property
{@code user.dir}
,
* as the working directory of the child process.
*
* @return
T
his process builder's working directory
* @return
t
his process builder's working directory
*/
public
File
directory
()
{
return
directory
;
...
...
@@ -344,50 +399,522 @@ public final class ProcessBuilder
*
* Subprocesses subsequently started by this object's {@link
* #start()} method will use this as their working directory.
* The argument may be
<code>null</code>
-- this means to use the
* The argument may be
{@code null}
-- this means to use the
* working directory of the current Java process, usually the
* directory named by the system property
<code>user.dir</code>
,
* as the working directory of the child process.
</p>
* directory named by the system property
{@code user.dir}
,
* as the working directory of the child process.
*
* @param
directory T
he new working directory
* @return
T
his process builder
* @param
directory t
he new working directory
* @return
t
his process builder
*/
public
ProcessBuilder
directory
(
File
directory
)
{
this
.
directory
=
directory
;
return
this
;
}
// ---------------- I/O Redirection ----------------
/**
* Implements a <a href="#redirect-output">null input stream</a>.
*/
static
class
NullInputStream
extends
InputStream
{
public
int
read
()
{
return
-
1
;
}
public
int
available
()
{
return
0
;
}
}
/**
* Implements a <a href="#redirect-input">null output stream</a>.
*/
static
class
NullOutputStream
extends
OutputStream
{
public
void
write
(
int
b
)
throws
IOException
{
throw
new
IOException
(
"Stream closed"
);
}
}
/**
* Represents a source of subprocess input or a destination of
* subprocess output.
*
* Each {@code Redirect} instance is one of the following:
*
* <ul>
* <li>the special value {@link #PIPE Redirect.PIPE}
* <li>the special value {@link #INHERIT Redirect.INHERIT}
* <li>a redirection to read from a file, created by an invocation of
* {@link Redirect#from Redirect.from(File)}
* <li>a redirection to write to a file, created by an invocation of
* {@link Redirect#to Redirect.to(File)}
* <li>a redirection to append to a file, created by an invocation of
* {@link Redirect#appendTo Redirect.appendTo(File)}
* </ul>
*
* <p>Each of the above categories has an associated unique
* {@link Type Type}.
*
* @since 1.7
*/
public
static
abstract
class
Redirect
{
/**
* The type of a {@link Redirect}.
*/
public
enum
Type
{
/**
* The type of {@link Redirect#PIPE Redirect.PIPE}.
*/
PIPE
,
/**
* The type of {@link Redirect#INHERIT Redirect.INHERIT}.
*/
INHERIT
,
/**
* The type of redirects returned from
* {@link Redirect#from Redirect.from(File)}.
*/
READ
,
/**
* The type of redirects returned from
* {@link Redirect#to Redirect.to(File)}.
*/
WRITE
,
/**
* The type of redirects returned from
* {@link Redirect#appendTo Redirect.appendTo(File)}.
*/
APPEND
};
/**
* Returns the type of this {@code Redirect}.
* @return the type of this {@code Redirect}
*/
public
abstract
Type
type
();
/**
* Indicates that subprocess I/O will be connected to the
* current Java process over a pipe.
*
* This is the default handling of subprocess standard I/O.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.PIPE.file() == null &&
* Redirect.PIPE.type() == Redirect.Type.PIPE
* }</pre>
*/
public
static
final
Redirect
PIPE
=
new
Redirect
()
{
public
Type
type
()
{
return
Type
.
PIPE
;
}
public
String
toString
()
{
return
type
().
toString
();
}};
/**
* Indicates that subprocess I/O source or destination will be the
* same as those of the current process. This is the normal
* behavior of most operating system command interpreters (shells).
*
* <p>It will always be true that
* <pre> {@code
* Redirect.INHERIT.file() == null &&
* Redirect.INHERIT.type() == Redirect.Type.INHERIT
* }</pre>
*/
public
static
final
Redirect
INHERIT
=
new
Redirect
()
{
public
Type
type
()
{
return
Type
.
INHERIT
;
}
public
String
toString
()
{
return
type
().
toString
();
}};
/**
* Returns the {@link File} source or destination associated
* with this redirect, or {@code null} if there is no such file.
*
* @return the file associated with this redirect,
* or {@code null} if there is no such file
*/
public
File
file
()
{
return
null
;
}
FileOutputStream
toFileOutputStream
()
throws
IOException
{
throw
new
UnsupportedOperationException
();
}
/**
* Returns a redirect to read from the specified file.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.from(file).file() == file &&
* Redirect.from(file).type() == Redirect.Type.READ
* }</pre>
*
* @throws NullPointerException if the specified file is null
* @return a redirect to read from the specified file
*/
public
static
Redirect
from
(
final
File
file
)
{
if
(
file
==
null
)
throw
new
NullPointerException
();
return
new
Redirect
()
{
public
Type
type
()
{
return
Type
.
READ
;
}
public
File
file
()
{
return
file
;
}
public
String
toString
()
{
return
"redirect to read from file \""
+
file
+
"\""
;
}
};
}
/**
* Returns a redirect to write to the specified file.
* If the specified file exists when the subprocess is started,
* its previous contents will be discarded.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.to(file).file() == file &&
* Redirect.to(file).type() == Redirect.Type.WRITE
* }</pre>
*
* @throws NullPointerException if the specified file is null
* @return a redirect to write to the specified file
*/
public
static
Redirect
to
(
final
File
file
)
{
if
(
file
==
null
)
throw
new
NullPointerException
();
return
new
Redirect
()
{
public
Type
type
()
{
return
Type
.
WRITE
;
}
public
File
file
()
{
return
file
;
}
public
String
toString
()
{
return
"redirect to write to file \""
+
file
+
"\""
;
}
FileOutputStream
toFileOutputStream
()
throws
IOException
{
return
new
FileOutputStream
(
file
,
false
);
}
};
}
/**
* Returns a redirect to append to the specified file.
* Each write operation first advances the position to the
* end of the file and then writes the requested data.
* Whether the advancement of the position and the writing
* of the data are done in a single atomic operation is
* system-dependent and therefore unspecified.
*
* <p>It will always be true that
* <pre> {@code
* Redirect.appendTo(file).file() == file &&
* Redirect.appendTo(file).type() == Redirect.Type.APPEND
* }</pre>
*
* @throws NullPointerException if the specified file is null
* @return a redirect to append to the specified file
*/
public
static
Redirect
appendTo
(
final
File
file
)
{
if
(
file
==
null
)
throw
new
NullPointerException
();
return
new
Redirect
()
{
public
Type
type
()
{
return
Type
.
APPEND
;
}
public
File
file
()
{
return
file
;
}
public
String
toString
()
{
return
"redirect to append to file \""
+
file
+
"\""
;
}
FileOutputStream
toFileOutputStream
()
throws
IOException
{
return
new
FileOutputStream
(
file
,
true
);
}
};
}
/**
* Compares the specified object with this {@code Redirect} for
* equality. Returns {@code true} if and only if the two
* objects are identical or both objects are {@code Redirect}
* instances of the same type associated with non-null equal
* {@code File} instances.
*/
public
boolean
equals
(
Object
obj
)
{
if
(
obj
==
this
)
return
true
;
if
(!
(
obj
instanceof
Redirect
))
return
false
;
Redirect
r
=
(
Redirect
)
obj
;
if
(
r
.
type
()
!=
this
.
type
())
return
false
;
assert
this
.
file
()
!=
null
;
return
this
.
file
().
equals
(
r
.
file
());
}
/**
* Returns a hash code value for this {@code Redirect}.
* @return a hash code value for this {@code Redirect}
*/
public
int
hashCode
()
{
File
file
=
file
();
if
(
file
==
null
)
return
super
.
hashCode
();
else
return
file
.
hashCode
();
}
/**
* No public constructors. Clients must use predefined
* static {@code Redirect} instances or factory methods.
*/
private
Redirect
()
{}
}
private
Redirect
[]
redirects
()
{
if
(
redirects
==
null
)
redirects
=
new
Redirect
[]
{
Redirect
.
PIPE
,
Redirect
.
PIPE
,
Redirect
.
PIPE
};
return
redirects
;
}
/**
* Sets this process builder's standard input source.
*
* Subprocesses subsequently started by this object's {@link #start()}
* method obtain their standard input from this source.
*
* <p>If the source is {@link Redirect#PIPE Redirect.PIPE}
* (the initial value), then the standard input of a
* subprocess can be written to using the output stream
* returned by {@link Process#getOutputStream()}.
* If the source is set to any other value, then
* {@link Process#getOutputStream()} will return a
* <a href="#redirect-input">null output stream</a>.
*
* @param source the new standard input source
* @return this process builder
* @throws IllegalArgumentException
* if the redirect does not correspond to a valid source
* of data, that is, has type
* {@link Redirect.Type#WRITE WRITE} or
* {@link Redirect.Type#APPEND APPEND}
* @since 1.7
*/
public
ProcessBuilder
redirectInput
(
Redirect
source
)
{
if
(
source
.
type
()
==
Redirect
.
Type
.
WRITE
||
source
.
type
()
==
Redirect
.
Type
.
APPEND
)
throw
new
IllegalArgumentException
(
"Redirect invalid for reading: "
+
source
);
redirects
()[
0
]
=
source
;
return
this
;
}
/**
* Sets this process builder's standard output destination.
*
* Subprocesses subsequently started by this object's {@link #start()}
* method send their standard output to this destination.
*
* <p>If the destination is {@link Redirect#PIPE Redirect.PIPE}
* (the initial value), then the standard output of a subprocess
* can be read using the input stream returned by {@link
* Process#getInputStream()}.
* If the destination is set to any other value, then
* {@link Process#getInputStream()} will return a
* <a href="#redirect-output">null input stream</a>.
*
* @param destination the new standard output destination
* @return this process builder
* @throws IllegalArgumentException
* if the redirect does not correspond to a valid
* destination of data, that is, has type
* {@link Redirect.Type#READ READ}
* @since 1.7
*/
public
ProcessBuilder
redirectOutput
(
Redirect
destination
)
{
if
(
destination
.
type
()
==
Redirect
.
Type
.
READ
)
throw
new
IllegalArgumentException
(
"Redirect invalid for writing: "
+
destination
);
redirects
()[
1
]
=
destination
;
return
this
;
}
/**
* Sets this process builder's standard error destination.
*
* Subprocesses subsequently started by this object's {@link #start()}
* method send their standard error to this destination.
*
* <p>If the destination is {@link Redirect#PIPE Redirect.PIPE}
* (the initial value), then the error output of a subprocess
* can be read using the input stream returned by {@link
* Process#getErrorStream()}.
* If the destination is set to any other value, then
* {@link Process#getErrorStream()} will return a
* <a href="#redirect-output">null input stream</a>.
*
* <p>If the {@link #redirectErrorStream redirectErrorStream}
* attribute has been set {@code true}, then the redirection set
* by this method has no effect.
*
* @param destination the new standard error destination
* @return this process builder
* @throws IllegalArgumentException
* if the redirect does not correspond to a valid
* destination of data, that is, has type
* {@link Redirect.Type#READ READ}
* @since 1.7
*/
public
ProcessBuilder
redirectError
(
Redirect
destination
)
{
if
(
destination
.
type
()
==
Redirect
.
Type
.
READ
)
throw
new
IllegalArgumentException
(
"Redirect invalid for writing: "
+
destination
);
redirects
()[
2
]
=
destination
;
return
this
;
}
/**
* Sets this process builder's standard input source to a file.
*
* <p>This is a convenience method. An invocation of the form
* {@code redirectInput(file)}
* behaves in exactly the same way as the invocation
* {@link #redirectInput(Redirect) redirectInput}
* {@code (Redirect.from(file))}.
*
* @param file the new standard input source
* @return this process builder
* @since 1.7
*/
public
ProcessBuilder
redirectInput
(
File
file
)
{
return
redirectInput
(
Redirect
.
from
(
file
));
}
/**
* Sets this process builder's standard output destination to a file.
*
* <p>This is a convenience method. An invocation of the form
* {@code redirectOutput(file)}
* behaves in exactly the same way as the invocation
* {@link #redirectOutput(Redirect) redirectOutput}
* {@code (Redirect.to(file))}.
*
* @param file the new standard output destination
* @return this process builder
* @since 1.7
*/
public
ProcessBuilder
redirectOutput
(
File
file
)
{
return
redirectOutput
(
Redirect
.
to
(
file
));
}
/**
* Sets this process builder's standard error destination to a file.
*
* <p>This is a convenience method. An invocation of the form
* {@code redirectError(file)}
* behaves in exactly the same way as the invocation
* {@link #redirectError(Redirect) redirectError}
* {@code (Redirect.to(file))}.
*
* @param file the new standard error destination
* @return this process builder
* @since 1.7
*/
public
ProcessBuilder
redirectError
(
File
file
)
{
return
redirectError
(
Redirect
.
to
(
file
));
}
/**
* Returns this process builder's standard input source.
*
* Subprocesses subsequently started by this object's {@link #start()}
* method obtain their standard input from this source.
* The initial value is {@link Redirect#PIPE Redirect.PIPE}.
*
* @return this process builder's standard input source
* @since 1.7
*/
public
Redirect
redirectInput
()
{
return
(
redirects
==
null
)
?
Redirect
.
PIPE
:
redirects
[
0
];
}
/**
* Returns this process builder's standard output destination.
*
* Subprocesses subsequently started by this object's {@link #start()}
* method redirect their standard output to this destination.
* The initial value is {@link Redirect#PIPE Redirect.PIPE}.
*
* @return this process builder's standard output destination
* @since 1.7
*/
public
Redirect
redirectOutput
()
{
return
(
redirects
==
null
)
?
Redirect
.
PIPE
:
redirects
[
1
];
}
/**
* Returns this process builder's standard error destination.
*
* Subprocesses subsequently started by this object's {@link #start()}
* method redirect their standard error to this destination.
* The initial value is {@link Redirect#PIPE Redirect.PIPE}.
*
* @return this process builder's standard error destination
* @since 1.7
*/
public
Redirect
redirectError
()
{
return
(
redirects
==
null
)
?
Redirect
.
PIPE
:
redirects
[
2
];
}
/**
* Sets the source and destination for subprocess standard I/O
* to be the same as those of the current Java process.
*
* <p>This is a convenience method. An invocation of the form
* <pre> {@code
* pb.inheritIO()
* }</pre>
* behaves in exactly the same way as the invocation
* <pre> {@code
* pb.redirectInput(Redirect.INHERIT)
* .redirectOutput(Redirect.INHERIT)
* .redirectError(Redirect.INHERIT)
* }</pre>
*
* This gives behavior equivalent to most operating system
* command interpreters, or the standard C library function
* {@code system()}.
*
* @return this process builder
* @since 1.7
*/
public
ProcessBuilder
inheritIO
()
{
Arrays
.
fill
(
redirects
(),
Redirect
.
INHERIT
);
return
this
;
}
/**
* Tells whether this process builder merges standard error and
* standard output.
*
* <p>If this property is
<code>true</code>
, then any error output
* <p>If this property is
{@code true}
, then any error output
* generated by subprocesses subsequently started by this object's
* {@link #start()} method will be merged with the standard
* output, so that both can be read using the
* {@link Process#getInputStream()} method. This makes it easier
* to correlate error messages with the corresponding output.
* The initial value is
<code>false</code>.</p>
* The initial value is
{@code false}.
*
* @return
This process builder's <code>redirectErrorStream</code>
property
* @return
this process builder's {@code redirectErrorStream}
property
*/
public
boolean
redirectErrorStream
()
{
return
redirectErrorStream
;
}
/**
* Sets this process builder's
<code>redirectErrorStream</code>
property.
* Sets this process builder's
{@code redirectErrorStream}
property.
*
* <p>If this property is
<code>true</code>
, then any error output
* <p>If this property is
{@code true}
, then any error output
* generated by subprocesses subsequently started by this object's
* {@link #start()} method will be merged with the standard
* output, so that both can be read using the
* {@link Process#getInputStream()} method. This makes it easier
* to correlate error messages with the corresponding output.
* The initial value is
<code>false</code>.</p>
* The initial value is
{@code false}.
*
* @param
redirectErrorStream T
he new property value
* @return
T
his process builder
* @param
redirectErrorStream t
he new property value
* @return
t
his process builder
*/
public
ProcessBuilder
redirectErrorStream
(
boolean
redirectErrorStream
)
{
this
.
redirectErrorStream
=
redirectErrorStream
;
...
...
@@ -410,7 +937,7 @@ public final class ProcessBuilder
* <p>If there is a security manager, its
* {@link SecurityManager#checkExec checkExec}
* method is called with the first component of this object's
*
<code>command</code>
array as its argument. This may result in
*
{@code command}
array as its argument. This may result in
* a {@link SecurityException} being thrown.
*
* <p>Starting an operating system process is highly system-dependent.
...
...
@@ -426,26 +953,42 @@ public final class ProcessBuilder
* subclass of {@link IOException}.
*
* <p>Subsequent modifications to this process builder will not
* affect the returned {@link Process}.</p>
* affect the returned {@link Process}.
*
* @return a new {@link Process} object for managing the subprocess
*
* @throws NullPointerException
* if an element of the command list is null
*
* @throws IndexOutOfBoundsException
* if the command is an empty list (has size {@code 0})
*
* @throws SecurityException
* if a security manager exists and
* <ul>
*
* @return A new {@link Process} object for managing the subprocess
* <li>its
* {@link SecurityManager#checkExec checkExec}
* method doesn't allow creation of the subprocess, or
*
* @throws NullPointerException
* If an element of the command list is null
* <li>the standard input to the subprocess was
* {@linkplain #redirectInput redirected from a file}
* and the security manager's
* {@link SecurityManager#checkRead checkRead} method
* denies read access to the file, or
*
* @throws IndexOutOfBoundsException
* If the command is an empty list (has size <code>0</code>)
* <li>the standard output or standard error of the
* subprocess was
* {@linkplain #redirectOutput redirected to a file}
* and the security manager's
* {@link SecurityManager#checkWrite checkWrite} method
* denies write access to the file
*
* @throws SecurityException
* If a security manager exists and its
* {@link SecurityManager#checkExec checkExec}
* method doesn't allow creation of the subprocess
* </ul>
*
* @throws IOException
* If an I/O error occurs
* @throws IOException if an I/O error occurs
*
* @see Runtime#exec(String[], String[], java.io.File)
* @see SecurityManager#checkExec(String)
* @see Runtime#exec(String[], String[], java.io.File)
*/
public
Process
start
()
throws
IOException
{
// Must convert to array first -- a malicious user-supplied
...
...
@@ -467,6 +1010,7 @@ public final class ProcessBuilder
return
ProcessImpl
.
start
(
cmdarray
,
environment
,
dir
,
redirects
,
redirectErrorStream
);
}
catch
(
IOException
e
)
{
// It's much easier for us to create a high-quality error
...
...
src/share/classes/sun/misc/JavaIOFileDescriptorAccess.java
浏览文件 @
095ce48d
...
...
@@ -33,4 +33,8 @@ import java.io.FileDescriptor;
public
interface
JavaIOFileDescriptorAccess
{
public
void
set
(
FileDescriptor
obj
,
int
fd
);
public
int
get
(
FileDescriptor
fd
);
// Only valid on Windows
public
void
setHandle
(
FileDescriptor
obj
,
long
handle
);
public
long
getHandle
(
FileDescriptor
obj
);
}
src/solaris/classes/java/io/FileDescriptor.java
浏览文件 @
095ce48d
...
...
@@ -152,11 +152,19 @@ public final class FileDescriptor {
public
int
get
(
FileDescriptor
obj
)
{
return
obj
.
fd
;
}
public
void
setHandle
(
FileDescriptor
obj
,
long
handle
)
{
throw
new
UnsupportedOperationException
();
}
public
long
getHandle
(
FileDescriptor
obj
)
{
throw
new
UnsupportedOperationException
();
}
}
);
}
// pac
akge private methods used by FIS,
FOS and RAF
// pac
kage private methods used by FIS,
FOS and RAF
int
incrementAndGetUseCount
()
{
return
useCount
.
incrementAndGet
();
...
...
src/solaris/classes/java/lang/ProcessImpl.java
浏览文件 @
095ce48d
...
...
@@ -26,7 +26,10 @@
package
java.lang
;
import
java.io.IOException
;
import
java.lang.Process
;
import
java.io.FileInputStream
;
import
java.io.FileOutputStream
;
import
java.lang.ProcessBuilder.Redirect
;
import
java.lang.ProcessBuilder.Redirect
;
/**
* This class is for the exclusive use of ProcessBuilder.start() to
...
...
@@ -36,6 +39,9 @@ import java.lang.Process;
* @since 1.5
*/
final
class
ProcessImpl
{
private
static
final
sun
.
misc
.
JavaIOFileDescriptorAccess
fdAccess
=
sun
.
misc
.
SharedSecrets
.
getJavaIOFileDescriptorAccess
();
private
ProcessImpl
()
{}
// Not instantiable
private
static
byte
[]
toCString
(
String
s
)
{
...
...
@@ -54,6 +60,7 @@ final class ProcessImpl {
static
Process
start
(
String
[]
cmdarray
,
java
.
util
.
Map
<
String
,
String
>
environment
,
String
dir
,
ProcessBuilder
.
Redirect
[]
redirects
,
boolean
redirectErrorStream
)
throws
IOException
{
...
...
@@ -78,11 +85,61 @@ final class ProcessImpl {
int
[]
envc
=
new
int
[
1
];
byte
[]
envBlock
=
ProcessEnvironment
.
toEnvironmentBlock
(
environment
,
envc
);
int
[]
std_fds
;
FileInputStream
f0
=
null
;
FileOutputStream
f1
=
null
;
FileOutputStream
f2
=
null
;
try
{
if
(
redirects
==
null
)
{
std_fds
=
new
int
[]
{
-
1
,
-
1
,
-
1
};
}
else
{
std_fds
=
new
int
[
3
];
if
(
redirects
[
0
]
==
Redirect
.
PIPE
)
std_fds
[
0
]
=
-
1
;
else
if
(
redirects
[
0
]
==
Redirect
.
INHERIT
)
std_fds
[
0
]
=
0
;
else
{
f0
=
new
FileInputStream
(
redirects
[
0
].
file
());
std_fds
[
0
]
=
fdAccess
.
get
(
f0
.
getFD
());
}
if
(
redirects
[
1
]
==
Redirect
.
PIPE
)
std_fds
[
1
]
=
-
1
;
else
if
(
redirects
[
1
]
==
Redirect
.
INHERIT
)
std_fds
[
1
]
=
1
;
else
{
f1
=
redirects
[
1
].
toFileOutputStream
();
std_fds
[
1
]
=
fdAccess
.
get
(
f1
.
getFD
());
}
if
(
redirects
[
2
]
==
Redirect
.
PIPE
)
std_fds
[
2
]
=
-
1
;
else
if
(
redirects
[
2
]
==
Redirect
.
INHERIT
)
std_fds
[
2
]
=
2
;
else
{
f2
=
redirects
[
2
].
toFileOutputStream
();
std_fds
[
2
]
=
fdAccess
.
get
(
f2
.
getFD
());
}
}
return
new
UNIXProcess
(
toCString
(
cmdarray
[
0
]),
argBlock
,
args
.
length
,
envBlock
,
envc
[
0
],
toCString
(
dir
),
std_fds
,
redirectErrorStream
);
}
finally
{
// In theory, close() can throw IOException
// (although it is rather unlikely to happen here)
try
{
if
(
f0
!=
null
)
f0
.
close
();
}
finally
{
try
{
if
(
f1
!=
null
)
f1
.
close
();
}
finally
{
if
(
f2
!=
null
)
f2
.
close
();
}
}
}
}
}
src/solaris/classes/java/lang/UNIXProcess.java.linux
浏览文件 @
095ce48d
/*
*
Copyright
1995
-
200
6
Sun
Microsystems
,
Inc
.
All
Rights
Reserved
.
/*
*
Copyright
1995
-
200
8
Sun
Microsystems
,
Inc
.
All
Rights
Reserved
.
*
DO
NOT
ALTER
OR
REMOVE
COPYRIGHT
NOTICES
OR
THIS
FILE
HEADER
.
*
*
This
code
is
free
software
;
you
can
redistribute
it
and
/
or
modify
it
...
...
@@ -34,9 +34,9 @@ import java.io.*;
*/
final
class
UNIXProcess
extends
Process
{
private
FileDescriptor
stdin_fd
;
private
FileDescriptor
stdout_fd
;
private
FileDescriptor
stderr_fd
;
private
static
final
sun
.
misc
.
JavaIOFileDescriptorAccess
fdAccess
=
sun
.
misc
.
SharedSecrets
.
getJavaIOFileDescriptorAccess
()
;
private
int
pid
;
private
int
exitcode
;
private
boolean
hasExited
;
...
...
@@ -48,15 +48,26 @@ final class UNIXProcess extends Process {
/*
this
is
for
the
reaping
thread
*/
private
native
int
waitForProcessExit
(
int
pid
);
/**
*
Create
a
process
using
fork
(
2
)
and
exec
(
2
).
*
*
@
param
std_fds
array
of
file
descriptors
.
Indexes
0
,
1
,
and
*
2
correspond
to
standard
input
,
standard
output
and
*
standard
error
,
respectively
.
On
input
,
a
value
of
-
1
*
means
to
create
a
pipe
to
connect
child
and
parent
*
processes
.
On
output
,
a
value
which
is
not
-
1
is
the
*
parent
pipe
fd
corresponding
to
the
pipe
which
has
*
been
created
.
An
element
of
this
array
is
-
1
on
input
*
if
and
only
if
it
is
<
em
>
not
</
em
>
-
1
on
output
.
*
@
return
the
pid
of
the
subprocess
*/
private
native
int
forkAndExec
(
byte
[]
prog
,
byte
[]
argBlock
,
int
argc
,
byte
[]
envBlock
,
int
envc
,
byte
[]
dir
,
boolean
redirectErrorStream
,
FileDescriptor
stdin_fd
,
FileDescriptor
stdout_fd
,
FileDescriptor
stderr_fd
)
throws
IOException
;
byte
[]
argBlock
,
int
argc
,
byte
[]
envBlock
,
int
envc
,
byte
[]
dir
,
int
[]
std_fds
,
boolean
redirectErrorStream
)
throws
IOException
;
/*
In
the
process
constructor
we
wait
on
this
gate
until
the
process
*/
/*
has
been
created
.
Then
we
return
from
the
constructor
.
*/
...
...
@@ -97,67 +108,82 @@ final class UNIXProcess extends Process {
}
UNIXProcess
(
final
byte
[]
prog
,
final
byte
[]
argBlock
,
final
int
argc
,
final
byte
[]
envBlock
,
final
int
envc
,
final
byte
[]
dir
,
final
boolean
redirectErrorStream
)
final
byte
[]
argBlock
,
final
int
argc
,
final
byte
[]
envBlock
,
final
int
envc
,
final
byte
[]
dir
,
final
int
[]
std_fds
,
final
boolean
redirectErrorStream
)
throws
IOException
{
stdin_fd
=
new
FileDescriptor
();
stdout_fd
=
new
FileDescriptor
();
stderr_fd
=
new
FileDescriptor
();
final
Gate
gate
=
new
Gate
();
/*
*
For
each
subprocess
forked
a
corresponding
reaper
thread
*
is
started
.
That
thread
is
the
only
thread
which
waits
*
for
the
subprocess
to
terminate
and
it
doesn
't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction
() {
public Object
run() {
Thread t = new Thread("process reaper") {
public void run() {
/*
*
For
each
subprocess
forked
a
corresponding
reaper
thread
*
is
started
.
That
thread
is
the
only
thread
which
waits
*
for
the
subprocess
to
terminate
and
it
doesn
't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>
() {
public Void
run() {
Thread t = new Thread("process reaper") {
public void run() {
try {
pid = forkAndExec(prog,
argBlock, argc,
envBlock, envc,
dir,
redirectErrorStream
,
stdin_fd, stdout_fd, stderr_fd
);
argBlock, argc,
envBlock, envc,
dir,
std_fds
,
redirectErrorStream
);
} catch (IOException e) {
gate.setException(e); /*remember to rethrow later*/
gate.exit();
return;
}
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
stdin_stream = new BufferedOutputStream(new
FileOutputStream(stdin_fd));
stdout_stream = new BufferedInputStream(new
FileInputStream(stdout_fd));
new java.security.PrivilegedAction<Void>() {
public Void run() {
if (std_fds[0] == -1)
stdin_stream = new ProcessBuilder.NullOutputStream();
else {
FileDescriptor stdin_fd = new FileDescriptor();
fdAccess.set(stdin_fd, std_fds[0]);
stdin_stream = new BufferedOutputStream(
new FileOutputStream(stdin_fd));
}
if (std_fds[1] == -1)
stdout_stream = new ProcessBuilder.NullInputStream();
else {
FileDescriptor stdout_fd = new FileDescriptor();
fdAccess.set(stdout_fd, std_fds[1]);
stdout_stream = new BufferedInputStream(
new FileInputStream(stdout_fd));
}
if (std_fds[2] == -1)
stderr_stream = new ProcessBuilder.NullInputStream();
else {
FileDescriptor stderr_fd = new FileDescriptor();
fdAccess.set(stderr_fd, std_fds[2]);
stderr_stream = new FileInputStream(stderr_fd);
return null;
}
});
return null; }});
gate.exit(); /* exit from constructor */
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
t.setDaemon(true);
t.start();
return null;
}
});
return null; }});
gate.waitForExit();
IOException e = gate.getException();
if (e != null)
...
...
@@ -165,43 +191,43 @@ final class UNIXProcess extends Process {
}
public OutputStream getOutputStream() {
return stdin_stream;
return stdin_stream;
}
public InputStream getInputStream() {
return stdout_stream;
return stdout_stream;
}
public InputStream getErrorStream() {
return stderr_stream;
return stderr_stream;
}
public synchronized int waitFor() throws InterruptedException {
while (!hasExited) {
wait();
}
return exitcode;
wait();
}
return exitcode;
}
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn'
t
exited
");
}
return exitcode;
if (!hasExited) {
throw new IllegalThreadStateException("process hasn'
t
exited
");
}
return exitcode;
}
private static native void destroyProcess(int pid);
public void destroy() {
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
destroyProcess(pid);
}
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
destroyProcess(pid);
}
try {
stdin_stream.close();
stdout_stream.close();
...
...
@@ -215,6 +241,6 @@ final class UNIXProcess extends Process {
private static native void initIDs();
static {
initIDs();
initIDs();
}
}
src/solaris/classes/java/lang/UNIXProcess.java.solaris
浏览文件 @
095ce48d
/*
*
Copyright
1995
-
200
6
Sun
Microsystems
,
Inc
.
All
Rights
Reserved
.
/*
*
Copyright
1995
-
200
8
Sun
Microsystems
,
Inc
.
All
Rights
Reserved
.
*
DO
NOT
ALTER
OR
REMOVE
COPYRIGHT
NOTICES
OR
THIS
FILE
HEADER
.
*
*
This
code
is
free
software
;
you
can
redistribute
it
and
/
or
modify
it
...
...
@@ -33,129 +33,155 @@ import java.io.*;
*/
final
class
UNIXProcess
extends
Process
{
private
FileDescriptor
stdin_fd
;
private
FileDescriptor
stdout_fd
;
private
FileDescriptor
stderr_fd
;
private
int
pid
;
private
static
final
sun
.
misc
.
JavaIOFileDescriptorAccess
fdAccess
=
sun
.
misc
.
SharedSecrets
.
getJavaIOFileDescriptorAccess
()
;
private
final
int
pid
;
private
int
exitcode
;
private
boolean
hasExited
;
private
OutputStream
stdin_stream
;
private
Buffered
InputStream
stdout_stream
;
private
InputStream
stdout_stream
;
private
DeferredCloseInputStream
stdout_inner_stream
;
private
DeferredClose
InputStream
stderr_stream
;
private
InputStream
stderr_stream
;
/*
this
is
for
the
reaping
thread
*/
private
native
int
waitForProcessExit
(
int
pid
);
/**
*
Create
a
process
using
fork
(
2
)
and
exec
(
2
).
*
*
@
param
std_fds
array
of
file
descriptors
.
Indexes
0
,
1
,
and
*
2
correspond
to
standard
input
,
standard
output
and
*
standard
error
,
respectively
.
On
input
,
a
value
of
-
1
*
means
to
create
a
pipe
to
connect
child
and
parent
*
processes
.
On
output
,
a
value
which
is
not
-
1
is
the
*
parent
pipe
fd
corresponding
to
the
pipe
which
has
*
been
created
.
An
element
of
this
array
is
-
1
on
input
*
if
and
only
if
it
is
<
em
>
not
</
em
>
-
1
on
output
.
*
@
return
the
pid
of
the
subprocess
*/
private
native
int
forkAndExec
(
byte
[]
prog
,
byte
[]
argBlock
,
int
argc
,
byte
[]
envBlock
,
int
envc
,
byte
[]
dir
,
boolean
redirectErrorStream
,
FileDescriptor
stdin_fd
,
FileDescriptor
stdout_fd
,
FileDescriptor
stderr_fd
)
throws
IOException
;
byte
[]
argBlock
,
int
argc
,
byte
[]
envBlock
,
int
envc
,
byte
[]
dir
,
int
[]
std_fds
,
boolean
redirectErrorStream
)
throws
IOException
;
UNIXProcess
(
final
byte
[]
prog
,
final
byte
[]
argBlock
,
int
argc
,
final
byte
[]
envBlock
,
int
envc
,
final
byte
[]
dir
,
final
boolean
redirectErrorStream
)
final
byte
[]
argBlock
,
int
argc
,
final
byte
[]
envBlock
,
int
envc
,
final
byte
[]
dir
,
final
int
[]
std_fds
,
final
boolean
redirectErrorStream
)
throws
IOException
{
stdin_fd
=
new
FileDescriptor
();
stdout_fd
=
new
FileDescriptor
();
stderr_fd
=
new
FileDescriptor
();
pid
=
forkAndExec
(
prog
,
argBlock
,
argc
,
envBlock
,
envc
,
dir
,
redirectErrorStream
,
stdin_fd
,
stdout_fd
,
stderr_fd
);
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
stdin_stream
=
new
BufferedOutputStream
(
new
FileOutputStream
(
stdin_fd
));
stdout_inner_stream
=
new
DeferredCloseInputStream
(
stdout_fd
);
stdout_stream
=
new
BufferedInputStream
(
stdout_inner_stream
);
stderr_stream
=
new
DeferredCloseInputStream
(
stderr_fd
);
return
null
;
}
});
/*
*
For
each
subprocess
forked
a
corresponding
reaper
thread
*
is
started
.
That
thread
is
the
only
thread
which
waits
*
for
the
subprocess
to
terminate
and
it
doesn
't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
Thread t = new Thread("process reaper") {
public void run() {
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
t.setDaemon(true);
t.start();
return null;
}
});
pid
=
forkAndExec
(
prog
,
argBlock
,
argc
,
envBlock
,
envc
,
dir
,
std_fds
,
redirectErrorStream
);
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
<
Void
>()
{
public
Void
run
()
{
if
(
std_fds
[
0
]
==
-
1
)
stdin_stream
=
new
ProcessBuilder
.
NullOutputStream
();
else
{
FileDescriptor
stdin_fd
=
new
FileDescriptor
();
fdAccess
.
set
(
stdin_fd
,
std_fds
[
0
]);
stdin_stream
=
new
BufferedOutputStream
(
new
FileOutputStream
(
stdin_fd
));
}
if
(
std_fds
[
1
]
==
-
1
)
stdout_stream
=
new
ProcessBuilder
.
NullInputStream
();
else
{
FileDescriptor
stdout_fd
=
new
FileDescriptor
();
fdAccess
.
set
(
stdout_fd
,
std_fds
[
1
]);
stdout_inner_stream
=
new
DeferredCloseInputStream
(
stdout_fd
);
stdout_stream
=
new
BufferedInputStream
(
stdout_inner_stream
);
}
if
(
std_fds
[
2
]
==
-
1
)
stderr_stream
=
new
ProcessBuilder
.
NullInputStream
();
else
{
FileDescriptor
stderr_fd
=
new
FileDescriptor
();
fdAccess
.
set
(
stderr_fd
,
std_fds
[
2
]);
stderr_stream
=
new
DeferredCloseInputStream
(
stderr_fd
);
}
return
null
;
}});
/*
*
For
each
subprocess
forked
a
corresponding
reaper
thread
*
is
started
.
That
thread
is
the
only
thread
which
waits
*
for
the
subprocess
to
terminate
and
it
doesn
't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() { public Void run() {
Thread t = new Thread("process reaper") {
public void run() {
int res = waitForProcessExit(pid);
synchronized (UNIXProcess.this) {
hasExited = true;
exitcode = res;
UNIXProcess.this.notifyAll();
}
}
};
t.setDaemon(true);
t.start();
return null; }});
}
public OutputStream getOutputStream() {
return stdin_stream;
return stdin_stream;
}
public InputStream getInputStream() {
return stdout_stream;
return stdout_stream;
}
public InputStream getErrorStream() {
return stderr_stream;
return stderr_stream;
}
public synchronized int waitFor() throws InterruptedException {
while (!hasExited) {
wait();
}
return exitcode;
wait();
}
return exitcode;
}
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn'
t
exited
");
}
return exitcode;
if (!hasExited) {
throw new IllegalThreadStateException("process hasn'
t
exited
");
}
return exitcode;
}
private static native void destroyProcess(int pid);
public synchronized void destroy() {
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
if (!hasExited)
destroyProcess(pid);
try {
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
if (!hasExited)
destroyProcess(pid);
try {
stdin_stream.close();
stdout_inner_stream.closeDeferred(stdout_stream);
stderr_stream.closeDeferred(stderr_stream);
if (stdout_inner_stream != null)
stdout_inner_stream.closeDeferred(stdout_stream);
if (stderr_stream instanceof DeferredCloseInputStream)
((DeferredCloseInputStream) stderr_stream)
.closeDeferred(stderr_stream);
} catch (IOException e) {
// ignore
}
...
...
@@ -172,99 +198,99 @@ final class UNIXProcess extends Process {
// (EOF) as they did before.
//
private static class DeferredCloseInputStream
extends FileInputStream
extends FileInputStream
{
private DeferredCloseInputStream(FileDescriptor fd) {
super(fd);
}
private Object lock = new Object();
// For the following fields
private boolean closePending = false;
private int useCount = 0;
private InputStream streamToClose;
private void raise() {
synchronized (lock) {
useCount++;
}
}
private void lower() throws IOException {
synchronized (lock) {
useCount--;
if (useCount == 0 && closePending) {
streamToClose.close();
}
}
}
// stc is the actual stream to be closed; it might be this object, or
// it might be an upstream object for which this object is downstream.
//
private void closeDeferred(InputStream stc) throws IOException {
synchronized (lock) {
if (useCount == 0) {
stc.close();
} else {
closePending = true;
streamToClose = stc;
}
}
}
public void close() throws IOException {
synchronized (lock) {
useCount = 0;
closePending = false;
}
super.close();
}
public int read() throws IOException {
raise();
try {
return super.read();
} finally {
lower();
}
}
public int read(byte[] b) throws IOException {
raise();
try {
return super.read(b);
} finally {
lower();
}
}
public int read(byte[] b, int off, int len) throws IOException {
raise();
try {
return super.read(b, off, len);
} finally {
lower();
}
}
public long skip(long n) throws IOException {
raise();
try {
return super.skip(n);
} finally {
lower();
}
}
public int available() throws IOException {
raise();
try {
return super.available();
} finally {
lower();
}
}
private DeferredCloseInputStream(FileDescriptor fd) {
super(fd);
}
private Object lock = new Object();
// For the following fields
private boolean closePending = false;
private int useCount = 0;
private InputStream streamToClose;
private void raise() {
synchronized (lock) {
useCount++;
}
}
private void lower() throws IOException {
synchronized (lock) {
useCount--;
if (useCount == 0 && closePending) {
streamToClose.close();
}
}
}
// stc is the actual stream to be closed; it might be this object, or
// it might be an upstream object for which this object is downstream.
//
private void closeDeferred(InputStream stc) throws IOException {
synchronized (lock) {
if (useCount == 0) {
stc.close();
} else {
closePending = true;
streamToClose = stc;
}
}
}
public void close() throws IOException {
synchronized (lock) {
useCount = 0;
closePending = false;
}
super.close();
}
public int read() throws IOException {
raise();
try {
return super.read();
} finally {
lower();
}
}
public int read(byte[] b) throws IOException {
raise();
try {
return super.read(b);
} finally {
lower();
}
}
public int read(byte[] b, int off, int len) throws IOException {
raise();
try {
return super.read(b, off, len);
} finally {
lower();
}
}
public long skip(long n) throws IOException {
raise();
try {
return super.skip(n);
} finally {
lower();
}
}
public int available() throws IOException {
raise();
try {
return super.available();
} finally {
lower();
}
}
}
...
...
@@ -272,6 +298,6 @@ final class UNIXProcess extends Process {
private static native void initIDs();
static {
initIDs();
initIDs();
}
}
src/solaris/native/java/lang/UNIXProcess_md.c
浏览文件 @
095ce48d
/*
* Copyright 1995-200
6
Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1995-200
8
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
...
...
@@ -491,10 +491,8 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
jbyteArray
argBlock
,
jint
argc
,
jbyteArray
envBlock
,
jint
envc
,
jbyteArray
dir
,
jboolean
redirectErrorStream
,
jobject
stdin_fd
,
jobject
stdout_fd
,
jobject
stderr_fd
)
jintArray
std_fds
,
jboolean
redirectErrorStream
)
{
int
errnum
;
int
resultPid
=
-
1
;
...
...
@@ -505,6 +503,7 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
const
char
*
pargBlock
=
getBytes
(
env
,
argBlock
);
const
char
*
penvBlock
=
getBytes
(
env
,
envBlock
);
const
char
*
pdir
=
getBytes
(
env
,
dir
);
jint
*
fds
=
NULL
;
in
[
0
]
=
in
[
1
]
=
out
[
0
]
=
out
[
1
]
=
err
[
0
]
=
err
[
1
]
=
fail
[
0
]
=
fail
[
1
]
=
-
1
;
...
...
@@ -527,9 +526,13 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
initVectorFromBlock
(
envv
,
penvBlock
,
envc
);
}
if
((
pipe
(
in
)
<
0
)
||
(
pipe
(
out
)
<
0
)
||
(
pipe
(
err
)
<
0
)
||
assert
(
std_fds
!=
NULL
);
fds
=
(
*
env
)
->
GetIntArrayElements
(
env
,
std_fds
,
NULL
);
if
(
fds
==
NULL
)
goto
Catch
;
if
((
fds
[
0
]
==
-
1
&&
pipe
(
in
)
<
0
)
||
(
fds
[
1
]
==
-
1
&&
pipe
(
out
)
<
0
)
||
(
fds
[
2
]
==
-
1
&&
pipe
(
err
)
<
0
)
||
(
pipe
(
fail
)
<
0
))
{
throwIOException
(
env
,
errno
,
"Bad file descriptor"
);
goto
Catch
;
...
...
@@ -544,23 +547,26 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
if
(
resultPid
==
0
)
{
/* Child process */
/* Close the parent sides of the pipe.
Give the child sides of the pipes the right fileno's.
/* Close the parent sides of the pipes.
Closing pipe fds here is redundant, since closeDescriptors()
would do it anyways, but a little paranoia is a good thing. */
closeSafely
(
in
[
1
]);
closeSafely
(
out
[
0
]);
closeSafely
(
err
[
0
]);
closeSafely
(
fail
[
0
]);
/* Give the child sides of the pipes the right fileno's. */
/* Note: it is possible for in[0] == 0 */
close
(
in
[
1
]);
moveDescriptor
(
in
[
0
],
STDIN_FILENO
);
close
(
out
[
0
]);
moveDescriptor
(
out
[
1
],
STDOUT_FILENO
);
close
(
err
[
0
]);
moveDescriptor
(
in
[
0
]
!=
-
1
?
in
[
0
]
:
fds
[
0
],
STDIN_FILENO
);
moveDescriptor
(
out
[
1
]
!=
-
1
?
out
[
1
]
:
fds
[
1
],
STDOUT_FILENO
);
if
(
redirectErrorStream
)
{
close
(
err
[
1
]);
close
Safely
(
err
[
1
]);
dup2
(
STDOUT_FILENO
,
STDERR_FILENO
);
}
else
{
moveDescriptor
(
err
[
1
],
STDERR_FILENO
);
moveDescriptor
(
err
[
1
]
!=
-
1
?
err
[
1
]
:
fds
[
2
]
,
STDERR_FILENO
);
}
close
(
fail
[
0
]);
moveDescriptor
(
fail
[
1
],
FAIL_FILENO
);
/* close everything */
...
...
@@ -606,9 +612,9 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
goto
Catch
;
}
(
*
env
)
->
SetIntField
(
env
,
stdin_fd
,
IO_fd_fdID
,
in
[
1
])
;
(
*
env
)
->
SetIntField
(
env
,
stdout_fd
,
IO_fd_fdID
,
out
[
0
])
;
(
*
env
)
->
SetIntField
(
env
,
stderr_fd
,
IO_fd_fdID
,
err
[
0
])
;
fds
[
0
]
=
(
in
[
1
]
!=
-
1
)
?
in
[
1
]
:
-
1
;
fds
[
1
]
=
(
out
[
0
]
!=
-
1
)
?
out
[
0
]
:
-
1
;
fds
[
2
]
=
(
err
[
0
]
!=
-
1
)
?
err
[
0
]
:
-
1
;
Finally:
/* Always clean up the child's side of the pipes */
...
...
@@ -628,6 +634,9 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
releaseBytes
(
env
,
envBlock
,
penvBlock
);
releaseBytes
(
env
,
dir
,
pdir
);
if
(
fds
!=
NULL
)
(
*
env
)
->
ReleaseIntArrayElements
(
env
,
std_fds
,
fds
,
0
);
return
resultPid
;
Catch:
...
...
src/windows/classes/java/io/FileDescriptor.java
浏览文件 @
095ce48d
...
...
@@ -29,17 +29,14 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Instances of the file descriptor class serve as an opaque handle
* to the underlying machine-specific structure representing an open
* file, an open socket, or another source or sink of bytes. The
* main practical use for a file descriptor is to create a
* <code>FileInputStream</code> or <code>FileOutputStream</code> to
* contain it.
* <p>
* Applications should not create their own file descriptors.
* to the underlying machine-specific structure representing an
* open file, an open socket, or another source or sink of bytes.
* The main practical use for a file descriptor is to create a
* {@link FileInputStream} or {@link FileOutputStream} to contain it.
*
* <p>Applications should not create their own file descriptors.
*
* @author Pavani Diwanji
* @see java.io.FileInputStream
* @see java.io.FileOutputStream
* @since JDK1.0
*/
public
final
class
FileDescriptor
{
...
...
@@ -81,6 +78,14 @@ public final class FileDescriptor {
public
int
get
(
FileDescriptor
obj
)
{
return
obj
.
fd
;
}
public
void
setHandle
(
FileDescriptor
obj
,
long
handle
)
{
obj
.
handle
=
handle
;
}
public
long
getHandle
(
FileDescriptor
obj
)
{
return
obj
.
handle
;
}
}
);
}
...
...
@@ -88,7 +93,7 @@ public final class FileDescriptor {
/**
* A handle to the standard input stream. Usually, this file
* descriptor is not used directly, but rather via the input stream
* known as
<code>System.in</code>
.
* known as
{@code System.in}
.
*
* @see java.lang.System#in
*/
...
...
@@ -97,7 +102,7 @@ public final class FileDescriptor {
/**
* A handle to the standard output stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as
<code>System.out</code>
.
* known as
{@code System.out}
.
* @see java.lang.System#out
*/
public
static
final
FileDescriptor
out
=
standardStream
(
1
);
...
...
@@ -105,7 +110,7 @@ public final class FileDescriptor {
/**
* A handle to the standard error stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as
<code>System.err</code>
.
* known as
{@code System.err}
.
*
* @see java.lang.System#err
*/
...
...
@@ -114,9 +119,9 @@ public final class FileDescriptor {
/**
* Tests if this file descriptor object is valid.
*
* @return
<code>true</code>
if the file descriptor object represents a
* @return
{@code true}
if the file descriptor object represents a
* valid, open file, socket, or other active I/O connection;
*
<code>false</code>
otherwise.
*
{@code false}
otherwise.
*/
public
boolean
valid
()
{
return
((
handle
!=
-
1
)
||
(
fd
!=
-
1
));
...
...
src/windows/classes/java/lang/ProcessImpl.java
浏览文件 @
095ce48d
...
...
@@ -25,7 +25,16 @@
package
java.lang
;
import
java.io.*
;
import
java.io.IOException
;
import
java.io.File
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.io.FileInputStream
;
import
java.io.FileOutputStream
;
import
java.io.FileDescriptor
;
import
java.io.BufferedInputStream
;
import
java.io.BufferedOutputStream
;
import
java.lang.ProcessBuilder.Redirect
;
/* This class is for the exclusive use of ProcessBuilder.start() to
* create new processes.
...
...
@@ -35,30 +44,82 @@ import java.io.*;
*/
final
class
ProcessImpl
extends
Process
{
private
static
final
sun
.
misc
.
JavaIOFileDescriptorAccess
fdAccess
=
sun
.
misc
.
SharedSecrets
.
getJavaIOFileDescriptorAccess
();
// System-dependent portion of ProcessBuilder.start()
static
Process
start
(
String
cmdarray
[],
java
.
util
.
Map
<
String
,
String
>
environment
,
String
dir
,
ProcessBuilder
.
Redirect
[]
redirects
,
boolean
redirectErrorStream
)
throws
IOException
{
String
envblock
=
ProcessEnvironment
.
toEnvironmentBlock
(
environment
);
return
new
ProcessImpl
(
cmdarray
,
envblock
,
dir
,
redirectErrorStream
);
FileInputStream
f0
=
null
;
FileOutputStream
f1
=
null
;
FileOutputStream
f2
=
null
;
try
{
long
[]
stdHandles
;
if
(
redirects
==
null
)
{
stdHandles
=
new
long
[]
{
-
1L
,
-
1L
,
-
1L
};
}
else
{
stdHandles
=
new
long
[
3
];
if
(
redirects
[
0
]
==
Redirect
.
PIPE
)
stdHandles
[
0
]
=
-
1L
;
else
if
(
redirects
[
0
]
==
Redirect
.
INHERIT
)
stdHandles
[
0
]
=
fdAccess
.
getHandle
(
FileDescriptor
.
in
);
else
{
f0
=
new
FileInputStream
(
redirects
[
0
].
file
());
stdHandles
[
0
]
=
fdAccess
.
getHandle
(
f0
.
getFD
());
}
if
(
redirects
[
1
]
==
Redirect
.
PIPE
)
stdHandles
[
1
]
=
-
1L
;
else
if
(
redirects
[
1
]
==
Redirect
.
INHERIT
)
stdHandles
[
1
]
=
fdAccess
.
getHandle
(
FileDescriptor
.
out
);
else
{
f1
=
redirects
[
1
].
toFileOutputStream
();
stdHandles
[
1
]
=
fdAccess
.
getHandle
(
f1
.
getFD
());
}
if
(
redirects
[
2
]
==
Redirect
.
PIPE
)
stdHandles
[
2
]
=
-
1L
;
else
if
(
redirects
[
2
]
==
Redirect
.
INHERIT
)
stdHandles
[
2
]
=
fdAccess
.
getHandle
(
FileDescriptor
.
err
);
else
{
f2
=
redirects
[
2
].
toFileOutputStream
();
stdHandles
[
2
]
=
fdAccess
.
getHandle
(
f2
.
getFD
());
}
}
return
new
ProcessImpl
(
cmdarray
,
envblock
,
dir
,
stdHandles
,
redirectErrorStream
);
}
finally
{
// In theory, close() can throw IOException
// (although it is rather unlikely to happen here)
try
{
if
(
f0
!=
null
)
f0
.
close
();
}
finally
{
try
{
if
(
f1
!=
null
)
f1
.
close
();
}
finally
{
if
(
f2
!=
null
)
f2
.
close
();
}
}
}
}
private
long
handle
=
0
;
private
FileDescriptor
stdin_fd
;
private
FileDescriptor
stdout_fd
;
private
FileDescriptor
stderr_fd
;
private
OutputStream
stdin_stream
;
private
InputStream
stdout_stream
;
private
InputStream
stderr_stream
;
private
ProcessImpl
(
String
cmd
[],
String
envblock
,
String
path
,
boolean
redirectErrorStream
)
private
ProcessImpl
(
final
String
cmd
[],
final
String
envblock
,
final
String
path
,
final
long
[]
stdHandles
,
final
boolean
redirectErrorStream
)
throws
IOException
{
// Win32 CreateProcess requires cmd[0] to be normalized
...
...
@@ -91,25 +152,39 @@ final class ProcessImpl extends Process {
}
String
cmdstr
=
cmdbuf
.
toString
();
stdin_fd
=
new
FileDescriptor
();
stdout_fd
=
new
FileDescriptor
();
stderr_fd
=
new
FileDescriptor
();
handle
=
create
(
cmdstr
,
envblock
,
path
,
redirectErrorStream
,
stdin_fd
,
stdout_fd
,
stderr_fd
);
handle
=
create
(
cmdstr
,
envblock
,
path
,
stdHandles
,
redirectErrorStream
);
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
()
{
public
Object
run
()
{
stdin_stream
=
new
BufferedOutputStream
(
new
FileOutputStream
(
stdin_fd
)
);
stdout_stream
=
new
BufferedInputStream
(
new
FileInputStream
(
stdout_fd
)
);
stderr_stream
=
new
FileInputStream
(
stderr_fd
);
return
null
;
new
java
.
security
.
PrivilegedAction
<
Void
>
()
{
public
Void
run
()
{
if
(
stdHandles
[
0
]
==
-
1L
)
stdin_stream
=
new
ProcessBuilder
.
NullOutputStream
(
);
else
{
FileDescriptor
stdin_fd
=
new
FileDescriptor
(
);
fdAccess
.
setHandle
(
stdin_fd
,
stdHandles
[
0
]);
stdin_stream
=
new
BufferedOutputStream
(
new
FileOutputStream
(
stdin_fd
))
;
}
});
if
(
stdHandles
[
1
]
==
-
1L
)
stdout_stream
=
new
ProcessBuilder
.
NullInputStream
();
else
{
FileDescriptor
stdout_fd
=
new
FileDescriptor
();
fdAccess
.
setHandle
(
stdout_fd
,
stdHandles
[
1
]);
stdout_stream
=
new
BufferedInputStream
(
new
FileInputStream
(
stdout_fd
));
}
if
(
stdHandles
[
2
]
==
-
1L
)
stderr_stream
=
new
ProcessBuilder
.
NullInputStream
();
else
{
FileDescriptor
stderr_fd
=
new
FileDescriptor
();
fdAccess
.
setHandle
(
stderr_fd
,
stdHandles
[
2
]);
stderr_stream
=
new
FileInputStream
(
stderr_fd
);
}
return
null
;
}});
}
public
OutputStream
getOutputStream
()
{
...
...
@@ -150,13 +225,30 @@ final class ProcessImpl extends Process {
public
void
destroy
()
{
terminateProcess
(
handle
);
}
private
static
native
void
terminateProcess
(
long
handle
);
/**
* Create a process using the win32 function CreateProcess.
*
* @param cmdstr the Windows commandline
* @param envblock NUL-separated, double-NUL-terminated list of
* environment strings in VAR=VALUE form
* @param dir the working directory of the process, or null if
* inheriting the current directory from the parent process
* @param stdHandles array of windows HANDLEs. Indexes 0, 1, and
* 2 correspond to standard input, standard output and
* standard error, respectively. On input, a value of -1
* means to create a pipe to connect child and parent
* processes. On output, a value which is not -1 is the
* parent pipe handle corresponding to the pipe which has
* been created. An element of this array is -1 on input
* if and only if it is <em>not</em> -1 on output.
* @param redirectErrorStream redirectErrorStream attribute
* @return the native subprocess HANDLE returned by CreateProcess
*/
private
static
native
long
create
(
String
cmdstr
,
String
envblock
,
String
dir
,
boolean
redirectErrorStream
,
FileDescriptor
in_fd
,
FileDescriptor
out_fd
,
FileDescriptor
err_fd
)
long
[]
stdHandles
,
boolean
redirectErrorStream
)
throws
IOException
;
private
static
native
boolean
closeHandle
(
long
handle
);
...
...
src/windows/native/java/lang/ProcessImpl_md.c
浏览文件 @
095ce48d
...
...
@@ -125,7 +125,7 @@ win32Error(JNIEnv *env, const char *functionName)
static
void
closeSafely
(
HANDLE
handle
)
{
if
(
handle
)
if
(
handle
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
handle
);
}
...
...
@@ -134,23 +134,22 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
jstring
cmd
,
jstring
envBlock
,
jstring
dir
,
jboolean
redirectErrorStream
,
jobject
in_fd
,
jobject
out_fd
,
jobject
err_fd
)
jlongArray
stdHandles
,
jboolean
redirectErrorStream
)
{
HANDLE
inRead
=
0
;
HANDLE
inWrite
=
0
;
HANDLE
outRead
=
0
;
HANDLE
outWrite
=
0
;
HANDLE
errRead
=
0
;
HANDLE
errWrite
=
0
;
HANDLE
inRead
=
INVALID_HANDLE_VALUE
;
HANDLE
inWrite
=
INVALID_HANDLE_VALUE
;
HANDLE
outRead
=
INVALID_HANDLE_VALUE
;
HANDLE
outWrite
=
INVALID_HANDLE_VALUE
;
HANDLE
errRead
=
INVALID_HANDLE_VALUE
;
HANDLE
errWrite
=
INVALID_HANDLE_VALUE
;
SECURITY_ATTRIBUTES
sa
;
PROCESS_INFORMATION
pi
;
STARTUPINFO
si
;
LPTSTR
pcmd
=
NULL
;
LPCTSTR
pdir
=
NULL
;
LPVOID
penvBlock
=
NULL
;
jlong
*
handles
=
NULL
;
jlong
ret
=
0
;
OSVERSIONINFO
ver
;
jboolean
onNT
=
JNI_FALSE
;
...
...
@@ -161,17 +160,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
if
(
ver
.
dwPlatformId
==
VER_PLATFORM_WIN32_NT
)
onNT
=
JNI_TRUE
;
sa
.
nLength
=
sizeof
(
sa
);
sa
.
lpSecurityDescriptor
=
0
;
sa
.
bInheritHandle
=
TRUE
;
if
(
!
(
CreatePipe
(
&
inRead
,
&
inWrite
,
&
sa
,
PIPE_SIZE
)
&&
CreatePipe
(
&
outRead
,
&
outWrite
,
&
sa
,
PIPE_SIZE
)
&&
CreatePipe
(
&
errRead
,
&
errWrite
,
&
sa
,
PIPE_SIZE
)))
{
win32Error
(
env
,
"CreatePipe"
);
goto
Catch
;
}
assert
(
cmd
!=
NULL
);
pcmd
=
(
LPTSTR
)
JNU_GetStringPlatformChars
(
env
,
cmd
,
NULL
);
if
(
pcmd
==
NULL
)
goto
Catch
;
...
...
@@ -189,19 +177,62 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
if
(
penvBlock
==
NULL
)
goto
Catch
;
}
assert
(
stdHandles
!=
NULL
);
handles
=
(
*
env
)
->
GetLongArrayElements
(
env
,
stdHandles
,
NULL
);
if
(
handles
==
NULL
)
goto
Catch
;
memset
(
&
si
,
0
,
sizeof
(
si
));
si
.
cb
=
sizeof
(
si
);
si
.
dwFlags
=
STARTF_USESTDHANDLES
;
si
.
hStdInput
=
inRead
;
si
.
hStdOutput
=
outWrite
;
si
.
hStdError
=
redirectErrorStream
?
outWrite
:
errWrite
;
SetHandleInformation
(
inWrite
,
HANDLE_FLAG_INHERIT
,
FALSE
);
SetHandleInformation
(
outRead
,
HANDLE_FLAG_INHERIT
,
FALSE
)
;
SetHandleInformation
(
errRead
,
HANDLE_FLAG_INHERIT
,
FALSE
)
;
sa
.
nLength
=
sizeof
(
sa
);
sa
.
lpSecurityDescriptor
=
0
;
sa
.
bInheritHandle
=
TRUE
;
if
(
redirectErrorStream
)
SetHandleInformation
(
errWrite
,
HANDLE_FLAG_INHERIT
,
FALSE
);
if
(
handles
[
0
]
!=
(
jlong
)
-
1
)
{
si
.
hStdInput
=
(
HANDLE
)
handles
[
0
];
handles
[
0
]
=
(
jlong
)
-
1
;
}
else
{
if
(
!
CreatePipe
(
&
inRead
,
&
inWrite
,
&
sa
,
PIPE_SIZE
))
{
win32Error
(
env
,
"CreatePipe"
);
goto
Catch
;
}
si
.
hStdInput
=
inRead
;
SetHandleInformation
(
inWrite
,
HANDLE_FLAG_INHERIT
,
FALSE
);
handles
[
0
]
=
(
jlong
)
inWrite
;
}
SetHandleInformation
(
si
.
hStdInput
,
HANDLE_FLAG_INHERIT
,
TRUE
);
if
(
handles
[
1
]
!=
(
jlong
)
-
1
)
{
si
.
hStdOutput
=
(
HANDLE
)
handles
[
1
];
handles
[
1
]
=
(
jlong
)
-
1
;
}
else
{
if
(
!
CreatePipe
(
&
outRead
,
&
outWrite
,
&
sa
,
PIPE_SIZE
))
{
win32Error
(
env
,
"CreatePipe"
);
goto
Catch
;
}
si
.
hStdOutput
=
outWrite
;
SetHandleInformation
(
outRead
,
HANDLE_FLAG_INHERIT
,
FALSE
);
handles
[
1
]
=
(
jlong
)
outRead
;
}
SetHandleInformation
(
si
.
hStdOutput
,
HANDLE_FLAG_INHERIT
,
TRUE
);
if
(
redirectErrorStream
)
{
si
.
hStdError
=
si
.
hStdOutput
;
handles
[
2
]
=
(
jlong
)
-
1
;
}
else
if
(
handles
[
2
]
!=
(
jlong
)
-
1
)
{
si
.
hStdError
=
(
HANDLE
)
handles
[
2
];
handles
[
2
]
=
(
jlong
)
-
1
;
}
else
{
if
(
!
CreatePipe
(
&
errRead
,
&
errWrite
,
&
sa
,
PIPE_SIZE
))
{
win32Error
(
env
,
"CreatePipe"
);
goto
Catch
;
}
si
.
hStdError
=
errWrite
;
SetHandleInformation
(
errRead
,
HANDLE_FLAG_INHERIT
,
FALSE
);
handles
[
2
]
=
(
jlong
)
errRead
;
}
SetHandleInformation
(
si
.
hStdError
,
HANDLE_FLAG_INHERIT
,
TRUE
);
if
(
onNT
)
processFlag
=
CREATE_NO_WINDOW
|
CREATE_UNICODE_ENVIRONMENT
;
...
...
@@ -237,9 +268,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
CloseHandle
(
pi
.
hThread
);
ret
=
(
jlong
)
pi
.
hProcess
;
(
*
env
)
->
SetLongField
(
env
,
in_fd
,
IO_handle_fdID
,
(
jlong
)
inWrite
);
(
*
env
)
->
SetLongField
(
env
,
out_fd
,
IO_handle_fdID
,
(
jlong
)
outRead
);
(
*
env
)
->
SetLongField
(
env
,
err_fd
,
IO_handle_fdID
,
(
jlong
)
errRead
);
Finally:
/* Always clean up the child's side of the pipes */
...
...
@@ -257,6 +285,9 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
else
JNU_ReleaseStringPlatformChars
(
env
,
dir
,
(
char
*
)
penvBlock
);
}
if
(
handles
!=
NULL
)
(
*
env
)
->
ReleaseLongArrayElements
(
env
,
stdHandles
,
handles
,
0
);
return
ret
;
Catch:
...
...
test/java/lang/ProcessBuilder/Basic.java
浏览文件 @
095ce48d
...
...
@@ -25,12 +25,15 @@
* @test
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
* 6464154 6523983 6206031
* 6464154 6523983 6206031
4960438 6631352 6631966
* @summary Basic tests for Process and Environment Variable code
* @run main/othervm Basic
* @author Martin Buchholz
*/
import
java.lang.ProcessBuilder.Redirect
;
import
static
java
.
lang
.
ProcessBuilder
.
Redirect
.*;
import
java.io.*
;
import
java.util.*
;
import
java.security.*
;
...
...
@@ -257,7 +260,29 @@ public class Basic {
public
static
class
JavaChild
{
public
static
void
main
(
String
args
[])
throws
Throwable
{
String
action
=
args
[
0
];
if
(
action
.
equals
(
"System.getenv(String)"
))
{
if
(
action
.
equals
(
"testIO"
))
{
String
expected
=
"standard input"
;
char
[]
buf
=
new
char
[
expected
.
length
()+
1
];
int
n
=
new
InputStreamReader
(
System
.
in
).
read
(
buf
,
0
,
buf
.
length
);
if
(
n
!=
expected
.
length
())
System
.
exit
(
5
);
if
(!
new
String
(
buf
,
0
,
n
).
equals
(
expected
))
System
.
exit
(
5
);
System
.
err
.
print
(
"standard error"
);
System
.
out
.
print
(
"standard output"
);
}
else
if
(
action
.
equals
(
"testInheritIO"
))
{
List
<
String
>
childArgs
=
new
ArrayList
<
String
>(
javaChildArgs
);
childArgs
.
add
(
"testIO"
);
ProcessBuilder
pb
=
new
ProcessBuilder
(
childArgs
);
pb
.
inheritIO
();
ProcessResults
r
=
run
(
pb
);
if
(!
r
.
out
().
equals
(
""
))
System
.
exit
(
7
);
if
(!
r
.
err
().
equals
(
""
))
System
.
exit
(
8
);
if
(
r
.
exitValue
()
!=
0
)
System
.
exit
(
9
);
}
else
if
(
action
.
equals
(
"System.getenv(String)"
))
{
String
val
=
System
.
getenv
(
args
[
1
]);
printUTF8
(
val
==
null
?
"null"
:
val
);
}
else
if
(
action
.
equals
(
"System.getenv(\\u1234)"
))
{
...
...
@@ -599,6 +624,333 @@ public class Basic {
}
catch
(
Throwable
t
)
{
unexpected
(
t
);
}
}
static
void
checkRedirects
(
ProcessBuilder
pb
,
Redirect
in
,
Redirect
out
,
Redirect
err
)
{
equal
(
pb
.
redirectInput
(),
in
);
equal
(
pb
.
redirectOutput
(),
out
);
equal
(
pb
.
redirectError
(),
err
);
}
static
void
redirectIO
(
ProcessBuilder
pb
,
Redirect
in
,
Redirect
out
,
Redirect
err
)
{
pb
.
redirectInput
(
in
);
pb
.
redirectOutput
(
out
);
pb
.
redirectError
(
err
);
}
static
void
setFileContents
(
File
file
,
String
contents
)
{
try
{
Writer
w
=
new
FileWriter
(
file
);
w
.
write
(
contents
);
w
.
close
();
}
catch
(
Throwable
t
)
{
unexpected
(
t
);
}
}
static
String
fileContents
(
File
file
)
{
try
{
Reader
r
=
new
FileReader
(
file
);
StringBuilder
sb
=
new
StringBuilder
();
char
[]
buffer
=
new
char
[
1024
];
int
n
;
while
((
n
=
r
.
read
(
buffer
))
!=
-
1
)
sb
.
append
(
buffer
,
0
,
n
);
r
.
close
();
return
new
String
(
sb
);
}
catch
(
Throwable
t
)
{
unexpected
(
t
);
return
""
;
}
}
static
void
testIORedirection
()
throws
Throwable
{
final
File
ifile
=
new
File
(
"ifile"
);
final
File
ofile
=
new
File
(
"ofile"
);
final
File
efile
=
new
File
(
"efile"
);
ifile
.
delete
();
ofile
.
delete
();
efile
.
delete
();
//----------------------------------------------------------------
// Check mutual inequality of different types of Redirect
//----------------------------------------------------------------
Redirect
[]
redirects
=
{
PIPE
,
INHERIT
,
Redirect
.
from
(
ifile
),
Redirect
.
to
(
ifile
),
Redirect
.
appendTo
(
ifile
),
Redirect
.
from
(
ofile
),
Redirect
.
to
(
ofile
),
Redirect
.
appendTo
(
ofile
),
};
for
(
int
i
=
0
;
i
<
redirects
.
length
;
i
++)
for
(
int
j
=
0
;
j
<
redirects
.
length
;
j
++)
equal
(
redirects
[
i
].
equals
(
redirects
[
j
]),
(
i
==
j
));
//----------------------------------------------------------------
// Check basic properties of different types of Redirect
//----------------------------------------------------------------
equal
(
PIPE
.
type
(),
Redirect
.
Type
.
PIPE
);
equal
(
PIPE
.
toString
(),
"PIPE"
);
equal
(
PIPE
.
file
(),
null
);
equal
(
INHERIT
.
type
(),
Redirect
.
Type
.
INHERIT
);
equal
(
INHERIT
.
toString
(),
"INHERIT"
);
equal
(
INHERIT
.
file
(),
null
);
equal
(
Redirect
.
from
(
ifile
).
type
(),
Redirect
.
Type
.
READ
);
equal
(
Redirect
.
from
(
ifile
).
toString
(),
"redirect to read from file \"ifile\""
);
equal
(
Redirect
.
from
(
ifile
).
file
(),
ifile
);
equal
(
Redirect
.
from
(
ifile
),
Redirect
.
from
(
ifile
));
equal
(
Redirect
.
from
(
ifile
).
hashCode
(),
Redirect
.
from
(
ifile
).
hashCode
());
equal
(
Redirect
.
to
(
ofile
).
type
(),
Redirect
.
Type
.
WRITE
);
equal
(
Redirect
.
to
(
ofile
).
toString
(),
"redirect to write to file \"ofile\""
);
equal
(
Redirect
.
to
(
ofile
).
file
(),
ofile
);
equal
(
Redirect
.
to
(
ofile
),
Redirect
.
to
(
ofile
));
equal
(
Redirect
.
to
(
ofile
).
hashCode
(),
Redirect
.
to
(
ofile
).
hashCode
());
equal
(
Redirect
.
appendTo
(
ofile
).
type
(),
Redirect
.
Type
.
APPEND
);
equal
(
Redirect
.
appendTo
(
efile
).
toString
(),
"redirect to append to file \"efile\""
);
equal
(
Redirect
.
appendTo
(
efile
).
file
(),
efile
);
equal
(
Redirect
.
appendTo
(
efile
),
Redirect
.
appendTo
(
efile
));
equal
(
Redirect
.
appendTo
(
efile
).
hashCode
(),
Redirect
.
appendTo
(
efile
).
hashCode
());
//----------------------------------------------------------------
// Check initial values of redirects
//----------------------------------------------------------------
List
<
String
>
childArgs
=
new
ArrayList
<
String
>(
javaChildArgs
);
childArgs
.
add
(
"testIO"
);
final
ProcessBuilder
pb
=
new
ProcessBuilder
(
childArgs
);
checkRedirects
(
pb
,
PIPE
,
PIPE
,
PIPE
);
//----------------------------------------------------------------
// Check inheritIO
//----------------------------------------------------------------
pb
.
inheritIO
();
checkRedirects
(
pb
,
INHERIT
,
INHERIT
,
INHERIT
);
//----------------------------------------------------------------
// Check setters and getters agree
//----------------------------------------------------------------
pb
.
redirectInput
(
ifile
);
equal
(
pb
.
redirectInput
().
file
(),
ifile
);
equal
(
pb
.
redirectInput
(),
Redirect
.
from
(
ifile
));
pb
.
redirectOutput
(
ofile
);
equal
(
pb
.
redirectOutput
().
file
(),
ofile
);
equal
(
pb
.
redirectOutput
(),
Redirect
.
to
(
ofile
));
pb
.
redirectError
(
efile
);
equal
(
pb
.
redirectError
().
file
(),
efile
);
equal
(
pb
.
redirectError
(),
Redirect
.
to
(
efile
));
THROWS
(
IllegalArgumentException
.
class
,
new
Fun
(){
void
f
()
{
pb
.
redirectInput
(
Redirect
.
to
(
ofile
));
}},
new
Fun
(){
void
f
()
{
pb
.
redirectInput
(
Redirect
.
appendTo
(
ofile
));
}},
new
Fun
(){
void
f
()
{
pb
.
redirectOutput
(
Redirect
.
from
(
ifile
));
}},
new
Fun
(){
void
f
()
{
pb
.
redirectError
(
Redirect
.
from
(
ifile
));
}});
THROWS
(
IOException
.
class
,
// Input file does not exist
new
Fun
(){
void
f
()
throws
Throwable
{
pb
.
start
();
}});
setFileContents
(
ifile
,
"standard input"
);
//----------------------------------------------------------------
// Writing to non-existent files
//----------------------------------------------------------------
{
ProcessResults
r
=
run
(
pb
);
equal
(
r
.
exitValue
(),
0
);
equal
(
fileContents
(
ofile
),
"standard output"
);
equal
(
fileContents
(
efile
),
"standard error"
);
equal
(
r
.
out
(),
""
);
equal
(
r
.
err
(),
""
);
ofile
.
delete
();
efile
.
delete
();
}
//----------------------------------------------------------------
// Both redirectErrorStream + redirectError
//----------------------------------------------------------------
{
pb
.
redirectErrorStream
(
true
);
ProcessResults
r
=
run
(
pb
);
equal
(
r
.
exitValue
(),
0
);
equal
(
fileContents
(
ofile
),
"standard error"
+
"standard output"
);
equal
(
fileContents
(
efile
),
""
);
equal
(
r
.
out
(),
""
);
equal
(
r
.
err
(),
""
);
ofile
.
delete
();
efile
.
delete
();
}
//----------------------------------------------------------------
// Appending to existing files
//----------------------------------------------------------------
{
setFileContents
(
ofile
,
"ofile-contents"
);
setFileContents
(
efile
,
"efile-contents"
);
pb
.
redirectOutput
(
Redirect
.
appendTo
(
ofile
));
pb
.
redirectError
(
Redirect
.
appendTo
(
efile
));
pb
.
redirectErrorStream
(
false
);
ProcessResults
r
=
run
(
pb
);
equal
(
r
.
exitValue
(),
0
);
equal
(
fileContents
(
ofile
),
"ofile-contents"
+
"standard output"
);
equal
(
fileContents
(
efile
),
"efile-contents"
+
"standard error"
);
equal
(
r
.
out
(),
""
);
equal
(
r
.
err
(),
""
);
ofile
.
delete
();
efile
.
delete
();
}
//----------------------------------------------------------------
// Replacing existing files
//----------------------------------------------------------------
{
setFileContents
(
ofile
,
"ofile-contents"
);
setFileContents
(
efile
,
"efile-contents"
);
pb
.
redirectOutput
(
ofile
);
pb
.
redirectError
(
Redirect
.
to
(
efile
));
ProcessResults
r
=
run
(
pb
);
equal
(
r
.
exitValue
(),
0
);
equal
(
fileContents
(
ofile
),
"standard output"
);
equal
(
fileContents
(
efile
),
"standard error"
);
equal
(
r
.
out
(),
""
);
equal
(
r
.
err
(),
""
);
ofile
.
delete
();
efile
.
delete
();
}
//----------------------------------------------------------------
// Appending twice to the same file?
//----------------------------------------------------------------
{
setFileContents
(
ofile
,
"ofile-contents"
);
setFileContents
(
efile
,
"efile-contents"
);
Redirect
appender
=
Redirect
.
appendTo
(
ofile
);
pb
.
redirectOutput
(
appender
);
pb
.
redirectError
(
appender
);
ProcessResults
r
=
run
(
pb
);
equal
(
r
.
exitValue
(),
0
);
equal
(
fileContents
(
ofile
),
"ofile-contents"
+
"standard error"
+
"standard output"
);
equal
(
fileContents
(
efile
),
"efile-contents"
);
equal
(
r
.
out
(),
""
);
equal
(
r
.
err
(),
""
);
ifile
.
delete
();
ofile
.
delete
();
efile
.
delete
();
}
//----------------------------------------------------------------
// Testing INHERIT is harder.
// Note that this requires __FOUR__ nested JVMs involved in one test,
// if you count the harness JVM.
//----------------------------------------------------------------
{
redirectIO
(
pb
,
PIPE
,
PIPE
,
PIPE
);
List
<
String
>
command
=
pb
.
command
();
command
.
set
(
command
.
size
()
-
1
,
"testInheritIO"
);
Process
p
=
pb
.
start
();
new
PrintStream
(
p
.
getOutputStream
()).
print
(
"standard input"
);
p
.
getOutputStream
().
close
();
ProcessResults
r
=
run
(
p
);
equal
(
r
.
exitValue
(),
0
);
equal
(
r
.
out
(),
"standard output"
);
equal
(
r
.
err
(),
"standard error"
);
}
//----------------------------------------------------------------
// Test security implications of I/O redirection
//----------------------------------------------------------------
// Read access to current directory is always granted;
// So create a tmpfile for input instead.
final
File
tmpFile
=
File
.
createTempFile
(
"Basic"
,
"tmp"
);
setFileContents
(
tmpFile
,
"standard input"
);
final
Policy
policy
=
new
Policy
();
Policy
.
setPolicy
(
policy
);
System
.
setSecurityManager
(
new
SecurityManager
());
try
{
final
Permission
xPermission
=
new
FilePermission
(
"<<ALL FILES>>"
,
"execute"
);
final
Permission
rxPermission
=
new
FilePermission
(
"<<ALL FILES>>"
,
"read,execute"
);
final
Permission
wxPermission
=
new
FilePermission
(
"<<ALL FILES>>"
,
"write,execute"
);
final
Permission
rwxPermission
=
new
FilePermission
(
"<<ALL FILES>>"
,
"read,write,execute"
);
THROWS
(
SecurityException
.
class
,
new
Fun
()
{
void
f
()
throws
IOException
{
policy
.
setPermissions
(
xPermission
);
redirectIO
(
pb
,
from
(
tmpFile
),
PIPE
,
PIPE
);
pb
.
start
();}},
new
Fun
()
{
void
f
()
throws
IOException
{
policy
.
setPermissions
(
rxPermission
);
redirectIO
(
pb
,
PIPE
,
to
(
ofile
),
PIPE
);
pb
.
start
();}},
new
Fun
()
{
void
f
()
throws
IOException
{
policy
.
setPermissions
(
rxPermission
);
redirectIO
(
pb
,
PIPE
,
PIPE
,
to
(
efile
));
pb
.
start
();}});
{
policy
.
setPermissions
(
rxPermission
);
redirectIO
(
pb
,
from
(
tmpFile
),
PIPE
,
PIPE
);
ProcessResults
r
=
run
(
pb
);
equal
(
r
.
out
(),
"standard output"
);
equal
(
r
.
err
(),
"standard error"
);
}
{
policy
.
setPermissions
(
wxPermission
);
redirectIO
(
pb
,
PIPE
,
to
(
ofile
),
to
(
efile
));
Process
p
=
pb
.
start
();
new
PrintStream
(
p
.
getOutputStream
()).
print
(
"standard input"
);
p
.
getOutputStream
().
close
();
ProcessResults
r
=
run
(
p
);
policy
.
setPermissions
(
rwxPermission
);
equal
(
fileContents
(
ofile
),
"standard output"
);
equal
(
fileContents
(
efile
),
"standard error"
);
}
{
policy
.
setPermissions
(
rwxPermission
);
redirectIO
(
pb
,
from
(
tmpFile
),
to
(
ofile
),
to
(
efile
));
ProcessResults
r
=
run
(
pb
);
policy
.
setPermissions
(
rwxPermission
);
equal
(
fileContents
(
ofile
),
"standard output"
);
equal
(
fileContents
(
efile
),
"standard error"
);
}
}
finally
{
policy
.
setPermissions
(
new
RuntimePermission
(
"setSecurityManager"
));
System
.
setSecurityManager
(
null
);
tmpFile
.
delete
();
ifile
.
delete
();
ofile
.
delete
();
efile
.
delete
();
}
}
private
static
void
realMain
(
String
[]
args
)
throws
Throwable
{
if
(
Windows
.
is
())
System
.
out
.
println
(
"This appears to be a Windows system."
);
...
...
@@ -607,6 +959,9 @@ public class Basic {
if
(
UnicodeOS
.
is
())
System
.
out
.
println
(
"This appears to be a Unicode-based OS."
);
try
{
testIORedirection
();
}
catch
(
Throwable
t
)
{
unexpected
(
t
);
}
//----------------------------------------------------------------
// Basic tests for setting, replacing and deleting envvars
//----------------------------------------------------------------
...
...
@@ -1354,7 +1709,8 @@ public class Basic {
execPermission
);
ProcessBuilder
pb
=
new
ProcessBuilder
(
"env"
);
pb
.
environment
().
put
(
"foo"
,
"bar"
);
pb
.
start
();
Process
p
=
pb
.
start
();
closeStreams
(
p
);
}
catch
(
IOException
e
)
{
// OK
}
catch
(
Throwable
t
)
{
unexpected
(
t
);
}
...
...
@@ -1378,6 +1734,14 @@ public class Basic {
}
static
void
closeStreams
(
Process
p
)
{
try
{
p
.
getOutputStream
().
close
();
p
.
getInputStream
().
close
();
p
.
getErrorStream
().
close
();
}
catch
(
Throwable
t
)
{
unexpected
(
t
);
}
}
//----------------------------------------------------------------
// A Policy class designed to make permissions fiddling very easy.
//----------------------------------------------------------------
...
...
@@ -1432,10 +1796,19 @@ public class Basic {
}
}
catch
(
Throwable
t
)
{
throwable
=
t
;
}
finally
{
try
{
is
.
close
();
}
catch
(
Throwable
t
)
{
throwable
=
t
;
}
}
}
}
static
ProcessResults
run
(
ProcessBuilder
pb
)
{
try
{
return
run
(
pb
.
start
());
}
catch
(
Throwable
t
)
{
unexpected
(
t
);
return
null
;
}
}
private
static
ProcessResults
run
(
Process
p
)
{
Throwable
throwable
=
null
;
int
exitValue
=
-
1
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录