2010-06-11 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   if (!CreateProcessA (path,                /* Program to start.  */
1524                        cmdline,             /* Command line arguments.  */
1525                        NULL,                 /* (not supported)  */
1526                        NULL,                 /* (not supported)  */
1527                        FALSE,                /* (not supported)  */
1528                        (CREATE_SUSPENDED),   /* Creation flags.  */
1529                        NULL,                 /* (not supported)  */
1530                        NULL,                 /* (not supported)  */
1531                        NULL,                 /* (not supported) */
1532                        &pi                   /* Returns process information.*/
1533                        ))
1534     {
1535       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1536       free (cmdline);
1537       gpg_err_set_errno (EIO);
1538       return TRACE_SYSRES (-1);
1539     }
1540
1541   /* Insert the inherited handles.  */
1542   for (i = 0; fd_list[i].fd != -1; i++)
1543     {
1544       /* Return the child name of this handle.  */
1545       fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
1546     }
1547
1548 #else
1549   SECURITY_ATTRIBUTES sec_attr;
1550   STARTUPINFOA si;
1551   int cr_flags = CREATE_DEFAULT_ERROR_MODE;
1552   char **args;
1553   char *arg_string;
1554   /* FIXME.  */
1555   int debug_me = 0;
1556   int tmp_fd;
1557   char *tmp_name;
1558
1559   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1560               "path=%s", path);
1561   i = 0;
1562   while (argv[i])
1563     {
1564       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1565       i++;
1566     }
1567
1568   /* We do not inherit any handles by default, and just insert those
1569      handles we want the child to have afterwards.  But some handle
1570      values occur on the command line, and we need to move
1571      stdin/out/err to the right location.  So we use a wrapper program
1572      which gets the information from a temporary file.  */
1573   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
1574     {
1575       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
1576       return TRACE_SYSRES (-1);
1577     }
1578   TRACE_LOG1 ("tmp_name = %s", tmp_name);
1579
1580   args = calloc (2 + i + 1, sizeof (*args));
1581   args[0] = (char *) _gpgme_get_w32spawn_path ();
1582   args[1] = tmp_name;
1583   args[2] = path;
1584   memcpy (&args[3], &argv[1], i * sizeof (*args));
1585
1586   memset (&sec_attr, 0, sizeof sec_attr);
1587   sec_attr.nLength = sizeof sec_attr;
1588   sec_attr.bInheritHandle = FALSE;
1589  
1590   arg_string = build_commandline (args);
1591   free (args);
1592   if (!arg_string)
1593     {
1594       close (tmp_fd);
1595       DeleteFileA (tmp_name);
1596       return TRACE_SYSRES (-1);
1597     }
1598
1599   memset (&si, 0, sizeof si);
1600   si.cb = sizeof (si);
1601   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1602   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
1603   si.hStdInput = INVALID_HANDLE_VALUE;
1604   si.hStdOutput = INVALID_HANDLE_VALUE;
1605   si.hStdError = INVALID_HANDLE_VALUE;
1606
1607   cr_flags |= CREATE_SUSPENDED; 
1608 #ifndef HAVE_W32CE_SYSTEM
1609   cr_flags |= DETACHED_PROCESS;
1610   cr_flags |= GetPriorityClass (GetCurrentProcess ());
1611 #endif
1612   if (!CreateProcessA (_gpgme_get_w32spawn_path (),
1613                        arg_string,
1614                        &sec_attr,     /* process security attributes */
1615                        &sec_attr,     /* thread security attributes */
1616                        FALSE,         /* inherit handles */
1617                        cr_flags,      /* creation flags */
1618                        NULL,          /* environment */
1619                        NULL,          /* use current drive/directory */
1620                        &si,           /* startup information */
1621                        &pi))          /* returns process information */
1622     {
1623       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1624       free (arg_string);
1625       close (tmp_fd);
1626       DeleteFileA (tmp_name);
1627
1628       /* FIXME: Should translate the error code.  */
1629       gpg_err_set_errno (EIO);
1630       return TRACE_SYSRES (-1);
1631     }
1632
1633   free (arg_string);
1634
1635   if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
1636     _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
1637
1638   /* Insert the inherited handles.  */
1639   for (i = 0; fd_list[i].fd != -1; i++)
1640     {
1641       HANDLE hd;
1642
1643       /* Make it inheritable for the wrapper process.  */
1644       if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd),
1645                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
1646         {
1647           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
1648           TerminateProcess (pi.hProcess, 0);
1649           /* Just in case TerminateProcess didn't work, let the
1650              process fail on its own.  */
1651           ResumeThread (pi.hThread);
1652           CloseHandle (pi.hThread);
1653           CloseHandle (pi.hProcess);
1654
1655           close (tmp_fd);
1656           DeleteFileA (tmp_name);
1657
1658           /* FIXME: Should translate the error code.  */
1659           gpg_err_set_errno (EIO);
1660           return TRACE_SYSRES (-1);
1661         }
1662       /* Return the child name of this handle.  */
1663       fd_list[i].peer_name = handle_to_fd (hd);
1664     }
1665   
1666   /* Write the handle translation information to the temporary
1667      file.  */
1668   {
1669     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
1670        notation: "0xFEDCBA9876543210" with an extra white space after
1671        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
1672        for a time when a HANDLE is 64 bit.  */
1673 #define BUFFER_MAX 810
1674     char line[BUFFER_MAX + 1];
1675     int res;
1676     int written;
1677     size_t len;
1678
1679     if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
1680       strcpy (line, "~1 \n");
1681     else
1682       strcpy (line, "\n");
1683     for (i = 0; fd_list[i].fd != -1; i++)
1684       {
1685         /* Strip the newline.  */
1686         len = strlen (line) - 1;
1687         
1688         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
1689         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
1690                   fd_list[i].fd, fd_list[i].dup_to,
1691                   fd_list[i].peer_name, fd_list[i].arg_loc);
1692         /* Rather safe than sorry.  */
1693         line[BUFFER_MAX - 1] = '\n';
1694         line[BUFFER_MAX] = '\0';
1695       }
1696     len = strlen (line);
1697     written = 0;
1698     do
1699       {
1700         res = write (tmp_fd, &line[written], len - written);
1701         if (res > 0)
1702           written += res;
1703       }
1704     while (res > 0 || (res < 0 && errno == EAGAIN));
1705   }
1706   close (tmp_fd);
1707   /* The temporary file is deleted by the gpgme-w32spawn process
1708      (hopefully).  */
1709 #endif
1710
1711
1712   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
1713               "dwProcessID=%d, dwThreadId=%d",
1714               pi.hProcess, pi.hThread, 
1715               (int) pi.dwProcessId, (int) pi.dwThreadId);
1716   
1717   if (r_pid)
1718     *r_pid = (pid_t)pi.dwProcessId;
1719
1720   
1721   if (ResumeThread (pi.hThread) < 0)
1722     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
1723   
1724   if (!CloseHandle (pi.hThread))
1725     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
1726                 (int) GetLastError ());
1727
1728   TRACE_LOG1 ("process=%p", pi.hProcess);
1729
1730   /* We don't need to wait for the process.  */
1731   if (!CloseHandle (pi.hProcess))
1732     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
1733                 (int) GetLastError ());
1734
1735   if (! (flags & IOSPAWN_FLAG_NOCLOSE))
1736     {
1737       for (i = 0; fd_list[i].fd != -1; i++)
1738         _gpgme_io_close (fd_list[i].fd);
1739     }
1740
1741   for (i = 0; fd_list[i].fd != -1; i++)
1742     if (fd_list[i].dup_to == -1)
1743       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
1744                   fd_list[i].peer_name);
1745     else
1746       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
1747                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
1748                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
1749
1750   return TRACE_SYSRES (0);
1751 }
1752
1753
1754 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
1755    nothing to select, > 0 = number of signaled fds.  */
1756 int
1757 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
1758 {
1759   HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
1760   int waitidx[MAXIMUM_WAIT_OBJECTS];
1761   int code;
1762   int nwait;
1763   int i;
1764   int any;
1765   int count;
1766   void *dbg_help;
1767   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
1768               "nfds=%u, nonblock=%u", nfds, nonblock);
1769
1770  restart:
1771   TRACE_SEQ (dbg_help, "select on [ ");
1772   any = 0;
1773   nwait = 0;
1774   count = 0;
1775   for (i=0; i < nfds; i++)
1776     {
1777       if (fds[i].fd == -1)
1778         continue;
1779       fds[i].signaled = 0;
1780       if (fds[i].for_read || fds[i].for_write)
1781         {
1782           if (fds[i].for_read)
1783             {
1784               struct reader_context_s *ctx = find_reader (fds[i].fd,0);
1785               
1786               if (!ctx)
1787                 TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
1788                             fds[i].fd);
1789               else
1790                 {
1791                   if (nwait >= DIM (waitbuf))
1792                     {
1793                       TRACE_END (dbg_help, "oops ]");
1794                       TRACE_LOG ("Too many objects for WFMO!");
1795                       /* FIXME: Should translate the error code.  */
1796                       gpg_err_set_errno (EIO);
1797                       return TRACE_SYSRES (-1);
1798                     }
1799                   waitidx[nwait] = i;
1800                   waitbuf[nwait++] = ctx->have_data_ev;
1801                 }
1802               TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
1803               any = 1;
1804             }
1805           else if (fds[i].for_write)
1806             {
1807               struct writer_context_s *ctx = find_writer (fds[i].fd,0);
1808               
1809               if (!ctx)
1810                 TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
1811                             fds[i].fd);
1812               else
1813                 {
1814                   if (nwait >= DIM (waitbuf))
1815                     {
1816                       TRACE_END (dbg_help, "oops ]");
1817                       TRACE_LOG ("Too many objects for WFMO!");
1818                       /* FIXME: Should translate the error code.  */
1819                       gpg_err_set_errno (EIO);
1820                       return TRACE_SYSRES (-1);
1821                     }
1822                   waitidx[nwait] = i;
1823                   waitbuf[nwait++] = ctx->is_empty;
1824                 }
1825               TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
1826               any = 1;
1827             }
1828         }
1829     }
1830   TRACE_END (dbg_help, "]");
1831   if (!any) 
1832     return TRACE_SYSRES (0);
1833
1834   code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
1835   if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
1836     {
1837       /* This WFMO is a really silly function: It does return either
1838          the index of the signaled object or if 2 objects have been
1839          signalled at the same time, the index of the object with the
1840          lowest object is returned - so and how do we find out how
1841          many objects have been signaled???.  The only solution I can
1842          imagine is to test each object starting with the returned
1843          index individually - how dull.  */
1844       any = 0;
1845       for (i = code - WAIT_OBJECT_0; i < nwait; i++)
1846         {
1847           if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
1848             {
1849               assert (waitidx[i] >=0 && waitidx[i] < nfds);
1850               fds[waitidx[i]].signaled = 1;
1851               any = 1;
1852               count++;
1853             }
1854         }
1855       if (!any)
1856         {
1857           TRACE_LOG ("no signaled objects found after WFMO");
1858           count = -1;
1859         }
1860     }
1861   else if (code == WAIT_TIMEOUT)
1862     TRACE_LOG ("WFMO timed out");
1863   else if (code == WAIT_FAILED)
1864     {
1865       int le = (int) GetLastError ();
1866 #if 0
1867       if (le == ERROR_INVALID_HANDLE)
1868         {
1869           int k;
1870           int j = handle_to_fd (waitbuf[i]);
1871           
1872           TRACE_LOG1 ("WFMO invalid handle %d removed", j);
1873           for (k = 0 ; k < nfds; k++)
1874             {
1875               if (fds[k].fd == j)
1876                 {
1877                   fds[k].for_read = fds[k].for_write = 0;
1878                   goto restart;
1879                 }
1880             }
1881           TRACE_LOG (" oops, or not???");
1882         }
1883 #endif
1884       TRACE_LOG1 ("WFMO failed: %d", le);
1885       count = -1;
1886     }
1887   else
1888     {
1889       TRACE_LOG1 ("WFMO returned %d", code);
1890       count = -1;
1891     }
1892   
1893   if (count > 0)
1894     {
1895       TRACE_SEQ (dbg_help, "select OK [ ");
1896       for (i = 0; i < nfds; i++)
1897         {
1898           if (fds[i].fd == -1)
1899             continue;
1900           if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
1901             TRACE_ADD2 (dbg_help, "%c0x%x ",
1902                         fds[i].for_read ? 'r' : 'w', fds[i].fd);
1903         }
1904       TRACE_END (dbg_help, "]");
1905     }
1906
1907   if (count < 0)
1908     {
1909       /* FIXME: Should determine a proper error code.  */
1910       gpg_err_set_errno (EIO);
1911     }
1912   
1913   return TRACE_SYSRES (count);
1914 }
1915
1916
1917 void
1918 _gpgme_io_subsystem_init (void)
1919 {
1920   /* Nothing to do.  */
1921 }
1922
1923
1924 /* Write the printable version of FD to the buffer BUF of length
1925    BUFLEN.  The printable version is the representation on the command
1926    line that the child process expects.  */
1927 int
1928 _gpgme_io_fd2str (char *buf, int buflen, int fd)
1929 {
1930 #ifdef HAVE_W32CE_SYSTEM
1931   /* FIXME: For now. See above.  */
1932   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
1933       || fd_table[fd].rvid == 0)
1934     fd = -1;
1935   else
1936     fd = fd_table[fd].rvid;
1937 #endif
1938
1939   return snprintf (buf, buflen, "%d", fd);
1940 }
1941
1942
1943 int
1944 _gpgme_io_dup (int fd)
1945 {
1946   int newfd;
1947   struct reader_context_s *rd_ctx;
1948   struct writer_context_s *wt_ctx;
1949   int i;
1950
1951   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
1952
1953   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1954     {
1955       gpg_err_set_errno (EINVAL);
1956       return TRACE_SYSRES (-1);
1957     }
1958
1959   newfd = new_fd();
1960   if (newfd == -1)
1961     return TRACE_SYSRES (-1);
1962   
1963   fd_table[newfd].handle = fd_table[fd].handle;
1964   fd_table[newfd].socket = fd_table[fd].socket;
1965   fd_table[newfd].rvid = fd_table[fd].rvid;
1966   fd_table[newfd].dup_from = fd;
1967
1968   rd_ctx = find_reader (fd, 0);
1969   if (rd_ctx)
1970     {
1971       /* No need for locking, as the only races are against the reader
1972          thread itself, which doesn't touch refcount.  */
1973       rd_ctx->refcount++;
1974
1975       LOCK (reader_table_lock);
1976       for (i = 0; i < reader_table_size; i++)
1977         if (!reader_table[i].used)
1978           break;
1979       /* FIXME.  */
1980       assert (i != reader_table_size);
1981       reader_table[i].fd = newfd;
1982       reader_table[i].context = rd_ctx;
1983       reader_table[i].used = 1;
1984       UNLOCK (reader_table_lock);
1985     }
1986
1987   wt_ctx = find_writer (fd, 0);
1988   if (wt_ctx)
1989     {
1990       /* No need for locking, as the only races are against the writer
1991          thread itself, which doesn't touch refcount.  */
1992       wt_ctx->refcount++;
1993
1994       LOCK (writer_table_lock);
1995       for (i = 0; i < writer_table_size; i++)
1996         if (!writer_table[i].used)
1997           break;
1998       /* FIXME.  */
1999       assert (i != writer_table_size);
2000       writer_table[i].fd = newfd;
2001       writer_table[i].context = wt_ctx;
2002       writer_table[i].used = 1;
2003       UNLOCK (writer_table_lock);
2004     }
2005
2006   return TRACE_SYSRES (newfd);
2007 }
2008
2009 \f
2010 /* The following interface is only useful for GPGME Glib and Qt.  */
2011
2012 /* Compatibility interface, obsolete.  */
2013 void *
2014 gpgme_get_giochannel (int fd)
2015 {
2016   return NULL;
2017 }
2018
2019
2020 /* Look up the giochannel or qiodevice for file descriptor FD.  */
2021 void *
2022 gpgme_get_fdptr (int fd)
2023 {
2024   return NULL;
2025 }
2026
2027 \f
2028 static int
2029 wsa2errno (int err)
2030 {
2031   switch (err)
2032     {
2033     case WSAENOTSOCK:
2034       return EINVAL;
2035     case WSAEWOULDBLOCK:
2036       return EAGAIN;
2037     case ERROR_BROKEN_PIPE:
2038       return EPIPE;
2039     case WSANOTINITIALISED:
2040       return ENOSYS;
2041     default:
2042       return EIO;
2043     }
2044 }
2045
2046
2047 int
2048 _gpgme_io_socket (int domain, int type, int proto)
2049 {
2050   int res;
2051   int fd;
2052
2053   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
2054               "type=%i, protp=%i", type, proto);
2055
2056   fd = new_fd();
2057   if (fd == -1)
2058     return TRACE_SYSRES (-1);
2059       
2060   res = socket (domain, type, proto);
2061   if (res == INVALID_SOCKET)
2062     {
2063       release_fd (fd);
2064       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2065       return TRACE_SYSRES (-1);
2066     }
2067   fd_table[fd].socket = res;
2068
2069   TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
2070   
2071   return res;
2072 }
2073
2074
2075 int
2076 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
2077 {
2078   int res;
2079
2080   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
2081               "addr=%p, addrlen=%i", addr, addrlen);
2082
2083   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
2084     {
2085       gpg_err_set_errno (EBADF);
2086       return TRACE_SYSRES (-1);
2087     }
2088     
2089   res = connect (fd_table[fd].socket, addr, addrlen);
2090   if (res)
2091     {
2092       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2093       return TRACE_SYSRES (-1);
2094     }
2095
2096   return TRACE_SUC ();
2097 }