1 /*
2 * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package java.lang;
27
28 import java.io.IOException;
29 import java.io.File;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.FileInputStream;
33 import java.io.FileOutputStream;
34 import java.io.FileDescriptor;
35 import java.io.BufferedInputStream;
36 import java.io.BufferedOutputStream;
37 import java.lang.ProcessBuilder.Redirect;
38
39 /* This class is for the exclusive use of ProcessBuilder.start() to
40 * create new processes.
41 *
42 * @author Martin Buchholz
43 * @since 1.5
44 */
45
46 final class ProcessImpl extends Process {
47 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
48 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
49
50 // System-dependent portion of ProcessBuilder.start()
51 static Process start(String cmdarray[],
52 java.util.Map<String,String> environment,
53 String dir,
54 ProcessBuilder.Redirect[] redirects,
55 boolean redirectErrorStream)
56 throws IOException
57 {
58 String envblock = ProcessEnvironment.toEnvironmentBlock(environment);
59
60 FileInputStream f0 = null;
61 FileOutputStream f1 = null;
62 FileOutputStream f2 = null;
63
64 try {
65 long[] stdHandles;
66 if (redirects == null) {
67 stdHandles = new long[] { -1L, -1L, -1L };
68 } else {
69 stdHandles = new long[3];
70
71 if (redirects[0] == Redirect.PIPE)
72 stdHandles[0] = -1L;
73 else if (redirects[0] == Redirect.INHERIT)
74 stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
75 else {
76 f0 = new FileInputStream(redirects[0].file());
77 stdHandles[0] = fdAccess.getHandle(f0.getFD());
78 }
79
80 if (redirects[1] == Redirect.PIPE)
81 stdHandles[1] = -1L;
82 else if (redirects[1] == Redirect.INHERIT)
83 stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
84 else {
85 f1 = redirects[1].toFileOutputStream();
86 stdHandles[1] = fdAccess.getHandle(f1.getFD());
87 }
88
89 if (redirects[2] == Redirect.PIPE)
90 stdHandles[2] = -1L;
91 else if (redirects[2] == Redirect.INHERIT)
92 stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
93 else {
94 f2 = redirects[2].toFileOutputStream();
95 stdHandles[2] = fdAccess.getHandle(f2.getFD());
96 }
97 }
98
99 return new ProcessImpl(cmdarray, envblock, dir,
100 stdHandles, redirectErrorStream);
101 } finally {
102 // In theory, close() can throw IOException
103 // (although it is rather unlikely to happen here)
104 try { if (f0 != null) f0.close(); }
105 finally {
106 try { if (f1 != null) f1.close(); }
107 finally { if (f2 != null) f2.close(); }
108 }
109 }
110
111 }
112
113 private long handle = 0;
114 private OutputStream stdin_stream;
115 private InputStream stdout_stream;
116 private InputStream stderr_stream;
117
118 private ProcessImpl(final String cmd[],
119 final String envblock,
120 final String path,
121 final long[] stdHandles,
122 final boolean redirectErrorStream)
123 throws IOException
124 {
125 // Win32 CreateProcess requires cmd[0] to be normalized
126 cmd[0] = new File(cmd[0]).getPath();
127
128 StringBuilder cmdbuf = new StringBuilder(80);
129 for (int i = 0; i < cmd.length; i++) {
130 if (i > 0) {
131 cmdbuf.append(' ');
132 }
133 String s = cmd[i];
134 if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0) {
135 if (s.charAt(0) != '"') {
136 cmdbuf.append('"');
137 cmdbuf.append(s);
138 if (s.endsWith("\\")) {
139 cmdbuf.append("\\");
140 }
141 cmdbuf.append('"');
142 } else if (s.endsWith("\"")) {
143 /* The argument has already been quoted. */
144 cmdbuf.append(s);
145 } else {
146 /* Unmatched quote for the argument. */
147 throw new IllegalArgumentException();
148 }
149 } else {
150 cmdbuf.append(s);
151 }
152 }
153 String cmdstr = cmdbuf.toString();
154
155 handle = create(cmdstr, envblock, path,
156 stdHandles, redirectErrorStream);
157
158 java.security.AccessController.doPrivileged(
159 new java.security.PrivilegedAction<Void>() {
160 public Void run() {
161 if (stdHandles[0] == -1L)
162 stdin_stream = new ProcessBuilder.NullOutputStream();
163 else {
164 FileDescriptor stdin_fd = new FileDescriptor();
165 fdAccess.setHandle(stdin_fd, stdHandles[0]);
166 stdin_stream = new BufferedOutputStream(
167 new FileOutputStream(stdin_fd));
168 }
169
170 if (stdHandles[1] == -1L)
171 stdout_stream = new ProcessBuilder.NullInputStream();
172 else {
173 FileDescriptor stdout_fd = new FileDescriptor();
174 fdAccess.setHandle(stdout_fd, stdHandles[1]);
175 stdout_stream = new BufferedInputStream(
176 new FileInputStream(stdout_fd));
177 }
178
179 if (stdHandles[2] == -1L)
180 stderr_stream = new ProcessBuilder.NullInputStream();
181 else {
182 FileDescriptor stderr_fd = new FileDescriptor();
183 fdAccess.setHandle(stderr_fd, stdHandles[2]);
184 stderr_stream = new FileInputStream(stderr_fd);
185 }
186
187 return null; }});
188 }
189
190 public OutputStream getOutputStream() {
191 return stdin_stream;
192 }
193
194 public InputStream getInputStream() {
195 return stdout_stream;
196 }
197
198 public InputStream getErrorStream() {
199 return stderr_stream;
200 }
201
202 public void finalize() {
203 closeHandle(handle);
204 }
205
206 private static final int STILL_ACTIVE = getStillActive();
207 private static native int getStillActive();
208
209 public int exitValue() {
210 int exitCode = getExitCodeProcess(handle);
211 if (exitCode == STILL_ACTIVE)
212 throw new IllegalThreadStateException("process has not exited");
213 return exitCode;
214 }
215 private static native int getExitCodeProcess(long handle);
216
217 public int waitFor() throws InterruptedException {
218 waitForInterruptibly(handle);
219 if (Thread.interrupted())
220 throw new InterruptedException();
221 return exitValue();
222 }
223 private static native void waitForInterruptibly(long handle);
224
225 public void destroy() { terminateProcess(handle); }
226 private static native void terminateProcess(long handle);
227
228 /**
229 * Create a process using the win32 function CreateProcess.
230 *
231 * @param cmdstr the Windows commandline
232 * @param envblock NUL-separated, double-NUL-terminated list of
233 * environment strings in VAR=VALUE form
234 * @param dir the working directory of the process, or null if
235 * inheriting the current directory from the parent process
236 * @param stdHandles array of windows HANDLEs. Indexes 0, 1, and
237 * 2 correspond to standard input, standard output and
238 * standard error, respectively. On input, a value of -1
239 * means to create a pipe to connect child and parent
240 * processes. On output, a value which is not -1 is the
241 * parent pipe handle corresponding to the pipe which has
242 * been created. An element of this array is -1 on input
243 * if and only if it is <em>not</em> -1 on output.
244 * @param redirectErrorStream redirectErrorStream attribute
245 * @return the native subprocess HANDLE returned by CreateProcess
246 */
247 private static native long create(String cmdstr,
248 String envblock,
249 String dir,
250 long[] stdHandles,
251 boolean redirectErrorStream)
252 throws IOException;
253
254 private static native boolean closeHandle(long handle);
255 }