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