Blame src/w32-io.c

Packit d7e8d0
/* w32-io.c - W32 API I/O functions.
Packit d7e8d0
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit d7e8d0
   Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, write to the Free Software
Packit d7e8d0
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
Packit d7e8d0
   02111-1307, USA.  */
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <stdio.h>
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#include <assert.h>
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
#include <fcntl.h>
Packit d7e8d0
#ifdef HAVE_SYS_TIME_H
Packit d7e8d0
# include <sys/time.h>
Packit d7e8d0
#endif
Packit d7e8d0
#ifdef HAVE_SYS_TYPES_H
Packit d7e8d0
# include <sys/types.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <io.h>
Packit d7e8d0
Packit d7e8d0
#include "util.h"
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
#include <assuan.h>
Packit d7e8d0
#include <winioctl.h>
Packit d7e8d0
#define GPGCEDEV_IOCTL_UNBLOCK                                        \
Packit d7e8d0
  CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS)
Packit d7e8d0
#define GPGCEDEV_IOCTL_ASSIGN_RVID                                    \
Packit d7e8d0
  CTL_CODE (FILE_DEVICE_STREAMS, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS)
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include "sema.h"
Packit d7e8d0
#include "priv-io.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
#include "sys-util.h"
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* FIXME: Optimize.  */
Packit d7e8d0
#define MAX_SLAFD 512
Packit d7e8d0
Packit d7e8d0
static struct
Packit d7e8d0
{
Packit d7e8d0
  int used;
Packit d7e8d0
Packit d7e8d0
  /* If this is not INVALID_HANDLE_VALUE, then it's a handle.  */
Packit d7e8d0
  HANDLE handle;
Packit d7e8d0
Packit d7e8d0
  /* If this is not INVALID_SOCKET, then it's a Windows socket.  */
Packit d7e8d0
  int socket;
Packit d7e8d0
Packit d7e8d0
  /* If this is not 0, then it's a rendezvous ID for the pipe server.  */
Packit d7e8d0
  int rvid;
Packit d7e8d0
Packit d7e8d0
  /* DUP_FROM is -1 if this file descriptor was allocated by pipe or
Packit d7e8d0
     socket functions.  Only then should the handle or socket be
Packit d7e8d0
     destroyed when this FD is closed.  This, together with the fact
Packit d7e8d0
     that dup'ed file descriptors are closed before the file
Packit d7e8d0
     descriptors from which they are dup'ed are closed, ensures that
Packit d7e8d0
     the handle or socket is always valid, and shared among all file
Packit d7e8d0
     descriptors referring to the same underlying object.
Packit d7e8d0
Packit d7e8d0
     The logic behind this is that there is only one reason for us to
Packit d7e8d0
     dup file descriptors anyway: to allow simpler book-keeping of
Packit d7e8d0
     file descriptors shared between GPGME and libassuan, which both
Packit d7e8d0
     want to close something.  Using the same handle for these
Packit d7e8d0
     duplicates works just fine.  */
Packit d7e8d0
  int dup_from;
Packit d7e8d0
} fd_table[MAX_SLAFD];
Packit d7e8d0
DEFINE_STATIC_LOCK (fd_table_lock);
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Returns the FD or -1 on resource limit.  */
Packit d7e8d0
int
Packit d7e8d0
new_fd (void)
Packit d7e8d0
{
Packit d7e8d0
  int idx;
Packit d7e8d0
Packit d7e8d0
  LOCK (fd_table_lock);
Packit d7e8d0
Packit d7e8d0
  for (idx = 0; idx < MAX_SLAFD; idx++)
Packit d7e8d0
    if (! fd_table[idx].used)
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
  if (idx == MAX_SLAFD)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      idx = -1;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      fd_table[idx].used = 1;
Packit d7e8d0
      fd_table[idx].handle = INVALID_HANDLE_VALUE;
Packit d7e8d0
      fd_table[idx].socket = INVALID_SOCKET;
Packit d7e8d0
      fd_table[idx].rvid = 0;
Packit d7e8d0
      fd_table[idx].dup_from = -1;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  UNLOCK (fd_table_lock);
Packit d7e8d0
Packit d7e8d0
  return idx;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
void
Packit d7e8d0
release_fd (int fd)
Packit d7e8d0
{
Packit d7e8d0
  if (fd < 0 || fd >= MAX_SLAFD)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  LOCK (fd_table_lock);
Packit d7e8d0
Packit d7e8d0
  if (fd_table[fd].used)
Packit d7e8d0
    {
Packit d7e8d0
      fd_table[fd].used = 0;
Packit d7e8d0
      fd_table[fd].handle = INVALID_HANDLE_VALUE;
Packit d7e8d0
      fd_table[fd].socket = INVALID_SOCKET;
Packit d7e8d0
      fd_table[fd].rvid = 0;
Packit d7e8d0
      fd_table[fd].dup_from = -1;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  UNLOCK (fd_table_lock);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
#define handle_to_fd(a)  ((int)(a))
Packit d7e8d0
Packit d7e8d0
#define READBUF_SIZE 4096
Packit d7e8d0
#define WRITEBUF_SIZE 4096
Packit d7e8d0
#define PIPEBUF_SIZE  4096
Packit d7e8d0
#define MAX_READERS 64
Packit d7e8d0
#define MAX_WRITERS 64
Packit d7e8d0
Packit d7e8d0
static struct
Packit d7e8d0
{
Packit d7e8d0
  int inuse;
Packit d7e8d0
  int fd;
Packit d7e8d0
  _gpgme_close_notify_handler_t handler;
Packit d7e8d0
  void *value;
Packit d7e8d0
} notify_table[MAX_SLAFD];
Packit d7e8d0
DEFINE_STATIC_LOCK (notify_table_lock);
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
struct reader_context_s
Packit d7e8d0
{
Packit d7e8d0
  HANDLE file_hd;
Packit d7e8d0
  int file_sock;
Packit d7e8d0
  HANDLE thread_hd;
Packit d7e8d0
  int refcount;
Packit d7e8d0
Packit d7e8d0
  DECLARE_LOCK (mutex);
Packit d7e8d0
Packit d7e8d0
  int stop_me;
Packit d7e8d0
  int eof;
Packit d7e8d0
  int eof_shortcut;
Packit d7e8d0
  int error;
Packit d7e8d0
  int error_code;
Packit d7e8d0
Packit d7e8d0
  /* This is manually reset.  */
Packit d7e8d0
  HANDLE have_data_ev;
Packit d7e8d0
  /* This is automatically reset.  */
Packit d7e8d0
  HANDLE have_space_ev;
Packit d7e8d0
  /* This is manually reset but actually only triggered once.  */
Packit d7e8d0
  HANDLE close_ev;
Packit d7e8d0
Packit d7e8d0
  size_t readpos, writepos;
Packit d7e8d0
  char buffer[READBUF_SIZE];
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static struct
Packit d7e8d0
{
Packit d7e8d0
  volatile int used;
Packit d7e8d0
  int fd;
Packit d7e8d0
  struct reader_context_s *context;
Packit d7e8d0
} reader_table[MAX_READERS];
Packit d7e8d0
static int reader_table_size= MAX_READERS;
Packit d7e8d0
DEFINE_STATIC_LOCK (reader_table_lock);
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
struct writer_context_s
Packit d7e8d0
{
Packit d7e8d0
  HANDLE file_hd;
Packit d7e8d0
  int file_sock;
Packit d7e8d0
  HANDLE thread_hd;
Packit d7e8d0
  int refcount;
Packit d7e8d0
Packit d7e8d0
  DECLARE_LOCK (mutex);
Packit d7e8d0
Packit d7e8d0
  int stop_me;
Packit d7e8d0
  int error;
Packit d7e8d0
  int error_code;
Packit d7e8d0
Packit d7e8d0
  /* This is manually reset.  */
Packit d7e8d0
  HANDLE have_data;
Packit d7e8d0
  HANDLE is_empty;
Packit d7e8d0
  HANDLE close_ev;
Packit d7e8d0
  size_t nbytes;
Packit d7e8d0
  char buffer[WRITEBUF_SIZE];
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static struct
Packit d7e8d0
{
Packit d7e8d0
  volatile int used;
Packit d7e8d0
  int fd;
Packit d7e8d0
  struct writer_context_s *context;
Packit d7e8d0
} writer_table[MAX_WRITERS];
Packit d7e8d0
static int writer_table_size= MAX_WRITERS;
Packit d7e8d0
DEFINE_STATIC_LOCK (writer_table_lock);
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static int
Packit d7e8d0
get_desired_thread_priority (void)
Packit d7e8d0
{
Packit d7e8d0
  int value;
Packit d7e8d0
Packit d7e8d0
  if (!_gpgme_get_conf_int ("IOThreadPriority", &value))
Packit d7e8d0
    {
Packit d7e8d0
      value = THREAD_PRIORITY_HIGHEST;
Packit d7e8d0
      TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
Packit d7e8d0
	      "%d (default)", value);
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
Packit d7e8d0
	      "%d (configured)", value);
Packit d7e8d0
    }
Packit d7e8d0
  return value;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static HANDLE
Packit d7e8d0
set_synchronize (HANDLE hd)
Packit d7e8d0
{
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  return hd;
Packit d7e8d0
#else
Packit d7e8d0
  HANDLE new_hd;
Packit d7e8d0
Packit d7e8d0
  /* For NT we have to set the sync flag.  It seems that the only way
Packit d7e8d0
     to do it is by duplicating the handle.  Tsss...  */
Packit d7e8d0
  if (!DuplicateHandle (GetCurrentProcess (), hd,
Packit d7e8d0
			GetCurrentProcess (), &new_hd,
Packit d7e8d0
			EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE1 (DEBUG_SYSIO, "gpgme:set_synchronize", hd,
Packit d7e8d0
	      "DuplicateHandle failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      /* FIXME: Should translate the error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return INVALID_HANDLE_VALUE;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  CloseHandle (hd);
Packit d7e8d0
  return new_hd;
Packit d7e8d0
#endif
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static DWORD CALLBACK
Packit d7e8d0
reader (void *arg)
Packit d7e8d0
{
Packit d7e8d0
  struct reader_context_s *ctx = arg;
Packit d7e8d0
  int nbytes;
Packit d7e8d0
  DWORD nread;
Packit d7e8d0
  int sock;
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
Packit d7e8d0
	      "file_sock=%d, thread=%p", ctx->file_sock, ctx->thread_hd);
Packit d7e8d0
Packit d7e8d0
  if (ctx->file_hd != INVALID_HANDLE_VALUE)
Packit d7e8d0
    sock = 0;
Packit d7e8d0
  else
Packit d7e8d0
    sock = 1;
Packit d7e8d0
Packit d7e8d0
  for (;;)
Packit d7e8d0
    {
Packit d7e8d0
      LOCK (ctx->mutex);
Packit d7e8d0
      /* Leave a 1 byte gap so that we can see whether it is empty or
Packit d7e8d0
	 full.  */
Packit d7e8d0
      if ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
Packit d7e8d0
	{
Packit d7e8d0
	  /* Wait for space.  */
Packit d7e8d0
	  if (!ResetEvent (ctx->have_space_ev))
Packit d7e8d0
	    TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  TRACE_LOG ("waiting for space");
Packit d7e8d0
	  WaitForSingleObject (ctx->have_space_ev, INFINITE);
Packit d7e8d0
	  TRACE_LOG ("got space");
Packit d7e8d0
	  LOCK (ctx->mutex);
Packit d7e8d0
       	}
Packit d7e8d0
      if (ctx->stop_me)
Packit d7e8d0
	{
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  break;
Packit d7e8d0
        }
Packit d7e8d0
      nbytes = (ctx->readpos + READBUF_SIZE
Packit d7e8d0
		- ctx->writepos - 1) % READBUF_SIZE;
Packit d7e8d0
      if (nbytes > READBUF_SIZE - ctx->writepos)
Packit d7e8d0
	nbytes = READBUF_SIZE - ctx->writepos;
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
      TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
Packit d7e8d0
Packit d7e8d0
      if (sock)
Packit d7e8d0
        {
Packit d7e8d0
          int n;
Packit d7e8d0
Packit d7e8d0
          n = recv (ctx->file_sock, ctx->buffer + ctx->writepos, nbytes, 0);
Packit d7e8d0
          if (n < 0)
Packit d7e8d0
            {
Packit d7e8d0
              ctx->error_code = (int) WSAGetLastError ();
Packit d7e8d0
              if (ctx->error_code == ERROR_BROKEN_PIPE)
Packit d7e8d0
                {
Packit d7e8d0
                  ctx->eof = 1;
Packit d7e8d0
                  TRACE_LOG ("got EOF (broken connection)");
Packit d7e8d0
                }
Packit d7e8d0
              else
Packit d7e8d0
                {
Packit d7e8d0
                  /* Check whether the shutdown triggered the error -
Packit d7e8d0
                     no need to to print a warning in this case.  */
Packit d7e8d0
                  if ( ctx->error_code == WSAECONNABORTED
Packit d7e8d0
                       || ctx->error_code == WSAECONNRESET)
Packit d7e8d0
                    {
Packit d7e8d0
                      LOCK (ctx->mutex);
Packit d7e8d0
                      if (ctx->stop_me)
Packit d7e8d0
                        {
Packit d7e8d0
                          UNLOCK (ctx->mutex);
Packit d7e8d0
                          TRACE_LOG ("got shutdown");
Packit d7e8d0
                          break;
Packit d7e8d0
                        }
Packit d7e8d0
                      UNLOCK (ctx->mutex);
Packit d7e8d0
                    }
Packit d7e8d0
Packit d7e8d0
                  ctx->error = 1;
Packit d7e8d0
                  TRACE_LOG1 ("recv error: ec=%d", ctx->error_code);
Packit d7e8d0
                }
Packit d7e8d0
              break;
Packit d7e8d0
            }
Packit d7e8d0
          nread = n;
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        {
Packit d7e8d0
          if (!ReadFile (ctx->file_hd,
Packit d7e8d0
                         ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
Packit d7e8d0
            {
Packit d7e8d0
              ctx->error_code = (int) GetLastError ();
Packit d7e8d0
	      /* NOTE (W32CE): Do not ignore ERROR_BUSY!  Check at
Packit d7e8d0
		 least stop_me if that happens.  */
Packit d7e8d0
              if (ctx->error_code == ERROR_BROKEN_PIPE)
Packit d7e8d0
                {
Packit d7e8d0
                  ctx->eof = 1;
Packit d7e8d0
                  TRACE_LOG ("got EOF (broken pipe)");
Packit d7e8d0
                }
Packit d7e8d0
              else
Packit d7e8d0
                {
Packit d7e8d0
                  ctx->error = 1;
Packit d7e8d0
                  TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
Packit d7e8d0
                }
Packit d7e8d0
              break;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
      LOCK (ctx->mutex);
Packit d7e8d0
      if (ctx->stop_me)
Packit d7e8d0
	{
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  break;
Packit d7e8d0
        }
Packit d7e8d0
      if (!nread)
Packit d7e8d0
	{
Packit d7e8d0
	  ctx->eof = 1;
Packit d7e8d0
	  TRACE_LOG ("got eof");
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  break;
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
      TRACE_LOG1 ("got %u bytes", nread);
Packit d7e8d0
Packit d7e8d0
      ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
Packit d7e8d0
      if (!SetEvent (ctx->have_data_ev))
Packit d7e8d0
	TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
Packit d7e8d0
		    (int) GetLastError ());
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
    }
Packit d7e8d0
  /* Indicate that we have an error or EOF.  */
Packit d7e8d0
  if (!SetEvent (ctx->have_data_ev))
Packit d7e8d0
    TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
Packit d7e8d0
                (int) GetLastError ());
Packit d7e8d0
Packit d7e8d0
  TRACE_LOG ("waiting for close");
Packit d7e8d0
  WaitForSingleObject (ctx->close_ev, INFINITE);
Packit d7e8d0
Packit d7e8d0
  CloseHandle (ctx->close_ev);
Packit d7e8d0
  CloseHandle (ctx->have_data_ev);
Packit d7e8d0
  CloseHandle (ctx->have_space_ev);
Packit d7e8d0
  CloseHandle (ctx->thread_hd);
Packit d7e8d0
  DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
  free (ctx);
Packit d7e8d0
Packit d7e8d0
  return TRACE_SUC ();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static struct reader_context_s *
Packit d7e8d0
create_reader (int fd)
Packit d7e8d0
{
Packit d7e8d0
  struct reader_context_s *ctx;
Packit d7e8d0
  SECURITY_ATTRIBUTES sec_attr;
Packit d7e8d0
  DWORD tid;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_SYSIO, "gpgme:create_reader", fd);
Packit d7e8d0
Packit d7e8d0
  memset (&sec_attr, 0, sizeof sec_attr);
Packit d7e8d0
  sec_attr.nLength = sizeof sec_attr;
Packit d7e8d0
  sec_attr.bInheritHandle = FALSE;
Packit d7e8d0
Packit d7e8d0
  ctx = calloc (1, sizeof *ctx);
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_SYSERR (errno);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
  TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
Packit d7e8d0
              fd, fd_table[fd].handle, fd_table[fd].socket,
Packit d7e8d0
              fd_table[fd].dup_from);
Packit d7e8d0
  ctx->file_hd = fd_table[fd].handle;
Packit d7e8d0
  ctx->file_sock = fd_table[fd].socket;
Packit d7e8d0
Packit d7e8d0
  ctx->refcount = 1;
Packit d7e8d0
  ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
Packit d7e8d0
  if (ctx->have_data_ev)
Packit d7e8d0
    ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
Packit d7e8d0
  if (ctx->have_space_ev)
Packit d7e8d0
    ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
Packit d7e8d0
  if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->close_ev)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      if (ctx->have_data_ev)
Packit d7e8d0
	CloseHandle (ctx->have_data_ev);
Packit d7e8d0
      if (ctx->have_space_ev)
Packit d7e8d0
	CloseHandle (ctx->have_space_ev);
Packit d7e8d0
      if (ctx->close_ev)
Packit d7e8d0
	CloseHandle (ctx->close_ev);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      /* FIXME: Translate the error code.  */
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
Packit d7e8d0
  INIT_LOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
Packit d7e8d0
				 STACK_SIZE_PARAM_IS_A_RESERVATION, &tid;;
Packit d7e8d0
#else
Packit d7e8d0
  ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid;;
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  if (!ctx->thread_hd)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
      if (ctx->have_data_ev)
Packit d7e8d0
	CloseHandle (ctx->have_data_ev);
Packit d7e8d0
      if (ctx->have_space_ev)
Packit d7e8d0
	CloseHandle (ctx->have_space_ev);
Packit d7e8d0
      if (ctx->close_ev)
Packit d7e8d0
	CloseHandle (ctx->close_ev);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      /* We set the priority of the thread higher because we know that
Packit d7e8d0
         it only runs for a short time.  This greatly helps to
Packit d7e8d0
         increase the performance of the I/O.  */
Packit d7e8d0
      SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  TRACE_SUC ();
Packit d7e8d0
  return ctx;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Prepare destruction of the reader thread for CTX.  Returns 0 if a
Packit d7e8d0
   call to this function is sufficient and destroy_reader_finish shall
Packit d7e8d0
   not be called.  */
Packit d7e8d0
static void
Packit d7e8d0
destroy_reader (struct reader_context_s *ctx)
Packit d7e8d0
{
Packit d7e8d0
  LOCK (ctx->mutex);
Packit d7e8d0
  ctx->refcount--;
Packit d7e8d0
  if (ctx->refcount != 0)
Packit d7e8d0
    {
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      return;
Packit d7e8d0
    }
Packit d7e8d0
  ctx->stop_me = 1;
Packit d7e8d0
  if (ctx->have_space_ev)
Packit d7e8d0
    SetEvent (ctx->have_space_ev);
Packit d7e8d0
  UNLOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  /* Scenario: We never create a full pipe, but already started
Packit d7e8d0
     reading.  Then we need to unblock the reader in the pipe driver
Packit d7e8d0
     to make our reader thread notice that we want it to go away.  */
Packit d7e8d0
Packit d7e8d0
  if (ctx->file_hd != INVALID_HANDLE_VALUE)
Packit d7e8d0
    {
Packit d7e8d0
      if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
Packit d7e8d0
			NULL, 0, NULL, 0, NULL, NULL))
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
Packit d7e8d0
		  "unblock control call failed for thread %p", ctx->thread_hd);
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  /* The reader thread is usually blocking in recv or ReadFile.  If
Packit d7e8d0
     the peer does not send an EOF or breaks the pipe the WFSO might
Packit d7e8d0
     get stuck waiting for the termination of the reader thread.  This
Packit d7e8d0
     happens quite often with sockets, thus we definitely need to get
Packit d7e8d0
     out of the recv.  A shutdown does this nicely.  For handles
Packit d7e8d0
     (i.e. pipes) it would also be nice to cancel the operation, but
Packit d7e8d0
     such a feature is only available since Vista.  Thus we need to
Packit d7e8d0
     dlopen that syscall.  */
Packit d7e8d0
  if (ctx->file_hd != INVALID_HANDLE_VALUE)
Packit d7e8d0
    {
Packit d7e8d0
      /* Fixme: Call CancelSynchronousIo (handle_of_thread).  */
Packit d7e8d0
    }
Packit d7e8d0
  else if (ctx->file_sock != INVALID_SOCKET)
Packit d7e8d0
    {
Packit d7e8d0
      if (shutdown (ctx->file_sock, 2))
Packit d7e8d0
        TRACE2 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
Packit d7e8d0
                "shutdown socket %d failed: %s",
Packit d7e8d0
                ctx->file_sock, (int) WSAGetLastError ());
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* After setting this event CTX is void. */
Packit d7e8d0
  SetEvent (ctx->close_ev);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Find a reader context or create a new one.  Note that the reader
Packit d7e8d0
   context will last until a _gpgme_io_close.  */
Packit d7e8d0
static struct reader_context_s *
Packit d7e8d0
find_reader (int fd, int start_it)
Packit d7e8d0
{
Packit d7e8d0
  struct reader_context_s *rd = NULL;
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
  LOCK (reader_table_lock);
Packit d7e8d0
  for (i = 0; i < reader_table_size; i++)
Packit d7e8d0
    if (reader_table[i].used && reader_table[i].fd == fd)
Packit d7e8d0
      rd = reader_table[i].context;
Packit d7e8d0
Packit d7e8d0
  if (rd || !start_it)
Packit d7e8d0
    {
Packit d7e8d0
      UNLOCK (reader_table_lock);
Packit d7e8d0
      return rd;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  for (i = 0; i < reader_table_size; i++)
Packit d7e8d0
    if (!reader_table[i].used)
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
  if (i != reader_table_size)
Packit d7e8d0
    {
Packit d7e8d0
      rd = create_reader (fd);
Packit d7e8d0
      reader_table[i].fd = fd;
Packit d7e8d0
      reader_table[i].context = rd;
Packit d7e8d0
      reader_table[i].used = 1;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  UNLOCK (reader_table_lock);
Packit d7e8d0
  return rd;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_read (int fd, void *buffer, size_t count)
Packit d7e8d0
{
Packit d7e8d0
  int nread;
Packit d7e8d0
  struct reader_context_s *ctx;
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
Packit d7e8d0
	      "buffer=%p, count=%u", buffer, count);
Packit d7e8d0
Packit d7e8d0
  ctx = find_reader (fd, 1);
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EBADF);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  if (ctx->eof_shortcut)
Packit d7e8d0
    return TRACE_SYSRES (0);
Packit d7e8d0
Packit d7e8d0
  LOCK (ctx->mutex);
Packit d7e8d0
  if (ctx->readpos == ctx->writepos && !ctx->error)
Packit d7e8d0
    {
Packit d7e8d0
      /* No data available.  */
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd);
Packit d7e8d0
      WaitForSingleObject (ctx->have_data_ev, INFINITE);
Packit d7e8d0
      TRACE_LOG1 ("data from thread %p available", ctx->thread_hd);
Packit d7e8d0
      LOCK (ctx->mutex);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (ctx->readpos == ctx->writepos || ctx->error)
Packit d7e8d0
    {
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      ctx->eof_shortcut = 1;
Packit d7e8d0
      if (ctx->eof)
Packit d7e8d0
	return TRACE_SYSRES (0);
Packit d7e8d0
      if (!ctx->error)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG ("EOF but ctx->eof flag not set");
Packit d7e8d0
	  return 0;
Packit d7e8d0
	}
Packit d7e8d0
      gpg_err_set_errno (ctx->error_code);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  nread = ctx->readpos < ctx->writepos
Packit d7e8d0
    ? ctx->writepos - ctx->readpos
Packit d7e8d0
    : READBUF_SIZE - ctx->readpos;
Packit d7e8d0
  if (nread > count)
Packit d7e8d0
    nread = count;
Packit d7e8d0
  memcpy (buffer, ctx->buffer + ctx->readpos, nread);
Packit d7e8d0
  ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
Packit d7e8d0
  if (ctx->readpos == ctx->writepos && !ctx->eof)
Packit d7e8d0
    {
Packit d7e8d0
      if (!ResetEvent (ctx->have_data_ev))
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  /* FIXME: Should translate the error code.  */
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  if (!SetEvent (ctx->have_space_ev))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d",
Packit d7e8d0
		  ctx->have_space_ev, (int) GetLastError ());
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      /* FIXME: Should translate the error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  UNLOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
  TRACE_LOGBUF (buffer, nread);
Packit d7e8d0
  return TRACE_SYSRES (nread);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* The writer does use a simple buffering strategy so that we are
Packit d7e8d0
   informed about write errors as soon as possible (i. e. with the the
Packit d7e8d0
   next call to the write function.  */
Packit d7e8d0
static DWORD CALLBACK
Packit d7e8d0
writer (void *arg)
Packit d7e8d0
{
Packit d7e8d0
  struct writer_context_s *ctx = arg;
Packit d7e8d0
  DWORD nwritten;
Packit d7e8d0
  int sock;
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
Packit d7e8d0
	      "file_sock=%d, thread=%p", ctx->file_sock, ctx->thread_hd);
Packit d7e8d0
Packit d7e8d0
  if (ctx->file_hd != INVALID_HANDLE_VALUE)
Packit d7e8d0
    sock = 0;
Packit d7e8d0
  else
Packit d7e8d0
    sock = 1;
Packit d7e8d0
Packit d7e8d0
  for (;;)
Packit d7e8d0
    {
Packit d7e8d0
      LOCK (ctx->mutex);
Packit d7e8d0
      if (ctx->stop_me && !ctx->nbytes)
Packit d7e8d0
	{
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  break;
Packit d7e8d0
        }
Packit d7e8d0
      if (!ctx->nbytes)
Packit d7e8d0
	{
Packit d7e8d0
	  if (!SetEvent (ctx->is_empty))
Packit d7e8d0
	    TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  if (!ResetEvent (ctx->have_data))
Packit d7e8d0
	    TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  TRACE_LOG ("idle");
Packit d7e8d0
	  WaitForSingleObject (ctx->have_data, INFINITE);
Packit d7e8d0
	  TRACE_LOG ("got data to send");
Packit d7e8d0
	  LOCK (ctx->mutex);
Packit d7e8d0
       	}
Packit d7e8d0
      if (ctx->stop_me && !ctx->nbytes)
Packit d7e8d0
	{
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  break;
Packit d7e8d0
        }
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
      TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
Packit d7e8d0
Packit d7e8d0
      /* Note that CTX->nbytes is not zero at this point, because
Packit d7e8d0
	 _gpgme_io_write always writes at least 1 byte before waking
Packit d7e8d0
	 us up, unless CTX->stop_me is true, which we catch above.  */
Packit d7e8d0
      if (sock)
Packit d7e8d0
        {
Packit d7e8d0
          /* We need to try send first because a socket handle can't
Packit d7e8d0
             be used with WriteFile.  */
Packit d7e8d0
          int n;
Packit d7e8d0
Packit d7e8d0
          n = send (ctx->file_sock, ctx->buffer, ctx->nbytes, 0);
Packit d7e8d0
          if (n < 0)
Packit d7e8d0
            {
Packit d7e8d0
              ctx->error_code = (int) WSAGetLastError ();
Packit d7e8d0
              ctx->error = 1;
Packit d7e8d0
              TRACE_LOG1 ("send error: ec=%d", ctx->error_code);
Packit d7e8d0
              break;
Packit d7e8d0
            }
Packit d7e8d0
          nwritten = n;
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        {
Packit d7e8d0
          if (!WriteFile (ctx->file_hd, ctx->buffer,
Packit d7e8d0
                          ctx->nbytes, &nwritten, NULL))
Packit d7e8d0
            {
Packit d7e8d0
	      if (GetLastError () == ERROR_BUSY)
Packit d7e8d0
		{
Packit d7e8d0
		  /* Probably stop_me is set now.  */
Packit d7e8d0
                  TRACE_LOG ("pipe busy (unblocked?)");
Packit d7e8d0
		  continue;
Packit d7e8d0
                }
Packit d7e8d0
Packit d7e8d0
              ctx->error_code = (int) GetLastError ();
Packit d7e8d0
              ctx->error = 1;
Packit d7e8d0
              TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
Packit d7e8d0
              break;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
      TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
Packit d7e8d0
Packit d7e8d0
      LOCK (ctx->mutex);
Packit d7e8d0
      ctx->nbytes -= nwritten;
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
    }
Packit d7e8d0
  /* Indicate that we have an error.  */
Packit d7e8d0
  if (!SetEvent (ctx->is_empty))
Packit d7e8d0
    TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
Packit d7e8d0
  TRACE_LOG ("waiting for close");
Packit d7e8d0
  WaitForSingleObject (ctx->close_ev, INFINITE);
Packit d7e8d0
Packit d7e8d0
  if (ctx->nbytes)
Packit d7e8d0
    TRACE_LOG1 ("still %d bytes in buffer at close time", ctx->nbytes);
Packit d7e8d0
Packit d7e8d0
  CloseHandle (ctx->close_ev);
Packit d7e8d0
  CloseHandle (ctx->have_data);
Packit d7e8d0
  CloseHandle (ctx->is_empty);
Packit d7e8d0
  CloseHandle (ctx->thread_hd);
Packit d7e8d0
  DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
  free (ctx);
Packit d7e8d0
Packit d7e8d0
  return TRACE_SUC ();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static struct writer_context_s *
Packit d7e8d0
create_writer (int fd)
Packit d7e8d0
{
Packit d7e8d0
  struct writer_context_s *ctx;
Packit d7e8d0
  SECURITY_ATTRIBUTES sec_attr;
Packit d7e8d0
  DWORD tid;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_SYSIO, "gpgme:create_writer", fd);
Packit d7e8d0
Packit d7e8d0
  memset (&sec_attr, 0, sizeof sec_attr);
Packit d7e8d0
  sec_attr.nLength = sizeof sec_attr;
Packit d7e8d0
  sec_attr.bInheritHandle = FALSE;
Packit d7e8d0
Packit d7e8d0
  ctx = calloc (1, sizeof *ctx);
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_SYSERR (errno);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
  TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
Packit d7e8d0
              fd, fd_table[fd].handle, fd_table[fd].socket,
Packit d7e8d0
              fd_table[fd].dup_from);
Packit d7e8d0
  ctx->file_hd = fd_table[fd].handle;
Packit d7e8d0
  ctx->file_sock = fd_table[fd].socket;
Packit d7e8d0
Packit d7e8d0
  ctx->refcount = 1;
Packit d7e8d0
  ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
Packit d7e8d0
  if (ctx->have_data)
Packit d7e8d0
    ctx->is_empty  = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
Packit d7e8d0
  if (ctx->is_empty)
Packit d7e8d0
    ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
Packit d7e8d0
  if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      if (ctx->have_data)
Packit d7e8d0
	CloseHandle (ctx->have_data);
Packit d7e8d0
      if (ctx->is_empty)
Packit d7e8d0
	CloseHandle (ctx->is_empty);
Packit d7e8d0
      if (ctx->close_ev)
Packit d7e8d0
	CloseHandle (ctx->close_ev);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      /* FIXME: Translate the error code.  */
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  ctx->is_empty = set_synchronize (ctx->is_empty);
Packit d7e8d0
  INIT_LOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
Packit d7e8d0
				 STACK_SIZE_PARAM_IS_A_RESERVATION, &tid;;
Packit d7e8d0
#else
Packit d7e8d0
  ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  if (!ctx->thread_hd)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
      if (ctx->have_data)
Packit d7e8d0
	CloseHandle (ctx->have_data);
Packit d7e8d0
      if (ctx->is_empty)
Packit d7e8d0
	CloseHandle (ctx->is_empty);
Packit d7e8d0
      if (ctx->close_ev)
Packit d7e8d0
	CloseHandle (ctx->close_ev);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      /* We set the priority of the thread higher because we know
Packit d7e8d0
	 that it only runs for a short time.  This greatly helps to
Packit d7e8d0
	 increase the performance of the I/O.  */
Packit d7e8d0
      SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  TRACE_SUC ();
Packit d7e8d0
  return ctx;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
destroy_writer (struct writer_context_s *ctx)
Packit d7e8d0
{
Packit d7e8d0
  LOCK (ctx->mutex);
Packit d7e8d0
  ctx->refcount--;
Packit d7e8d0
  if (ctx->refcount != 0)
Packit d7e8d0
    {
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      return;
Packit d7e8d0
    }
Packit d7e8d0
  ctx->stop_me = 1;
Packit d7e8d0
  if (ctx->have_data)
Packit d7e8d0
    SetEvent (ctx->have_data);
Packit d7e8d0
  UNLOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
  /* Give the writer a chance to flush the buffer.  */
Packit d7e8d0
  WaitForSingleObject (ctx->is_empty, INFINITE);
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  /* Scenario: We never create a full pipe, but already started
Packit d7e8d0
     writing more than the pipe buffer.  Then we need to unblock the
Packit d7e8d0
     writer in the pipe driver to make our writer thread notice that
Packit d7e8d0
     we want it to go away.  */
Packit d7e8d0
Packit d7e8d0
  if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
Packit d7e8d0
			NULL, 0, NULL, 0, NULL, NULL))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
Packit d7e8d0
	      "unblock control call failed for thread %p", ctx->thread_hd);
Packit d7e8d0
    }
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  /* After setting this event CTX is void.  */
Packit d7e8d0
  SetEvent (ctx->close_ev);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Find a writer context or create a new one.  Note that the writer
Packit d7e8d0
   context will last until a _gpgme_io_close.  */
Packit d7e8d0
static struct writer_context_s *
Packit d7e8d0
find_writer (int fd, int start_it)
Packit d7e8d0
{
Packit d7e8d0
  struct writer_context_s *wt = NULL;
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
  LOCK (writer_table_lock);
Packit d7e8d0
  for (i = 0; i < writer_table_size; i++)
Packit d7e8d0
    if (writer_table[i].used && writer_table[i].fd == fd)
Packit d7e8d0
      wt = writer_table[i].context;
Packit d7e8d0
Packit d7e8d0
  if (wt || !start_it)
Packit d7e8d0
    {
Packit d7e8d0
      UNLOCK (writer_table_lock);
Packit d7e8d0
      return wt;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  for (i = 0; i < writer_table_size; i++)
Packit d7e8d0
    if (!writer_table[i].used)
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
  if (i != writer_table_size)
Packit d7e8d0
    {
Packit d7e8d0
      wt = create_writer (fd);
Packit d7e8d0
      writer_table[i].fd = fd;
Packit d7e8d0
      writer_table[i].context = wt;
Packit d7e8d0
      writer_table[i].used = 1;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  UNLOCK (writer_table_lock);
Packit d7e8d0
  return wt;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_write (int fd, const void *buffer, size_t count)
Packit d7e8d0
{
Packit d7e8d0
  struct writer_context_s *ctx;
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
Packit d7e8d0
	      "buffer=%p, count=%u", buffer, count);
Packit d7e8d0
  TRACE_LOGBUF (buffer, count);
Packit d7e8d0
Packit d7e8d0
  if (count == 0)
Packit d7e8d0
    return TRACE_SYSRES (0);
Packit d7e8d0
Packit d7e8d0
  ctx = find_writer (fd, 1);
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_SYSRES (-1);
Packit d7e8d0
Packit d7e8d0
  LOCK (ctx->mutex);
Packit d7e8d0
  if (!ctx->error && ctx->nbytes)
Packit d7e8d0
    {
Packit d7e8d0
      /* Bytes are pending for send.  */
Packit d7e8d0
Packit d7e8d0
      /* Reset the is_empty event.  Better safe than sorry.  */
Packit d7e8d0
      if (!ResetEvent (ctx->is_empty))
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  /* FIXME: Should translate the error code.  */
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
	}
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd);
Packit d7e8d0
      WaitForSingleObject (ctx->is_empty, INFINITE);
Packit d7e8d0
      TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd);
Packit d7e8d0
      LOCK (ctx->mutex);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (ctx->error)
Packit d7e8d0
    {
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      if (ctx->error_code == ERROR_NO_DATA)
Packit d7e8d0
        gpg_err_set_errno (EPIPE);
Packit d7e8d0
      else
Packit d7e8d0
        gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* If no error occurred, the number of bytes in the buffer must be
Packit d7e8d0
     zero.  */
Packit d7e8d0
  assert (!ctx->nbytes);
Packit d7e8d0
Packit d7e8d0
  if (count > WRITEBUF_SIZE)
Packit d7e8d0
    count = WRITEBUF_SIZE;
Packit d7e8d0
  memcpy (ctx->buffer, buffer, count);
Packit d7e8d0
  ctx->nbytes = count;
Packit d7e8d0
Packit d7e8d0
  /* We have to reset the is_empty event early, because it is also
Packit d7e8d0
     used by the select() implementation to probe the channel.  */
Packit d7e8d0
  if (!ResetEvent (ctx->is_empty))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      /* FIXME: Should translate the error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  if (!SetEvent (ctx->have_data))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      UNLOCK (ctx->mutex);
Packit d7e8d0
      /* FIXME: Should translate the error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  UNLOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
  return TRACE_SYSRES ((int) count);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_pipe (int filedes[2], int inherit_idx)
Packit d7e8d0
{
Packit d7e8d0
  int rfd;
Packit d7e8d0
  int wfd;
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  HANDLE hd;
Packit d7e8d0
  int rvid;
Packit d7e8d0
#else
Packit d7e8d0
  HANDLE rh;
Packit d7e8d0
  HANDLE wh;
Packit d7e8d0
  SECURITY_ATTRIBUTES sec_attr;
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
Packit d7e8d0
	      "inherit_idx=%i (GPGME uses it for %s)",
Packit d7e8d0
	      inherit_idx, inherit_idx ? "reading" : "writing");
Packit d7e8d0
Packit d7e8d0
  rfd = new_fd ();
Packit d7e8d0
  if (rfd == -1)
Packit d7e8d0
    return TRACE_SYSRES (-1);
Packit d7e8d0
  wfd = new_fd ();
Packit d7e8d0
  if (wfd == -1)
Packit d7e8d0
    {
Packit d7e8d0
      release_fd (rfd);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
Packit d7e8d0
  if (hd == INVALID_HANDLE_VALUE)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d",
Packit d7e8d0
		  (int) GetLastError ());
Packit d7e8d0
      release_fd (rfd);
Packit d7e8d0
      release_fd (wfd);
Packit d7e8d0
      /* FIXME: Should translate the error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (inherit_idx == 0)
Packit d7e8d0
    {
Packit d7e8d0
      fd_table[rfd].rvid = rvid;
Packit d7e8d0
      fd_table[wfd].handle = hd;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      fd_table[rfd].handle = hd;
Packit d7e8d0
      fd_table[wfd].rvid = rvid;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
#else
Packit d7e8d0
Packit d7e8d0
  memset (&sec_attr, 0, sizeof (sec_attr));
Packit d7e8d0
  sec_attr.nLength = sizeof (sec_attr);
Packit d7e8d0
  sec_attr.bInheritHandle = FALSE;
Packit d7e8d0
Packit d7e8d0
  if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      release_fd (rfd);
Packit d7e8d0
      release_fd (wfd);
Packit d7e8d0
      /* FIXME: Should translate the error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Make one end inheritable.  */
Packit d7e8d0
  if (inherit_idx == 0)
Packit d7e8d0
    {
Packit d7e8d0
      HANDLE hd;
Packit d7e8d0
      if (!DuplicateHandle (GetCurrentProcess(), rh,
Packit d7e8d0
			    GetCurrentProcess(), &hd, 0,
Packit d7e8d0
			    TRUE, DUPLICATE_SAME_ACCESS))
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
Packit d7e8d0
		      (int) GetLastError ());
Packit d7e8d0
	  release_fd (rfd);
Packit d7e8d0
	  release_fd (wfd);
Packit d7e8d0
	  CloseHandle (rh);
Packit d7e8d0
	  CloseHandle (wh);
Packit d7e8d0
	  /* FIXME: Should translate the error code.  */
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
        }
Packit d7e8d0
      CloseHandle (rh);
Packit d7e8d0
      rh = hd;
Packit d7e8d0
    }
Packit d7e8d0
  else if (inherit_idx == 1)
Packit d7e8d0
    {
Packit d7e8d0
      HANDLE hd;
Packit d7e8d0
      if (!DuplicateHandle( GetCurrentProcess(), wh,
Packit d7e8d0
			    GetCurrentProcess(), &hd, 0,
Packit d7e8d0
			    TRUE, DUPLICATE_SAME_ACCESS))
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
Packit d7e8d0
		      (int) GetLastError ());
Packit d7e8d0
	  release_fd (rfd);
Packit d7e8d0
	  release_fd (wfd);
Packit d7e8d0
	  CloseHandle (rh);
Packit d7e8d0
	  CloseHandle (wh);
Packit d7e8d0
	  /* FIXME: Should translate the error code.  */
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
        }
Packit d7e8d0
      CloseHandle (wh);
Packit d7e8d0
      wh = hd;
Packit d7e8d0
    }
Packit d7e8d0
  fd_table[rfd].handle = rh;
Packit d7e8d0
  fd_table[wfd].handle = wh;
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  filedes[0] = rfd;
Packit d7e8d0
  filedes[1] = wfd;
Packit d7e8d0
  return TRACE_SUC6 ("read=0x%x (%p/0x%x), write=0x%x (%p/0x%x)",
Packit d7e8d0
		     rfd, fd_table[rfd].handle, fd_table[rfd].rvid,
Packit d7e8d0
		     wfd, fd_table[wfd].handle, fd_table[wfd].rvid);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_close (int fd)
Packit d7e8d0
{
Packit d7e8d0
  int i;
Packit d7e8d0
  _gpgme_close_notify_handler_t handler = NULL;
Packit d7e8d0
  void *value = NULL;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
Packit d7e8d0
Packit d7e8d0
  if (fd == -1)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EBADF);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EBADF);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
Packit d7e8d0
              fd, fd_table[fd].handle, fd_table[fd].socket,
Packit d7e8d0
              fd_table[fd].dup_from);
Packit d7e8d0
Packit d7e8d0
  LOCK (reader_table_lock);
Packit d7e8d0
  for (i = 0; i < reader_table_size; i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (reader_table[i].used && reader_table[i].fd == fd)
Packit d7e8d0
	{
Packit d7e8d0
	  destroy_reader (reader_table[i].context);
Packit d7e8d0
	  reader_table[i].context = NULL;
Packit d7e8d0
	  reader_table[i].used = 0;
Packit d7e8d0
	  break;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  UNLOCK (reader_table_lock);
Packit d7e8d0
Packit d7e8d0
  LOCK (writer_table_lock);
Packit d7e8d0
  for (i = 0; i < writer_table_size; i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (writer_table[i].used && writer_table[i].fd == fd)
Packit d7e8d0
	{
Packit d7e8d0
	  destroy_writer (writer_table[i].context);
Packit d7e8d0
	  writer_table[i].context = NULL;
Packit d7e8d0
	  writer_table[i].used = 0;
Packit d7e8d0
	  break;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  UNLOCK (writer_table_lock);
Packit d7e8d0
Packit d7e8d0
  LOCK (notify_table_lock);
Packit d7e8d0
  for (i = 0; i < DIM (notify_table); i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (notify_table[i].inuse && notify_table[i].fd == fd)
Packit d7e8d0
	{
Packit d7e8d0
	  handler = notify_table[i].handler;
Packit d7e8d0
	  value   = notify_table[i].value;
Packit d7e8d0
	  notify_table[i].handler = NULL;
Packit d7e8d0
	  notify_table[i].value = NULL;
Packit d7e8d0
	  notify_table[i].inuse = 0;
Packit d7e8d0
	  break;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  UNLOCK (notify_table_lock);
Packit d7e8d0
  if (handler)
Packit d7e8d0
    handler (fd, value);
Packit d7e8d0
Packit d7e8d0
  if (fd_table[fd].dup_from == -1)
Packit d7e8d0
    {
Packit d7e8d0
      if (fd_table[fd].handle != INVALID_HANDLE_VALUE)
Packit d7e8d0
	{
Packit d7e8d0
	  if (!CloseHandle (fd_table[fd].handle))
Packit d7e8d0
	    {
Packit d7e8d0
	      TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	      /* FIXME: Should translate the error code.  */
Packit d7e8d0
	      gpg_err_set_errno (EIO);
Packit d7e8d0
	      return TRACE_SYSRES (-1);
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
      else if (fd_table[fd].socket != INVALID_SOCKET)
Packit d7e8d0
	{
Packit d7e8d0
	  if (closesocket (fd_table[fd].socket))
Packit d7e8d0
	    {
Packit d7e8d0
	      TRACE_LOG1 ("closesocket failed: ec=%d", (int) WSAGetLastError ());
Packit d7e8d0
	      /* FIXME: Should translate the error code.  */
Packit d7e8d0
	      gpg_err_set_errno (EIO);
Packit d7e8d0
	      return TRACE_SYSRES (-1);
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
      /* Nothing to do for RVIDs.  */
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  release_fd (fd);
Packit d7e8d0
Packit d7e8d0
  return TRACE_SYSRES (0);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
Packit d7e8d0
			    void *value)
Packit d7e8d0
{
Packit d7e8d0
  int i;
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
Packit d7e8d0
	      "close_handler=%p/%p", handler, value);
Packit d7e8d0
Packit d7e8d0
  assert (fd != -1);
Packit d7e8d0
Packit d7e8d0
  LOCK (notify_table_lock);
Packit d7e8d0
  for (i=0; i < DIM (notify_table); i++)
Packit d7e8d0
    if (notify_table[i].inuse && notify_table[i].fd == fd)
Packit d7e8d0
      break;
Packit d7e8d0
  if (i == DIM (notify_table))
Packit d7e8d0
    for (i = 0; i < DIM (notify_table); i++)
Packit d7e8d0
      if (!notify_table[i].inuse)
Packit d7e8d0
	break;
Packit d7e8d0
  if (i == DIM (notify_table))
Packit d7e8d0
    {
Packit d7e8d0
      UNLOCK (notify_table_lock);
Packit d7e8d0
      gpg_err_set_errno (EINVAL);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  notify_table[i].fd = fd;
Packit d7e8d0
  notify_table[i].handler = handler;
Packit d7e8d0
  notify_table[i].value = value;
Packit d7e8d0
  notify_table[i].inuse = 1;
Packit d7e8d0
  UNLOCK (notify_table_lock);
Packit d7e8d0
  return TRACE_SYSRES (0);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_set_nonblocking (int fd)
Packit d7e8d0
{
Packit d7e8d0
  TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
static char *
Packit d7e8d0
build_commandline (char **argv, int fd0, int fd0_isnull,
Packit d7e8d0
		   int fd1, int fd1_isnull,
Packit d7e8d0
		   int fd2, int fd2_isnull)
Packit d7e8d0
{
Packit d7e8d0
  int i, n;
Packit d7e8d0
  const char *s;
Packit d7e8d0
  char *buf, *p;
Packit d7e8d0
  char fdbuf[3*30];
Packit d7e8d0
Packit d7e8d0
  p = fdbuf;
Packit d7e8d0
  *p = 0;
Packit d7e8d0
Packit d7e8d0
  if (fd0 != -1)
Packit d7e8d0
    {
Packit d7e8d0
      if (fd0_isnull)
Packit d7e8d0
        strcpy (p, "-&S0=null ");
Packit d7e8d0
      else
Packit d7e8d0
	snprintf (p, 25, "-&S0=%d ", fd_table[fd0].rvid);
Packit d7e8d0
      p += strlen (p);
Packit d7e8d0
    }
Packit d7e8d0
  if (fd1 != -1)
Packit d7e8d0
    {
Packit d7e8d0
      if (fd1_isnull)
Packit d7e8d0
        strcpy (p, "-&S1=null ");
Packit d7e8d0
      else
Packit d7e8d0
	snprintf (p, 25, "-&S1=%d ", fd_table[fd1].rvid);
Packit d7e8d0
      p += strlen (p);
Packit d7e8d0
    }
Packit d7e8d0
  if (fd2 != -1)
Packit d7e8d0
    {
Packit d7e8d0
      if (fd2_isnull)
Packit d7e8d0
        strcpy (p, "-&S2=null ");
Packit d7e8d0
      else
Packit d7e8d0
        snprintf (p, 25, "-&S2=%d ", fd_table[fd2].rvid);
Packit d7e8d0
      p += strlen (p);
Packit d7e8d0
    }
Packit d7e8d0
  strcpy (p, "-&S2=null ");
Packit d7e8d0
  p += strlen (p);
Packit d7e8d0
Packit d7e8d0
  n = strlen (fdbuf);
Packit d7e8d0
  for (i=0; (s = argv[i]); i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (!i)
Packit d7e8d0
        continue; /* Ignore argv[0].  */
Packit d7e8d0
      n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
Packit d7e8d0
      for (; *s; s++)
Packit d7e8d0
        if (*s == '\"')
Packit d7e8d0
          n++;  /* Need to double inner quotes.  */
Packit d7e8d0
    }
Packit d7e8d0
  n++;
Packit d7e8d0
  buf = p = malloc (n);
Packit d7e8d0
  if (! buf)
Packit d7e8d0
    return NULL;
Packit d7e8d0
Packit d7e8d0
  p = stpcpy (p, fdbuf);
Packit d7e8d0
  for (i = 0; argv[i]; i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (!i)
Packit d7e8d0
        continue; /* Ignore argv[0].  */
Packit d7e8d0
      if (i > 1)
Packit d7e8d0
        p = stpcpy (p, " ");
Packit d7e8d0
Packit d7e8d0
      if (! *argv[i]) /* Empty string. */
Packit d7e8d0
        p = stpcpy (p, "\"\"");
Packit d7e8d0
      else if (strpbrk (argv[i], " \t\n\v\f\""))
Packit d7e8d0
        {
Packit d7e8d0
          p = stpcpy (p, "\"");
Packit d7e8d0
          for (s = argv[i]; *s; s++)
Packit d7e8d0
            {
Packit d7e8d0
              *p++ = *s;
Packit d7e8d0
              if (*s == '\"')
Packit d7e8d0
                *p++ = *s;
Packit d7e8d0
            }
Packit d7e8d0
          *p++ = '\"';
Packit d7e8d0
          *p = 0;
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        p = stpcpy (p, argv[i]);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return buf;
Packit d7e8d0
}
Packit d7e8d0
#else
Packit d7e8d0
static char *
Packit d7e8d0
build_commandline (char **argv)
Packit d7e8d0
{
Packit d7e8d0
  int i;
Packit d7e8d0
  int n = 0;
Packit d7e8d0
  char *buf;
Packit d7e8d0
  char *p;
Packit d7e8d0
Packit d7e8d0
  /* We have to quote some things because under Windows the program
Packit d7e8d0
     parses the commandline and does some unquoting.  We enclose the
Packit d7e8d0
     whole argument in double-quotes, and escape literal double-quotes
Packit d7e8d0
     as well as backslashes with a backslash.  We end up with a
Packit d7e8d0
     trailing space at the end of the line, but that is harmless.  */
Packit d7e8d0
  for (i = 0; argv[i]; i++)
Packit d7e8d0
    {
Packit d7e8d0
      p = argv[i];
Packit d7e8d0
      /* The leading double-quote.  */
Packit d7e8d0
      n++;
Packit d7e8d0
      while (*p)
Packit d7e8d0
	{
Packit d7e8d0
	  /* An extra one for each literal that must be escaped.  */
Packit d7e8d0
	  if (*p == '\\' || *p == '"')
Packit d7e8d0
	    n++;
Packit d7e8d0
	  n++;
Packit d7e8d0
	  p++;
Packit d7e8d0
	}
Packit d7e8d0
      /* The trailing double-quote and the delimiter.  */
Packit d7e8d0
      n += 2;
Packit d7e8d0
    }
Packit d7e8d0
  /* And a trailing zero.  */
Packit d7e8d0
  n++;
Packit d7e8d0
Packit d7e8d0
  buf = p = malloc (n);
Packit d7e8d0
  if (!buf)
Packit d7e8d0
    return NULL;
Packit d7e8d0
  for (i = 0; argv[i]; i++)
Packit d7e8d0
    {
Packit d7e8d0
      char *argvp = argv[i];
Packit d7e8d0
Packit d7e8d0
      *(p++) = '"';
Packit d7e8d0
      while (*argvp)
Packit d7e8d0
	{
Packit d7e8d0
	  if (*argvp == '\\' || *argvp == '"')
Packit d7e8d0
	    *(p++) = '\\';
Packit d7e8d0
	  *(p++) = *(argvp++);
Packit d7e8d0
	}
Packit d7e8d0
      *(p++) = '"';
Packit d7e8d0
      *(p++) = ' ';
Packit d7e8d0
    }
Packit d7e8d0
  *(p++) = 0;
Packit d7e8d0
Packit d7e8d0
  return buf;
Packit d7e8d0
}
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
Packit d7e8d0
		 struct spawn_fd_item_s *fd_list,
Packit d7e8d0
		 void (*atfork) (void *opaque, int reserved),
Packit d7e8d0
		 void *atforkvalue, pid_t *r_pid)
Packit d7e8d0
{
Packit d7e8d0
  PROCESS_INFORMATION pi =
Packit d7e8d0
    {
Packit d7e8d0
      NULL,      /* returns process handle */
Packit d7e8d0
      0,         /* returns primary thread handle */
Packit d7e8d0
      0,         /* returns pid */
Packit d7e8d0
      0          /* returns tid */
Packit d7e8d0
    };
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  int fd_in = -1;
Packit d7e8d0
  int fd_out = -1;
Packit d7e8d0
  int fd_err = -1;
Packit d7e8d0
  int fd_in_isnull = 1;
Packit d7e8d0
  int fd_out_isnull = 1;
Packit d7e8d0
  int fd_err_isnull = 1;
Packit d7e8d0
  char *cmdline;
Packit d7e8d0
  HANDLE hd = INVALID_HANDLE_VALUE;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
Packit d7e8d0
	      "path=%s", path);
Packit d7e8d0
  i = 0;
Packit d7e8d0
  while (argv[i])
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
Packit d7e8d0
      i++;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  for (i = 0; fd_list[i].fd != -1; i++)
Packit d7e8d0
    {
Packit d7e8d0
      int fd = fd_list[i].fd;
Packit d7e8d0
Packit d7e8d0
      TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd, fd_list[i].dup_to);
Packit d7e8d0
      if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("invalid fd 0x%x", fd);
Packit d7e8d0
	  gpg_err_set_errno (EBADF);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
	}
Packit d7e8d0
      if (fd_table[fd].rvid == 0)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("fd 0x%x not inheritable (not an RVID)", fd);
Packit d7e8d0
	  gpg_err_set_errno (EBADF);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      if (fd_list[i].dup_to == 0)
Packit d7e8d0
	{
Packit d7e8d0
	  fd_in = fd_list[i].fd;
Packit d7e8d0
	  fd_in_isnull = 0;
Packit d7e8d0
	}
Packit d7e8d0
      else if (fd_list[i].dup_to == 1)
Packit d7e8d0
	{
Packit d7e8d0
	  fd_out = fd_list[i].fd;
Packit d7e8d0
	  fd_out_isnull = 0;
Packit d7e8d0
	}
Packit d7e8d0
      else if (fd_list[i].dup_to == 2)
Packit d7e8d0
	{
Packit d7e8d0
	  fd_err = fd_list[i].fd;
Packit d7e8d0
	  fd_err_isnull = 0;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  cmdline = build_commandline (argv, fd_in, fd_in_isnull,
Packit d7e8d0
			       fd_out, fd_out_isnull, fd_err, fd_err_isnull);
Packit d7e8d0
  if (!cmdline)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("build_commandline failed: %s", strerror (errno));
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (!CreateProcessA (path,                /* Program to start.  */
Packit d7e8d0
		       cmdline,             /* Command line arguments.  */
Packit d7e8d0
		       NULL,                 /* (not supported)  */
Packit d7e8d0
		       NULL,                 /* (not supported)  */
Packit d7e8d0
		       FALSE,                /* (not supported)  */
Packit d7e8d0
		       (CREATE_SUSPENDED),   /* Creation flags.  */
Packit d7e8d0
		       NULL,                 /* (not supported)  */
Packit d7e8d0
		       NULL,                 /* (not supported)  */
Packit d7e8d0
		       NULL,                 /* (not supported) */
Packit d7e8d0
		       &pi                   /* Returns process information.*/
Packit d7e8d0
		       ))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      free (cmdline);
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Create arbitrary pipe descriptor to send in ASSIGN_RVID
Packit d7e8d0
     commands.  Errors are ignored.  We don't need read or write access,
Packit d7e8d0
     as ASSIGN_RVID works without any permissions, yay!  */
Packit d7e8d0
  hd = CreateFile (L"GPG1:", 0, 0,
Packit d7e8d0
		   NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
Packit d7e8d0
  if (hd == INVALID_HANDLE_VALUE)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("CreateFile failed (ignored): ec=%d",
Packit d7e8d0
		  (int) GetLastError ());
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Insert the inherited handles.  */
Packit d7e8d0
  for (i = 0; fd_list[i].fd != -1; i++)
Packit d7e8d0
    {
Packit d7e8d0
      /* Return the child name of this handle.  */
Packit d7e8d0
      fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
Packit d7e8d0
Packit d7e8d0
      if (hd != INVALID_HANDLE_VALUE)
Packit d7e8d0
	{
Packit d7e8d0
	  DWORD data[2];
Packit d7e8d0
	  data[0] = (DWORD) fd_table[fd_list[i].fd].rvid;
Packit d7e8d0
	  data[1] = pi.dwProcessId;
Packit d7e8d0
	  if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_ASSIGN_RVID,
Packit d7e8d0
				data, sizeof (data), NULL, 0, NULL, NULL))
Packit d7e8d0
	    {
Packit d7e8d0
	      TRACE_LOG3 ("ASSIGN_RVID(%i, %i) failed (ignored): %i",
Packit d7e8d0
			  data[0], data[1], (int) GetLastError ());
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  if (hd != INVALID_HANDLE_VALUE)
Packit d7e8d0
    CloseHandle (hd);
Packit d7e8d0
Packit d7e8d0
#else
Packit d7e8d0
  SECURITY_ATTRIBUTES sec_attr;
Packit d7e8d0
  STARTUPINFOA si;
Packit d7e8d0
  int cr_flags = CREATE_DEFAULT_ERROR_MODE;
Packit d7e8d0
  char **args;
Packit d7e8d0
  char *arg_string;
Packit d7e8d0
  /* FIXME.  */
Packit d7e8d0
  int debug_me = 0;
Packit d7e8d0
  int tmp_fd;
Packit d7e8d0
  char *tmp_name;
Packit d7e8d0
  const char *spawnhelper;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
Packit d7e8d0
	      "path=%s", path);
Packit d7e8d0
  i = 0;
Packit d7e8d0
  while (argv[i])
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
Packit d7e8d0
      i++;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* We do not inherit any handles by default, and just insert those
Packit d7e8d0
     handles we want the child to have afterwards.  But some handle
Packit d7e8d0
     values occur on the command line, and we need to move
Packit d7e8d0
     stdin/out/err to the right location.  So we use a wrapper program
Packit d7e8d0
     which gets the information from a temporary file.  */
Packit d7e8d0
  if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  TRACE_LOG1 ("tmp_name = %s", tmp_name);
Packit d7e8d0
Packit d7e8d0
  args = calloc (2 + i + 1, sizeof (*args));
Packit d7e8d0
  args[0] = (char *) _gpgme_get_w32spawn_path ();
Packit d7e8d0
  args[1] = tmp_name;
Packit d7e8d0
  args[2] = (char *)path;
Packit d7e8d0
  memcpy (&args[3], &argv[1], i * sizeof (*args));
Packit d7e8d0
Packit d7e8d0
  memset (&sec_attr, 0, sizeof sec_attr);
Packit d7e8d0
  sec_attr.nLength = sizeof sec_attr;
Packit d7e8d0
  sec_attr.bInheritHandle = FALSE;
Packit d7e8d0
Packit d7e8d0
  arg_string = build_commandline (args);
Packit d7e8d0
  free (args);
Packit d7e8d0
  if (!arg_string)
Packit d7e8d0
    {
Packit d7e8d0
      close (tmp_fd);
Packit d7e8d0
      DeleteFileA (tmp_name);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  memset (&si, 0, sizeof si);
Packit d7e8d0
  si.cb = sizeof (si);
Packit d7e8d0
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
Packit d7e8d0
  si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
Packit d7e8d0
  si.hStdInput = INVALID_HANDLE_VALUE;
Packit d7e8d0
  si.hStdOutput = INVALID_HANDLE_VALUE;
Packit d7e8d0
  si.hStdError = INVALID_HANDLE_VALUE;
Packit d7e8d0
Packit d7e8d0
  cr_flags |= CREATE_SUSPENDED;
Packit d7e8d0
  if ((flags & IOSPAWN_FLAG_DETACHED))
Packit d7e8d0
    cr_flags |= DETACHED_PROCESS;
Packit d7e8d0
  cr_flags |= GetPriorityClass (GetCurrentProcess ());
Packit d7e8d0
  spawnhelper = _gpgme_get_w32spawn_path ();
Packit d7e8d0
  if (!spawnhelper)
Packit d7e8d0
    {
Packit d7e8d0
      /* This is a common mistake for new users of gpgme not to include
Packit d7e8d0
         gpgme-w32spawn.exe with their binary. So we want to make
Packit d7e8d0
         this transparent to developers. If users have somehow messed
Packit d7e8d0
         up their installation this should also be properly communicated
Packit d7e8d0
         as otherwise calls to gnupg will result in unsupported protocol
Packit d7e8d0
         errors that do not explain a lot. */
Packit d7e8d0
      char *msg;
Packit d7e8d0
      gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
Packit d7e8d0
                            "detected installation directory of GpgME"
Packit d7e8d0
                            "\n\t\"%s\"\n\n"
Packit d7e8d0
                            "Crypto operations will not work.\n\n"
Packit d7e8d0
                            "If you see this it indicates a problem "
Packit d7e8d0
                            "with your installation.\n"
Packit d7e8d0
                            "Please report the problem to your "
Packit d7e8d0
                            "distributor of GpgME.\n\n"
Packit d7e8d0
                            "Developer's Note: The install dir can be "
Packit d7e8d0
                            "manually set with: gpgme_set_global_flag",
Packit d7e8d0
                            _gpgme_get_inst_dir ());
Packit d7e8d0
      MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
Packit d7e8d0
      gpgrt_free (msg);
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  if (!CreateProcessA (spawnhelper,
Packit d7e8d0
		       arg_string,
Packit d7e8d0
		       &sec_attr,     /* process security attributes */
Packit d7e8d0
		       &sec_attr,     /* thread security attributes */
Packit d7e8d0
		       FALSE,         /* inherit handles */
Packit d7e8d0
		       cr_flags,      /* creation flags */
Packit d7e8d0
		       NULL,          /* environment */
Packit d7e8d0
		       NULL,          /* use current drive/directory */
Packit d7e8d0
		       &si,           /* startup information */
Packit d7e8d0
		       &pi))          /* returns process information */
Packit d7e8d0
    {
Packit d7e8d0
      int lasterr = (int)GetLastError ();
Packit d7e8d0
      TRACE_LOG1 ("CreateProcess failed: ec=%d", lasterr);
Packit d7e8d0
      free (arg_string);
Packit d7e8d0
      close (tmp_fd);
Packit d7e8d0
      DeleteFileA (tmp_name);
Packit d7e8d0
Packit d7e8d0
      /* FIXME: Should translate the error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  free (arg_string);
Packit d7e8d0
Packit d7e8d0
  if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
Packit d7e8d0
    _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
Packit d7e8d0
Packit d7e8d0
  /* Insert the inherited handles.  */
Packit d7e8d0
  for (i = 0; fd_list[i].fd != -1; i++)
Packit d7e8d0
    {
Packit d7e8d0
      int fd = fd_list[i].fd;
Packit d7e8d0
      HANDLE ohd = INVALID_HANDLE_VALUE;
Packit d7e8d0
      HANDLE hd = INVALID_HANDLE_VALUE;
Packit d7e8d0
Packit d7e8d0
      /* Make it inheritable for the wrapper process.  */
Packit d7e8d0
      if (fd >= 0 && fd < MAX_SLAFD && fd_table[fd].used)
Packit d7e8d0
	ohd = fd_table[fd].handle;
Packit d7e8d0
Packit d7e8d0
      if (!DuplicateHandle (GetCurrentProcess(), ohd,
Packit d7e8d0
			    pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  TerminateProcess (pi.hProcess, 0);
Packit d7e8d0
	  /* Just in case TerminateProcess didn't work, let the
Packit d7e8d0
	     process fail on its own.  */
Packit d7e8d0
	  ResumeThread (pi.hThread);
Packit d7e8d0
	  CloseHandle (pi.hThread);
Packit d7e8d0
	  CloseHandle (pi.hProcess);
Packit d7e8d0
Packit d7e8d0
	  close (tmp_fd);
Packit d7e8d0
	  DeleteFileA (tmp_name);
Packit d7e8d0
Packit d7e8d0
	  /* FIXME: Should translate the error code.  */
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
        }
Packit d7e8d0
      /* Return the child name of this handle.  */
Packit d7e8d0
      fd_list[i].peer_name = handle_to_fd (hd);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Write the handle translation information to the temporary
Packit d7e8d0
     file.  */
Packit d7e8d0
  {
Packit d7e8d0
    /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
Packit d7e8d0
       notation: "0xFEDCBA9876543210" with an extra white space after
Packit d7e8d0
       every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
Packit d7e8d0
       for a time when a HANDLE is 64 bit.  */
Packit d7e8d0
#define BUFFER_MAX 810
Packit d7e8d0
    char line[BUFFER_MAX + 1];
Packit d7e8d0
    int res;
Packit d7e8d0
    int written;
Packit d7e8d0
    size_t len;
Packit d7e8d0
Packit d7e8d0
    if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
Packit d7e8d0
      strcpy (line, "~1 \n");
Packit d7e8d0
    else
Packit d7e8d0
      strcpy (line, "\n");
Packit d7e8d0
    for (i = 0; fd_list[i].fd != -1; i++)
Packit d7e8d0
      {
Packit d7e8d0
	/* Strip the newline.  */
Packit d7e8d0
	len = strlen (line) - 1;
Packit d7e8d0
Packit d7e8d0
	/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
Packit d7e8d0
	snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
Packit d7e8d0
		  fd_list[i].fd, fd_list[i].dup_to,
Packit d7e8d0
		  fd_list[i].peer_name, fd_list[i].arg_loc);
Packit d7e8d0
	/* Rather safe than sorry.  */
Packit d7e8d0
	line[BUFFER_MAX - 1] = '\n';
Packit d7e8d0
	line[BUFFER_MAX] = '\0';
Packit d7e8d0
      }
Packit d7e8d0
    len = strlen (line);
Packit d7e8d0
    written = 0;
Packit d7e8d0
    do
Packit d7e8d0
      {
Packit d7e8d0
	res = write (tmp_fd, &line[written], len - written);
Packit d7e8d0
	if (res > 0)
Packit d7e8d0
	  written += res;
Packit d7e8d0
      }
Packit d7e8d0
    while (res > 0 || (res < 0 && errno == EAGAIN));
Packit d7e8d0
  }
Packit d7e8d0
  close (tmp_fd);
Packit d7e8d0
  /* The temporary file is deleted by the gpgme-w32spawn process
Packit d7e8d0
     (hopefully).  */
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
Packit d7e8d0
	      "dwProcessID=%d, dwThreadId=%d",
Packit d7e8d0
	      pi.hProcess, pi.hThread,
Packit d7e8d0
	      (int) pi.dwProcessId, (int) pi.dwThreadId);
Packit d7e8d0
Packit d7e8d0
  if (r_pid)
Packit d7e8d0
    *r_pid = (pid_t)pi.dwProcessId;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  if (ResumeThread (pi.hThread) < 0)
Packit d7e8d0
    TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
Packit d7e8d0
  if (!CloseHandle (pi.hThread))
Packit d7e8d0
    TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
Packit d7e8d0
		(int) GetLastError ());
Packit d7e8d0
Packit d7e8d0
  TRACE_LOG1 ("process=%p", pi.hProcess);
Packit d7e8d0
Packit d7e8d0
  /* We don't need to wait for the process.  */
Packit d7e8d0
  if (!CloseHandle (pi.hProcess))
Packit d7e8d0
    TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
Packit d7e8d0
		(int) GetLastError ());
Packit d7e8d0
Packit d7e8d0
  if (! (flags & IOSPAWN_FLAG_NOCLOSE))
Packit d7e8d0
    {
Packit d7e8d0
      for (i = 0; fd_list[i].fd != -1; i++)
Packit d7e8d0
	_gpgme_io_close (fd_list[i].fd);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  for (i = 0; fd_list[i].fd != -1; i++)
Packit d7e8d0
    if (fd_list[i].dup_to == -1)
Packit d7e8d0
      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
Packit d7e8d0
		  fd_list[i].peer_name);
Packit d7e8d0
    else
Packit d7e8d0
      TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
Packit d7e8d0
		  fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
Packit d7e8d0
		  ((fd_list[i].dup_to == 1) ? "out" : "err"));
Packit d7e8d0
Packit d7e8d0
  return TRACE_SYSRES (0);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
Packit d7e8d0
   nothing to select, > 0 = number of signaled fds.  */
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
Packit d7e8d0
{
Packit d7e8d0
  HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
Packit d7e8d0
  int waitidx[MAXIMUM_WAIT_OBJECTS];
Packit d7e8d0
  int code;
Packit d7e8d0
  int nwait;
Packit d7e8d0
  int i;
Packit d7e8d0
  int any;
Packit d7e8d0
  int count;
Packit d7e8d0
  void *dbg_help;
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
Packit d7e8d0
	      "nfds=%u, nonblock=%u", nfds, nonblock);
Packit d7e8d0
Packit d7e8d0
#if 0
Packit d7e8d0
 restart:
Packit d7e8d0
#endif
Packit d7e8d0
  TRACE_SEQ (dbg_help, "select on [ ");
Packit d7e8d0
  any = 0;
Packit d7e8d0
  nwait = 0;
Packit d7e8d0
  count = 0;
Packit d7e8d0
  for (i=0; i < nfds; i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (fds[i].fd == -1)
Packit d7e8d0
	continue;
Packit d7e8d0
      fds[i].signaled = 0;
Packit d7e8d0
      if (fds[i].for_read || fds[i].for_write)
Packit d7e8d0
	{
Packit d7e8d0
	  if (fds[i].for_read)
Packit d7e8d0
	    {
Packit d7e8d0
	      struct reader_context_s *ctx = find_reader (fds[i].fd,1);
Packit d7e8d0
Packit d7e8d0
	      if (!ctx)
Packit d7e8d0
		TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
Packit d7e8d0
			    fds[i].fd);
Packit d7e8d0
	      else
Packit d7e8d0
		{
Packit d7e8d0
		  if (nwait >= DIM (waitbuf))
Packit d7e8d0
		    {
Packit d7e8d0
		      TRACE_END (dbg_help, "oops ]");
Packit d7e8d0
		      TRACE_LOG ("Too many objects for WFMO!");
Packit d7e8d0
		      /* FIXME: Should translate the error code.  */
Packit d7e8d0
		      gpg_err_set_errno (EIO);
Packit d7e8d0
		      return TRACE_SYSRES (-1);
Packit d7e8d0
                    }
Packit d7e8d0
		  waitidx[nwait] = i;
Packit d7e8d0
		  waitbuf[nwait++] = ctx->have_data_ev;
Packit d7e8d0
                }
Packit d7e8d0
	      TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
Packit d7e8d0
	      any = 1;
Packit d7e8d0
            }
Packit d7e8d0
	  else if (fds[i].for_write)
Packit d7e8d0
	    {
Packit d7e8d0
	      struct writer_context_s *ctx = find_writer (fds[i].fd,1);
Packit d7e8d0
Packit d7e8d0
	      if (!ctx)
Packit d7e8d0
		TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
Packit d7e8d0
			    fds[i].fd);
Packit d7e8d0
	      else
Packit d7e8d0
		{
Packit d7e8d0
		  if (nwait >= DIM (waitbuf))
Packit d7e8d0
		    {
Packit d7e8d0
		      TRACE_END (dbg_help, "oops ]");
Packit d7e8d0
		      TRACE_LOG ("Too many objects for WFMO!");
Packit d7e8d0
		      /* FIXME: Should translate the error code.  */
Packit d7e8d0
		      gpg_err_set_errno (EIO);
Packit d7e8d0
		      return TRACE_SYSRES (-1);
Packit d7e8d0
                    }
Packit d7e8d0
		  waitidx[nwait] = i;
Packit d7e8d0
		  waitbuf[nwait++] = ctx->is_empty;
Packit d7e8d0
                }
Packit d7e8d0
	      TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
Packit d7e8d0
	      any = 1;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
  TRACE_END (dbg_help, "]");
Packit d7e8d0
  if (!any)
Packit d7e8d0
    return TRACE_SYSRES (0);
Packit d7e8d0
Packit d7e8d0
  code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
Packit d7e8d0
  if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
Packit d7e8d0
    {
Packit d7e8d0
      /* This WFMO is a really silly function: It does return either
Packit d7e8d0
	 the index of the signaled object or if 2 objects have been
Packit d7e8d0
	 signalled at the same time, the index of the object with the
Packit d7e8d0
	 lowest object is returned - so and how do we find out how
Packit d7e8d0
	 many objects have been signaled???.  The only solution I can
Packit d7e8d0
	 imagine is to test each object starting with the returned
Packit d7e8d0
	 index individually - how dull.  */
Packit d7e8d0
      any = 0;
Packit d7e8d0
      for (i = code - WAIT_OBJECT_0; i < nwait; i++)
Packit d7e8d0
	{
Packit d7e8d0
	  if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
Packit d7e8d0
	    {
Packit d7e8d0
	      assert (waitidx[i] >=0 && waitidx[i] < nfds);
Packit d7e8d0
	      fds[waitidx[i]].signaled = 1;
Packit d7e8d0
	      any = 1;
Packit d7e8d0
	      count++;
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
      if (!any)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG ("no signaled objects found after WFMO");
Packit d7e8d0
	  count = -1;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  else if (code == WAIT_TIMEOUT)
Packit d7e8d0
    TRACE_LOG ("WFMO timed out");
Packit d7e8d0
  else if (code == WAIT_FAILED)
Packit d7e8d0
    {
Packit d7e8d0
      int le = (int) GetLastError ();
Packit d7e8d0
#if 0
Packit d7e8d0
      if (le == ERROR_INVALID_HANDLE)
Packit d7e8d0
	{
Packit d7e8d0
	  int k;
Packit d7e8d0
	  int j = handle_to_fd (waitbuf[i]);
Packit d7e8d0
Packit d7e8d0
	  TRACE_LOG1 ("WFMO invalid handle %d removed", j);
Packit d7e8d0
	  for (k = 0 ; k < nfds; k++)
Packit d7e8d0
	    {
Packit d7e8d0
	      if (fds[k].fd == j)
Packit d7e8d0
		{
Packit d7e8d0
		  fds[k].for_read = fds[k].for_write = 0;
Packit d7e8d0
		  goto restart;
Packit d7e8d0
                }
Packit d7e8d0
            }
Packit d7e8d0
	  TRACE_LOG (" oops, or not???");
Packit d7e8d0
        }
Packit d7e8d0
#endif
Packit d7e8d0
      TRACE_LOG1 ("WFMO failed: %d", le);
Packit d7e8d0
      count = -1;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_LOG1 ("WFMO returned %d", code);
Packit d7e8d0
      count = -1;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (count > 0)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_SEQ (dbg_help, "select OK [ ");
Packit d7e8d0
      for (i = 0; i < nfds; i++)
Packit d7e8d0
	{
Packit d7e8d0
	  if (fds[i].fd == -1)
Packit d7e8d0
	    continue;
Packit d7e8d0
	  if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
Packit d7e8d0
	    TRACE_ADD2 (dbg_help, "%c0x%x ",
Packit d7e8d0
			fds[i].for_read ? 'r' : 'w', fds[i].fd);
Packit d7e8d0
        }
Packit d7e8d0
      TRACE_END (dbg_help, "]");
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (count < 0)
Packit d7e8d0
    {
Packit d7e8d0
      /* FIXME: Should determine a proper error code.  */
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return TRACE_SYSRES (count);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
void
Packit d7e8d0
_gpgme_io_subsystem_init (void)
Packit d7e8d0
{
Packit d7e8d0
  /* Nothing to do.  */
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Write the printable version of FD to the buffer BUF of length
Packit d7e8d0
   BUFLEN.  The printable version is the representation on the command
Packit d7e8d0
   line that the child process expects.  */
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_fd2str (char *buf, int buflen, int fd)
Packit d7e8d0
{
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
  /* FIXME: For now. See above.  */
Packit d7e8d0
  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
Packit d7e8d0
      || fd_table[fd].rvid == 0)
Packit d7e8d0
    fd = -1;
Packit d7e8d0
  else
Packit d7e8d0
    fd = fd_table[fd].rvid;
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  return snprintf (buf, buflen, "%d", fd);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_dup (int fd)
Packit d7e8d0
{
Packit d7e8d0
  int newfd;
Packit d7e8d0
  struct reader_context_s *rd_ctx;
Packit d7e8d0
  struct writer_context_s *wt_ctx;
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
Packit d7e8d0
Packit d7e8d0
  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EINVAL);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  newfd = new_fd();
Packit d7e8d0
  if (newfd == -1)
Packit d7e8d0
    return TRACE_SYSRES (-1);
Packit d7e8d0
Packit d7e8d0
  fd_table[newfd].handle = fd_table[fd].handle;
Packit d7e8d0
  fd_table[newfd].socket = fd_table[fd].socket;
Packit d7e8d0
  fd_table[newfd].rvid = fd_table[fd].rvid;
Packit d7e8d0
  fd_table[newfd].dup_from = fd;
Packit d7e8d0
Packit d7e8d0
  rd_ctx = find_reader (fd, 1);
Packit d7e8d0
  if (rd_ctx)
Packit d7e8d0
    {
Packit d7e8d0
      /* No need for locking, as the only races are against the reader
Packit d7e8d0
	 thread itself, which doesn't touch refcount.  */
Packit d7e8d0
      rd_ctx->refcount++;
Packit d7e8d0
Packit d7e8d0
      LOCK (reader_table_lock);
Packit d7e8d0
      for (i = 0; i < reader_table_size; i++)
Packit d7e8d0
	if (!reader_table[i].used)
Packit d7e8d0
	  break;
Packit d7e8d0
      /* FIXME.  */
Packit d7e8d0
      assert (i != reader_table_size);
Packit d7e8d0
      reader_table[i].fd = newfd;
Packit d7e8d0
      reader_table[i].context = rd_ctx;
Packit d7e8d0
      reader_table[i].used = 1;
Packit d7e8d0
      UNLOCK (reader_table_lock);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  wt_ctx = find_writer (fd, 1);
Packit d7e8d0
  if (wt_ctx)
Packit d7e8d0
    {
Packit d7e8d0
      /* No need for locking, as the only races are against the writer
Packit d7e8d0
	 thread itself, which doesn't touch refcount.  */
Packit d7e8d0
      wt_ctx->refcount++;
Packit d7e8d0
Packit d7e8d0
      LOCK (writer_table_lock);
Packit d7e8d0
      for (i = 0; i < writer_table_size; i++)
Packit d7e8d0
	if (!writer_table[i].used)
Packit d7e8d0
	  break;
Packit d7e8d0
      /* FIXME.  */
Packit d7e8d0
      assert (i != writer_table_size);
Packit d7e8d0
      writer_table[i].fd = newfd;
Packit d7e8d0
      writer_table[i].context = wt_ctx;
Packit d7e8d0
      writer_table[i].used = 1;
Packit d7e8d0
      UNLOCK (writer_table_lock);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return TRACE_SYSRES (newfd);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* The following interface is only useful for GPGME Glib and Qt.  */
Packit d7e8d0
Packit d7e8d0
/* Compatibility interface, obsolete.  */
Packit d7e8d0
void *
Packit d7e8d0
gpgme_get_giochannel (int fd)
Packit d7e8d0
{
Packit d7e8d0
  return NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Look up the giochannel or qiodevice for file descriptor FD.  */
Packit d7e8d0
void *
Packit d7e8d0
gpgme_get_fdptr (int fd)
Packit d7e8d0
{
Packit d7e8d0
  return NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static int
Packit d7e8d0
wsa2errno (int err)
Packit d7e8d0
{
Packit d7e8d0
  switch (err)
Packit d7e8d0
    {
Packit d7e8d0
    case WSAENOTSOCK:
Packit d7e8d0
      return EINVAL;
Packit d7e8d0
    case WSAEWOULDBLOCK:
Packit d7e8d0
      return EAGAIN;
Packit d7e8d0
    case ERROR_BROKEN_PIPE:
Packit d7e8d0
      return EPIPE;
Packit d7e8d0
    case WSANOTINITIALISED:
Packit d7e8d0
      return ENOSYS;
Packit d7e8d0
    default:
Packit d7e8d0
      return EIO;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_socket (int domain, int type, int proto)
Packit d7e8d0
{
Packit d7e8d0
  int res;
Packit d7e8d0
  int fd;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
Packit d7e8d0
	      "type=%i, protp=%i", type, proto);
Packit d7e8d0
Packit d7e8d0
  fd = new_fd();
Packit d7e8d0
  if (fd == -1)
Packit d7e8d0
    return TRACE_SYSRES (-1);
Packit d7e8d0
Packit d7e8d0
  res = socket (domain, type, proto);
Packit d7e8d0
  if (res == INVALID_SOCKET)
Packit d7e8d0
    {
Packit d7e8d0
      release_fd (fd);
Packit d7e8d0
      gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
  fd_table[fd].socket = res;
Packit d7e8d0
Packit d7e8d0
  TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
Packit d7e8d0
Packit d7e8d0
  return fd;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
Packit d7e8d0
{
Packit d7e8d0
  int res;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
Packit d7e8d0
	      "addr=%p, addrlen=%i", addr, addrlen);
Packit d7e8d0
Packit d7e8d0
  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EBADF);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  res = connect (fd_table[fd].socket, addr, addrlen);
Packit d7e8d0
  if (res)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return TRACE_SUC ();
Packit d7e8d0
}