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