mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			350 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			350 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
| // natWin32Process.cc - Native side of Win32 process code.
 | |
| 
 | |
| /* Copyright (C) 2003  Free Software Foundation
 | |
| 
 | |
|    This file is part of libgcj.
 | |
| 
 | |
| This software is copyrighted work licensed under the terms of the
 | |
| Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
 | |
| details.  */
 | |
| 
 | |
| #include <config.h>
 | |
| #include <platform.h>
 | |
| 
 | |
| // Conflicts with the definition in "java/lang/reflect/Modifier.h"
 | |
| #undef STRICT
 | |
| 
 | |
| #include <java/lang/ConcreteProcess.h>
 | |
| #include <java/lang/IllegalThreadStateException.h>
 | |
| #include <java/lang/InterruptedException.h>
 | |
| #include <java/lang/NullPointerException.h>
 | |
| #include <java/lang/Thread.h>
 | |
| #include <java/io/File.h>
 | |
| #include <java/io/FileDescriptor.h>
 | |
| #include <java/io/FileInputStream.h>
 | |
| #include <java/io/FileOutputStream.h>
 | |
| #include <java/io/IOException.h>
 | |
| #include <java/lang/OutOfMemoryError.h>
 | |
| #include <gnu/java/nio/channels/FileChannelImpl.h>
 | |
| 
 | |
| using gnu::java::nio::channels::FileChannelImpl;
 | |
| 
 | |
| void
 | |
| java::lang::ConcreteProcess::cleanup (void)
 | |
