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