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