Blame src/w32-io.c

Packit d7e8d0
/* w32-io.c - W32 API I/O functions.
Packit Service 30b792
 * Copyright (C) 2000 Werner Koch (dd9jn)
Packit Service 30b792
 * Copyright (C) 2001-2004, 2007, 2010, 2018 g10 Code GmbH
Packit Service 30b792
 *
Packit Service 30b792
 * This file is part of GPGME.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is free software; you can redistribute it and/or modify it
Packit Service 30b792
 * under the terms of the GNU Lesser General Public License as
Packit Service 30b792
 * published by the Free Software Foundation; either version 2.1 of
Packit Service 30b792
 * the License, or (at your option) any later version.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is distributed in the hope that it will be useful, but
Packit Service 30b792
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 30b792
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 30b792
 * Lesser General Public License for more details.
Packit Service 30b792
 *
Packit Service 30b792
 * You should have received a copy of the GNU Lesser General Public
Packit Service 30b792
 * License along with this program; if not, see <https://gnu.org/licenses/>.
Packit Service 30b792
 * SPDX-License-Identifier: LGPL-2.1+
Packit Service 30b792
 */
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
#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 Service 30b792
/* The number of entries in our file table.  We may eventually use a
Packit Service 30b792
 * lower value and dynamically resize the table.  */
