Fix hang in socket closing.
[gpgme.git] / src / w32-io.c
1 /* w32-io.c - W32 API I/O functions.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010 g10 Code GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #ifdef HAVE_SYS_TIME_H
32 # include <sys/time.h>
33 #endif
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37 #include <io.h>
38
39 #include "util.h"
40
41 #ifdef HAVE_W32CE_SYSTEM
42 #include <assuan.h>
43 #include <winioctl.h>
44 #define GPGCEDEV_IOCTL_UNBLOCK                                        \
45   CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS)
46 #define GPGCEDEV_IOCTL_ASSIGN_RVID                                    \
47   CTL_CODE (FILE_DEVICE_STREAMS, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS)
48 #endif
49
50 #include "sema.h"
51 #include "priv-io.h"
52 #include "debug.h"
53
54
55 /* FIXME: Optimize.  */
56 #define MAX_SLAFD 512
57
58 static struct
59 {
60   int used;
61
62   /* If this is not INVALID_HANDLE_VALUE, then it's a handle.  */
63   HANDLE handle;
64
65   /* If this is not INVALID_SOCKET, then it's a Windows socket.  */
66   int socket;
67
68   /* If this is not 0, then it's a rendezvous ID for the pipe server.  */
69   int rvid;
70
71   /* DUP_FROM is -1 if this file descriptor was allocated by pipe or
72      socket functions.  Only then should the handle or socket be
73      destroyed when this FD is closed.  This, together with the fact
74      that dup'ed file descriptors are closed before the file
75      descriptors from which they are dup'ed are closed, ensures that
76      the handle or socket is always valid, and shared among all file
77      descriptors refering to the same underlying object.
78
79      The logic behind this is that there is only one reason for us to
80      dup file descriptors anyway: to allow simpler book-keeping of
81      file descriptors shared between GPGME and libassuan, which both
82      want to close something.  Using the same handle for these
83      duplicates works just fine.  */
84   int dup_from;
85 } fd_table[MAX_SLAFD];
86
87
88 /* Returns the FD or -1 on resource limit.  */
89 int
90 new_fd (void)
91 {
92   int idx;
93
94   for (idx = 0; idx < MAX_SLAFD; idx++)
95     if (! fd_table[idx].used)
96       break;
97
98   if (idx == MAX_SLAFD)
99     {
100       gpg_err_set_errno (EIO);
101       return -1;
102     }
103
104   fd_table[idx].used = 1;
105   fd_table[idx].handle = INVALID_HANDLE_VALUE;
106   fd_table[idx].socket = INVALID_SOCKET;
107   fd_table[idx].rvid = 0;
108   fd_table[idx].dup_from = -1;
109
110   return idx;
111 }
112
113
114 void
115 release_fd (int fd)
116 {
117   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
118     return;
119
120   fd_table[fd].used = 0;
121   fd_table[fd].handle = INVALID_HANDLE_VALUE;
122   fd_table[fd].socket = INVALID_SOCKET;
123   fd_table[fd].rvid = 0;
124   fd_table[fd].dup_from = -1;
125 }
126
127
128 #define handle_to_fd(a)  ((int)(a))
129
130 #define READBUF_SIZE 4096
131 #define WRITEBUF_SIZE 4096
132 #define PIPEBUF_SIZE  4096
133 #define MAX_READERS 64
134 #define MAX_WRITERS 64
135
136 static struct
137 {
138   int inuse;
139   int fd;
140   _gpgme_close_notify_handler_t handler;
141   void *value;
142 } notify_table[MAX_SLAFD];
143 DEFINE_STATIC_LOCK (notify_table_lock);
144
145
146 struct reader_context_s
147 {
148   HANDLE file_hd;
149   int file_sock;
150   HANDLE thread_hd;
151   int refcount;
152
153   DECLARE_LOCK (mutex);
154
155   int stop_me;
156   int eof;
157   int eof_shortcut;
158   int error;
159   int error_code;
160
161   /* This is manually reset.  */
162   HANDLE have_data_ev;
163   /* This is automatically reset.  */
164   HANDLE have_space_ev;
165   HANDLE stopped;
166   size_t readpos, writepos;
167   char buffer[READBUF_SIZE];
168 };
169
170
171 static struct
172 {
173   volatile int used;
174   int fd;
175   struct reader_context_s *context;
176 } reader_table[MAX_READERS];
177 static int reader_table_size= MAX_READERS;
178 DEFINE_STATIC_LOCK (reader_table_lock);
179
180
181 struct writer_context_s
182 {
183   HANDLE file_hd;
184   int file_sock;
185   HANDLE thread_hd;
186   int refcount;
187
188   DECLARE_LOCK (mutex);
189
190   int stop_me;
191   int error;
192   int error_code;
193
194   /* This is manually reset.  */
195   HANDLE have_data;
196   HANDLE is_empty;
197   HANDLE stopped;
198   size_t nbytes;
199   char buffer[WRITEBUF_SIZE];
200 };
201
202
203 static struct
204 {
205   volatile int used;
206   int fd;
207   struct writer_context_s *context;
208 } writer_table[MAX_WRITERS];
209 static int writer_table_size= MAX_WRITERS;
210 DEFINE_STATIC_LOCK (writer_table_lock);
211
212
213 static int
214 get_desired_thread_priority (void)
215 {
216   int value;
217
218   if (!_gpgme_get_conf_int ("IOThreadPriority", &value))
219     {
220       value = THREAD_PRIORITY_HIGHEST;
221       TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
222               "%d (default)", value);
223     }
224   else
225     {
226       TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
227               "%d (configured)", value);
228     }
229   return value;
230 }
231
232
233 static HANDLE
234 set_synchronize (HANDLE hd)
235 {
236 #ifdef HAVE_W32CE_SYSTEM
237   return hd;
238 #else
239   HANDLE new_hd;
240
241   /* For NT we have to set the sync flag.  It seems that the only way
242      to do it is by duplicating the handle.  Tsss...  */
243   if (!DuplicateHandle (GetCurrentProcess (), hd,
244                         GetCurrentProcess (), &new_hd,
245                         EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0))
246     {
247       TRACE1 (DEBUG_SYSIO, "gpgme:set_synchronize", hd,
248               "DuplicateHandle failed: ec=%d", (int) GetLastError ());
249       /* FIXME: Should translate the error code.  */
250       gpg_err_set_errno (EIO);
251       return INVALID_HANDLE_VALUE;
252     }
253
254   CloseHandle (hd);
255   return new_hd;
256 #endif
257 }
258
259
260 static DWORD CALLBACK
261 reader (void *arg)
262 {
263   struct reader_context_s *ctx = arg;
264   int nbytes;
265   DWORD nread;
266   int sock;
267   TRACE_BEG2 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
268               "file_sock=%d, thread=%p", ctx->file_sock, ctx->thread_hd);
269
270   if (ctx->file_hd != INVALID_HANDLE_VALUE)
271     sock = 0;
272   else
273     sock = 1;
274
275   for (;;)
276     {
277       LOCK (ctx->mutex);
278       /* Leave a 1 byte gap so that we can see whether it is empty or
279          full.  */
280       if ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
281         {
282           /* Wait for space.  */
283           if (!ResetEvent (ctx->have_space_ev))
284             TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
285           UNLOCK (ctx->mutex);
286           TRACE_LOG ("waiting for space");
287           WaitForSingleObject (ctx->have_space_ev, INFINITE);
288           TRACE_LOG ("got space");
289           LOCK (ctx->mutex);
290         }
291       if (ctx->stop_me)
292         {
293           UNLOCK (ctx->mutex);
294           break;
295         }
296       nbytes = (ctx->readpos + READBUF_SIZE
297                 - ctx->writepos - 1) % READBUF_SIZE;
298       if (nbytes > READBUF_SIZE - ctx->writepos)
299         nbytes = READBUF_SIZE - ctx->writepos;
300       UNLOCK (ctx->mutex);
301
302       TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
303
304       if (sock)
305         {
306           int n;
307
308           n = recv (ctx->file_sock, ctx->buffer + ctx->writepos, nbytes, 0);
309           if (n < 0)
310             {
311               ctx->error_code = (int) WSAGetLastError ();
312               if (ctx->error_code == ERROR_BROKEN_PIPE)
313                 {
314                   ctx->eof = 1;
315                   TRACE_LOG ("got EOF (broken connection)");
316                 }
317               else
318                 {
319                   /* Check whether the shutdown triggered the error -
320                      no need to to print a warning in this case.  */
321                   if ( ctx->error_code == WSAECONNABORTED
322                        || ctx->error_code == WSAECONNRESET)
323                     {
324                       LOCK (ctx->mutex);
325                       if (ctx->stop_me)
326                         {
327                           UNLOCK (ctx->mutex);
328                           TRACE_LOG ("got shutdown");
329                           break;
330                         }
331                       UNLOCK (ctx->mutex);
332                     }
333
334                   ctx->error = 1;
335                   TRACE_LOG1 ("recv error: ec=%d", ctx->error_code);
336                 }
337               break;
338             }
339           nread = n;
340         }
341       else
342         {
343           if (!ReadFile (ctx->file_hd,
344                          ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
345             {
346               ctx->error_code = (int) GetLastError ();
347               /* NOTE (W32CE): Do not ignore ERROR_BUSY!  Check at
348                  least stop_me if that happens.  */
349               if (ctx->error_code == ERROR_BROKEN_PIPE)
350                 {
351                   ctx->eof = 1;
352                   TRACE_LOG ("got EOF (broken pipe)");
353                 }
354               else
355                 {
356                   ctx->error = 1;
357                   TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
358                 }
359               break;
360             }
361         }
362       LOCK (ctx->mutex);
363       if (ctx->stop_me)
364         {
365           UNLOCK (ctx->mutex);
366           break;
367         }
368       if (!nread)
369         {
370           ctx->eof = 1;
371           TRACE_LOG ("got eof");
372           UNLOCK (ctx->mutex);
373           break;
374         }
375
376       TRACE_LOG1 ("got %u bytes", nread);
377
378       ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
379       if (!SetEvent (ctx->have_data_ev))
380         TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
381                     (int) GetLastError ());
382       UNLOCK (ctx->mutex);
383     }
384   /* Indicate that we have an error or EOF.  */
385   if (!SetEvent (ctx->have_data_ev))
386         TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
387                     (int) GetLastError ());
388   SetEvent (ctx->stopped);
389
390   return TRACE_SUC ();
391 }
392
393
394 static struct reader_context_s *
395 create_reader (int fd)
396 {
397   struct reader_context_s *ctx;
398   SECURITY_ATTRIBUTES sec_attr;
399   DWORD tid;
400
401   TRACE_BEG (DEBUG_SYSIO, "gpgme:create_reader", fd);
402
403   memset (&sec_attr, 0, sizeof sec_attr);
404   sec_attr.nLength = sizeof sec_attr;
405   sec_attr.bInheritHandle = FALSE;
406
407   ctx = calloc (1, sizeof *ctx);
408   if (!ctx)
409     {
410       TRACE_SYSERR (errno);
411       return NULL;
412     }
413
414   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
415     {
416       TRACE_SYSERR (EIO);
417       return NULL;
418     }
419   TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
420               fd, fd_table[fd].handle, fd_table[fd].socket,
421               fd_table[fd].dup_from);
422   ctx->file_hd = fd_table[fd].handle;
423   ctx->file_sock = fd_table[fd].socket;
424
425   ctx->refcount = 1;
426   ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
427   if (ctx->have_data_ev)
428     ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
429   if (ctx->have_space_ev)
430     ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
431   if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->stopped)
432     {
433       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
434       if (ctx->have_data_ev)
435         CloseHandle (ctx->have_data_ev);
436       if (ctx->have_space_ev)
437         CloseHandle (ctx->have_space_ev);
438       if (ctx->stopped)
439         CloseHandle (ctx->stopped);
440       free (ctx);
441       /* FIXME: Translate the error code.  */
442       TRACE_SYSERR (EIO);
443       return NULL;
444     }
445
446   ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
447   INIT_LOCK (ctx->mutex);
448
449 #ifdef HAVE_W32CE_SYSTEM
450   ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
451                                  STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
452 #else
453   ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
454 #endif
455
456   if (!ctx->thread_hd)
457     {
458       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
459       DESTROY_LOCK (ctx->mutex);
460       if (ctx->have_data_ev)
461         CloseHandle (ctx->have_data_ev);
462       if (ctx->have_space_ev)
463         CloseHandle (ctx->have_space_ev);
464       if (ctx->stopped)
465         CloseHandle (ctx->stopped);
466       free (ctx);
467       TRACE_SYSERR (EIO);
468       return NULL;
469     }
470   else
471     {
472       /* We set the priority of the thread higher because we know that
473          it only runs for a short time.  This greatly helps to
474          increase the performance of the I/O.  */
475       SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
476     }
477
478   TRACE_SUC ();
479   return ctx;
480 }
481
482
483 static void
484 destroy_reader (struct reader_context_s *ctx)
485 {
486   LOCK (ctx->mutex);
487   ctx->refcount--;
488   if (ctx->refcount != 0)
489     {
490       UNLOCK (ctx->mutex);
491       return;
492     }
493   ctx->stop_me = 1;
494   if (ctx->have_space_ev)
495     SetEvent (ctx->have_space_ev);
496   UNLOCK (ctx->mutex);
497
498 #ifdef HAVE_W32CE_SYSTEM
499   /* Scenario: We never create a full pipe, but already started
500      reading.  Then we need to unblock the reader in the pipe driver
501      to make our reader thread notice that we want it to go away.  */
502
503   if (ctx->file_hd != INVALID_HANDLE_VALUE)
504     {
505       if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
506                         NULL, 0, NULL, 0, NULL, NULL))
507         {
508           TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
509                   "unblock control call failed for thread %p", ctx->thread_hd);
510         }
511     }
512 #endif
513
514   /* The reader thread is usually blocking in recv or ReadFile.  If
515      the peer does not send an EOF or breaks the pipe the WFSO might
516      get stuck waiting for the termination of the reader thread.  This
517      happens quite often with sockets, thus we definitely need to get
518      out of the recv.  A shutdown does this nicely.  For handles
519      (i.e. pipes) it would also be nice to cancel the operation, but
520      such a feature is only available since Vista.  Thus we need to
521      dlopen that syscall.  */
522   if (ctx->file_hd != INVALID_HANDLE_VALUE)
523     {
524       /* Fixme: Call CancelSynchronousIo (handle_of_thread).  */
525     }
526   else if (ctx->file_sock != INVALID_SOCKET)
527     {
528       if (shutdown (ctx->file_sock, 2))
529         TRACE2 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
530                 "shutdown socket %d failed: %s",
531                 ctx->file_sock, (int) WSAGetLastError ());
532     }
533
534   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
535           "waiting for termination of thread %p", ctx->thread_hd);
536   WaitForSingleObject (ctx->stopped, INFINITE);
537   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
538           "thread %p has terminated", ctx->thread_hd);
539
540   if (ctx->stopped)
541     CloseHandle (ctx->stopped);
542   if (ctx->have_data_ev)
543     CloseHandle (ctx->have_data_ev);
544   if (ctx->have_space_ev)
545     CloseHandle (ctx->have_space_ev);
546   CloseHandle (ctx->thread_hd);
547   DESTROY_LOCK (ctx->mutex);
548   free (ctx);
549 }
550
551
552 /* Find a reader context or create a new one.  Note that the reader
553    context will last until a _gpgme_io_close.  */
554 static struct reader_context_s *
555 find_reader (int fd, int start_it)
556 {
557   struct reader_context_s *rd = NULL;
558   int i;
559
560   LOCK (reader_table_lock);
561   for (i = 0; i < reader_table_size; i++)
562     if (reader_table[i].used && reader_table[i].fd == fd)
563       rd = reader_table[i].context;
564
565   if (rd || !start_it)
566     {
567       UNLOCK (reader_table_lock);
568       return rd;
569     }
570
571   for (i = 0; i < reader_table_size; i++)
572     if (!reader_table[i].used)
573       break;
574
575   if (i != reader_table_size)
576     {
577       rd = create_reader (fd);
578       reader_table[i].fd = fd;
579       reader_table[i].context = rd;
580       reader_table[i].used = 1;
581     }
582
583   UNLOCK (reader_table_lock);
584   return rd;
585 }
586
587
588 static void
589 kill_reader (int fd)
590 {
591   int i;
592
593   LOCK (reader_table_lock);
594   for (i = 0; i < reader_table_size; i++)
595     {
596       if (reader_table[i].used && reader_table[i].fd == fd)
597         {
598           destroy_reader (reader_table[i].context);
599           reader_table[i].context = NULL;
600           reader_table[i].used = 0;
601           break;
602         }
603     }
604   UNLOCK (reader_table_lock);
605 }
606
607
608 int
609 _gpgme_io_read (int fd, void *buffer, size_t count)
610 {
611   int nread;
612   struct reader_context_s *ctx;
613   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
614               "buffer=%p, count=%u", buffer, count);
615
616   ctx = find_reader (fd, 1);
617   if (!ctx)
618     {
619       gpg_err_set_errno (EBADF);
620       return TRACE_SYSRES (-1);
621     }
622   if (ctx->eof_shortcut)
623     return TRACE_SYSRES (0);
624
625   LOCK (ctx->mutex);
626   if (ctx->readpos == ctx->writepos && !ctx->error)
627     {
628       /* No data available.  */
629       UNLOCK (ctx->mutex);
630       TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd);
631       WaitForSingleObject (ctx->have_data_ev, INFINITE);
632       TRACE_LOG1 ("data from thread %p available", ctx->thread_hd);
633       LOCK (ctx->mutex);
634     }
635
636   if (ctx->readpos == ctx->writepos || ctx->error)
637     {
638       UNLOCK (ctx->mutex);
639       ctx->eof_shortcut = 1;
640       if (ctx->eof)
641         return TRACE_SYSRES (0);
642       if (!ctx->error)
643         {
644           TRACE_LOG ("EOF but ctx->eof flag not set");
645           return 0;
646         }
647       gpg_err_set_errno (ctx->error_code);
648       return TRACE_SYSRES (-1);
649     }
650
651   nread = ctx->readpos < ctx->writepos
652     ? ctx->writepos - ctx->readpos
653     : READBUF_SIZE - ctx->readpos;
654   if (nread > count)
655     nread = count;
656   memcpy (buffer, ctx->buffer + ctx->readpos, nread);
657   ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
658   if (ctx->readpos == ctx->writepos && !ctx->eof)
659     {
660       if (!ResetEvent (ctx->have_data_ev))
661         {
662           TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
663           UNLOCK (ctx->mutex);
664           /* FIXME: Should translate the error code.  */
665           gpg_err_set_errno (EIO);
666           return TRACE_SYSRES (-1);
667         }
668     }
669   if (!SetEvent (ctx->have_space_ev))
670     {
671       TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d",
672                   ctx->have_space_ev, (int) GetLastError ());
673       UNLOCK (ctx->mutex);
674       /* FIXME: Should translate the error code.  */
675       gpg_err_set_errno (EIO);
676       return TRACE_SYSRES (-1);
677     }
678   UNLOCK (ctx->mutex);
679
680   TRACE_LOGBUF (buffer, nread);
681   return TRACE_SYSRES (nread);
682 }
683
684
685 /* The writer does use a simple buffering strategy so that we are
686    informed about write errors as soon as possible (i. e. with the the
687    next call to the write function.  */
688 static DWORD CALLBACK
689 writer (void *arg)
690 {
691   struct writer_context_s *ctx = arg;
692   DWORD nwritten;
693   int sock;
694   TRACE_BEG2 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
695               "file_sock=%d, thread=%p", ctx->file_sock, ctx->thread_hd);
696
697   if (ctx->file_hd != INVALID_HANDLE_VALUE)
698     sock = 0;
699   else
700     sock = 1;
701
702   for (;;)
703     {
704       LOCK (ctx->mutex);
705       if (ctx->stop_me)
706         {
707           UNLOCK (ctx->mutex);
708           break;
709         }
710       if (!ctx->nbytes)
711         {
712           if (!SetEvent (ctx->is_empty))
713             TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
714           if (!ResetEvent (ctx->have_data))
715             TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
716           UNLOCK (ctx->mutex);
717           TRACE_LOG ("idle");
718           WaitForSingleObject (ctx->have_data, INFINITE);
719           TRACE_LOG ("got data to send");
720           LOCK (ctx->mutex);
721         }
722       if (ctx->stop_me)
723         {
724           UNLOCK (ctx->mutex);
725           break;
726         }
727       UNLOCK (ctx->mutex);
728
729       TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
730
731       /* Note that CTX->nbytes is not zero at this point, because
732          _gpgme_io_write always writes at least 1 byte before waking
733          us up, unless CTX->stop_me is true, which we catch above.  */
734       if (sock)
735         {
736           /* We need to try send first because a socket handle can't
737              be used with WriteFile.  */
738           int n;
739
740           n = send (ctx->file_sock, ctx->buffer, ctx->nbytes, 0);
741           if (n < 0)
742             {
743               ctx->error_code = (int) WSAGetLastError ();
744               ctx->error = 1;
745               TRACE_LOG1 ("send error: ec=%d", ctx->error_code);
746               break;
747             }
748           nwritten = n;
749         }
750       else
751         {
752           if (!WriteFile (ctx->file_hd, ctx->buffer,
753                           ctx->nbytes, &nwritten, NULL))
754             {
755               if (GetLastError () == ERROR_BUSY)
756                 {
757                   /* Probably stop_me is set now.  */
758                   TRACE_LOG ("pipe busy (unblocked?)");
759                   continue;
760                 }
761
762               ctx->error_code = (int) GetLastError ();
763               ctx->error = 1;
764               TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
765               break;
766             }
767         }
768       TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
769
770       LOCK (ctx->mutex);
771       ctx->nbytes -= nwritten;
772       UNLOCK (ctx->mutex);
773     }
774   /* Indicate that we have an error.  */
775   if (!SetEvent (ctx->is_empty))
776     TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
777   SetEvent (ctx->stopped);
778
779   return TRACE_SUC ();
780 }
781
782
783 static struct writer_context_s *
784 create_writer (int fd)
785 {
786   struct writer_context_s *ctx;
787   SECURITY_ATTRIBUTES sec_attr;
788   DWORD tid;
789
790   TRACE_BEG (DEBUG_SYSIO, "gpgme:create_writer", fd);
791
792   memset (&sec_attr, 0, sizeof sec_attr);
793   sec_attr.nLength = sizeof sec_attr;
794   sec_attr.bInheritHandle = FALSE;
795
796   ctx = calloc (1, sizeof *ctx);
797   if (!ctx)
798     {
799       TRACE_SYSERR (errno);
800       return NULL;
801     }
802
803   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
804     {
805       TRACE_SYSERR (EIO);
806       return NULL;
807     }
808   TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
809               fd, fd_table[fd].handle, fd_table[fd].socket,
810               fd_table[fd].dup_from);
811   ctx->file_hd = fd_table[fd].handle;
812   ctx->file_sock = fd_table[fd].socket;
813
814   ctx->refcount = 1;
815   ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
816   if (ctx->have_data)
817     ctx->is_empty  = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
818   if (ctx->is_empty)
819     ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
820   if (!ctx->have_data || !ctx->is_empty || !ctx->stopped)
821     {
822       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
823       if (ctx->have_data)
824         CloseHandle (ctx->have_data);
825       if (ctx->is_empty)
826         CloseHandle (ctx->is_empty);
827       if (ctx->stopped)
828         CloseHandle (ctx->stopped);
829       free (ctx);
830       /* FIXME: Translate the error code.  */
831       TRACE_SYSERR (EIO);
832       return NULL;
833     }
834
835   ctx->is_empty = set_synchronize (ctx->is_empty);
836   INIT_LOCK (ctx->mutex);
837
838 #ifdef HAVE_W32CE_SYSTEM
839   ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
840                                  STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
841 #else
842   ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
843 #endif
844
845   if (!ctx->thread_hd)
846     {
847       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
848       DESTROY_LOCK (ctx->mutex);
849       if (ctx->have_data)
850         CloseHandle (ctx->have_data);
851       if (ctx->is_empty)
852         CloseHandle (ctx->is_empty);
853       if (ctx->stopped)
854         CloseHandle (ctx->stopped);
855       free (ctx);
856       TRACE_SYSERR (EIO);
857       return NULL;
858     }
859   else
860     {
861       /* We set the priority of the thread higher because we know
862          that it only runs for a short time.  This greatly helps to
863          increase the performance of the I/O.  */
864       SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
865     }
866
867   TRACE_SUC ();
868   return ctx;
869 }
870
871 static void
872 destroy_writer (struct writer_context_s *ctx)
873 {
874   LOCK (ctx->mutex);
875   ctx->refcount--;
876   if (ctx->refcount != 0)
877     {
878       UNLOCK (ctx->mutex);
879       return;
880     }
881   ctx->stop_me = 1;
882   if (ctx->have_data)
883     SetEvent (ctx->have_data);
884   UNLOCK (ctx->mutex);
885
886 #ifdef HAVE_W32CE_SYSTEM
887   /* Scenario: We never create a full pipe, but already started
888      writing more than the pipe buffer.  Then we need to unblock the
889      writer in the pipe driver to make our writer thread notice that
890      we want it to go away.  */
891
892   if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
893                         NULL, 0, NULL, 0, NULL, NULL))
894     {
895       TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
896               "unblock control call failed for thread %p", ctx->thread_hd);
897     }
898 #endif
899
900   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
901           "waiting for termination of thread %p", ctx->thread_hd);
902   WaitForSingleObject (ctx->stopped, INFINITE);
903   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
904           "thread %p has terminated", ctx->thread_hd);
905
906   if (ctx->stopped)
907     CloseHandle (ctx->stopped);
908   if (ctx->have_data)
909     CloseHandle (ctx->have_data);
910   if (ctx->is_empty)
911     CloseHandle (ctx->is_empty);
912   CloseHandle (ctx->thread_hd);
913   DESTROY_LOCK (ctx->mutex);
914   free (ctx);
915 }
916
917
918 /* Find a writer context or create a new one.  Note that the writer
919    context will last until a _gpgme_io_close.  */
920 static struct writer_context_s *
921 find_writer (int fd, int start_it)
922 {
923   struct writer_context_s *wt = NULL;
924   int i;
925
926   LOCK (writer_table_lock);
927   for (i = 0; i < writer_table_size; i++)
928     if (writer_table[i].used && writer_table[i].fd == fd)
929       wt = writer_table[i].context;
930
931   if (wt || !start_it)
932     {
933       UNLOCK (writer_table_lock);
934       return wt;
935     }
936
937   for (i = 0; i < writer_table_size; i++)
938     if (!writer_table[i].used)
939       break;
940
941   if (i != writer_table_size)
942     {
943       wt = create_writer (fd);
944       writer_table[i].fd = fd;
945       writer_table[i].context = wt;
946       writer_table[i].used = 1;
947     }
948
949   UNLOCK (writer_table_lock);
950   return wt;
951 }
952
953
954 static void
955 kill_writer (int fd)
956 {
957   int i;
958
959   LOCK (writer_table_lock);
960   for (i = 0; i < writer_table_size; i++)
961     {
962       if (writer_table[i].used && writer_table[i].fd == fd)
963         {
964           destroy_writer (writer_table[i].context);
965           writer_table[i].context = NULL;
966           writer_table[i].used = 0;
967           break;
968         }
969     }
970   UNLOCK (writer_table_lock);
971 }
972
973
974 int
975 _gpgme_io_write (int fd, const void *buffer, size_t count)
976 {
977   struct writer_context_s *ctx;
978   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
979               "buffer=%p, count=%u", buffer, count);
980   TRACE_LOGBUF (buffer, count);
981
982   if (count == 0)
983     return TRACE_SYSRES (0);
984
985   ctx = find_writer (fd, 1);
986   if (!ctx)
987     return TRACE_SYSRES (-1);
988
989   LOCK (ctx->mutex);
990   if (!ctx->error && ctx->nbytes)
991     {
992       /* Bytes are pending for send.  */
993
994       /* Reset the is_empty event.  Better safe than sorry.  */
995       if (!ResetEvent (ctx->is_empty))
996         {
997           TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
998           UNLOCK (ctx->mutex);
999           /* FIXME: Should translate the error code.  */
1000           gpg_err_set_errno (EIO);
1001           return TRACE_SYSRES (-1);
1002         }
1003       UNLOCK (ctx->mutex);
1004       TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd);
1005       WaitForSingleObject (ctx->is_empty, INFINITE);
1006       TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd);
1007       LOCK (ctx->mutex);
1008     }
1009
1010   if (ctx->error)
1011     {
1012       UNLOCK (ctx->mutex);
1013       if (ctx->error_code == ERROR_NO_DATA)
1014         gpg_err_set_errno (EPIPE);
1015       else
1016         gpg_err_set_errno (EIO);
1017       return TRACE_SYSRES (-1);
1018     }
1019
1020   /* If no error occured, the number of bytes in the buffer must be
1021      zero.  */
1022   assert (!ctx->nbytes);
1023
1024   if (count > WRITEBUF_SIZE)
1025     count = WRITEBUF_SIZE;
1026   memcpy (ctx->buffer, buffer, count);
1027   ctx->nbytes = count;
1028
1029   /* We have to reset the is_empty event early, because it is also
1030      used by the select() implementation to probe the channel.  */
1031   if (!ResetEvent (ctx->is_empty))
1032     {
1033       TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
1034       UNLOCK (ctx->mutex);
1035       /* FIXME: Should translate the error code.  */
1036       gpg_err_set_errno (EIO);
1037       return TRACE_SYSRES (-1);
1038     }
1039   if (!SetEvent (ctx->have_data))
1040     {
1041       TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
1042       UNLOCK (ctx->mutex);
1043       /* FIXME: Should translate the error code.  */
1044       gpg_err_set_errno (EIO);
1045       return TRACE_SYSRES (-1);
1046     }
1047   UNLOCK (ctx->mutex);
1048
1049   return TRACE_SYSRES ((int) count);
1050 }
1051
1052
1053 int
1054 _gpgme_io_pipe (int filedes[2], int inherit_idx)
1055 {
1056   int rfd;
1057   int wfd;
1058 #ifdef HAVE_W32CE_SYSTEM
1059   HANDLE hd;
1060   int rvid;
1061 #else
1062   HANDLE rh;
1063   HANDLE wh;
1064   SECURITY_ATTRIBUTES sec_attr;
1065 #endif
1066
1067   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
1068               "inherit_idx=%i (GPGME uses it for %s)",
1069               inherit_idx, inherit_idx ? "reading" : "writing");
1070
1071   rfd = new_fd ();
1072   if (rfd == -1)
1073     return TRACE_SYSRES (-1);
1074   wfd = new_fd ();
1075   if (wfd == -1)
1076     {
1077       release_fd (rfd);
1078       return TRACE_SYSRES (-1);
1079     }
1080
1081 #ifdef HAVE_W32CE_SYSTEM
1082   hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
1083   if (hd == INVALID_HANDLE_VALUE)
1084     {
1085       TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d",
1086                   (int) GetLastError ());
1087       release_fd (rfd);
1088       release_fd (wfd);
1089       /* FIXME: Should translate the error code.  */
1090       gpg_err_set_errno (EIO);
1091       return TRACE_SYSRES (-1);
1092     }
1093
1094   if (inherit_idx == 0)
1095     {
1096       fd_table[rfd].rvid = rvid;
1097       fd_table[wfd].handle = hd;
1098     }
1099   else
1100     {
1101       fd_table[rfd].handle = hd;
1102       fd_table[wfd].rvid = rvid;
1103     }
1104
1105 #else
1106
1107   memset (&sec_attr, 0, sizeof (sec_attr));
1108   sec_attr.nLength = sizeof (sec_attr);
1109   sec_attr.bInheritHandle = FALSE;
1110
1111   if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
1112     {
1113       TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
1114       release_fd (rfd);
1115       release_fd (wfd);
1116       /* FIXME: Should translate the error code.  */
1117       gpg_err_set_errno (EIO);
1118       return TRACE_SYSRES (-1);
1119     }
1120
1121   /* Make one end inheritable.  */
1122   if (inherit_idx == 0)
1123     {
1124       HANDLE hd;
1125       if (!DuplicateHandle (GetCurrentProcess(), rh,
1126                             GetCurrentProcess(), &hd, 0,
1127                             TRUE, DUPLICATE_SAME_ACCESS))
1128         {
1129           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
1130                       (int) GetLastError ());
1131           release_fd (rfd);
1132           release_fd (wfd);
1133           CloseHandle (rh);
1134           CloseHandle (wh);
1135           /* FIXME: Should translate the error code.  */
1136           gpg_err_set_errno (EIO);
1137           return TRACE_SYSRES (-1);
1138         }
1139       CloseHandle (rh);
1140       rh = hd;
1141     }
1142   else if (inherit_idx == 1)
1143     {
1144       HANDLE hd;
1145       if (!DuplicateHandle( GetCurrentProcess(), wh,
1146                             GetCurrentProcess(), &hd, 0,
1147                             TRUE, DUPLICATE_SAME_ACCESS))
1148         {
1149           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
1150                       (int) GetLastError ());
1151           release_fd (rfd);
1152           release_fd (wfd);
1153           CloseHandle (rh);
1154           CloseHandle (wh);
1155           /* FIXME: Should translate the error code.  */
1156           gpg_err_set_errno (EIO);
1157           return TRACE_SYSRES (-1);
1158         }
1159       CloseHandle (wh);
1160       wh = hd;
1161     }
1162   fd_table[rfd].handle = rh;
1163   fd_table[wfd].handle = wh;
1164 #endif
1165
1166   filedes[0] = rfd;
1167   filedes[1] = wfd;
1168   return TRACE_SUC6 ("read=0x%x (%p/0x%x), write=0x%x (%p/0x%x)",
1169                      rfd, fd_table[rfd].handle, fd_table[rfd].rvid,
1170                      wfd, fd_table[wfd].handle, fd_table[wfd].rvid);
1171 }
1172
1173
1174 int
1175 _gpgme_io_close (int fd)
1176 {
1177   int i;
1178   _gpgme_close_notify_handler_t handler = NULL;
1179   void *value = NULL;
1180
1181   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
1182
1183   if (fd == -1)
1184     {
1185       gpg_err_set_errno (EBADF);
1186       return TRACE_SYSRES (-1);
1187     }
1188   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1189     {
1190       gpg_err_set_errno (EBADF);
1191       return TRACE_SYSRES (-1);
1192     }
1193
1194   TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
1195               fd, fd_table[fd].handle, fd_table[fd].socket,
1196               fd_table[fd].dup_from);
1197
1198   kill_reader (fd);
1199   kill_writer (fd);
1200   LOCK (notify_table_lock);
1201   for (i = 0; i < DIM (notify_table); i++)
1202     {
1203       if (notify_table[i].inuse && notify_table[i].fd == fd)
1204         {
1205           handler = notify_table[i].handler;
1206           value   = notify_table[i].value;
1207           notify_table[i].handler = NULL;
1208           notify_table[i].value = NULL;
1209           notify_table[i].inuse = 0;
1210           break;
1211         }
1212     }
1213   UNLOCK (notify_table_lock);
1214   if (handler)
1215     handler (fd, value);
1216
1217   if (fd_table[fd].dup_from == -1)
1218     {
1219       if (fd_table[fd].handle != INVALID_HANDLE_VALUE)
1220         {
1221           if (!CloseHandle (fd_table[fd].handle))
1222             {
1223               TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
1224               /* FIXME: Should translate the error code.  */
1225               gpg_err_set_errno (EIO);
1226               return TRACE_SYSRES (-1);
1227             }
1228         }
1229       else if (fd_table[fd].socket != INVALID_SOCKET)
1230         {
1231           if (closesocket (fd_table[fd].socket))
1232             {
1233               TRACE_LOG1 ("closesocket failed: ec=%d", (int) WSAGetLastError ());
1234               /* FIXME: Should translate the error code.  */
1235               gpg_err_set_errno (EIO);
1236               return TRACE_SYSRES (-1);
1237             }
1238         }
1239       /* Nothing to do for RVIDs.  */
1240     }
1241
1242   release_fd (fd);
1243
1244   return TRACE_SYSRES (0);
1245 }
1246
1247
1248 int
1249 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
1250                             void *value)
1251 {
1252   int i;
1253   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
1254               "close_handler=%p/%p", handler, value);
1255
1256   assert (fd != -1);
1257
1258   LOCK (notify_table_lock);
1259   for (i=0; i < DIM (notify_table); i++)
1260     if (notify_table[i].inuse && notify_table[i].fd == fd)
1261       break;
1262   if (i == DIM (notify_table))
1263     for (i = 0; i < DIM (notify_table); i++)
1264       if (!notify_table[i].inuse)
1265         break;
1266   if (i == DIM (notify_table))
1267     {
1268       UNLOCK (notify_table_lock);
1269       gpg_err_set_errno (EINVAL);
1270       return TRACE_SYSRES (-1);
1271     }
1272   notify_table[i].fd = fd;
1273   notify_table[i].handler = handler;
1274   notify_table[i].value = value;
1275   notify_table[i].inuse = 1;
1276   UNLOCK (notify_table_lock);
1277   return TRACE_SYSRES (0);
1278 }
1279
1280
1281 int
1282 _gpgme_io_set_nonblocking (int fd)
1283 {
1284   TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
1285   return 0;
1286 }
1287
1288
1289 #ifdef HAVE_W32CE_SYSTEM
1290 static char *
1291 build_commandline (char **argv, int fd0, int fd0_isnull,
1292                    int fd1, int fd1_isnull,
1293                    int fd2, int fd2_isnull)
1294 {
1295   int i, n;
1296   const char *s;
1297   char *buf, *p;
1298   char fdbuf[3*30];
1299
1300   p = fdbuf;
1301   *p = 0;
1302
1303   if (fd0 != -1)
1304     {
1305       if (fd0_isnull)
1306         strcpy (p, "-&S0=null ");
1307       else
1308         snprintf (p, 25, "-&S0=%d ", fd_table[fd0].rvid);
1309       p += strlen (p);
1310     }
1311   if (fd1 != -1)
1312     {
1313       if (fd1_isnull)
1314         strcpy (p, "-&S1=null ");
1315       else
1316         snprintf (p, 25, "-&S1=%d ", fd_table[fd1].rvid);
1317       p += strlen (p);
1318     }
1319   if (fd2 != -1)
1320     {
1321       if (fd2_isnull)
1322         strcpy (p, "-&S2=null ");
1323       else
1324         snprintf (p, 25, "-&S2=%d ", fd_table[fd2].rvid);
1325       p += strlen (p);
1326     }
1327   strcpy (p, "-&S2=null ");
1328   p += strlen (p);
1329
1330   n = strlen (fdbuf);
1331   for (i=0; (s = argv[i]); i++)
1332     {
1333       if (!i)
1334         continue; /* Ignore argv[0].  */
1335       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
1336       for (; *s; s++)
1337         if (*s == '\"')
1338           n++;  /* Need to double inner quotes.  */
1339     }
1340   n++;
1341   buf = p = malloc (n);
1342   if (! buf)
1343     return NULL;
1344
1345   p = stpcpy (p, fdbuf);
1346   for (i = 0; argv[i]; i++)
1347     {
1348       if (!i)
1349         continue; /* Ignore argv[0].  */
1350       if (i > 1)
1351         p = stpcpy (p, " ");
1352
1353       if (! *argv[i]) /* Empty string. */
1354         p = stpcpy (p, "\"\"");
1355       else if (strpbrk (argv[i], " \t\n\v\f\""))
1356         {
1357           p = stpcpy (p, "\"");
1358           for (s = argv[i]; *s; s++)
1359             {
1360               *p++ = *s;
1361               if (*s == '\"')
1362                 *p++ = *s;
1363             }
1364           *p++ = '\"';
1365           *p = 0;
1366         }
1367       else
1368         p = stpcpy (p, argv[i]);
1369     }
1370
1371   return buf;
1372 }
1373 #else
1374 static char *
1375 build_commandline (char **argv)
1376 {
1377   int i;
1378   int n = 0;
1379   char *buf;
1380   char *p;
1381
1382   /* We have to quote some things because under Windows the program
1383      parses the commandline and does some unquoting.  We enclose the
1384      whole argument in double-quotes, and escape literal double-quotes
1385      as well as backslashes with a backslash.  We end up with a
1386      trailing space at the end of the line, but that is harmless.  */
1387   for (i = 0; argv[i]; i++)
1388     {
1389       p = argv[i];
1390       /* The leading double-quote.  */
1391       n++;
1392       while (*p)
1393         {
1394           /* An extra one for each literal that must be escaped.  */
1395           if (*p == '\\' || *p == '"')
1396             n++;
1397           n++;
1398           p++;
1399         }
1400       /* The trailing double-quote and the delimiter.  */
1401       n += 2;
1402     }
1403   /* And a trailing zero.  */
1404   n++;
1405
1406   buf = p = malloc (n);
1407   if (!buf)
1408     return NULL;
1409   for (i = 0; argv[i]; i++)
1410     {
1411       char *argvp = argv[i];
1412
1413       *(p++) = '"';
1414       while (*argvp)
1415         {
1416           if (*argvp == '\\' || *argvp == '"')
1417             *(p++) = '\\';
1418           *(p++) = *(argvp++);
1419         }
1420       *(p++) = '"';
1421       *(p++) = ' ';
1422     }
1423   *(p++) = 0;
1424
1425   return buf;
1426 }
1427 #endif
1428
1429
1430 int
1431 _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
1432                  struct spawn_fd_item_s *fd_list,
1433                  void (*atfork) (void *opaque, int reserved),
1434                  void *atforkvalue, pid_t *r_pid)
1435 {
1436   PROCESS_INFORMATION pi =
1437     {
1438       NULL,      /* returns process handle */
1439       0,         /* returns primary thread handle */
1440       0,         /* returns pid */
1441       0          /* returns tid */
1442     };
1443   int i;
1444
1445 #ifdef HAVE_W32CE_SYSTEM
1446   int fd_in = -1;
1447   int fd_out = -1;
1448   int fd_err = -1;
1449   int fd_in_isnull = 1;
1450   int fd_out_isnull = 1;
1451   int fd_err_isnull = 1;
1452   char *cmdline;
1453   HANDLE hd = INVALID_HANDLE_VALUE;
1454
1455   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1456               "path=%s", path);
1457   i = 0;
1458   while (argv[i])
1459     {
1460       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1461       i++;
1462     }
1463
1464   for (i = 0; fd_list[i].fd != -1; i++)
1465     {
1466       int fd = fd_list[i].fd;
1467
1468       TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd, fd_list[i].dup_to);
1469       if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1470         {
1471           TRACE_LOG1 ("invalid fd 0x%x", fd);
1472           gpg_err_set_errno (EBADF);
1473           return TRACE_SYSRES (-1);
1474         }
1475       if (fd_table[fd].rvid == 0)
1476         {
1477           TRACE_LOG1 ("fd 0x%x not inheritable (not an RVID)", fd);
1478           gpg_err_set_errno (EBADF);
1479           return TRACE_SYSRES (-1);
1480         }
1481
1482       if (fd_list[i].dup_to == 0)
1483         {
1484           fd_in = fd_list[i].fd;
1485           fd_in_isnull = 0;
1486         }
1487       else if (fd_list[i].dup_to == 1)
1488         {
1489           fd_out = fd_list[i].fd;
1490           fd_out_isnull = 0;
1491         }
1492       else if (fd_list[i].dup_to == 2)
1493         {
1494           fd_err = fd_list[i].fd;
1495           fd_err_isnull = 0;
1496         }
1497     }
1498
1499   cmdline = build_commandline (argv, fd_in, fd_in_isnull,
1500                                fd_out, fd_out_isnull, fd_err, fd_err_isnull);
1501   if (!cmdline)
1502     {
1503       TRACE_LOG1 ("build_commandline failed: %s", strerror (errno));
1504       return TRACE_SYSRES (-1);
1505     }
1506
1507   if (!CreateProcessA (path,                /* Program to start.  */
1508                        cmdline,             /* Command line arguments.  */
1509                        NULL,                 /* (not supported)  */
1510                        NULL,                 /* (not supported)  */
1511                        FALSE,                /* (not supported)  */
1512                        (CREATE_SUSPENDED),   /* Creation flags.  */
1513                        NULL,                 /* (not supported)  */
1514                        NULL,                 /* (not supported)  */
1515                        NULL,                 /* (not supported) */
1516                        &pi                   /* Returns process information.*/
1517                        ))
1518     {
1519       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1520       free (cmdline);
1521       gpg_err_set_errno (EIO);
1522       return TRACE_SYSRES (-1);
1523     }
1524
1525   /* Create arbitrary pipe descriptor to send in ASSIGN_RVID
1526      commands.  Errors are ignored.  We don't need read or write access,
1527      as ASSIGN_RVID works without any permissions, yay!  */
1528   hd = CreateFile (L"GPG1:", 0, 0,
1529                    NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1530   if (hd == INVALID_HANDLE_VALUE)
1531     {
1532       TRACE_LOG1 ("CreateFile failed (ignored): ec=%d",
1533                   (int) GetLastError ());
1534     }
1535
1536   /* Insert the inherited handles.  */
1537   for (i = 0; fd_list[i].fd != -1; i++)
1538     {
1539       /* Return the child name of this handle.  */
1540       fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
1541
1542       if (hd != INVALID_HANDLE_VALUE)
1543         {
1544           DWORD data[2];
1545           data[0] = (DWORD) fd_table[fd_list[i].fd].rvid;
1546           data[1] = pi.dwProcessId;
1547           if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_ASSIGN_RVID,
1548                                 data, sizeof (data), NULL, 0, NULL, NULL))
1549             {
1550               TRACE_LOG3 ("ASSIGN_RVID(%i, %i) failed (ignored): %i",
1551                           data[0], data[1], (int) GetLastError ());
1552             }
1553         }
1554     }
1555   if (hd != INVALID_HANDLE_VALUE)
1556     CloseHandle (hd);
1557
1558 #else
1559   SECURITY_ATTRIBUTES sec_attr;
1560   STARTUPINFOA si;
1561   int cr_flags = CREATE_DEFAULT_ERROR_MODE;
1562   char **args;
1563   char *arg_string;
1564   /* FIXME.  */
1565   int debug_me = 0;
1566   int tmp_fd;
1567   char *tmp_name;
1568
1569   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1570               "path=%s", path);
1571   i = 0;
1572   while (argv[i])
1573     {
1574       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1575       i++;
1576     }
1577
1578   /* We do not inherit any handles by default, and just insert those
1579      handles we want the child to have afterwards.  But some handle
1580      values occur on the command line, and we need to move
1581      stdin/out/err to the right location.  So we use a wrapper program
1582      which gets the information from a temporary file.  */
1583   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
1584     {
1585       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
1586       return TRACE_SYSRES (-1);
1587     }
1588   TRACE_LOG1 ("tmp_name = %s", tmp_name);
1589
1590   args = calloc (2 + i + 1, sizeof (*args));
1591   args[0] = (char *) _gpgme_get_w32spawn_path ();
1592   args[1] = tmp_name;
1593   args[2] = (char *)path;
1594   memcpy (&args[3], &argv[1], i * sizeof (*args));
1595
1596   memset (&sec_attr, 0, sizeof sec_attr);
1597   sec_attr.nLength = sizeof sec_attr;
1598   sec_attr.bInheritHandle = FALSE;
1599
1600   arg_string = build_commandline (args);
1601   free (args);
1602   if (!arg_string)
1603     {
1604       close (tmp_fd);
1605       DeleteFileA (tmp_name);
1606       return TRACE_SYSRES (-1);
1607     }
1608
1609   memset (&si, 0, sizeof si);
1610   si.cb = sizeof (si);
1611   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1612   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
1613   si.hStdInput = INVALID_HANDLE_VALUE;
1614   si.hStdOutput = INVALID_HANDLE_VALUE;
1615   si.hStdError = INVALID_HANDLE_VALUE;
1616
1617   cr_flags |= CREATE_SUSPENDED;
1618   cr_flags |= DETACHED_PROCESS;
1619   cr_flags |= GetPriorityClass (GetCurrentProcess ());
1620   if (!CreateProcessA (_gpgme_get_w32spawn_path (),
1621                        arg_string,
1622                        &sec_attr,     /* process security attributes */
1623                        &sec_attr,     /* thread security attributes */
1624                        FALSE,         /* inherit handles */
1625                        cr_flags,      /* creation flags */
1626                        NULL,          /* environment */
1627                        NULL,          /* use current drive/directory */
1628                        &si,           /* startup information */
1629                        &pi))          /* returns process information */
1630     {
1631       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1632       free (arg_string);
1633       close (tmp_fd);
1634       DeleteFileA (tmp_name);
1635
1636       /* FIXME: Should translate the error code.  */
1637       gpg_err_set_errno (EIO);
1638       return TRACE_SYSRES (-1);
1639     }
1640
1641   free (arg_string);
1642
1643   if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
1644     _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
1645
1646   /* Insert the inherited handles.  */
1647   for (i = 0; fd_list[i].fd != -1; i++)
1648     {
1649       int fd = fd_list[i].fd;
1650       HANDLE ohd = INVALID_HANDLE_VALUE;
1651       HANDLE hd = INVALID_HANDLE_VALUE;
1652
1653       /* Make it inheritable for the wrapper process.  */
1654       if (fd >= 0 && fd < MAX_SLAFD && fd_table[fd].used)
1655         ohd = fd_table[fd].handle;
1656
1657       if (!DuplicateHandle (GetCurrentProcess(), ohd,
1658                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
1659         {
1660           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
1661           TerminateProcess (pi.hProcess, 0);
1662           /* Just in case TerminateProcess didn't work, let the
1663              process fail on its own.  */
1664           ResumeThread (pi.hThread);
1665           CloseHandle (pi.hThread);
1666           CloseHandle (pi.hProcess);
1667
1668           close (tmp_fd);
1669           DeleteFileA (tmp_name);
1670
1671           /* FIXME: Should translate the error code.  */
1672           gpg_err_set_errno (EIO);
1673           return TRACE_SYSRES (-1);
1674         }
1675       /* Return the child name of this handle.  */
1676       fd_list[i].peer_name = handle_to_fd (hd);
1677     }
1678
1679   /* Write the handle translation information to the temporary
1680      file.  */
1681   {
1682     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
1683        notation: "0xFEDCBA9876543210" with an extra white space after
1684        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
1685        for a time when a HANDLE is 64 bit.  */
1686 #define BUFFER_MAX 810
1687     char line[BUFFER_MAX + 1];
1688     int res;
1689     int written;
1690     size_t len;
1691
1692     if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
1693       strcpy (line, "~1 \n");
1694     else
1695       strcpy (line, "\n");
1696     for (i = 0; fd_list[i].fd != -1; i++)
1697       {
1698         /* Strip the newline.  */
1699         len = strlen (line) - 1;
1700
1701         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
1702         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
1703                   fd_list[i].fd, fd_list[i].dup_to,
1704                   fd_list[i].peer_name, fd_list[i].arg_loc);
1705         /* Rather safe than sorry.  */
1706         line[BUFFER_MAX - 1] = '\n';
1707         line[BUFFER_MAX] = '\0';
1708       }
1709     len = strlen (line);
1710     written = 0;
1711     do
1712       {
1713         res = write (tmp_fd, &line[written], len - written);
1714         if (res > 0)
1715           written += res;
1716       }
1717     while (res > 0 || (res < 0 && errno == EAGAIN));
1718   }
1719   close (tmp_fd);
1720   /* The temporary file is deleted by the gpgme-w32spawn process
1721      (hopefully).  */
1722 #endif
1723
1724
1725   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
1726               "dwProcessID=%d, dwThreadId=%d",
1727               pi.hProcess, pi.hThread,
1728               (int) pi.dwProcessId, (int) pi.dwThreadId);
1729
1730   if (r_pid)
1731     *r_pid = (pid_t)pi.dwProcessId;
1732
1733
1734   if (ResumeThread (pi.hThread) < 0)
1735     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
1736
1737   if (!CloseHandle (pi.hThread))
1738     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
1739                 (int) GetLastError ());
1740
1741   TRACE_LOG1 ("process=%p", pi.hProcess);
1742
1743   /* We don't need to wait for the process.  */
1744   if (!CloseHandle (pi.hProcess))
1745     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
1746                 (int) GetLastError ());
1747
1748   if (! (flags & IOSPAWN_FLAG_NOCLOSE))
1749     {
1750       for (i = 0; fd_list[i].fd != -1; i++)
1751         _gpgme_io_close (fd_list[i].fd);
1752     }
1753
1754   for (i = 0; fd_list[i].fd != -1; i++)
1755     if (fd_list[i].dup_to == -1)
1756       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
1757                   fd_list[i].peer_name);
1758     else
1759       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
1760                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
1761                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
1762
1763   return TRACE_SYSRES (0);
1764 }
1765
1766
1767 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
1768    nothing to select, > 0 = number of signaled fds.  */
1769 int
1770 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
1771 {
1772   HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
1773   int waitidx[MAXIMUM_WAIT_OBJECTS];
1774   int code;
1775   int nwait;
1776   int i;
1777   int any;
1778   int count;
1779   void *dbg_help;
1780   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
1781               "nfds=%u, nonblock=%u", nfds, nonblock);
1782
1783 #if 0
1784  restart:
1785 #endif
1786   TRACE_SEQ (dbg_help, "select on [ ");
1787   any = 0;
1788   nwait = 0;
1789   count = 0;
1790   for (i=0; i < nfds; i++)
1791     {
1792       if (fds[i].fd == -1)
1793         continue;
1794       fds[i].signaled = 0;
1795       if (fds[i].for_read || fds[i].for_write)
1796         {
1797           if (fds[i].for_read)
1798             {
1799               struct reader_context_s *ctx = find_reader (fds[i].fd,1);
1800
1801               if (!ctx)
1802                 TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
1803                             fds[i].fd);
1804               else
1805                 {
1806                   if (nwait >= DIM (waitbuf))
1807                     {
1808                       TRACE_END (dbg_help, "oops ]");
1809                       TRACE_LOG ("Too many objects for WFMO!");
1810                       /* FIXME: Should translate the error code.  */
1811                       gpg_err_set_errno (EIO);
1812                       return TRACE_SYSRES (-1);
1813                     }
1814                   waitidx[nwait] = i;
1815                   waitbuf[nwait++] = ctx->have_data_ev;
1816                 }
1817               TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
1818               any = 1;
1819             }
1820           else if (fds[i].for_write)
1821             {
1822               struct writer_context_s *ctx = find_writer (fds[i].fd,1);
1823
1824               if (!ctx)
1825                 TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
1826                             fds[i].fd);
1827               else
1828                 {
1829                   if (nwait >= DIM (waitbuf))
1830                     {
1831                       TRACE_END (dbg_help, "oops ]");
1832                       TRACE_LOG ("Too many objects for WFMO!");
1833                       /* FIXME: Should translate the error code.  */
1834                       gpg_err_set_errno (EIO);
1835                       return TRACE_SYSRES (-1);
1836                     }
1837                   waitidx[nwait] = i;
1838                   waitbuf[nwait++] = ctx->is_empty;
1839                 }
1840               TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
1841               any = 1;
1842             }
1843         }
1844     }
1845   TRACE_END (dbg_help, "]");
1846   if (!any)
1847     return TRACE_SYSRES (0);
1848
1849   code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
1850   if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
1851     {
1852       /* This WFMO is a really silly function: It does return either
1853          the index of the signaled object or if 2 objects have been
1854          signalled at the same time, the index of the object with the
1855          lowest object is returned - so and how do we find out how
1856          many objects have been signaled???.  The only solution I can
1857          imagine is to test each object starting with the returned
1858          index individually - how dull.  */
1859       any = 0;
1860       for (i = code - WAIT_OBJECT_0; i < nwait; i++)
1861         {
1862           if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
1863             {
1864               assert (waitidx[i] >=0 && waitidx[i] < nfds);
1865               fds[waitidx[i]].signaled = 1;
1866               any = 1;
1867               count++;
1868             }
1869         }
1870       if (!any)
1871         {
1872           TRACE_LOG ("no signaled objects found after WFMO");
1873           count = -1;
1874         }
1875     }
1876   else if (code == WAIT_TIMEOUT)
1877     TRACE_LOG ("WFMO timed out");
1878   else if (code == WAIT_FAILED)
1879     {
1880       int le = (int) GetLastError ();
1881 #if 0
1882       if (le == ERROR_INVALID_HANDLE)
1883         {
1884           int k;
1885           int j = handle_to_fd (waitbuf[i]);
1886
1887           TRACE_LOG1 ("WFMO invalid handle %d removed", j);
1888           for (k = 0 ; k < nfds; k++)
1889             {
1890               if (fds[k].fd == j)
1891                 {
1892                   fds[k].for_read = fds[k].for_write = 0;
1893                   goto restart;
1894                 }
1895             }
1896           TRACE_LOG (" oops, or not???");
1897         }
1898 #endif
1899       TRACE_LOG1 ("WFMO failed: %d", le);
1900       count = -1;
1901     }
1902   else
1903     {
1904       TRACE_LOG1 ("WFMO returned %d", code);
1905       count = -1;
1906     }
1907
1908   if (count > 0)
1909     {
1910       TRACE_SEQ (dbg_help, "select OK [ ");
1911       for (i = 0; i < nfds; i++)
1912         {
1913           if (fds[i].fd == -1)
1914             continue;
1915           if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
1916             TRACE_ADD2 (dbg_help, "%c0x%x ",
1917                         fds[i].for_read ? 'r' : 'w', fds[i].fd);
1918         }
1919       TRACE_END (dbg_help, "]");
1920     }
1921
1922   if (count < 0)
1923     {
1924       /* FIXME: Should determine a proper error code.  */
1925       gpg_err_set_errno (EIO);
1926     }
1927
1928   return TRACE_SYSRES (count);
1929 }
1930
1931
1932 void
1933 _gpgme_io_subsystem_init (void)
1934 {
1935   /* Nothing to do.  */
1936 }
1937
1938
1939 /* Write the printable version of FD to the buffer BUF of length
1940    BUFLEN.  The printable version is the representation on the command
1941    line that the child process expects.  */
1942 int
1943 _gpgme_io_fd2str (char *buf, int buflen, int fd)
1944 {
1945 #ifdef HAVE_W32CE_SYSTEM
1946   /* FIXME: For now. See above.  */
1947   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
1948       || fd_table[fd].rvid == 0)
1949     fd = -1;
1950   else
1951     fd = fd_table[fd].rvid;
1952 #endif
1953
1954   return snprintf (buf, buflen, "%d", fd);
1955 }
1956
1957
1958 int
1959 _gpgme_io_dup (int fd)
1960 {
1961   int newfd;
1962   struct reader_context_s *rd_ctx;
1963   struct writer_context_s *wt_ctx;
1964   int i;
1965
1966   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
1967
1968   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1969     {
1970       gpg_err_set_errno (EINVAL);
1971       return TRACE_SYSRES (-1);
1972     }
1973
1974   newfd = new_fd();
1975   if (newfd == -1)
1976     return TRACE_SYSRES (-1);
1977
1978   fd_table[newfd].handle = fd_table[fd].handle;
1979   fd_table[newfd].socket = fd_table[fd].socket;
1980   fd_table[newfd].rvid = fd_table[fd].rvid;
1981   fd_table[newfd].dup_from = fd;
1982
1983   rd_ctx = find_reader (fd, 1);
1984   if (rd_ctx)
1985     {
1986       /* No need for locking, as the only races are against the reader
1987          thread itself, which doesn't touch refcount.  */
1988       rd_ctx->refcount++;
1989
1990       LOCK (reader_table_lock);
1991       for (i = 0; i < reader_table_size; i++)
1992         if (!reader_table[i].used)
1993           break;
1994       /* FIXME.  */
1995       assert (i != reader_table_size);
1996       reader_table[i].fd = newfd;
1997       reader_table[i].context = rd_ctx;
1998       reader_table[i].used = 1;
1999       UNLOCK (reader_table_lock);
2000     }
2001
2002   wt_ctx = find_writer (fd, 1);
2003   if (wt_ctx)
2004     {
2005       /* No need for locking, as the only races are against the writer
2006          thread itself, which doesn't touch refcount.  */
2007       wt_ctx->refcount++;
2008
2009       LOCK (writer_table_lock);
2010       for (i = 0; i < writer_table_size; i++)
2011         if (!writer_table[i].used)
2012           break;
2013       /* FIXME.  */
2014       assert (i != writer_table_size);
2015       writer_table[i].fd = newfd;
2016       writer_table[i].context = wt_ctx;
2017       writer_table[i].used = 1;
2018       UNLOCK (writer_table_lock);
2019     }
2020
2021   return TRACE_SYSRES (newfd);
2022 }
2023
2024 \f
2025 /* The following interface is only useful for GPGME Glib and Qt.  */
2026
2027 /* Compatibility interface, obsolete.  */
2028 void *
2029 gpgme_get_giochannel (int fd)
2030 {
2031   return NULL;
2032 }
2033
2034
2035 /* Look up the giochannel or qiodevice for file descriptor FD.  */
2036 void *
2037 gpgme_get_fdptr (int fd)
2038 {
2039   return NULL;
2040 }
2041
2042 \f
2043 static int
2044 wsa2errno (int err)
2045 {
2046   switch (err)
2047     {
2048     case WSAENOTSOCK:
2049       return EINVAL;
2050     case WSAEWOULDBLOCK:
2051       return EAGAIN;
2052     case ERROR_BROKEN_PIPE:
2053       return EPIPE;
2054     case WSANOTINITIALISED:
2055       return ENOSYS;
2056     default:
2057       return EIO;
2058     }
2059 }
2060
2061
2062 int
2063 _gpgme_io_socket (int domain, int type, int proto)
2064 {
2065   int res;
2066   int fd;
2067
2068   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
2069               "type=%i, protp=%i", type, proto);
2070
2071   fd = new_fd();
2072   if (fd == -1)
2073     return TRACE_SYSRES (-1);
2074
2075   res = socket (domain, type, proto);
2076   if (res == INVALID_SOCKET)
2077     {
2078       release_fd (fd);
2079       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2080       return TRACE_SYSRES (-1);
2081     }
2082   fd_table[fd].socket = res;
2083
2084   TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
2085
2086   return fd;
2087 }
2088
2089
2090 int
2091 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
2092 {
2093   int res;
2094
2095   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
2096               "addr=%p, addrlen=%i", addr, addrlen);
2097
2098   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
2099     {
2100       gpg_err_set_errno (EBADF);
2101       return TRACE_SYSRES (-1);
2102     }
2103
2104   res = connect (fd_table[fd].socket, addr, addrlen);
2105   if (res)
2106     {
2107       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2108       return TRACE_SYSRES (-1);
2109     }
2110
2111   return TRACE_SUC ();
2112 }