2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / lang / natWin32Process.cc
blob7337ab34969caf63d5c26d80541b8cc64124b0fd
1 // natWin32Process.cc - Native side of Win32 process code.
3 /* Copyright (C) 2003 Free Software Foundation
5 This file is part of libgcj.
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9 details. */
11 #include <config.h>
12 #include <platform.h>
14 // Conflicts with the definition in "java/lang/reflect/Modifier.h"
15 #undef STRICT
17 #include <java/lang/ConcreteProcess.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>
29 void
30 java::lang::ConcreteProcess::cleanup (void)
32 // FIXME:
33 // We used to close the input, output and
34 // error streams here, but we can't do that
35 // because the caller also has the right
36 // to close these and FileInputStream and FileOutputStream
37 // scream if you attempt to close() them twice. Presently,
38 // we use _Jv_platform_close_on_exec, which is similar
39 // to the POSIX approach.
41 // What I wanted to do is have private nested
42 // classes in ConcreteProcess which extend FileInputStream
43 // and FileOutputStream, respectively, but override
44 // close() to permit multiple calls to close(). This
45 // led to class header and platform configury issues
46 // that I didn't feel like dealing with. However,
47 // this approach could conceivably be a good multiplatform
48 // one since delaying the pipe close until process
49 // termination could be wasteful if many child processes
50 // are spawned within the parent process' lifetime.
51 inputStream = NULL;
52 outputStream = NULL;
53 errorStream = NULL;
55 if (procHandle)
57 CloseHandle((HANDLE) procHandle);
58 procHandle = (jint) INVALID_HANDLE_VALUE;
62 void
63 java::lang::ConcreteProcess::destroy (void)
65 if (! hasExited ())
67 // Kill it forcibly and assign an (arbitrary) exit code of 0.
68 TerminateProcess ((HANDLE) procHandle, 0);
69 exitCode = 0;
71 cleanup ();
75 jboolean
76 java::lang::ConcreteProcess::hasExited (void)
78 DWORD exitStatus;
80 if (GetExitCodeProcess ((HANDLE) procHandle, &exitStatus) != 0)
82 // NOTE: STILL_ACTIVE is defined as "259" by Win32 - if the
83 // child actually exits with this return code, we have a
84 // problem here. See MSDN documentation on GetExitCodeProcess( ).
86 if (exitStatus == STILL_ACTIVE)
87 return false;
88 else
90 cleanup ();
91 exitCode = exitStatus;
92 return true;
95 else
96 return true;
99 jint
100 java::lang::ConcreteProcess::waitFor (void)
102 if (! hasExited ())
104 DWORD exitStatus = 0UL;
106 // Set up our waitable objects array
107 // - 0: the handle to the process we just launched
108 // - 1: our thread's interrupt event
109 HANDLE arh[2];
110 arh[0] = (HANDLE) procHandle;
111 arh[1] = _Jv_Win32GetInterruptEvent ();
112 DWORD rval = WaitForMultipleObjects (2, arh, 0, INFINITE);
114 // Use the returned value from WaitForMultipleObjects
115 // instead of our thread's interrupt_flag to test for
116 // thread interruption. See the comment for
117 // _Jv_Win32GetInterruptEvent().
118 bool bInterrupted = rval == (WAIT_OBJECT_0 + 1);
120 if (bInterrupted)
122 // Querying this forces a reset our thread's interrupt flag.
123 Thread::interrupted();
125 cleanup ();
126 throw new InterruptedException ();
129 GetExitCodeProcess ((HANDLE) procHandle, &exitStatus);
130 exitCode = exitStatus;
132 cleanup ();
135 return exitCode;
139 // Helper class for creating and managing the pipes
140 // used for I/O redirection for child processes.
141 class ChildProcessPipe
143 public:
144 // Indicates from the child process' point of view
145 // whether the pipe is for reading or writing.
146 enum EType {INPUT, OUTPUT};
148 ChildProcessPipe(EType eType);
149 ~ChildProcessPipe();
151 // Returns a pipe handle suitable for use by the parent process
152 HANDLE getParentHandle();
154 // Returns a pipe handle suitable for use by the child process.
155 HANDLE getChildHandle();
157 private:
158 EType m_eType;
159 HANDLE m_hRead, m_hWrite;
162 ChildProcessPipe::ChildProcessPipe(EType eType):
163 m_eType(eType)
165 SECURITY_ATTRIBUTES sAttrs;
167 // Explicitly allow the handles to the pipes to be inherited.
168 sAttrs.nLength = sizeof (SECURITY_ATTRIBUTES);
169 sAttrs.bInheritHandle = 1;
170 sAttrs.lpSecurityDescriptor = NULL;
172 if (CreatePipe (&m_hRead, &m_hWrite, &sAttrs, 0) == 0)
174 DWORD dwErrorCode = GetLastError ();
175 throw new java::io::IOException (
176 _Jv_WinStrError (_T("Error creating pipe"), dwErrorCode));
179 // If this is the read end of the child, we need
180 // to make the parent write end non-inheritable. Similarly,
181 // if this is the write end of the child, we need to make
182 // the parent read end non-inheritable. If we didn't
183 // do this, the child would inherit these ends and we wouldn't
184 // be able to close them from our end. For full details,
185 // do a Google search on "Q190351".
186 HANDLE& rhStd = m_eType==INPUT ? m_hWrite : m_hRead;
187 _Jv_platform_close_on_exec (rhStd);
190 ChildProcessPipe::~ChildProcessPipe()
192 // Close the parent end of the pipe. This
193 // destructor is called after the child process
194 // has been spawned.
195 CloseHandle(getChildHandle());
198 HANDLE ChildProcessPipe::getParentHandle()
200 return m_eType==INPUT ? m_hWrite : m_hRead;
203 HANDLE ChildProcessPipe::getChildHandle()
205 return m_eType==INPUT ? m_hRead : m_hWrite;
208 void
209 java::lang::ConcreteProcess::startProcess (jstringArray progarray,
210 jstringArray envp,
211 java::io::File *dir)
213 using namespace java::io;
215 procHandle = (jint) INVALID_HANDLE_VALUE;
217 // Reconstruct the command line.
218 jstring *elts = elements (progarray);
220 int cmdLineLen = 0;
222 for (int i = 0; i < progarray->length; ++i)
223 cmdLineLen += (elts[i]->length() + 1);
225 LPTSTR cmdLine = (LPTSTR) _Jv_Malloc ((cmdLineLen + 1) * sizeof(TCHAR));
226 LPTSTR cmdLineCurPos = cmdLine;
228 for (int i = 0; i < progarray->length; ++i)
230 if (i > 0)
231 *cmdLineCurPos++ = _T(' ');
233 jint len = elts[i]->length();
234 JV_TEMP_STRING_WIN32(thiselt, elts[i]);
235 _tcscpy(cmdLineCurPos, thiselt);
236 cmdLineCurPos += len;
238 *cmdLineCurPos = _T('\0');
240 // Get the environment, if any.
241 LPTSTR env = NULL;
242 if (envp)
244 elts = elements (envp);
246 int envLen = 0;
247 for (int i = 0; i < envp->length; ++i)
248 envLen += (elts[i]->length() + 1);
250 env = (LPTSTR) _Jv_Malloc ((envLen + 1) * sizeof(TCHAR));
252 int j = 0;
253 for (int i = 0; i < envp->length; ++i)
255 jint len = elts[i]->length();
257 JV_TEMP_STRING_WIN32(thiselt, elts[i]);
258 _tcscpy(env + j, thiselt);
260 j += len;
262 // Skip past the null terminator that _tcscpy just inserted.
263 j++;
265 *(env + j) = _T('\0');
268 // Get the working directory path, if specified.
269 JV_TEMP_STRING_WIN32 (wdir, dir ? dir->getPath () : 0);
271 errorStream = NULL;
272 inputStream = NULL;
273 outputStream = NULL;
275 java::lang::Throwable *exc = NULL;
279 // We create anonymous pipes to communicate with the child
280 // on each of standard streams.
281 ChildProcessPipe aChildStdIn(ChildProcessPipe::INPUT);
282 ChildProcessPipe aChildStdOut(ChildProcessPipe::OUTPUT);
283 ChildProcessPipe aChildStdErr(ChildProcessPipe::OUTPUT);
285 outputStream = new FileOutputStream (new FileDescriptor (
286 (jint) aChildStdIn.getParentHandle ()));
287 inputStream = new FileInputStream (new FileDescriptor (
288 (jint) aChildStdOut.getParentHandle ()));
289 errorStream = new FileInputStream (new FileDescriptor (
290 (jint) aChildStdErr.getParentHandle ()));
292 // Now create the child process.
293 PROCESS_INFORMATION pi;
294 STARTUPINFO si;
296 ZeroMemory (&pi, sizeof (PROCESS_INFORMATION));
298 ZeroMemory (&si, sizeof (STARTUPINFO));
299 si.cb = sizeof (STARTUPINFO);
301 // Explicitly specify the handles to the standard streams.
302 si.dwFlags |= STARTF_USESTDHANDLES;
304 si.hStdInput = aChildStdIn.getChildHandle();
305 si.hStdOutput = aChildStdOut.getChildHandle();
306 si.hStdError = aChildStdErr.getChildHandle();
308 // Spawn the process. CREATE_NO_WINDOW only applies when
309 // starting a console application; it suppresses the
310 // creation of a console window. This flag is ignored on
311 // Win9X.
313 if (CreateProcess (NULL,
314 cmdLine,
315 NULL,
316 NULL,
318 CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
319 env,
320 wdir,
321 &si,
322 &pi) == 0)
324 DWORD dwErrorCode = GetLastError ();
325 throw new IOException (
326 _Jv_WinStrError (_T("Error creating child process"), dwErrorCode));
329 procHandle = (jint ) pi.hProcess;
331 _Jv_Free (cmdLine);
332 if (env != NULL)
333 _Jv_Free (env);
335 catch (java::lang::Throwable *thrown)
337 cleanup ();
338 exc = thrown;
341 if (exc != NULL)
342 throw exc;