core: Use gpgrt locking for thread safeness
[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)
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)
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   CloseHandle (ctx->close_ev);
780   CloseHandle (ctx->have_data);
781   CloseHandle (ctx->is_empty);
782   CloseHandle (ctx->thread_hd);
783   DESTROY_LOCK (ctx->mutex);
784   free (ctx);
785
786   return TRACE_SUC ();
787 }
788
789
790 static struct writer_context_s *
791 create_writer (int fd)
792 {
793   struct writer_context_s *ctx;
794   SECURITY_ATTRIBUTES sec_attr;
795   DWORD tid;
796
797   TRACE_BEG (DEBUG_SYSIO, "gpgme:create_writer", fd);
798
799   memset (&sec_attr, 0, sizeof sec_attr);
800   sec_attr.nLength = sizeof sec_attr;
801   sec_attr.bInheritHandle = FALSE;
802
803   ctx = calloc (1, sizeof *ctx);
804   if (!ctx)
805     {
806       TRACE_SYSERR (errno);
807       return NULL;
808     }
809
810   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
811     {
812       TRACE_SYSERR (EIO);
813       free (ctx);
814       return NULL;
815     }
816   TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
817               fd, fd_table[fd].handle, fd_table[fd].socket,
818               fd_table[fd].dup_from);
819   ctx->file_hd = fd_table[fd].handle;
820   ctx->file_sock = fd_table[fd].socket;
821
822   ctx->refcount = 1;
823   ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
824   if (ctx->have_data)
825     ctx->is_empty  = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
826   if (ctx->is_empty)
827     ctx->close_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
828   if (!ctx->have_data || !ctx->is_empty || !ctx->close_ev)
829     {
830       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
831       if (ctx->have_data)
832         CloseHandle (ctx->have_data);
833       if (ctx->is_empty)
834         CloseHandle (ctx->is_empty);
835       if (ctx->close_ev)
836         CloseHandle (ctx->close_ev);
837       free (ctx);
838       /* FIXME: Translate the error code.  */
839       TRACE_SYSERR (EIO);
840       return NULL;
841     }
842
843   ctx->is_empty = set_synchronize (ctx->is_empty);
844   INIT_LOCK (ctx->mutex);
845
846 #ifdef HAVE_W32CE_SYSTEM
847   ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
848                                  STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
849 #else
850   ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
851 #endif
852
853   if (!ctx->thread_hd)
854     {
855       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
856       DESTROY_LOCK (ctx->mutex);
857       if (ctx->have_data)
858         CloseHandle (ctx->have_data);
859       if (ctx->is_empty)
860         CloseHandle (ctx->is_empty);
861       if (ctx->close_ev)
862         CloseHandle (ctx->close_ev);
863       free (ctx);
864       TRACE_SYSERR (EIO);
865       return NULL;
866     }
867   else
868     {
869       /* We set the priority of the thread higher because we know
870          that it only runs for a short time.  This greatly helps to
871          increase the performance of the I/O.  */
872       SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
873     }
874
875   TRACE_SUC ();
876   return ctx;
877 }
878
879
880 static void
881 destroy_writer (struct writer_context_s *ctx)
882 {
883   LOCK (ctx->mutex);
884   ctx->refcount--;
885   if (ctx->refcount != 0)
886     {
887       UNLOCK (ctx->mutex);
888       return;
889     }
890   ctx->stop_me = 1;
891   if (ctx->have_data)
892     SetEvent (ctx->have_data);
893   UNLOCK (ctx->mutex);
894
895 #ifdef HAVE_W32CE_SYSTEM
896   /* Scenario: We never create a full pipe, but already started
897      writing more than the pipe buffer.  Then we need to unblock the
898      writer in the pipe driver to make our writer thread notice that
899      we want it to go away.  */
900
901   if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
902                         NULL, 0, NULL, 0, NULL, NULL))
903     {
904       TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
905               "unblock control call failed for thread %p", ctx->thread_hd);
906     }
907 #endif
908
909   /* After setting this event CTX is void.  */
910   SetEvent (ctx->close_ev);
911 }
912
913
914 /* Find a writer context or create a new one.  Note that the writer
915    context will last until a _gpgme_io_close.  */
916 static struct writer_context_s *
917 find_writer (int fd, int start_it)
918 {
919   struct writer_context_s *wt = NULL;
920   int i;
921
922   LOCK (writer_table_lock);
923   for (i = 0; i < writer_table_size; i++)
924     if (writer_table[i].used && writer_table[i].fd == fd)
925       wt = writer_table[i].context;
926
927   if (wt || !start_it)
928     {
929       UNLOCK (writer_table_lock);
930       return wt;
931     }
932
933   for (i = 0; i < writer_table_size; i++)
934     if (!writer_table[i].used)
935       break;
936
937   if (i != writer_table_size)
938     {
939       wt = create_writer (fd);
940       writer_table[i].fd = fd;
941       writer_table[i].context = wt;
942       writer_table[i].used = 1;
943     }
944
945   UNLOCK (writer_table_lock);
946   return wt;
947 }
948
949
950 int
951 _gpgme_io_write (int fd, const void *buffer, size_t count)
952 {
953   struct writer_context_s *ctx;
954   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
955               "buffer=%p, count=%u", buffer, count);
956   TRACE_LOGBUF (buffer, count);
957
958   if (count == 0)
959     return TRACE_SYSRES (0);
960
961   ctx = find_writer (fd, 1);
962   if (!ctx)
963     return TRACE_SYSRES (-1);
964
965   LOCK (ctx->mutex);
966   if (!ctx->error && ctx->nbytes)
967     {
968       /* Bytes are pending for send.  */
969
970       /* Reset the is_empty event.  Better safe than sorry.  */
971       if (!ResetEvent (ctx->is_empty))
972         {
973           TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
974           UNLOCK (ctx->mutex);
975           /* FIXME: Should translate the error code.  */
976           gpg_err_set_errno (EIO);
977           return TRACE_SYSRES (-1);
978         }
979       UNLOCK (ctx->mutex);
980       TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd);
981       WaitForSingleObject (ctx->is_empty, INFINITE);
982       TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd);
983       LOCK (ctx->mutex);
984     }
985
986   if (ctx->error)
987     {
988       UNLOCK (ctx->mutex);
989       if (ctx->error_code == ERROR_NO_DATA)
990         gpg_err_set_errno (EPIPE);
991       else
992         gpg_err_set_errno (EIO);
993       return TRACE_SYSRES (-1);
994     }
995
996   /* If no error occurred, the number of bytes in the buffer must be
997      zero.  */
998   assert (!ctx->nbytes);
999
1000   if (count > WRITEBUF_SIZE)
1001     count = WRITEBUF_SIZE;
1002   memcpy (ctx->buffer, buffer, count);
1003   ctx->nbytes = count;
1004
1005   /* We have to reset the is_empty event early, because it is also
1006      used by the select() implementation to probe the channel.  */
1007   if (!ResetEvent (ctx->is_empty))
1008     {
1009       TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
1010       UNLOCK (ctx->mutex);
1011       /* FIXME: Should translate the error code.  */
1012       gpg_err_set_errno (EIO);
1013       return TRACE_SYSRES (-1);
1014     }
1015   if (!SetEvent (ctx->have_data))
1016     {
1017       TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
1018       UNLOCK (ctx->mutex);
1019       /* FIXME: Should translate the error code.  */
1020       gpg_err_set_errno (EIO);
1021       return TRACE_SYSRES (-1);
1022     }
1023   UNLOCK (ctx->mutex);
1024
1025   return TRACE_SYSRES ((int) count);
1026 }
1027
1028
1029 int
1030 _gpgme_io_pipe (int filedes[2], int inherit_idx)
1031 {
1032   int rfd;
1033   int wfd;
1034 #ifdef HAVE_W32CE_SYSTEM
1035   HANDLE hd;
1036   int rvid;
1037 #else
1038   HANDLE rh;
1039   HANDLE wh;
1040   SECURITY_ATTRIBUTES sec_attr;
1041 #endif
1042
1043   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
1044               "inherit_idx=%i (GPGME uses it for %s)",
1045               inherit_idx, inherit_idx ? "reading" : "writing");
1046
1047   rfd = new_fd ();
1048   if (rfd == -1)
1049     return TRACE_SYSRES (-1);
1050   wfd = new_fd ();
1051   if (wfd == -1)
1052     {
1053       release_fd (rfd);
1054       return TRACE_SYSRES (-1);
1055     }
1056
1057 #ifdef HAVE_W32CE_SYSTEM
1058   hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
1059   if (hd == INVALID_HANDLE_VALUE)
1060     {
1061       TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d",
1062                   (int) GetLastError ());
1063       release_fd (rfd);
1064       release_fd (wfd);
1065       /* FIXME: Should translate the error code.  */
1066       gpg_err_set_errno (EIO);
1067       return TRACE_SYSRES (-1);
1068     }
1069
1070   if (inherit_idx == 0)
1071     {
1072       fd_table[rfd].rvid = rvid;
1073       fd_table[wfd].handle = hd;
1074     }
1075   else
1076     {
1077       fd_table[rfd].handle = hd;
1078       fd_table[wfd].rvid = rvid;
1079     }
1080
1081 #else
1082
1083   memset (&sec_attr, 0, sizeof (sec_attr));
1084   sec_attr.nLength = sizeof (sec_attr);
1085   sec_attr.bInheritHandle = FALSE;
1086
1087   if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
1088     {
1089       TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
1090       release_fd (rfd);
1091       release_fd (wfd);
1092       /* FIXME: Should translate the error code.  */
1093       gpg_err_set_errno (EIO);
1094       return TRACE_SYSRES (-1);
1095     }
1096
1097   /* Make one end inheritable.  */
1098   if (inherit_idx == 0)
1099     {
1100       HANDLE hd;
1101       if (!DuplicateHandle (GetCurrentProcess(), rh,
1102                             GetCurrentProcess(), &hd, 0,
1103                             TRUE, DUPLICATE_SAME_ACCESS))
1104         {
1105           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
1106                       (int) GetLastError ());
1107           release_fd (rfd);
1108           release_fd (wfd);
1109           CloseHandle (rh);
1110           CloseHandle (wh);
1111           /* FIXME: Should translate the error code.  */
1112           gpg_err_set_errno (EIO);
1113           return TRACE_SYSRES (-1);
1114         }
1115       CloseHandle (rh);
1116       rh = hd;
1117     }
1118   else if (inherit_idx == 1)
1119     {
1120       HANDLE hd;
1121       if (!DuplicateHandle( GetCurrentProcess(), wh,
1122                             GetCurrentProcess(), &hd, 0,
1123                             TRUE, DUPLICATE_SAME_ACCESS))
1124         {
1125           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
1126                       (int) GetLastError ());
1127           release_fd (rfd);
1128           release_fd (wfd);
1129           CloseHandle (rh);
1130           CloseHandle (wh);
1131           /* FIXME: Should translate the error code.  */
1132           gpg_err_set_errno (EIO);
1133           return TRACE_SYSRES (-1);
1134         }
1135       CloseHandle (wh);
1136       wh = hd;
1137     }
1138   fd_table[rfd].handle = rh;
1139   fd_table[wfd].handle = wh;
1140 #endif
1141
1142   filedes[0] = rfd;
1143   filedes[1] = wfd;
1144   return TRACE_SUC6 ("read=0x%x (%p/0x%x), write=0x%x (%p/0x%x)",
1145                      rfd, fd_table[rfd].handle, fd_table[rfd].rvid,
1146                      wfd, fd_table[wfd].handle, fd_table[wfd].rvid);
1147 }
1148
1149
1150 int
1151 _gpgme_io_close (int fd)
1152 {
1153   int i;
1154   _gpgme_close_notify_handler_t handler = NULL;
1155   void *value = NULL;
1156
1157   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
1158
1159   if (fd == -1)
1160     {
1161       gpg_err_set_errno (EBADF);
1162       return TRACE_SYSRES (-1);
1163     }
1164   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1165     {
1166       gpg_err_set_errno (EBADF);
1167       return TRACE_SYSRES (-1);
1168     }
1169
1170   TRACE_LOG4 ("fd=%d -> handle=%p socket=%d dupfrom=%d",
1171               fd, fd_table[fd].handle, fd_table[fd].socket,
1172               fd_table[fd].dup_from);
1173
1174   LOCK (reader_table_lock);
1175   for (i = 0; i < reader_table_size; i++)
1176     {
1177       if (reader_table[i].used && reader_table[i].fd == fd)
1178         {
1179           destroy_reader (reader_table[i].context);
1180           reader_table[i].context = NULL;
1181           reader_table[i].used = 0;
1182           break;
1183         }
1184     }
1185   UNLOCK (reader_table_lock);
1186
1187   LOCK (writer_table_lock);
1188   for (i = 0; i < writer_table_size; i++)
1189     {
1190       if (writer_table[i].used && writer_table[i].fd == fd)
1191         {
1192           destroy_writer (writer_table[i].context);
1193           writer_table[i].context = NULL;
1194           writer_table[i].used = 0;
1195           break;
1196         }
1197     }
1198   UNLOCK (writer_table_lock);
1199
1200   LOCK (notify_table_lock);
1201   for (i = 0; i < DIM (notify_table); i++)
1202     {
1203       if (notify_table[i].inuse && notify_table[i].fd == fd)
1204         {
1205           handler = notify_table[i].handler;
1206           value   = notify_table[i].value;
1207           notify_table[i].handler = NULL;
1208           notify_table[i].value = NULL;
1209           notify_table[i].inuse = 0;
1210           break;
1211         }
1212     }
1213   UNLOCK (notify_table_lock);
1214   if (handler)
1215     handler (fd, value);
1216
1217   if (fd_table[fd].dup_from == -1)
1218     {
1219       if (fd_table[fd].handle != INVALID_HANDLE_VALUE)
1220         {
1221           if (!CloseHandle (fd_table[fd].handle))
1222             {
1223               TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
1224               /* FIXME: Should translate the error code.  */
1225               gpg_err_set_errno (EIO);
1226               return TRACE_SYSRES (-1);
1227             }
1228         }
1229       else if (fd_table[fd].socket != INVALID_SOCKET)
1230         {
1231           if (closesocket (fd_table[fd].socket))
1232             {
1233               TRACE_LOG1 ("closesocket failed: ec=%d", (int) WSAGetLastError ());
1234               /* FIXME: Should translate the error code.  */
1235               gpg_err_set_errno (EIO);
1236               return TRACE_SYSRES (-1);
1237             }
1238         }
1239       /* Nothing to do for RVIDs.  */
1240     }
1241
1242   release_fd (fd);
1243
1244   return TRACE_SYSRES (0);
1245 }
1246
1247
1248 int
1249 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
1250                             void *value)
1251 {
1252   int i;
1253   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
1254               "close_handler=%p/%p", handler, value);
1255
1256   assert (fd != -1);
1257
1258   LOCK (notify_table_lock);
1259   for (i=0; i < DIM (notify_table); i++)
1260     if (notify_table[i].inuse && notify_table[i].fd == fd)
1261       break;
1262   if (i == DIM (notify_table))
1263     for (i = 0; i < DIM (notify_table); i++)
1264       if (!notify_table[i].inuse)
1265         break;
1266   if (i == DIM (notify_table))
1267     {
1268       UNLOCK (notify_table_lock);
1269       gpg_err_set_errno (EINVAL);
1270       return TRACE_SYSRES (-1);
1271     }
1272   notify_table[i].fd = fd;
1273   notify_table[i].handler = handler;
1274   notify_table[i].value = value;
1275   notify_table[i].inuse = 1;
1276   UNLOCK (notify_table_lock);
1277   return TRACE_SYSRES (0);
1278 }
1279
1280
1281 int
1282 _gpgme_io_set_nonblocking (int fd)
1283 {
1284   TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
1285   return 0;
1286 }
1287
1288
1289 #ifdef HAVE_W32CE_SYSTEM
1290 static char *
1291 build_commandline (char **argv, int fd0, int fd0_isnull,
1292                    int fd1, int fd1_isnull,
1293                    int fd2, int fd2_isnull)
1294 {
1295   int i, n;
1296   const char *s;
1297   char *buf, *p;
1298   char fdbuf[3*30];
1299
1300   p = fdbuf;
1301   *p = 0;
1302
1303   if (fd0 != -1)
1304     {
1305       if (fd0_isnull)
1306         strcpy (p, "-&S0=null ");
1307       else
1308         snprintf (p, 25, "-&S0=%d ", fd_table[fd0].rvid);
1309       p += strlen (p);
1310     }
1311   if (fd1 != -1)
1312     {
1313       if (fd1_isnull)
1314         strcpy (p, "-&S1=null ");
1315       else
1316         snprintf (p, 25, "-&S1=%d ", fd_table[fd1].rvid);
1317       p += strlen (p);
1318     }
1319   if (fd2 != -1)
1320     {
1321       if (fd2_isnull)
1322         strcpy (p, "-&S2=null ");
1323       else
1324         snprintf (p, 25, "-&S2=%d ", fd_table[fd2].rvid);
1325       p += strlen (p);
1326     }
1327   strcpy (p, "-&S2=null ");
1328   p += strlen (p);
1329
1330   n = strlen (fdbuf);
1331   for (i=0; (s = argv[i]); i++)
1332     {
1333       if (!i)
1334         continue; /* Ignore argv[0].  */
1335       n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting) */
1336       for (; *s; s++)
1337         if (*s == '\"')
1338           n++;  /* Need to double inner quotes.  */
1339     }
1340   n++;
1341   buf = p = malloc (n);
1342   if (! buf)
1343     return NULL;
1344
1345   p = stpcpy (p, fdbuf);
1346   for (i = 0; argv[i]; i++)
1347     {
1348       if (!i)
1349         continue; /* Ignore argv[0].  */
1350       if (i > 1)
1351         p = stpcpy (p, " ");
1352
1353       if (! *argv[i]) /* Empty string. */
1354         p = stpcpy (p, "\"\"");
1355       else if (strpbrk (argv[i], " \t\n\v\f\""))
1356         {
1357           p = stpcpy (p, "\"");
1358           for (s = argv[i]; *s; s++)
1359             {
1360               *p++ = *s;
1361               if (*s == '\"')
1362                 *p++ = *s;
1363             }
1364           *p++ = '\"';
1365           *p = 0;
1366         }
1367       else
1368         p = stpcpy (p, argv[i]);
1369     }
1370
1371   return buf;
1372 }
1373 #else
1374 static char *
1375 build_commandline (char **argv)
1376 {
1377   int i;
1378   int n = 0;
1379   char *buf;
1380   char *p;
1381
1382   /* We have to quote some things because under Windows the program
1383      parses the commandline and does some unquoting.  We enclose the
1384      whole argument in double-quotes, and escape literal double-quotes
1385      as well as backslashes with a backslash.  We end up with a
1386      trailing space at the end of the line, but that is harmless.  */
1387   for (i = 0; argv[i]; i++)
1388     {
1389       p = argv[i];
1390       /* The leading double-quote.  */
1391       n++;
1392       while (*p)
1393         {
1394           /* An extra one for each literal that must be escaped.  */
1395           if (*p == '\\' || *p == '"')
1396             n++;
1397           n++;
1398           p++;
1399         }
1400       /* The trailing double-quote and the delimiter.  */
1401       n += 2;
1402     }
1403   /* And a trailing zero.  */
1404   n++;
1405
1406   buf = p = malloc (n);
1407   if (!buf)
1408     return NULL;
1409   for (i = 0; argv[i]; i++)
1410     {
1411       char *argvp = argv[i];
1412
1413       *(p++) = '"';
1414       while (*argvp)
1415         {
1416           if (*argvp == '\\' || *argvp == '"')
1417             *(p++) = '\\';
1418           *(p++) = *(argvp++);
1419         }
1420       *(p++) = '"';
1421       *(p++) = ' ';
1422     }
1423   *(p++) = 0;
1424
1425   return buf;
1426 }
1427 #endif
1428
1429
1430 int
1431 _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
1432                  struct spawn_fd_item_s *fd_list,
1433                  void (*atfork) (void *opaque, int reserved),
1434                  void *atforkvalue, pid_t *r_pid)
1435 {
1436   PROCESS_INFORMATION pi =
1437     {
1438       NULL,      /* returns process handle */
1439       0,         /* returns primary thread handle */
1440       0,         /* returns pid */
1441       0          /* returns tid */
1442     };
1443   int i;
1444
1445 #ifdef HAVE_W32CE_SYSTEM
1446   int fd_in = -1;
1447   int fd_out = -1;
1448   int fd_err = -1;
1449   int fd_in_isnull = 1;
1450   int fd_out_isnull = 1;
1451   int fd_err_isnull = 1;
1452   char *cmdline;
1453   HANDLE hd = INVALID_HANDLE_VALUE;
1454
1455   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1456               "path=%s", path);
1457   i = 0;
1458   while (argv[i])
1459     {
1460       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1461       i++;
1462     }
1463
1464   for (i = 0; fd_list[i].fd != -1; i++)
1465     {
1466       int fd = fd_list[i].fd;
1467
1468       TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd, fd_list[i].dup_to);
1469       if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1470         {
1471           TRACE_LOG1 ("invalid fd 0x%x", fd);
1472           gpg_err_set_errno (EBADF);
1473           return TRACE_SYSRES (-1);
1474         }
1475       if (fd_table[fd].rvid == 0)
1476         {
1477           TRACE_LOG1 ("fd 0x%x not inheritable (not an RVID)", fd);
1478           gpg_err_set_errno (EBADF);
1479           return TRACE_SYSRES (-1);
1480         }
1481
1482       if (fd_list[i].dup_to == 0)
1483         {
1484           fd_in = fd_list[i].fd;
1485           fd_in_isnull = 0;
1486         }
1487       else if (fd_list[i].dup_to == 1)
1488         {
1489           fd_out = fd_list[i].fd;
1490           fd_out_isnull = 0;
1491         }
1492       else if (fd_list[i].dup_to == 2)
1493         {
1494           fd_err = fd_list[i].fd;
1495           fd_err_isnull = 0;
1496         }
1497     }
1498
1499   cmdline = build_commandline (argv, fd_in, fd_in_isnull,
1500                                fd_out, fd_out_isnull, fd_err, fd_err_isnull);
1501   if (!cmdline)
1502     {
1503       TRACE_LOG1 ("build_commandline failed: %s", strerror (errno));
1504       return TRACE_SYSRES (-1);
1505     }
1506
1507   if (!CreateProcessA (path,                /* Program to start.  */
1508                        cmdline,             /* Command line arguments.  */
1509                        NULL,                 /* (not supported)  */
1510                        NULL,                 /* (not supported)  */
1511                        FALSE,                /* (not supported)  */
1512                        (CREATE_SUSPENDED),   /* Creation flags.  */
1513                        NULL,                 /* (not supported)  */
1514                        NULL,                 /* (not supported)  */
1515                        NULL,                 /* (not supported) */
1516                        &pi                   /* Returns process information.*/
1517                        ))
1518     {
1519       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1520       free (cmdline);
1521       gpg_err_set_errno (EIO);
1522       return TRACE_SYSRES (-1);
1523     }
1524
1525   /* Create arbitrary pipe descriptor to send in ASSIGN_RVID
1526      commands.  Errors are ignored.  We don't need read or write access,
1527      as ASSIGN_RVID works without any permissions, yay!  */
1528   hd = CreateFile (L"GPG1:", 0, 0,
1529                    NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1530   if (hd == INVALID_HANDLE_VALUE)
1531     {
1532       TRACE_LOG1 ("CreateFile failed (ignored): ec=%d",
1533                   (int) GetLastError ());
1534     }
1535
1536   /* Insert the inherited handles.  */
1537   for (i = 0; fd_list[i].fd != -1; i++)
1538     {
1539       /* Return the child name of this handle.  */
1540       fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
1541
1542       if (hd != INVALID_HANDLE_VALUE)
1543         {
1544           DWORD data[2];
1545           data[0] = (DWORD) fd_table[fd_list[i].fd].rvid;
1546           data[1] = pi.dwProcessId;
1547           if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_ASSIGN_RVID,
1548                                 data, sizeof (data), NULL, 0, NULL, NULL))
1549             {
1550               TRACE_LOG3 ("ASSIGN_RVID(%i, %i) failed (ignored): %i",
1551                           data[0], data[1], (int) GetLastError ());
1552             }
1553         }
1554     }
1555   if (hd != INVALID_HANDLE_VALUE)
1556     CloseHandle (hd);
1557
1558 #else
1559   SECURITY_ATTRIBUTES sec_attr;
1560   STARTUPINFOA si;
1561   int cr_flags = CREATE_DEFAULT_ERROR_MODE;
1562   char **args;
1563   char *arg_string;
1564   /* FIXME.  */
1565   int debug_me = 0;
1566   int tmp_fd;
1567   char *tmp_name;
1568   const char *spawnhelper;
1569
1570   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1571               "path=%s", path);
1572   i = 0;
1573   while (argv[i])
1574     {
1575       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1576       i++;
1577     }
1578
1579   /* We do not inherit any handles by default, and just insert those
1580      handles we want the child to have afterwards.  But some handle
1581      values occur on the command line, and we need to move
1582      stdin/out/err to the right location.  So we use a wrapper program
1583      which gets the information from a temporary file.  */
1584   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
1585     {
1586       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
1587       return TRACE_SYSRES (-1);
1588     }
1589   TRACE_LOG1 ("tmp_name = %s", tmp_name);
1590
1591   args = calloc (2 + i + 1, sizeof (*args));
1592   args[0] = (char *) _gpgme_get_w32spawn_path ();
1593   args[1] = tmp_name;
1594   args[2] = (char *)path;
1595   memcpy (&args[3], &argv[1], i * sizeof (*args));
1596
1597   memset (&sec_attr, 0, sizeof sec_attr);
1598   sec_attr.nLength = sizeof sec_attr;
1599   sec_attr.bInheritHandle = FALSE;
1600
1601   arg_string = build_commandline (args);
1602   free (args);
1603   if (!arg_string)
1604     {
1605       close (tmp_fd);
1606       DeleteFileA (tmp_name);
1607       return TRACE_SYSRES (-1);
1608     }
1609
1610   memset (&si, 0, sizeof si);
1611   si.cb = sizeof (si);
1612   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1613   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
1614   si.hStdInput = INVALID_HANDLE_VALUE;
1615   si.hStdOutput = INVALID_HANDLE_VALUE;
1616   si.hStdError = INVALID_HANDLE_VALUE;
1617
1618   cr_flags |= CREATE_SUSPENDED;
1619   if ((flags & IOSPAWN_FLAG_DETACHED))
1620     cr_flags |= DETACHED_PROCESS;
1621   cr_flags |= GetPriorityClass (GetCurrentProcess ());
1622   spawnhelper = _gpgme_get_w32spawn_path ();
1623   if (!spawnhelper)
1624     {
1625       /* This is a common mistake for new users of gpgme not to include
1626          gpgme-w32spawn.exe with their binary. So we want to make
1627          this transparent to developers. If users have somehow messed
1628          up their installation this should also be properly communicated
1629          as otherwise calls to gnupg will result in unsupported protocol
1630          errors that do not explain a lot. */
1631       char *msg;
1632       gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
1633                             "detected installation directory of GpgME"
1634                             "\n\t\"%s\"\n\n"
1635                             "Crypto operations will not work.\n\n"
1636                             "If you see this it indicates a problem "
1637                             "with your installation.\n"
1638                             "Please report the problem to your "
1639                             "distributor of GpgME.\n\n"
1640                             "Developers Note: The install dir can be "
1641                             "manually set with: gpgme_set_global_flag",
1642                             _gpgme_get_inst_dir ());
1643       MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
1644       free (msg);
1645       gpg_err_set_errno (EIO);
1646       return TRACE_SYSRES (-1);
1647     }
1648   if (!CreateProcessA (spawnhelper,
1649                        arg_string,
1650                        &sec_attr,     /* process security attributes */
1651                        &sec_attr,     /* thread security attributes */
1652                        FALSE,         /* inherit handles */
1653                        cr_flags,      /* creation flags */
1654                        NULL,          /* environment */
1655                        NULL,          /* use current drive/directory */
1656                        &si,           /* startup information */
1657                        &pi))          /* returns process information */
1658     {
1659       int lasterr = (int)GetLastError ();
1660       TRACE_LOG1 ("CreateProcess failed: ec=%d", lasterr);
1661       free (arg_string);
1662       close (tmp_fd);
1663       DeleteFileA (tmp_name);
1664
1665       /* FIXME: Should translate the error code.  */
1666       gpg_err_set_errno (EIO);
1667       return TRACE_SYSRES (-1);
1668     }
1669
1670   free (arg_string);
1671
1672   if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
1673     _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
1674
1675   /* Insert the inherited handles.  */
1676   for (i = 0; fd_list[i].fd != -1; i++)
1677     {
1678       int fd = fd_list[i].fd;
1679       HANDLE ohd = INVALID_HANDLE_VALUE;
1680       HANDLE hd = INVALID_HANDLE_VALUE;
1681
1682       /* Make it inheritable for the wrapper process.  */
1683       if (fd >= 0 && fd < MAX_SLAFD && fd_table[fd].used)
1684         ohd = fd_table[fd].handle;
1685
1686       if (!DuplicateHandle (GetCurrentProcess(), ohd,
1687                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
1688         {
1689           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
1690           TerminateProcess (pi.hProcess, 0);
1691           /* Just in case TerminateProcess didn't work, let the
1692              process fail on its own.  */
1693           ResumeThread (pi.hThread);
1694           CloseHandle (pi.hThread);
1695           CloseHandle (pi.hProcess);
1696
1697           close (tmp_fd);
1698           DeleteFileA (tmp_name);
1699
1700           /* FIXME: Should translate the error code.  */
1701           gpg_err_set_errno (EIO);
1702           return TRACE_SYSRES (-1);
1703         }
1704       /* Return the child name of this handle.  */
1705       fd_list[i].peer_name = handle_to_fd (hd);
1706     }
1707
1708   /* Write the handle translation information to the temporary
1709      file.  */
1710   {
1711     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
1712        notation: "0xFEDCBA9876543210" with an extra white space after
1713        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
1714        for a time when a HANDLE is 64 bit.  */
1715 #define BUFFER_MAX 810
1716     char line[BUFFER_MAX + 1];
1717     int res;
1718     int written;
1719     size_t len;
1720
1721     if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
1722       strcpy (line, "~1 \n");
1723     else
1724       strcpy (line, "\n");
1725     for (i = 0; fd_list[i].fd != -1; i++)
1726       {
1727         /* Strip the newline.  */
1728         len = strlen (line) - 1;
1729
1730         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
1731         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
1732                   fd_list[i].fd, fd_list[i].dup_to,
1733                   fd_list[i].peer_name, fd_list[i].arg_loc);
1734         /* Rather safe than sorry.  */
1735         line[BUFFER_MAX - 1] = '\n';
1736         line[BUFFER_MAX] = '\0';
1737       }
1738     len = strlen (line);
1739     written = 0;
1740     do
1741       {
1742         res = write (tmp_fd, &line[written], len - written);
1743         if (res > 0)
1744           written += res;
1745       }
1746     while (res > 0 || (res < 0 && errno == EAGAIN));
1747   }
1748   close (tmp_fd);
1749   /* The temporary file is deleted by the gpgme-w32spawn process
1750      (hopefully).  */
1751 #endif
1752
1753
1754   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
1755               "dwProcessID=%d, dwThreadId=%d",
1756               pi.hProcess, pi.hThread,
1757               (int) pi.dwProcessId, (int) pi.dwThreadId);
1758
1759   if (r_pid)
1760     *r_pid = (pid_t)pi.dwProcessId;
1761
1762
1763   if (ResumeThread (pi.hThread) < 0)
1764     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
1765
1766   if (!CloseHandle (pi.hThread))
1767     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
1768                 (int) GetLastError ());
1769
1770   TRACE_LOG1 ("process=%p", pi.hProcess);
1771
1772   /* We don't need to wait for the process.  */
1773   if (!CloseHandle (pi.hProcess))
1774     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
1775                 (int) GetLastError ());
1776
1777   if (! (flags & IOSPAWN_FLAG_NOCLOSE))
1778     {
1779       for (i = 0; fd_list[i].fd != -1; i++)
1780         _gpgme_io_close (fd_list[i].fd);
1781     }
1782
1783   for (i = 0; fd_list[i].fd != -1; i++)
1784     if (fd_list[i].dup_to == -1)
1785       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
1786                   fd_list[i].peer_name);
1787     else
1788       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
1789                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
1790                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
1791
1792   return TRACE_SYSRES (0);
1793 }
1794
1795
1796 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
1797    nothing to select, > 0 = number of signaled fds.  */
1798 int
1799 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
1800 {
1801   HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
1802   int waitidx[MAXIMUM_WAIT_OBJECTS];
1803   int code;
1804   int nwait;
1805   int i;
1806   int any;
1807   int count;
1808   void *dbg_help;
1809   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
1810               "nfds=%u, nonblock=%u", nfds, nonblock);
1811
1812 #if 0
1813  restart:
1814 #endif
1815   TRACE_SEQ (dbg_help, "select on [ ");
1816   any = 0;
1817   nwait = 0;
1818   count = 0;
1819   for (i=0; i < nfds; i++)
1820     {
1821       if (fds[i].fd == -1)
1822         continue;
1823       fds[i].signaled = 0;
1824       if (fds[i].for_read || fds[i].for_write)
1825         {
1826           if (fds[i].for_read)
1827             {
1828               struct reader_context_s *ctx = find_reader (fds[i].fd,1);
1829
1830               if (!ctx)
1831                 TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
1832                             fds[i].fd);
1833               else
1834                 {
1835                   if (nwait >= DIM (waitbuf))
1836                     {
1837                       TRACE_END (dbg_help, "oops ]");
1838                       TRACE_LOG ("Too many objects for WFMO!");
1839                       /* FIXME: Should translate the error code.  */
1840                       gpg_err_set_errno (EIO);
1841                       return TRACE_SYSRES (-1);
1842                     }
1843                   waitidx[nwait] = i;
1844                   waitbuf[nwait++] = ctx->have_data_ev;
1845                 }
1846               TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
1847               any = 1;
1848             }
1849           else if (fds[i].for_write)
1850             {
1851               struct writer_context_s *ctx = find_writer (fds[i].fd,1);
1852
1853               if (!ctx)
1854                 TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
1855                             fds[i].fd);
1856               else
1857                 {
1858                   if (nwait >= DIM (waitbuf))
1859                     {
1860                       TRACE_END (dbg_help, "oops ]");
1861                       TRACE_LOG ("Too many objects for WFMO!");
1862                       /* FIXME: Should translate the error code.  */
1863                       gpg_err_set_errno (EIO);
1864                       return TRACE_SYSRES (-1);
1865                     }
1866                   waitidx[nwait] = i;
1867                   waitbuf[nwait++] = ctx->is_empty;
1868                 }
1869               TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
1870               any = 1;
1871             }
1872         }
1873     }
1874   TRACE_END (dbg_help, "]");
1875   if (!any)
1876     return TRACE_SYSRES (0);
1877
1878   code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
1879   if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
1880     {
1881       /* This WFMO is a really silly function: It does return either
1882          the index of the signaled object or if 2 objects have been
1883          signalled at the same time, the index of the object with the
1884          lowest object is returned - so and how do we find out how
1885          many objects have been signaled???.  The only solution I can
1886          imagine is to test each object starting with the returned
1887          index individually - how dull.  */
1888       any = 0;
1889       for (i = code - WAIT_OBJECT_0; i < nwait; i++)
1890         {
1891           if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
1892             {
1893               assert (waitidx[i] >=0 && waitidx[i] < nfds);
1894               fds[waitidx[i]].signaled = 1;
1895               any = 1;
1896               count++;
1897             }
1898         }
1899       if (!any)
1900         {
1901           TRACE_LOG ("no signaled objects found after WFMO");
1902           count = -1;
1903         }
1904     }
1905   else if (code == WAIT_TIMEOUT)
1906     TRACE_LOG ("WFMO timed out");
1907   else if (code == WAIT_FAILED)
1908     {
1909       int le = (int) GetLastError ();
1910 #if 0
1911       if (le == ERROR_INVALID_HANDLE)
1912         {
1913           int k;
1914           int j = handle_to_fd (waitbuf[i]);
1915
1916           TRACE_LOG1 ("WFMO invalid handle %d removed", j);
1917           for (k = 0 ; k < nfds; k++)
1918             {
1919               if (fds[k].fd == j)
1920                 {
1921                   fds[k].for_read = fds[k].for_write = 0;
1922                   goto restart;
1923                 }
1924             }
1925           TRACE_LOG (" oops, or not???");
1926         }
1927 #endif
1928       TRACE_LOG1 ("WFMO failed: %d", le);
1929       count = -1;
1930     }
1931   else
1932     {
1933       TRACE_LOG1 ("WFMO returned %d", code);
1934       count = -1;
1935     }
1936
1937   if (count > 0)
1938     {
1939       TRACE_SEQ (dbg_help, "select OK [ ");
1940       for (i = 0; i < nfds; i++)
1941         {
1942           if (fds[i].fd == -1)
1943             continue;
1944           if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
1945             TRACE_ADD2 (dbg_help, "%c0x%x ",
1946                         fds[i].for_read ? 'r' : 'w', fds[i].fd);
1947         }
1948       TRACE_END (dbg_help, "]");
1949     }
1950
1951   if (count < 0)
1952     {
1953       /* FIXME: Should determine a proper error code.  */
1954       gpg_err_set_errno (EIO);
1955     }
1956
1957   return TRACE_SYSRES (count);
1958 }
1959
1960
1961 void
1962 _gpgme_io_subsystem_init (void)
1963 {
1964   /* Nothing to do.  */
1965 }
1966
1967
1968 /* Write the printable version of FD to the buffer BUF of length
1969    BUFLEN.  The printable version is the representation on the command
1970    line that the child process expects.  */
1971 int
1972 _gpgme_io_fd2str (char *buf, int buflen, int fd)
1973 {
1974 #ifdef HAVE_W32CE_SYSTEM
1975   /* FIXME: For now. See above.  */
1976   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
1977       || fd_table[fd].rvid == 0)
1978     fd = -1;
1979   else
1980     fd = fd_table[fd].rvid;
1981 #endif
1982
1983   return snprintf (buf, buflen, "%d", fd);
1984 }
1985
1986
1987 int
1988 _gpgme_io_dup (int fd)
1989 {
1990   int newfd;
1991   struct reader_context_s *rd_ctx;
1992   struct writer_context_s *wt_ctx;
1993   int i;
1994
1995   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
1996
1997   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
1998     {
1999       gpg_err_set_errno (EINVAL);
2000       return TRACE_SYSRES (-1);
2001     }
2002
2003   newfd = new_fd();
2004   if (newfd == -1)
2005     return TRACE_SYSRES (-1);
2006
2007   fd_table[newfd].handle = fd_table[fd].handle;
2008   fd_table[newfd].socket = fd_table[fd].socket;
2009   fd_table[newfd].rvid = fd_table[fd].rvid;
2010   fd_table[newfd].dup_from = fd;
2011
2012   rd_ctx = find_reader (fd, 1);
2013   if (rd_ctx)
2014     {
2015       /* No need for locking, as the only races are against the reader
2016          thread itself, which doesn't touch refcount.  */
2017       rd_ctx->refcount++;
2018
2019       LOCK (reader_table_lock);
2020       for (i = 0; i < reader_table_size; i++)
2021         if (!reader_table[i].used)
2022           break;
2023       /* FIXME.  */
2024       assert (i != reader_table_size);
2025       reader_table[i].fd = newfd;
2026       reader_table[i].context = rd_ctx;
2027       reader_table[i].used = 1;
2028       UNLOCK (reader_table_lock);
2029     }
2030
2031   wt_ctx = find_writer (fd, 1);
2032   if (wt_ctx)
2033     {
2034       /* No need for locking, as the only races are against the writer
2035          thread itself, which doesn't touch refcount.  */
2036       wt_ctx->refcount++;
2037
2038       LOCK (writer_table_lock);
2039       for (i = 0; i < writer_table_size; i++)
2040         if (!writer_table[i].used)
2041           break;
2042       /* FIXME.  */
2043       assert (i != writer_table_size);
2044       writer_table[i].fd = newfd;
2045       writer_table[i].context = wt_ctx;
2046       writer_table[i].used = 1;
2047       UNLOCK (writer_table_lock);
2048     }
2049
2050   return TRACE_SYSRES (newfd);
2051 }
2052
2053 \f
2054 /* The following interface is only useful for GPGME Glib and Qt.  */
2055
2056 /* Compatibility interface, obsolete.  */
2057 void *
2058 gpgme_get_giochannel (int fd)
2059 {
2060   return NULL;
2061 }
2062
2063
2064 /* Look up the giochannel or qiodevice for file descriptor FD.  */
2065 void *
2066 gpgme_get_fdptr (int fd)
2067 {
2068   return NULL;
2069 }
2070
2071 \f
2072 static int
2073 wsa2errno (int err)
2074 {
2075   switch (err)
2076     {
2077     case WSAENOTSOCK:
2078       return EINVAL;
2079     case WSAEWOULDBLOCK:
2080       return EAGAIN;
2081     case ERROR_BROKEN_PIPE:
2082       return EPIPE;
2083     case WSANOTINITIALISED:
2084       return ENOSYS;
2085     default:
2086       return EIO;
2087     }
2088 }
2089
2090
2091 int
2092 _gpgme_io_socket (int domain, int type, int proto)
2093 {
2094   int res;
2095   int fd;
2096
2097   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
2098               "type=%i, protp=%i", type, proto);
2099
2100   fd = new_fd();
2101   if (fd == -1)
2102     return TRACE_SYSRES (-1);
2103
2104   res = socket (domain, type, proto);
2105   if (res == INVALID_SOCKET)
2106     {
2107       release_fd (fd);
2108       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2109       return TRACE_SYSRES (-1);
2110     }
2111   fd_table[fd].socket = res;
2112
2113   TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
2114
2115   return fd;
2116 }
2117
2118
2119 int
2120 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
2121 {
2122   int res;
2123
2124   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
2125               "addr=%p, addrlen=%i", addr, addrlen);
2126
2127   if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
2128     {
2129       gpg_err_set_errno (EBADF);
2130       return TRACE_SYSRES (-1);
2131     }
2132
2133   res = connect (fd_table[fd].socket, addr, addrlen);
2134   if (res)
2135     {
2136       gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
2137       return TRACE_SYSRES (-1);
2138     }
2139
2140   return TRACE_SUC ();
2141 }