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