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