2007-09-07 Marcus Brinkmann <marcus@g10code.de>
[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 GIOChannel *giochannel_table[MAX_SLAFD];
82
83
84 static GIOChannel *
85 find_channel (int fd, int create)
86 {
87   if (fd < 0 || fd >= MAX_SLAFD)
88     return NULL;
89
90   if (create && !giochannel_table[fd])
91     {
92       giochannel_table[fd] = g_io_channel_win32_new_fd (fd);
93       g_io_channel_set_encoding (giochannel_table[fd], NULL, NULL);
94       g_io_channel_set_buffered (giochannel_table[fd], FALSE);
95     }
96
97   return giochannel_table[fd];
98 }
99
100
101 /* Compatibility interface.  Obsolete.  */
102 void *
103 gpgme_get_giochannel (int fd)
104 {
105   return find_channel (fd, 0);
106 }
107
108
109 /* Look up the giochannel for "file descriptor" FD.  */
110 void *
111 gpgme_get_fdptr (int fd)
112 {
113   return find_channel (fd, 0);
114 }
115
116
117 /* Write the printable version of FD to the buffer BUF of length
118    BUFLEN.  The printable version is the representation on the command
119    line that the child process expects.  */
120 int
121 _gpgme_io_fd2str (char *buf, int buflen, int fd)
122 {
123   return snprintf (buf, buflen, "%ld", (long) _get_osfhandle (fd));
124 }
125
126 \f
127 void
128 _gpgme_io_subsystem_init (void)
129 {
130 }
131
132 \f
133 static struct
134 {
135   _gpgme_close_notify_handler_t handler;
136   void *value;
137 } notify_table[MAX_SLAFD];
138
139
140 int
141 _gpgme_io_read (int fd, void *buffer, size_t count)
142 {
143   int saved_errno = 0;
144   gsize nread;
145   GIOChannel *chan;
146   GIOStatus status;
147   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
148               "buffer=%p, count=%u", buffer, count);
149
150   chan = find_channel (fd, 0);
151   if (!chan)
152     {
153       TRACE_LOG ("no channel registered");
154       errno = EINVAL;
155       return TRACE_SYSRES (-1);
156     }
157   TRACE_LOG1 ("channel %p", chan);
158
159   {
160     GError *err = NULL;
161     status = g_io_channel_read_chars (chan, (gchar *) buffer,
162                                       count, &nread, &err);
163     if (err)
164       {
165         TRACE_LOG2 ("status %i, err %s", status, err->message);
166         g_error_free (err);
167       }
168   }
169
170   if (status == G_IO_STATUS_EOF)
171     nread = 0;
172   else if (status != G_IO_STATUS_NORMAL)
173     {
174       TRACE_LOG1 ("status %d", status);
175       nread = -1;
176       saved_errno = EIO;
177     }
178
179   TRACE_LOGBUF (buffer, nread);
180
181   errno = saved_errno;
182   return TRACE_SYSRES (nread);
183 }
184
185
186 int
187 _gpgme_io_write (int fd, const void *buffer, size_t count)
188 {
189   int saved_errno = 0;
190   gsize nwritten;
191   GIOChannel *chan;
192   GIOStatus status;
193   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
194               "buffer=%p, count=%u", buffer, count);
195   TRACE_LOGBUF (buffer, count);
196
197   chan = find_channel (fd, 0);
198   if (!chan)
199     {
200       TRACE_LOG ("fd %d: no channel registered");
201       errno = EINVAL;
202       return -1;
203     }
204
205   status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
206                                      &nwritten, NULL);
207   if (status != G_IO_STATUS_NORMAL)
208     {
209       nwritten = -1;
210       saved_errno = EIO;
211     }
212   errno = saved_errno;
213
214   return TRACE_SYSRES (nwritten);
215 }
216
217
218 int
219 _gpgme_io_pipe (int filedes[2], int inherit_idx)
220 {
221   GIOChannel *chan;
222   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
223               "inherit_idx=%i (GPGME uses it for %s)",
224               inherit_idx, inherit_idx ? "writing" : "reading");
225
226 #define PIPEBUF_SIZE  4096
227   if (_pipe (filedes, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
228     return TRACE_SYSRES (-1);
229
230   /* Make one end inheritable. */
231   if (inherit_idx == 0)
232     {
233       int new_read;
234
235       new_read = _dup (filedes[0]);
236       _close (filedes[0]);
237       filedes[0] = new_read;
238
239       if (new_read < 0)
240         {
241           _close (filedes[1]);
242           return TRACE_SYSRES (-1);
243         }
244     }
245   else if (inherit_idx == 1)
246     {
247       int new_write;
248
249       new_write = _dup (filedes[1]);
250       _close (filedes[1]);
251       filedes[1] = new_write;
252
253       if (new_write < 0)
254         {
255           _close (filedes[0]);
256           return TRACE_SYSRES (-1);
257         }
258     }
259
260   /* Now we have a pipe with the right end inheritable.  The other end
261      should have a giochannel.  */
262   chan = find_channel (filedes[1 - inherit_idx], 1);
263   if (!chan)
264     {
265       int saved_errno = errno;
266       _close (filedes[0]);
267       _close (filedes[1]);
268       errno = saved_errno;
269       return TRACE_SYSRES (-1);
270     }
271
272   return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
273           filedes[0], (HANDLE) _get_osfhandle (filedes[0]),
274           filedes[1], (HANDLE) _get_osfhandle (filedes[1]),
275           chan);
276 }
277
278
279 int
280 _gpgme_io_close (int fd)
281 {
282   GIOChannel *chan;
283   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
284
285   if (fd < 0 || fd >= MAX_SLAFD)
286     {
287       errno = EBADF;
288       return TRACE_SYSRES (-1);
289     }
290
291   /* First call the notify handler.  */
292   if (notify_table[fd].handler)
293     {
294       notify_table[fd].handler (fd, notify_table[fd].value);
295       notify_table[fd].handler = NULL;
296       notify_table[fd].value = NULL;
297     }
298
299   /* Then do the close.  */    
300   chan = giochannel_table[fd];
301   if (chan)
302     {
303       g_io_channel_shutdown (chan, 1, NULL);
304       g_io_channel_unref (chan);
305       giochannel_table[fd] = NULL;
306     }
307   else
308     _close (fd);
309
310   return 0;
311 }
312
313
314 int
315 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
316                             void *value)
317 {
318   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
319               "close_handler=%p/%p", handler, value);
320
321   assert (fd != -1);
322
323   if (fd < 0 || fd >= (int) DIM (notify_table))
324     {
325       errno = EINVAL;
326       return TRACE_SYSRES (-1);
327     }
328   notify_table[fd].handler = handler;
329   notify_table[fd].value = value;
330   return TRACE_SYSRES (0);
331 }
332
333
334 int
335 _gpgme_io_set_nonblocking (int fd)
336 {
337   GIOChannel *chan;
338   GIOStatus status;
339  
340   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
341
342   chan = find_channel (fd, 0);
343   if (!chan)
344     {
345       errno = EIO;
346       return TRACE_SYSRES (-1);
347     }
348
349    status = g_io_channel_set_flags (chan,
350                                    g_io_channel_get_flags (chan) |
351                                    G_IO_FLAG_NONBLOCK, NULL);
352   if (status != G_IO_STATUS_NORMAL)
353     {
354 #if 0
355       /* glib 1.9.2 does not implement set_flags and returns an
356          error.  */
357       errno = EIO;
358       return TRACE_SYSRES (-1);
359 #else
360       TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
361                   status);
362 #endif
363     }
364
365   return TRACE_SYSRES (0);
366 }
367
368
369 static char *
370 build_commandline (char **argv)
371 {
372   int i;
373   int n = 0;
374   char *buf;
375   char *p;
376   
377   /* We have to quote some things because under Windows the program
378      parses the commandline and does some unquoting.  We enclose the
379      whole argument in double-quotes, and escape literal double-quotes
380      as well as backslashes with a backslash.  We end up with a
381      trailing space at the end of the line, but that is harmless.  */
382   for (i = 0; argv[i]; i++)
383     {
384       p = argv[i];
385       /* The leading double-quote.  */
386       n++;
387       while (*p)
388         {
389           /* An extra one for each literal that must be escaped.  */
390           if (*p == '\\' || *p == '"')
391             n++;
392           n++;
393           p++;
394         }
395       /* The trailing double-quote and the delimiter.  */
396       n += 2;
397     }
398   /* And a trailing zero.  */
399   n++;
400
401   buf = p = malloc (n);
402   if (!buf)
403     return NULL;
404   for (i = 0; argv[i]; i++)
405     {
406       char *argvp = argv[i];
407
408       *(p++) = '"';
409       while (*argvp)
410         {
411           if (*argvp == '\\' || *argvp == '"')
412             *(p++) = '\\';
413           *(p++) = *(argvp++);
414         }
415       *(p++) = '"';
416       *(p++) = ' ';
417     }
418   *(p++) = 0;
419
420   return buf;
421 }
422
423
424 int
425 _gpgme_io_spawn (const char *path, char **argv,
426                  struct spawn_fd_item_s *fd_child_list,
427                  struct spawn_fd_item_s *fd_parent_list)
428 {
429   SECURITY_ATTRIBUTES sec_attr;
430   PROCESS_INFORMATION pi =
431     {
432       NULL,      /* returns process handle */
433       0,         /* returns primary thread handle */
434       0,         /* returns pid */
435       0         /* returns tid */
436     };
437   STARTUPINFO si;
438   char *envblock = NULL;
439   int cr_flags = CREATE_DEFAULT_ERROR_MODE
440     | GetPriorityClass (GetCurrentProcess ());
441   int i;
442   char *arg_string;
443   int duped_stdin = 0;
444   int duped_stderr = 0;
445   HANDLE hnul = INVALID_HANDLE_VALUE;
446   /* FIXME.  */
447   int debug_me = 0;
448   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
449               "path=%s", path);
450   i = 0;
451   while (argv[i])
452     {
453       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
454       i++;
455     }
456   
457   memset (&sec_attr, 0, sizeof sec_attr);
458   sec_attr.nLength = sizeof sec_attr;
459   sec_attr.bInheritHandle = FALSE;
460   
461   arg_string = build_commandline (argv);
462   if (!arg_string)
463     return TRACE_SYSRES (-1);
464   
465   memset (&si, 0, sizeof si);
466   si.cb = sizeof (si);
467   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
468   si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
469   si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
470   si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
471   si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
472   
473   for (i = 0; fd_child_list[i].fd != -1; i++)
474     {
475       if (fd_child_list[i].dup_to == 0)
476         {
477           si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
478           TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd,
479                       _get_osfhandle (fd_child_list[i].fd));
480           duped_stdin = 1;
481         }
482       else if (fd_child_list[i].dup_to == 1)
483         {
484           si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
485           TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd,
486                       _get_osfhandle (fd_child_list[i].fd));
487         }
488       else if (fd_child_list[i].dup_to == 2)
489         {
490           si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
491           TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd,
492                       _get_osfhandle (fd_child_list[i].fd));
493           duped_stderr = 1;
494         }
495     }
496   
497   if (!duped_stdin || !duped_stderr)
498     {
499       SECURITY_ATTRIBUTES sa;
500       
501       memset (&sa, 0, sizeof sa);
502       sa.nLength = sizeof sa;
503       sa.bInheritHandle = TRUE;
504       hnul = CreateFile ("nul",
505                          GENERIC_READ|GENERIC_WRITE,
506                          FILE_SHARE_READ|FILE_SHARE_WRITE,
507                          &sa,
508                          OPEN_EXISTING,
509                          FILE_ATTRIBUTE_NORMAL,
510                          NULL);
511       if (hnul == INVALID_HANDLE_VALUE)
512         {
513           TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d",
514                       (int) GetLastError ());
515           free (arg_string);
516           /* FIXME: Should translate the error code.  */
517           errno = EIO;
518           return TRACE_SYSRES (-1);
519         }
520       /* Make sure that the process has a connected stdin.  */
521       if (!duped_stdin)
522         {
523           si.hStdInput = hnul;
524           TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul);
525         }
526       /* We normally don't want all the normal output.  */
527       if (!duped_stderr)
528         {
529           si.hStdError = hnul;
530           TRACE_LOG1 ("using %d for dummy stderr", (int)hnul);
531         }
532     }
533   
534   cr_flags |= CREATE_SUSPENDED;
535   cr_flags |= DETACHED_PROCESS;
536   if (!CreateProcessA (path,
537                        arg_string,
538                        &sec_attr,     /* process security attributes */
539                        &sec_attr,     /* thread security attributes */
540                        TRUE,          /* inherit handles */
541                        cr_flags,      /* creation flags */
542                        envblock,      /* environment */
543                        NULL,          /* use current drive/directory */
544                        &si,           /* startup information */
545                        &pi))          /* returns process information */
546     {
547       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
548       free (arg_string);
549       /* FIXME: Should translate the error code.  */
550       errno = EIO;
551       return TRACE_SYSRES (-1);
552     }
553   
554   /* Close the /dev/nul handle if used.  */
555   if (hnul != INVALID_HANDLE_VALUE)
556     {
557       if (!CloseHandle (hnul))
558         TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)",
559                     (int) GetLastError ());
560     }
561   
562   /* Close the other ends of the pipes.  */
563   for (i = 0; fd_parent_list[i].fd != -1; i++)
564     _gpgme_io_close (fd_parent_list[i].fd);
565   
566   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
567               "dwProcessID=%d, dwThreadId=%d",
568               pi.hProcess, pi.hThread, 
569               (int) pi.dwProcessId, (int) pi.dwThreadId);
570   
571   if (ResumeThread (pi.hThread) < 0)
572     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
573   
574   if (!CloseHandle (pi.hThread))
575     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
576                 (int) GetLastError ());
577
578   TRACE_SUC1 ("process=%p", pi.hProcess);
579   return 0;
580 }
581
582
583 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
584    nothing to select, > 0 = number of signaled fds.  */
585 int
586 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
587 {
588   int npollfds;
589   GPollFD *pollfds;
590   int *pollfds_map; 
591   int i;
592   int j;
593   int any;
594   int n;
595   int count;
596   /* Use a 1s timeout.  */
597   int timeout = 1000;
598   void *dbg_help = NULL;
599   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
600               "nfds=%u, nonblock=%u", nfds, nonblock);
601
602   if (nonblock)
603     timeout = 0;
604
605   pollfds = calloc (nfds, sizeof *pollfds);
606   if (!pollfds)
607     return -1;
608   pollfds_map = calloc (nfds, sizeof *pollfds_map);
609   if (!pollfds_map)
610     {
611       free (pollfds);
612       return -1;
613     }
614   npollfds = 0;
615
616   TRACE_SEQ (dbg_help, "select on [ ");
617   any = 0;
618   for (i = 0; i < nfds; i++)
619     {
620       if (fds[i].fd == -1) 
621         continue;
622       if (fds[i].frozen)
623         TRACE_ADD1 (dbg_help, "f0x%x ", fds[i].fd);
624       else if (fds[i].for_read )
625         {
626           GIOChannel *chan = find_channel (fds[i].fd, 0);
627           assert (chan);
628           g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
629           pollfds_map[npollfds] = i;
630           TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
631           npollfds++;
632           any = 1;
633         }
634       else if (fds[i].for_write)
635         {
636           GIOChannel *chan = find_channel (fds[i].fd, 0);
637           assert (chan);
638           g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
639           pollfds_map[npollfds] = i;
640           TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
641           npollfds++;
642           any = 1;
643         }
644       fds[i].signaled = 0;
645     }
646   TRACE_END (dbg_help, "]"); 
647   if (!any)
648     {
649       count = 0;
650       goto leave;
651     }
652
653
654   count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
655   if (count < 0)
656     {
657       int saved_errno = errno;
658       errno = saved_errno;
659       goto leave;
660     }
661
662   TRACE_SEQ (dbg_help, "select OK [ ");
663   if (TRACE_ENABLED (dbg_help))
664     {
665       for (i = 0; i < npollfds; i++)
666         {
667           if ((pollfds[i].revents & G_IO_IN))
668             TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
669           if ((pollfds[i].revents & G_IO_OUT))
670             TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
671         }
672       TRACE_END (dbg_help, "]");
673     }
674     
675   /* COUNT is used to stop the lop as soon as possible.  */
676   for (n = count, i = 0; i < npollfds && n; i++)
677     {
678       j = pollfds_map[i];
679       assert (j >= 0 && j < nfds);
680       if (fds[j].fd == -1)
681         ;
682       else if (fds[j].for_read)
683         {
684           if ((pollfds[i].revents & G_IO_IN))
685             {
686               fds[j].signaled = 1;
687               n--;
688             }
689         }
690       else if (fds[j].for_write)
691         {
692           if ((pollfds[i].revents & G_IO_OUT))
693             {
694               fds[j].signaled = 1;
695               n--;
696             }
697         }
698     }
699
700 leave:
701   free (pollfds);
702   free (pollfds_map);
703   return TRACE_SYSRES (count);
704 }
705
706
707 int
708 _gpgme_io_dup (int fd)
709 {
710   return _dup (fd);
711 }