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