Packit d7e8d0
#define MAX_SLAFD 512
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
Packit Service 30b792
Packit Service 30b792
/* An object to store handles or sockets.  */
Packit Service 30b792
struct hddesc_s
Packit d7e8d0
{
Packit Service 30b792
  HANDLE hd;
Packit Service 30b792
  SOCKET sock;
Packit Service 30b792
  int refcount;
Packit Service 30b792
};
Packit Service 30b792
typedef struct hddesc_s *hddesc_t;
Packit Service 30b792
Packit d7e8d0
Packit d7e8d0
Packit Service 30b792
/* The context used by a reader thread.  */
Packit d7e8d0
struct reader_context_s
Packit d7e8d0
{
Packit Service 30b792
  hddesc_t hdd;
Packit d7e8d0
  HANDLE thread_hd;
Packit Service 30b792
  int refcount;   /* Bumped if the FD has been duped and thus we have
Packit Service 30b792
                   * another FD referencing this context.  */
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 Service 30b792
/* The context used by a writer thread.  */
Packit d7e8d0
struct writer_context_s
Packit d7e8d0
{
Packit Service 30b792
  hddesc_t hdd;
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 Service 30b792
/* An object to keep track of HANDLEs and sockets and map them to an
Packit Service 30b792
 * integer similar to what we use in Unix.  Note that despite this
Packit Service 30b792
 * integer is often named "fd", it is not a file descriptor but really
Packit Service 30b792
 * only an index into this table.  Never ever pass such an fd to any
Packit Service 30b792
 * other function except for those implemented here.  */
Packit d7e8d0
static struct
Packit d7e8d0
{
Packit Service 30b792
  int used;
Packit Service 30b792
Packit Service 30b792
  /* The handle descriptor.  */
Packit Service 30b792
  hddesc_t hdd;
Packit Service 30b792
Packit Service 30b792
  /* DUP_FROM is just a debug helper to show from which fd this fd was
Packit Service 30b792
   * dup-ed. */
Packit Service 30b792
  int dup_from;
Packit Service 30b792
Packit Service 30b792
  /* Two flags to indicate whether a reader or writer (or both) are
Packit Service 30b792
   * needed.  This is so that we can delay the actual thread creation
Packit Service 30b792
   * until they are needed.  */
Packit Service 30b792
  unsigned int want_reader:1;
Packit Service 30b792
  unsigned int want_writer:1;
Packit Service 30b792
Packit Service 30b792
  /* The context of an associated reader object or NULL.  */
Packit Service 30b792
  struct reader_context_s *reader;
Packit Service 30b792
Packit Service 30b792
  /* The context of an associated writer object or NULL.  */
Packit Service 30b792
  struct writer_context_s *writer;
Packit Service 30b792
Packit Service 30b792
  /* A notification handler.  Noet that we current support only one
Packit Service 30b792
   * callback per fd.  */
Packit Service 30b792
  struct {
Packit Service 30b792
    _gpgme_close_notify_handler_t handler;
Packit Service 30b792
    void *value;
Packit Service 30b792
  } notify;
Packit Service 30b792
Packit Service 30b792
} fd_table[MAX_SLAFD];
Packit Service 30b792
static size_t fd_table_size = MAX_SLAFD;
Packit Service 30b792
Packit Service 30b792
DEFINE_STATIC_LOCK (fd_table_lock);
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* We use a single global lock for all hddesc_t objects.  */
Packit Service 30b792
DEFINE_STATIC_LOCK (hddesc_lock);
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792

Packit Service 30b792
/* Wrapper around CloseHandle to print an error.  */
Packit Service 30b792
#define close_handle(hd) _close_handle ((hd), __LINE__);
Packit Service 30b792
static void
Packit Service 30b792
_close_handle (HANDLE hd, int line)
Packit Service 30b792
{
Packit Service 30b792
  if (!CloseHandle (hd))
Packit Service 30b792
    {
Packit Service 30b792
      TRACE (DEBUG_INIT, "w32-io", hd, "CloseHandle failed at line %d: ec=%d",
Packit Service 30b792
              line, (int) GetLastError ());
Packit Service 30b792
    }
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
/* Wrapper around WaitForSingleObject to print an error.  */
Packit Service 30b792
#define wait_for_single_object(hd,msec) \
Packit Service 30b792
        _wait_for_single_object ((hd), (msec), __LINE__)
Packit Service 30b792
static DWORD
Packit Service 30b792
_wait_for_single_object (HANDLE hd, DWORD msec, int line)
Packit Service 30b792
{
Packit Service 30b792
  DWORD res;
Packit Service 30b792
Packit Service 30b792
  res = WaitForSingleObject (hd, msec);
Packit Service 30b792
  if (res == WAIT_FAILED)
Packit Service 30b792
    {
Packit Service 30b792
      TRACE (DEBUG_INIT, "w32-io", hd,
Packit Service 30b792
              "WFSO failed at line %d: ec=%d", line, (int) GetLastError ());
Packit Service 30b792
    }
Packit Service 30b792
  return res;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* Create a new handle descriptor object.  */
Packit Service 30b792
static hddesc_t
Packit Service 30b792
new_hddesc (void)
Packit Service 30b792
{
Packit Service 30b792
  hddesc_t hdd;
Packit Service 30b792
Packit Service 30b792
  hdd = malloc (sizeof *hdd);
Packit Service 30b792
  if (!hdd)
Packit Service 30b792
    return NULL;
Packit Service 30b792
  hdd->hd = INVALID_HANDLE_VALUE;
Packit Service 30b792
  hdd->sock = INVALID_SOCKET;
Packit Service 30b792
  hdd->refcount = 0;
Packit Service 30b792
Packit Service 30b792
  return hdd;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
static hddesc_t
Packit Service 30b792
ref_hddesc (hddesc_t hdd)
Packit Service 30b792
{
Packit Service 30b792
  LOCK (hddesc_lock);
Packit Service 30b792
  hdd->refcount++;
Packit Service 30b792
  UNLOCK (hddesc_lock);
Packit Service 30b792
  return hdd;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* Release a handle descriptor object and close its handle or socket
Packit Service 30b792
 * if needed.  */
Packit Service 30b792
static void
Packit Service 30b792
release_hddesc (hddesc_t hdd)
Packit Service 30b792
{
Packit Service 30b792
  if (!hdd)
Packit Service 30b792
    return;
Packit Service 30b792
Packit Service 30b792
  LOCK (hddesc_lock);
Packit Service 30b792
  hdd->refcount--;
Packit Service 30b792
  if (hdd->refcount < 1)
Packit Service 30b792
    {
Packit Service 30b792
      /* Holds a valid handle or was never initialized (in which case
Packit Service 30b792
       * REFCOUNT would be -1 here).  */
Packit Service 30b792
      TRACE_BEG  (DEBUG_SYSIO, "gpgme:release_hddesc", hdd,
Packit Service 30b792
                  "hd=%p, sock=%d, refcount=%d",
Packit Service 30b792
                  hdd->hd, hdd->sock, hdd->refcount);
Packit Service 30b792
Packit Service 30b792
      if (hdd->hd != INVALID_HANDLE_VALUE)
Packit Service 30b792
        close_handle (hdd->hd);
Packit Service 30b792
Packit Service 30b792
      if (hdd->sock != INVALID_SOCKET)
Packit Service 30b792
        {
Packit Service 30b792
          TRACE_LOG  ("closing socket %d", hdd->sock);
Packit Service 30b792
          if (closesocket (hdd->sock))
Packit Service 30b792
            {
Packit Service 30b792
              TRACE_LOG  ("closesocket failed: ec=%d", (int)WSAGetLastError ());
Packit Service 30b792
            }
Packit Service 30b792
        }
Packit Service 30b792
Packit Service 30b792
      free (hdd);
Packit Service 30b792
      TRACE_SUC ("");
Packit Service 30b792
    }
Packit Service 30b792
  UNLOCK (hddesc_lock);
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* Returns our FD or -1 on resource limit.  The returned integer
Packit Service 30b792
 * references a new object which has not been initialized but can be
Packit Service 30b792
 * release with release_fd.  */
Packit Service 30b792
static int
Packit Service 30b792
new_fd (void)
Packit Service 30b792
{
Packit Service 30b792
  int idx;
Packit Service 30b792
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
Packit Service 30b792
  for (idx = 0; idx < fd_table_size; idx++)
Packit Service 30b792
    if (! fd_table[idx].used)
Packit Service 30b792
      break;
Packit Service 30b792
Packit Service 30b792
  if (idx == fd_table_size)
Packit Service 30b792
    {
Packit Service 30b792
      gpg_err_set_errno (EIO);
Packit Service 30b792
      idx = -1;
Packit Service 30b792
    }
Packit Service 30b792
  else
Packit Service 30b792
    {
Packit Service 30b792
      fd_table[idx].used = 1;
Packit Service 30b792
      fd_table[idx].hdd = NULL;
Packit Service 30b792
      fd_table[idx].dup_from = -1;
Packit Service 30b792
      fd_table[idx].want_reader = 0;
Packit Service 30b792
      fd_table[idx].want_writer = 0;
Packit Service 30b792
      fd_table[idx].reader = NULL;
Packit Service 30b792
      fd_table[idx].writer = NULL;
Packit Service 30b792
      fd_table[idx].notify.handler = NULL;
Packit Service 30b792
      fd_table[idx].notify.value = NULL;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit Service 30b792
Packit Service 30b792
  return idx;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* Releases our FD but it this is just this entry.  No close operation
Packit Service 30b792
 * is involved here; it must be done prior to calling this
Packit Service 30b792
 * function.  */
Packit Service 30b792
static void
Packit Service 30b792
release_fd (int fd)
Packit Service 30b792
{
Packit Service 30b792
  if (fd < 0 || fd >= fd_table_size)
Packit Service 30b792
    return;
Packit Service 30b792
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
Packit Service 30b792
  if (fd_table[fd].used)
Packit Service 30b792
    {
Packit Service 30b792
      release_hddesc (fd_table[fd].hdd);
Packit Service 30b792
      fd_table[fd].used = 0;
Packit Service 30b792
      fd_table[fd].hdd = NULL;
Packit Service 30b792
      fd_table[fd].dup_from = -1;
Packit Service 30b792
      fd_table[fd].want_reader = 0;
Packit Service 30b792
      fd_table[fd].want_writer = 0;
Packit Service 30b792
      fd_table[fd].reader = NULL;
Packit Service 30b792
      fd_table[fd].writer = NULL;
Packit Service 30b792
      fd_table[fd].notify.handler = NULL;
Packit Service 30b792
      fd_table[fd].notify.value = NULL;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit Service 30b792
}
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 Service 30b792
      TRACE (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
Packit d7e8d0
	      "%d (default)", value);
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit Service 30b792
      TRACE (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 Service 30b792
/* The reader thread.  Created on the fly by gpgme_io_read and
Packit Service 30b792
 * destroyed by destroy_reader.  Note that this functions works with a
Packit Service 30b792
 * copy of the value of the HANDLE variable frm the FS_TABLE.  */
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
Packit Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "gpgme:reader", ctx->hdd,
Packit Service 30b792
	      "hd=%p, sock=%d, thread=%p, refcount=%d",
Packit Service 30b792
              ctx->hdd->hd, ctx->hdd->sock, ctx->thread_hd, ctx->refcount);
Packit Service 30b792
Packit Service 30b792
  if (ctx->hdd->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 Service 30b792
            {
Packit Service 30b792
              TRACE_LOG  ("ResetEvent failed: ec=%d", (int) GetLastError ());
Packit Service 30b792
            }
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit Service 30b792
	  TRACE_LOG  ("waiting for space (refcnt=%d)", ctx->refcount);
Packit Service 30b792
	  wait_for_single_object (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 Service 30b792
      TRACE_LOG  ("%s %d bytes", sock? "receiving":"reading", nbytes);
Packit d7e8d0
Packit d7e8d0
      if (sock)
Packit d7e8d0
        {
Packit d7e8d0
          int n;
Packit d7e8d0
Packit Service 30b792
          n = recv (ctx->hdd->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 Service 30b792
                  TRACE_LOG  ("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 Service 30b792
          if (!ReadFile (ctx->hdd->hd,
Packit d7e8d0
                         ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
Packit d7e8d0
            {
Packit d7e8d0
              ctx->error_code = (int) GetLastError ();
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 Service 30b792
              else if (ctx->error_code == ERROR_OPERATION_ABORTED)
Packit Service 30b792
                {
Packit Service 30b792
                  ctx->eof = 1;
Packit Service 30b792
                  TRACE_LOG ("got EOF (closed by us)");
Packit Service 30b792
                }
Packit d7e8d0
              else
Packit d7e8d0
                {
Packit d7e8d0
                  ctx->error = 1;
Packit Service 30b792
                  TRACE_LOG  ("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 Service 30b792
      TRACE_LOG  ("got %lu bytes (refcnt=%d)", nread, ctx->refcount);
Packit d7e8d0
Packit d7e8d0
      ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
Packit d7e8d0
      if (!SetEvent (ctx->have_data_ev))
Packit Service 30b792
        {
Packit Service 30b792
          TRACE_LOG  ("SetEvent (%p) failed: ec=%d", ctx->have_data_ev,
Packit Service 30b792
                      (int) GetLastError ());
Packit Service 30b792
        }
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 Service 30b792
    {
Packit Service 30b792
      TRACE_LOG ("SetEvent (%p) failed: ec=%d", ctx->have_data_ev,
Packit d7e8d0
                (int) GetLastError ());
Packit Service 30b792
    }
Packit d7e8d0
Packit d7e8d0
  TRACE_LOG ("waiting for close");
Packit Service 30b792
  wait_for_single_object (ctx->close_ev, INFINITE);
Packit d7e8d0
Packit Service 30b792
  release_hddesc (ctx->hdd);
Packit Service 30b792
  close_handle (ctx->close_ev);
Packit Service 30b792
  close_handle (ctx->have_data_ev);
Packit Service 30b792
  close_handle (ctx->have_space_ev);
Packit Service 30b792
  close_handle (ctx->thread_hd);
Packit d7e8d0
  DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
  free (ctx);
Packit d7e8d0
Packit Service 30b792
  TRACE_SUC ("");
Packit Service 30b792
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit Service 30b792
/* Create a new reader thread and return its context object.  The
Packit Service 30b792
 * input is the handle descriptor HDD.  This function may not call any
Packit Service 30b792
 * fd based functions because the caller already holds a lock on the
Packit Service 30b792
 * fd_table.  */
Packit d7e8d0
static struct reader_context_s *
Packit Service 30b792
create_reader (hddesc_t hdd)
Packit d7e8d0
{
Packit d7e8d0
  struct reader_context_s *ctx;
Packit d7e8d0
  SECURITY_ATTRIBUTES sec_attr;
Packit d7e8d0
  DWORD tid;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "gpgme:create_reader", hdd,
Packit Service 30b792
              "handle=%p sock=%d refhdd=%d",
Packit Service 30b792
              hdd->hd, hdd->sock, hdd->refcount);
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 Service 30b792
  ctx->hdd = ref_hddesc (hdd);
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 Service 30b792
      TRACE_LOG  ("CreateEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      if (ctx->have_data_ev)
Packit Service 30b792
	close_handle (ctx->have_data_ev);
Packit d7e8d0
      if (ctx->have_space_ev)
Packit Service 30b792
	close_handle (ctx->have_space_ev);
Packit d7e8d0
      if (ctx->close_ev)
Packit Service 30b792
	close_handle (ctx->close_ev);
Packit Service 30b792
      release_hddesc (ctx->hdd);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  INIT_LOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
  ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid;;
Packit d7e8d0
Packit d7e8d0
  if (!ctx->thread_hd)
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_LOG  ("CreateThread failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
      if (ctx->have_data_ev)
Packit Service 30b792
	close_handle (ctx->have_data_ev);
Packit d7e8d0
      if (ctx->have_space_ev)
Packit Service 30b792
	close_handle (ctx->have_space_ev);
Packit d7e8d0
      if (ctx->close_ev)
Packit Service 30b792
	close_handle (ctx->close_ev);
Packit Service 30b792
      release_hddesc (ctx->hdd);
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 Service 30b792
  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 Service 30b792
      TRACE (DEBUG_SYSIO, "gpgme:destroy_reader", ctx,
Packit Service 30b792
              "hdd=%p refcount now %d", ctx->hdd, ctx->refcount);
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 Service 30b792
  TRACE (DEBUG_SYSIO, "gpgme:destroy_reader", ctx,
Packit Service 30b792
          "hdd=%p close triggered", ctx->hdd);
Packit d7e8d0
  UNLOCK (ctx->mutex);
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 Service 30b792
  assert (ctx->hdd);
Packit Service 30b792
  if (ctx->hdd && ctx->hdd->hd != INVALID_HANDLE_VALUE)
Packit d7e8d0
    {
Packit Service 30b792
      _gpgme_w32_cancel_synchronous_io (ctx->thread_hd);
Packit d7e8d0
    }
Packit Service 30b792
  else if (ctx->hdd && ctx->hdd->sock != INVALID_SOCKET)
Packit d7e8d0
    {
Packit Service 30b792
      if (shutdown (ctx->hdd->sock, 2))
Packit Service 30b792
        TRACE (DEBUG_SYSIO, "gpgme:destroy_reader", ctx,
Packit Service 30b792
                "shutdown socket %d failed: ec=%d",
Packit Service 30b792
                ctx->hdd->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 Service 30b792
 * context will last until a _gpgme_io_close.  NULL is returned for a
Packit Service 30b792
 * bad FD or for other errors.  */
Packit d7e8d0
static struct reader_context_s *
Packit Service 30b792
find_reader (int fd)
Packit d7e8d0
{
Packit d7e8d0
  struct reader_context_s *rd = NULL;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG (DEBUG_SYSIO, "gpgme:find_reader", fd, "");
Packit d7e8d0
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
  if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used)
Packit d7e8d0
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      gpg_err_set_errno (EBADF);
Packit Service 30b792
      TRACE_SUC ("EBADF");
Packit Service 30b792
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  rd = fd_table[fd].reader;
Packit Service 30b792
  if (rd)
Packit d7e8d0
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      TRACE_SUC ("rd=%p", rd);
Packit Service 30b792
      return rd;  /* Return already initialized reader thread object.  */
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  /* Create a new reader thread.  */
Packit Service 30b792
  TRACE_LOG  ("fd=%d -> hdd=%p dupfrom=%d creating reader",
Packit Service 30b792
              fd, fd_table[fd].hdd, fd_table[fd].dup_from);
Packit Service 30b792
  rd = create_reader (fd_table[fd].hdd);
Packit Service 30b792
  if (!rd)
Packit Service 30b792
    gpg_err_set_errno (EIO);
Packit Service 30b792
  else
Packit Service 30b792
    fd_table[fd].reader = rd;
Packit Service 30b792
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit Service 30b792
  TRACE_SUC ("rd=%p (new)", rd);
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 Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_read", fd,
Packit d7e8d0
	      "buffer=%p, count=%u", buffer, count);
Packit d7e8d0
Packit Service 30b792
  ctx = find_reader (fd);
Packit d7e8d0
  if (!ctx)
Packit Service 30b792
    return TRACE_SYSRES (-1);
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 Service 30b792
      TRACE_LOG  ("waiting for data from thread %p", ctx->thread_hd);
Packit Service 30b792
      wait_for_single_object (ctx->have_data_ev, INFINITE);
Packit Service 30b792
      TRACE_LOG  ("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 Service 30b792
	  TRACE_LOG  ("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 Service 30b792
      TRACE_LOG  ("SetEvent (%p) 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 Service 30b792
  TRACE_LOGBUFX (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 Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "gpgme:writer", ctx->hdd,
Packit Service 30b792
	      "hd=%p, sock=%d, thread=%p, refcount=%d",
Packit Service 30b792
              ctx->hdd->hd, ctx->hdd->sock, ctx->thread_hd, ctx->refcount);
Packit d7e8d0
Packit Service 30b792
  if (ctx->hdd->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 Service 30b792
	    TRACE_LOG  ("SetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  if (!ResetEvent (ctx->have_data))
Packit Service 30b792
	    TRACE_LOG  ("ResetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
	  UNLOCK (ctx->mutex);
Packit d7e8d0
	  TRACE_LOG ("idle");
Packit Service 30b792
	  wait_for_single_object (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 Service 30b792
      TRACE_LOG  ("%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 Service 30b792
          n = send (ctx->hdd->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 Service 30b792
              TRACE_LOG  ("send error: ec=%d", ctx->error_code);
Packit d7e8d0
              break;
Packit d7e8d0
            }
Packit d7e8d0
          nwritten = n;
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        {
Packit Service 30b792
          if (!WriteFile (ctx->hdd->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 Service 30b792
              TRACE_LOG  ("write error: ec=%d", ctx->error_code);
Packit d7e8d0
              break;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit Service 30b792
      TRACE_LOG  ("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 Service 30b792
    TRACE_LOG  ("SetEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
Packit d7e8d0
  TRACE_LOG ("waiting for close");
Packit Service 30b792
  wait_for_single_object (ctx->close_ev, INFINITE);
Packit d7e8d0
Packit d7e8d0
  if (ctx->nbytes)
Packit Service 30b792
    TRACE_LOG  ("still %d bytes in buffer at close time", ctx->nbytes);
Packit d7e8d0
Packit Service 30b792
  release_hddesc (ctx->hdd);
Packit Service 30b792
  close_handle (ctx->close_ev);
Packit Service 30b792
  close_handle (ctx->have_data);
Packit Service 30b792
  close_handle (ctx->is_empty);
Packit Service 30b792
  close_handle (ctx->thread_hd);
Packit d7e8d0
  DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
  free (ctx);
Packit d7e8d0
Packit Service 30b792
  TRACE_SUC ("");
Packit Service 30b792
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static struct writer_context_s *
Packit Service 30b792
create_writer (hddesc_t hdd)
Packit d7e8d0
{
Packit d7e8d0
  struct writer_context_s *ctx;
Packit d7e8d0
  SECURITY_ATTRIBUTES sec_attr;
Packit d7e8d0
  DWORD tid;
Packit d7e8d0
Packit Service 30b792
Packit Service 30b792
TRACE_BEG  (DEBUG_SYSIO, "gpgme:create_writer", hdd,
Packit Service 30b792
             "handle=%p sock=%d refhdd=%d",
Packit Service 30b792
             hdd->hd, hdd->sock, hdd->refcount);
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 Service 30b792
  ctx->hdd = ref_hddesc (hdd);
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 Service 30b792
      TRACE_LOG  ("CreateEvent failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      if (ctx->have_data)
Packit Service 30b792
	close_handle (ctx->have_data);
Packit d7e8d0
      if (ctx->is_empty)
Packit Service 30b792
	close_handle (ctx->is_empty);
Packit d7e8d0
      if (ctx->close_ev)
Packit Service 30b792
	close_handle (ctx->close_ev);
Packit Service 30b792
      release_hddesc (ctx->hdd);
Packit d7e8d0
      free (ctx);
Packit d7e8d0
      TRACE_SYSERR (EIO);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  INIT_LOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
  ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
Packit d7e8d0
  if (!ctx->thread_hd)
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_LOG  ("CreateThread failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      DESTROY_LOCK (ctx->mutex);
Packit d7e8d0
      if (ctx->have_data)
Packit Service 30b792
	close_handle (ctx->have_data);
Packit d7e8d0
      if (ctx->is_empty)
Packit Service 30b792
	close_handle (ctx->is_empty);
Packit d7e8d0
      if (ctx->close_ev)
Packit Service 30b792
	close_handle (ctx->close_ev);
Packit Service 30b792
      release_hddesc (ctx->hdd);
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 Service 30b792
  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 Service 30b792
      TRACE (DEBUG_SYSIO, "gpgme:destroy_writer", ctx,
Packit Service 30b792
              "hdd=%p refcount now %d", ctx->hdd, ctx->refcount);
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 Service 30b792
  TRACE (DEBUG_SYSIO, "gpgme:destroy_writer", ctx,
Packit Service 30b792
          "hdd=%p close triggered", ctx->hdd);
Packit d7e8d0
  UNLOCK (ctx->mutex);
Packit d7e8d0
Packit d7e8d0
  /* Give the writer a chance to flush the buffer.  */
Packit Service 30b792
  wait_for_single_object (ctx->is_empty, INFINITE);
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 Service 30b792
 * context will last until a _gpgme_io_close.  NULL is returned for a
Packit Service 30b792
 * bad FD or for other errors.  */
Packit d7e8d0
static struct writer_context_s *
Packit Service 30b792
find_writer (int fd)
Packit d7e8d0
{
Packit d7e8d0
  struct writer_context_s *wt = NULL;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG (DEBUG_SYSIO, "gpgme:find_writer", fd, "");
Packit d7e8d0
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
  if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used)
Packit d7e8d0
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      gpg_err_set_errno (EBADF);
Packit Service 30b792
      TRACE_SUC ("EBADF");
Packit Service 30b792
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  wt = fd_table[fd].writer;
Packit Service 30b792
  if (wt)
Packit d7e8d0
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      TRACE_SUC ("wt=%p", wt);
Packit Service 30b792
      return wt;  /* Return already initialized writer thread object.  */
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  /* Create a new writer thread.  */
Packit Service 30b792
  TRACE_LOG  ("fd=%d -> handle=%p socket=%d dupfrom=%d creating writer",
Packit Service 30b792
              fd, fd_table[fd].hdd->hd, fd_table[fd].hdd->sock,
Packit Service 30b792
              fd_table[fd].dup_from);
Packit Service 30b792
  wt = create_writer (fd_table[fd].hdd);
Packit Service 30b792
  if (!wt)
Packit Service 30b792
    gpg_err_set_errno (EIO);
Packit Service 30b792
  else
Packit Service 30b792
    fd_table[fd].writer = wt;
Packit Service 30b792
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit Service 30b792
  TRACE_SUC ("wt=%p (new)", wt);
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 Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_write", fd,
Packit d7e8d0
	      "buffer=%p, count=%u", buffer, count);
Packit Service 30b792
  TRACE_LOGBUFX (buffer, count);
Packit d7e8d0
Packit d7e8d0
  if (count == 0)
Packit d7e8d0
    return TRACE_SYSRES (0);
Packit d7e8d0
Packit Service 30b792
  ctx = find_writer (fd);
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 Service 30b792
	  TRACE_LOG  ("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 Service 30b792
      TRACE_LOG  ("waiting for empty buffer in thread %p", ctx->thread_hd);
Packit Service 30b792
      wait_for_single_object (ctx->is_empty, INFINITE);
Packit Service 30b792
      TRACE_LOG  ("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 Service 30b792
   * used by the select() implementation to probe the channel.  */
Packit d7e8d0
  if (!ResetEvent (ctx->is_empty))
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_LOG  ("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 Service 30b792
      TRACE_LOG  ("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
  HANDLE rh;
Packit d7e8d0
  HANDLE wh;
Packit Service 30b792
  hddesc_t rhdesc;
Packit Service 30b792
  hddesc_t whdesc;
Packit d7e8d0
  SECURITY_ATTRIBUTES sec_attr;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG  (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 Service 30b792
  /* Get a new empty file descriptor.  */
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 Service 30b792
  rhdesc = new_hddesc ();
Packit Service 30b792
  if (!rhdesc)
Packit d7e8d0
    {
Packit d7e8d0
      release_fd (rfd);
Packit d7e8d0
      release_fd (wfd);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit Service 30b792
  whdesc = new_hddesc ();
Packit Service 30b792
  if (!whdesc)
Packit d7e8d0
    {
Packit Service 30b792
      release_fd (rfd);
Packit Service 30b792
      release_fd (wfd);
Packit Service 30b792
      release_hddesc (rhdesc);
Packit Service 30b792
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  /* Create a pipe.  */
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 Service 30b792
      TRACE_LOG  ("CreatePipe failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
      release_fd (rfd);
Packit d7e8d0
      release_fd (wfd);
Packit Service 30b792
      release_hddesc (rhdesc);
Packit Service 30b792
      release_hddesc (whdesc);
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 Service 30b792
	  TRACE_LOG  ("DuplicateHandle failed: ec=%d",
Packit d7e8d0
		      (int) GetLastError ());
Packit d7e8d0
	  release_fd (rfd);
Packit d7e8d0
	  release_fd (wfd);
Packit Service 30b792
	  close_handle (rh);
Packit Service 30b792
	  close_handle (wh);
Packit Service 30b792
          release_hddesc (rhdesc);
Packit Service 30b792
          release_hddesc (whdesc);
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
        }
Packit Service 30b792
      close_handle (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 Service 30b792
	  TRACE_LOG  ("DuplicateHandle failed: ec=%d",
Packit d7e8d0
		      (int) GetLastError ());
Packit d7e8d0
	  release_fd (rfd);
Packit d7e8d0
	  release_fd (wfd);
Packit Service 30b792
	  close_handle (rh);
Packit Service 30b792
	  close_handle (wh);
Packit Service 30b792
          release_hddesc (rhdesc);
Packit Service 30b792
          release_hddesc (whdesc);
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit d7e8d0
	  return TRACE_SYSRES (-1);
Packit d7e8d0
        }
Packit Service 30b792
      close_handle (wh);
Packit d7e8d0
      wh = hd;
Packit d7e8d0
    }
Packit Service 30b792
Packit Service 30b792
  /* Put the HANDLEs of the new pipe into the file descriptor table.
Packit Service 30b792
   * Note that we don't need to lock the table because we have just
Packit Service 30b792
   * acquired these two fresh fds and they are not known by any other
Packit Service 30b792
   * thread.  */
Packit Service 30b792
  fd_table[rfd].want_reader = 1;
Packit Service 30b792
  ref_hddesc (rhdesc)->hd = rh;
Packit Service 30b792
  fd_table[rfd].hdd = rhdesc;
Packit Service 30b792
Packit Service 30b792
  fd_table[wfd].want_writer = 1;
Packit Service 30b792
  ref_hddesc (whdesc)->hd = wh;
Packit Service 30b792
  fd_table[wfd].hdd = whdesc;
Packit d7e8d0
Packit d7e8d0
  filedes[0] = rfd;
Packit d7e8d0
  filedes[1] = wfd;
Packit Service 30b792
  TRACE_SUC ("read=0x%x (hdd=%p,hd=%p), write=0x%x (hdd=%p,hd=%p)",
Packit Service 30b792
             rfd, fd_table[rfd].hdd, fd_table[rfd].hdd->hd,
Packit Service 30b792
             wfd, fd_table[wfd].hdd, fd_table[wfd].hdd->hd);
Packit Service 30b792
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit Service 30b792
/* Close out File descriptor FD.  */
Packit d7e8d0
int
Packit d7e8d0
_gpgme_io_close (int fd)
Packit d7e8d0
{
Packit d7e8d0
  _gpgme_close_notify_handler_t handler = NULL;
Packit d7e8d0
  void *value = NULL;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd, "");
Packit d7e8d0
Packit Service 30b792
  if (fd < 0)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EBADF);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit Service 30b792
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
  /* Check the size in the locked state because we may eventually add
Packit Service 30b792
   * code to change that size.  */
Packit Service 30b792
  if (fd >= fd_table_size || !fd_table[fd].used)
Packit d7e8d0
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit d7e8d0
      gpg_err_set_errno (EBADF);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  TRACE_LOG  ("hdd=%p dupfrom=%d", fd_table[fd].hdd, fd_table[fd].dup_from);
Packit d7e8d0
Packit Service 30b792
  if (fd_table[fd].reader)
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_LOG  ("destroying reader %p", fd_table[fd].reader);
Packit Service 30b792
      destroy_reader (fd_table[fd].reader);
Packit Service 30b792
      fd_table[fd].reader = NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  if (fd_table[fd].writer)
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_LOG  ("destroying writer %p", fd_table[fd].writer);
Packit Service 30b792
      destroy_writer (fd_table[fd].writer);
Packit Service 30b792
      fd_table[fd].writer = NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  /* The handler may not use any fd function because the table is
Packit Service 30b792
   * locked.  Can we avoid this?  */
Packit Service 30b792
  handler = fd_table[fd].notify.handler;
Packit Service 30b792
  value   = fd_table[fd].notify.value;
Packit Service 30b792
Packit Service 30b792
  /* Release our reference to the handle descriptor.  Note that if no
Packit Service 30b792
   * reader or writer threads were used this release will also take
Packit Service 30b792
   * care that the handle descriptor is closed
Packit Service 30b792
   * (i.e. CloseHandle(hdd->hd) is called).  */
Packit Service 30b792
  release_hddesc (fd_table[fd].hdd);
Packit Service 30b792
  fd_table[fd].hdd = NULL;
Packit Service 30b792
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit Service 30b792
Packit Service 30b792
  /* Run the notification callback.  */
Packit d7e8d0
  if (handler)
Packit d7e8d0
    handler (fd, value);
Packit d7e8d0
Packit Service 30b792
  release_fd (fd);  /* FIXME: We should have a release_fd_locked () */
Packit d7e8d0
Packit d7e8d0
  return TRACE_SYSRES (0);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit Service 30b792
/* Set a close notification callback which is called right after FD
Packit Service 30b792
 * has been closed but before its slot (ie. the FD number) is being
Packit Service 30b792
 * released.  The HANDLER may thus use the provided value of the FD
Packit Service 30b792
 * but it may not pass it to any I/O functions.  Note: Only the last
Packit Service 30b792
 * handler set for an FD is used.  */
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 Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
Packit d7e8d0
	      "close_handler=%p/%p", handler, value);
Packit d7e8d0
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
  if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used)
Packit Service 30b792
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      gpg_err_set_errno (EBADF);
Packit Service 30b792
      return TRACE_SYSRES (-1);;
Packit d7e8d0
    }
Packit Service 30b792
Packit Service 30b792
  fd_table[fd].notify.handler = handler;
Packit Service 30b792
  fd_table[fd].notify.value = value;
Packit Service 30b792
  UNLOCK (fd_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 Service 30b792
  TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd, "");
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
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
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
  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 Service 30b792
  static int spawn_warning_shown = 0;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_spawn", path,
Packit d7e8d0
	      "path=%s", path);
Packit Service 30b792
Packit Service 30b792
  (void)atfork;
Packit Service 30b792
  (void)atforkvalue;
Packit Service 30b792
Packit d7e8d0
  i = 0;
Packit d7e8d0
  while (argv[i])
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_LOG  ("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 Service 30b792
      TRACE_LOG  ("_gpgme_mkstemp failed: %s", strerror (errno));
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit Service 30b792
  TRACE_LOG  ("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 Service 30b792
      free (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 Service 30b792
      if (!spawn_warning_shown)
Packit Service 30b792
        {
Packit Service 30b792
          char *msg;
Packit Service 30b792
          gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
Packit Service 30b792
                                "detected installation directory of GpgME"
Packit Service 30b792
                                "\n\t\"%s\"\n\n"
Packit Service 30b792
                                "Crypto operations will not work.\n\n"
Packit Service 30b792
                                "If you see this it indicates a problem "
Packit Service 30b792
                                "with your installation.\n"
Packit Service 30b792
                                "Please report the problem to your "
Packit Service 30b792
                                "distributor of GpgME.\n\n"
Packit Service 30b792
                                "Developer's Note: The install dir can be "
Packit Service 30b792
                                "manually set with: gpgme_set_global_flag",
Packit Service 30b792
                                _gpgme_get_inst_dir ());
Packit Service 30b792
          MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
Packit Service 30b792
          gpgrt_free (msg);
Packit Service 30b792
          spawn_warning_shown = 1;
Packit Service 30b792
        }
Packit d7e8d0
      gpg_err_set_errno (EIO);
Packit Service 30b792
      close (tmp_fd);
Packit Service 30b792
      DeleteFileA (tmp_name);
Packit Service 30b792
      free (tmp_name);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit Service 30b792
  if (!_gpgme_create_process_utf8 (spawnhelper,
Packit Service 30b792
                                   arg_string,
Packit Service 30b792
                                   &sec_attr, /* process security attributes */
Packit Service 30b792
                                   &sec_attr, /* thread security attributes */
Packit Service 30b792
                                   FALSE,     /* inherit handles */
Packit Service 30b792
                                   cr_flags,  /* creation flags */
Packit Service 30b792
                                   NULL,      /* environment */
Packit Service 30b792
                                   NULL,      /* use current drive/directory */
Packit Service 30b792
                                   &si,       /* startup information */
Packit Service 30b792
                                   &pi))      /* returns process information */
Packit d7e8d0
    {
Packit d7e8d0
      int lasterr = (int)GetLastError ();
Packit Service 30b792
      TRACE_LOG  ("CreateProcess failed: ec=%d", lasterr);
Packit d7e8d0
      free (arg_string);
Packit d7e8d0
      close (tmp_fd);
Packit d7e8d0
      DeleteFileA (tmp_name);
Packit Service 30b792
      free (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
  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 Service 30b792
  LOCK (fd_table_lock);
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 Service 30b792
      if (fd >= 0 && fd < fd_table_size && fd_table[fd].used
Packit Service 30b792
          && fd_table[fd].hdd)
Packit Service 30b792
	ohd = fd_table[fd].hdd->hd;
Packit d7e8d0
Packit d7e8d0
      if (!DuplicateHandle (GetCurrentProcess(), ohd,
Packit d7e8d0
			    pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
Packit d7e8d0
	{
Packit Service 30b792
	  TRACE_LOG  ("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 Service 30b792
	  close_handle (pi.hThread);
Packit Service 30b792
	  close_handle (pi.hProcess);
Packit d7e8d0
Packit d7e8d0
	  close (tmp_fd);
Packit d7e8d0
	  DeleteFileA (tmp_name);
Packit Service 30b792
          free (tmp_name);
Packit d7e8d0
Packit d7e8d0
	  /* FIXME: Should translate the error code.  */
Packit d7e8d0
	  gpg_err_set_errno (EIO);
Packit Service 30b792
          UNLOCK (fd_table_lock);
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 Service 30b792
    if (flags)
Packit Service 30b792
      snprintf (line, BUFFER_MAX, "~%i \n", flags);
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
Packit Service 30b792
  free (tmp_name);
Packit Service 30b792
  free (arg_string);
Packit d7e8d0
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit Service 30b792
Packit Service 30b792
  TRACE_LOG  ("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 Service 30b792
  if (ResumeThread (pi.hThread) == (DWORD)(-1))
Packit Service 30b792
    TRACE_LOG  ("ResumeThread failed: ec=%d", (int) GetLastError ());
Packit d7e8d0
Packit Service 30b792
  close_handle (pi.hThread);
Packit d7e8d0
Packit Service 30b792
  TRACE_LOG  ("process=%p", pi.hProcess);
Packit d7e8d0
Packit d7e8d0
  /* We don't need to wait for the process.  */
Packit Service 30b792
  close_handle (pi.hProcess);
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 Service 30b792
      TRACE_LOG  ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
Packit d7e8d0
		  fd_list[i].peer_name);
Packit d7e8d0
    else
Packit Service 30b792
      TRACE_LOG  ("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 Service 30b792
  TRACE_BEG  (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 Service 30b792
              /* FIXME: A find_reader_locked() along with separate
Packit Service 30b792
               * lock calls might be a better appaoched here.  */
Packit Service 30b792
	      struct reader_context_s *ctx = find_reader (fds[i].fd);
Packit d7e8d0
Packit d7e8d0
	      if (!ctx)
Packit Service 30b792
		TRACE_LOG  ("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 Service 30b792
	      struct writer_context_s *ctx = find_writer (fds[i].fd);
Packit d7e8d0
Packit d7e8d0
	      if (!ctx)
Packit Service 30b792
		TRACE_LOG  ("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 Service 30b792
  if (code < WAIT_OBJECT_0 + nwait)
Packit d7e8d0
    {
Packit Service 30b792
      /* The 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 Service 30b792
	 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 Service 30b792
	  if (wait_for_single_object (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 Service 30b792
	  TRACE_LOG  ("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 Service 30b792
      TRACE_LOG  ("WFMO failed: %d", le);
Packit d7e8d0
      count = -1;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_LOG  ("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 Service 30b792
/* Write the printable version of FD to BUFFER which has an allocated
Packit Service 30b792
 * length of BUFLEN.  The printable version is the representation on
Packit Service 30b792
 * the command line that the child process expects.  Note that this
Packit Service 30b792
 * works closely together with the gpgme-32spawn wrapper process which
Packit Service 30b792
 * translates these command line args to the real handles. */
Packit d7e8d0
int
Packit Service 30b792
_gpgme_io_fd2str (char *buffer, int buflen, int fd)
Packit d7e8d0
{
Packit Service 30b792
  return snprintf (buffer, 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 Service 30b792
  int want_reader, want_writer;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd, "");
Packit d7e8d0
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
  if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used)
Packit d7e8d0
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      gpg_err_set_errno (EBADF);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  newfd = new_fd();
Packit d7e8d0
  if (newfd == -1)
Packit Service 30b792
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      gpg_err_set_errno (EMFILE);
Packit Service 30b792
      return TRACE_SYSRES (-1);
Packit Service 30b792
    }
Packit d7e8d0
Packit Service 30b792
  fd_table[newfd].hdd = ref_hddesc (fd_table[fd].hdd);
Packit d7e8d0
  fd_table[newfd].dup_from = fd;
Packit Service 30b792
  want_reader = fd_table[fd].want_reader;
Packit Service 30b792
  want_writer = fd_table[fd].want_writer;
Packit Service 30b792
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit d7e8d0
Packit Service 30b792
  rd_ctx = want_reader? find_reader (fd) : NULL;
Packit d7e8d0
  if (rd_ctx)
Packit d7e8d0
    {
Packit Service 30b792
      /* NEWFD initializes a freshly allocated slot and does not need
Packit Service 30b792
       * to be locked.  */
Packit Service 30b792
      LOCK (rd_ctx->mutex);
Packit d7e8d0
      rd_ctx->refcount++;
Packit Service 30b792
      UNLOCK (rd_ctx->mutex);
Packit Service 30b792
      fd_table[newfd].reader = rd_ctx;
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  wt_ctx = want_writer? find_writer (fd) : NULL;
Packit d7e8d0
  if (wt_ctx)
Packit d7e8d0
    {
Packit Service 30b792
      LOCK (wt_ctx->mutex);
Packit d7e8d0
      wt_ctx->refcount++;
Packit Service 30b792
      UNLOCK (wt_ctx->mutex);
Packit Service 30b792
      fd_table[newfd].writer = wt_ctx;
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 Service 30b792
  (void)fd;
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 Service 30b792
  (void)fd;
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 Service 30b792
  hddesc_t hdd;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG  (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 Service 30b792
  hdd = new_hddesc ();
Packit Service 30b792
  if (!hdd)
Packit Service 30b792
    {
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit Service 30b792
      release_fd (fd);
Packit Service 30b792
      gpg_err_set_errno (ENOMEM);
Packit Service 30b792
      return TRACE_SYSRES (-1);
Packit Service 30b792
    }
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 Service 30b792
  ref_hddesc (hdd)->sock = res;
Packit Service 30b792
  fd_table[fd].hdd = hdd;
Packit Service 30b792
  fd_table[fd].want_reader = 1;
Packit Service 30b792
  fd_table[fd].want_writer = 1;
Packit d7e8d0
Packit Service 30b792
  TRACE_SUC ("hdd=%p, socket=0x%x (0x%x)", hdd, fd, hdd->sock);
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 Service 30b792
  int sock;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_connect", fd,
Packit d7e8d0
	      "addr=%p, addrlen=%i", addr, addrlen);
Packit d7e8d0
Packit Service 30b792
  LOCK (fd_table_lock);
Packit Service 30b792
  if (fd < 0 || fd >= fd_table_size || !fd_table[fd].used || !fd_table[fd].hdd)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_err_set_errno (EBADF);
Packit Service 30b792
      UNLOCK (fd_table_lock);
Packit d7e8d0
      return TRACE_SYSRES (-1);
Packit d7e8d0
    }
Packit Service 30b792
  sock = fd_table[fd].hdd->sock;
Packit Service 30b792
  UNLOCK (fd_table_lock);
Packit d7e8d0
Packit Service 30b792
  res = connect (sock, 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 Service 30b792
  TRACE_SUC ("");
Packit Service 30b792
  return 0;
Packit d7e8d0
}