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