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