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