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