1 |
twoaday |
2 |
/* 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; i++ ) { |
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 |
|
|
} |