9381b2e63036bf4be5c1605b5aeca336e40b74aa
[gpgme.git] / gpgme / w32-glib-io.c
1 /* w32-glib-io.c - W32 Glib I/O functions
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2004, 2005 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 <signal.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <glib.h>
36 #include <windows.h>
37 #include <io.h>
38
39 #include "util.h"
40 #include "priv-io.h"
41 #include "sema.h"
42 #include "debug.h"
43
44 #ifndef O_BINARY
45 #ifdef _O_BINARY
46 #define O_BINARY        _O_BINARY
47 #else
48 #define O_BINARY        0
49 #endif
50 #endif
51
52 \f
53 /* This file is an ugly hack to get GPGME working with glib on Windows
54    targets.  On Windows, you can not select() on file descriptors.
55    The only way to check if there is something to read is to read
56    something.  This means that GPGME can not let glib check for data
57    without letting glib also handle the data on Windows targets.
58
59    The ugly consequence is that we need to work on GIOChannels in
60    GPGME, creating a glib dependency.  Also, we need to export an
61    interface for the application to get at GPGME's GIOChannel.  There
62    is no good way to abstract all this with callbacks, because the
63    whole thing is also interconnected with the creation of pipes and
64    child processes.
65
66    The following rule applies only to this I/O backend:
67
68    * ALL operations must use the user defined event loop.  GPGME can
69    not anymore provide its own event loop.  This is mostly a sanity
70    requirement: Although we have in theory all information we need to
71    make the GPGME W32 code for select still work, it would be a big
72    complication and require changes throughout GPGME.
73
74    Eventually, we probably have to bite the bullet and make some
75    really nice callback interfaces to let the user control all this at
76    a per-context level.  */
77
78 \f
79 #define MAX_SLAFD 256
80
81 static struct 
82 {
83   GIOChannel *chan;
84   /* The boolean PRIMARY is true if this file descriptor caused the
85      allocation of CHAN.  Only then should CHAN be destroyed when this
86      FD is closed.  This, together with the fact that dup'ed file
87      descriptors are closed before the file descriptors from which
88      they are dup'ed are closed, ensures that CHAN is always valid,
89      and shared among all file descriptors refering to the same
90      underlying object.
91
92      The logic behind this is that there is only one reason for us to
93      dup file descriptors anyway: to allow simpler book-keeping of
94      file descriptors shared between GPGME and libassuan, which both
95      want to close something.  Using the same channel for these
96      duplicates works just fine (and in fact, using different channels
97      does not work because the W32 backend in glib does not support
98      that: One would end up with several competing reader/writer
99      threads.  */
100   int primary;
101 } giochannel_table[MAX_SLAFD];
102
103
104 static GIOChannel *
105 find_channel (int fd, int create)
106 {
107   if (fd < 0 || fd >= MAX_SLAFD)
108     return NULL;
109
110   if (create && !giochannel_table[fd].chan)
111     {
112       giochannel_table[fd].chan = g_io_channel_win32_new_fd (fd);
113       giochannel_table[fd].primary = 1;
114       g_io_channel_set_encoding (giochannel_table[fd].chan, NULL, NULL);
115       g_io_channel_set_buffered (giochannel_table[fd].chan, FALSE);
116     }
117
118   return giochannel_table[fd].chan;
119 }
120
121
122 /* Compatibility interface.  Obsolete.  */
123 void *
124 gpgme_get_giochannel (int fd)
125 {
126   return find_channel (fd, 0);
127 }
128
129
130 /* Look up the giochannel for "file descriptor" FD.  */
131 void *
132 gpgme_get_fdptr (int fd)
133 {
134   return find_channel (fd, 0);
135 }
136
137
138 /* Write the printable version of FD to the buffer BUF of length
139    BUFLEN.  The printable version is the representation on the command
140    line that the child process expects.  */
141 int
142 _gpgme_io_fd2str (char *buf, int buflen, int fd)
143 {
144   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd);
145   TRACE_SUC1 ("syshd=%p", _get_osfhandle (fd));
146   return snprintf (buf, buflen, "%ld", (long) _get_osfhandle (fd));
147 }
148
149 \f
150 void
151 _gpgme_io_subsystem_init (void)
152 {
153 }
154
155 \f
156 static struct
157 {
158   _gpgme_close_notify_handler_t handler;
159   void *value;
160 } notify_table[MAX_SLAFD];
161
162
163 int
164 _gpgme_io_read (int fd, void *buffer, size_t count)
165 {
166   int saved_errno = 0;
167   gsize nread;
168   GIOChannel *chan;
169   GIOStatus status;
170   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
171               "buffer=%p, count=%u", buffer, count);
172
173   chan = find_channel (fd, 0);
174   if (!chan)
175     {
176       TRACE_LOG ("no channel registered");
177       errno = EINVAL;
178       return TRACE_SYSRES (-1);
179     }
180   TRACE_LOG1 ("channel %p", chan);
181
182   {
183     GError *err = NULL;
184     status = g_io_channel_read_chars (chan, (gchar *) buffer,
185                                       count, &nread, &err);
186     if (err)
187       {
188         TRACE_LOG2 ("status %i, err %s", status, err->message);
189         g_error_free (err);
190       }
191   }
192
193   if (status == G_IO_STATUS_EOF)
194     nread = 0;
195   else if (status != G_IO_STATUS_NORMAL)
196     {
197       TRACE_LOG1 ("status %d", status);
198       nread = -1;
199       saved_errno = EIO;
200     }
201
202   TRACE_LOGBUF (buffer, nread);
203
204   errno = saved_errno;
205   return TRACE_SYSRES (nread);
206 }
207
208
209 int
210 _gpgme_io_write (int fd, const void *buffer, size_t count)
211 {
212   int saved_errno = 0;
213   gsize nwritten;
214   GIOChannel *chan;
215   GIOStatus status;
216   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
217               "buffer=%p, count=%u", buffer, count);
218   TRACE_LOGBUF (buffer, count);
219
220   chan = find_channel (fd, 0);
221   if (!chan)
222     {
223       TRACE_LOG ("fd %d: no channel registered");
224       errno = EINVAL;
225       return -1;
226     }
227
228   status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
229                                      &nwritten, NULL);
230   if (status != G_IO_STATUS_NORMAL)
231     {
232       nwritten = -1;
233       saved_errno = EIO;
234     }
235   errno = saved_errno;
236
237   return TRACE_SYSRES (nwritten);
238 }
239
240
241 int
242 _gpgme_io_pipe (int filedes[2], int inherit_idx)
243 {
244   GIOChannel *chan;
245   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
246               "inherit_idx=%i (GPGME uses it for %s)",
247               inherit_idx, inherit_idx ? "reading" : "writing");
248
249 #define PIPEBUF_SIZE  4096
250   if (_pipe (filedes, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
251     return TRACE_SYSRES (-1);
252
253   /* Make one end inheritable. */
254   if (inherit_idx == 0)
255     {
256       int new_read;
257
258       new_read = _dup (filedes[0]);
259       _close (filedes[0]);
260       filedes[0] = new_read;
261
262       if (new_read < 0)
263         {
264           _close (filedes[1]);
265           return TRACE_SYSRES (-1);
266         }
267     }
268   else if (inherit_idx == 1)
269     {
270       int new_write;
271
272       new_write = _dup (filedes[1]);
273       _close (filedes[1]);
274       filedes[1] = new_write;
275
276       if (new_write < 0)
277         {
278           _close (filedes[0]);
279           return TRACE_SYSRES (-1);
280         }
281     }
282
283   /* Now we have a pipe with the right end inheritable.  The other end
284      should have a giochannel.  */
285   chan = find_channel (filedes[1 - inherit_idx], 1);
286   if (!chan)
287     {
288       int saved_errno = errno;
289       _close (filedes[0]);
290       _close (filedes[1]);
291       errno = saved_errno;
292       return TRACE_SYSRES (-1);
293     }
294
295   return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
296           filedes[0], (HANDLE) _get_osfhandle (filedes[0]),
297           filedes[1], (HANDLE) _get_osfhandle (filedes[1]),
298           chan);
299 }
300
301
302 int
303 _gpgme_io_close (int fd)
304 {
305   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
306
307   if (fd < 0 || fd >= MAX_SLAFD)
308     {
309       errno = EBADF;
310       return TRACE_SYSRES (-1);
311     }
312
313   /* First call the notify handler.  */
314   if (notify_table[fd].handler)
315     {
316       notify_table[fd].handler (fd, notify_table[fd].value);
317       notify_table[fd].handler = NULL;
318       notify_table[fd].value = NULL;
319     }
320
321   /* Then do the close.  */    
322   if (giochannel_table[fd].chan)
323     {
324       if (giochannel_table[fd].primary)
325         g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
326       else
327         _close (fd);
328
329       g_io_channel_unref (giochannel_table[fd].chan);
330       giochannel_table[fd].chan = NULL;
331     }
332   else
333     _close (fd);
334
335   TRACE_SUC ();
336   return 0;
337 }
338
339
340 int
341 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
342                             void *value)
343 {
344   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
345               "close_handler=%p/%p", handler, value);
346
347   assert (fd != -1);
348
349   if (fd < 0 || fd >= (int) DIM (notify_table))
350     {
351       errno = EINVAL;
352       return TRACE_SYSRES (-1);
353     }
354   notify_table[fd].handler = handler;
355   notify_table[fd].value = value;
356   return TRACE_SYSRES (0);
357 }
358
359
360 int
361 _gpgme_io_set_nonblocking (int fd)
362 {
363   GIOChannel *chan;
364   GIOStatus status;
365  
366   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
367
368   chan = find_channel (fd, 0);
369   if (!chan)
370     {
371       errno = EIO;
372       return TRACE_SYSRES (-1);
373     }
374
375    status = g_io_channel_set_flags (chan,
376                                    g_io_channel_get_flags (chan) |
377                                    G_IO_FLAG_NONBLOCK, NULL);
378   if (status != G_IO_STATUS_NORMAL)
379     {
380 #if 0
381       /* glib 1.9.2 does not implement set_flags and returns an
382          error.  */
383       errno = EIO;
384       return TRACE_SYSRES (-1);
385 #else
386       TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
387                   status);
388 #endif
389     }
390
391   return TRACE_SYSRES (0);
392 }
393
394
395 static char *
396 build_commandline (char **argv)
397 {
398   int i;
399   int n = 0;
400   char *buf;
401   char *p;
402   
403   /* We have to quote some things because under Windows the program
404      parses the commandline and does some unquoting.  We enclose the
405      whole argument in double-quotes, and escape literal double-quotes
406      as well as backslashes with a backslash.  We end up with a
407      trailing space at the end of the line, but that is harmless.  */
408   for (i = 0; argv[i]; i++)
409     {
410       p = argv[i];
411       /* The leading double-quote.  */
412       n++;
413       while (*p)
414         {
415           /* An extra one for each literal that must be escaped.  */
416           if (*p == '\\' || *p == '"')
417             n++;
418           n++;
419           p++;
420         }
421       /* The trailing double-quote and the delimiter.  */
422       n += 2;
423     }
424   /* And a trailing zero.  */
425   n++;
426
427   buf = p = malloc (n);
428   if (!buf)
429     return NULL;
430   for (i = 0; argv[i]; i++)
431     {
432       char *argvp = argv[i];
433
434       *(p++) = '"';
435       while (*argvp)
436         {
437           if (*argvp == '\\' || *argvp == '"')
438             *(p++) = '\\';
439           *(p++) = *(argvp++);
440         }
441       *(p++) = '"';
442       *(p++) = ' ';
443     }
444   *(p++) = 0;
445
446   return buf;
447 }
448
449
450 int
451 _gpgme_io_spawn (const char *path, char **argv,
452                  struct spawn_fd_item_s *fd_list, pid_t *r_pid)
453 {
454   SECURITY_ATTRIBUTES sec_attr;
455   PROCESS_INFORMATION pi =
456     {
457       NULL,      /* returns process handle */
458       0,         /* returns primary thread handle */
459       0,         /* returns pid */
460       0          /* returns tid */
461     };
462   STARTUPINFO si;
463   int cr_flags = CREATE_DEFAULT_ERROR_MODE
464     | GetPriorityClass (GetCurrentProcess ());
465   int i;
466   char **args;
467   char *arg_string;
468   /* FIXME.  */
469   int debug_me = 0;
470   int tmp_fd;
471   char *tmp_name;
472
473   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
474               "path=%s", path);
475   i = 0;
476   while (argv[i])
477     {
478       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
479       i++;
480     }
481   
482   /* We do not inherit any handles by default, and just insert those
483      handles we want the child to have afterwards.  But some handle
484      values occur on the command line, and we need to move
485      stdin/out/err to the right location.  So we use a wrapper program
486      which gets the information from a temporary file.  */
487   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
488     {
489       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
490       return TRACE_SYSRES (-1);
491     }
492   TRACE_LOG1 ("tmp_name = %s", tmp_name);
493
494   args = calloc (2 + i + 1, sizeof (*args));
495   args[0] = (char *) _gpgme_get_w32spawn_path ();
496   args[1] = tmp_name;
497   args[2] = path;
498   memcpy (&args[3], &argv[1], i * sizeof (*args));
499
500   memset (&sec_attr, 0, sizeof sec_attr);
501   sec_attr.nLength = sizeof sec_attr;
502   sec_attr.bInheritHandle = FALSE;
503   
504   arg_string = build_commandline (args);
505   free (args);
506   if (!arg_string)
507     {
508       close (tmp_fd);
509       DeleteFile (tmp_name);
510       return TRACE_SYSRES (-1);
511     }
512
513   memset (&si, 0, sizeof si);
514   si.cb = sizeof (si);
515   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
516   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
517   si.hStdInput = INVALID_HANDLE_VALUE;
518   si.hStdOutput = INVALID_HANDLE_VALUE;
519   si.hStdError = INVALID_HANDLE_VALUE;
520
521   cr_flags |= CREATE_SUSPENDED;
522   cr_flags |= DETACHED_PROCESS;
523   if (!CreateProcessA (_gpgme_get_w32spawn_path (),
524                        arg_string,
525                        &sec_attr,     /* process security attributes */
526                        &sec_attr,     /* thread security attributes */
527                        FALSE,         /* inherit handles */
528                        cr_flags,      /* creation flags */
529                        NULL,          /* environment */
530                        NULL,          /* use current drive/directory */
531                        &si,           /* startup information */
532                        &pi))          /* returns process information */
533     {
534       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
535       free (arg_string);
536       close (tmp_fd);
537       DeleteFile (tmp_name);
538
539       /* FIXME: Should translate the error code.  */
540       errno = EIO;
541       return TRACE_SYSRES (-1);
542     }
543
544   free (arg_string);
545   
546   /* Insert the inherited handles.  */
547   for (i = 0; fd_list[i].fd != -1; i++)
548     {
549       HANDLE hd;
550
551       /* Make it inheritable for the wrapper process.  */
552       if (!DuplicateHandle (GetCurrentProcess(), _get_osfhandle (fd_list[i].fd),
553                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
554         {
555           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
556           TerminateProcess (pi.hProcess, 0);
557           /* Just in case TerminateProcess didn't work, let the
558              process fail on its own.  */
559           ResumeThread (pi.hThread);
560           CloseHandle (pi.hThread);
561           CloseHandle (pi.hProcess);
562
563           close (tmp_fd);
564           DeleteFile (tmp_name);
565
566           /* FIXME: Should translate the error code.  */
567           errno = EIO;
568           return TRACE_SYSRES (-1);
569         }
570       /* Return the child name of this handle.  */
571       fd_list[i].peer_name = (int) hd;
572     }
573
574   /* Write the handle translation information to the temporary
575      file.  */
576   {
577     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
578        notation: "0xFEDCBA9876543210" with an extra white space after
579        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
580        for a time when a HANDLE is 64 bit.  */
581 #define BUFFER_MAX 800
582     char line[BUFFER_MAX + 1];
583     int res;
584     int written;
585     size_t len;
586
587     line[0] = '\n';
588     line[1] = '\0';
589     for (i = 0; fd_list[i].fd != -1; i++)
590       {
591         /* Strip the newline.  */
592         len = strlen (line) - 1;
593         
594         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
595         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
596                   fd_list[i].fd, fd_list[i].dup_to,
597                   fd_list[i].peer_name, fd_list[i].arg_loc);
598         /* Rather safe than sorry.  */
599         line[BUFFER_MAX - 1] = '\n';
600         line[BUFFER_MAX] = '\0';
601       }
602     len = strlen (line);
603     written = 0;
604     do
605       {
606         res = write (tmp_fd, &line[written], len - written);
607         if (res > 0)
608           written += res;
609       }
610     while (res > 0 || (res < 0 && errno == EAGAIN));
611   }
612   close (tmp_fd);
613   /* The temporary file is deleted by the gpgme-w32spawn process
614      (hopefully).  */
615     
616   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
617               "dwProcessID=%d, dwThreadId=%d",
618               pi.hProcess, pi.hThread, 
619               (int) pi.dwProcessId, (int) pi.dwThreadId);
620   
621   if (r_pid)
622     *r_pid = (pid_t)pi.dwProcessId;
623
624   if (ResumeThread (pi.hThread) < 0)
625     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
626   
627   if (!CloseHandle (pi.hThread))
628     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
629                 (int) GetLastError ());
630
631   TRACE_LOG1 ("process=%p", pi.hProcess);
632
633   /* We don't need to wait for the process.  */
634   if (!CloseHandle (pi.hProcess))
635     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
636                 (int) GetLastError ());
637
638   for (i = 0; fd_list[i].fd != -1; i++)
639     _gpgme_io_close (fd_list[i].fd);
640
641   for (i = 0; fd_list[i].fd != -1; i++)
642     if (fd_list[i].dup_to == -1)
643       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
644                   fd_list[i].peer_name);
645     else
646       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
647                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
648                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
649
650   return TRACE_SYSRES (0);
651 }
652
653
654 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
655    nothing to select, > 0 = number of signaled fds.  */
656 int
657 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
658 {
659   int npollfds;
660   GPollFD *pollfds;
661   int *pollfds_map; 
662   int i;
663   int j;
664   int any;
665   int n;
666   int count;
667   /* Use a 1s timeout.  */
668   int timeout = 1000;
669   void *dbg_help = NULL;
670   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
671               "nfds=%u, nonblock=%u", nfds, nonblock);
672
673   if (nonblock)
674     timeout = 0;
675
676   pollfds = calloc (nfds, sizeof *pollfds);
677   if (!pollfds)
678     return -1;
679   pollfds_map = calloc (nfds, sizeof *pollfds_map);
680   if (!pollfds_map)
681     {
682       free (pollfds);
683       return -1;
684     }
685   npollfds = 0;
686
687   TRACE_SEQ (dbg_help, "select on [ ");
688   any = 0;
689   for (i = 0; i < nfds; i++)
690     {
691       GIOChannel *chan = NULL;
692
693       if (fds[i].fd == -1) 
694         continue;
695
696       if ((fds[i].for_read || fds[i].for_write)
697           && !(chan = find_channel (fds[i].fd, 0)))
698         {
699           TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
700           TRACE_END (dbg_help, "]"); 
701           assert (!"see log file");
702         }
703       else if (fds[i].for_read )
704         {
705           assert(chan);
706           g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
707           pollfds_map[npollfds] = i;
708           TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
709           npollfds++;
710           any = 1;
711         }
712       else if (fds[i].for_write)
713         {
714           assert(chan);
715           g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
716           pollfds_map[npollfds] = i;
717           TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
718           npollfds++;
719           any = 1;
720         }
721       fds[i].signaled = 0;
722     }
723   TRACE_END (dbg_help, "]"); 
724   if (!any)
725     {
726       count = 0;
727       goto leave;
728     }
729
730
731   count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
732   if (count < 0)
733     {
734       int saved_errno = errno;
735       errno = saved_errno;
736       goto leave;
737     }
738
739   TRACE_SEQ (dbg_help, "select OK [ ");
740   if (TRACE_ENABLED (dbg_help))
741     {
742       for (i = 0; i < npollfds; i++)
743         {
744           if ((pollfds[i].revents & G_IO_IN))
745             TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
746           if ((pollfds[i].revents & G_IO_OUT))
747             TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
748         }
749       TRACE_END (dbg_help, "]");
750     }
751     
752   /* COUNT is used to stop the lop as soon as possible.  */
753   for (n = count, i = 0; i < npollfds && n; i++)
754     {
755       j = pollfds_map[i];
756       assert (j >= 0 && j < nfds);
757       if (fds[j].fd == -1)
758         ;
759       else if (fds[j].for_read)
760         {
761           if ((pollfds[i].revents & G_IO_IN))
762             {
763               fds[j].signaled = 1;
764               n--;
765             }
766         }
767       else if (fds[j].for_write)
768         {
769           if ((pollfds[i].revents & G_IO_OUT))
770             {
771               fds[j].signaled = 1;
772               n--;
773             }
774         }
775     }
776
777 leave:
778   free (pollfds);
779   free (pollfds_map);
780   return TRACE_SYSRES (count);
781 }
782
783
784 int
785 _gpgme_io_dup (int fd)
786 {
787   int newfd;
788   GIOChannel *chan;
789   
790   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "dup (%d)", fd);
791
792   newfd = _dup (fd);
793   if (newfd == -1)
794     return TRACE_SYSRES (-1);
795   if (newfd < 0 || newfd >= MAX_SLAFD)
796     {
797       /* New FD won't fit into our table.  */
798       _close (newfd);
799       errno = EIO; 
800       return TRACE_SYSRES (-1);
801     }
802   assert (giochannel_table[newfd].chan == NULL);
803
804   chan = find_channel (fd, 0);
805   assert (chan);
806
807   g_io_channel_ref (chan);
808   giochannel_table[newfd].chan = chan;
809   giochannel_table[newfd].primary = 0;
810
811   return TRACE_SYSRES (newfd);
812 }