1
/* w32-glib-io.c - W32 Glib I/O functions
2
Copyright (C) 2000 Werner Koch (dd9jn)
3
Copyright (C) 2001, 2002, 2004, 2005 g10 Code GmbH
5
This file is part of GPGME.
7
GPGME is free software; you can redistribute it and/or modify it
8
under the terms of the GNU Lesser General Public License as
9
published by the Free Software Foundation; either version 2.1 of
10
the License, or (at your option) any later version.
12
GPGME is distributed in the hope that it will be useful, but
13
WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
Lesser General Public License for more details.
17
You should have received a copy of the GNU Lesser General Public
18
License along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
34
#include <sys/types.h>
46
#define O_BINARY _O_BINARY
53
/* This file is an ugly hack to get GPGME working with glib on Windows
54
targets. On Windows, you can not select() on file descriptors.
55
The only way to check if there is something to read is to read
56
something. This means that GPGME can not let glib check for data
57
without letting glib also handle the data on Windows targets.
59
The ugly consequence is that we need to work on GIOChannels in
60
GPGME, creating a glib dependency. Also, we need to export an
61
interface for the application to get at GPGME's GIOChannel. There
62
is no good way to abstract all this with callbacks, because the
63
whole thing is also interconnected with the creation of pipes and
66
The following rule applies only to this I/O backend:
68
* ALL operations must use the user defined event loop. GPGME can
69
not anymore provide its own event loop. This is mostly a sanity
70
requirement: Although we have in theory all information we need to
71
make the GPGME W32 code for select still work, it would be a big
72
complication and require changes throughout GPGME.
74
Eventually, we probably have to bite the bullet and make some
75
really nice callback interfaces to let the user control all this at
76
a per-context level. */
84
/* The boolean PRIMARY is true if this file descriptor caused the
85
allocation of CHAN. Only then should CHAN be destroyed when this
86
FD is closed. This, together with the fact that dup'ed file
87
descriptors are closed before the file descriptors from which
88
they are dup'ed are closed, ensures that CHAN is always valid,
89
and shared among all file descriptors refering to the same
92
The logic behind this is that there is only one reason for us to
93
dup file descriptors anyway: to allow simpler book-keeping of
94
file descriptors shared between GPGME and libassuan, which both
95
want to close something. Using the same channel for these
96
duplicates works just fine (and in fact, using different channels
97
does not work because the W32 backend in glib does not support
98
that: One would end up with several competing reader/writer
101
} giochannel_table[MAX_SLAFD];
105
find_channel (int fd, int create)
107
if (fd < 0 || fd >= MAX_SLAFD)
110
if (create && !giochannel_table[fd].chan)
112
giochannel_table[fd].chan = g_io_channel_win32_new_fd (fd);
113
giochannel_table[fd].primary = 1;
114
g_io_channel_set_encoding (giochannel_table[fd].chan, NULL, NULL);
115
g_io_channel_set_buffered (giochannel_table[fd].chan, FALSE);
118
return giochannel_table[fd].chan;
122
/* Compatibility interface. Obsolete. */
124
gpgme_get_giochannel (int fd)
126
return find_channel (fd, 0);
130
/* Look up the giochannel for "file descriptor" FD. */
132
gpgme_get_fdptr (int fd)
134
return find_channel (fd, 0);
138
/* Write the printable version of FD to the buffer BUF of length
139
BUFLEN. The printable version is the representation on the command
140
line that the child process expects. */
142
_gpgme_io_fd2str (char *buf, int buflen, int fd)
144
TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd);
145
TRACE_SUC1 ("syshd=%p", _get_osfhandle (fd));
146
return snprintf (buf, buflen, "%d", (int) _get_osfhandle (fd));
151
_gpgme_io_subsystem_init (void)
158
_gpgme_close_notify_handler_t handler;
160
} notify_table[MAX_SLAFD];
164
_gpgme_io_read (int fd, void *buffer, size_t count)
170
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
171
"buffer=%p, count=%u", buffer, count);
173
chan = find_channel (fd, 0);
176
TRACE_LOG ("no channel registered");
178
return TRACE_SYSRES (-1);
180
TRACE_LOG1 ("channel %p", chan);
184
status = g_io_channel_read_chars (chan, (gchar *) buffer,
185
count, &nread, &err);
188
TRACE_LOG2 ("status %i, err %s", status, err->message);
193
if (status == G_IO_STATUS_EOF)
195
else if (status != G_IO_STATUS_NORMAL)
197
TRACE_LOG1 ("status %d", status);
202
TRACE_LOGBUF (buffer, nread);
205
return TRACE_SYSRES (nread);
210
_gpgme_io_write (int fd, const void *buffer, size_t count)
216
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
217
"buffer=%p, count=%u", buffer, count);
218
TRACE_LOGBUF (buffer, count);
220
chan = find_channel (fd, 0);
223
TRACE_LOG ("fd %d: no channel registered");
228
status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
230
if (status != G_IO_STATUS_NORMAL)
237
return TRACE_SYSRES (nwritten);
242
_gpgme_io_pipe (int filedes[2], int inherit_idx)
245
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
246
"inherit_idx=%i (GPGME uses it for %s)",
247
inherit_idx, inherit_idx ? "reading" : "writing");
249
#define PIPEBUF_SIZE 4096
250
if (_pipe (filedes, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
251
return TRACE_SYSRES (-1);
253
/* Make one end inheritable. */
254
if (inherit_idx == 0)
258
new_read = _dup (filedes[0]);
260
filedes[0] = new_read;
265
return TRACE_SYSRES (-1);
268
else if (inherit_idx == 1)
272
new_write = _dup (filedes[1]);
274
filedes[1] = new_write;
279
return TRACE_SYSRES (-1);
283
/* Now we have a pipe with the right end inheritable. The other end
284
should have a giochannel. */
285
chan = find_channel (filedes[1 - inherit_idx], 1);
288
int saved_errno = errno;
292
return TRACE_SYSRES (-1);
295
return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
296
filedes[0], (HANDLE) _get_osfhandle (filedes[0]),
297
filedes[1], (HANDLE) _get_osfhandle (filedes[1]),
303
_gpgme_io_close (int fd)
305
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
307
if (fd < 0 || fd >= MAX_SLAFD)
310
return TRACE_SYSRES (-1);
313
/* First call the notify handler. */
314
if (notify_table[fd].handler)
316
notify_table[fd].handler (fd, notify_table[fd].value);
317
notify_table[fd].handler = NULL;
318
notify_table[fd].value = NULL;
321
/* Then do the close. */
322
if (giochannel_table[fd].chan)
324
if (giochannel_table[fd].primary)
325
g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
329
g_io_channel_unref (giochannel_table[fd].chan);
330
giochannel_table[fd].chan = NULL;
341
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
344
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
345
"close_handler=%p/%p", handler, value);
349
if (fd < 0 || fd >= (int) DIM (notify_table))
352
return TRACE_SYSRES (-1);
354
notify_table[fd].handler = handler;
355
notify_table[fd].value = value;
356
return TRACE_SYSRES (0);
361
_gpgme_io_set_nonblocking (int fd)
366
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
368
chan = find_channel (fd, 0);
372
return TRACE_SYSRES (-1);
375
status = g_io_channel_set_flags (chan,
376
g_io_channel_get_flags (chan) |
377
G_IO_FLAG_NONBLOCK, NULL);
378
if (status != G_IO_STATUS_NORMAL)
381
/* glib 1.9.2 does not implement set_flags and returns an
384
return TRACE_SYSRES (-1);
386
TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
391
return TRACE_SYSRES (0);
396
build_commandline (char **argv)
403
/* We have to quote some things because under Windows the program
404
parses the commandline and does some unquoting. We enclose the
405
whole argument in double-quotes, and escape literal double-quotes
406
as well as backslashes with a backslash. We end up with a
407
trailing space at the end of the line, but that is harmless. */
408
for (i = 0; argv[i]; i++)
411
/* The leading double-quote. */
415
/* An extra one for each literal that must be escaped. */
416
if (*p == '\\' || *p == '"')
421
/* The trailing double-quote and the delimiter. */
424
/* And a trailing zero. */
427
buf = p = malloc (n);
430
for (i = 0; argv[i]; i++)
432
char *argvp = argv[i];
437
if (*argvp == '\\' || *argvp == '"')
451
_gpgme_io_spawn (const char *path, char **argv,
452
struct spawn_fd_item_s *fd_list, pid_t *r_pid)
454
SECURITY_ATTRIBUTES sec_attr;
455
PROCESS_INFORMATION pi =
457
NULL, /* returns process handle */
458
0, /* returns primary thread handle */
463
int cr_flags = CREATE_DEFAULT_ERROR_MODE
464
| GetPriorityClass (GetCurrentProcess ());
473
TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
478
TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
482
/* We do not inherit any handles by default, and just insert those
483
handles we want the child to have afterwards. But some handle
484
values occur on the command line, and we need to move
485
stdin/out/err to the right location. So we use a wrapper program
486
which gets the information from a temporary file. */
487
if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
489
TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
490
return TRACE_SYSRES (-1);
492
TRACE_LOG1 ("tmp_name = %s", tmp_name);
494
args = calloc (2 + i + 1, sizeof (*args));
495
args[0] = (char *) _gpgme_get_w32spawn_path ();
498
memcpy (&args[3], &argv[1], i * sizeof (*args));
500
memset (&sec_attr, 0, sizeof sec_attr);
501
sec_attr.nLength = sizeof sec_attr;
502
sec_attr.bInheritHandle = FALSE;
504
arg_string = build_commandline (args);
509
DeleteFile (tmp_name);
510
return TRACE_SYSRES (-1);
513
memset (&si, 0, sizeof si);
515
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
516
si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
517
si.hStdInput = INVALID_HANDLE_VALUE;
518
si.hStdOutput = INVALID_HANDLE_VALUE;
519
si.hStdError = INVALID_HANDLE_VALUE;
521
cr_flags |= CREATE_SUSPENDED;
522
cr_flags |= DETACHED_PROCESS;
523
if (!CreateProcessA (_gpgme_get_w32spawn_path (),
525
&sec_attr, /* process security attributes */
526
&sec_attr, /* thread security attributes */
527
FALSE, /* inherit handles */
528
cr_flags, /* creation flags */
529
NULL, /* environment */
530
NULL, /* use current drive/directory */
531
&si, /* startup information */
532
&pi)) /* returns process information */
534
TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
537
DeleteFile (tmp_name);
539
/* FIXME: Should translate the error code. */
541
return TRACE_SYSRES (-1);
546
/* Insert the inherited handles. */
547
for (i = 0; fd_list[i].fd != -1; i++)
551
/* Make it inheritable for the wrapper process. */
552
if (!DuplicateHandle (GetCurrentProcess(), _get_osfhandle (fd_list[i].fd),
553
pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
555
TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
556
TerminateProcess (pi.hProcess, 0);
557
/* Just in case TerminateProcess didn't work, let the
558
process fail on its own. */
559
ResumeThread (pi.hThread);
560
CloseHandle (pi.hThread);
561
CloseHandle (pi.hProcess);
564
DeleteFile (tmp_name);
566
/* FIXME: Should translate the error code. */
568
return TRACE_SYSRES (-1);
570
/* Return the child name of this handle. */
571
fd_list[i].peer_name = (int) hd;
574
/* Write the handle translation information to the temporary
577
/* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
578
notation: "0xFEDCBA9876543210" with an extra white space after
579
every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
580
for a time when a HANDLE is 64 bit. */
581
#define BUFFER_MAX 800
582
char line[BUFFER_MAX + 1];
589
for (i = 0; fd_list[i].fd != -1; i++)
591
/* Strip the newline. */
592
len = strlen (line) - 1;
594
/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
595
snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
596
fd_list[i].fd, fd_list[i].dup_to,
597
fd_list[i].peer_name, fd_list[i].arg_loc);
598
/* Rather safe than sorry. */
599
line[BUFFER_MAX - 1] = '\n';
600
line[BUFFER_MAX] = '\0';
606
res = write (tmp_fd, &line[written], len - written);
610
while (res > 0 || (res < 0 && errno == EAGAIN));
613
/* The temporary file is deleted by the gpgme-w32spawn process
616
TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
617
"dwProcessID=%d, dwThreadId=%d",
618
pi.hProcess, pi.hThread,
619
(int) pi.dwProcessId, (int) pi.dwThreadId);
622
*r_pid = (pid_t)pi.dwProcessId;
624
if (ResumeThread (pi.hThread) < 0)
625
TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
627
if (!CloseHandle (pi.hThread))
628
TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
629
(int) GetLastError ());
631
TRACE_LOG1 ("process=%p", pi.hProcess);
633
/* We don't need to wait for the process. */
634
if (!CloseHandle (pi.hProcess))
635
TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
636
(int) GetLastError ());
638
for (i = 0; fd_list[i].fd != -1; i++)
639
_gpgme_io_close (fd_list[i].fd);
641
for (i = 0; fd_list[i].fd != -1; i++)
642
if (fd_list[i].dup_to == -1)
643
TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
644
fd_list[i].peer_name);
646
TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
647
fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
648
((fd_list[i].dup_to == 1) ? "out" : "err"));
650
return TRACE_SYSRES (0);
654
/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
655
nothing to select, > 0 = number of signaled fds. */
657
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
667
/* Use a 1s timeout. */
669
void *dbg_help = NULL;
670
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
671
"nfds=%u, nonblock=%u", nfds, nonblock);
676
pollfds = calloc (nfds, sizeof *pollfds);
679
pollfds_map = calloc (nfds, sizeof *pollfds_map);
687
TRACE_SEQ (dbg_help, "select on [ ");
689
for (i = 0; i < nfds; i++)
691
GIOChannel *chan = NULL;
696
if ((fds[i].for_read || fds[i].for_write)
697
&& !(chan = find_channel (fds[i].fd, 0)))
699
TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
700
TRACE_END (dbg_help, "]");
701
assert (!"see log file");
703
else if (fds[i].for_read )
706
g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
707
pollfds_map[npollfds] = i;
708
TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
712
else if (fds[i].for_write)
715
g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
716
pollfds_map[npollfds] = i;
717
TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
723
TRACE_END (dbg_help, "]");
731
count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
734
int saved_errno = errno;
739
TRACE_SEQ (dbg_help, "select OK [ ");
740
if (TRACE_ENABLED (dbg_help))
742
for (i = 0; i < npollfds; i++)
744
if ((pollfds[i].revents & G_IO_IN))
745
TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
746
if ((pollfds[i].revents & G_IO_OUT))
747
TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
749
TRACE_END (dbg_help, "]");
752
/* COUNT is used to stop the lop as soon as possible. */
753
for (n = count, i = 0; i < npollfds && n; i++)
756
assert (j >= 0 && j < nfds);
759
else if (fds[j].for_read)
761
if ((pollfds[i].revents & G_IO_IN))
767
else if (fds[j].for_write)
769
if ((pollfds[i].revents & G_IO_OUT))
780
return TRACE_SYSRES (count);
785
_gpgme_io_dup (int fd)
790
TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "dup (%d)", fd);
794
return TRACE_SYSRES (-1);
795
if (newfd < 0 || newfd >= MAX_SLAFD)
797
/* New FD won't fit into our table. */
800
return TRACE_SYSRES (-1);
802
assert (giochannel_table[newfd].chan == NULL);
804
chan = find_channel (fd, 0);
807
g_io_channel_ref (chan);
808
giochannel_table[newfd].chan = chan;
809
giochannel_table[newfd].primary = 0;
811
return TRACE_SYSRES (newfd);