2007-09-27 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 ? "reading" : "writing");
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
580   /* We don't need to wait for the process. */
581   CloseHandle (pi.hProcess);
582
583   return TRACE_SYSRES (0);
584 }
585
586
587 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
588    nothing to select, > 0 = number of signaled fds.  */
589 int
590 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
591 {
592   int npollfds;
593   GPollFD *pollfds;
594   int *pollfds_map; 
595   int i;
596   int j;
597   int any;
598   int n;
599   int count;
600   /* Use a 1s timeout.  */
601   int timeout = 1000;
602   void *dbg_help = NULL;
603   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
604               "nfds=%u, nonblock=%u", nfds, nonblock);
605
606   if (nonblock)
607     timeout = 0;
608
609   pollfds = calloc (nfds, sizeof *pollfds);
610   if (!pollfds)
611     return -1;
612   pollfds_map = calloc (nfds, sizeof *pollfds_map);
613   if (!pollfds_map)
614     {
615       free (pollfds);
616       return -1;
617     }
618   npollfds = 0;
619
620   TRACE_SEQ (dbg_help, "select on [ ");
621   any = 0;
622   for (i = 0; i < nfds; i++)
623     {
624       if (fds[i].fd == -1) 
625         continue;
626       if (fds[i].frozen)
627         TRACE_ADD1 (dbg_help, "f0x%x ", fds[i].fd);
628       else if (fds[i].for_read )
629         {
630           GIOChannel *chan = find_channel (fds[i].fd, 0);
631           assert (chan);
632           g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
633           pollfds_map[npollfds] = i;
634           TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
635           npollfds++;
636           any = 1;
637         }
638       else if (fds[i].for_write)
639         {
640           GIOChannel *chan = find_channel (fds[i].fd, 0);
641           assert (chan);
642           g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
643           pollfds_map[npollfds] = i;
644           TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
645           npollfds++;
646           any = 1;
647         }
648       fds[i].signaled = 0;
649     }
650   TRACE_END (dbg_help, "]"); 
651   if (!any)
652     {
653       count = 0;
654       goto leave;
655     }
656
657
658   count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
659   if (count < 0)
660     {
661       int saved_errno = errno;
662       errno = saved_errno;
663       goto leave;
664     }
665
666   TRACE_SEQ (dbg_help, "select OK [ ");
667   if (TRACE_ENABLED (dbg_help))
668     {
669       for (i = 0; i < npollfds; i++)
670         {
671           if ((pollfds[i].revents & G_IO_IN))
672             TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
673           if ((pollfds[i].revents & G_IO_OUT))
674             TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
675         }
676       TRACE_END (dbg_help, "]");
677     }
678     
679   /* COUNT is used to stop the lop as soon as possible.  */
680   for (n = count, i = 0; i < npollfds && n; i++)
681     {
682       j = pollfds_map[i];
683       assert (j >= 0 && j < nfds);
684       if (fds[j].fd == -1)
685         ;
686       else if (fds[j].for_read)
687         {
688           if ((pollfds[i].revents & G_IO_IN))
689             {
690               fds[j].signaled = 1;
691               n--;
692             }
693         }
694       else if (fds[j].for_write)
695         {
696           if ((pollfds[i].revents & G_IO_OUT))
697             {
698               fds[j].signaled = 1;
699               n--;
700             }
701         }
702     }
703
704 leave:
705   free (pollfds);
706   free (pollfds_map);
707   return TRACE_SYSRES (count);
708 }
709
710
711 int
712 _gpgme_io_dup (int fd)
713 {
714   return _dup (fd);
715 }