/* w32-io.c - W32 API I/O functions.
Copyright (C) 2000 Werner Koch (dd9jn)
- Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
+ Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010 g10 Code GmbH
This file is part of GPGME.
#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>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
#include <io.h>
#include "util.h"
+
+#ifdef HAVE_W32CE_SYSTEM
+#include <assuan.h>
+#include <winioctl.h>
+#define GPGCEDEV_IOCTL_UNBLOCK \
+ CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define GPGCEDEV_IOCTL_ASSIGN_RVID \
+ CTL_CODE (FILE_DEVICE_STREAMS, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
#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. */
+/* FIXME: Optimize. */
+#define MAX_SLAFD 512
+
+static struct
+{
+ int used;
+
+ /* If this is not INVALID_HANDLE_VALUE, then it's a handle. */
+ HANDLE handle;
+
+ /* If this is not INVALID_SOCKET, then it's a Windows socket. */
+ int socket;
+
+ /* If this is not 0, then it's a rendezvous ID for the pipe server. */
+ int rvid;
+
+ /* DUP_FROM is -1 if this file descriptor was allocated by pipe or
+ socket functions. Only then should the handle or socket be
+ destroyed when this FD is closed. This, together with the fact
+ that dup'ed file descriptors are closed before the file
+ descriptors from which they are dup'ed are closed, ensures that
+ the handle or socket is always valid, and shared among all file
+ descriptors refering to the same underlying object.
+
+ The logic behind this is that there is only one reason for us to
+ dup file descriptors anyway: to allow simpler book-keeping of
+ file descriptors shared between GPGME and libassuan, which both
+ want to close something. Using the same handle for these
+ duplicates works just fine. */
+ int dup_from;
+} fd_table[MAX_SLAFD];
+
+
+/* Returns the FD or -1 on resource limit. */
+int
+new_fd (void)
+{
+ int idx;
+
+ for (idx = 0; idx < MAX_SLAFD; idx++)
+ if (! fd_table[idx].used)
+ break;
+
+ if (idx == MAX_SLAFD)
+ {
+ gpg_err_set_errno (EIO);
+ return -1;
+ }
+
+ fd_table[idx].used = 1;
+ fd_table[idx].handle = INVALID_HANDLE_VALUE;
+ fd_table[idx].socket = INVALID_SOCKET;
+ fd_table[idx].rvid = 0;
+ fd_table[idx].dup_from = -1;
+
+ return idx;
+}
+
+
+void
+release_fd (int fd)
+{
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ return;
+
+ fd_table[fd].used = 0;
+ fd_table[fd].handle = INVALID_HANDLE_VALUE;
+ fd_table[fd].socket = INVALID_SOCKET;
+ fd_table[fd].rvid = 0;
+ fd_table[fd].dup_from = -1;
+}
+
-#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 40
-#define MAX_WRITERS 40
+#define MAX_READERS 64
+#define MAX_WRITERS 64
static struct
{
int fd;
_gpgme_close_notify_handler_t handler;
void *value;
-} notify_table[256];
+} notify_table[MAX_SLAFD];
DEFINE_STATIC_LOCK (notify_table_lock);
struct reader_context_s
{
HANDLE file_hd;
+ int file_sock;
HANDLE thread_hd;
int refcount;
struct writer_context_s
{
HANDLE file_hd;
+ int file_sock;
HANDLE thread_hd;
int refcount;
static HANDLE
set_synchronize (HANDLE hd)
{
+#ifdef HAVE_W32CE_SYSTEM
+ return hd;
+#else
HANDLE new_hd;
/* For NT we have to set the sync flag. It seems that the only way
TRACE1 (DEBUG_SYSIO, "gpgme:set_synchronize", hd,
"DuplicateHandle failed: ec=%d", (int) GetLastError ());
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return INVALID_HANDLE_VALUE;
}
CloseHandle (hd);
return new_hd;
+#endif
}
struct reader_context_s *ctx = arg;
int nbytes;
DWORD nread;
+ int sock;
TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
"thread=%p", ctx->thread_hd);
+ if (ctx->file_hd != INVALID_HANDLE_VALUE)
+ sock = 0;
+ else
+ sock = 1;
+
for (;;)
{
LOCK (ctx->mutex);
nbytes = READBUF_SIZE - ctx->writepos;
UNLOCK (ctx->mutex);
- TRACE_LOG1 ("reading %d bytes", nbytes);
- if (!ReadFile (ctx->file_hd,
- ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
- {
- ctx->error_code = (int) GetLastError ();
- if (ctx->error_code == ERROR_BROKEN_PIPE)
- {
- ctx->eof = 1;
- TRACE_LOG ("got EOF (broken pipe)");
+ TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
+
+ if (sock)
+ {
+ int n;
+
+ n = recv (ctx->file_sock, ctx->buffer + ctx->writepos, nbytes, 0);
+ if (n < 0)
+ {
+ ctx->error_code = (int) WSAGetLastError ();
+ if (ctx->error_code == ERROR_BROKEN_PIPE)
+ {
+ ctx->eof = 1;
+ TRACE_LOG ("got EOF (broken connection)");
+ }
+ else
+ {
+ ctx->error = 1;
+ TRACE_LOG1 ("recv error: ec=%d", ctx->error_code);
+ }
+ break;
}
- else
- {
- ctx->error = 1;
- TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
+ nread = n;
+ }
+ else
+ {
+ if (!ReadFile (ctx->file_hd,
+ ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
+ {
+ ctx->error_code = (int) GetLastError ();
+ /* NOTE (W32CE): Do not ignore ERROR_BUSY! Check at
+ least stop_me if that happens. */
+ if (ctx->error_code == ERROR_BROKEN_PIPE)
+ {
+ ctx->eof = 1;
+ TRACE_LOG ("got EOF (broken pipe)");
+ }
+ else
+ {
+ ctx->error = 1;
+ TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
+ }
+ break;
}
+ }
+ LOCK (ctx->mutex);
+ if (ctx->stop_me)
+ {
+ UNLOCK (ctx->mutex);
break;
}
if (!nread)
{
ctx->eof = 1;
TRACE_LOG ("got eof");
+ UNLOCK (ctx->mutex);
break;
}
TRACE_LOG1 ("got %u bytes", nread);
- LOCK (ctx->mutex);
- if (ctx->stop_me)
- {
- UNLOCK (ctx->mutex);
- break;
- }
ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
if (!SetEvent (ctx->have_data_ev))
TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
static struct reader_context_s *
-create_reader (HANDLE fd)
+create_reader (int fd)
{
struct reader_context_s *ctx;
SECURITY_ATTRIBUTES sec_attr;
return NULL;
}
- ctx->file_hd = fd;
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+ ctx->file_hd = fd_table[fd].handle;
+ ctx->file_sock = fd_table[fd].socket;
+
ctx->refcount = 1;
ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (ctx->have_data_ev)
ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
INIT_LOCK (ctx->mutex);
+#ifdef HAVE_W32CE_SYSTEM
+ ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
+ STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
+#else
ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
+#endif
+
if (!ctx->thread_hd)
{
TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
SetEvent (ctx->have_space_ev);
UNLOCK (ctx->mutex);
+#ifdef HAVE_W32CE_SYSTEM
+ /* Scenario: We never create a full pipe, but already started
+ reading. Then we need to unblock the reader in the pipe driver
+ to make our reader thread notice that we want it to go away. */
+
+ if (ctx->file_hd != INVALID_HANDLE_VALUE)
+ {
+ if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
+ NULL, 0, NULL, 0, NULL, NULL))
+ {
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
+ "unblock control call failed for thread %p", ctx->thread_hd);
+ }
+ }
+#endif
+
TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
"waiting for termination of thread %p", ctx->thread_hd);
WaitForSingleObject (ctx->stopped, INFINITE);
if (i != reader_table_size)
{
- rd = create_reader (fd_to_handle (fd));
+ rd = create_reader (fd);
reader_table[i].fd = fd;
reader_table[i].context = rd;
reader_table[i].used = 1;
ctx = find_reader (fd, 1);
if (!ctx)
{
- errno = EBADF;
+ gpg_err_set_errno (EBADF);
return TRACE_SYSRES (-1);
}
if (ctx->eof_shortcut)
TRACE_LOG ("EOF but ctx->eof flag not set");
return 0;
}
- errno = ctx->error_code;
+ gpg_err_set_errno (ctx->error_code);
return TRACE_SYSRES (-1);
}
TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
UNLOCK (ctx->mutex);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
}
ctx->have_space_ev, (int) GetLastError ());
UNLOCK (ctx->mutex);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
UNLOCK (ctx->mutex);
{
struct writer_context_s *ctx = arg;
DWORD nwritten;
+ int sock;
TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
"thread=%p", ctx->thread_hd);
+ if (ctx->file_hd != INVALID_HANDLE_VALUE)
+ sock = 0;
+ else
+ sock = 1;
+
for (;;)
{
LOCK (ctx->mutex);
}
UNLOCK (ctx->mutex);
- TRACE_LOG1 ("writing %d bytes", ctx->nbytes);
+ TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
+
/* Note that CTX->nbytes is not zero at this point, because
_gpgme_io_write always writes at least 1 byte before waking
us up, unless CTX->stop_me is true, which we catch above. */
- if (!WriteFile (ctx->file_hd, ctx->buffer,
- ctx->nbytes, &nwritten, NULL))
- {
- ctx->error_code = (int) GetLastError ();
- ctx->error = 1;
- TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
- break;
- }
+ if (sock)
+ {
+ /* We need to try send first because a socket handle can't
+ be used with WriteFile. */
+ int n;
+
+ n = send (ctx->file_sock, ctx->buffer, ctx->nbytes, 0);
+ if (n < 0)
+ {
+ ctx->error_code = (int) WSAGetLastError ();
+ ctx->error = 1;
+ TRACE_LOG1 ("send error: ec=%d", ctx->error_code);
+ break;
+ }
+ nwritten = n;
+ }
+ else
+ {
+ if (!WriteFile (ctx->file_hd, ctx->buffer,
+ ctx->nbytes, &nwritten, NULL))
+ {
+ if (GetLastError () == ERROR_BUSY)
+ {
+ /* Probably stop_me is set now. */
+ TRACE_LOG ("pipe busy (unblocked?)");
+ continue;
+ }
+
+ ctx->error_code = (int) GetLastError ();
+ ctx->error = 1;
+ TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
+ break;
+ }
+ }
TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
LOCK (ctx->mutex);
static struct writer_context_s *
-create_writer (HANDLE fd)
+create_writer (int fd)
{
struct writer_context_s *ctx;
SECURITY_ATTRIBUTES sec_attr;
return NULL;
}
- ctx->file_hd = fd;
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+ ctx->file_hd = fd_table[fd].handle;
+ ctx->file_sock = fd_table[fd].socket;
+
ctx->refcount = 1;
ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
if (ctx->have_data)
ctx->is_empty = set_synchronize (ctx->is_empty);
INIT_LOCK (ctx->mutex);
+#ifdef HAVE_W32CE_SYSTEM
+ ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
+ STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
+#else
ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
+#endif
+
if (!ctx->thread_hd)
{
TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
if (ctx->have_data)
SetEvent (ctx->have_data);
UNLOCK (ctx->mutex);
+
+#ifdef HAVE_W32CE_SYSTEM
+ /* Scenario: We never create a full pipe, but already started
+ writing more than the pipe buffer. Then we need to unblock the
+ writer in the pipe driver to make our writer thread notice that
+ we want it to go away. */
+
+ if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
+ NULL, 0, NULL, 0, NULL, NULL))
+ {
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
+ "unblock control call failed for thread %p", ctx->thread_hd);
+ }
+#endif
TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
"waiting for termination of thread %p", ctx->thread_hd);
if (i != writer_table_size)
{
- wt = create_writer (fd_to_handle (fd));
+ wt = create_writer (fd);
writer_table[i].fd = fd;
writer_table[i].context = wt;
writer_table[i].used = 1;
TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
UNLOCK (ctx->mutex);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
UNLOCK (ctx->mutex);
{
UNLOCK (ctx->mutex);
if (ctx->error_code == ERROR_NO_DATA)
- errno = EPIPE;
+ gpg_err_set_errno (EPIPE);
else
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
UNLOCK (ctx->mutex);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
if (!SetEvent (ctx->have_data))
TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
UNLOCK (ctx->mutex);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
UNLOCK (ctx->mutex);
int
_gpgme_io_pipe (int filedes[2], int inherit_idx)
{
+ int rfd;
+ int wfd;
+#ifdef HAVE_W32CE_SYSTEM
+ HANDLE hd;
+ int rvid;
+#else
HANDLE rh;
HANDLE wh;
SECURITY_ATTRIBUTES sec_attr;
+#endif
+
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
"inherit_idx=%i (GPGME uses it for %s)",
inherit_idx, inherit_idx ? "reading" : "writing");
+ rfd = new_fd ();
+ if (rfd == -1)
+ return TRACE_SYSRES (-1);
+ wfd = new_fd ();
+ if (wfd == -1)
+ {
+ release_fd (rfd);
+ return TRACE_SYSRES (-1);
+ }
+
+#ifdef HAVE_W32CE_SYSTEM
+ hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
+ if (hd == INVALID_HANDLE_VALUE)
+ {
+ TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d",
+ (int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+
+ if (inherit_idx == 0)
+ {
+ fd_table[rfd].rvid = rvid;
+ fd_table[wfd].handle = hd;
+ }
+ else
+ {
+ fd_table[rfd].handle = hd;
+ fd_table[wfd].rvid = rvid;
+ }
+
+#else
+
memset (&sec_attr, 0, sizeof (sec_attr));
sec_attr.nLength = sizeof (sec_attr);
sec_attr.bInheritHandle = FALSE;
if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
{
TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
{
TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
(int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
CloseHandle (rh);
CloseHandle (wh);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
CloseHandle (rh);
{
TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
(int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
CloseHandle (rh);
CloseHandle (wh);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
CloseHandle (wh);
wh = hd;
}
-
- filedes[0] = handle_to_fd (rh);
- filedes[1] = handle_to_fd (wh);
- return TRACE_SUC2 ("read=%p, write=%p", rh, wh);
+ fd_table[rfd].handle = rh;
+ fd_table[wfd].handle = wh;
+#endif
+
+ filedes[0] = rfd;
+ filedes[1] = wfd;
+ return TRACE_SUC6 ("read=0x%x (%p/0x%x), write=0x%x (%p/0x%x)",
+ rfd, fd_table[rfd].handle, fd_table[rfd].rvid,
+ wfd, fd_table[wfd].handle, fd_table[wfd].rvid);
}
int i;
_gpgme_close_notify_handler_t handler = NULL;
void *value = NULL;
+
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
if (fd == -1)
{
- errno = EBADF;
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ gpg_err_set_errno (EBADF);
return TRACE_SYSRES (-1);
}
if (handler)
handler (fd, value);
- if (!CloseHandle (fd_to_handle (fd)))
- {
- TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
- /* FIXME: Should translate the error code. */
- errno = EIO;
- return TRACE_SYSRES (-1);
+ if (fd_table[fd].dup_from == -1)
+ {
+ if (fd_table[fd].handle != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle (fd_table[fd].handle))
+ {
+ TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ }
+ else if (fd_table[fd].socket != INVALID_SOCKET)
+ {
+ if (closesocket (fd_table[fd].socket))
+ {
+ TRACE_LOG1 ("closesocket failed: ec=%d", (int) WSAGetLastError ());
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ }
+ /* Nothing to do for RVIDs. */
}
+ release_fd (fd);
+
return TRACE_SYSRES (0);
}
if (i == DIM (notify_table))
{
UNLOCK (notify_table_lock);
- errno = EINVAL;
+ gpg_err_set_errno (EINVAL);
return TRACE_SYSRES (-1);
}
notify_table[i].fd = fd;
}
+#ifdef HAVE_W32CE_SYSTEM
+static char *
+build_commandline (char **argv, int fd0, int fd0_isnull,
+ int fd1, int fd1_isnull,
+ int fd2, int fd2_isnull)
+{
+ int i, n;
+ const char *s;
+ char *buf, *p;
+ char fdbuf[3*30];
+
+ p = fdbuf;
+ *p = 0;
+
+ if (fd0 != -1)
+ {
+ if (fd0_isnull)
+ strcpy (p, "-&S0=null ");
+ else
+ snprintf (p, 25, "-&S0=%d ", fd_table[fd0].rvid);
+ p += strlen (p);
+ }
+ if (fd1 != -1)
+ {
+ if (fd1_isnull)
+ strcpy (p, "-&S1=null ");
+ else
+ snprintf (p, 25, "-&S1=%d ", fd_table[fd1].rvid);
+ p += strlen (p);
+ }
+ if (fd2 != -1)
+ {
+ if (fd2_isnull)
+ strcpy (p, "-&S2=null ");
+ else
+ snprintf (p, 25, "-&S2=%d ", fd_table[fd2].rvid);
+ p += strlen (p);
+ }
+ strcpy (p, "-&S2=null ");
+ p += strlen (p);
+
+ n = strlen (fdbuf);
+ for (i=0; (s = argv[i]); i++)
+ {
+ if (!i)
+ continue; /* Ignore argv[0]. */
+ n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */
+ for (; *s; s++)
+ if (*s == '\"')
+ n++; /* Need to double inner quotes. */
+ }
+ n++;
+ buf = p = malloc (n);
+ if (! buf)
+ return NULL;
+
+ p = stpcpy (p, fdbuf);
+ for (i = 0; argv[i]; i++)
+ {
+ if (!i)
+ continue; /* Ignore argv[0]. */
+ if (i > 1)
+ p = stpcpy (p, " ");
+
+ if (! *argv[i]) /* Empty string. */
+ p = stpcpy (p, "\"\"");
+ else if (strpbrk (argv[i], " \t\n\v\f\""))
+ {
+ p = stpcpy (p, "\"");
+ for (s = argv[i]; *s; s++)
+ {
+ *p++ = *s;
+ if (*s == '\"')
+ *p++ = *s;
+ }
+ *p++ = '\"';
+ *p = 0;
+ }
+ else
+ p = stpcpy (p, argv[i]);
+ }
+
+ return buf;
+}
+#else
static char *
build_commandline (char **argv)
{
return buf;
}
+#endif
int
-_gpgme_io_spawn (const char *path, char *const argv[],
- struct spawn_fd_item_s *fd_list, pid_t *r_pid)
+_gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
+ struct spawn_fd_item_s *fd_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, pid_t *r_pid)
{
- SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* returns process handle */
0, /* returns pid */
0 /* returns tid */
};
- STARTUPINFO si;
- int cr_flags = CREATE_DEFAULT_ERROR_MODE
- | GetPriorityClass (GetCurrentProcess ());
int i;
+
+#ifdef HAVE_W32CE_SYSTEM
+ int fd_in = -1;
+ int fd_out = -1;
+ int fd_err = -1;
+ int fd_in_isnull = 1;
+ int fd_out_isnull = 1;
+ int fd_err_isnull = 1;
+ char *cmdline;
+ HANDLE hd = INVALID_HANDLE_VALUE;
+
+ TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+ "path=%s", path);
+ i = 0;
+ while (argv[i])
+ {
+ TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+ i++;
+ }
+
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ int fd = fd_list[i].fd;
+
+ TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd, fd_list[i].dup_to);
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ TRACE_LOG1 ("invalid fd 0x%x", fd);
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+ if (fd_table[fd].rvid == 0)
+ {
+ TRACE_LOG1 ("fd 0x%x not inheritable (not an RVID)", fd);
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+
+ if (fd_list[i].dup_to == 0)
+ {
+ fd_in = fd_list[i].fd;
+ fd_in_isnull = 0;
+ }
+ else if (fd_list[i].dup_to == 1)
+ {
+ fd_out = fd_list[i].fd;
+ fd_out_isnull = 0;
+ }
+ else if (fd_list[i].dup_to == 2)
+ {
+ fd_err = fd_list[i].fd;
+ fd_err_isnull = 0;
+ }
+ }
+
+ cmdline = build_commandline (argv, fd_in, fd_in_isnull,
+ fd_out, fd_out_isnull, fd_err, fd_err_isnull);
+ if (!cmdline)
+ {
+ TRACE_LOG1 ("build_commandline failed: %s", strerror (errno));
+ return TRACE_SYSRES (-1);
+ }
+
+ if (!CreateProcessA (path, /* Program to start. */
+ cmdline, /* Command line arguments. */
+ NULL, /* (not supported) */
+ NULL, /* (not supported) */
+ FALSE, /* (not supported) */
+ (CREATE_SUSPENDED), /* Creation flags. */
+ NULL, /* (not supported) */
+ NULL, /* (not supported) */
+ NULL, /* (not supported) */
+ &pi /* Returns process information.*/
+ ))
+ {
+ TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+ free (cmdline);
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+
+ /* Create arbitrary pipe descriptor to send in ASSIGN_RVID
+ commands. Errors are ignored. We don't need read or write access,
+ as ASSIGN_RVID works without any permissions, yay! */
+ hd = CreateFile (L"GPG1:", 0, 0,
+ NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hd == INVALID_HANDLE_VALUE)
+ {
+ TRACE_LOG1 ("CreateFile failed (ignored): ec=%d",
+ (int) GetLastError ());
+ }
+
+ /* Insert the inherited handles. */
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ /* Return the child name of this handle. */
+ fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
+
+ if (hd != INVALID_HANDLE_VALUE)
+ {
+ DWORD data[2];
+ data[0] = (DWORD) fd_table[fd_list[i].fd].rvid;
+ data[1] = pi.dwProcessId;
+ if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_ASSIGN_RVID,
+ data, sizeof (data), NULL, 0, NULL, NULL))
+ {
+ TRACE_LOG3 ("ASSIGN_RVID(%i, %i) failed (ignored): %i",
+ data[0], data[1], (int) GetLastError ());
+ }
+ }
+ }
+ if (hd != INVALID_HANDLE_VALUE)
+ CloseHandle (hd);
+
+#else
+ SECURITY_ATTRIBUTES sec_attr;
+ STARTUPINFOA si;
+ int cr_flags = CREATE_DEFAULT_ERROR_MODE;
char **args;
char *arg_string;
/* FIXME. */
if (!arg_string)
{
close (tmp_fd);
- DeleteFile (tmp_name);
+ DeleteFileA (tmp_name);
return TRACE_SYSRES (-1);
}
si.hStdOutput = INVALID_HANDLE_VALUE;
si.hStdError = INVALID_HANDLE_VALUE;
- cr_flags |= CREATE_SUSPENDED;
+ cr_flags |= CREATE_SUSPENDED;
cr_flags |= DETACHED_PROCESS;
+ cr_flags |= GetPriorityClass (GetCurrentProcess ());
if (!CreateProcessA (_gpgme_get_w32spawn_path (),
arg_string,
&sec_attr, /* process security attributes */
TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
free (arg_string);
close (tmp_fd);
- DeleteFile (tmp_name);
+ DeleteFileA (tmp_name);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
free (arg_string);
+ if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
+ _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
+
/* Insert the inherited handles. */
for (i = 0; fd_list[i].fd != -1; i++)
{
- HANDLE hd;
-
+ int fd = fd_list[i].fd;
+ HANDLE ohd = INVALID_HANDLE_VALUE;
+ HANDLE hd = INVALID_HANDLE_VALUE;
+
/* Make it inheritable for the wrapper process. */
- if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd),
+ if (fd >= 0 && fd < MAX_SLAFD && fd_table[fd].used)
+ ohd = fd_table[fd].handle;
+
+ if (!DuplicateHandle (GetCurrentProcess(), ohd,
pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
CloseHandle (pi.hProcess);
close (tmp_fd);
- DeleteFile (tmp_name);
+ DeleteFileA (tmp_name);
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
/* Return the child name of this handle. */
notation: "0xFEDCBA9876543210" with an extra white space after
every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
for a time when a HANDLE is 64 bit. */
-#define BUFFER_MAX 800
+#define BUFFER_MAX 810
char line[BUFFER_MAX + 1];
int res;
int written;
size_t len;
- line[0] = '\n';
- line[1] = '\0';
+ if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
+ strcpy (line, "~1 \n");
+ else
+ strcpy (line, "\n");
for (i = 0; fd_list[i].fd != -1; i++)
{
/* Strip the newline. */
close (tmp_fd);
/* The temporary file is deleted by the gpgme-w32spawn process
(hopefully). */
+#endif
+
TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
"dwProcessID=%d, dwThreadId=%d",
if (r_pid)
*r_pid = (pid_t)pi.dwProcessId;
+
if (ResumeThread (pi.hThread) < 0)
TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
(int) GetLastError ());
- for (i = 0; fd_list[i].fd != -1; i++)
- _gpgme_io_close (fd_list[i].fd);
+ if (! (flags & IOSPAWN_FLAG_NOCLOSE))
+ {
+ for (i = 0; fd_list[i].fd != -1; i++)
+ _gpgme_io_close (fd_list[i].fd);
+ }
for (i = 0; fd_list[i].fd != -1; i++)
if (fd_list[i].dup_to == -1)
TRACE_END (dbg_help, "oops ]");
TRACE_LOG ("Too many objects for WFMO!");
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
waitidx[nwait] = i;
TRACE_END (dbg_help, "oops ]");
TRACE_LOG ("Too many objects for WFMO!");
/* FIXME: Should translate the error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
return TRACE_SYSRES (-1);
}
waitidx[nwait] = i;
else if (code == WAIT_FAILED)
{
int le = (int) GetLastError ();
+#if 0
if (le == ERROR_INVALID_HANDLE)
{
int k;
}
TRACE_LOG (" oops, or not???");
}
+#endif
TRACE_LOG1 ("WFMO failed: %d", le);
count = -1;
}
if (count < 0)
{
/* FIXME: Should determine a proper error code. */
- errno = EIO;
+ gpg_err_set_errno (EIO);
}
return TRACE_SYSRES (count);
int
_gpgme_io_fd2str (char *buf, int buflen, int fd)
{
+#ifdef HAVE_W32CE_SYSTEM
+ /* FIXME: For now. See above. */
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
+ || fd_table[fd].rvid == 0)
+ fd = -1;
+ else
+ fd = fd_table[fd].rvid;
+#endif
+
return snprintf (buf, buflen, "%d", fd);
}
int
_gpgme_io_dup (int fd)
{
- HANDLE handle = fd_to_handle (fd);
- HANDLE new_handle = fd_to_handle (fd);
- int i;
+ int newfd;
struct reader_context_s *rd_ctx;
struct writer_context_s *wt_ctx;
+ int i;
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
- if (!DuplicateHandle (GetCurrentProcess(), handle,
- GetCurrentProcess(), &new_handle,
- 0, FALSE, DUPLICATE_SAME_ACCESS))
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
{
- TRACE_LOG1 ("DuplicateHandle failed: ec=%d\n", (int) GetLastError ());
- /* FIXME: Translate error code. */
- errno = EIO;
+ gpg_err_set_errno (EINVAL);
return TRACE_SYSRES (-1);
}
+ newfd = new_fd();
+ if (newfd == -1)
+ return TRACE_SYSRES (-1);
+
+ fd_table[newfd].handle = fd_table[fd].handle;
+ fd_table[newfd].socket = fd_table[fd].socket;
+ fd_table[newfd].rvid = fd_table[fd].rvid;
+ fd_table[newfd].dup_from = fd;
+
rd_ctx = find_reader (fd, 1);
if (rd_ctx)
{
break;
/* FIXME. */
assert (i != reader_table_size);
- reader_table[i].fd = handle_to_fd (new_handle);
+ reader_table[i].fd = newfd;
reader_table[i].context = rd_ctx;
reader_table[i].used = 1;
UNLOCK (reader_table_lock);
break;
/* FIXME. */
assert (i != writer_table_size);
- writer_table[i].fd = handle_to_fd (new_handle);
+ writer_table[i].fd = newfd;
writer_table[i].context = wt_ctx;
writer_table[i].used = 1;
UNLOCK (writer_table_lock);
}
- return TRACE_SYSRES (handle_to_fd (new_handle));
+ return TRACE_SYSRES (newfd);
}
\f
_gpgme_io_socket (int domain, int type, int proto)
{
int res;
+ int fd;
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
"type=%i, protp=%i", type, proto);
+ fd = new_fd();
+ if (fd == -1)
+ return TRACE_SYSRES (-1);
+
res = socket (domain, type, proto);
if (res == INVALID_SOCKET)
{
- errno = wsa2errno (WSAGetLastError ());
+ release_fd (fd);
+ gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
return TRACE_SYSRES (-1);
}
+ fd_table[fd].socket = res;
- TRACE_SUC1 ("socket=0x%x", res);
+ TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
return res;
}
int
_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
{
- int sockfd;
int res;
TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
"addr=%p, addrlen=%i", addr, addrlen);
- res = connect (sockfd, addr, addrlen);
- if (!res)
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+
+ res = connect (fd_table[fd].socket, addr, addrlen);
+ if (res)
{
- errno = wsa2errno (WSAGetLastError ());
+ gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
return TRACE_SYSRES (-1);
}