0e7f60d6cadba9789f25738f8a3f87fd08d223ef
[gcc.git] / libjava / java / lang / natWin32Process.cc
1 // natWin32Process.cc - Native side of Win32 process code.
2
3 /* Copyright (C) 2003, 2006, 2007 Free Software Foundation
4
5 This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9 details. */
10
11 #include <config.h>
12 #include <platform.h>
13
14 // Conflicts with the definition in "java/lang/reflect/Modifier.h"
15 #undef STRICT
16
17 #include <java/lang/Win32Process.h>
18 #include <java/lang/IllegalThreadStateException.h>
19 #include <java/lang/InterruptedException.h>
20 #include <java/lang/NullPointerException.h>
21 #include <java/lang/Thread.h>
22 #include <java/io/File.h>
23 #include <java/io/FileDescriptor.h>
24 #include <java/io/FileInputStream.h>
25 #include <java/io/FileOutputStream.h>
26 #include <java/io/IOException.h>
27 #include <java/lang/OutOfMemoryError.h>
28 #include <java/lang/Win32Process$EOFInputStream.h>
29 #include <gnu/java/nio/channels/FileChannelImpl.h>
30
31 using gnu::java::nio::channels::FileChannelImpl;
32
33 void
34 java::lang::Win32Process::cleanup (void)
35 {
36 // FIXME:
37 // We used to close the input, output and
38 // error streams here, but we can't do that
39 // because the caller also has the right
40 // to close these and FileInputStream and FileOutputStream
41 // scream if you attempt to close() them twice. Presently,
42 // we use _Jv_platform_close_on_exec, which is similar
43 // to the POSIX approach.
44 //
45 // What I wanted to do is have private nested
46 // classes in Win32Process which extend FileInputStream
47 // and FileOutputStream, respectively, but override
48 // close() to permit multiple calls to close(). This
49 // led to class header and platform configury issues
50 // that I didn't feel like dealing with. However,
51 // this approach could conceivably be a good multiplatform
52 // one since delaying the pipe close until process
53 // termination could be wasteful if many child processes
54 // are spawned within the parent process' lifetime.
55 inputStream = NULL;
56 outputStream = NULL;
57 errorStream = NULL;
58
59 if (procHandle)
60 {
61 CloseHandle((HANDLE) procHandle);
62 procHandle = (jint) INVALID_HANDLE_VALUE;
63 }
64 }
65
66 void
67 java::lang::Win32Process::destroy (void)
68 {
69 if (! hasExited ())
70 {
71 // Kill it forcibly and assign an (arbitrary) exit code of 0.
72 TerminateProcess ((HANDLE) procHandle, 0);
73 exitCode = 0;
74
75 cleanup ();
76 }
77 }
78
79 jboolean
80 java::lang::Win32Process::hasExited (void)
81 {
82 DWORD exitStatus;
83
84 if (GetExitCodeProcess ((HANDLE) procHandle, &exitStatus) != 0)
85 {
86 // NOTE: STILL_ACTIVE is defined as "259" by Win32 - if the
87 // child actually exits with this return code, we have a
88 // problem here. See MSDN documentation on GetExitCodeProcess( ).
89
90 if (exitStatus == STILL_ACTIVE)
91 return false;
92 else
93 {
94 cleanup ();
95 exitCode = exitStatus;
96 return true;
97 }
98 }
99 else
100 return true;
101 }
102
103 jint
104 java::lang::Win32Process::waitFor (void)
105 {
106 if (! hasExited ())
107 {
108 DWORD exitStatus = 0UL;
109
110 // Set up our waitable objects array
111 // - 0: the handle to the process we just launched
112 // - 1: our thread's interrupt event
113 HANDLE arh[2];
114 arh[0] = (HANDLE) procHandle;
115 arh[1] = _Jv_Win32GetInterruptEvent ();
116 DWORD rval = WaitForMultipleObjects (2, arh, 0, INFINITE);
117
118 // Use the returned value from WaitForMultipleObjects
119 // instead of our thread's interrupt_flag to test for
120 // thread interruption. See the comment for
121 // _Jv_Win32GetInterruptEvent().
122 bool bInterrupted = rval == (WAIT_OBJECT_0 + 1);
123
124 if (bInterrupted)
125 {
126 // Querying this forces a reset our thread's interrupt flag.
127 Thread::interrupted();
128
129 cleanup ();
130 throw new InterruptedException ();
131 }
132
133 GetExitCodeProcess ((HANDLE) procHandle, &exitStatus);
134 exitCode = exitStatus;
135
136 cleanup ();
137 }
138
139 return exitCode;
140 }
141
142
143 // Helper class for creating and managing the pipes
144 // used for I/O redirection for child processes.
145 class ChildProcessPipe
146 {
147 public:
148 // Indicates from the child process' point of view
149 // whether the pipe is for reading or writing.
150 enum EType {INPUT, OUTPUT, DUMMY};
151
152 ChildProcessPipe(EType eType);
153 ~ChildProcessPipe();
154
155 // Returns a pipe handle suitable for use by the parent process
156 HANDLE getParentHandle();
157
158 // Returns a pipe handle suitable for use by the child process.
159 HANDLE getChildHandle();
160
161 private:
162 EType m_eType;
163 HANDLE m_hRead, m_hWrite;
164 };
165
166 ChildProcessPipe::ChildProcessPipe(EType eType):
167 m_eType(eType), m_hRead(0), m_hWrite(0)
168 {
169 if (eType == DUMMY)
170 return;
171
172 SECURITY_ATTRIBUTES sAttrs;
173
174 // Explicitly allow the handles to the pipes to be inherited.
175 sAttrs.nLength = sizeof (SECURITY_ATTRIBUTES);
176 sAttrs.bInheritHandle = 1;
177 sAttrs.lpSecurityDescriptor = NULL;
178
179 if (CreatePipe (&m_hRead, &m_hWrite, &sAttrs, 0) == 0)
180 {
181 DWORD dwErrorCode = GetLastError ();
182 throw new java::io::IOException (
183 _Jv_WinStrError (_T("Error creating pipe"), dwErrorCode));
184 }
185
186 // If this is the read end of the child, we need
187 // to make the parent write end non-inheritable. Similarly,
188 // if this is the write end of the child, we need to make
189 // the parent read end non-inheritable. If we didn't
190 // do this, the child would inherit these ends and we wouldn't
191 // be able to close them from our end. For full details,
192 // do a Google search on "Q190351".
193 HANDLE& rhStd = m_eType==INPUT ? m_hWrite : m_hRead;
194 _Jv_platform_close_on_exec (rhStd);
195 }
196
197 ChildProcessPipe::~ChildProcessPipe()
198 {
199 // Close the parent end of the pipe. This
200 // destructor is called after the child process
201 // has been spawned.
202 if (m_eType != DUMMY)
203 CloseHandle(getChildHandle());
204 }
205
206 HANDLE ChildProcessPipe::getParentHandle()
207 {
208 return m_eType==INPUT ? m_hWrite : m_hRead;
209 }
210
211 HANDLE ChildProcessPipe::getChildHandle()
212 {
213 return m_eType==INPUT ? m_hRead : m_hWrite;
214 }
215
216 void
217 java::lang::Win32Process::startProcess (jstringArray progarray,
218 jstringArray envp,
219 java::io::File *dir,
220 jboolean redirect)
221 {
222 using namespace java::io;
223
224 procHandle = (jint) INVALID_HANDLE_VALUE;
225
226 // Reconstruct the command line.
227 jstring *elts = elements (progarray);
228
229 int cmdLineLen = 0;
230
231 for (int i = 0; i < progarray->length; ++i)
232 cmdLineLen += (elts[i]->length() + 1);
233
234 LPTSTR cmdLine = (LPTSTR) _Jv_Malloc ((cmdLineLen + 1) * sizeof(TCHAR));
235 LPTSTR cmdLineCurPos = cmdLine;
236
237 for (int i = 0; i < progarray->length; ++i)
238 {
239 if (i > 0)
240 *cmdLineCurPos++ = _T(' ');
241
242 jint len = elts[i]->length();
243 JV_TEMP_STRING_WIN32(thiselt, elts[i]);
244 _tcscpy(cmdLineCurPos, thiselt);
245 cmdLineCurPos += len;
246 }
247 *cmdLineCurPos = _T('\0');
248
249 // Get the environment, if any.
250 LPTSTR env = NULL;
251 if (envp)
252 {
253 elts = elements (envp);
254
255 int envLen = 0;
256 for (int i = 0; i < envp->length; ++i)
257 envLen += (elts[i]->length() + 1);
258
259 env = (LPTSTR) _Jv_Malloc ((envLen + 1) * sizeof(TCHAR));
260
261 int j = 0;
262 for (int i = 0; i < envp->length; ++i)
263 {
264 jint len = elts[i]->length();
265
266 JV_TEMP_STRING_WIN32(thiselt, elts[i]);
267 _tcscpy(env + j, thiselt);
268
269 j += len;
270
271 // Skip past the null terminator that _tcscpy just inserted.
272 j++;
273 }
274 *(env + j) = _T('\0');
275 }
276
277 // Get the working directory path, if specified.
278 JV_TEMP_STRING_WIN32 (wdir, dir ? dir->getPath () : 0);
279
280 errorStream = NULL;
281 inputStream = NULL;
282 outputStream = NULL;
283
284 java::lang::Throwable *exc = NULL;
285
286 try
287 {
288 // We create anonymous pipes to communicate with the child
289 // on each of standard streams.
290 ChildProcessPipe aChildStdIn(ChildProcessPipe::INPUT);
291 ChildProcessPipe aChildStdOut(ChildProcessPipe::OUTPUT);
292 ChildProcessPipe aChildStdErr(redirect ? ChildProcessPipe::DUMMY
293 : ChildProcessPipe::OUTPUT);
294
295 outputStream = new FileOutputStream (new FileChannelImpl (
296 (jint) aChildStdIn.getParentHandle (),
297 FileChannelImpl::WRITE));
298 inputStream = new FileInputStream (new FileChannelImpl (
299 (jint) aChildStdOut.getParentHandle (),
300 FileChannelImpl::READ));
301 if (redirect)
302 errorStream = Win32Process$EOFInputStream::instance;
303 else
304 errorStream = new FileInputStream (new FileChannelImpl (
305 (jint) aChildStdErr.getParentHandle (),
306 FileChannelImpl::READ));
307
308 // Now create the child process.
309 PROCESS_INFORMATION pi;
310 STARTUPINFO si;
311
312 ZeroMemory (&pi, sizeof (PROCESS_INFORMATION));
313
314 ZeroMemory (&si, sizeof (STARTUPINFO));
315 si.cb = sizeof (STARTUPINFO);
316
317 // Explicitly specify the handles to the standard streams.
318 si.dwFlags |= STARTF_USESTDHANDLES;
319
320 si.hStdInput = aChildStdIn.getChildHandle();
321 si.hStdOutput = aChildStdOut.getChildHandle();
322 si.hStdError = redirect ? aChildStdOut.getChildHandle()
323 : aChildStdErr.getChildHandle();
324
325 // Spawn the process. CREATE_NO_WINDOW only applies when
326 // starting a console application; it suppresses the
327 // creation of a console window. This flag is ignored on
328 // Win9X.
329
330 if (CreateProcess (NULL,
331 cmdLine,
332 NULL,
333 NULL,
334 1,
335 CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
336 env,
337 wdir,
338 &si,
339 &pi) == 0)
340 {
341 DWORD dwErrorCode = GetLastError ();
342 throw new IOException (
343 _Jv_WinStrError (_T("Error creating child process"), dwErrorCode));
344 }
345
346 procHandle = (jint ) pi.hProcess;
347
348 _Jv_Free (cmdLine);
349 if (env != NULL)
350 _Jv_Free (env);
351 }
352 catch (java::lang::Throwable *thrown)
353 {
354 cleanup ();
355 exc = thrown;
356 }
357
358 if (exc != NULL)
359 throw exc;
360 }