| {
 | |
|   // FIXME:
 | |
|   // We used to close the input, output and
 | |
|   // error streams here, but we can't do that
 | |
|   // because the caller also has the right
 | |
|   // to close these and FileInputStream and FileOutputStream
 | |
|   // scream if you attempt to close() them twice. Presently,
 | |
|   // we use _Jv_platform_close_on_exec, which is similar
 | |
|   // to the POSIX approach.
 | |
|   //
 | |
|   // What I wanted to do is have private nested
 | |
|   // classes in ConcreteProcess which extend FileInputStream
 | |
|   // and FileOutputStream, respectively, but override
 | |
|   // close() to permit multiple calls to close(). This
 | |
|   // led to class header and platform configury issues
 | |
|   // that I didn't feel like dealing with. However,
 | |
|   // this approach could conceivably be a good multiplatform
 | |
|   // one since delaying the pipe close until process
 | |
|   // termination could be wasteful if many child processes
 | |
|   // are spawned within the parent process' lifetime.
 | |
|   inputStream = NULL;
 | |
|   outputStream = NULL;
 | |
|   errorStream = NULL;
 | |
|   
 | |
|   if (procHandle)
 | |
|     {
 | |
|       CloseHandle((HANDLE) procHandle);
 | |
|       procHandle = (jint) INVALID_HANDLE_VALUE;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| java::lang::ConcreteProcess::destroy (void)
 | |
| {
 | |
|   if (! hasExited ())
 | |
|     {
 | |
|       // Kill it forcibly and assign an (arbitrary) exit code of 0.
 | |
|       TerminateProcess ((HANDLE) procHandle, 0);
 | |
|       exitCode = 0;
 | |
| 
 | |
|       cleanup ();
 | |
|     }
 | |
| }
 | |
| 
 | |
| jboolean
 | |
| java::lang::ConcreteProcess::hasExited (void)
 | |
| {
 | |
|   DWORD exitStatus;
 | |
| 
 | |
|   if (GetExitCodeProcess ((HANDLE) procHandle, &exitStatus) != 0)
 | |
|     {
 | |
|       // NOTE: STILL_ACTIVE is defined as "259" by Win32 - if the
 | |
|       // child actually exits with this return code, we have a
 | |
|       // problem here. See MSDN documentation on GetExitCodeProcess( ).
 | |
| 
 | |
|       if (exitStatus == STILL_ACTIVE)
 | |
|         return false;
 | |
|       else
 | |
|         {
 | |
|           cleanup ();
 | |
|           exitCode = exitStatus;
 | |
|           return true;
 | |
|         }
 | |
|     }
 | |
|   else
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| jint
 | |
| java::lang::ConcreteProcess::waitFor (void)
 | |
| {
 | |
|   if (! hasExited ())
 | |
|     {
 | |
|       DWORD exitStatus = 0UL;
 | |
| 
 | |
|       // Set up our waitable objects array
 | |
|       // - 0: the handle to the process we just launched
 | |
|       // - 1: our thread's interrupt event
 | |
|       HANDLE arh[2];
 | |
|       arh[0] = (HANDLE) procHandle;
 | |
|       arh[1] = _Jv_Win32GetInterruptEvent ();
 | |
|       DWORD rval = WaitForMultipleObjects (2, arh, 0, INFINITE);
 | |
| 
 | |
|       // Use the returned value from WaitForMultipleObjects
 | |
|       // instead of our thread's interrupt_flag to test for
 | |
|       // thread interruption. See the comment for
 | |
|       // _Jv_Win32GetInterruptEvent().
 | |
|       bool bInterrupted = rval == (WAIT_OBJECT_0 + 1);
 | |
|       
 | |
|       if (bInterrupted)
 | |
|         {
 | |
|           // Querying this forces a reset our thread's interrupt flag.
 | |
|           Thread::interrupted();
 | |
|           
 | |
|           cleanup ();
 | |
|           throw new InterruptedException ();
 | |
|         }
 | |
| 
 | |
|       GetExitCodeProcess ((HANDLE) procHandle, &exitStatus);
 | |
|       exitCode = exitStatus;
 | |
| 
 | |
|       cleanup ();
 | |
|     }
 | |
| 
 | |
|   return exitCode;
 | |
| }
 | |
| 
 | |
| 
 | |
| // Helper class for creating and managing the pipes
 | |
| // used for I/O redirection for child processes.
 | |
| class ChildProcessPipe
 | |
| {
 | |
| public:
 | |
|   // Indicates from the child process' point of view
 | |
|   // whether the pipe is for reading or writing.
 | |
|   enum EType {INPUT, OUTPUT};
 | |
| 
 | |
|   ChildProcessPipe(EType eType);
 | |
|   ~ChildProcessPipe();
 | |
|   
 | |
|   // Returns a pipe handle suitable for use by the parent process
 | |
|   HANDLE getParentHandle();
 | |
|   
 | |
|   // Returns a pipe handle suitable for use by the child process.
 | |
|   HANDLE getChildHandle();
 | |
|   
 | |
| private:
 | |
|   EType m_eType;
 | |
|   HANDLE m_hRead, m_hWrite;
 | |
| };
 | |
| 
 | |
| ChildProcessPipe::ChildProcessPipe(EType eType):
 | |
|   m_eType(eType)
 | |
| {
 | |
|   SECURITY_ATTRIBUTES sAttrs;
 | |
| 
 | |
|   // Explicitly allow the handles to the pipes to be inherited.
 | |
|   sAttrs.nLength = sizeof (SECURITY_ATTRIBUTES);
 | |
|   sAttrs.bInheritHandle = 1;
 | |
|   sAttrs.lpSecurityDescriptor = NULL;
 | |
| 
 | |
|   if (CreatePipe (&m_hRead, &m_hWrite, &sAttrs, 0) == 0)
 | |
|     {
 | |
|       DWORD dwErrorCode = GetLastError ();
 | |
|       throw new java::io::IOException (
 | |
|         _Jv_WinStrError (_T("Error creating pipe"), dwErrorCode));
 | |
|     }
 | |
| 
 | |
|   // If this is the read end of the child, we need
 | |
|   // to make the parent write end non-inheritable. Similarly,
 | |
|   // if this is the write end of the child, we need to make
 | |
|   // the parent read end non-inheritable. If we didn't
 | |
|   // do this, the child would inherit these ends and we wouldn't
 | |
|   // be able to close them from our end. For full details,
 | |
|   // do a Google search on "Q190351".
 | |
|   HANDLE& rhStd = m_eType==INPUT ? m_hWrite : m_hRead;
 | |
|   _Jv_platform_close_on_exec (rhStd);
 | |
| }
 | |
| 
 | |
| ChildProcessPipe::~ChildProcessPipe()
 | |
| {
 | |
|   // Close the parent end of the pipe. This
 | |
|   // destructor is called after the child process
 | |
|   // has been spawned.
 | |
|   CloseHandle(getChildHandle());
 | |
| }
 | |
| 
 | |
| HANDLE ChildProcessPipe::getParentHandle()
 | |
| {
 | |
|   return m_eType==INPUT ? m_hWrite : m_hRead;
 | |
| }
 | |
| 
 | |
| HANDLE ChildProcessPipe::getChildHandle()
 | |
| {
 | |
|   return m_eType==INPUT ? m_hRead : m_hWrite;
 | |
| }
 | |
| 
 | |
| void
 | |
| java::lang::ConcreteProcess::startProcess (jstringArray progarray,
 | |
|                                            jstringArray envp,
 | |
|                                            java::io::File *dir)
 | |
| {
 | |
|   using namespace java::io;
 | |
| 
 | |
|   procHandle = (jint) INVALID_HANDLE_VALUE;
 | |
| 
 | |
|   // Reconstruct the command line.
 | |
|   jstring *elts = elements (progarray);
 | |
| 
 | |
|   int cmdLineLen = 0;
 | |
| 
 | |
|   for (int i = 0; i < progarray->length; ++i)
 | |
|     cmdLineLen += (elts[i]->length() + 1);
 | |
| 
 | |
|   LPTSTR cmdLine = (LPTSTR) _Jv_Malloc ((cmdLineLen + 1) * sizeof(TCHAR));
 | |
|   LPTSTR cmdLineCurPos = cmdLine;
 | |
| 
 | |
|   for (int i = 0; i < progarray->length; ++i)
 | |
|     {
 | |
|       if (i > 0)
 | |
|         *cmdLineCurPos++ = _T(' ');
 | |
|         
 | |
|       jint len = elts[i]->length();
 | |
|       JV_TEMP_STRING_WIN32(thiselt, elts[i]);
 | |
|       _tcscpy(cmdLineCurPos, thiselt);
 | |
|       cmdLineCurPos += len;
 | |
|     }
 | |
|   *cmdLineCurPos = _T('\0');
 | |
| 
 | |
|   // Get the environment, if any.
 | |
|   LPTSTR env = NULL;
 | |
|   if (envp)
 | |
|     {
 | |
|       elts = elements (envp);
 | |
| 
 | |
|       int envLen = 0;
 | |
|       for (int i = 0; i < envp->length; ++i)
 | |
|         envLen += (elts[i]->length() + 1);
 | |
| 
 | |
|       env = (LPTSTR) _Jv_Malloc ((envLen + 1) * sizeof(TCHAR));
 | |
| 
 | |
|       int j = 0;
 | |
|       for (int i = 0; i < envp->length; ++i)
 | |
|         {
 | |
|           jint len = elts[i]->length();
 | |
|           
 | |
|           JV_TEMP_STRING_WIN32(thiselt, elts[i]);
 | |
|           _tcscpy(env + j, thiselt);
 | |
|           
 | |
|           j += len;
 | |
|           
 | |
|           // Skip past the null terminator that _tcscpy just inserted.
 | |
|           j++;
 | |
|         }
 | |
|       *(env + j) = _T('\0');
 | |
|     }
 | |
| 
 | |
|   // Get the working directory path, if specified.
 | |
|   JV_TEMP_STRING_WIN32 (wdir, dir ? dir->getPath () : 0);
 | |
| 
 | |
|   errorStream = NULL;
 | |
|   inputStream = NULL;
 | |
|   outputStream = NULL;
 | |
| 
 | |
|   java::lang::Throwable *exc = NULL;
 | |
| 
 | |
|   try
 | |
|     {
 | |
|       // We create anonymous pipes to communicate with the child
 | |
|       // on each of standard streams.
 | |
|       ChildProcessPipe aChildStdIn(ChildProcessPipe::INPUT);
 | |
|       ChildProcessPipe aChildStdOut(ChildProcessPipe::OUTPUT);
 | |
|       ChildProcessPipe aChildStdErr(ChildProcessPipe::OUTPUT);
 | |
| 
 | |
|       outputStream = new FileOutputStream (new FileChannelImpl (
 | |
|                            (jint) aChildStdIn.getParentHandle (),
 | |
| 			   FileChannelImpl::WRITE));
 | |
|       inputStream = new FileInputStream (new FileChannelImpl (
 | |
|                            (jint) aChildStdOut.getParentHandle (),
 | |
| 			   FileChannelImpl::READ));
 | |
|       errorStream = new FileInputStream (new FileChannelImpl (
 | |
|                            (jint) aChildStdErr.getParentHandle (),
 | |
| 			   FileChannelImpl::READ));
 | |
| 
 | |
|       // Now create the child process.
 | |
|       PROCESS_INFORMATION pi;
 | |
|       STARTUPINFO si;
 | |
| 
 | |
|       ZeroMemory (&pi, sizeof (PROCESS_INFORMATION));
 | |
| 
 | |
|       ZeroMemory (&si, sizeof (STARTUPINFO));
 | |
|       si.cb = sizeof (STARTUPINFO);
 | |
| 
 | |
|       // Explicitly specify the handles to the standard streams.
 | |
|       si.dwFlags |= STARTF_USESTDHANDLES;
 | |
| 
 | |
|       si.hStdInput = aChildStdIn.getChildHandle();
 | |
|       si.hStdOutput = aChildStdOut.getChildHandle();
 | |
|       si.hStdError = aChildStdErr.getChildHandle();
 | |
| 
 | |
|       // Spawn the process. CREATE_NO_WINDOW only applies when
 | |
|       // starting a console application; it suppresses the
 | |
|       // creation of a console window. This flag is ignored on
 | |
|       // Win9X.
 | |
|       
 | |
|       if (CreateProcess (NULL,
 | |
|                          cmdLine,
 | |
|                          NULL,
 | |
|                          NULL,
 | |
|                          1,
 | |
|                          CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
 | |
|                          env,
 | |
|                          wdir,
 | |
|                          &si,
 | |
|                          &pi) == 0)
 | |
|         {
 | |
|           DWORD dwErrorCode = GetLastError ();
 | |
|           throw new IOException (
 | |
|             _Jv_WinStrError (_T("Error creating child process"), dwErrorCode));
 | |
|         }
 | |
| 
 | |
|       procHandle = (jint ) pi.hProcess;
 | |
| 
 | |
|       _Jv_Free (cmdLine);
 | |
|       if (env != NULL)
 | |
|         _Jv_Free (env);
 | |
|     }
 | |
|   catch (java::lang::Throwable *thrown)
 | |
|     {
 | |
|       cleanup ();
 | |
|       exc = thrown;
 | |
|     }
 | |
| 
 | |
|   if (exc != NULL)
 | |
|     throw exc;
 | |
| }
 |