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