2005-11-17 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 <windows.h>
36 #include <io.h>
37
38 #include "util.h"
39 #include "priv-io.h"
40 #include "sema.h"
41 #include "debug.h"
42
43 #include <glib.h>
44
45 \f
46 static GIOChannel *giochannel_table[256];
47
48 static HANDLE handle_table[256];
49 #define fd_to_handle(x) handle_table[x]
50
51 static GIOChannel *
52 find_channel (int fd, int create)
53 {
54   if (fd < 0 || fd > (int) DIM (giochannel_table))
55     return NULL;
56
57   if (giochannel_table[fd] == NULL && create)
58     giochannel_table[fd] = g_io_channel_unix_new (fd);
59
60   return giochannel_table[fd];
61 }
62
63
64 /* Look up the giochannel for file descriptor FD.  */
65 GIOChannel *
66 gpgme_get_giochannel (int fd)
67 {
68   return find_channel (fd, 0);
69 }
70
71 \f
72 void
73 _gpgme_io_subsystem_init (void)
74 {
75 }
76
77 \f
78 static struct
79 {
80   void (*handler) (int,void*);
81   void *value;
82 } notify_table[256];
83
84 int
85 _gpgme_io_read (int fd, void *buffer, size_t count)
86 {
87   int saved_errno = 0;
88   gsize nread;
89   GIOChannel *chan;
90   GIOStatus status;
91
92   DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int) count);
93
94   chan = find_channel (fd, 0);
95   if (!chan)
96     {
97       DEBUG1 ("fd %d: no channel registered\n", fd);
98       errno = EINVAL;
99       return -1;
100     }
101
102   status = g_io_channel_read_chars (chan, (gchar *) buffer,
103                                     count, &nread, NULL);
104   if (status == G_IO_STATUS_EOF)
105     nread = 0;
106   else if (status != G_IO_STATUS_NORMAL)
107     {
108       nread = -1;
109       saved_errno = EIO;
110     }
111
112   DEBUG2 ("fd %d: got %d bytes\n", fd, nread);
113   if (nread > 0)
114     _gpgme_debug (2, "fd %d: got `%.*s'\n", fd, nread, buffer);
115
116   errno = saved_errno;
117   return nread;
118 }
119
120
121 int
122 _gpgme_io_write (int fd, const void *buffer, size_t count)
123 {
124   int saved_errno = 0;
125   gsize nwritten;
126   GIOChannel *chan;
127   GIOStatus status;
128
129   DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int) count);
130   _gpgme_debug (2, "fd %d: write `%.*s'\n", fd, (int) count, buffer);
131
132   chan = find_channel (fd, 0);
133   if (!chan)
134     {
135       DEBUG1 ("fd %d: no channel registered\n", fd);
136       errno = EINVAL;
137       return -1;
138     }
139
140   status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
141                                      &nwritten, NULL);
142   if (status != G_IO_STATUS_NORMAL)
143     {
144       nwritten = -1;
145       saved_errno = EIO;
146     }
147   DEBUG2 ("fd %d:          wrote %d bytes\n", fd, (int) nwritten);
148   errno = saved_errno;
149   return nwritten;
150 }
151
152
153 int
154 _gpgme_io_pipe ( int filedes[2], int inherit_idx )
155 {
156     HANDLE r, w;
157     SECURITY_ATTRIBUTES sec_attr;
158
159     memset (&sec_attr, 0, sizeof sec_attr );
160     sec_attr.nLength = sizeof sec_attr;
161     sec_attr.bInheritHandle = FALSE;
162     
163 #define PIPEBUF_SIZE  4096
164     if (!CreatePipe ( &r, &w, &sec_attr, PIPEBUF_SIZE))
165         return -1;
166     /* Make one end inheritable. */
167     if ( inherit_idx == 0 ) {
168         HANDLE h;
169         if (!DuplicateHandle( GetCurrentProcess(), r,
170                               GetCurrentProcess(), &h, 0,
171                               TRUE, DUPLICATE_SAME_ACCESS ) ) {
172             DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
173             CloseHandle (r);
174             CloseHandle (w);
175             return -1;
176         }
177         CloseHandle (r);
178         r = h;
179     }
180     else if ( inherit_idx == 1 ) {
181         HANDLE h;
182         if (!DuplicateHandle( GetCurrentProcess(), w,
183                               GetCurrentProcess(), &h, 0,
184                               TRUE, DUPLICATE_SAME_ACCESS ) ) {
185             DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
186             CloseHandle (r);
187             CloseHandle (w);
188             return -1;
189         }
190         CloseHandle (w);
191         w = h;
192     }
193     filedes[0] = _open_osfhandle ((long) r, 0 );
194     if (filedes[0] == -1)
195       {
196         DEBUG1 ("_open_osfhandle failed: ec=%d\n", errno);
197         CloseHandle (r);
198         CloseHandle (w);
199         return -1;
200       }
201     filedes[1] = _open_osfhandle ((long) w, 0 );
202       {
203         DEBUG1 ("_open_osfhandle failed: ec=%d\n", errno);
204         _gpgme_io_close (filedes[0]);
205         CloseHandle (r);
206         CloseHandle (w);
207         return -1;
208       }
209
210     /* The fd that is not inherited will be used locally.  Create a
211        channel for it.  */
212     if (inherit_idx == 0)
213       {
214         if (!find_channel (filedes[1], 1))
215           {
216             DEBUG1 ("channel creation failed for %d\n", filedes[1]);
217             _gpgme_io_close (filedes[0]);
218             _gpgme_io_close (filedes[1]);
219             CloseHandle (r);
220             CloseHandle (w);
221             return -1;
222           }
223       }
224     else
225       {
226         if (!find_channel (filedes[0], 1))
227           {
228             DEBUG1 ("channel creation failed for %d\n", filedes[1]);
229             _gpgme_io_close (filedes[0]);
230             _gpgme_io_close (filedes[1]);
231             CloseHandle (r);
232             CloseHandle (w);
233             return -1;
234           }
235       }
236
237     /* Remember the handles for later.  */
238     handle_table[filedes[0]] = r;
239     handle_table[filedes[1]] = w;
240
241     DEBUG5 ("CreatePipe %p %p %d %d inherit=%d\n", r, w,
242                    filedes[0], filedes[1], inherit_idx );
243     return 0;
244 }
245
246
247 int
248 _gpgme_io_close (int fd)
249 {
250   GIOChannel *chan;
251
252   if (fd == -1)
253     return -1;
254
255   /* First call the notify handler.  */
256   DEBUG1 ("closing fd %d", fd);
257   if (fd >= 0 && fd < (int) DIM (notify_table))
258     {
259       if (notify_table[fd].handler)
260         {
261           notify_table[fd].handler (fd, notify_table[fd].value);
262           notify_table[fd].handler = NULL;
263           notify_table[fd].value = NULL;
264         }
265     }
266   /* Then do the close.  */    
267   chan = find_channel (fd, 0);
268   if (chan)
269     {
270       g_io_channel_shutdown (chan, 1, NULL);
271       g_io_channel_unref (chan);
272       giochannel_table[fd] = NULL;
273       return 0;
274     }
275   else
276     return close (fd);
277 }
278
279
280 int
281 _gpgme_io_set_close_notify (int fd, void (*handler)(int, void*), void *value)
282 {
283   assert (fd != -1);
284
285   if (fd < 0 || fd >= (int) DIM (notify_table))
286     return -1;
287   DEBUG1 ("set notification for fd %d", fd);
288   notify_table[fd].handler = handler;
289   notify_table[fd].value = value;
290   return 0;
291 }
292
293
294 int
295 _gpgme_io_set_nonblocking (int fd)
296 {
297   GIOChannel *chan;
298   GIOStatus status;
299
300   chan = find_channel (fd, 0);
301   if (!chan)
302     {
303       errno = EIO;
304       return -1;
305     }
306
307   status = g_io_channel_set_flags (chan,
308                                    g_io_channel_get_flags (chan) |
309                                    G_IO_FLAG_NONBLOCK, NULL);
310   if (status != G_IO_STATUS_NORMAL)
311     {
312       errno = EIO;
313       return -1;
314     }
315
316   return 0;
317 }
318
319
320 static char *
321 build_commandline ( char **argv )
322 {
323   int i, n = 0;
324   char *buf, *p;
325   
326   /* FIXME: we have to quote some things because under Windows the
327    * program parses the commandline and does some unquoting.  For now
328    * we only do very basic quoting to the first argument because this
329    * one often contains a space (e.g. C:\\Program Files\GNU\GnuPG\gpg.exe) 
330    * and we would produce an invalid line in that case.  */
331   for (i=0; argv[i]; i++)
332     n += strlen (argv[i]) + 2 + 1; /* 2 extra bytes for possible quoting */
333   buf = p = malloc (n);
334   if ( !buf )
335     return NULL;
336   *buf = 0;
337   if ( argv[0] )
338     {
339       if (strpbrk (argv[0], " \t"))
340         p = stpcpy (stpcpy (stpcpy (p, "\""), argv[0]), "\"");
341       else
342         p = stpcpy (p, argv[0]);
343       for (i = 1; argv[i]; i++)
344         {
345           if (!*argv[i])
346             p = stpcpy (p, " \"\"");
347           else
348             p = stpcpy (stpcpy (p, " "), argv[i]);
349         }
350     }
351   
352   return buf;
353 }
354
355
356 int
357 _gpgme_io_spawn ( const char *path, char **argv,
358                   struct spawn_fd_item_s *fd_child_list,
359                   struct spawn_fd_item_s *fd_parent_list )
360 {
361     SECURITY_ATTRIBUTES sec_attr;
362     PROCESS_INFORMATION pi = {
363         NULL,      /* returns process handle */
364         0,         /* returns primary thread handle */
365         0,         /* returns pid */
366         0         /* returns tid */
367     };
368     STARTUPINFO si;
369     char *envblock = NULL;
370     int cr_flags = CREATE_DEFAULT_ERROR_MODE
371                  | GetPriorityClass (GetCurrentProcess ());
372     int i;
373     char *arg_string;
374     int duped_stdin = 0;
375     int duped_stderr = 0;
376     HANDLE hnul = INVALID_HANDLE_VALUE;
377     /* FIXME.  */
378     int debug_me = 0;
379
380     memset (&sec_attr, 0, sizeof sec_attr );
381     sec_attr.nLength = sizeof sec_attr;
382     sec_attr.bInheritHandle = FALSE;
383
384     arg_string = build_commandline ( argv );
385     if (!arg_string )
386         return -1; 
387
388     memset (&si, 0, sizeof si);
389     si.cb = sizeof (si);
390     si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
391     si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
392     si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
393     si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
394     si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
395
396     for (i=0; fd_child_list[i].fd != -1; i++ ) {
397         if (fd_child_list[i].dup_to == 0 ) {
398             si.hStdInput = fd_to_handle (fd_child_list[i].fd);
399             DEBUG1 ("using %d for stdin", fd_child_list[i].fd );
400             duped_stdin=1;
401         }
402         else if (fd_child_list[i].dup_to == 1 ) {
403             si.hStdOutput = fd_to_handle (fd_child_list[i].fd);
404             DEBUG1 ("using %d for stdout", fd_child_list[i].fd );
405         }
406         else if (fd_child_list[i].dup_to == 2 ) {
407             si.hStdError = fd_to_handle (fd_child_list[i].fd);
408             DEBUG1 ("using %d for stderr", fd_child_list[i].fd );
409             duped_stderr = 1;
410         }
411     }
412
413     if( !duped_stdin || !duped_stderr ) {
414         SECURITY_ATTRIBUTES sa;
415
416         memset (&sa, 0, sizeof sa );
417         sa.nLength = sizeof sa;
418         sa.bInheritHandle = TRUE;
419         hnul = CreateFile ( "nul",
420                             GENERIC_READ|GENERIC_WRITE,
421                             FILE_SHARE_READ|FILE_SHARE_WRITE,
422                             &sa,
423                             OPEN_EXISTING,
424                             FILE_ATTRIBUTE_NORMAL,
425                             NULL );
426         if ( hnul == INVALID_HANDLE_VALUE ) {
427             DEBUG1 ("can't open `nul': ec=%d\n", (int)GetLastError ());
428             free (arg_string);
429             return -1;
430         }
431         /* Make sure that the process has a connected stdin */
432         if ( !duped_stdin ) {
433             si.hStdInput = hnul;
434             DEBUG1 ("using %d for dummy stdin", (int)hnul );
435         }
436         /* We normally don't want all the normal output */
437         if ( !duped_stderr ) {
438             si.hStdError = hnul;
439             DEBUG1 ("using %d for dummy stderr", (int)hnul );
440         }
441     }
442
443     DEBUG2 ("CreateProcess, path=`%s' args=`%s'", path, arg_string);
444     cr_flags |= CREATE_SUSPENDED; 
445     if ( !CreateProcessA (path,
446                           arg_string,
447                           &sec_attr,     /* process security attributes */
448                           &sec_attr,     /* thread security attributes */
449                           TRUE,          /* inherit handles */
450                           cr_flags,      /* creation flags */
451                           envblock,      /* environment */
452                           NULL,          /* use current drive/directory */
453                           &si,           /* startup information */
454                           &pi            /* returns process information */
455         ) ) {
456         DEBUG1 ("CreateProcess failed: ec=%d\n", (int) GetLastError ());
457         free (arg_string);
458         return -1;
459     }
460
461     /* Close the /dev/nul handle if used. */
462     if (hnul != INVALID_HANDLE_VALUE ) {
463         if ( !CloseHandle ( hnul ) )
464             DEBUG1 ("CloseHandle(hnul) failed: ec=%d\n", (int)GetLastError());
465     }
466
467     /* Close the other ends of the pipes. */
468     for (i = 0; fd_parent_list[i].fd != -1; i++)
469       _gpgme_io_close (fd_parent_list[i].fd);
470
471     DEBUG4 ("CreateProcess ready\n"
472             "-   hProcess=%p  hThread=%p\n"
473             "-   dwProcessID=%d dwThreadId=%d\n",
474             pi.hProcess, pi.hThread, 
475             (int) pi.dwProcessId, (int) pi.dwThreadId);
476
477     if ( ResumeThread ( pi.hThread ) < 0 ) {
478         DEBUG1 ("ResumeThread failed: ec=%d\n", (int)GetLastError ());
479     }
480
481     if ( !CloseHandle (pi.hThread) ) { 
482         DEBUG1 ("CloseHandle of thread failed: ec=%d\n",
483                  (int)GetLastError ());
484     }
485
486     return 0;
487 }
488
489
490 /*
491  * Select on the list of fds.
492  * Returns: -1 = error
493  *           0 = timeout or nothing to select
494  *          >0 = number of signaled fds
495  */
496 int
497 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
498 {
499   assert (!"ARGH!  The user of this library MUST define io callbacks!");
500   errno = EINVAL;
501   return -1;
502 }