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