2010-06-10 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / src / w32-io.c
1 /* w32-io.c - W32 API I/O functions.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010 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 <fcntl.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <windows.h>
34 #include <io.h>
35
36 #ifdef HAVE_W32CE_SYSTEM
37 #include <assuan.h>
38 #include <winioctl.h>
39 #define GPGCEDEV_IOCTL_UNBLOCK                                        \
40   CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS)
41 #endif
42
43 #include "util.h"
44 #include "sema.h"
45 #include "priv-io.h"
46 #include "debug.h"
47
48
49 /* FIXME: Optimize.  */
50 #define MAX_SLAFD 512
51
52 static struct
53 {
54   int used;
55
56   /* If this is not INVALID_HANDLE_VALUE, then it's a handle.  */
57   HANDLE handle;
58
59   /* If this is not INVALID_SOCKET, then it's a Windows socket.  */
60   int socket;
61
62   /* If this is not 0, then it's a rendezvous ID for the pipe server.  */
63   int rvid;
64
65   /* DUP_FROM is -1 if this file descriptor was allocated by pipe or
66      socket functions.  Only then should the handle or socket be
67      destroyed when this FD is closed.  This, together with the fact
68      that dup'ed file descriptors are closed before the file
69      descriptors from which they are dup'ed are closed, ensures that
70      the handle or socket is always valid, and shared among all file
71      descriptors refering to the same underlying object.
72
73      The logic behind this is that there is only one reason for us to
74      dup file descriptors anyway: to allow simpler book-keeping of
75      file descriptors shared between GPGME and libassuan, which both
76      want to close something.  Using the same handle for these
77      duplicates works just fine.  */
78   int dup_from;
79 } fd_table[MAX_SLAFD];  
80
81
82 /* Returns the FD or -1 on resource limit.  */
83 int
84 new_fd (void)
85 {
86   int idx;
87
88   for (idx = 0; idx < MAX_SLAFD; idx++)
89     if (! fd_table[idx].used)
90       break;
91
92   if (idx == MAX_SLAFD)
93     {
94       gpg_err_set_errno (EIO);
95       return -1;
96     }
97
98   fd_table[idx].used = 1;
99   fd_table[idx].handle = INVALID_HANDLE_VALUE;
100   fd_table[idx].socket = INVALID_SOCKET;
101   fd_table[idx].rvid = 0;
102   fd_table[idx].dup_from = -1;
103
104   return idx;
105 }
106
107
108 void
109 release_fd (int fd)
110 {
111   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
112     return;
113
114   fd_table[fd].used = 0;
115   fd_table[fd].handle = INVALID_HANDLE_VALUE;
116   fd_table[fd].socket = INVALID_SOCKET;
117   fd_table[fd].rvid = 0;
118   fd_table[fd].dup_from = -1;
119 }
120
121
122 #define pid_to_handle(a) ((HANDLE)(a))
123 #define handle_to_pid(a) ((int)(a))
124
125 #define READBUF_SIZE 4096
126 #define WRITEBUF_SIZE 4096
127 #define PIPEBUF_SIZE  4096
128 #define MAX_READERS 64
129 #define MAX_WRITERS 64
130
131 static struct
132 {
133   int inuse;
134   int fd;
135   _gpgme_close_notify_handler_t handler;
136   void *value;
137 } notify_table[MAX_SLAFD];
138 DEFINE_STATIC_LOCK (notify_table_lock);
139
140
141 struct reader_context_s
142 {
143   HANDLE file_hd;
144   int file_sock;
145   HANDLE thread_hd;     
146   int refcount;
147
148   DECLARE_LOCK (mutex);
149
150   int stop_me;
151   int eof;
152   int eof_shortcut;
153   int error;
154   int error_code;
155   
156   /* This is manually reset.  */
157   HANDLE have_data_ev;
158   /* This is automatically reset.  */
159   HANDLE have_space_ev;
160   HANDLE stopped;
161   size_t readpos, writepos;
162   char buffer[READBUF_SIZE];
163 };
164
165
166 static struct
167 {
168   volatile int used;
169   int fd;
170   struct reader_context_s *context;
171 } reader_table[MAX_READERS];
172 static int reader_table_size= MAX_READERS;
173 DEFINE_STATIC_LOCK (reader_table_lock);
174
175
176 struct writer_context_s
177 {
178   HANDLE file_hd;
179   int file_sock;
180   HANDLE thread_hd;     
181   int refcount;
182
183   DECLARE_LOCK (mutex);
184   
185   int stop_me;
186   int error;
187   int error_code;
188
189   /* This is manually reset.  */
190   HANDLE have_data;
191   HANDLE is_empty;
192   HANDLE stopped;
193   size_t nbytes; 
194   char buffer[WRITEBUF_SIZE];
195 };
196
197
198 static struct
199 {
200   volatile int used;
201   int fd;
202   struct writer_context_s *context;
203 } writer_table[MAX_WRITERS];
204 static int writer_table_size= MAX_WRITERS;
205 DEFINE_STATIC_LOCK (writer_table_lock);
206
207
208 static int
209 get_desired_thread_priority (void)
210 {
211   int value;
212
213   if (!_gpgme_get_conf_int ("IOThreadPriority", &value))
214     {
215       value = THREAD_PRIORITY_HIGHEST;
216       TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
217               "%d (default)", value);
218     }
219   else
220     {
221       TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
222               "%d (configured)", value);
223     }
224   return value;
225 }
226
227
228 static HANDLE
229 set_synchronize (HANDLE hd)
230 {
231 #ifdef HAVE_W32CE_SYSTEM
232   return hd;
233 #else
234   HANDLE new_hd;
235
236   /* For NT we have to set the sync flag.  It seems that the only way
237      to do it is by duplicating the handle.  Tsss...  */
238   if (!DuplicateHandle (GetCurrentProcess (), hd,
239                         GetCurrentProcess (), &new_hd,
240                         EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0))
241     {
242       TRACE1 (DEBUG_SYSIO, "gpgme:set_synchronize", hd,
243               "DuplicateHandle failed: ec=%d", (int) GetLastError ());
244       /* FIXME: Should translate the error code.  */
245       gpg_err_set_errno (EIO);
246       return INVALID_HANDLE_VALUE;
247     }
248
249   CloseHandle (hd);
250   return new_hd;
251 #endif
252 }
253
254
255 /* Return 1 if HD refers to a socket, 0 if it does not refer to a
256    socket, and -1 for unknown (autodetect).  */
257 static int
258 is_socket (HANDLE hd)
259 {
260 #ifdef HAVE_W32CE_SYSTEM
261   return -1;
262 #else
263   /* We need to figure out whether we are working on a socket or on a
264      handle.  A trivial way would be to check for the return code of
265      recv and see if it is WSAENOTSOCK.  However the recv may block
266      after the server process died and thus the destroy_reader will
267      hang.  Another option is to use getsockopt to test whether it is
268      a socket.  The bug here is that once a socket with a certain
269      values has been opened, closed and later a CreatePipe returned
270      the same value (i.e. handle), getsockopt still believes it is a
271      socket.  What we do now is to use a combination of GetFileType
272      and GetNamedPipeInfo.  The specs say that the latter may be used
273      on anonymous pipes as well.  Note that there are claims that
274      since winsocket version 2 ReadFile may be used on a socket but
275      only if it is supported by the service provider.  Tests on a
276      stock XP using a local TCP socket show that it does not work.  */
277   DWORD dummyflags, dummyoutsize, dummyinsize, dummyinst;
278
279   if (GetFileType (hd) == FILE_TYPE_PIPE
280       && !GetNamedPipeInfo (hd, &dummyflags, &dummyoutsize,
281                             &dummyinsize, &dummyinst))
282     return 1; /* Function failed; thus we assume it is a socket.  */
283   else
284     return 0; /* Success; this is not a socket.  */
285 #endif
286 }
287
288
289 static DWORD CALLBACK 
290 reader (void *arg)
291 {
292   struct reader_context_s *ctx = arg;
293   int nbytes;
294   DWORD nread;
295   int sock;
296   TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
297               "thread=%p", ctx->thread_hd);
298
299   if (ctx->file_hd != INVALID_HANDLE_VALUE)
300     sock = 0;
301   else
302     sock = 1;
303
304   for (;;)
305     {
306       LOCK (ctx->mutex);
307       /* Leave a 1 byte gap so that we can see whether it is empty or
308          full.  */
309       if ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
310         { 
311           /* Wait for space.  */
312           if (!ResetEvent (ctx->have_space_ev))
313             TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
314           UNLOCK (ctx->mutex);
315           TRACE_LOG ("waiting for space");
316           WaitForSingleObject (ctx->have_space_ev, INFINITE);
317           TRACE_LOG ("got space");
318           LOCK (ctx->mutex);
319         }
320       if (ctx->stop_me)
321         {
322           UNLOCK (ctx->mutex);
323           break;
324         }
325       nbytes = (ctx->readpos + READBUF_SIZE
326                 - ctx->writepos - 1) % READBUF_SIZE;
327       if (nbytes > READBUF_SIZE - ctx->writepos)
328         nbytes = READBUF_SIZE - ctx->writepos;
329       UNLOCK (ctx->mutex);
330       
331       TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
332
333       if (sock)
334         {
335           int n;
336
337           n = recv (ctx->file_sock, ctx->buffer + ctx->writepos, nbytes, 0);
338           if (n < 0)
339             {
340               ctx->error_code = (int) WSAGetLastError ();
341               if (ctx->error_code == ERROR_BROKEN_PIPE)
342                 {
343                   ctx->eof = 1;
344                   TRACE_LOG ("got EOF (broken connection)");
345                 }
346               else
347                 {
348                   ctx->error = 1;
349                   TRACE_LOG1 ("recv error: ec=%d", ctx->error_code);
350                 }
351               break;
352             }
353           nread = n;
354         }
355       else
356         {
357           if (!ReadFile (ctx->file_hd,
358                          ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
359             {
360               ctx->error_code = (int) GetLastError ();
361               /* NOTE (W32CE): Do not ignore ERROR_BUSY!  Check at
362                  least stop_me if that happens.  */
363               if (ctx->error_code == ERROR_BROKEN_PIPE)
364                 {
365                   ctx->eof = 1;
366                   TRACE_LOG ("got EOF (broken pipe)");
367                 }
368               else
369                 {
370                   ctx->error = 1;
371                   TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
372                 }
373               break;
374             }
375         }
376       LOCK (ctx->mutex);
377       if (ctx->stop_me)
378         {
379           UNLOCK (ctx->mutex);
380           break;
381         }
382       if (!nread)
383         {
384           ctx->eof = 1;
385           TRACE_LOG ("got eof");
386           UNLOCK (ctx->mutex);
387           break;
388         }
389       TRACE_LOG1 ("got %u bytes", nread);
390       
391       ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
392       if (!SetEvent (ctx->have_data_ev))
393         TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
394                     (int) GetLastError ());
395       UNLOCK (ctx->mutex);
396     }
397   /* Indicate that we have an error or EOF.  */
398   if (!SetEvent (ctx->have_data_ev))
399         TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
400                     (int) GetLastError ());
401   SetEvent (ctx->stopped);
402   
403   return TRACE_SUC ();
404 }
405
406
407 static struct reader_context_s *
408 create_reader (int fd)
409 {
410   struct reader_context_s *ctx;
411   SECURITY_ATTRIBUTES sec_attr;
412   DWORD tid;
413
414   TRACE_BEG (DEBUG_SYSIO, "gpgme:create_reader", fd);
415
416   memset (&sec_attr, 0, sizeof sec_attr);
417   sec_attr.nLength = sizeof sec_attr;
418   sec_attr.bInheritHandle = FALSE;
419   
420   ctx = calloc (1, sizeof *ctx);
421   if (!ctx)
422     {
423       TRACE_SYSERR (errno);
424       return NULL;
425     }
426
427   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
428     {
429       TRACE_SYSERR (EIO);
430       return NULL;
431     }
432   ctx->file_hd = fd_table[fd].handle;
433   ctx->file_sock = fd_table[fd].socket;
434
435   ctx->refcount = 1;
436   ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
437   if (ctx->have_data_ev)
438     ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
439   if (ctx->have_space_ev)
440     ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
441   if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->stopped)
442     {
443       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
444       if (ctx->have_data_ev)
445         CloseHandle (ctx->have_data_ev);
446       if (ctx->have_space_ev)
447         CloseHandle (ctx->have_space_ev);
448       if (ctx->stopped)
449         CloseHandle (ctx->stopped);
450       free (ctx);
451       /* FIXME: Translate the error code.  */
452       TRACE_SYSERR (EIO);
453       return NULL;
454     }
455
456   ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
457   INIT_LOCK (ctx->mutex);
458
459   ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
460   if (!ctx->thread_hd)
461     {
462       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
463       DESTROY_LOCK (ctx->mutex);
464       if (ctx->have_data_ev)
465         CloseHandle (ctx->have_data_ev);
466       if (ctx->have_space_ev)
467         CloseHandle (ctx->have_space_ev);
468       if (ctx->stopped)
469         CloseHandle (ctx->stopped);
470       free (ctx);
471       TRACE_SYSERR (EIO);
472       return NULL;
473     }    
474   else
475     {
476       /* We set the priority of the thread higher because we know that
477          it only runs for a short time.  This greatly helps to
478          increase the performance of the I/O.  */
479       SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
480     }
481
482   TRACE_SUC ();
483   return ctx;
484 }
485
486
487 static void
488 destroy_reader (struct reader_context_s *ctx)
489 {
490   LOCK (ctx->mutex);
491   ctx->refcount--;
492   if (ctx->refcount != 0)
493     {
494       UNLOCK (ctx->mutex);
495       return;
496     }
497   ctx->stop_me = 1;
498   if (ctx->have_space_ev) 
499     SetEvent (ctx->have_space_ev);
500   UNLOCK (ctx->mutex);
501
502 #ifdef HAVE_W32CE_SYSTEM
503   /* Scenario: We never create a full pipe, but already started
504      reading.  Then we need to unblock the reader in the pipe driver
505      to make our reader thread notice that we want it to go away.  */
506
507   if (ctx->file_hd != INVALID_HANDLE_VALUE)
508     {
509       if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
510                         NULL, 0, NULL, 0, NULL, NULL))
511         {
512           TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
513                   "unblock control call failed for thread %p", ctx->thread_hd);
514         }
515     }
516 #endif
517
518   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
519           "waiting for termination of thread %p", ctx->thread_hd);
520   WaitForSingleObject (ctx->stopped, INFINITE);
521   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
522           "thread %p has terminated", ctx->thread_hd);
523     
524   if (ctx->stopped)
525     CloseHandle (ctx->stopped);
526   if (ctx->have_data_ev)
527     CloseHandle (ctx->have_data_ev);
528   if (ctx->have_space_ev)
529     CloseHandle (ctx->have_space_ev);
530   CloseHandle (ctx->thread_hd);
531   DESTROY_LOCK (ctx->mutex);
532   free (ctx);
533 }
534
535
536 /* Find a reader context or create a new one.  Note that the reader
537    context will last until a _gpgme_io_close.  */
538 static struct reader_context_s *
539 find_reader (int fd, int start_it)
540 {
541   struct reader_context_s *rd = NULL;
542   int i;
543
544   LOCK (reader_table_lock);
545   for (i = 0; i < reader_table_size; i++)
546     if (reader_table[i].used && reader_table[i].fd == fd)
547       rd = reader_table[i].context;
548
549   if (rd || !start_it)
550     {
551       UNLOCK (reader_table_lock);
552       return rd;
553     }
554
555   for (i = 0; i < reader_table_size; i++)
556     if (!reader_table[i].used)
557       break;
558
559   if (i != reader_table_size)
560     {
561       rd = create_reader (fd);
562       if (rd)
563         {
564           reader_table[i].fd = fd;
565           reader_table[i].context = rd;
566           reader_table[i].used = 1;
567         }
568     }
569
570   UNLOCK (reader_table_lock);
571   return rd;
572 }
573
574
575 static void
576 kill_reader (int fd)
577 {
578   int i;
579
580   LOCK (reader_table_lock);
581   for (i = 0; i < reader_table_size; i++)
582     {
583       if (reader_table[i].used && reader_table[i].fd == fd)
584         {
585           destroy_reader (reader_table[i].context);
586           reader_table[i].context = NULL;
587           reader_table[i].used = 0;
588           break;
589         }
590     }
591   UNLOCK (reader_table_lock);
592 }
593
594
595 int
596 _gpgme_io_read (int fd, void *buffer, size_t count)
597 {
598   int nread;
599   struct reader_context_s *ctx;
600   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
601               "buffer=%p, count=%u", buffer, count);
602   
603   ctx = find_reader (fd, 1);
604   if (!ctx)
605     {
606       gpg_err_set_errno (EBADF);
607       return TRACE_SYSRES (-1);
608     }
609   if (ctx->eof_shortcut)
610     return TRACE_SYSRES (0);
611
612   LOCK (ctx->mutex);
613   if (ctx->readpos == ctx->writepos && !ctx->error)
614     {
615       /* No data available.  */
616       UNLOCK (ctx->mutex);
617       TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd);
618       WaitForSingleObject (ctx->have_data_ev, INFINITE);
619       TRACE_LOG1 ("data from thread %p available", ctx->thread_hd);
620       LOCK (ctx->mutex);
621     }
622   
623   if (ctx->readpos == ctx->writepos || ctx->error)
624     {
625       UNLOCK (ctx->mutex);
626       ctx->eof_shortcut = 1;
627       if (ctx->eof)
628         return TRACE_SYSRES (0);
629       if (!ctx->error)
630         {
631           TRACE_LOG ("EOF but ctx->eof flag not set");
632           return 0;
633         }
634       gpg_err_set_errno (ctx->error_code);
635       return TRACE_SYSRES (-1);
636     }
637   
638   nread = ctx->readpos < ctx->writepos
639     ? ctx->writepos - ctx->readpos
640     : READBUF_SIZE - ctx->readpos;
641   if (nread > count)
642     nread = count;
643   memcpy (buffer, ctx->buffer + ctx->readpos, nread);
644   ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
645   if (ctx->readpos == ctx->writepos && !ctx->eof)
646     {
647       if (!ResetEvent (ctx->have_data_ev))
648         {
649           TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
650           UNLOCK (ctx->mutex);
651           /* FIXME: Should translate the error code.  */
652           gpg_err_set_errno (EIO);
653           return TRACE_SYSRES (-1);
654         }
655     }
656   if (!SetEvent (ctx->have_space_ev))
657     {
658       TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d",
659                   ctx->have_space_ev, (int) GetLastError ());
660       UNLOCK (ctx->mutex);
661       /* FIXME: Should translate the error code.  */
662       gpg_err_set_errno (EIO);
663       return TRACE_SYSRES (-1);
664     }
665   UNLOCK (ctx->mutex);
666   
667   TRACE_LOGBUF (buffer, nread);
668   return TRACE_SYSRES (nread);
669 }
670
671
672 /* The writer does use a simple buffering strategy so that we are
673    informed about write errors as soon as possible (i. e. with the the
674    next call to the write function.  */
675 static DWORD CALLBACK 
676 writer (void *arg)
677 {
678   struct writer_context_s *ctx = arg;
679   DWORD nwritten;
680   int sock;
681   TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
682               "thread=%p", ctx->thread_hd);
683
684   if (ctx->file_hd != INVALID_HANDLE_VALUE)
685     sock = 0;
686   else
687     sock = 1;
688
689   for (;;)
690     {
691       LOCK (ctx->mutex);
692       if (ctx->stop_me)
693         {
694           UNLOCK (ctx->mutex);
695           break;
696         }
697       if (!ctx->nbytes)
698         { 
699           if (!SetEvent (ctx->is_empty))
700             TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
701           if (!ResetEvent (ctx->have_data))
702             TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
703           UNLOCK (ctx->mutex);
704           TRACE_LOG ("idle");
705           WaitForSingleObject (ctx->have_data, INFINITE);
706           TRACE_LOG ("got data to send");
707           LOCK (ctx->mutex);
708         }
709       if (ctx->stop_me)
710         {
711           UNLOCK (ctx->mutex);
712           break;
713         }
714       UNLOCK (ctx->mutex);
715       
716       TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
717
718       /* Note that CTX->nbytes is not zero at this point, because
719          _gpgme_io_write always writes at least 1 byte before waking
720          us up, unless CTX->stop_me is true, which we catch above.  */
721       if (sock)
722         {
723           /* We need to try send first because a socket handle can't
724              be used with WriteFile.  */
725           int n;
726
727           n = send (ctx->file_sock, ctx->buffer, ctx->nbytes, 0);
728           if (n < 0)
729             {
730               ctx->error_code = (int) WSAGetLastError ();
731               ctx->error = 1;
732               TRACE_LOG1 ("send error: ec=%d", ctx->error_code);
733               break;
734             }
735           nwritten = n;
736         }
737       else
738         {
739           if (!WriteFile (ctx->file_hd, ctx->buffer,
740                           ctx->nbytes, &nwritten, NULL))
741             {
742               if (GetLastError () == ERROR_BUSY)
743                 {
744                   /* Probably stop_me is set now.  */
745                   TRACE_LOG ("pipe busy (unblocked?)");
746                   continue;
747                 }
748
749               ctx->error_code = (int) GetLastError ();
750               ctx->error = 1;
751               TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
752               break;
753             }
754         }
755       TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
756       
757       LOCK (ctx->mutex);
758       ctx->nbytes -= nwritten;
759       UNLOCK (ctx->mutex);
760     }
761   /* Indicate that we have an error.  */
762   if (!SetEvent (ctx->is_empty))
763     TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
764   SetEvent (ctx->stopped);
765
766   return TRACE_SUC ();
767 }
768
769
770 static struct writer_context_s *
771 create_writer (int fd)
772 {
773   struct writer_context_s *ctx;
774   SECURITY_ATTRIBUTES sec_attr;
775   DWORD tid;
776
777   TRACE_BEG (DEBUG_SYSIO, "gpgme:create_writer", fd);
778
779   memset (&sec_attr, 0, sizeof sec_attr);
780   sec_attr.nLength = sizeof sec_attr;
781   sec_attr.bInheritHandle = FALSE;
782
783   ctx = calloc (1, sizeof *ctx);
784   if (!ctx)
785     {
786       TRACE_SYSERR (errno);
787       return NULL;
788     }
789   
790   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
791     {
792       TRACE_SYSERR (EIO);
793       return NULL;
794     }
795   ctx->file_hd = fd_table[fd].handle;
796   ctx->file_sock = fd_table[fd].socket;
797
798   ctx->refcount = 1;
799   ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
800   if (ctx->have_data)
801     ctx->is_empty  = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
802   if (ctx->is_empty)
803     ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
804   if (!ctx->have_data || !ctx->is_empty || !ctx->stopped)
805     {
806       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
807       if (ctx->have_data)
808         CloseHandle (ctx->have_data);
809       if (ctx->is_empty)
810         CloseHandle (ctx->is_empty);
811       if (ctx->stopped)
812         CloseHandle (ctx->stopped);
813       free (ctx);
814       /* FIXME: Translate the error code.  */
815       TRACE_SYSERR (EIO);
816       return NULL;
817     }
818
819   ctx->is_empty = set_synchronize (ctx->is_empty);
820   INIT_LOCK (ctx->mutex);
821
822   ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
823   if (!ctx->thread_hd)
824     {
825       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
826       DESTROY_LOCK (ctx->mutex);
827       if (ctx->have_data)
828         CloseHandle (ctx->have_data);
829       if (ctx->is_empty)
830         CloseHandle (ctx->is_empty);
831       if (ctx->stopped)
832         CloseHandle (ctx->stopped);
833       free (ctx);
834       TRACE_SYSERR (EIO);
835       return NULL;
836     }    
837   else
838     {
839       /* We set the priority of the thread higher because we know
840          that it only runs for a short time.  This greatly helps to
841          increase the performance of the I/O.  */
842       SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
843     }
844
845   TRACE_SUC ();
846   return ctx;
847 }
848
849 static void
850 destroy_writer (struct writer_context_s *ctx)
851 {
852   LOCK (ctx->mutex);
853   ctx->refcount--;
854   if (ctx->refcount != 0)
855     {
856       UNLOCK (ctx->mutex);
857       return;
858     }
859   ctx->stop_me = 1;
860   if (ctx->have_data) 
861     SetEvent (ctx->have_data);
862   UNLOCK (ctx->mutex);
863
864 #ifdef HAVE_W32CE_SYSTEM
865   /* Scenario: We never create a full pipe, but already started
866      reading.  Then we need to unblock the reader in the pipe driver
867      to make our reader thread notice that we want it to go away.  */
868
869   if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
870                         NULL, 0, NULL, 0, NULL, NULL))
871     {
872       TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
873               "unblock control call failed for thread %p", ctx->thread_hd);
874     }
875 #endif
876   
877   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
878           "waiting for termination of thread %p", ctx->thread_hd);
879   WaitForSingleObject (ctx->stopped, INFINITE);
880   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
881           "thread %p has terminated", ctx->thread_hd);
882   
883   if (ctx->stopped)
884     CloseHandle (ctx->stopped);
885   if (ctx->have_data)
886     CloseHandle (ctx->have_data);
887   if (ctx->is_empty)
888     CloseHandle (ctx->is_empty);
889   CloseHandle (ctx->thread_hd);
890   DESTROY_LOCK (ctx->mutex);
891   free (ctx);
892 }
893
894
895 /* Find a writer context or create a new one.  Note that the writer
896    context will last until a _gpgme_io_close.  */
897 static struct writer_context_s *
898 find_writer (int fd, int start_it)
899 {
900   struct writer_context_s *wt = NULL;
901   int i;
902
903   LOCK (writer_table_lock);
904   for (i = 0; i < writer_table_size; i++)
905     if (writer_table[i].used && writer_table[i].fd == fd)
906       wt = writer_table[i].context;
907
908   if (wt || !start_it)
909     {
910       UNLOCK (writer_table_lock);
911       return wt;
912     }
913
914   for (i = 0; i < writer_table_size; i++)
915     if (!writer_table[i].used)
916       break;
917
918   if (i != writer_table_size)
919     {
920       wt = create_writer (fd);
921       if (wt)
922         {
923           writer_table[i].fd = fd;
924           writer_table[i].context = wt; 
925           writer_table[i].used = 1;
926         }
927     }
928
929   UNLOCK (writer_table_lock);
930   return wt;
931 }
932
933
934 static void
935 kill_writer (int fd)
936 {
937   int i;
938
939   LOCK (writer_table_lock);
940   for (i = 0; i < writer_table_size; i++)
941     {
942       if (writer_table[i].used && writer_table[i].fd == fd)
943         {
944           destroy_writer (writer_table[i].context);
945           writer_table[i].context = NULL;
946           writer_table[i].used = 0;
947           break;
948         }
949     }
950   UNLOCK (writer_table_lock);
951 }
952
953
954 int
955 _gpgme_io_write (int fd, const void *buffer, size_t count)
956 {
957   struct writer_context_s *ctx;
958   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
959               "buffer=%p, count=%u", buffer, count);
960   TRACE_LOGBUF (buffer, count);
961
962   if (count == 0)
963     return TRACE_SYSRES (0);
964
965   ctx = find_writer (fd, 0);
966   if (!ctx)
967     return TRACE_SYSRES (-1);
968
969   LOCK (ctx->mutex);
970   if (!ctx->error && ctx->nbytes)
971     {
972       /* Bytes are pending for send.  */
973
974       /* Reset the is_empty event.  Better safe than sorry.  */
975       if (!ResetEvent (ctx->is_empty))
976         {
977           TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
978           UNLOCK (ctx->mutex);
979           /* FIXME: Should translate the error code.  */
980           gpg_err_set_errno (EIO);
981           return TRACE_SYSRES (-1);
982         }
983       UNLOCK (ctx->mutex);
984       TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd);
985       WaitForSingleObject (ctx->is_empty, INFINITE);
986       TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd);
987       LOCK (ctx->mutex);
988     }
989
990   if (ctx->error)
991     {
992       UNLOCK (ctx->mutex);
993       if (ctx->error_code == ERROR_NO_DATA)
994         gpg_err_set_errno (EPIPE);
995       else
996         gpg_err_set_errno (EIO);
997       return TRACE_SYSRES (-1);
998     }
999
1000   /* If no error occured, the number of bytes in the buffer must be
1001      zero.  */
1002   assert (!ctx->nbytes);
1003
1004   if (count > WRITEBUF_SIZE)
1005     count = WRITEBUF_SIZE;
1006   memcpy (ctx->buffer, buffer, count);
1007   ctx->nbytes = count;
1008
1009   /* We have to reset the is_empty event early, because it is also
1010      used by the select() implementation to probe the channel.  */
1011   if (!ResetEvent (ctx->is_empty))
1012     {
1013       TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
1014       UNLOCK (ctx->mutex);
1015       /* FIXME: Should translate the error code.  */
1016       gpg_err_set_errno (EIO);
1017       return TRACE_SYSRES (-1);
1018     }
1019   if (!SetEvent (ctx->have_data))
1020     {
1021       TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
1022       UNLOCK (ctx->mutex);
1023       /* FIXME: Should translate the error code.  */
1024       gpg_err_set_errno (EIO);
1025       return TRACE_SYSRES (-1);
1026     }
1027   UNLOCK (ctx->mutex);
1028
1029   return TRACE_SYSRES ((int) count);
1030 }
1031
1032
1033 int
1034 _gpgme_io_pipe (int filedes[2], int inherit_idx)
1035 {
1036   int rfd;
1037   int wfd;
1038 #ifdef HAVE_W32CE_SYSTEM
1039   HANDLE hd;
1040   int rvid;
1041 #else
1042   HANDLE rh;
1043   HANDLE wh;
1044   SECURITY_ATTRIBUTES sec_attr;
1045 #endif
1046
1047   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
1048               "inherit_idx=%i (GPGME uses it for %s)",
1049               inherit_idx, inherit_idx ? "reading" : "writing");
1050
1051   rfd = new_fd ();
1052   if (rfd == -1)
1053     return TRACE_SYSRES (-1);
1054   wfd = new_fd ();
1055   if (wfd == -1)
1056     {
1057       release_fd (rfd);
1058       return TRACE_SYSRES (-1);
1059     }
1060
1061 #ifdef HAVE_W32CE_SYSTEM
1062   hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
1063   if (hd == INVALID_HANDLE_VALUE)
1064     {
1065       TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d",
1066                   (int) GetLastError ());
1067       release_fd (rfd);
1068       release_fd (wfd);
1069       /* FIXME: Should translate the error code.  */
1070       gpg_err_set_errno (EIO);
1071       return TRACE_SYSRES (-1);
1072     }
1073
1074   if (inherit_idx == 0)
1075     {
1076       fd_table[rfd].rvid = rvid;
1077       fd_table[wfd].handle = hd;
1078     }
1079   else
1080     {
1081       fd_table[rfd].handle = hd;
1082       fd_table[wfd].rvid = rvid;
1083     }  
1084
1085 #else
1086
1087   memset (&sec_attr, 0, sizeof (sec_attr));
1088   sec_attr.nLength = sizeof (sec_attr);
1089   sec_attr.bInheritHandle = FALSE;
1090   
1091   if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
1092     {
1093       TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
1094       release_fd (rfd);
1095       release_fd (wfd);
1096       /* FIXME: Should translate the error code.  */
1097       gpg_err_set_errno (EIO);
1098       return TRACE_SYSRES (-1);
1099     }
1100
1101   /* Make one end inheritable.  */
1102   if (inherit_idx == 0)
1103     {
1104       HANDLE hd;
1105       if (!DuplicateHandle (GetCurrentProcess(), rh,
1106                             GetCurrentProcess(), &hd, 0,
1107                             TRUE, DUPLICATE_SAME_ACCESS))
1108         {
1109           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
1110                       (int) GetLastError ());
1111           release_fd (rfd);
1112           release_fd (wfd);
1113           CloseHandle (rh);
1114           CloseHandle (wh);
1115           /* FIXME: Should translate the error code.  */
1116           gpg_err_set_errno (EIO);
1117           return TRACE_SYSRES (-1);
1118         }
1119       CloseHandle (rh);
1120       rh = hd;
1121     }
1122   else if (inherit_idx == 1)
1123     {
1124       HANDLE hd;
1125       if (!DuplicateHandle( GetCurrentProcess(), wh,
1126                             GetCurrentProcess(), &hd, 0,
1127                             TRUE, DUPLICATE_SAME_ACCESS))
1128         {
1129           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
1130                       (int) GetLastError ());
1131           release_fd (rfd);
1132           release_fd (wfd);
1133           CloseHandle (rh);
1134           CloseHandle (wh);
1135           /* FIXME: Should translate the error code.  */
1136           gpg_err_set_errno (EIO);
1137           return TRACE_SYSRES (-1);
1138         }
1139       CloseHandle (wh);
1140       wh = hd;
1141     }
1142   fd_table[rfd].handle = rh;
1143   fd_table[wfd].handle = wh;
1144 #endif
1145
1146   if (inherit_idx == 0)
1147     {
1148       struct writer_context_s *ctx;
1149       ctx = find_writer (wfd, 0);
1150       assert (ctx == NULL);
1151       ctx = find_writer (wfd, 1);
1152       if (!ctx)
1153         {
1154           /* No way/need to close RVIDs on Windows CE.  */
1155           if (fd_table[rfd].handle)
1156             CloseHandle (fd_table[rfd].handle);
1157           if (fd_table[wfd].handle)
1158             CloseHandle (fd_table[wfd].handle);
1159           release_fd (rfd);
1160           release_fd (wfd);
1161           /* FIXME: Should translate the error code.  */
1162           gpg_err_set_errno (EIO);
1163           return TRACE_SYSRES (-1);
1164         }
1165     }
1166   else if (inherit_idx == 1)
1167     {
1168       struct reader_context_s *ctx;
1169       ctx = find_reader (rfd, 0);
1170       assert (ctx == NULL);
1171       ctx = find_reader (rfd, 1);
1172       if (!ctx)
1173         {
1174           if (fd_table[rfd].handle)
1175             CloseHandle (fd_table[rfd].handle);
1176           /* No way/need to close RVIDs on Windows CE.  */
1177           if (fd_table[wfd].handle)
1178             CloseHandle (fd_table[wfd].handle);
1179           release_fd (rfd);
1180           release_fd (wfd);
1181           /* FIXME: Should translate the error code.  */
1182           gpg_err_set_errno (EIO);
1183           return TRACE_SYSRES (-1);
1184         }
1185     }
1186   
1187   filedes[0] = rfd;
1188   filedes[1] = wfd;
1189   return TRACE_SUC6 ("read=0x%x (%p/0x%x), write=0x%x (%p/0x%x)",
1190                      rfd, fd_table[rfd].handle, fd_table[rfd].rvid,
1191                      wfd, fd_table[wfd].handle, fd_table[wfd].rvid);
1192 }
1193
1194
1195 int
1196 _gpgme_io_close (int fd)
1197 {
1198   int i;
1199   _gpgme_close_notify_handler_t handler = NULL;
1200   void *value = NULL;
1201
1202   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
1203
1204   if (fd == -1)
1205     {
1206       gpg_err_set_errno (EBADF);
1207       return TRACE_SYSRES (-1);
1208     }
1209   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1210     {
1211       gpg_err_set_errno (EBADF);
1212       return TRACE_SYSRES (-1);
1213     }
1214
1215   kill_reader (fd);
1216   kill_writer (fd);
1217   LOCK (notify_table_lock);
1218   for (i = 0; i < DIM (notify_table); i++)
1219     {
1220       if (notify_table[i].inuse && notify_table[i].fd == fd)
1221         {
1222           handler = notify_table[i].handler;
1223           value   = notify_table[i].value;
1224           notify_table[i].handler = NULL;
1225           notify_table[i].value = NULL;
1226           notify_table[i].inuse = 0;
1227           break;
1228         }
1229     }
1230   UNLOCK (notify_table_lock);
1231   if (handler)
1232     handler (fd, value);
1233
1234   if (fd_table[fd].dup_from == -1)
1235     {
1236       if (fd_table[fd].handle != INVALID_HANDLE_VALUE)
1237         {
1238           if (!CloseHandle (fd_table[fd].handle))
1239             { 
1240               TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
1241               /* FIXME: Should translate the error code.  */
1242               gpg_err_set_errno (EIO);
1243               return TRACE_SYSRES (-1);
1244             }
1245         }
1246       else if (fd_table[fd].socket != INVALID_SOCKET)
1247         {
1248           if (closesocket (fd_table[fd].socket))
1249             { 
1250               TRACE_LOG1 ("closesocket failed: ec=%d", (int) WSAGetLastError ());
1251               /* FIXME: Should translate the error code.  */
1252               gpg_err_set_errno (EIO);
1253               return TRACE_SYSRES (-1);
1254             }
1255         }
1256       /* Nothing to do for RVIDs.  */
1257     }
1258
1259   release_fd (fd);
1260       
1261   return TRACE_SYSRES (0);
1262 }
1263
1264
1265 int
1266 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
1267                             void *value)
1268 {
1269   int i;
1270   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
1271               "close_handler=%p/%p", handler, value);
1272
1273   assert (fd != -1);
1274
1275   LOCK (notify_table_lock);
1276   for (i=0; i < DIM (notify_table); i++)
1277     if (notify_table[i].inuse && notify_table[i].fd == fd)
1278       break;
1279   if (i == DIM (notify_table))
1280     for (i = 0; i < DIM (notify_table); i++)
1281       if (!notify_table[i].inuse)
1282         break;
1283   if (i == DIM (notify_table))
1284     {
1285       UNLOCK (notify_table_lock);
1286       gpg_err_set_errno (EINVAL);
1287       return TRACE_SYSRES (-1);
1288     }
1289   notify_table[i].fd = fd;
1290   notify_table[i].handler = handler;
1291   notify_table[i].value = value;
1292   notify_table[i].inuse = 1;
1293   UNLOCK (notify_table_lock);
1294   return TRACE_SYSRES (0);
1295 }
1296
1297
1298 int
1299 _gpgme_io_set_nonblocking (int fd)
1300 {
1301   TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
1302   return 0;
1303 }
1304
1305
1306 #ifdef HAVE_W32CE_SYSTEM
1307 static char *
1308 build_commandline (char **argv, int fd0, int fd0_isnull,
1309                    int fd1, int fd1_isnull,
1310                    int fd2, int fd2_isnull)
1311 {
1312   int i, n;
1313   const char *s;
1314   char *buf, *p;
1315   char fdbuf[3*30];
1316
1317   p = fdbuf;
1318   *p = 0;
1319   
1320   if (fd0 != -1)
1321     {
1322       if (fd0_isnull)
1323         strcpy (p, "-&S0=null ");
1324       else
1325         snprintf (p, 25, "-&S0=%d ", fd_table[fd0].rvid);
1326       p += strlen (p);
1327     }
1328   if (fd1 != -1)
1329     {
1330       if (fd1_isnull)
1331         strcpy (p, "-&S1=null ");
1332       else
1333         snprintf (p, 25, "-&S1=%d ", fd_table[fd1].rvid);
1334       p += strlen (p);
1335     }
1336   if (fd2 != -1)
1337     {
1338       if (fd2_isnull)
1339         strcpy (p, "-&S2=null ");
1340       else
1341         snprintf (p, 25, "-&S2=%d ", fd_table[fd2].rvid);
1342       p += strlen (p);
1343     }
1344   strcpy (p, "-&S2=null ");
1345   p += strlen (p);
1346   
1347   n = strlen (fdbuf);
1348   for (i=0; (s = argv[i]); i++)
1349     {
1350       if (!i)
1351         continue; /* Ignore argv[0].  */
1352       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
1353       for (; *s; s++)
1354         if (*s == '\"')
1355           n++;  /* Need to double inner quotes.  */
1356     }
1357   n++;
1358   buf = p = malloc (n);
1359   if (! buf)
1360     return NULL;
1361
1362   p = stpcpy (p, fdbuf);
1363   for (i = 0; argv[i]; i++) 
1364     {
1365       if (!i)
1366         continue; /* Ignore argv[0].  */
1367       if (i > 1)
1368         p = stpcpy (p, " ");
1369
1370       if (! *argv[i]) /* Empty string. */
1371         p = stpcpy (p, "\"\"");
1372       else if (strpbrk (argv[i], " \t\n\v\f\""))
1373         {
1374           p = stpcpy (p, "\"");
1375           for (s = argv[i]; *s; s++)
1376             {
1377               *p++ = *s;
1378               if (*s == '\"')
1379                 *p++ = *s;
1380             }
1381           *p++ = '\"';
1382           *p = 0;
1383         }
1384       else
1385         p = stpcpy (p, argv[i]);
1386     }
1387
1388   return buf;  
1389 }
1390 #else
1391 static char *
1392 build_commandline (char **argv)
1393 {
1394   int i;
1395   int n = 0;
1396   char *buf;
1397   char *p;
1398   
1399   /* We have to quote some things because under Windows the program
1400      parses the commandline and does some unquoting.  We enclose the
1401      whole argument in double-quotes, and escape literal double-quotes
1402      as well as backslashes with a backslash.  We end up with a
1403      trailing space at the end of the line, but that is harmless.  */
1404   for (i = 0; argv[i]; i++)
1405     {
1406       p = argv[i];
1407       /* The leading double-quote.  */
1408       n++;
1409       while (*p)
1410         {
1411           /* An extra one for each literal that must be escaped.  */
1412           if (*p == '\\' || *p == '"')
1413             n++;
1414           n++;
1415           p++;
1416         }
1417       /* The trailing double-quote and the delimiter.  */
1418       n += 2;
1419     }
1420   /* And a trailing zero.  */
1421   n++;
1422
1423   buf = p = malloc (n);
1424   if (!buf)
1425     return NULL;
1426   for (i = 0; argv[i]; i++)
1427     {
1428       char *argvp = argv[i];
1429
1430       *(p++) = '"';
1431       while (*argvp)
1432         {
1433           if (*argvp == '\\' || *argvp == '"')
1434             *(p++) = '\\';
1435           *(p++) = *(argvp++);
1436         }
1437       *(p++) = '"';
1438       *(p++) = ' ';
1439     }
1440   *(p++) = 0;
1441
1442   return buf;
1443 }
1444 #endif
1445
1446
1447 int
1448 _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
1449                  struct spawn_fd_item_s *fd_list,
1450                  void (*atfork) (void *opaque, int reserved),
1451                  void *atforkvalue, pid_t *r_pid)
1452 {
1453   PROCESS_INFORMATION pi =
1454     {
1455       NULL,      /* returns process handle */
1456       0,         /* returns primary thread handle */
1457       0,         /* returns pid */
1458       0          /* returns tid */
1459     };
1460   int i;
1461
1462 #ifdef HAVE_W32CE_SYSTEM
1463   int fd_in = -1;
1464   int fd_out = -1;
1465   int fd_err = -1;
1466   int fd_in_isnull = 1;
1467   int fd_out_isnull = 1;
1468   int fd_err_isnull = 1;
1469   char *cmdline;
1470
1471   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1472               "path=%s", path);
1473   i = 0;
1474   while (argv[i])
1475     {
1476       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1477       i++;
1478     }
1479
1480   for (i = 0; fd_list[i].fd != -1; i++)
1481     {
1482       int fd = fd_list[i].fd;
1483
1484       TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd, fd_list[i].dup_to);
1485       if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1486         {
1487           TRACE_LOG1 ("invalid fd 0x%x", fd);
1488           gpg_err_set_errno (EBADF);
1489           return TRACE_SYSRES (-1);
1490         }
1491       if (fd_table[fd].rvid == 0)
1492         {
1493           TRACE_LOG1 ("fd 0x%x not inheritable (not an RVID)", fd);
1494           gpg_err_set_errno (EBADF);
1495           return TRACE_SYSRES (-1);
1496         }
1497       
1498       if (fd_list[i].dup_to == 0)
1499         {
1500           fd_in = fd_list[i].fd;
1501           fd_in_isnull = 0;
1502         }
1503       else if (fd_list[i].dup_to == 1)
1504         {
1505           fd_out = fd_list[i].fd;
1506           fd_out_isnull = 0;
1507         }
1508       else if (fd_list[i].dup_to == 2)
1509         {
1510           fd_err = fd_list[i].fd;
1511           fd_err_isnull = 0;
1512         }
1513     }
1514
1515   cmdline = build_commandline (argv, fd_in, fd_in_isnull,
1516                                fd_out, fd_out_isnull, fd_err, fd_err_isnull);
1517   if (!cmdline)
1518     {
1519       TRACE_LOG1 ("build_commandline failed: %s", strerror (errno));
1520       return TRACE_SYSRES (-1);
1521     }
1522
1523   fprintf (stderr, "SPAWNY: %s\n", cmdline);
1524
1525   if (!CreateProcessA (path,                /* Program to start.  */
1526                        cmdline,             /* Command line arguments.  */
1527                        NULL,                 /* (not supported)  */
1528                        NULL,                 /* (not supported)  */
1529                        FALSE,                /* (not supported)  */
1530                        (CREATE_SUSPENDED),   /* Creation flags.  */
1531                        NULL,                 /* (not supported)  */
1532                        NULL,                 /* (not supported)  */
1533                        NULL,                 /* (not supported) */
1534                        &pi                   /* Returns process information.*/
1535                        ))
1536     {
1537       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1538       free (cmdline);
1539       gpg_err_set_errno (EIO);
1540       return TRACE_SYSRES (-1);
1541     }
1542
1543   /* Insert the inherited handles.  */
1544   for (i = 0; fd_list[i].fd != -1; i++)
1545     {
1546       /* Return the child name of this handle.  */
1547       fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
1548     }
1549
1550 #else
1551   SECURITY_ATTRIBUTES sec_attr;
1552   STARTUPINFOA si;
1553   int cr_flags = CREATE_DEFAULT_ERROR_MODE;
1554   char **args;
1555   char *arg_string;
1556   /* FIXME.  */
1557   int debug_me = 0;
1558   int tmp_fd;
1559   char *tmp_name;
1560
1561   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1562               "path=%s", path);
1563   i = 0;
1564   while (argv[i])
1565     {
1566       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1567       i++;
1568     }
1569
1570   /* We do not inherit any handles by default, and just insert those
1571      handles we want the child to have afterwards.  But some handle
1572      values occur on the command line, and we need to move
1573      stdin/out/err to the right location.  So we use a wrapper program
1574      which gets the information from a temporary file.  */
1575   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
1576     {
1577       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
1578       return TRACE_SYSRES (-1);
1579     }
1580   TRACE_LOG1 ("tmp_name = %s", tmp_name);
1581
1582   args = calloc (2 + i + 1, sizeof (*args));
1583   args[0] = (char *) _gpgme_get_w32spawn_path ();
1584   args[1] = tmp_name;
1585   args[2] = path;
1586   memcpy (&args[3], &argv[1], i * sizeof (*args));
1587
1588   memset (&sec_attr, 0, sizeof sec_attr);
1589   sec_attr.nLength = sizeof sec_attr;
1590   sec_attr.bInheritHandle = FALSE;
1591  
1592   arg_string = build_commandline (args);
1593   free (args);
1594   if (!arg_string)
1595     {
1596       close (tmp_fd);
1597       DeleteFileA (tmp_name);
1598       return TRACE_SYSRES (-1);
1599     }
1600
1601   memset (&si, 0, sizeof si);
1602   si.cb = sizeof (si);
1603   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1604   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
1605   si.hStdInput = INVALID_HANDLE_VALUE;
1606   si.hStdOutput = INVALID_HANDLE_VALUE;
1607   si.hStdError = INVALID_HANDLE_VALUE;
1608
1609   cr_flags |= CREATE_SUSPENDED; 
1610 #ifndef HAVE_W32CE_SYSTEM
1611   cr_flags |= DETACHED_PROCESS;
1612   cr_flags |= GetPriorityClass (GetCurrentProcess ());
1613 #endif
1614   if (!CreateProcessA (_gpgme_get_w32spawn_path (),
1615                        arg_string,
1616                        &sec_attr,     /* process security attributes */
1617                        &sec_attr,     /* thread security attributes */
1618                        FALSE,         /* inherit handles */
1619                        cr_flags,      /* creation flags */
1620                        NULL,          /* environment */
1621                        NULL,          /* use current drive/directory */
1622                        &si,           /* startup information */
1623                        &pi))          /* returns process information */
1624     {
1625       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1626       free (arg_string);
1627       close (tmp_fd);
1628       DeleteFileA (tmp_name);
1629
1630       /* FIXME: Should translate the error code.  */
1631       gpg_err_set_errno (EIO);
1632       return TRACE_SYSRES (-1);
1633     }
1634
1635   free (arg_string);
1636
1637   if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
1638     _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
1639
1640   /* Insert the inherited handles.  */
1641   for (i = 0; fd_list[i].fd != -1; i++)
1642     {
1643       HANDLE hd;
1644
1645       /* Make it inheritable for the wrapper process.  */
1646       if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd),
1647                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
1648         {
1649           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
1650           TerminateProcess (pi.hProcess, 0);
1651           /* Just in case TerminateProcess didn't work, let the
1652              process fail on its own.  */
1653           ResumeThread (pi.hThread);
1654           CloseHandle (pi.hThread);
1655           CloseHandle (pi.hProcess);
1656
1657           close (tmp_fd);
1658           DeleteFileA (tmp_name);
1659
1660           /* FIXME: Should translate the error code.  */
1661           gpg_err_set_errno (EIO);
1662           return TRACE_SYSRES (-1);
1663         }
1664       /* Return the child name of this handle.  */
1665       fd_list[i].peer_name = handle_to_fd (hd);
1666     }
1667   
1668   /* Write the handle translation information to the temporary
1669      file.  */
1670   {
1671     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
1672        notation: "0xFEDCBA9876543210" with an extra white space after
1673        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
1674        for a time when a HANDLE is 64 bit.  */
1675 #define BUFFER_MAX 810
1676     char line[BUFFER_MAX + 1];
1677     int res;
1678     int written;
1679     size_t len;
1680
1681     if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
1682       strcpy (line, "~1 \n");
1683     else
1684       strcpy (line, "\n");
1685     for (i = 0; fd_list[i].fd != -1; i++)
1686       {
1687         /* Strip the newline.  */
1688         len = strlen (line) - 1;
1689         
1690         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
1691         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
1692                   fd_list[i].fd, fd_list[i].dup_to,
1693                   fd_list[i].peer_name, fd_list[i].arg_loc);
1694         /* Rather safe than sorry.  */
1695         line[BUFFER_MAX - 1] = '\n';
1696         line[BUFFER_MAX] = '\0';
1697       }
1698     len = strlen (line);
1699     written = 0;
1700     do
1701       {
1702         res = write (tmp_fd, &line[written], len - written);
1703         if (res > 0)
1704           written += res;
1705       }
1706     while (res > 0 || (res < 0 && errno == EAGAIN));
1707   }
1708   close (tmp_fd);
1709   /* The temporary file is deleted by the gpgme-w32spawn process
1710      (hopefully).  */
1711 #endif
1712
1713
1714   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
1715               "dwProcessID=%d, dwThreadId=%d",
1716               pi.hProcess, pi.hThread, 
1717               (int) pi.dwProcessId, (int) pi.dwThreadId);
1718   
1719   if (r_pid)
1720     *r_pid = (pid_t)pi.dwProcessId;
1721
1722   
1723   if (ResumeThread (pi.hThread) < 0)
1724     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
1725   
1726   if (!CloseHandle (pi.hThread))
1727     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
1728                 (int) GetLastError ());
1729
1730   TRACE_LOG1 ("process=%p", pi.hProcess);
1731
1732   /* We don't need to wait for the process.  */
1733   if (!CloseHandle (pi.hProcess))
1734     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
1735                 (int) GetLastError ());
1736
1737   if (! (flags & IOSPAWN_FLAG_NOCLOSE))
1738     {
1739       for (i = 0; fd_list[i].fd != -1; i++)
1740         _gpgme_io_close (fd_list[i].fd);
1741     }
1742
1743   for (i = 0; fd_list[i].fd != -1; i++)
1744     if (fd_list[i].dup_to == -1)
1745       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
1746                   fd_list[i].peer_name);
1747     else
1748       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
1749                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
1750                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
1751
1752   return TRACE_SYSRES (0);
1753 }
1754
1755
1756 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
1757    nothing to select, > 0 = number of signaled fds.  */
1758 int
1759 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
1760 {
1761   HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
1762   int waitidx[MAXIMUM_WAIT_OBJECTS];
1763   int code;
1764   int nwait;
1765   int i;
1766   int any;
1767   int count;
1768   void *dbg_help;
1769   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
1770               "nfds=%u, nonblock=%u", nfds, nonblock);
1771
1772  restart:
1773   TRACE_SEQ (dbg_help, "select on [ ");
1774   any = 0;
1775   nwait = 0;
1776   count = 0;
1777   for (i=0; i < nfds; i++)
1778     {
1779       if (fds[i].fd == -1)
1780         continue;
1781       fds[i].signaled = 0;
1782       if (fds[i].for_read || fds[i].for_write)
1783         {
1784           if (fds[i].for_read)
1785             {
1786               struct reader_context_s *ctx = find_reader (fds[i].fd,0);
1787               
1788               if (!ctx)
1789                 TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
1790                             fds[i].fd);
1791               else
1792                 {
1793                   if (nwait >= DIM (waitbuf))
1794                     {
1795                       TRACE_END (dbg_help, "oops ]");
1796                       TRACE_LOG ("Too many objects for WFMO!");
1797                       /* FIXME: Should translate the error code.  */
1798                       gpg_err_set_errno (EIO);
1799                       return TRACE_SYSRES (-1);
1800                     }
1801                   waitidx[nwait] = i;
1802                   waitbuf[nwait++] = ctx->have_data_ev;
1803                 }
1804               TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
1805               any = 1;
1806             }
1807           else if (fds[i].for_write)
1808             {
1809               struct writer_context_s *ctx = find_writer (fds[i].fd,0);
1810               
1811               if (!ctx)
1812                 TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
1813                             fds[i].fd);
1814               else
1815                 {
1816                   if (nwait >= DIM (waitbuf))
1817                     {
1818                       TRACE_END (dbg_help, "oops ]");
1819                       TRACE_LOG ("Too many objects for WFMO!");
1820                       /* FIXME: Should translate the error code.  */
1821                       gpg_err_set_errno (EIO);
1822                       return TRACE_SYSRES (-1);
1823                     }
1824                   waitidx[nwait] = i;
1825                   waitbuf[nwait++] = ctx->is_empty;
1826                 }
1827               TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
1828               any = 1;
1829             }
1830         }
1831     }
1832   TRACE_END (dbg_help, "]");
1833   if (!any) 
1834     return TRACE_SYSRES (0);
1835
1836   code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
1837   if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
1838     {
1839       /* This WFMO is a really silly function: It does return either
1840          the index of the signaled object or if 2 objects have been
1841          signalled at the same time, the index of the object with the
1842          lowest object is returned - so and how do we find out how
1843          many objects have been signaled???.  The only solution I can
1844          imagine is to test each object starting with the returned
1845          index individually - how dull.  */
1846       any = 0;
1847       for (i = code - WAIT_OBJECT_0; i < nwait; i++)
1848         {
1849           if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
1850             {
1851               assert (waitidx[i] >=0 && waitidx[i] < nfds);
1852               fds[waitidx[i]].signaled = 1;
1853               any = 1;
1854               count++;
1855             }
1856         }
1857       if (!any)
1858         {
1859           TRACE_LOG ("no signaled objects found after WFMO");
1860           count = -1;
1861         }
1862     }
1863   else if (code == WAIT_TIMEOUT)
1864     TRACE_LOG ("WFMO timed out");
1865   else if (code == WAIT_FAILED)
1866     {
1867       int le = (int) GetLastError ();
1868 #if 0
1869       if (le == ERROR_INVALID_HANDLE)
1870         {
1871           int k;
1872           int j = handle_to_fd (waitbuf[i]);
1873           
1874           TRACE_LOG1 ("WFMO invalid handle %d removed", j);
1875           for (k = 0 ; k < nfds; k++)
1876             {
1877               if (fds[k].fd == j)
1878                 {
1879                   fds[k].for_read = fds[k].for_write = 0;
1880                   goto restart;
1881                 }
1882             }
1883           TRACE_LOG (" oops, or not???");
1884         }
1885 #endif
1886       TRACE_LOG1 ("WFMO failed: %d", le);
1887       count = -1;
1888     }
1889   else
1890     {
1891       TRACE_LOG1 ("WFMO returned %d", code);
1892       count = -1;
1893     }
1894   
1895   if (count > 0)
1896     {
1897       TRACE_SEQ (dbg_help, "select OK [ ");
1898       for (i = 0; i < nfds; i++)
1899         {
1900           if (fds[i].fd == -1)
1901             continue;
1902           if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
1903             TRACE_ADD2 (dbg_help, "%c0x%x ",
1904                         fds[i].for_read ? 'r' : 'w', fds[i].fd);
1905         }
1906       TRACE_END (dbg_help, "]");
1907     }
1908
1909   if (count < 0)
1910     {
1911       /* FIXME: Should determine a proper error code.  */
1912       gpg_err_set_errno (EIO);
1913     }
1914   
1915   return TRACE_SYSRES (count);
1916 }
1917
1918
1919 void
1920 _gpgme_io_subsystem_init (void)
1921 {
1922   /* Nothing to do.  */
1923 }
1924
1925
1926 /* Write the printable version of FD to the buffer BUF of length
1927    BUFLEN.  The printable version is the representation on the command
1928    line that the child process expects.  */
1929 int
1930 _gpgme_io_fd2str (char *buf, int buflen, int fd)
1931 {
1932 #ifdef HAVE_W32CE_SYSTEM
1933   /* FIXME: For now. See above.  */
1934   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
1935       || fd_table[fd].rvid == 0)
1936     fd = -1;
1937   else
1938     fd = fd_table[fd].rvid;
1939 #endif
1940
1941   return snprintf (buf, buflen, "%d", fd);
1942 }
1943
1944
1945 int
1946 _gpgme_io_dup (int fd)
1947 {
1948   int newfd;
1949   struct reader_context_s *rd_ctx;
1950   struct writer_context_s *wt_ctx;
1951   int i;
1952
1953   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
1954
1955   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1956     {
1957       gpg_err_set_errno (EINVAL);
1958       return TRACE_SYSRES (-1);
1959     }
1960
1961   newfd = new_fd();
1962   if (newfd == -1)
1963     return TRACE_SYSRES (-1);
1964   
1965   fd_table[newfd].handle = fd_table[fd].handle;
1966   fd_table[newfd].socket = fd_table[fd].socket;
1967   fd_table[newfd].rvid = fd_table[fd].rvid;
1968   fd_table[newfd].dup_from = fd;
1969
1970   rd_ctx = find_reader (fd, 0);
1971   if (rd_ctx)
1972     {
1973       /* No need for locking, as the only races are against the reader
1974          thread itself, which doesn't touch refcount.  */
1975       rd_ctx->refcount++;
1976
1977       LOCK (reader_table_lock);
1978       for (i = 0; i < reader_table_size; i++)
1979         if (!reader_table[i].used)
1980           break;
1981       /* FIXME.  */
1982       assert (i != reader_table_size);
1983       reader_table[i].fd = newfd;
1984       reader_table[i].context = rd_ctx;
1985       reader_table[i].used = 1;
1986       UNLOCK (reader_table_lock);
1987     }
1988
1989   wt_ctx = find_writer (fd, 0);
1990   if (wt_ctx)
1991     {
1992       /* No need for locking, as the only races are against the writer
1993          thread itself, which doesn't touch refcount.  */
1994       wt_ctx->refcount++;
1995
1996       LOCK (writer_table_lock);
1997       for (i = 0; i < writer_table_size; i++)
1998         if (!writer_table[i].used)
1999           break;
2000       /* FIXME.  */
2001       assert (i != writer_table_size);
2002       writer_table[i].fd = newfd;
2003       writer_table[i].context = wt_ctx;
2004       writer_table[i].used = 1;
2005       UNLOCK (writer_table_lock);
2006     }
2007
2008   return TRACE_SYSRES (newfd);
2009 }
2010
2011 \f
2012 /* The following interface is only useful for GPGME Glib and Qt.  */
2013
2014 /* Compatibility interface, obsolete.  */
2015 void *
2016 gpgme_get_giochannel (int fd)
2017 {
2018   return NULL;
2019 }
2020
2021
2022 /* Look up the giochannel or qiodevice for file descriptor FD.  */
2023 void *
2024 gpgme_get_fdptr (int fd)
2025 {
2026   return NULL;
2027 }
2028
2029 \f
2030 static int
2031 wsa2errno (int err)
2032 {
2033   switch (err)
2034     {
2035     case WSAENOTSOCK:
2036       return EINVAL;
2037     case WSAEWOULDBLOCK:
2038       return EAGAIN;
2039     case ERROR_BROKEN_PIPE:
2040       return EPIPE;
2041     case WSANOTINITIALISED:
2042       return ENOSYS;
2043     default:
2044       return EIO;
2045     }
2046 }
2047
2048
2049 int
2050 _gpgme_io_socket (int domain, int type, int proto)
2051 {
2052   int res;
2053   int fd;
2054
2055   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
2056               "type=%i, protp=%i", type, proto);
2057
2058   fd = new_fd();
2059   if (fd == -1)
2060     return TRACE_SYSRES (-1);
2061       
2062   res = socket (domain, type, proto);
2063   if (res == INVALID_SOCKET)
2064     {
2065       release_fd (fd);
2066       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2067       return TRACE_SYSRES (-1);
2068     }
2069   fd_table[fd].socket = res;
2070
2071   TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
2072   
2073   return res;
2074 }
2075
2076
2077 int
2078 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
2079 {
2080   int res;
2081
2082   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
2083               "addr=%p, addrlen=%i", addr, addrlen);
2084
2085   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
2086     {
2087       gpg_err_set_errno (EBADF);
2088       return TRACE_SYSRES (-1);
2089     }
2090     
2091   res = connect (fd_table[fd].socket, addr, addrlen);
2092   if (res)
2093     {
2094       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2095       return TRACE_SYSRES (-1);
2096     }
2097
2098   return TRACE_SUC ();
2099 }