3204b5785bbcb3de89a299b800f9744e2f1dace6
[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_child_list,
453                  struct spawn_fd_item_s *fd_parent_list)
454 {
455   SECURITY_ATTRIBUTES sec_attr;
456   PROCESS_INFORMATION pi =
457     {
458       NULL,      /* returns process handle */
459       0,         /* returns primary thread handle */
460       0,         /* returns pid */
461       0         /* returns tid */
462     };
463   STARTUPINFO si;
464   char *envblock = NULL;
465   int cr_flags = CREATE_DEFAULT_ERROR_MODE
466     | GetPriorityClass (GetCurrentProcess ());
467   int i;
468   char *arg_string;
469   int duped_stdin = 0;
470   int duped_stderr = 0;
471   HANDLE hnul = INVALID_HANDLE_VALUE;
472   /* FIXME.  */
473   int debug_me = 0;
474   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
475               "path=%s", path);
476   i = 0;
477   while (argv[i])
478     {
479       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
480       i++;
481     }
482   
483   memset (&sec_attr, 0, sizeof sec_attr);
484   sec_attr.nLength = sizeof sec_attr;
485   sec_attr.bInheritHandle = FALSE;
486   
487   arg_string = build_commandline (argv);
488   if (!arg_string)
489     return TRACE_SYSRES (-1);
490   
491   memset (&si, 0, sizeof si);
492   si.cb = sizeof (si);
493   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
494   si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
495   si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
496   si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
497   si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
498   
499   for (i = 0; fd_child_list[i].fd != -1; i++)
500     {
501       if (fd_child_list[i].dup_to == 0)
502         {
503           si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
504           TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd,
505                       _get_osfhandle (fd_child_list[i].fd));
506           duped_stdin = 1;
507         }
508       else if (fd_child_list[i].dup_to == 1)
509         {
510           si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
511           TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd,
512                       _get_osfhandle (fd_child_list[i].fd));
513         }
514       else if (fd_child_list[i].dup_to == 2)
515         {
516           si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
517           TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd,
518                       _get_osfhandle (fd_child_list[i].fd));
519           duped_stderr = 1;
520         }
521     }
522   
523   if (!duped_stdin || !duped_stderr)
524     {
525       SECURITY_ATTRIBUTES sa;
526       
527       memset (&sa, 0, sizeof sa);
528       sa.nLength = sizeof sa;
529       sa.bInheritHandle = TRUE;
530       hnul = CreateFile ("nul",
531                          GENERIC_READ|GENERIC_WRITE,
532                          FILE_SHARE_READ|FILE_SHARE_WRITE,
533                          &sa,
534                          OPEN_EXISTING,
535                          FILE_ATTRIBUTE_NORMAL,
536                          NULL);
537       if (hnul == INVALID_HANDLE_VALUE)
538         {
539           TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d",
540                       (int) GetLastError ());
541           free (arg_string);
542           /* FIXME: Should translate the error code.  */
543           errno = EIO;
544           return TRACE_SYSRES (-1);
545         }
546       /* Make sure that the process has a connected stdin.  */
547       if (!duped_stdin)
548         {
549           si.hStdInput = hnul;
550           TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul);
551         }
552       /* We normally don't want all the normal output.  */
553       if (!duped_stderr)
554         {
555           si.hStdError = hnul;
556           TRACE_LOG1 ("using %d for dummy stderr", (int)hnul);
557         }
558     }
559   
560   cr_flags |= CREATE_SUSPENDED;
561   cr_flags |= DETACHED_PROCESS;
562   if (!CreateProcessA (path,
563                        arg_string,
564                        &sec_attr,     /* process security attributes */
565                        &sec_attr,     /* thread security attributes */
566                        TRUE,          /* inherit handles */
567                        cr_flags,      /* creation flags */
568                        envblock,      /* environment */
569                        NULL,          /* use current drive/directory */
570                        &si,           /* startup information */
571                        &pi))          /* returns process information */
572     {
573       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
574       free (arg_string);
575       /* FIXME: Should translate the error code.  */
576       errno = EIO;
577       return TRACE_SYSRES (-1);
578     }
579   
580   /* Close the /dev/nul handle if used.  */
581   if (hnul != INVALID_HANDLE_VALUE)
582     {
583       if (!CloseHandle (hnul))
584         TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)",
585                     (int) GetLastError ());
586     }
587   
588   /* Close the other ends of the pipes.  */
589   for (i = 0; fd_parent_list[i].fd != -1; i++)
590     _gpgme_io_close (fd_parent_list[i].fd);
591   
592   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
593               "dwProcessID=%d, dwThreadId=%d",
594               pi.hProcess, pi.hThread, 
595               (int) pi.dwProcessId, (int) pi.dwThreadId);
596   
597   if (ResumeThread (pi.hThread) < 0)
598     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
599   
600   if (!CloseHandle (pi.hThread))
601     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
602                 (int) GetLastError ());
603
604   TRACE_SUC1 ("process=%p", pi.hProcess);
605
606   /* We don't need to wait for the process. */
607   CloseHandle (pi.hProcess);
608
609   return TRACE_SYSRES (0);
610 }
611
612
613 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
614    nothing to select, > 0 = number of signaled fds.  */
615 int
616 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
617 {
618   int npollfds;
619   GPollFD *pollfds;
620   int *pollfds_map; 
621   int i;
622   int j;
623   int any;
624   int n;
625   int count;
626   /* Use a 1s timeout.  */
627   int timeout = 1000;
628   void *dbg_help = NULL;
629   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
630               "nfds=%u, nonblock=%u", nfds, nonblock);
631
632   if (nonblock)
633     timeout = 0;
634
635   pollfds = calloc (nfds, sizeof *pollfds);
636   if (!pollfds)
637     return -1;
638   pollfds_map = calloc (nfds, sizeof *pollfds_map);
639   if (!pollfds_map)
640     {
641       free (pollfds);
642       return -1;
643     }
644   npollfds = 0;
645
646   TRACE_SEQ (dbg_help, "select on [ ");
647   any = 0;
648   for (i = 0; i < nfds; i++)
649     {
650       GIOChannel *chan = NULL;
651
652       if (fds[i].fd == -1) 
653         continue;
654
655       if ((fds[i].for_read || fds[i].for_write)
656           && !(chan = find_channel (fds[i].fd, 0)))
657         {
658           TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
659           TRACE_END (dbg_help, "]"); 
660           assert (!"see log file");
661         }
662       else if (fds[i].for_read )
663         {
664           assert(chan);
665           g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
666           pollfds_map[npollfds] = i;
667           TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
668           npollfds++;
669           any = 1;
670         }
671       else if (fds[i].for_write)
672         {
673           assert(chan);
674           g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
675           pollfds_map[npollfds] = i;
676           TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
677           npollfds++;
678           any = 1;
679         }
680       fds[i].signaled = 0;
681     }
682   TRACE_END (dbg_help, "]"); 
683   if (!any)
684     {
685       count = 0;
686       goto leave;
687     }
688
689
690   count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
691   if (count < 0)
692     {
693       int saved_errno = errno;
694       errno = saved_errno;
695       goto leave;
696     }
697
698   TRACE_SEQ (dbg_help, "select OK [ ");
699   if (TRACE_ENABLED (dbg_help))
700     {
701       for (i = 0; i < npollfds; i++)
702         {
703           if ((pollfds[i].revents & G_IO_IN))
704             TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
705           if ((pollfds[i].revents & G_IO_OUT))
706             TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
707         }
708       TRACE_END (dbg_help, "]");
709     }
710     
711   /* COUNT is used to stop the lop as soon as possible.  */
712   for (n = count, i = 0; i < npollfds && n; i++)
713     {
714       j = pollfds_map[i];
715       assert (j >= 0 && j < nfds);
716       if (fds[j].fd == -1)
717         ;
718       else if (fds[j].for_read)
719         {
720           if ((pollfds[i].revents & G_IO_IN))
721             {
722               fds[j].signaled = 1;
723               n--;
724             }
725         }
726       else if (fds[j].for_write)
727         {
728           if ((pollfds[i].revents & G_IO_OUT))
729             {
730               fds[j].signaled = 1;
731               n--;
732             }
733         }
734     }
735
736 leave:
737   free (pollfds);
738   free (pollfds_map);
739   return TRACE_SYSRES (count);
740 }
741
742
743 int
744 _gpgme_io_dup (int fd)
745 {
746   int newfd;
747   GIOChannel *chan;
748   
749   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "dup (%d)", fd);
750
751   newfd = _dup (fd);
752   if (newfd == -1)
753     return TRACE_SYSRES (-1);
754   if (newfd < 0 || newfd >= MAX_SLAFD)
755     {
756       /* New FD won't fit into our table.  */
757       _close (newfd);
758       errno = EIO; 
759       return TRACE_SYSRES (-1);
760     }
761   assert (giochannel_table[newfd].chan == NULL);
762
763   chan = find_channel (fd, 0);
764   assert (chan);
765
766   g_io_channel_ref (chan);
767   giochannel_table[newfd].chan = chan;
768   giochannel_table[newfd].primary = 0;
769
770   return TRACE_SYSRES (newfd);
771 }