2007-07-13 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / w32-io.c
1 /* w32-io.c - W32 API I/O functions.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004 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 <sys/time.h>
33 #include <sys/types.h>
34 #include <windows.h>
35 #include <io.h>
36
37 #include "util.h"
38 #include "sema.h"
39 #include "priv-io.h"
40 #include "debug.h"
41
42 /* We assume that a HANDLE can be represented by an int which should
43    be true for all i386 systems (HANDLE is defined as void *) and
44    these are the only systems for which Windows is available.  Further
45    we assume that -1 denotes an invalid handle.  */
46
47 #define fd_to_handle(a)  ((HANDLE)(a))
48 #define handle_to_fd(a)  ((int)(a))
49 #define pid_to_handle(a) ((HANDLE)(a))
50 #define handle_to_pid(a) ((int)(a))
51
52 #define READBUF_SIZE 4096
53 #define WRITEBUF_SIZE 4096
54 #define PIPEBUF_SIZE  4096
55 #define MAX_READERS 20
56 #define MAX_WRITERS 20
57
58 static struct
59 {
60   int inuse;
61   int fd;
62   _gpgme_close_notify_handler_t handler;
63   void *value;
64 } notify_table[256];
65 DEFINE_STATIC_LOCK (notify_table_lock);
66
67
68 struct reader_context_s {
69     HANDLE file_hd;
70     HANDLE thread_hd;   
71     DECLARE_LOCK (mutex);
72
73     int stop_me;
74     int eof;
75     int eof_shortcut;
76     int error;
77     int error_code;
78
79     HANDLE have_data_ev;  /* manually reset */
80     HANDLE have_space_ev; /* auto reset */
81     HANDLE stopped;
82     size_t readpos, writepos;
83     char buffer[READBUF_SIZE];
84 };
85
86
87 static struct {
88     volatile int used;
89     int fd;
90     struct reader_context_s *context;
91 } reader_table[MAX_READERS];
92 static int reader_table_size= MAX_READERS;
93 DEFINE_STATIC_LOCK (reader_table_lock);
94
95
96 struct writer_context_s {
97     HANDLE file_hd;
98     HANDLE thread_hd;   
99     DECLARE_LOCK (mutex);
100
101     int stop_me;
102     int error;
103     int error_code;
104
105     HANDLE have_data;  /* manually reset */
106     HANDLE is_empty;
107     HANDLE stopped;
108     size_t nbytes; 
109     char buffer[WRITEBUF_SIZE];
110 };
111
112
113 static struct {
114     volatile int used;
115     int fd;
116     struct writer_context_s *context;
117 } writer_table[MAX_WRITERS];
118 static int writer_table_size= MAX_WRITERS;
119 DEFINE_STATIC_LOCK (writer_table_lock);
120
121
122
123 static int
124 get_desired_thread_priority (void)
125 {
126   int value;
127
128   if (!_gpgme_get_conf_int ("IOThreadPriority", &value))
129     {
130       value = THREAD_PRIORITY_HIGHEST;
131       DEBUG1 ("** Using standard IOThreadPriority of %d\n", value);
132     }
133   else
134     DEBUG1 ("** Configured IOThreadPriority is %d\n", value);
135
136   return value;
137 }
138
139
140 static HANDLE
141 set_synchronize (HANDLE h)
142 {
143     HANDLE tmp;
144     
145     /* For NT we have to set the sync flag.  It seems that the only
146      * way to do it is by duplicating the handle.  Tsss.. */
147     if (!DuplicateHandle( GetCurrentProcess(), h,
148                           GetCurrentProcess(), &tmp,
149                           EVENT_MODIFY_STATE|SYNCHRONIZE, FALSE, 0 ) ) {
150         DEBUG1 ("** Set SYNCRONIZE failed: ec=%d\n", (int)GetLastError());
151     }
152     else {
153         CloseHandle (h);
154         h = tmp;
155     }
156     return h;
157 }
158
159
160
161 static DWORD CALLBACK 
162 reader (void *arg)
163 {
164     struct reader_context_s *c = arg;
165     int nbytes;
166     DWORD nread;
167
168     DEBUG2 ("reader thread %p for file %p started", c->thread_hd, c->file_hd );
169     for (;;) {
170         LOCK (c->mutex);
171         /* leave a 1 byte gap so that we can see whether it is empty or full*/
172         if ((c->writepos + 1) % READBUF_SIZE == c->readpos) { 
173             /* wait for space */
174             if (!ResetEvent (c->have_space_ev) )
175                 DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
176             UNLOCK (c->mutex);
177             DEBUG1 ("reader thread %p: waiting for space ...", c->thread_hd );
178             WaitForSingleObject (c->have_space_ev, INFINITE);
179             DEBUG1 ("reader thread %p: got space", c->thread_hd );
180             LOCK (c->mutex);
181         }
182         if ( c->stop_me ) {
183             UNLOCK (c->mutex);
184             break;
185         }
186         nbytes = (c->readpos + READBUF_SIZE - c->writepos-1) % READBUF_SIZE;
187         if ( nbytes > READBUF_SIZE - c->writepos )
188             nbytes = READBUF_SIZE - c->writepos;
189         UNLOCK (c->mutex);
190
191         DEBUG2 ("reader thread %p: reading %d bytes", c->thread_hd, nbytes );
192         if ( !ReadFile ( c->file_hd,
193                          c->buffer+c->writepos, nbytes, &nread, NULL) ) {
194             c->error_code = (int)GetLastError ();
195             if (c->error_code == ERROR_BROKEN_PIPE ) {
196                 c->eof=1;
197                 DEBUG1 ("reader thread %p: got eof (broken pipe)",
198                         c->thread_hd );
199             }
200             else {
201                 c->error = 1;
202                 DEBUG2 ("reader thread %p: read error: ec=%d",
203                         c->thread_hd, c->error_code );
204             }
205             break;
206         }
207         if ( !nread ) {
208             c->eof = 1;
209             DEBUG1 ("reader thread %p: got eof", c->thread_hd );
210             break;
211         }
212         DEBUG2 ("reader thread %p: got %d bytes", c->thread_hd, (int)nread );
213       
214         LOCK (c->mutex);
215         if (c->stop_me) {
216             UNLOCK (c->mutex);
217             break;
218         }
219         c->writepos = (c->writepos + nread) % READBUF_SIZE;
220         if ( !SetEvent (c->have_data_ev) )
221             DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
222         UNLOCK (c->mutex);
223     }
224     /* indicate that we have an error or eof */
225     if ( !SetEvent (c->have_data_ev) )
226         DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
227     DEBUG1 ("reader thread %p ended", c->thread_hd );
228     SetEvent (c->stopped);
229
230     return 0;
231 }
232
233
234 static struct reader_context_s *
235 create_reader (HANDLE fd)
236 {
237     struct reader_context_s *c;
238     SECURITY_ATTRIBUTES sec_attr;
239     DWORD tid;
240
241     DEBUG1 ("creating new read thread for file handle %p", fd );
242     memset (&sec_attr, 0, sizeof sec_attr );
243     sec_attr.nLength = sizeof sec_attr;
244     sec_attr.bInheritHandle = FALSE;
245
246     c = calloc (1, sizeof *c );
247     if (!c)
248         return NULL;
249
250     c->file_hd = fd;
251     c->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
252     c->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
253     c->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
254     if (!c->have_data_ev || !c->have_space_ev || !c->stopped ) {
255         DEBUG1 ("** CreateEvent failed: ec=%d\n", (int)GetLastError ());
256         if (c->have_data_ev)
257             CloseHandle (c->have_data_ev);
258         if (c->have_space_ev)
259             CloseHandle (c->have_space_ev);
260         if (c->stopped)
261             CloseHandle (c->stopped);
262         free (c);
263         return NULL;
264     }
265
266     c->have_data_ev = set_synchronize (c->have_data_ev);
267     INIT_LOCK (c->mutex);
268
269     c->thread_hd = CreateThread (&sec_attr, 0, reader, c, 0, &tid );
270     if (!c->thread_hd) {
271         DEBUG1 ("** failed to create reader thread: ec=%d\n",
272                  (int)GetLastError ());
273         DESTROY_LOCK (c->mutex);
274         if (c->have_data_ev)
275             CloseHandle (c->have_data_ev);
276         if (c->have_space_ev)
277             CloseHandle (c->have_space_ev);
278         if (c->stopped)
279             CloseHandle (c->stopped);
280         free (c);
281         return NULL;
282     }    
283     else {
284       /* We set the priority of the thread higher because we know that
285          it only runs for a short time.  This greatly helps to increase
286          the performance of the I/O. */
287       SetThreadPriority (c->thread_hd, get_desired_thread_priority ());
288     }
289
290     return c;
291 }
292
293 static void
294 destroy_reader (struct reader_context_s *c)
295 {
296     LOCK (c->mutex);
297     c->stop_me = 1;
298     if (c->have_space_ev) 
299         SetEvent (c->have_space_ev);
300     UNLOCK (c->mutex);
301
302     DEBUG1 ("waiting for thread %p termination ...", c->thread_hd );
303     WaitForSingleObject (c->stopped, INFINITE);
304     DEBUG1 ("thread %p has terminated", c->thread_hd );
305     
306     if (c->stopped)
307         CloseHandle (c->stopped);
308     if (c->have_data_ev)
309         CloseHandle (c->have_data_ev);
310     if (c->have_space_ev)
311         CloseHandle (c->have_space_ev);
312     CloseHandle (c->thread_hd);
313     DESTROY_LOCK (c->mutex);
314     free (c);
315 }
316
317
318 /* 
319  * Find a reader context or create a new one 
320  * Note that the reader context will last until a io_close.
321  */
322 static struct reader_context_s *
323 find_reader (int fd, int start_it)
324 {
325     int i;
326
327     for (i=0; i < reader_table_size ; i++ ) {
328         if ( reader_table[i].used && reader_table[i].fd == fd )
329             return reader_table[i].context;
330     }
331     if (!start_it)
332         return NULL;
333
334     LOCK (reader_table_lock);
335     for (i=0; i < reader_table_size; i++ ) {
336         if (!reader_table[i].used) {
337             reader_table[i].fd = fd;
338             reader_table[i].context = create_reader (fd_to_handle (fd));
339             reader_table[i].used = 1;
340             UNLOCK (reader_table_lock);
341             return reader_table[i].context;
342         }
343     }
344     UNLOCK (reader_table_lock);
345     return NULL;
346 }
347
348
349 static void
350 kill_reader (int fd)
351 {
352     int i;
353
354     LOCK (reader_table_lock);
355     for (i=0; i < reader_table_size; i++ ) {
356         if (reader_table[i].used && reader_table[i].fd == fd ) {
357             destroy_reader (reader_table[i].context);
358             reader_table[i].context = NULL;
359             reader_table[i].used = 0;
360             break;
361         }
362     }
363     UNLOCK (reader_table_lock);
364 }
365
366
367
368 int
369 _gpgme_io_read ( int fd, void *buffer, size_t count )
370 {
371     int nread;
372     struct reader_context_s *c = find_reader (fd,1);
373
374     DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int)count );
375     if ( !c ) {
376         DEBUG0 ( "no reader thread\n");
377         errno = EBADF;
378         return -1;
379     }
380     if (c->eof_shortcut) {
381         DEBUG1 ("fd %d: EOF (again)", fd );
382         return 0;
383     }
384
385     LOCK (c->mutex);
386     if (c->readpos == c->writepos && !c->error) { /*no data avail*/
387         UNLOCK (c->mutex);
388         DEBUG2 ("fd %d: waiting for data from thread %p", fd, c->thread_hd);
389         WaitForSingleObject (c->have_data_ev, INFINITE);
390         DEBUG2 ("fd %d: data from thread %p available", fd, c->thread_hd);
391         LOCK (c->mutex);
392     }
393     
394     if (c->readpos == c->writepos || c->error) {
395         UNLOCK (c->mutex);
396         c->eof_shortcut = 1;
397         if (c->eof) {
398             DEBUG1 ("fd %d: EOF", fd );
399             return 0;
400         }
401         if (!c->error) {
402             DEBUG1 ("fd %d: EOF but eof flag not set", fd );
403             return 0;
404         }
405         DEBUG1 ("fd %d: read error", fd );
406         errno = c->error_code;
407         return -1;
408     }
409       
410     nread = c->readpos < c->writepos? c->writepos - c->readpos
411                                     : READBUF_SIZE - c->readpos;
412     if (nread > count)
413         nread = count;
414     memcpy (buffer, c->buffer+c->readpos, nread);
415     c->readpos = (c->readpos + nread) % READBUF_SIZE;
416     if (c->readpos == c->writepos && !c->eof) {
417         if ( !ResetEvent (c->have_data_ev) )
418             DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
419     }
420     if (!SetEvent (c->have_space_ev))
421         DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
422     UNLOCK (c->mutex);
423
424     DEBUG2 ("fd %d: got %d bytes\n", fd, nread );
425     if (nread > 0)
426       _gpgme_debug (2, "fd %d: got `%.*s'\n", fd, nread, buffer);
427
428     return nread;
429 }
430 /*
431  * The writer does use a simple buffering strategy so that we are
432  * informed about write errors as soon as possible (i.e. with the the
433  * next call to the write function
434  */
435 static DWORD CALLBACK 
436 writer (void *arg)
437 {
438     struct writer_context_s *c = arg;
439     DWORD nwritten;
440
441     DEBUG2 ("writer thread %p for file %p started", c->thread_hd, c->file_hd );
442     for (;;) {
443         LOCK (c->mutex);
444         if ( c->stop_me ) {
445             UNLOCK (c->mutex);
446             break;
447         }
448         if ( !c->nbytes ) { 
449             if (!SetEvent (c->is_empty))
450                 DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
451             if (!ResetEvent (c->have_data) )
452                 DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
453             UNLOCK (c->mutex);
454             DEBUG1 ("writer thread %p: idle ...", c->thread_hd );
455             WaitForSingleObject (c->have_data, INFINITE);
456             DEBUG1 ("writer thread %p: got data to send", c->thread_hd );
457             LOCK (c->mutex);
458         }
459         if ( c->stop_me ) {
460             UNLOCK (c->mutex);
461             break;
462         }
463         UNLOCK (c->mutex);
464
465         DEBUG2 ("writer thread %p: writing %d bytes",
466                 c->thread_hd, c->nbytes );
467         if ( c->nbytes && !WriteFile ( c->file_hd,  c->buffer, c->nbytes,
468                                        &nwritten, NULL)) {
469             c->error_code = (int)GetLastError ();
470             c->error = 1;
471             DEBUG2 ("writer thread %p: write error: ec=%d",
472                     c->thread_hd, c->error_code );
473             break;
474         }
475         DEBUG2 ("writer thread %p: wrote %d bytes",
476                 c->thread_hd, (int)nwritten );
477       
478         LOCK (c->mutex);
479         c->nbytes -= nwritten;
480         UNLOCK (c->mutex);
481     }
482     /* indicate that we have an error  */
483     if ( !SetEvent (c->is_empty) )
484         DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
485     DEBUG1 ("writer thread %p ended", c->thread_hd );
486     SetEvent (c->stopped);
487
488     return 0;
489 }
490
491
492 static struct writer_context_s *
493 create_writer (HANDLE fd)
494 {
495     struct writer_context_s *c;
496     SECURITY_ATTRIBUTES sec_attr;
497     DWORD tid;
498
499     DEBUG1 ("creating new write thread for file handle %p", fd );
500     memset (&sec_attr, 0, sizeof sec_attr );
501     sec_attr.nLength = sizeof sec_attr;
502     sec_attr.bInheritHandle = FALSE;
503
504     c = calloc (1, sizeof *c );
505     if (!c)
506         return NULL;
507
508     c->file_hd = fd;
509     c->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
510     c->is_empty  = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
511     c->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
512     if (!c->have_data || !c->is_empty || !c->stopped ) {
513         DEBUG1 ("** CreateEvent failed: ec=%d\n", (int)GetLastError ());
514         if (c->have_data)
515             CloseHandle (c->have_data);
516         if (c->is_empty)
517             CloseHandle (c->is_empty);
518         if (c->stopped)
519             CloseHandle (c->stopped);
520         free (c);
521         return NULL;
522     }
523
524     c->is_empty = set_synchronize (c->is_empty);
525     INIT_LOCK (c->mutex);
526
527     c->thread_hd = CreateThread (&sec_attr, 0, writer, c, 0, &tid );
528     if (!c->thread_hd) {
529         DEBUG1 ("** failed to create writer thread: ec=%d\n",
530                  (int)GetLastError ());
531         DESTROY_LOCK (c->mutex);
532         if (c->have_data)
533             CloseHandle (c->have_data);
534         if (c->is_empty)
535             CloseHandle (c->is_empty);
536         if (c->stopped)
537             CloseHandle (c->stopped);
538         free (c);
539         return NULL;
540     }    
541     else {
542       /* We set the priority of the thread higher because we know that
543          it only runs for a short time.  This greatly helps to increase
544          the performance of the I/O. */
545       SetThreadPriority (c->thread_hd, get_desired_thread_priority ());
546     }
547
548     return c;
549 }
550
551 static void
552 destroy_writer (struct writer_context_s *c)
553 {
554     LOCK (c->mutex);
555     c->stop_me = 1;
556     if (c->have_data) 
557         SetEvent (c->have_data);
558     UNLOCK (c->mutex);
559
560     DEBUG1 ("waiting for thread %p termination ...", c->thread_hd );
561     WaitForSingleObject (c->stopped, INFINITE);
562     DEBUG1 ("thread %p has terminated", c->thread_hd );
563     
564     if (c->stopped)
565         CloseHandle (c->stopped);
566     if (c->have_data)
567         CloseHandle (c->have_data);
568     if (c->is_empty)
569         CloseHandle (c->is_empty);
570     CloseHandle (c->thread_hd);
571     DESTROY_LOCK (c->mutex);
572     free (c);
573 }
574
575
576 /* 
577  * Find a writer context or create a new one 
578  * Note that the writer context will last until a io_close.
579  */
580 static struct writer_context_s *
581 find_writer (int fd, int start_it)
582 {
583     int i;
584
585     for (i=0; i < writer_table_size ; i++ ) {
586         if ( writer_table[i].used && writer_table[i].fd == fd )
587             return writer_table[i].context;
588     }
589     if (!start_it)
590         return NULL;
591
592     LOCK (writer_table_lock);
593     for (i=0; i < writer_table_size; i++ ) {
594         if (!writer_table[i].used) {
595             writer_table[i].fd = fd;
596             writer_table[i].context = create_writer (fd_to_handle (fd));
597             writer_table[i].used = 1;
598             UNLOCK (writer_table_lock);
599             return writer_table[i].context;
600         }
601     }
602     UNLOCK (writer_table_lock);
603     return NULL;
604 }
605
606
607 static void
608 kill_writer (int fd)
609 {
610     int i;
611
612     LOCK (writer_table_lock);
613     for (i=0; i < writer_table_size; i++ ) {
614         if (writer_table[i].used && writer_table[i].fd == fd ) {
615             destroy_writer (writer_table[i].context);
616             writer_table[i].context = NULL;
617             writer_table[i].used = 0;
618             break;
619         }
620     }
621     UNLOCK (writer_table_lock);
622 }
623
624
625
626
627 int
628 _gpgme_io_write ( int fd, const void *buffer, size_t count )
629 {
630     struct writer_context_s *c = find_writer (fd,1);
631
632     DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int)count );
633     _gpgme_debug (2, "fd %d: write `%.*s'\n", fd, (int) count, buffer);
634     if ( !c ) {
635         DEBUG0 ( "no writer thread\n");
636         errno = EBADF;
637         return -1;
638     }
639
640     LOCK (c->mutex);
641     if ( !c->error && c->nbytes ) { /* bytes are pending for send */
642         /* Reset the is_empty event.  Better safe than sorry.  */
643         if (!ResetEvent (c->is_empty))
644             DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
645         UNLOCK (c->mutex);
646         DEBUG2 ("fd %d: waiting for empty buffer in thread %p",
647                 fd, c->thread_hd);
648         WaitForSingleObject (c->is_empty, INFINITE);
649         DEBUG2 ("fd %d: thread %p buffer is empty", fd, c->thread_hd);
650         LOCK (c->mutex);
651     }
652     
653     if ( c->error) {
654         UNLOCK (c->mutex);
655         DEBUG1 ("fd %d: write error", fd );
656         errno = c->error_code;
657         return -1;
658     }
659
660     /* If no error occured, the number of bytes in the buffer must be
661        zero.  */
662     assert (!c->nbytes);
663
664     if (count > WRITEBUF_SIZE)
665         count = WRITEBUF_SIZE;
666     memcpy (c->buffer, buffer, count);
667     c->nbytes = count;
668
669     /* We have to reset the is_empty event early, because it is also
670        used by the select() implementation to probe the channel.  */
671     if (!ResetEvent (c->is_empty))
672         DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
673     if (!SetEvent (c->have_data))
674         DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
675     UNLOCK (c->mutex);
676
677     DEBUG2 ("fd %d:         copied %d bytes\n",
678                    fd, (int)count );
679     return (int)count;
680 }
681
682
683 int
684 _gpgme_io_pipe ( int filedes[2], int inherit_idx )
685 {
686     HANDLE r, w;
687     SECURITY_ATTRIBUTES sec_attr;
688
689     memset (&sec_attr, 0, sizeof sec_attr );
690     sec_attr.nLength = sizeof sec_attr;
691     sec_attr.bInheritHandle = FALSE;
692     
693     if (!CreatePipe ( &r, &w, &sec_attr, PIPEBUF_SIZE))
694         return -1;
695     /* Make one end inheritable. */
696     if ( inherit_idx == 0 ) {
697         HANDLE h;
698         if (!DuplicateHandle( GetCurrentProcess(), r,
699                               GetCurrentProcess(), &h, 0,
700                               TRUE, DUPLICATE_SAME_ACCESS ) ) {
701             DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
702             CloseHandle (r);
703             CloseHandle (w);
704             return -1;
705         }
706         CloseHandle (r);
707         r = h;
708     }
709     else if ( inherit_idx == 1 ) {
710         HANDLE h;
711         if (!DuplicateHandle( GetCurrentProcess(), w,
712                               GetCurrentProcess(), &h, 0,
713                               TRUE, DUPLICATE_SAME_ACCESS ) ) {
714             DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
715             CloseHandle (r);
716             CloseHandle (w);
717             return -1;
718         }
719         CloseHandle (w);
720         w = h;
721     }
722
723     filedes[0] = handle_to_fd (r);
724     filedes[1] = handle_to_fd (w);
725     DEBUG5 ("CreatePipe %p %p %d %d inherit=%d\n", r, w,
726                    filedes[0], filedes[1], inherit_idx );
727     return 0;
728 }
729
730 int
731 _gpgme_io_close ( int fd )
732 {
733     int i;
734     _gpgme_close_notify_handler_t handler = NULL;
735     void *value = NULL;
736
737     if ( fd == -1 )
738         return -1;
739
740     DEBUG1 ("** closing handle for fd %d\n", fd);
741     kill_reader (fd);
742     kill_writer (fd);
743     LOCK (notify_table_lock);
744     for ( i=0; i < DIM (notify_table); i++ ) {
745         if (notify_table[i].inuse && notify_table[i].fd == fd) {
746             handler = notify_table[i].handler;
747             value   = notify_table[i].value;
748             notify_table[i].handler = NULL;
749             notify_table[i].value = NULL;
750             notify_table[i].inuse = 0;
751             break;
752         }
753     }
754     UNLOCK (notify_table_lock);
755     if (handler)
756         handler (fd, value);
757
758     if ( !CloseHandle (fd_to_handle (fd)) ) { 
759         DEBUG2 ("CloseHandle for fd %d failed: ec=%d\n",
760                  fd, (int)GetLastError ());
761         return -1;
762     }
763
764     return 0;
765 }
766
767 int
768 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
769                             void *value)
770 {
771     int i;
772
773     assert (fd != -1);
774
775     LOCK (notify_table_lock);
776     for (i=0; i < DIM (notify_table); i++ ) {
777         if ( notify_table[i].inuse && notify_table[i].fd == fd )
778             break;
779     }
780     if ( i == DIM (notify_table) ) {
781         for (i=0; i < DIM (notify_table); i++ ) {
782             if ( !notify_table[i].inuse )
783                 break;
784         }
785     }
786     if ( i == DIM (notify_table) ) {
787         UNLOCK (notify_table_lock);
788         return -1;
789     }
790     notify_table[i].fd = fd;
791     notify_table[i].handler = handler;
792     notify_table[i].value = value;
793     notify_table[i].inuse = 1;
794     UNLOCK (notify_table_lock);
795     DEBUG2 ("set notification for fd %d (idx=%d)", fd, i );
796     return 0;
797 }
798
799
800 int
801 _gpgme_io_set_nonblocking ( int fd )
802 {
803     return 0;
804 }
805
806
807 static char *
808 build_commandline (char **argv)
809 {
810   int i;
811   int j;
812   int n = 0;
813   char *buf;
814   char *p;
815   
816   /* We have to quote some things because under Windows the program
817      parses the commandline and does some unquoting.  We enclose the
818      whole argument in double-quotes, and escape literal double-quotes
819      as well as backslashes with a backslash.  We end up with a
820      trailing space at the end of the line, but that is harmless.  */
821   for (i = 0; argv[i]; i++)
822     {
823       p = argv[i];
824       /* The leading double-quote.  */
825       n++;
826       while (*p)
827         {
828           /* An extra one for each literal that must be escaped.  */
829           if (*p == '\\' || *p == '"')
830             n++;
831           n++;
832           p++;
833         }
834       /* The trailing double-quote and the delimiter.  */
835       n += 2;
836     }
837   /* And a trailing zero.  */
838   n++;
839
840   buf = p = malloc (n);
841   if (!buf)
842     return NULL;
843   for (i = 0; argv[i]; i++)
844     {
845       char *argvp = argv[i];
846
847       *(p++) = '"';
848       while (*argvp)
849         {
850           if (*argvp == '\\' || *argvp == '"')
851             *(p++) = '\\';
852           *(p++) = *(argvp++);
853         }
854       *(p++) = '"';
855       *(p++) = ' ';
856     }
857   *(p++) = 0;
858
859   return buf;
860 }
861
862
863 int
864 _gpgme_io_spawn ( const char *path, char **argv,
865                   struct spawn_fd_item_s *fd_child_list,
866                   struct spawn_fd_item_s *fd_parent_list )
867 {
868     SECURITY_ATTRIBUTES sec_attr;
869     PROCESS_INFORMATION pi = {
870         NULL,      /* returns process handle */
871         0,         /* returns primary thread handle */
872         0,         /* returns pid */
873         0         /* returns tid */
874     };
875     STARTUPINFO si;
876     char *envblock = NULL;
877     int cr_flags = CREATE_DEFAULT_ERROR_MODE
878                  | GetPriorityClass (GetCurrentProcess ());
879     int i;
880     char *arg_string;
881     int duped_stdin = 0;
882     int duped_stderr = 0;
883     HANDLE hnul = INVALID_HANDLE_VALUE;
884     /* FIXME.  */
885     int debug_me = 0;
886
887     memset (&sec_attr, 0, sizeof sec_attr );
888     sec_attr.nLength = sizeof sec_attr;
889     sec_attr.bInheritHandle = FALSE;
890
891     arg_string = build_commandline ( argv );
892     if (!arg_string )
893         return -1; 
894
895     memset (&si, 0, sizeof si);
896     si.cb = sizeof (si);
897     si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
898     si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
899     si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
900     si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
901     si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
902
903     for (i=0; fd_child_list[i].fd != -1; i++ ) {
904         if (fd_child_list[i].dup_to == 0 ) {
905             si.hStdInput = fd_to_handle (fd_child_list[i].fd);
906             DEBUG1 ("using %d for stdin", fd_child_list[i].fd );
907             duped_stdin=1;
908         }
909         else if (fd_child_list[i].dup_to == 1 ) {
910             si.hStdOutput = fd_to_handle (fd_child_list[i].fd);
911             DEBUG1 ("using %d for stdout", fd_child_list[i].fd );
912         }
913         else if (fd_child_list[i].dup_to == 2 ) {
914             si.hStdError = fd_to_handle (fd_child_list[i].fd);
915             DEBUG1 ("using %d for stderr", fd_child_list[i].fd );
916             duped_stderr = 1;
917         }
918     }
919
920     if( !duped_stdin || !duped_stderr ) {
921         SECURITY_ATTRIBUTES sa;
922
923         memset (&sa, 0, sizeof sa );
924         sa.nLength = sizeof sa;
925         sa.bInheritHandle = TRUE;
926         hnul = CreateFile ( "nul",
927                             GENERIC_READ|GENERIC_WRITE,
928                             FILE_SHARE_READ|FILE_SHARE_WRITE,
929                             &sa,
930                             OPEN_EXISTING,
931                             FILE_ATTRIBUTE_NORMAL,
932                             NULL );
933         if ( hnul == INVALID_HANDLE_VALUE ) {
934             DEBUG1 ("can't open `nul': ec=%d\n", (int)GetLastError ());
935             free (arg_string);
936             return -1;
937         }
938         /* Make sure that the process has a connected stdin */
939         if ( !duped_stdin ) {
940             si.hStdInput = hnul;
941             DEBUG1 ("using %d for dummy stdin", (int)hnul );
942         }
943         /* We normally don't want all the normal output */
944         if ( !duped_stderr ) {
945             si.hStdError = hnul;
946             DEBUG1 ("using %d for dummy stderr", (int)hnul );
947         }
948     }
949
950     DEBUG2 ("CreateProcess, path=`%s' args=`%s'", path, arg_string);
951     cr_flags |= CREATE_SUSPENDED; 
952     if ( !CreateProcessA (path,
953                           arg_string,
954                           &sec_attr,     /* process security attributes */
955                           &sec_attr,     /* thread security attributes */
956                           TRUE,          /* inherit handles */
957                           cr_flags,      /* creation flags */
958                           envblock,      /* environment */
959                           NULL,          /* use current drive/directory */
960                           &si,           /* startup information */
961                           &pi            /* returns process information */
962         ) ) {
963         DEBUG1 ("CreateProcess failed: ec=%d\n", (int) GetLastError ());
964         free (arg_string);
965         return -1;
966     }
967
968     /* Close the /dev/nul handle if used. */
969     if (hnul != INVALID_HANDLE_VALUE ) {
970         if ( !CloseHandle ( hnul ) )
971             DEBUG1 ("CloseHandle(hnul) failed: ec=%d\n", (int)GetLastError());
972     }
973
974     /* Close the other ends of the pipes. */
975     for (i = 0; fd_parent_list[i].fd != -1; i++)
976       _gpgme_io_close (fd_parent_list[i].fd);
977
978     DEBUG4 ("CreateProcess ready\n"
979             "-   hProcess=%p  hThread=%p\n"
980             "-   dwProcessID=%d dwThreadId=%d\n",
981             pi.hProcess, pi.hThread, 
982             (int) pi.dwProcessId, (int) pi.dwThreadId);
983
984     if ( ResumeThread ( pi.hThread ) < 0 ) {
985         DEBUG1 ("ResumeThread failed: ec=%d\n", (int)GetLastError ());
986     }
987
988     if ( !CloseHandle (pi.hThread) ) { 
989         DEBUG1 ("CloseHandle of thread failed: ec=%d\n",
990                  (int)GetLastError ());
991     }
992
993     return handle_to_pid (pi.hProcess);
994 }
995
996
997 /*
998  * Select on the list of fds.
999  * Returns: -1 = error
1000  *           0 = timeout or nothing to select
1001  *          >0 = number of signaled fds
1002  */
1003 int
1004 _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds, int nonblock )
1005 {
1006     HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
1007     int    waitidx[MAXIMUM_WAIT_OBJECTS];
1008     int code, nwait;
1009     int i, any;
1010     int count;
1011     void *dbg_help;
1012
1013  restart:
1014     DEBUG_BEGIN (dbg_help, 3, "select on [ ");
1015     any = 0;
1016     nwait = 0;
1017     count = 0;
1018     for ( i=0; i < nfds; i++ ) {
1019         if ( fds[i].fd == -1 ) 
1020             continue;
1021         fds[i].signaled = 0;
1022         if ( fds[i].for_read || fds[i].for_write ) {
1023             if ( fds[i].frozen ) {
1024                 DEBUG_ADD1 (dbg_help, "f%d ", fds[i].fd );
1025             }
1026             else if ( fds[i].for_read ) {
1027                 struct reader_context_s *c = find_reader (fds[i].fd,1);
1028                 
1029                 if (!c) { 
1030                     DEBUG1 ("oops: no reader thread for fd %d", fds[i].fd);
1031                 }
1032                 else {
1033                     if ( nwait >= DIM (waitbuf) ) {
1034                         DEBUG_END (dbg_help, "oops ]");
1035                         DEBUG0 ("Too many objects for WFMO!" );
1036                         return -1;
1037                     }
1038                     waitidx[nwait]   = i;
1039                     waitbuf[nwait++] = c->have_data_ev;
1040                 }
1041                 DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd );
1042                 any = 1;
1043             }
1044             else if ( fds[i].for_write ) {
1045                 struct writer_context_s *c = find_writer (fds[i].fd,1);
1046                 
1047                 if (!c) { 
1048                     DEBUG1 ("oops: no writer thread for fd %d", fds[i].fd);
1049                 }
1050                 else {
1051                     if ( nwait >= DIM (waitbuf) ) {
1052                         DEBUG_END (dbg_help, "oops ]");
1053                         DEBUG0 ("Too many objects for WFMO!" );
1054                         return -1;
1055                     }
1056                     waitidx[nwait]   = i;
1057                     waitbuf[nwait++] = c->is_empty;
1058                 }
1059                 DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd );
1060                 any = 1;
1061             }
1062         }
1063     }
1064     DEBUG_END (dbg_help, "]");
1065     if (!any) 
1066         return 0;
1067
1068     code = WaitForMultipleObjects ( nwait, waitbuf, 0, nonblock ? 0 : 1000);
1069     if ( code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait ) {
1070         /* This WFMO is a really silly function:  It does return either
1071          * the index of the signaled object or if 2 objects have been
1072          * signalled at the same time, the index of the object with the
1073          * lowest object is returned - so and how do we find out
1074          * how many objects have been signaled???.
1075          * The only solution I can imagine is to test each object starting
1076          * with the returned index individually - how dull.
1077          */
1078         any = 0;
1079         for (i=code - WAIT_OBJECT_0; i < nwait; i++ ) {
1080             if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0) {
1081                 assert (waitidx[i] >=0 && waitidx[i] < nfds);
1082                 fds[waitidx[i]].signaled = 1;
1083                 any = 1;
1084                 count++;
1085             }
1086         }
1087         if (!any) {
1088             DEBUG0 ("Oops: No signaled objects found after WFMO");
1089             count = -1;
1090         }
1091     }
1092     else if ( code == WAIT_TIMEOUT ) {
1093         DEBUG0 ("WFMO timed out\n" );
1094     }  
1095     else if (code == WAIT_FAILED ) {
1096         int le = (int)GetLastError ();
1097         if ( le == ERROR_INVALID_HANDLE ) {
1098             int k, j = handle_to_fd (waitbuf[i]);
1099                     
1100             DEBUG1 ("WFMO invalid handle %d removed\n", j);
1101             for (k=0 ; k < nfds; k++ ) {
1102                 if ( fds[k].fd == j ) {
1103                     fds[k].for_read = fds[k].for_write = 0;
1104                     goto restart;
1105                 }
1106             }
1107             DEBUG0 (" oops, or not???\n");
1108         }
1109         DEBUG1 ("WFMO failed: %d\n", le );
1110         count = -1;
1111     }
1112     else {
1113         DEBUG1 ("WFMO returned %d\n", code );
1114         count = -1;
1115     }
1116
1117     if ( count ) {
1118         DEBUG_BEGIN (dbg_help, 3, " signaled [ ");
1119         for ( i=0; i < nfds; i++ ) {
1120             if ( fds[i].fd == -1 ) 
1121                 continue;
1122             if ( (fds[i].for_read || fds[i].for_write) && fds[i].signaled ) {
1123                 DEBUG_ADD2 (dbg_help, "%c%d ",
1124                             fds[i].for_read? 'r':'w',fds[i].fd );
1125             }
1126         }
1127         DEBUG_END (dbg_help, "]");
1128     }
1129     
1130     return count;
1131 }
1132
1133 void
1134 _gpgme_io_subsystem_init (void)
1135 {
1136   
1137 }
1138
1139
1140 /* Write the printable version of FD to the buffer BUF of length
1141    BUFLEN.  The printable version is the representation on the command
1142    line that the child process expects.  */
1143 int
1144 _gpgme_io_fd2str (char *buf, int buflen, int fd)
1145 {
1146   return snprintf (buf, buflen, "%d", fd);
1147 }
1148
1149 \f
1150 /* The following interface is only useful for GPGME Glib.  */
1151
1152 /* Look up the giochannel for file descriptor FD.  */
1153 void *
1154 gpgme_get_giochannel (int fd)
1155 {
1156   return NULL;
1157 }
1158