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