e83aa0b9fce14ac1f80b8730e02260ce21ba7e76
[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, pid_t *r_pid)
1046 {
1047   SECURITY_ATTRIBUTES sec_attr;
1048   PROCESS_INFORMATION pi =
1049     {
1050       NULL,      /* returns process handle */
1051       0,         /* returns primary thread handle */
1052       0,         /* returns pid */
1053       0          /* returns tid */
1054     };
1055   STARTUPINFO si;
1056   int cr_flags = (CREATE_DEFAULT_ERROR_MODE
1057                   | GetPriorityClass (GetCurrentProcess ()));
1058   int i;
1059   char **args;
1060   char *arg_string;
1061   /* FIXME.  */
1062   int debug_me = 0;
1063   int tmp_fd;
1064   char *tmp_name;
1065
1066   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1067               "path=%s", path);
1068   i = 0;
1069   while (argv[i])
1070     {
1071       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1072       i++;
1073     }
1074
1075   /* We do not inherit any handles by default, and just insert those
1076      handles we want the child to have afterwards.  But some handle
1077      values occur on the command line, and we need to move
1078      stdin/out/err to the right location.  So we use a wrapper program
1079      which gets the information from a temporary file.  */
1080   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
1081     {
1082       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
1083       return TRACE_SYSRES (-1);
1084     }
1085   TRACE_LOG1 ("tmp_name = %s", tmp_name);
1086
1087   args = calloc (2 + i + 1, sizeof (*args));
1088   args[0] = (char *) _gpgme_get_w32spawn_path ();
1089   args[1] = tmp_name;
1090   args[2] = path;
1091   memcpy (&args[3], &argv[1], i * sizeof (*args));
1092
1093   memset (&sec_attr, 0, sizeof sec_attr);
1094   sec_attr.nLength = sizeof sec_attr;
1095   sec_attr.bInheritHandle = FALSE;
1096  
1097   arg_string = build_commandline (args);
1098   free (args);
1099   if (!arg_string)
1100     {
1101       close (tmp_fd);
1102       DeleteFile (tmp_name);
1103       return TRACE_SYSRES (-1);
1104     }
1105
1106   memset (&si, 0, sizeof si);
1107   si.cb = sizeof (si);
1108   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1109   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
1110   si.hStdInput = INVALID_HANDLE_VALUE;
1111   si.hStdOutput = INVALID_HANDLE_VALUE;
1112   si.hStdError = INVALID_HANDLE_VALUE;
1113
1114   cr_flags |= CREATE_SUSPENDED; 
1115   cr_flags |= DETACHED_PROCESS;
1116   if (!CreateProcessA (_gpgme_get_w32spawn_path (),
1117                        arg_string,
1118                        &sec_attr,     /* process security attributes */
1119                        &sec_attr,     /* thread security attributes */
1120                        FALSE,         /* inherit handles */
1121                        cr_flags,      /* creation flags */
1122                        NULL,          /* environment */
1123                        NULL,          /* use current drive/directory */
1124                        &si,           /* startup information */
1125                        &pi))          /* returns process information */
1126     {
1127       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1128       free (arg_string);
1129       close (tmp_fd);
1130       DeleteFile (tmp_name);
1131
1132       /* FIXME: Should translate the error code.  */
1133       errno = EIO;
1134       return TRACE_SYSRES (-1);
1135     }
1136
1137   free (arg_string);
1138
1139   if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
1140     _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
1141
1142   /* Insert the inherited handles.  */
1143   for (i = 0; fd_list[i].fd != -1; i++)
1144     {
1145       HANDLE hd;
1146
1147       /* Make it inheritable for the wrapper process.  */
1148       if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd),
1149                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
1150         {
1151           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
1152           TerminateProcess (pi.hProcess, 0);
1153           /* Just in case TerminateProcess didn't work, let the
1154              process fail on its own.  */
1155           ResumeThread (pi.hThread);
1156           CloseHandle (pi.hThread);
1157           CloseHandle (pi.hProcess);
1158
1159           close (tmp_fd);
1160           DeleteFile (tmp_name);
1161
1162           /* FIXME: Should translate the error code.  */
1163           errno = EIO;
1164           return TRACE_SYSRES (-1);
1165         }
1166       /* Return the child name of this handle.  */
1167       fd_list[i].peer_name = handle_to_fd (hd);
1168     }
1169   
1170   /* Write the handle translation information to the temporary
1171      file.  */
1172   {
1173     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
1174        notation: "0xFEDCBA9876543210" with an extra white space after
1175        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
1176        for a time when a HANDLE is 64 bit.  */
1177 #define BUFFER_MAX 810
1178     char line[BUFFER_MAX + 1];
1179     int res;
1180     int written;
1181     size_t len;
1182
1183     if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
1184       strcpy (line, "~1 \n");
1185     else
1186       strcpy (line, "\n");
1187     for (i = 0; fd_list[i].fd != -1; i++)
1188       {
1189         /* Strip the newline.  */
1190         len = strlen (line) - 1;
1191         
1192         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
1193         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
1194                   fd_list[i].fd, fd_list[i].dup_to,
1195                   fd_list[i].peer_name, fd_list[i].arg_loc);
1196         /* Rather safe than sorry.  */
1197         line[BUFFER_MAX - 1] = '\n';
1198         line[BUFFER_MAX] = '\0';
1199       }
1200     len = strlen (line);
1201     written = 0;
1202     do
1203       {
1204         res = write (tmp_fd, &line[written], len - written);
1205         if (res > 0)
1206           written += res;
1207       }
1208     while (res > 0 || (res < 0 && errno == EAGAIN));
1209   }
1210   close (tmp_fd);
1211   /* The temporary file is deleted by the gpgme-w32spawn process
1212      (hopefully).  */
1213
1214   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
1215               "dwProcessID=%d, dwThreadId=%d",
1216               pi.hProcess, pi.hThread, 
1217               (int) pi.dwProcessId, (int) pi.dwThreadId);
1218   
1219   if (r_pid)
1220     *r_pid = (pid_t)pi.dwProcessId;
1221
1222   
1223   if (ResumeThread (pi.hThread) < 0)
1224     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
1225   
1226   if (!CloseHandle (pi.hThread))
1227     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
1228                 (int) GetLastError ());
1229
1230   TRACE_LOG1 ("process=%p", pi.hProcess);
1231
1232   /* We don't need to wait for the process.  */
1233   if (!CloseHandle (pi.hProcess))
1234     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
1235                 (int) GetLastError ());
1236
1237   if (! (flags & IOSPAWN_FLAG_NOCLOSE))
1238     {
1239       for (i = 0; fd_list[i].fd != -1; i++)
1240         _gpgme_io_close (fd_list[i].fd);
1241     }
1242
1243   for (i = 0; fd_list[i].fd != -1; i++)
1244     if (fd_list[i].dup_to == -1)
1245       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
1246                   fd_list[i].peer_name);
1247     else
1248       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
1249                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
1250                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
1251
1252   return TRACE_SYSRES (0);
1253 }
1254
1255
1256 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
1257    nothing to select, > 0 = number of signaled fds.  */
1258 int
1259 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
1260 {
1261   HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
1262   int waitidx[MAXIMUM_WAIT_OBJECTS];
1263   int code;
1264   int nwait;
1265   int i;
1266   int any;
1267   int count;
1268   void *dbg_help;
1269   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
1270               "nfds=%u, nonblock=%u", nfds, nonblock);
1271
1272  restart:
1273   TRACE_SEQ (dbg_help, "select on [ ");
1274   any = 0;
1275   nwait = 0;
1276   count = 0;
1277   for (i=0; i < nfds; i++)
1278     {
1279       if (fds[i].fd == -1)
1280         continue;
1281       fds[i].signaled = 0;
1282       if (fds[i].for_read || fds[i].for_write)
1283         {
1284           if (fds[i].for_read)
1285             {
1286               struct reader_context_s *ctx = find_reader (fds[i].fd,0);
1287               
1288               if (!ctx)
1289                 TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
1290                             fds[i].fd);
1291               else
1292                 {
1293                   if (nwait >= DIM (waitbuf))
1294                     {
1295                       TRACE_END (dbg_help, "oops ]");
1296                       TRACE_LOG ("Too many objects for WFMO!");
1297                       /* FIXME: Should translate the error code.  */
1298                       errno = EIO;
1299                       return TRACE_SYSRES (-1);
1300                     }
1301                   waitidx[nwait] = i;
1302                   waitbuf[nwait++] = ctx->have_data_ev;
1303                 }
1304               TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
1305               any = 1;
1306             }
1307           else if (fds[i].for_write)
1308             {
1309               struct writer_context_s *ctx = find_writer (fds[i].fd,0);
1310               
1311               if (!ctx)
1312                 TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
1313                             fds[i].fd);
1314               else
1315                 {
1316                   if (nwait >= DIM (waitbuf))
1317                     {
1318                       TRACE_END (dbg_help, "oops ]");
1319                       TRACE_LOG ("Too many objects for WFMO!");
1320                       /* FIXME: Should translate the error code.  */
1321                       errno = EIO;
1322                       return TRACE_SYSRES (-1);
1323                     }
1324                   waitidx[nwait] = i;
1325                   waitbuf[nwait++] = ctx->is_empty;
1326                 }
1327               TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
1328               any = 1;
1329             }
1330         }
1331     }
1332   TRACE_END (dbg_help, "]");
1333   if (!any) 
1334     return TRACE_SYSRES (0);
1335
1336   code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
1337   if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
1338     {
1339       /* This WFMO is a really silly function: It does return either
1340          the index of the signaled object or if 2 objects have been
1341          signalled at the same time, the index of the object with the
1342          lowest object is returned - so and how do we find out how
1343          many objects have been signaled???.  The only solution I can
1344          imagine is to test each object starting with the returned
1345          index individually - how dull.  */
1346       any = 0;
1347       for (i = code - WAIT_OBJECT_0; i < nwait; i++)
1348         {
1349           if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
1350             {
1351               assert (waitidx[i] >=0 && waitidx[i] < nfds);
1352               fds[waitidx[i]].signaled = 1;
1353               any = 1;
1354               count++;
1355             }
1356         }
1357       if (!any)
1358         {
1359           TRACE_LOG ("no signaled objects found after WFMO");
1360           count = -1;
1361         }
1362     }
1363   else if (code == WAIT_TIMEOUT)
1364     TRACE_LOG ("WFMO timed out");
1365   else if (code == WAIT_FAILED)
1366     {
1367       int le = (int) GetLastError ();
1368       if (le == ERROR_INVALID_HANDLE)
1369         {
1370           int k;
1371           int j = handle_to_fd (waitbuf[i]);
1372           
1373           TRACE_LOG1 ("WFMO invalid handle %d removed", j);
1374           for (k = 0 ; k < nfds; k++)
1375             {
1376               if (fds[k].fd == j)
1377                 {
1378                   fds[k].for_read = fds[k].for_write = 0;
1379                   goto restart;
1380                 }
1381             }
1382           TRACE_LOG (" oops, or not???");
1383         }
1384       TRACE_LOG1 ("WFMO failed: %d", le);
1385       count = -1;
1386     }
1387   else
1388     {
1389       TRACE_LOG1 ("WFMO returned %d", code);
1390       count = -1;
1391     }
1392   
1393   if (count > 0)
1394     {
1395       TRACE_SEQ (dbg_help, "select OK [ ");
1396       for (i = 0; i < nfds; i++)
1397         {
1398           if (fds[i].fd == -1)
1399             continue;
1400           if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
1401             TRACE_ADD2 (dbg_help, "%c0x%x ",
1402                         fds[i].for_read ? 'r' : 'w', fds[i].fd);
1403         }
1404       TRACE_END (dbg_help, "]");
1405     }
1406
1407   if (count < 0)
1408     {
1409       /* FIXME: Should determine a proper error code.  */
1410       errno = EIO;
1411     }
1412   
1413   return TRACE_SYSRES (count);
1414 }
1415
1416
1417 void
1418 _gpgme_io_subsystem_init (void)
1419 {
1420   /* Nothing to do.  */
1421 }
1422
1423
1424 /* Write the printable version of FD to the buffer BUF of length
1425    BUFLEN.  The printable version is the representation on the command
1426    line that the child process expects.  */
1427 int
1428 _gpgme_io_fd2str (char *buf, int buflen, int fd)
1429 {
1430   return snprintf (buf, buflen, "%d", fd);
1431 }
1432
1433
1434 int
1435 _gpgme_io_dup (int fd)
1436 {
1437   HANDLE handle = fd_to_handle (fd);
1438   HANDLE new_handle = fd_to_handle (fd);
1439   int i;
1440   struct reader_context_s *rd_ctx;
1441   struct writer_context_s *wt_ctx;
1442
1443   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
1444
1445   if (!DuplicateHandle (GetCurrentProcess(), handle,
1446                         GetCurrentProcess(), &new_handle,
1447                         0, FALSE, DUPLICATE_SAME_ACCESS))
1448     {
1449       TRACE_LOG1 ("DuplicateHandle failed: ec=%d\n", (int) GetLastError ());
1450       /* FIXME: Translate error code.  */
1451       errno = EIO;
1452       return TRACE_SYSRES (-1);
1453     }
1454
1455   rd_ctx = find_reader (fd, 0);
1456   if (rd_ctx)
1457     {
1458       /* No need for locking, as the only races are against the reader
1459          thread itself, which doesn't touch refcount.  */
1460       rd_ctx->refcount++;
1461
1462       LOCK (reader_table_lock);
1463       for (i = 0; i < reader_table_size; i++)
1464         if (!reader_table[i].used)
1465           break;
1466       /* FIXME.  */
1467       assert (i != reader_table_size);
1468       reader_table[i].fd = handle_to_fd (new_handle);
1469       reader_table[i].context = rd_ctx;
1470       reader_table[i].used = 1;
1471       UNLOCK (reader_table_lock);
1472     }
1473
1474   wt_ctx = find_writer (fd, 0);
1475   if (wt_ctx)
1476     {
1477       /* No need for locking, as the only races are against the writer
1478          thread itself, which doesn't touch refcount.  */
1479       wt_ctx->refcount++;
1480
1481       LOCK (writer_table_lock);
1482       for (i = 0; i < writer_table_size; i++)
1483         if (!writer_table[i].used)
1484           break;
1485       /* FIXME.  */
1486       assert (i != writer_table_size);
1487       writer_table[i].fd = handle_to_fd (new_handle);
1488       writer_table[i].context = wt_ctx;
1489       writer_table[i].used = 1;
1490       UNLOCK (writer_table_lock);
1491     }
1492
1493   return TRACE_SYSRES (handle_to_fd (new_handle));
1494 }
1495
1496 \f
1497 /* The following interface is only useful for GPGME Glib and Qt.  */
1498
1499 /* Compatibility interface, obsolete.  */
1500 void *
1501 gpgme_get_giochannel (int fd)
1502 {
1503   return NULL;
1504 }
1505
1506
1507 /* Look up the giochannel or qiodevice for file descriptor FD.  */
1508 void *
1509 gpgme_get_fdptr (int fd)
1510 {
1511   return NULL;
1512 }
1513
1514 \f
1515 static int
1516 wsa2errno (int err)
1517 {
1518   switch (err)
1519     {
1520     case WSAENOTSOCK:
1521       return EINVAL;
1522     case WSAEWOULDBLOCK:
1523       return EAGAIN;
1524     case ERROR_BROKEN_PIPE:
1525       return EPIPE;
1526     case WSANOTINITIALISED:
1527       return ENOSYS;
1528     default:
1529       return EIO;
1530     }
1531 }
1532
1533
1534 int
1535 _gpgme_io_socket (int domain, int type, int proto)
1536 {
1537   int res;
1538
1539   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
1540               "type=%i, protp=%i", type, proto);
1541
1542   res = socket (domain, type, proto);
1543   if (res == INVALID_SOCKET)
1544     {
1545       errno = wsa2errno (WSAGetLastError ());
1546       return TRACE_SYSRES (-1);
1547     }
1548
1549   TRACE_SUC1 ("socket=0x%x", res);
1550   
1551   return res;
1552 }
1553
1554
1555 int
1556 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
1557 {
1558   int res;
1559
1560   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
1561               "addr=%p, addrlen=%i", addr, addrlen);
1562
1563   res = connect (fd, addr, addrlen);
1564   if (!res)
1565     {
1566       errno = wsa2errno (WSAGetLastError ());
1567       return TRACE_SYSRES (-1);
1568     }
1569
1570   return TRACE_SUC ();
1571 }