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