X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=src%2Fw32-io.c;h=10e0dade187d600dc7bd069bd91776f057fd32e0;hb=129741d2f713305a862a1505f20738a0ce2ea656;hp=1a65e537db695cd74c846a1f71339b0ebfa89c9c;hpb=d255b4bec9a1fdf0864640750161ea12999e43a5;p=gpgme.git diff --git a/src/w32-io.c b/src/w32-io.c index 1a65e53..10e0dad 100644 --- a/src/w32-io.c +++ b/src/w32-io.c @@ -1,6 +1,6 @@ /* 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. @@ -27,34 +27,111 @@ #include #include #include -#include #include -#include -#include -#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif #include #include "util.h" + +#ifdef HAVE_W32CE_SYSTEM +#include +#include +#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 { @@ -62,13 +139,14 @@ 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; @@ -103,6 +181,7 @@ DEFINE_STATIC_LOCK (reader_table_lock); struct writer_context_s { HANDLE file_hd; + int file_sock; HANDLE thread_hd; int refcount; @@ -154,6 +233,9 @@ get_desired_thread_priority (void) 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 @@ -165,12 +247,13 @@ set_synchronize (HANDLE hd) 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 } @@ -180,9 +263,15 @@ reader (void *arg) 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); @@ -210,37 +299,66 @@ reader (void *arg) 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, @@ -258,7 +376,7 @@ reader (void *arg) static struct reader_context_s * -create_reader (HANDLE fd) +create_reader (int fd) { struct reader_context_s *ctx; SECURITY_ATTRIBUTES sec_attr; @@ -277,7 +395,14 @@ create_reader (HANDLE fd) 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) @@ -302,7 +427,13 @@ create_reader (HANDLE fd) 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 ()); @@ -345,6 +476,22 @@ destroy_reader (struct reader_context_s *ctx) 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); @@ -388,7 +535,7 @@ find_reader (int fd, int start_it) 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; @@ -430,7 +577,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count) ctx = find_reader (fd, 1); if (!ctx) { - errno = EBADF; + gpg_err_set_errno (EBADF); return TRACE_SYSRES (-1); } if (ctx->eof_shortcut) @@ -458,7 +605,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count) 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); } @@ -476,7 +623,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count) 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); } } @@ -486,7 +633,7 @@ _gpgme_io_read (int fd, void *buffer, size_t count) 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); @@ -504,9 +651,15 @@ writer (void *arg) { 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); @@ -534,18 +687,45 @@ writer (void *arg) } 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); @@ -562,7 +742,7 @@ writer (void *arg) static struct writer_context_s * -create_writer (HANDLE fd) +create_writer (int fd) { struct writer_context_s *ctx; SECURITY_ATTRIBUTES sec_attr; @@ -581,7 +761,14 @@ create_writer (HANDLE fd) 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) @@ -606,7 +793,13 @@ create_writer (HANDLE fd) 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 ()); @@ -647,6 +840,20 @@ destroy_writer (struct writer_context_s *ctx) 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); @@ -691,7 +898,7 @@ find_writer (int fd, int start_it) 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; @@ -748,7 +955,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count) 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); @@ -762,9 +969,9 @@ _gpgme_io_write (int fd, const void *buffer, size_t count) { 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); } @@ -784,7 +991,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count) 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)) @@ -792,7 +999,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count) 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); @@ -804,13 +1011,57 @@ _gpgme_io_write (int fd, const void *buffer, size_t count) 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; @@ -818,8 +1069,10 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx) 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); } @@ -833,10 +1086,12 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx) { 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); @@ -851,19 +1106,26 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx) { 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); } @@ -873,11 +1135,17 @@ _gpgme_io_close (int fd) 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); } @@ -900,14 +1168,33 @@ _gpgme_io_close (int fd) 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); } @@ -933,7 +1220,7 @@ _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, 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; @@ -953,6 +1240,91 @@ _gpgme_io_set_nonblocking (int 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) { @@ -1006,13 +1378,15 @@ 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 */ @@ -1020,10 +1394,125 @@ _gpgme_io_spawn (const char *path, char *const argv[], 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. */ @@ -1067,7 +1556,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], if (!arg_string) { close (tmp_fd); - DeleteFile (tmp_name); + DeleteFileA (tmp_name); return TRACE_SYSRES (-1); } @@ -1079,8 +1568,9 @@ _gpgme_io_spawn (const char *path, char *const argv[], 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 */ @@ -1095,22 +1585,30 @@ _gpgme_io_spawn (const char *path, char *const argv[], 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 ()); @@ -1122,10 +1620,10 @@ _gpgme_io_spawn (const char *path, char *const argv[], 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. */ @@ -1139,14 +1637,16 @@ _gpgme_io_spawn (const char *path, char *const argv[], 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. */ @@ -1173,6 +1673,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], 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", @@ -1181,6 +1683,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], if (r_pid) *r_pid = (pid_t)pi.dwProcessId; + if (ResumeThread (pi.hThread) < 0) TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ()); @@ -1196,8 +1699,11 @@ _gpgme_io_spawn (const char *path, char *const argv[], 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) @@ -1254,7 +1760,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) 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; @@ -1277,7 +1783,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) 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; @@ -1324,6 +1830,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) else if (code == WAIT_FAILED) { int le = (int) GetLastError (); +#if 0 if (le == ERROR_INVALID_HANDLE) { int k; @@ -1340,6 +1847,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) } TRACE_LOG (" oops, or not???"); } +#endif TRACE_LOG1 ("WFMO failed: %d", le); count = -1; } @@ -1366,7 +1874,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) if (count < 0) { /* FIXME: Should determine a proper error code. */ - errno = EIO; + gpg_err_set_errno (EIO); } return TRACE_SYSRES (count); @@ -1386,6 +1894,15 @@ _gpgme_io_subsystem_init (void) 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); } @@ -1393,24 +1910,28 @@ _gpgme_io_fd2str (char *buf, int buflen, int 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) { @@ -1424,7 +1945,7 @@ _gpgme_io_dup (int fd) 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); @@ -1443,13 +1964,13 @@ _gpgme_io_dup (int fd) 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); } @@ -1494,18 +2015,25 @@ int _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; } @@ -1514,16 +2042,21 @@ _gpgme_io_socket (int domain, int type, int proto) 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); }