OSDN Git Service

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