You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/libtdenetwork/libgpgme-copy/gpgme/w32-io.c

1153 lines
33 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/* w32-io.c - W32 API I/O functions.
Copyright (C) 2000 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
GPGME is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <windows.h>
#include <io.h>
#include "util.h"
#include "sema.h"
#include "priv-io.h"
#include "debug.h"
/* We assume that a HANDLE can be represented by an int which should
be true for all i386 systems (HANDLE is defined as void *) and
these are the only systems for which Windows is available. Further
we assume that -1 denotes an invalid handle. */
#define fd_to_handle(a) ((HANDLE)(a))
#define handle_to_fd(a) ((int)(a))
#define pid_to_handle(a) ((HANDLE)(a))
#define handle_to_pid(a) ((int)(a))
#define READBUF_SIZE 4096
#define WRITEBUF_SIZE 4096
#define PIPEBUF_SIZE 4096
#define MAX_READERS 20
#define MAX_WRITERS 20
static struct {
int inuse;
int fd;
void (*handler)(int,void*);
void *value;
} notify_table[256];
DEFINE_STATIC_LOCK (notify_table_lock);
struct reader_context_s {
HANDLE file_hd;
HANDLE thread_hd;
DECLARE_LOCK (mutex);
int stop_me;
int eof;
int eof_shortcut;
int error;
int error_code;
HANDLE have_data_ev; /* manually reset */
HANDLE have_space_ev; /* auto reset */
HANDLE stopped;
size_t readpos, writepos;
char buffer[READBUF_SIZE];
};
static struct {
volatile int used;
int fd;
struct reader_context_s *context;
} reader_table[MAX_READERS];
static int reader_table_size= MAX_READERS;
DEFINE_STATIC_LOCK (reader_table_lock);
struct writer_context_s {
HANDLE file_hd;
HANDLE thread_hd;
DECLARE_LOCK (mutex);
int stop_me;
int error;
int error_code;
HANDLE have_data; /* manually reset */
HANDLE is_empty;
HANDLE stopped;
size_t nbytes;
char buffer[WRITEBUF_SIZE];
};
static struct {
volatile int used;
int fd;
struct writer_context_s *context;
} writer_table[MAX_WRITERS];
static int writer_table_size= MAX_WRITERS;
DEFINE_STATIC_LOCK (writer_table_lock);
static int
get_desired_thread_priority (void)
{
int value;
if (!_gpgme_get_conf_int ("IOThreadPriority", &value))
{
value = THREAD_PRIORITY_HIGHEST;
DEBUG1 ("** Using standard IOThreadPriority of %d\n", value);
}
else
DEBUG1 ("** Configured IOThreadPriority is %d\n", value);
return value;
}
static HANDLE
set_synchronize (HANDLE h)
{
HANDLE tmp;
/* For NT we have to set the sync flag. It seems that the only
* way to do it is by duplicating the handle. Tsss.. */
if (!DuplicateHandle( GetCurrentProcess(), h,
GetCurrentProcess(), &tmp,
EVENT_MODIFY_STATE|SYNCHRONIZE, FALSE, 0 ) ) {
DEBUG1 ("** Set SYNCRONIZE failed: ec=%d\n", (int)GetLastError());
}
else {
CloseHandle (h);
h = tmp;
}
return h;
}
static DWORD CALLBACK
reader (void *arg)
{
struct reader_context_s *c = arg;
int nbytes;
DWORD nread;
DEBUG2 ("reader thread %p for file %p started", c->thread_hd, c->file_hd );
for (;;) {
LOCK (c->mutex);
/* leave a 1 byte gap so that we can see whether it is empty or full*/
if ((c->writepos + 1) % READBUF_SIZE == c->readpos) {
/* wait for space */
if (!ResetEvent (c->have_space_ev) )
DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
UNLOCK (c->mutex);
DEBUG1 ("reader thread %p: waiting for space ...", c->thread_hd );
WaitForSingleObject (c->have_space_ev, INFINITE);
DEBUG1 ("reader thread %p: got space", c->thread_hd );
LOCK (c->mutex);
}
if ( c->stop_me ) {
UNLOCK (c->mutex);
break;
}
nbytes = (c->readpos + READBUF_SIZE - c->writepos-1) % READBUF_SIZE;
if ( nbytes > READBUF_SIZE - c->writepos )
nbytes = READBUF_SIZE - c->writepos;
UNLOCK (c->mutex);
DEBUG2 ("reader thread %p: reading %d bytes", c->thread_hd, nbytes );
if ( !ReadFile ( c->file_hd,
c->buffer+c->writepos, nbytes, &nread, NULL) ) {
c->error_code = (int)GetLastError ();
if (c->error_code == ERROR_BROKEN_PIPE ) {
c->eof=1;
DEBUG1 ("reader thread %p: got eof (broken pipe)",
c->thread_hd );
}
else {
c->error = 1;
DEBUG2 ("reader thread %p: read error: ec=%d",
c->thread_hd, c->error_code );
}
break;
}
if ( !nread ) {
c->eof = 1;
DEBUG1 ("reader thread %p: got eof", c->thread_hd );
break;
}
DEBUG2 ("reader thread %p: got %d bytes", c->thread_hd, (int)nread );
LOCK (c->mutex);
if (c->stop_me) {
UNLOCK (c->mutex);
break;
}
c->writepos = (c->writepos + nread) % READBUF_SIZE;
if ( !SetEvent (c->have_data_ev) )
DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
UNLOCK (c->mutex);
}
/* indicate that we have an error or eof */
if ( !SetEvent (c->have_data_ev) )
DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
DEBUG1 ("reader thread %p ended", c->thread_hd );
SetEvent (c->stopped);
return 0;
}
static struct reader_context_s *
create_reader (HANDLE fd)
{
struct reader_context_s *c;
SECURITY_ATTRIBUTES sec_attr;
DWORD tid;
DEBUG1 ("creating new read thread for file handle %p", fd );
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
c = calloc (1, sizeof *c );
if (!c)
return NULL;
c->file_hd = fd;
c->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
c->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
c->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (!c->have_data_ev || !c->have_space_ev || !c->stopped ) {
DEBUG1 ("** CreateEvent failed: ec=%d\n", (int)GetLastError ());
if (c->have_data_ev)
CloseHandle (c->have_data_ev);
if (c->have_space_ev)
CloseHandle (c->have_space_ev);
if (c->stopped)
CloseHandle (c->stopped);
free (c);
return NULL;
}
c->have_data_ev = set_synchronize (c->have_data_ev);
INIT_LOCK (c->mutex);
c->thread_hd = CreateThread (&sec_attr, 0, reader, c, 0, &tid );
if (!c->thread_hd) {
DEBUG1 ("** failed to create reader thread: ec=%d\n",
(int)GetLastError ());
DESTROY_LOCK (c->mutex);
if (c->have_data_ev)
CloseHandle (c->have_data_ev);
if (c->have_space_ev)
CloseHandle (c->have_space_ev);
if (c->stopped)
CloseHandle (c->stopped);
free (c);
return NULL;
}
else {
/* We set the priority of the thread higher because we know that
it only runs for a short time. This greatly helps to increase
the performance of the I/O. */
SetThreadPriority (c->thread_hd, get_desired_thread_priority ());
}
return c;
}
static void
destroy_reader (struct reader_context_s *c)
{
LOCK (c->mutex);
c->stop_me = 1;
if (c->have_space_ev)
SetEvent (c->have_space_ev);
UNLOCK (c->mutex);
DEBUG1 ("waiting for thread %p termination ...", c->thread_hd );
WaitForSingleObject (c->stopped, INFINITE);
DEBUG1 ("thread %p has terminated", c->thread_hd );
if (c->stopped)
CloseHandle (c->stopped);
if (c->have_data_ev)
CloseHandle (c->have_data_ev);
if (c->have_space_ev)
CloseHandle (c->have_space_ev);
CloseHandle (c->thread_hd);
DESTROY_LOCK (c->mutex);
free (c);
}
/*
* Find a reader context or create a new one
* Note that the reader context will last until a io_close.
*/
static struct reader_context_s *
find_reader (int fd, int start_it)
{
int i;
for (i=0; i < reader_table_size ; i++ ) {
if ( reader_table[i].used && reader_table[i].fd == fd )
return reader_table[i].context;
}
if (!start_it)
return NULL;
LOCK (reader_table_lock);
for (i=0; i < reader_table_size; i++ ) {
if (!reader_table[i].used) {
reader_table[i].fd = fd;
reader_table[i].context = create_reader (fd_to_handle (fd));
reader_table[i].used = 1;
UNLOCK (reader_table_lock);
return reader_table[i].context;
}
}
UNLOCK (reader_table_lock);
return NULL;
}
static void
kill_reader (int fd)
{
int i;
LOCK (reader_table_lock);
for (i=0; i < reader_table_size; i++ ) {
if (reader_table[i].used && reader_table[i].fd == fd ) {
destroy_reader (reader_table[i].context);
reader_table[i].context = NULL;
reader_table[i].used = 0;
break;
}
}
UNLOCK (reader_table_lock);
}
int
_gpgme_io_read ( int fd, void *buffer, size_t count )
{
int nread;
struct reader_context_s *c = find_reader (fd,1);
DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int)count );
if ( !c ) {
DEBUG0 ( "no reader thread\n");
return -1;
}
if (c->eof_shortcut) {
DEBUG1 ("fd %d: EOF (again)", fd );
return 0;
}
LOCK (c->mutex);
if (c->readpos == c->writepos && !c->error) { /*no data avail*/
UNLOCK (c->mutex);
DEBUG2 ("fd %d: waiting for data from thread %p", fd, c->thread_hd);
WaitForSingleObject (c->have_data_ev, INFINITE);
DEBUG2 ("fd %d: data from thread %p available", fd, c->thread_hd);
LOCK (c->mutex);
}
if (c->readpos == c->writepos || c->error) {
UNLOCK (c->mutex);
c->eof_shortcut = 1;
if (c->eof) {
DEBUG1 ("fd %d: EOF", fd );
return 0;
}
if (!c->error) {
DEBUG1 ("fd %d: EOF but eof flag not set", fd );
return 0;
}
DEBUG1 ("fd %d: read error", fd );
return -1;
}
nread = c->readpos < c->writepos? c->writepos - c->readpos
: READBUF_SIZE - c->readpos;
if (nread > count)
nread = count;
memcpy (buffer, c->buffer+c->readpos, nread);
c->readpos = (c->readpos + nread) % READBUF_SIZE;
if (c->readpos == c->writepos && !c->eof) {
if ( !ResetEvent (c->have_data_ev) )
DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
}
if (!SetEvent (c->have_space_ev))
DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
UNLOCK (c->mutex);
DEBUG2 ("fd %d: got %d bytes\n", fd, nread );
if (nread > 0)
_gpgme_debug (2, "fd %d: got `%.*s'\n", fd, nread, buffer);
return nread;
}
/*
* The writer does use a simple buffering strategy so that we are
* informed about write errors as soon as possible (i.e. with the the
* next call to the write function
*/
static DWORD CALLBACK
writer (void *arg)
{
struct writer_context_s *c = arg;
DWORD nwritten;
DEBUG2 ("writer thread %p for file %p started", c->thread_hd, c->file_hd );
for (;;) {
LOCK (c->mutex);
if ( c->stop_me ) {
UNLOCK (c->mutex);
break;
}
if ( !c->nbytes ) {
if (!SetEvent (c->is_empty))
DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
if (!ResetEvent (c->have_data) )
DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
UNLOCK (c->mutex);
DEBUG1 ("writer thread %p: idle ...", c->thread_hd );
WaitForSingleObject (c->have_data, INFINITE);
DEBUG1 ("writer thread %p: got data to send", c->thread_hd );
LOCK (c->mutex);
}
if ( c->stop_me ) {
UNLOCK (c->mutex);
break;
}
UNLOCK (c->mutex);
DEBUG2 ("writer thread %p: writing %d bytes",
c->thread_hd, c->nbytes );
if ( c->nbytes && !WriteFile ( c->file_hd, c->buffer, c->nbytes,
&nwritten, NULL)) {
c->error_code = (int)GetLastError ();
c->error = 1;
DEBUG2 ("writer thread %p: write error: ec=%d",
c->thread_hd, c->error_code );
break;
}
DEBUG2 ("writer thread %p: wrote %d bytes",
c->thread_hd, (int)nwritten );
LOCK (c->mutex);
c->nbytes -= nwritten;
UNLOCK (c->mutex);
}
/* indicate that we have an error */
if ( !SetEvent (c->is_empty) )
DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
DEBUG1 ("writer thread %p ended", c->thread_hd );
SetEvent (c->stopped);
return 0;
}
static struct writer_context_s *
create_writer (HANDLE fd)
{
struct writer_context_s *c;
SECURITY_ATTRIBUTES sec_attr;
DWORD tid;
DEBUG1 ("creating new write thread for file handle %p", fd );
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
c = calloc (1, sizeof *c );
if (!c)
return NULL;
c->file_hd = fd;
c->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
c->is_empty = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
c->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (!c->have_data || !c->is_empty || !c->stopped ) {
DEBUG1 ("** CreateEvent failed: ec=%d\n", (int)GetLastError ());
if (c->have_data)
CloseHandle (c->have_data);
if (c->is_empty)
CloseHandle (c->is_empty);
if (c->stopped)
CloseHandle (c->stopped);
free (c);
return NULL;
}
c->is_empty = set_synchronize (c->is_empty);
INIT_LOCK (c->mutex);
c->thread_hd = CreateThread (&sec_attr, 0, writer, c, 0, &tid );
if (!c->thread_hd) {
DEBUG1 ("** failed to create writer thread: ec=%d\n",
(int)GetLastError ());
DESTROY_LOCK (c->mutex);
if (c->have_data)
CloseHandle (c->have_data);
if (c->is_empty)
CloseHandle (c->is_empty);
if (c->stopped)
CloseHandle (c->stopped);
free (c);
return NULL;
}
else {
/* We set the priority of the thread higher because we know that
it only runs for a short time. This greatly helps to increase
the performance of the I/O. */
SetThreadPriority (c->thread_hd, get_desired_thread_priority ());
}
return c;
}
static void
destroy_writer (struct writer_context_s *c)
{
LOCK (c->mutex);
c->stop_me = 1;
if (c->have_data)
SetEvent (c->have_data);
UNLOCK (c->mutex);
DEBUG1 ("waiting for thread %p termination ...", c->thread_hd );
WaitForSingleObject (c->stopped, INFINITE);
DEBUG1 ("thread %p has terminated", c->thread_hd );
if (c->stopped)
CloseHandle (c->stopped);
if (c->have_data)
CloseHandle (c->have_data);
if (c->is_empty)
CloseHandle (c->is_empty);
CloseHandle (c->thread_hd);
DESTROY_LOCK (c->mutex);
free (c);
}
/*
* Find a writer context or create a new one
* Note that the writer context will last until a io_close.
*/
static struct writer_context_s *
find_writer (int fd, int start_it)
{
int i;
for (i=0; i < writer_table_size ; i++ ) {
if ( writer_table[i].used && writer_table[i].fd == fd )
return writer_table[i].context;
}
if (!start_it)
return NULL;
LOCK (writer_table_lock);
for (i=0; i < writer_table_size; i++ ) {
if (!writer_table[i].used) {
writer_table[i].fd = fd;
writer_table[i].context = create_writer (fd_to_handle (fd));
writer_table[i].used = 1;
UNLOCK (writer_table_lock);
return writer_table[i].context;
}
}
UNLOCK (writer_table_lock);
return NULL;
}
static void
kill_writer (int fd)
{
int i;
LOCK (writer_table_lock);
for (i=0; i < writer_table_size; i++ ) {
if (writer_table[i].used && writer_table[i].fd == fd ) {
destroy_writer (writer_table[i].context);
writer_table[i].context = NULL;
writer_table[i].used = 0;
break;
}
}
UNLOCK (writer_table_lock);
}
int
_gpgme_io_write ( int fd, const void *buffer, size_t count )
{
struct writer_context_s *c = find_writer (fd,1);
DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int)count );
_gpgme_debug (2, "fd %d: write `%.*s'\n", fd, (int) count, buffer);
if ( !c ) {
DEBUG0 ( "no writer thread\n");
return -1;
}
LOCK (c->mutex);
if ( c->nbytes ) { /* bytes are pending for send */
/* Reset the is_empty event. Better safe than sorry. */
if (!ResetEvent (c->is_empty))
DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
UNLOCK (c->mutex);
DEBUG2 ("fd %d: waiting for empty buffer in thread %p",
fd, c->thread_hd);
WaitForSingleObject (c->is_empty, INFINITE);
DEBUG2 ("fd %d: thread %p buffer is empty", fd, c->thread_hd);
LOCK (c->mutex);
}
if ( c->error) {
UNLOCK (c->mutex);
DEBUG1 ("fd %d: write error", fd );
return -1;
}
/* If no error occured, the number of bytes in the buffer must be
zero. */
assert (!c->nbytes);
if (count > WRITEBUF_SIZE)
count = WRITEBUF_SIZE;
memcpy (c->buffer, buffer, count);
c->nbytes = count;
/* We have to reset the is_empty event early, because it is also
used by the select() implementation to probe the channel. */
if (!ResetEvent (c->is_empty))
DEBUG1 ("ResetEvent failed: ec=%d", (int)GetLastError ());
if (!SetEvent (c->have_data))
DEBUG1 ("SetEvent failed: ec=%d", (int)GetLastError ());
UNLOCK (c->mutex);
DEBUG2 ("fd %d: copied %d bytes\n",
fd, (int)count );
return (int)count;
}
int
_gpgme_io_pipe ( int filedes[2], int inherit_idx )
{
HANDLE r, w;
SECURITY_ATTRIBUTES sec_attr;
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
if (!CreatePipe ( &r, &w, &sec_attr, PIPEBUF_SIZE))
return -1;
/* Make one end inheritable. */
if ( inherit_idx == 0 ) {
HANDLE h;
if (!DuplicateHandle( GetCurrentProcess(), r,
GetCurrentProcess(), &h, 0,
TRUE, DUPLICATE_SAME_ACCESS ) ) {
DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
CloseHandle (r);
CloseHandle (w);
return -1;
}
CloseHandle (r);
r = h;
}
else if ( inherit_idx == 1 ) {
HANDLE h;
if (!DuplicateHandle( GetCurrentProcess(), w,
GetCurrentProcess(), &h, 0,
TRUE, DUPLICATE_SAME_ACCESS ) ) {
DEBUG1 ("DuplicateHandle failed: ec=%d\n", (int)GetLastError());
CloseHandle (r);
CloseHandle (w);
return -1;
}
CloseHandle (w);
w = h;
}
filedes[0] = handle_to_fd (r);
filedes[1] = handle_to_fd (w);
DEBUG5 ("CreatePipe %p %p %d %d inherit=%d\n", r, w,
filedes[0], filedes[1], inherit_idx );
return 0;
}
int
_gpgme_io_close ( int fd )
{
int i;
void (*handler)(int, void*) = NULL;
void *value = NULL;
if ( fd == -1 )
return -1;
DEBUG1 ("** closing handle for fd %d\n", fd);
kill_reader (fd);
kill_writer (fd);
LOCK (notify_table_lock);
for ( i=0; i < DIM (notify_table); i++ ) {
if (notify_table[i].inuse && notify_table[i].fd == fd) {
handler = notify_table[i].handler;
value = notify_table[i].value;
notify_table[i].handler = NULL;
notify_table[i].value = NULL;
notify_table[i].inuse = 0;
break;
}
}
UNLOCK (notify_table_lock);
if (handler)
handler (fd, value);
if ( !CloseHandle (fd_to_handle (fd)) ) {
DEBUG2 ("CloseHandle for fd %d failed: ec=%d\n",
fd, (int)GetLastError ());
return -1;
}
return 0;
}
int
_gpgme_io_set_close_notify (int fd, void (*handler)(int, void*), void *value)
{
int i;
assert (fd != -1);
LOCK (notify_table_lock);
for (i=0; i < DIM (notify_table); i++ ) {
if ( notify_table[i].inuse && notify_table[i].fd == fd )
break;
}
if ( i == DIM (notify_table) ) {
for (i=0; i < DIM (notify_table); i++ ) {
if ( !notify_table[i].inuse )
break;
}
}
if ( i == DIM (notify_table) ) {
UNLOCK (notify_table_lock);
return -1;
}
notify_table[i].fd = fd;
notify_table[i].handler = handler;
notify_table[i].value = value;
notify_table[i].inuse = 1;
UNLOCK (notify_table_lock);
DEBUG2 ("set notification for fd %d (idx=%d)", fd, i );
return 0;
}
int
_gpgme_io_set_nonblocking ( int fd )
{
return 0;
}
static char *
build_commandline (char **argv)
{
int i;
int j;
int n = 0;
char *buf;
char *p;
/* We have to quote some things because under Windows the program
parses the commandline and does some unquoting. We enclose the
whole argument in double-quotes, and escape literal double-quotes
as well as backslashes with a backslash. We end up with a
trailing space at the end of the line, but that is harmless. */
for (i = 0; argv[i]; i++)
{
p = argv[i];
/* The leading double-quote. */
n++;
while (*p)
{
/* An extra one for each literal that must be escaped. */
if (*p == '\\' || *p == '"')
n++;
n++;
p++;
}
/* The trailing double-quote and the delimiter. */
n += 2;
}
/* And a trailing zero. */
n++;
buf = p = malloc (n);
if (!buf)
return NULL;
for (i = 0; argv[i]; i++)
{
char *argvp = argv[i];
*(p++) = '"';
while (*argvp)
{
if (*argvp == '\\' || *argvp == '"')
*(p++) = '\\';
*(p++) = *(argvp++);
}
*(p++) = '"';
*(p++) = ' ';
}
*(p++) = 0;
return buf;
}
int
_gpgme_io_spawn ( const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list )
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi = {
NULL, /* returns process handle */
0, /* returns primary thread handle */
0, /* returns pid */
0 /* returns tid */
};
STARTUPINFO si;
char *envblock = NULL;
int cr_flags = CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ());
int i;
char *arg_string;
int duped_stdin = 0;
int duped_stderr = 0;
HANDLE hnul = INVALID_HANDLE_VALUE;
/* FIXME. */
int debug_me = 0;
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
arg_string = build_commandline ( argv );
if (!arg_string )
return -1;
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
for (i=0; fd_child_list[i].fd != -1; i++ ) {
if (fd_child_list[i].dup_to == 0 ) {
si.hStdInput = fd_to_handle (fd_child_list[i].fd);
DEBUG1 ("using %d for stdin", fd_child_list[i].fd );
duped_stdin=1;
}
else if (fd_child_list[i].dup_to == 1 ) {
si.hStdOutput = fd_to_handle (fd_child_list[i].fd);
DEBUG1 ("using %d for stdout", fd_child_list[i].fd );
}
else if (fd_child_list[i].dup_to == 2 ) {
si.hStdError = fd_to_handle (fd_child_list[i].fd);
DEBUG1 ("using %d for stderr", fd_child_list[i].fd );
duped_stderr = 1;
}
}
if( !duped_stdin || !duped_stderr ) {
SECURITY_ATTRIBUTES sa;
memset (&sa, 0, sizeof sa );
sa.nLength = sizeof sa;
sa.bInheritHandle = TRUE;
hnul = CreateFile ( "nul",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if ( hnul == INVALID_HANDLE_VALUE ) {
DEBUG1 ("can't open `nul': ec=%d\n", (int)GetLastError ());
free (arg_string);
return -1;
}
/* Make sure that the process has a connected stdin */
if ( !duped_stdin ) {
si.hStdInput = hnul;
DEBUG1 ("using %d for dummy stdin", (int)hnul );
}
/* We normally don't want all the normal output */
if ( !duped_stderr ) {
si.hStdError = hnul;
DEBUG1 ("using %d for dummy stderr", (int)hnul );
}
}
DEBUG2 ("CreateProcess, path=`%s' args=`%s'", path, arg_string);
cr_flags |= CREATE_SUSPENDED;
if ( !CreateProcessA (path,
arg_string,
&sec_attr, /* process security attributes */
&sec_attr, /* thread security attributes */
TRUE, /* inherit handles */
cr_flags, /* creation flags */
envblock, /* environment */
NULL, /* use current drive/directory */
&si, /* startup information */
&pi /* returns process information */
) ) {
DEBUG1 ("CreateProcess failed: ec=%d\n", (int) GetLastError ());
free (arg_string);
return -1;
}
/* Close the /dev/nul handle if used. */
if (hnul != INVALID_HANDLE_VALUE ) {
if ( !CloseHandle ( hnul ) )
DEBUG1 ("CloseHandle(hnul) failed: ec=%d\n", (int)GetLastError());
}
/* Close the other ends of the pipes. */
for (i = 0; fd_parent_list[i].fd != -1; i++)
_gpgme_io_close (fd_parent_list[i].fd);
DEBUG4 ("CreateProcess ready\n"
"- hProcess=%p hThread=%p\n"
"- dwProcessID=%d dwThreadId=%d\n",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
if ( ResumeThread ( pi.hThread ) < 0 ) {
DEBUG1 ("ResumeThread failed: ec=%d\n", (int)GetLastError ());
}
if ( !CloseHandle (pi.hThread) ) {
DEBUG1 ("CloseHandle of thread failed: ec=%d\n",
(int)GetLastError ());
}
return handle_to_pid (pi.hProcess);
}
/*
* Select on the list of fds.
* Returns: -1 = error
* 0 = timeout or nothing to select
* >0 = number of signaled fds
*/
int
_gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds, int nonblock )
{
HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
int waitidx[MAXIMUM_WAIT_OBJECTS];
int code, nwait;
int i, any;
int count;
void *dbg_help;
restart:
DEBUG_BEGIN (dbg_help, 3, "select on [ ");
any = 0;
nwait = 0;
count = 0;
for ( i=0; i < nfds; i++ ) {
if ( fds[i].fd == -1 )
continue;
fds[i].signaled = 0;
if ( fds[i].for_read || fds[i].for_write ) {
if ( fds[i].frozen ) {
DEBUG_ADD1 (dbg_help, "f%d ", fds[i].fd );
}
else if ( fds[i].for_read ) {
struct reader_context_s *c = find_reader (fds[i].fd,1);
if (!c) {
DEBUG1 ("oops: no reader thread for fd %d", fds[i].fd);
}
else {
if ( nwait >= DIM (waitbuf) ) {
DEBUG_END (dbg_help, "oops ]");
DEBUG0 ("Too many objects for WFMO!" );
return -1;
}
waitidx[nwait] = i;
waitbuf[nwait++] = c->have_data_ev;
}
DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd );
any = 1;
}
else if ( fds[i].for_write ) {
struct writer_context_s *c = find_writer (fds[i].fd,1);
if (!c) {
DEBUG1 ("oops: no writer thread for fd %d", fds[i].fd);
}
else {
if ( nwait >= DIM (waitbuf) ) {
DEBUG_END (dbg_help, "oops ]");
DEBUG0 ("Too many objects for WFMO!" );
return -1;
}
waitidx[nwait] = i;
waitbuf[nwait++] = c->is_empty;
}
DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd );
any = 1;
}
}
}
DEBUG_END (dbg_help, "]");
if (!any)
return 0;
code = WaitForMultipleObjects ( nwait, waitbuf, 0, nonblock ? 0 : 1000);
if ( code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait ) {
/* This WFMO is a really silly function: It does return either
* the index of the signaled object or if 2 objects have been
* signalled at the same time, the index of the object with the
* lowest object is returned - so and how do we find out
* how many objects have been signaled???.
* The only solution I can imagine is to test each object starting
* with the returned index individually - how dull.
*/
any = 0;
for (i=code - WAIT_OBJECT_0; i < nwait; i++ ) {
if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0) {
assert (waitidx[i] >=0 && waitidx[i] < nfds);
fds[waitidx[i]].signaled = 1;
any = 1;
count++;
}
}
if (!any) {
DEBUG0 ("Oops: No signaled objects found after WFMO");
count = -1;
}
}
else if ( code == WAIT_TIMEOUT ) {
DEBUG0 ("WFMO timed out\n" );
}
else if (code == WAIT_FAILED ) {
int le = (int)GetLastError ();
if ( le == ERROR_INVALID_HANDLE ) {
int k, j = handle_to_fd (waitbuf[i]);
DEBUG1 ("WFMO invalid handle %d removed\n", j);
for (k=0 ; k < nfds; k++ ) {
if ( fds[k].fd == j ) {
fds[k].for_read = fds[k].for_write = 0;
goto restart;
}
}
DEBUG0 (" oops, or not???\n");
}
DEBUG1 ("WFMO failed: %d\n", le );
count = -1;
}
else {
DEBUG1 ("WFMO returned %d\n", code );
count = -1;
}
if ( count ) {
DEBUG_BEGIN (dbg_help, 3, " signaled [ ");
for ( i=0; i < nfds; i++ ) {
if ( fds[i].fd == -1 )
continue;
if ( (fds[i].for_read || fds[i].for_write) && fds[i].signaled ) {
DEBUG_ADD2 (dbg_help, "%c%d ",
fds[i].for_read? 'r':'w',fds[i].fd );
}
}
DEBUG_END (dbg_help, "]");
}
return count;
}
void
_gpgme_io_subsystem_init (void)
{
}
/* Write the printable version of FD to the buffer BUF of length
BUFLEN. The printable version is the representation on the command
line that the child process expects. */
int
_gpgme_io_fd2str (char *buf, int buflen, int fd)
{
return snprintf (buf, buflen, "%d", fd);
}
/* The following interface is only useful for GPGME Glib. */
/* Look up the giochannel for file descriptor FD. */
void *
gpgme_get_giochannel (int fd)
{
return NULL;
}