6b7cd15062b1adb844a29edfcce935a6e84fee90
[gpgme.git] / src / 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 <fcntl.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
36 #endif
37 #include <sys/types.h>
38 #include <glib.h>
39 #include <windows.h>
40 #include <io.h>
41
42 #include "util.h"
43 #include "priv-io.h"
44 #include "sema.h"
45 #include "debug.h"
46
47 #ifndef O_BINARY
48 #ifdef _O_BINARY
49 #define O_BINARY        _O_BINARY
50 #else
51 #define O_BINARY        0
52 #endif
53 #endif
54
55 \f
56 /* This file is an ugly hack to get GPGME working with glib on Windows
57    targets.  On Windows, you can not select() on file descriptors.
58    The only way to check if there is something to read is to read
59    something.  This means that GPGME can not let glib check for data
60    without letting glib also handle the data on Windows targets.
61
62    The ugly consequence is that we need to work on GIOChannels in
63    GPGME, creating a glib dependency.  Also, we need to export an
64    interface for the application to get at GPGME's GIOChannel.  There
65    is no good way to abstract all this with callbacks, because the
66    whole thing is also interconnected with the creation of pipes and
67    child processes.
68
69    The following rule applies only to this I/O backend:
70
71    * ALL operations must use the user defined event loop.  GPGME can
72    not anymore provide its own event loop.  This is mostly a sanity
73    requirement: Although we have in theory all information we need to
74    make the GPGME W32 code for select still work, it would be a big
75    complication and require changes throughout GPGME.
76
77    Eventually, we probably have to bite the bullet and make some
78    really nice callback interfaces to let the user control all this at
79    a per-context level.  */
80
81 \f
82 #define MAX_SLAFD 256
83
84 static struct 
85 {
86   int used;
87
88   /* If this is not -1, then it's a libc file descriptor.  */
89   int fd;
90   /* If fd is -1, this is the Windows socket handle.  */
91   int socket;
92
93   GIOChannel *chan;
94   /* The boolean PRIMARY is true if this file descriptor caused the
95      allocation of CHAN.  Only then should CHAN be destroyed when this
96      FD is closed.  This, together with the fact that dup'ed file
97      descriptors are closed before the file descriptors from which
98      they are dup'ed are closed, ensures that CHAN is always valid,
99      and shared among all file descriptors refering to the same
100      underlying object.
101
102      The logic behind this is that there is only one reason for us to
103      dup file descriptors anyway: to allow simpler book-keeping of
104      file descriptors shared between GPGME and libassuan, which both
105      want to close something.  Using the same channel for these
106      duplicates works just fine (and in fact, using different channels
107      does not work because the W32 backend in glib does not support
108      that: One would end up with several competing reader/writer
109      threads.  */
110   int primary;
111 } giochannel_table[MAX_SLAFD];
112
113
114 static GIOChannel *
115 find_channel (int fd)
116 {
117   if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
118     return NULL;
119
120   return giochannel_table[fd].chan;
121 }
122
123
124 /* Returns the FD or -1 on resource limit.  */
125 int
126 new_dummy_channel_from_fd (int cfd)
127 {
128   int idx;
129
130   for (idx = 0; idx < MAX_SLAFD; idx++)
131     if (! giochannel_table[idx].used)
132       break;
133
134   if (idx == MAX_SLAFD)
135     {
136       errno = EIO;
137       return -1;
138     }
139
140   giochannel_table[idx].used = 1;
141   giochannel_table[idx].chan = NULL;
142   giochannel_table[idx].fd = cfd;
143   giochannel_table[idx].socket = INVALID_SOCKET;
144   giochannel_table[idx].primary = 1;
145
146   return idx;
147 }
148
149
150 /* Returns the FD or -1 on resource limit.  */
151 int
152 new_channel_from_fd (int cfd)
153 {
154   int idx;
155
156   for (idx = 0; idx < MAX_SLAFD; idx++)
157     if (! giochannel_table[idx].used)
158       break;
159
160   if (idx == MAX_SLAFD)
161     {
162       errno = EIO;
163       return -1;
164     }
165
166   giochannel_table[idx].used = 1;
167   giochannel_table[idx].chan = g_io_channel_win32_new_fd (cfd);
168   giochannel_table[idx].fd = cfd;
169   giochannel_table[idx].socket = INVALID_SOCKET;
170   giochannel_table[idx].primary = 1;
171
172   g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
173   g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
174
175   return idx;
176 }
177
178
179 /* Returns the FD or -1 on resource limit.  */
180 int
181 new_channel_from_socket (int sock)
182 {
183   int idx;
184
185   for (idx = 0; idx < MAX_SLAFD; idx++)
186     if (! giochannel_table[idx].used)
187       break;
188
189   if (idx == MAX_SLAFD)
190     {
191       errno = EIO;
192       return -1;
193     }
194
195   giochannel_table[idx].used = 1;
196   giochannel_table[idx].chan = g_io_channel_win32_new_socket (sock);
197   giochannel_table[idx].fd = -1;
198   giochannel_table[idx].socket = sock;
199   giochannel_table[idx].primary = 1;
200
201   g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
202   g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
203
204   return idx;
205 }
206
207
208 /* Compatibility interface.  Obsolete.  */
209 void *
210 gpgme_get_giochannel (int fd)
211 {
212   return find_channel (fd);
213 }
214
215
216 /* Look up the giochannel for "file descriptor" FD.  */
217 void *
218 gpgme_get_fdptr (int fd)
219 {
220   return find_channel (fd);
221 }
222
223
224 /* Write the printable version of FD to the buffer BUF of length
225    BUFLEN.  The printable version is the representation on the command
226    line that the child process expects.  */
227 int
228 _gpgme_io_fd2str (char *buf, int buflen, int fd)
229 {
230   HANDLE hndl;
231     
232   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd);
233   if (giochannel_table[fd].fd != -1)
234     hndl = (HANDLE) _get_osfhandle (giochannel_table[fd].fd);
235   else
236     hndl = (HANDLE) giochannel_table[fd].socket;
237
238   TRACE_SUC1 ("syshd=%p", hndl);
239   
240   return snprintf (buf, buflen, "%d", (int) hndl);
241 }
242
243 \f
244 void
245 _gpgme_io_subsystem_init (void)
246 {
247 }
248
249 \f
250 static struct
251 {
252   _gpgme_close_notify_handler_t handler;
253   void *value;
254 } notify_table[MAX_SLAFD];
255
256
257 int
258 _gpgme_io_read (int fd, void *buffer, size_t count)
259 {
260   int saved_errno = 0;
261   gsize nread;
262   GIOChannel *chan;
263   GIOStatus status;
264   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
265               "buffer=%p, count=%u", buffer, count);
266
267   chan = find_channel (fd);
268   if (!chan)
269     {
270       TRACE_LOG ("no channel registered");
271       errno = EINVAL;
272       return TRACE_SYSRES (-1);
273     }
274   TRACE_LOG1 ("channel %p", chan);
275
276   {
277     GError *err = NULL;
278     status = g_io_channel_read_chars (chan, (gchar *) buffer,
279                                       count, &nread, &err);
280     if (err)
281       {
282         TRACE_LOG2 ("status %i, err %s", status, err->message);
283         g_error_free (err);
284       }
285   }
286
287   if (status == G_IO_STATUS_EOF)
288     nread = 0;
289   else if (status == G_IO_STATUS_AGAIN)
290     {
291       nread = -1;
292       saved_errno = EAGAIN;
293     }
294   else if (status != G_IO_STATUS_NORMAL)
295     {
296       TRACE_LOG1 ("status %d", status);
297       nread = -1;
298       saved_errno = EIO;
299     }
300   
301   if (nread != 0 && nread != -1)
302     TRACE_LOGBUF (buffer, nread);
303
304   errno = saved_errno;
305   return TRACE_SYSRES (nread);
306 }
307
308
309 int
310 _gpgme_io_write (int fd, const void *buffer, size_t count)
311 {
312   int saved_errno = 0;
313   gsize nwritten;
314   GIOChannel *chan;
315   GIOStatus status;
316   GError *err = NULL;
317
318   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
319               "buffer=%p, count=%u", buffer, count);
320   TRACE_LOGBUF (buffer, count);
321
322   chan = find_channel (fd);
323   if (!chan)
324     {
325       TRACE_LOG ("fd %d: no channel registered");
326       errno = EINVAL;
327       return -1;
328     }
329
330   status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
331                                      &nwritten, &err);
332   if (err)
333     {
334       TRACE_LOG1 ("write error: %s", err->message);
335       g_error_free (err);
336     }
337
338   if (status == G_IO_STATUS_AGAIN)
339     {
340       nwritten = -1;
341       saved_errno = EAGAIN;
342     }
343   else if (status != G_IO_STATUS_NORMAL)
344     {
345       nwritten = -1;
346       saved_errno = EIO;
347     }
348   errno = saved_errno;
349
350   return TRACE_SYSRES (nwritten);
351 }
352
353
354 int
355 _gpgme_io_pipe (int filedes[2], int inherit_idx)
356 {
357   int fds[2];
358
359   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
360               "inherit_idx=%i (GPGME uses it for %s)",
361               inherit_idx, inherit_idx ? "reading" : "writing");
362
363 #define PIPEBUF_SIZE  4096
364   if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
365     return TRACE_SYSRES (-1);
366
367   /* Make one end inheritable. */
368   if (inherit_idx == 0)
369     {
370       int new_read;
371
372       new_read = _dup (fds[0]);
373       _close (fds[0]);
374       fds[0] = new_read;
375
376       if (new_read < 0)
377         {
378           _close (fds[1]);
379           return TRACE_SYSRES (-1);
380         }
381     }
382   else if (inherit_idx == 1)
383     {
384       int new_write;
385
386       new_write = _dup (fds[1]);
387       _close (fds[1]);
388       fds[1] = new_write;
389
390       if (new_write < 0)
391         {
392           _close (fds[0]);
393           return TRACE_SYSRES (-1);
394         }
395     }
396
397   /* For _gpgme_io_close.  */
398   filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]);
399   if (filedes[inherit_idx] < 0)
400     {
401       int saved_errno = errno;
402       
403       _close (fds[0]);
404       _close (fds[1]);
405       errno = saved_errno;
406       return TRACE_SYSRES (-1);
407     }
408
409   /* Now we have a pipe with the correct end inheritable.  The other end
410      should have a giochannel.  */
411   filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]);
412   if (filedes[1 - inherit_idx] < 0)
413     {
414       int saved_errno = errno;
415       
416       _gpgme_io_close (fds[inherit_idx]);
417       _close (fds[1 - inherit_idx]);
418       errno = saved_errno;
419       return TRACE_SYSRES (-1);
420     }
421   
422   return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
423                      filedes[0],
424                      (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
425                      filedes[1],
426                      (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
427                      giochannel_table[1 - inherit_idx].chan);
428 }
429
430
431 int
432 _gpgme_io_close (int fd)
433 {
434   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
435
436   if (fd < 0 || fd >= MAX_SLAFD)
437     {
438       errno = EBADF;
439       return TRACE_SYSRES (-1);
440     }
441
442   assert (giochannel_table[fd].used);
443
444   /* First call the notify handler.  */
445   if (notify_table[fd].handler)
446     {
447       notify_table[fd].handler (fd, notify_table[fd].value);
448       notify_table[fd].handler = NULL;
449       notify_table[fd].value = NULL;
450     }
451
452   /* Then do the close.  */
453   if (giochannel_table[fd].chan)
454     {
455       if (giochannel_table[fd].primary)
456         g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
457       
458       g_io_channel_unref (giochannel_table[fd].chan);
459     }
460   else
461     {
462       /* Dummy entry, just close.  */
463       assert (giochannel_table[fd].fd != -1);
464       _close (giochannel_table[fd].fd);
465     }
466         
467   giochannel_table[fd].used = 0;
468   giochannel_table[fd].fd = -1;
469   giochannel_table[fd].socket = INVALID_SOCKET;
470   giochannel_table[fd].chan = NULL;
471   giochannel_table[fd].primary = 0;
472
473   TRACE_SUC ();
474   return 0;
475 }
476
477
478 int
479 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
480                             void *value)
481 {
482   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
483               "close_handler=%p/%p", handler, value);
484
485   assert (fd != -1);
486
487   if (fd < 0 || fd >= (int) DIM (notify_table))
488     {
489       errno = EINVAL;
490       return TRACE_SYSRES (-1);
491     }
492   notify_table[fd].handler = handler;
493   notify_table[fd].value = value;
494   return TRACE_SYSRES (0);
495 }
496
497
498 int
499 _gpgme_io_set_nonblocking (int fd)
500 {
501   GIOChannel *chan;
502   GIOStatus status;
503  
504   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
505
506   chan = find_channel (fd);
507   if (!chan)
508     {
509       errno = EIO;
510       return TRACE_SYSRES (-1);
511     }
512
513   status = g_io_channel_set_flags (chan,
514                                    g_io_channel_get_flags (chan) |
515                                    G_IO_FLAG_NONBLOCK, NULL);
516
517   if (status != G_IO_STATUS_NORMAL)
518     {
519 #if 0
520       /* glib 1.9.2 does not implement set_flags and returns an
521          error.  */
522       errno = EIO;
523       return TRACE_SYSRES (-1);
524 #else
525       TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
526                   status);
527 #endif
528     }
529
530   return TRACE_SYSRES (0);
531 }
532
533
534 static char *
535 build_commandline (char **argv)
536 {
537   int i;
538   int n = 0;
539   char *buf;
540   char *p;
541   
542   /* We have to quote some things because under Windows the program
543      parses the commandline and does some unquoting.  We enclose the
544      whole argument in double-quotes, and escape literal double-quotes
545      as well as backslashes with a backslash.  We end up with a
546      trailing space at the end of the line, but that is harmless.  */
547   for (i = 0; argv[i]; i++)
548     {
549       p = argv[i];
550       /* The leading double-quote.  */
551       n++;
552       while (*p)
553         {
554           /* An extra one for each literal that must be escaped.  */
555           if (*p == '\\' || *p == '"')
556             n++;
557           n++;
558           p++;
559         }
560       /* The trailing double-quote and the delimiter.  */
561       n += 2;
562     }
563   /* And a trailing zero.  */
564   n++;
565
566   buf = p = malloc (n);
567   if (!buf)
568     return NULL;
569   for (i = 0; argv[i]; i++)
570     {
571       char *argvp = argv[i];
572
573       *(p++) = '"';
574       while (*argvp)
575         {
576           if (*argvp == '\\' || *argvp == '"')
577             *(p++) = '\\';
578           *(p++) = *(argvp++);
579         }
580       *(p++) = '"';
581       *(p++) = ' ';
582     }
583   *(p++) = 0;
584
585   return buf;
586 }
587
588
589 int
590 _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
591                  struct spawn_fd_item_s *fd_list,
592                  void (*atfork) (void *opaque, int reserved),
593                  void *atforkvalue, pid_t *r_pid)
594 {
595   SECURITY_ATTRIBUTES sec_attr;
596   PROCESS_INFORMATION pi =
597     {
598       NULL,      /* returns process handle */
599       0,         /* returns primary thread handle */
600       0,         /* returns pid */
601       0          /* returns tid */
602     };
603   STARTUPINFO si;
604   int cr_flags = (CREATE_DEFAULT_ERROR_MODE
605                   | GetPriorityClass (GetCurrentProcess ()));
606   int i;
607   char **args;
608   char *arg_string;
609   /* FIXME.  */
610   int debug_me = 0;
611   int tmp_fd;
612   char *tmp_name;
613
614   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
615               "path=%s", path);
616   i = 0;
617   while (argv[i])
618     {
619       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
620       i++;
621     }
622   
623   /* We do not inherit any handles by default, and just insert those
624      handles we want the child to have afterwards.  But some handle
625      values occur on the command line, and we need to move
626      stdin/out/err to the right location.  So we use a wrapper program
627      which gets the information from a temporary file.  */
628   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
629     {
630       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
631       return TRACE_SYSRES (-1);
632     }
633   TRACE_LOG1 ("tmp_name = %s", tmp_name);
634
635   args = calloc (2 + i + 1, sizeof (*args));
636   args[0] = (char *) _gpgme_get_w32spawn_path ();
637   args[1] = tmp_name;
638   args[2] = path;
639   memcpy (&args[3], &argv[1], i * sizeof (*args));
640
641   memset (&sec_attr, 0, sizeof sec_attr);
642   sec_attr.nLength = sizeof sec_attr;
643   sec_attr.bInheritHandle = FALSE;
644   
645   arg_string = build_commandline (args);
646   free (args);
647   if (!arg_string)
648     {
649       close (tmp_fd);
650       DeleteFile (tmp_name);
651       return TRACE_SYSRES (-1);
652     }
653
654   memset (&si, 0, sizeof si);
655   si.cb = sizeof (si);
656   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
657   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
658   si.hStdInput = INVALID_HANDLE_VALUE;
659   si.hStdOutput = INVALID_HANDLE_VALUE;
660   si.hStdError = INVALID_HANDLE_VALUE;
661
662   cr_flags |= CREATE_SUSPENDED;
663   cr_flags |= DETACHED_PROCESS;
664   if (!CreateProcessA (_gpgme_get_w32spawn_path (),
665                        arg_string,
666                        &sec_attr,     /* process security attributes */
667                        &sec_attr,     /* thread security attributes */
668                        FALSE,         /* inherit handles */
669                        cr_flags,      /* creation flags */
670                        NULL,          /* environment */
671                        NULL,          /* use current drive/directory */
672                        &si,           /* startup information */
673                        &pi))          /* returns process information */
674     {
675       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
676       free (arg_string);
677       close (tmp_fd);
678       DeleteFile (tmp_name);
679
680       /* FIXME: Should translate the error code.  */
681       errno = EIO;
682       return TRACE_SYSRES (-1);
683     }
684
685   free (arg_string);
686   
687   if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
688     _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
689
690   /* Insert the inherited handles.  */
691   for (i = 0; fd_list[i].fd != -1; i++)
692     {
693       HANDLE hd;
694
695       /* Make it inheritable for the wrapper process.  */
696       if (!DuplicateHandle (GetCurrentProcess(),
697                             _get_osfhandle (giochannel_table[fd_list[i].fd].fd),
698                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
699         {
700           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
701           TerminateProcess (pi.hProcess, 0);
702           /* Just in case TerminateProcess didn't work, let the
703              process fail on its own.  */
704           ResumeThread (pi.hThread);
705           CloseHandle (pi.hThread);
706           CloseHandle (pi.hProcess);
707
708           close (tmp_fd);
709           DeleteFile (tmp_name);
710
711           /* FIXME: Should translate the error code.  */
712           errno = EIO;
713           return TRACE_SYSRES (-1);
714         }
715       /* Return the child name of this handle.  */
716       fd_list[i].peer_name = (int) hd;
717     }
718
719   /* Write the handle translation information to the temporary
720      file.  */
721   {
722     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
723        notation: "0xFEDCBA9876543210" with an extra white space after
724        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
725        for a time when a HANDLE is 64 bit.  */
726 #define BUFFER_MAX 800
727     char line[BUFFER_MAX + 1];
728     int res;
729     int written;
730     size_t len;
731
732     if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
733       strcpy (line, "~1 \n");
734     else
735       strcpy (line, "\n");
736     for (i = 0; fd_list[i].fd != -1; i++)
737       {
738         /* Strip the newline.  */
739         len = strlen (line) - 1;
740         
741         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
742         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
743                   fd_list[i].fd, fd_list[i].dup_to,
744                   fd_list[i].peer_name, fd_list[i].arg_loc);
745         /* Rather safe than sorry.  */
746         line[BUFFER_MAX - 1] = '\n';
747         line[BUFFER_MAX] = '\0';
748       }
749     len = strlen (line);
750     written = 0;
751     do
752       {
753         res = write (tmp_fd, &line[written], len - written);
754         if (res > 0)
755           written += res;
756       }
757     while (res > 0 || (res < 0 && errno == EAGAIN));
758   }
759   close (tmp_fd);
760   /* The temporary file is deleted by the gpgme-w32spawn process
761      (hopefully).  */
762     
763   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
764               "dwProcessID=%d, dwThreadId=%d",
765               pi.hProcess, pi.hThread, 
766               (int) pi.dwProcessId, (int) pi.dwThreadId);
767   
768   if (r_pid)
769     *r_pid = (pid_t)pi.dwProcessId;
770
771   if (ResumeThread (pi.hThread) < 0)
772     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
773   
774   if (!CloseHandle (pi.hThread))
775     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
776                 (int) GetLastError ());
777
778   TRACE_LOG1 ("process=%p", pi.hProcess);
779
780   /* We don't need to wait for the process.  */
781   if (!CloseHandle (pi.hProcess))
782     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
783                 (int) GetLastError ());
784
785   if (! (flags & IOSPAWN_FLAG_NOCLOSE))
786     {
787       for (i = 0; fd_list[i].fd != -1; i++)
788         _gpgme_io_close (fd_list[i].fd);
789     }
790
791   for (i = 0; fd_list[i].fd != -1; i++)
792     if (fd_list[i].dup_to == -1)
793       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
794                   fd_list[i].peer_name);
795     else
796       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
797                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
798                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
799
800   return TRACE_SYSRES (0);
801 }
802
803
804 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
805    nothing to select, > 0 = number of signaled fds.  */
806 int
807 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
808 {
809   int npollfds;
810   GPollFD *pollfds;
811   int *pollfds_map; 
812   int i;
813   int j;
814   int any;
815   int n;
816   int count;
817   /* Use a 1s timeout.  */
818   int timeout = 1000;
819   void *dbg_help = NULL;
820   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
821               "nfds=%u, nonblock=%u", nfds, nonblock);
822
823   if (nonblock)
824     timeout = 0;
825
826   pollfds = calloc (nfds, sizeof *pollfds);
827   if (!pollfds)
828     return -1;
829   pollfds_map = calloc (nfds, sizeof *pollfds_map);
830   if (!pollfds_map)
831     {
832       free (pollfds);
833       return -1;
834     }
835   npollfds = 0;
836
837   TRACE_SEQ (dbg_help, "select on [ ");
838   any = 0;
839   for (i = 0; i < nfds; i++)
840     {
841       GIOChannel *chan = NULL;
842
843       if (fds[i].fd == -1) 
844         continue;
845
846       if ((fds[i].for_read || fds[i].for_write)
847           && !(chan = find_channel (fds[i].fd)))
848         {
849           TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
850           TRACE_END (dbg_help, "]"); 
851           assert (!"see log file");
852         }
853       else if (fds[i].for_read )
854         {
855           assert(chan);
856           g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
857           pollfds_map[npollfds] = i;
858           TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
859           npollfds++;
860           any = 1;
861         }
862       else if (fds[i].for_write)
863         {
864           assert(chan);
865           g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
866           pollfds_map[npollfds] = i;
867           TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
868           npollfds++;
869           any = 1;
870         }
871       fds[i].signaled = 0;
872     }
873   TRACE_END (dbg_help, "]"); 
874   if (!any)
875     {
876       count = 0;
877       goto leave;
878     }
879
880
881   count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
882   if (count < 0)
883     {
884       int saved_errno = errno;
885       errno = saved_errno;
886       goto leave;
887     }
888
889   TRACE_SEQ (dbg_help, "select OK [ ");
890   if (TRACE_ENABLED (dbg_help))
891     {
892       for (i = 0; i < npollfds; i++)
893         {
894           if ((pollfds[i].revents & G_IO_IN))
895             TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
896           if ((pollfds[i].revents & G_IO_OUT))
897             TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
898         }
899       TRACE_END (dbg_help, "]");
900     }
901     
902   /* COUNT is used to stop the lop as soon as possible.  */
903   for (n = count, i = 0; i < npollfds && n; i++)
904     {
905       j = pollfds_map[i];
906       assert (j >= 0 && j < nfds);
907       if (fds[j].fd == -1)
908         ;
909       else if (fds[j].for_read)
910         {
911           if ((pollfds[i].revents & G_IO_IN))
912             {
913               fds[j].signaled = 1;
914               n--;
915             }
916         }
917       else if (fds[j].for_write)
918         {
919           if ((pollfds[i].revents & G_IO_OUT))
920             {
921               fds[j].signaled = 1;
922               n--;
923             }
924         }
925     }
926
927 leave:
928   free (pollfds);
929   free (pollfds_map);
930   return TRACE_SYSRES (count);
931 }
932
933
934 int
935 _gpgme_io_dup (int fd)
936 {
937   int newfd;
938   GIOChannel *chan;
939
940   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
941
942   if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
943     {
944       errno = EINVAL;
945       return TRACE_SYSRES (-1);
946     }
947
948   for (newfd = 0; newfd < MAX_SLAFD; newfd++)
949     if (! giochannel_table[newfd].used)
950       break;
951   if (newfd == MAX_SLAFD)
952     {
953       errno = EIO;
954       return TRACE_SYSRES (-1);
955     }
956   
957   chan = giochannel_table[fd].chan;
958   g_io_channel_ref (chan);
959   giochannel_table[newfd].used = 1;
960   giochannel_table[newfd].chan = chan;
961   giochannel_table[newfd].fd = -1;
962   giochannel_table[newfd].socket = INVALID_SOCKET;
963   giochannel_table[newfd].primary = 0;
964
965   return TRACE_SYSRES (newfd);
966 }
967
968 \f
969
970
971 \f
972 static int
973 wsa2errno (int err)
974 {
975   switch (err)
976     {
977     case WSAENOTSOCK:
978       return EINVAL;
979     case WSAEWOULDBLOCK:
980       return EAGAIN;
981     case ERROR_BROKEN_PIPE:
982       return EPIPE;
983     case WSANOTINITIALISED:
984       return ENOSYS;
985     default:
986       return EIO;
987     }
988 }
989
990
991 int
992 _gpgme_io_socket (int domain, int type, int proto)
993 {
994   int res;
995   int fd;
996
997   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
998               "type=%i, protp=%i", type, proto);
999
1000   res = socket (domain, type, proto);
1001   if (res == INVALID_SOCKET)
1002     {
1003       errno = wsa2errno (WSAGetLastError ());
1004       return TRACE_SYSRES (-1);
1005     }
1006
1007   fd = new_channel_from_socket (res);
1008   if (fd < 0)
1009     {
1010       int saved_errno = errno;
1011       closesocket (res);
1012       errno = saved_errno;
1013       return TRACE_SYSRES (-1);
1014     }
1015
1016   TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res);
1017   
1018   return fd;
1019 }
1020
1021
1022 int
1023 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
1024 {
1025   GIOChannel *chan; 
1026   int sockfd;
1027   int res;
1028   GIOFlags flags;
1029   GIOStatus status;
1030   GError *err = NULL;
1031
1032   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
1033               "addr=%p, addrlen=%i", addr, addrlen);
1034
1035   chan = find_channel (fd);
1036   if (! chan)
1037     {
1038       errno = EINVAL;
1039       return TRACE_SYSRES (-1);
1040     }
1041
1042   flags = g_io_channel_get_flags (chan);
1043   if (flags & G_IO_FLAG_NONBLOCK)
1044     {
1045       status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err);
1046       if (err)
1047         {
1048           TRACE_LOG1 ("setting flags error: %s", err->message);
1049           g_error_free (err);
1050           err = NULL;
1051         }
1052       if (status != G_IO_STATUS_NORMAL)
1053         {
1054           errno = EIO;
1055           return TRACE_SYSRES (-1);
1056         }
1057     }
1058
1059   sockfd = giochannel_table[fd].socket;
1060   if (sockfd == INVALID_SOCKET)
1061     {
1062       errno = EINVAL;
1063       return TRACE_SYSRES (-1);
1064     }
1065
1066   TRACE_LOG1 ("connect sockfd=0x%x", sockfd);
1067   res = connect (sockfd, addr, addrlen);
1068
1069   /* FIXME: Error ignored here.  */
1070   if (! (flags & G_IO_FLAG_NONBLOCK))
1071     g_io_channel_set_flags (chan, flags, NULL);
1072
1073   if (res)
1074     {
1075       TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ());
1076
1077       errno = wsa2errno (WSAGetLastError ());
1078       return TRACE_SYSRES (-1);
1079     }
1080
1081   return TRACE_SUC ();
1082 }