| // 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; |
| } |