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