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