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