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