natWin32Process.cc 9.96 KB
Newer Older
1 2
// natWin32Process.cc - Native side of Win32 process code.

Tom Tromey committed
3
/* Copyright (C) 2003, 2006, 2007  Free Software Foundation
4 5 6 7 8 9 10 11

   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>
Mohan Embar committed
12
#include <platform.h>
13 14 15 16

// Conflicts with the definition in "java/lang/reflect/Modifier.h"
#undef STRICT

17
#include <java/lang/Win32Process.h>
18 19 20 21 22 23 24 25 26 27
#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>
28
#include <java/lang/Win32Process$EOFInputStream.h>
29 30 31
#include <gnu/java/nio/channels/FileChannelImpl.h>

using gnu::java::nio::channels::FileChannelImpl;
32 33

void
34
java::lang::Win32Process::cleanup (void)
35
{
36 37 38 39 40 41 42 43 44 45
  // 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
46
  // classes in Win32Process which extend FileInputStream
47 48 49 50 51 52 53 54 55 56 57 58
  // 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;
  
59 60 61 62 63
  if (procHandle)
    {
      CloseHandle((HANDLE) procHandle);
      procHandle = (jint) INVALID_HANDLE_VALUE;
    }
64 65 66
}

void
67
java::lang::Win32Process::destroy (void)
68 69 70 71 72 73 74 75 76 77 78 79
{
  if (! hasExited ())
    {
      // Kill it forcibly and assign an (arbitrary) exit code of 0.
      TerminateProcess ((HANDLE) procHandle, 0);
      exitCode = 0;

      cleanup ();
    }
}

jboolean
80
java::lang::Win32Process::hasExited (void)
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
{
  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
104
java::lang::Win32Process::waitFor (void)
105 106 107 108 109
{
  if (! hasExited ())
    {
      DWORD exitStatus = 0UL;

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
      // 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 ();
        }
132 133 134 135 136 137 138 139 140 141

      GetExitCodeProcess ((HANDLE) procHandle, &exitStatus);
      exitCode = exitStatus;

      cleanup ();
    }

  return exitCode;
}

142 143 144 145 146 147 148 149

// 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.
150
  enum EType {INPUT, OUTPUT, DUMMY};
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

  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):
167
  m_eType(eType), m_hRead(0), m_hWrite(0)
168
{
169 170 171
  if (eType == DUMMY)
    return;
  
172 173 174 175 176 177 178 179 180 181 182
  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 (
183
        _Jv_WinStrError (_T("Error creating pipe"), dwErrorCode));
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
    }

  // 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.
202 203
  if (m_eType != DUMMY)
    CloseHandle(getChildHandle());
204 205 206 207 208 209 210 211 212 213 214 215
}

HANDLE ChildProcessPipe::getParentHandle()
{
  return m_eType==INPUT ? m_hWrite : m_hRead;
}

HANDLE ChildProcessPipe::getChildHandle()
{
  return m_eType==INPUT ? m_hRead : m_hWrite;
}

216
void
217
java::lang::Win32Process::startProcess (jstringArray progarray,
Tom Tromey committed
218 219 220
					jstringArray envp,
					java::io::File *dir,
					jboolean redirect)
221 222 223 224 225 226 227 228 229 230 231
{
  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)
232
    cmdLineLen += (elts[i]->length() + 1);
233

234 235
  LPTSTR cmdLine = (LPTSTR) _Jv_Malloc ((cmdLineLen + 1) * sizeof(TCHAR));
  LPTSTR cmdLineCurPos = cmdLine;
236 237 238

  for (int i = 0; i < progarray->length; ++i)
    {
239
      if (i > 0)
240 241 242 243 244 245
        *cmdLineCurPos++ = _T(' ');
        
      jint len = elts[i]->length();
      JV_TEMP_STRING_WIN32(thiselt, elts[i]);
      _tcscpy(cmdLineCurPos, thiselt);
      cmdLineCurPos += len;
246
    }
247
  *cmdLineCurPos = _T('\0');
248 249

  // Get the environment, if any.
250
  LPTSTR env = NULL;
251 252 253 254 255 256
  if (envp)
    {
      elts = elements (envp);

      int envLen = 0;
      for (int i = 0; i < envp->length; ++i)
257
        envLen += (elts[i]->length() + 1);
258

259
      env = (LPTSTR) _Jv_Malloc ((envLen + 1) * sizeof(TCHAR));
260 261 262 263

      int j = 0;
      for (int i = 0; i < envp->length; ++i)
        {
264 265 266 267 268 269 270 271
          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.
272 273
          j++;
        }
274
      *(env + j) = _T('\0');
275 276 277
    }

  // Get the working directory path, if specified.
278
  JV_TEMP_STRING_WIN32 (wdir, dir ? dir->getPath () : 0);
279 280 281 282 283 284 285 286 287 288 289

  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.
290 291
      ChildProcessPipe aChildStdIn(ChildProcessPipe::INPUT);
      ChildProcessPipe aChildStdOut(ChildProcessPipe::OUTPUT);
292 293
      ChildProcessPipe aChildStdErr(redirect ? ChildProcessPipe::DUMMY
				    : ChildProcessPipe::OUTPUT);
294

295 296 297 298 299 300
      outputStream = new FileOutputStream (new FileChannelImpl (
                           (jint) aChildStdIn.getParentHandle (),
			   FileChannelImpl::WRITE));
      inputStream = new FileInputStream (new FileChannelImpl (
                           (jint) aChildStdOut.getParentHandle (),
			   FileChannelImpl::READ));
301 302 303 304
      if (redirect)
        errorStream = Win32Process$EOFInputStream::instance;
      else
        errorStream = new FileInputStream (new FileChannelImpl (
305 306
                           (jint) aChildStdErr.getParentHandle (),
			   FileChannelImpl::READ));
307 308 309 310 311 312 313 314 315 316 317 318 319

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

320 321
      si.hStdInput = aChildStdIn.getChildHandle();
      si.hStdOutput = aChildStdOut.getChildHandle();
322 323
      si.hStdError = redirect ? aChildStdOut.getChildHandle()
                              : aChildStdErr.getChildHandle();
324

325 326 327 328
      // 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.
329
      
330 331 332 333 334
      if (CreateProcess (NULL,
                         cmdLine,
                         NULL,
                         NULL,
                         1,
335
                         CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
336 337 338 339 340
                         env,
                         wdir,
                         &si,
                         &pi) == 0)
        {
Mohan Embar committed
341 342
          DWORD dwErrorCode = GetLastError ();
          throw new IOException (
343
            _Jv_WinStrError (_T("Error creating child process"), dwErrorCode));
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
        }

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