blob: 3c1a4f02c8bbdec03bff02caa85d51083410e372 [file] [log] [blame]
// 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;
}