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