diff options
-rw-r--r-- | ChangeLog | 70 | ||||
-rw-r--r-- | configure.ac | 20 | ||||
-rw-r--r-- | doc/assuan.texi | 13 | ||||
-rw-r--r-- | src/ChangeLog | 34 | ||||
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/assuan-defs.h | 1 | ||||
-rw-r--r-- | src/assuan-pipe-connect.c | 8 | ||||
-rw-r--r-- | src/assuan.h.in (renamed from src/assuan.h) | 12 | ||||
-rw-r--r-- | src/gpgcedev.c | 183 | ||||
-rw-r--r-- | src/libassuan.def | 2 | ||||
-rw-r--r-- | src/mkheader.c | 192 | ||||
-rw-r--r-- | src/system-w32.c | 27 | ||||
-rw-r--r-- | src/system-w32ce.c | 425 | ||||
-rw-r--r-- | src/system.c | 16 | ||||
-rw-r--r-- | src/sysutils.c | 81 | ||||
-rw-r--r-- | tests/ChangeLog | 67 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/ce-createpipe.c | 4 | ||||
-rw-r--r-- | tests/ce-server.c | 2 | ||||
-rw-r--r-- | tests/pipeconnect.c | 397 |
20 files changed, 1196 insertions, 375 deletions
@@ -1,13 +1,15 @@ -2010-02-25 Werner Koch <[email protected]> +2010-03-22 Werner Koch <[email protected]> - * m4/libtool.m4 (_LT_CHECK_MAGIC_METHOD): Fix for non x86 mingw - targets. + * configure.ac (CC_FOR_BUILD): Add test. -2010-02-24 Werner Koch <[email protected]> +2010-03-17 Werner Koch <[email protected]> - * tests/ce-server.c: New. + * tests/ChangeLog: New. Move all relevant entries to there. - * tests/ce-createpipe.c [W32CE]: New. +2010-02-25 Werner Koch <[email protected]> + + * m4/libtool.m4 (_LT_CHECK_MAGIC_METHOD): Fix for non x86 mingw + targets. 2010-02-11 Werner Koch <[email protected]> @@ -17,10 +19,6 @@ * configure.ac (AC_TYPE_UINT16_T): New. -2010-01-27 Werner Koch <[email protected]> - - * tests/common.h (SOCKET2HANDLE, HANDLE2SOCKET): New. - 2010-01-26 Werner Koch <[email protected]> * configure.ac (NETLIBS) [W32CE]: Use -lws2. @@ -49,16 +47,6 @@ * configure.ac: Bump version to 2.0.0. -2009-11-05 Marcus Brinkmann <[email protected]> - - * tests/fdpassing.c (main): Call assuan_pipe_connect instead - of assuan_pipe_connect_ext. - -2009-11-04 Werner Koch <[email protected]> - - * tests/fdpassing.c (register_commands): Add NULL arg to - assuan_register_command. - 2009-10-16 Marcus Brinkmann <[email protected]> * autogen.sh: Remove --with-pth-prefix from configure invocation. @@ -74,7 +62,6 @@ 2009-09-19 Marcus Brinkmann <[email protected]> - * tests/fdpassing.c: Update to new API. * configure.ac: Check for stdint.h and inttypes.h. Invoke AC_TYPE_UINTPTR_T. @@ -101,18 +88,11 @@ (AC_CONFIG_FILES): Add src/versioninfo.rc. * ltmain.sh, m4/libtool.m4, m4/ltoptions.m4, m4/ltsugar.m4, m4/ltversion.m4, m4/lt~obsolete.m4: New files from libtool 2.2.6. - * tests/Makefile.am (AM_CFLAGS, LDADD): Add gpg-error. - * tests/fdpassing.c: Change error values to gpg-error ones. 2009-01-22 Werner Koch <[email protected]> * configure.ac: Check for nanoleep only in libc. -2008-11-03 Marcus Brinkmann <[email protected]> - - * tests/fdpassing.c (register_commands): Add missing initializer - to silence gcc -W warning. - 2008-05-25 Werner Koch <[email protected]> Released 1.0.5. @@ -130,7 +110,7 @@ 2007-08-24 Werner Koch <[email protected]> Released 1.0.3. - + Switched license of the library code back to LGPLv2.1. See NEWS. * COPYING.LIB: Replaced by LPGLv2.1 @@ -138,7 +118,7 @@ 2007-07-05 Werner Koch <[email protected]> Released 1.0.2. - + Relicensed to LGPLv3. * COPYING: Replaced by GPLv3. @@ -162,13 +142,13 @@ 2007-05-30 Werner Koch <[email protected]> - * autogen.sh <--build-w32>: Modernize. + * autogen.sh <--build-w32>: Modernize. 2007-05-29 Werner Koch <[email protected]> * configure.ac: Require automake 1.10 and autoconf 2.61. (AM_PROG_CC_C_O): New. Error out if no C-89 cc is installed. - (gl_HEADER_SYS_SOCKET): Explicitly add this for documentation. + (gl_HEADER_SYS_SOCKET): Explicitly add this for documentation. 2007-05-24 Werner Koch <[email protected]> @@ -202,8 +182,6 @@ Released 0.9.3. - * tests/Makefile.am (LDADD): Add NETLIBS. - * configure.ac: Check for cmsghdr. (USE_DESCRIPTOR_PASSING): Define it then. @@ -214,29 +192,15 @@ 2006-10-04 Werner Koch <[email protected]> Released 0.9.2. - + 2006-10-04 Werner Koch <[email protected]> Released 0.9.1. - + * configure.ac (AB_INIT): New. * m4/autobuild.m4: New. -2006-09-19 Werner Koch <[email protected]> - - * tests/fdpassing.c: Reverted Marcus changes. - (client): New arg FNAME to replace hardwired file name. - (main): Pass motd to client. - * tests/Makefile.am (AM_CPPFLAGS): Removed. - (EXTRA_DIST): Add motd. - -2006-09-19 Marcus Brinkmann <[email protected]> - - * tests/fdpassing.c (MOTD): New macro. - * tests/Makefile.am (AM_CPPFLAGS): New variable. - * tests/motd: New file. - 2006-09-14 Werner Koch <[email protected]> Released 0.9.0. @@ -292,7 +256,7 @@ 2004-09-27 Werner Koch <[email protected]> - * config.sub, config.guess: Updated. + * config.sub, config.guess: Updated. 2004-06-23 Marcus Brinkmann <[email protected]> @@ -398,7 +362,7 @@ * tests: New directory. - Copyright 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + Copyright 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without @@ -407,5 +371,3 @@ This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - diff --git a/configure.ac b/configure.ac index 8c9f3ab..feb918a 100644 --- a/configure.ac +++ b/configure.ac @@ -58,7 +58,7 @@ VERSION=$PACKAGE_VERSION AM_INIT_AUTOMAKE AM_MAINTAINER_MODE -AC_CONFIG_SRCDIR(src/assuan.h) +AC_CONFIG_SRCDIR(src/assuan.h.in) AC_CONFIG_MACRO_DIR(m4) AM_CONFIG_HEADER(config.h) AC_CANONICAL_HOST @@ -118,6 +118,22 @@ AC_PROG_LN_S AC_PROG_MAKE_SET #AC_ARG_PROGRAM +# We need to compile and run a program on the build machine. A +# comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in +# the AC archive is broken for autoconf 2.57. Given that there is no +# newer version of that macro, we assume that it is also broken for +# autoconf 2.61 and thus we use a simple but usually sufficient +# approach. +AC_MSG_CHECKING(for cc for build) +if test "$cross_compiling" = "yes"; then + CC_FOR_BUILD="${CC_FOR_BUILD-cc}" +else + CC_FOR_BUILD="${CC_FOR_BUILD-$CC}" +fi +AC_MSG_RESULT($CC_FOR_BUILD) +AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler]) + + if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" @@ -325,8 +341,6 @@ if test $assuan_cv_sys_so_peercred = yes; then fi - - # Create the config files. AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([m4/Makefile]) diff --git a/doc/assuan.texi b/doc/assuan.texi index be60ecd..9176973 100644 --- a/doc/assuan.texi +++ b/doc/assuan.texi @@ -1064,12 +1064,13 @@ program @var{name}, passing the arguments given in the NULL-terminated list @var{argv}. A list of file descriptors not to be closed may be given using the @code{ASSUAN_INVLID_FD} terminated array @var{fd_child_list}. -If @var{name} is a null pointer, only a fork but no exec is done. -Thus the child continues to run. However all file descriptors are -closed and some special environment variables are set. To let the -caller detect whether the child or the parent continues, the parent -returns with @code{"client"} returned in @var{argv} and the child -returns with @code{"server"} in @var{argv}. +If @var{name} is a null pointer, only a fork but no exec is done. Thus +the child continues to run. However all file descriptors are closed and +some special environment variables are set. To let the caller detect +whether the child or the parent continues, the parent returns with +@code{"client"} returned in @var{argv} and the child returns with +@code{"server"} in @var{argv}. This feature is only available on POSIX +platforms. If @var{atfork} is not NULL, this function is called in the child right after the fork and the value @var{atforkvalue} is passed as the first diff --git a/src/ChangeLog b/src/ChangeLog index 1d5ff2e..6034272 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,39 @@ +2010-03-22 Werner Koch <[email protected]> + + * Makefile.am (mkheader, assuan.h): Build header file. + * mkheader.c: New. + * assuan.h: Rename to assuan.h.in. + +2010-03-18 Werner Koch <[email protected]> + + * libassuan.def (_assuan_w32ce_prepare_pipe) + (_assuan_w32ce_finish_pipe): New + * gpgcedev.c (struct opnctx_s): Replace HD by RVID. + (GPGCEDEV_IOCTL_SET_HANDLE): Remove. + (GPGCEDEV_IOCTL_GET_RVID): New. + (create_rendezvous_id): New. + (get_new_opnctx): Init the RVID. + (set_handle): Remove. + (find_and_lock_opnctx, make_pipe, GPG_IOControl): Change to new + method. + * system-w32ce.c (_assuan_w32ce_prepare_pipe) + (_assuan_w32ce_finish_pipe): New. + (_assuan_w32ce_create_pipe): Re-implement using the new functions. + (__assuan_pipe): Create an inheritable pipe. + (build_w32_commandline): New arg FD2_ISNULL. + * system.c (_assuan_close_inheritable): New. + * assuan-pipe-connect.c (pipe_connect): Use the new function. + + * sysutils.c (_assuan_w32ce_create_pipe): Move to system-w32ce.c. + 2010-03-16 Werner Koch <[email protected]> + * system-w32ce.c (build_w32_commandline): Add args to pass the + special options for the standard descriptors. + (utf8_to_wchar, free_wchar): New. + (__assuan_spawn): Adjust for changes. Convert strings for + CreateProcess to wchar_t. + * system.c: For better readability move platform dependend code to .. * system-posix.c, system-w32.c, system-w32ce.c: .. New. * Makefile.am (common_sources): Account for this change. diff --git a/src/Makefile.am b/src/Makefile.am index c946f11..6723648 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ # Assuan Makefile -# Copyright (C) 2001, 2002, 2003, 2009 Free Software Foundation, Inc. +# Copyright (C) 2001, 2002, 2003, 2009, 2010 Free Software Foundation, Inc. # # This file is part of Assuan. # @@ -18,7 +18,7 @@ ## Process this file with automake to produce Makefile.in EXTRA_DIST = libassuan-config.in libassuan.m4 libassuan.vers \ - versioninfo.rc.in libassuan.def + versioninfo.rc.in libassuan.def mkheader.c INCLUDES = -I.. -I$(top_srcdir)/include bin_SCRIPTS = libassuan-config @@ -37,8 +37,12 @@ else libassuan_version_script_cmd = endif +CLEANFILES = mkheader assuan.h + +BUILT_SOURCES = assuan.h common_sources = \ + assuan.h.in assuan.h w32ce-add.h \ assuan-defs.h \ assuan.c context.c system.c \ debug.c debug.h conversion.c sysutils.c \ @@ -121,3 +125,10 @@ libgpgcedev_la_DEPENDENCIES = gpgcedev.def gpgcemgr_SOURCES = gpgcemgr.c gpgcemgr_CPPFLAGS = $(AM_CPPFLAGS) endif + +mkheader: mkheader.c Makefile + $(CC_FOR_BUILD) -I. -I$(srcdir) -o $@ $(srcdir)/mkheader.c + +assuan.h: assuan.h.in mkheader w32ce-add.h + ./mkheader $(host_os) $(srcdir)/assuan.h.in >$@ + diff --git a/src/assuan-defs.h b/src/assuan-defs.h index dd91f09..63f0d10 100644 --- a/src/assuan-defs.h +++ b/src/assuan-defs.h @@ -230,6 +230,7 @@ void _assuan_free (assuan_context_t ctx, void *ptr); void _assuan_usleep (assuan_context_t ctx, unsigned int usec); int _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx); int _assuan_close (assuan_context_t ctx, assuan_fd_t fd); +int _assuan_close_inheritable (assuan_context_t ctx, assuan_fd_t fd); ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size); ssize_t _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, diff --git a/src/assuan-pipe-connect.c b/src/assuan-pipe-connect.c index dec8ccc..4eccdde 100644 --- a/src/assuan-pipe-connect.c +++ b/src/assuan-pipe-connect.c @@ -175,7 +175,7 @@ pipe_connect (assuan_context_t ctx, if (_assuan_pipe (ctx, wp, 0) < 0) { _assuan_close (ctx, rp[0]); - _assuan_close (ctx, rp[1]); + _assuan_close_inheritable (ctx, rp[1]); return _assuan_error (ctx, gpg_err_code_from_syserror ()); } @@ -197,8 +197,8 @@ pipe_connect (assuan_context_t ctx, } /* Close the stdin/stdout child fds in the parent. */ - _assuan_close (ctx, rp[1]); - _assuan_close (ctx, wp[0]); + _assuan_close_inheritable (ctx, rp[1]); + _assuan_close_inheritable (ctx, wp[0]); ctx->engine.release = _assuan_client_release; ctx->engine.readfnc = _assuan_simple_read; @@ -393,7 +393,7 @@ socketpair_connect (assuan_context_t ctx, environment variables are set. To let the caller detect whether the child or the parent continues, the child returns "client" or "server" in *ARGV (but it is sufficient to check only the first - character). */ + character). This feature is only available on POSIX platforms. */ gpg_error_t assuan_pipe_connect (assuan_context_t ctx, const char *name, const char *argv[], diff --git a/src/assuan.h b/src/assuan.h.in index c275287..91b75ab 100644 --- a/src/assuan.h +++ b/src/assuan.h.in @@ -1,4 +1,4 @@ -/* assuan.h - Definitions for the Assuan IPC library +/* assuan.h - Definitions for the Assuan IPC library -*- c -*- Copyright (C) 2001, 2002, 2003, 2005, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. @@ -16,6 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with this program; if not, see <http://www.gnu.org/licenses/>. + + @configure_input@ */ #ifndef ASSUAN_H @@ -639,13 +641,7 @@ int __assuan_socketpair (assuan_context_t ctx, int _namespace, int style, extern struct assuan_system_hooks _assuan_system_pth; #define ASSUAN_SYSTEM_PTH &_assuan_system_pth -#ifdef __MINGW32CE__ -/* FIXME: Include this code only if build for this platform. */ -DWORD _assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, - LPSECURITY_ATTRIBUTES sec_attr, DWORD size); -#define CreatePipe(a,b,c,d) _assuan_w32ce_create_pipe ((a),(b),(c),(d)) - -#endif /*__MINGW32CE__*/ +@include:w32ce-add@ #ifdef __cplusplus } diff --git a/src/gpgcedev.c b/src/gpgcedev.c index 763ab29..fe9c80a 100644 --- a/src/gpgcedev.c +++ b/src/gpgcedev.c @@ -17,6 +17,7 @@ License along with this program; if not, see <http://www.gnu.org/licenses/>. */ + #include <stdio.h> #include <stdarg.h> #include <windows.h> @@ -35,19 +36,21 @@ #endif /*IOCTL_PSL_NOTIFY*/ -/* The IOCTL used to tell the device about the handle. +/* The IOCTL to return the rendezvous id of the handle. - The required inbuf parameter is the address of a variable holding - the handle. */ -#define GPGCEDEV_IOCTL_SET_HANDLE \ + The required outbuf parameter is the address of a variable to store + the rendezvous ID, which is a LONG value. */ +#define GPGCEDEV_IOCTL_GET_RVID \ CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS) /* The IOCTL used to create the pipe. - The caller sends this IOCTL to the read handle. The required inbuf - parameter is the address of variable holding the write handle. - Note that the SET_HANDLE IOCTLs must have been used prior to this - one. */ + The caller sends this IOCTL to the read or the write handle. The + required inbuf parameter is address of a variable holding the + rendezvous id of the pipe's other end. There is one possible + problem with eocdde: If a pipe is kept in non-rendezvous state + until after the rendezvous ids overflow, it is possible that the + wrong end will be used. However this is not a realistic scenario. */ #define GPGCEDEV_IOCTL_MAKE_PIPE \ CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -62,7 +65,7 @@ struct opnctx_s other context; i.e. a pipe has been established. */ int is_write; /* True if this is the write end of the pipe. */ - HANDLE hd; /* The system's handle object or INVALID_HANDLE_VALUE. */ + LONG rvid; /* The unique rendezvous identifier. */ DWORD access_code;/* Value from OpenFile. */ DWORD share_mode; /* Value from OpenFile. */ CRITICAL_SECTION critsect; /* Lock for all operations. */ @@ -118,6 +121,19 @@ log_debug (const char *fmt, ...) } +/* Return a new rendezvous next command id. Command Ids are used to group + resources of one command. We will never return an RVID of 0. */ +static LONG +create_rendezvous_id (void) +{ + static LONG rendezvous_id; + LONG rvid; + + while (!(rvid = InterlockedIncrement (&rendezvous_id))) + ; + return rvid; +} + /* Return a new opnctx handle and mark it as used. Returns NULL and @@ -152,8 +168,7 @@ get_new_opnctx (void) } opnctx = opnctx_table + idx; opnctx->assoc = NULL; - opnctx->hd = INVALID_HANDLE_VALUE; - opnctx->assoc = 0; + opnctx->rvid = create_rendezvous_id (); opnctx->buffer_size = 512; opnctx->buffer = malloc (opnctx->buffer_size); if (!opnctx->buffer) @@ -173,36 +188,45 @@ get_new_opnctx (void) leave: LeaveCriticalSection (&opnctx_table_cs); - log_debug ("get_new_opnctx -> %p\n", opnctx); + if (opnctx) + log_debug ("get_new_opnctx -> %p (rvid=%ld)\n", opnctx, opnctx->rvid); + else + log_debug ("get_new_opnctx -> failed\n"); return opnctx; } -/* Find the OPNCTX for handle HD. */ +/* Find the OPNCTX object with the rendezvous id RVID. */ static opnctx_t -find_and_lock_opnctx (HANDLE hd) +find_and_lock_opnctx (LONG rvid) { opnctx_t result = NULL; int idx; EnterCriticalSection (&opnctx_table_cs); for (idx=0; idx < opnctx_table_size; idx++) - if (opnctx_table[idx].inuse && opnctx_table[idx].hd == hd) + if (opnctx_table[idx].inuse && opnctx_table[idx].rvid == rvid) { result = opnctx_table + idx; break; } LeaveCriticalSection (&opnctx_table_cs); if (!result) - SetLastError (ERROR_INVALID_HANDLE); + { + SetLastError (ERROR_INVALID_HANDLE); + log_debug ("find_opnctx -> invalid rendezvous id\n"); + } else if (TryEnterCriticalSection (&result->critsect)) - result->locked++; + { + result->locked++; + log_debug ("find_opnctx -> %p (rvid=%ld)\n", result, result->rvid); + } else { SetLastError (ERROR_BUSY); result = NULL; + log_debug ("find_opnctx -> busy\n"); } - log_debug ("find_opnctx -> %p\n", result); return result; } @@ -353,14 +377,10 @@ GPG_Close (DWORD opnctx_arg) for (idx=0; idx < opnctx_table_size; idx++) if (opnctx_table[idx].inuse && (opnctx_table + idx) == opnctx) { - if (opnctx->hd != INVALID_HANDLE_VALUE) + if (opnctx->assoc) { - if (opnctx->assoc) - { - opnctx->assoc->assoc = NULL; - opnctx->assoc = NULL; - } - opnctx->hd = INVALID_HANDLE_VALUE; + opnctx->assoc->assoc = NULL; + opnctx->assoc = NULL; } if (opnctx->locked) { @@ -419,7 +439,7 @@ GPG_Read (DWORD opnctx_arg, void *buffer, DWORD count) SetLastError (ERROR_INVALID_ACCESS); goto leave; } - if (rctx->hd == INVALID_HANDLE_VALUE || !rctx->assoc) + if (!rctx->assoc) { SetLastError (ERROR_BROKEN_PIPE); goto leave; @@ -490,7 +510,7 @@ GPG_Write (DWORD opnctx_arg, const void *buffer, DWORD count) SetLastError (ERROR_INVALID_ACCESS); goto leave; } - if (wctx->hd == INVALID_HANDLE_VALUE || !wctx->assoc) + if (!wctx->assoc) { SetLastError (ERROR_BROKEN_PIPE); goto leave; @@ -546,79 +566,78 @@ GPG_Seek (DWORD opnctx, long amount, WORD type) static BOOL -set_handle (opnctx_t opnctx, HANDLE hd) -{ - log_debug (" set_handle(%p, hd=%p)\n", opnctx, hd); - if (opnctx->hd != INVALID_HANDLE_VALUE) - { - SetLastError (ERROR_ALREADY_ASSIGNED); - return FALSE; - } - opnctx->hd = hd; - return TRUE; -} - -static BOOL -make_pipe (opnctx_t rctx, HANDLE hd) +make_pipe (opnctx_t ctx, LONG rvid) { BOOL result = FALSE; - opnctx_t wctx = NULL; + opnctx_t peerctx = NULL; - log_debug (" make_pipe(%p, hd=%p)\n", rctx, hd); - if (rctx->hd == INVALID_HANDLE_VALUE) - { - SetLastError (ERROR_NOT_READY); - goto leave; - } - if (rctx->assoc) + log_debug (" make_pipe(%p, rvid=%ld)\n", ctx, rvid); + if (ctx->assoc) { SetLastError (ERROR_ALREADY_ASSIGNED); goto leave; } - if (!(rctx->access_code & GENERIC_READ)) - { - SetLastError (ERROR_INVALID_ACCESS); - goto leave; - } - wctx = find_and_lock_opnctx (hd); - if (!wctx) + peerctx = find_and_lock_opnctx (rvid); + if (!peerctx) { SetLastError (ERROR_NOT_FOUND); goto leave; } - if (wctx == rctx) + if (peerctx == ctx) { SetLastError (ERROR_INVALID_TARGET_HANDLE); goto leave; } - if (wctx->hd == INVALID_HANDLE_VALUE) + if (peerctx->assoc) { - SetLastError (ERROR_NOT_READY); + SetLastError (ERROR_ALREADY_ASSIGNED); goto leave; } - if (wctx->assoc) + + if ((ctx->access_code & GENERIC_READ)) { - SetLastError (ERROR_ALREADY_ASSIGNED); - goto leave; + /* Check that the peer is a write end. */ + if (!(peerctx->access_code & GENERIC_WRITE)) + { + SetLastError (ERROR_INVALID_ACCESS); + goto leave; + } + peerctx->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); + peerctx->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); + + ctx->assoc = peerctx; + peerctx->assoc = ctx; + ctx->is_write = 0; + peerctx->is_write = 1; + result = TRUE; + } + else if ((ctx->access_code & GENERIC_WRITE)) + { + /* Check that the peer is a read end. */ + if (!(peerctx->access_code & GENERIC_READ)) + { + SetLastError (ERROR_INVALID_ACCESS); + goto leave; + } + ctx->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); + ctx->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); + + ctx->assoc = peerctx; + peerctx->assoc = ctx; + ctx->is_write = 1; + peerctx->is_write = 0; + result = TRUE; } - if (!(wctx->access_code & GENERIC_WRITE)) + else { SetLastError (ERROR_INVALID_ACCESS); goto leave; } - wctx->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); - wctx->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); - - rctx->assoc = wctx; - wctx->assoc = rctx; - rctx->is_write = 0; - wctx->is_write = 1; - result = TRUE; leave: - if (wctx) - unlock_opnctx (wctx); + if (peerctx) + unlock_opnctx (peerctx); return result; } @@ -629,6 +648,7 @@ GPG_IOControl (DWORD opnctx_arg, DWORD code, void *inbuf, DWORD inbuflen, { opnctx_t opnctx = (opnctx_t)opnctx_arg; BOOL result = FALSE; + LONG rvid; log_debug ("GPG_IOControl(%p, %d)\n", (void*)opnctx, code); if (!validate_and_lock_opnctx (opnctx, LOCK_TRY)) @@ -636,25 +656,28 @@ GPG_IOControl (DWORD opnctx_arg, DWORD code, void *inbuf, DWORD inbuflen, switch (code) { - case GPGCEDEV_IOCTL_SET_HANDLE: - if (!opnctx || !inbuf || inbuflen < sizeof (HANDLE) - || outbuf || outbuflen || actualoutlen ) + case GPGCEDEV_IOCTL_GET_RVID: + if (!opnctx || inbuf || inbuflen + || !outbuf || outbuflen < sizeof (LONG)) { SetLastError (ERROR_INVALID_PARAMETER); goto leave; } - if (set_handle (opnctx, *(HANDLE*)inbuf)) - result = TRUE; + memcpy (outbuf, &opnctx->rvid, sizeof (LONG)); + if (actualoutlen) + *actualoutlen = sizeof (LONG); + result = TRUE; break; case GPGCEDEV_IOCTL_MAKE_PIPE: - if (!opnctx || !inbuf || inbuflen < sizeof (HANDLE) + if (!opnctx || !inbuf || inbuflen < sizeof (LONG) || outbuf || outbuflen || actualoutlen ) { SetLastError (ERROR_INVALID_PARAMETER); goto leave; } - if (make_pipe (opnctx, *(HANDLE*)inbuf)) + memcpy (&rvid, inbuf, sizeof (LONG)); + if (make_pipe (opnctx, rvid)) result = TRUE; break; diff --git a/src/libassuan.def b/src/libassuan.def index ba8ed8d..59aba41 100644 --- a/src/libassuan.def +++ b/src/libassuan.def @@ -99,6 +99,8 @@ EXPORTS assuan_set_sock_nonce @78 _assuan_w32ce_create_pipe @79 assuan_free @80 + _assuan_w32ce_prepare_pipe @81 + _assuan_w32ce_finish_pipe @82 ; END diff --git a/src/mkheader.c b/src/mkheader.c new file mode 100644 index 0000000..22cdc94 --- /dev/null +++ b/src/mkheader.c @@ -0,0 +1,192 @@ +/* mkheader.c - Create a header file for libassuan. + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#define PGM "mkheader" + +#define LINESIZE 1024 + +static const char *host_os; +static char *srcdir; + + +/* Include the file NAME form the source directory. The included file + is not further expanded. It may have comments indicated by a + double hash masrk at the begin of a line. */ +static void +include_file (const char *fname, int lnr, const char *name) +{ + FILE *fp; + char *incfname; + char line[LINESIZE]; + + incfname = malloc (strlen (srcdir) + strlen (name) + 1); + if (!incfname) + { + fputs (PGM ": out of core\n", stderr); + exit (1); + } + strcpy (incfname, srcdir); + strcat (incfname, name); + + fp = fopen (incfname, "r"); + if (!fp) + { + fprintf (stderr, "%s:%d: error including `%s': %s\n", + fname, lnr, incfname, strerror (errno)); + exit (1); + } + + while (fgets (line, LINESIZE, fp)) + { + if (line[0] != '#' && line[1] != '#') + fputs (line, stdout); + } + if (ferror (fp)) + { + fprintf (stderr, "%s:%d: error reading `%s': %s\n", + fname, lnr, incfname, strerror (errno)); + exit (1); + } + fclose (fp); + free (incfname); +} + + +static int +write_special (const char *fname, int lnr, const char *tag) +{ + if (!strcmp (tag, "include:w32ce-add")) + { + if (!strcmp (host_os, "mingw32ce")) + include_file (fname, lnr, "w32ce-add.h"); + } + else + return 0; /* Unknown tag. */ + + return 1; /* Tag processed. */ +} + + +int +main (int argc, char **argv) +{ + FILE *fp; + char line[LINESIZE]; + int lnr = 0; + const char *fname, *s; + char *p1, *p2; + + if (argc) + { + argc--; argv++; + } + + if (argc != 2) + { + fputs ("usage: " PGM " host_os template.h\n", stderr); + return 1; + } + host_os = argv[0]; + fname = argv[1]; + + srcdir = malloc (strlen (fname) + 2 + 1); + if (!srcdir) + { + fputs (PGM ": out of core\n", stderr); + return 1; + } + strcpy (srcdir, fname); + p1 = strrchr (srcdir, '/'); + if (p1) + p1[1] = 0; + else + strcpy (srcdir, "./"); + + fp = fopen (fname, "r"); + if (!fp) + { + fprintf (stderr, "%s:%d: can't open file: %s", + fname, lnr, strerror (errno)); + return 1; + } + + while (fgets (line, LINESIZE, fp)) + { + size_t n = strlen (line); + + lnr++; + if (!n || line[n-1] != '\n') + { + fprintf (stderr, + "%s:%d: trailing linefeed missing, line too long or " + "embedded Nul character", fname, lnr); + break; + } + line[--n] = 0; + + p1 = strchr (line, '@'); + p2 = p1? strchr (p1+1, '@') : NULL; + if (!p1 || !p2 || p2-p1 == 1) + { + puts (line); + continue; + } + *p1++ = 0; + *p2++ = 0; + fputs (line, stdout); + + if (!strcmp (p1, "configure_input")) + { + s = strrchr (fname, '/'); + printf ("Do not edit. Generated from %s by %s for %s.", + s? s+1 : fname, PGM, host_os); + } + else if (!write_special (fname, lnr, p1)) + { + putchar ('@'); + fputs (p1, stdout); + putchar ('@'); + } + + fputs (p2, stdout); + putchar ('\n'); + } + + if (ferror (fp)) + { + fprintf (stderr, "%s:%d: error reading file: %s\n", + fname, lnr, strerror (errno)); + return 1; + } + + fputs ("/*\n" + "Local Variables:\n" + "buffer-read-only: t\n" + "End:\n" + "*/\n", stdout); + + if (ferror (stdout)) + { + fprintf (stderr, PGM ": error writing stdout: %s\n", strerror (errno)); + return 1; + } + + fclose (fp); + + return 0; +} diff --git a/src/system-w32.c b/src/system-w32.c index 31da194..85f0e40 100644 --- a/src/system-w32.c +++ b/src/system-w32.c @@ -64,6 +64,33 @@ __assuan_usleep (assuan_context_t ctx, unsigned int usec) +/* Three simple wrappers, only used because thes function are named in + the def file. */ +HANDLE +_assuan_w32ce_prepare_pipe (int *r_rvid, int write_end) +{ + (void)r_rvid; + (void)write_end; + return INVALID_HANDLE_VALUE; +} + +HANDLE +_assuan_w32ce_finish_pipe (int rvid, int write_end) +{ + (void)rvid; + (void)write_end; + return INVALID_HANDLE_VALUE; +} + +DWORD +_assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, + LPSECURITY_ATTRIBUTES sec_attr, DWORD size) +{ + return CreatePipe (read_hd, write_hd, sec_attr, size); +} + + + /* Create a pipe with one inheritable end. Default implementation. */ int __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) diff --git a/src/system-w32ce.c b/src/system-w32ce.c index ce0796c..1ecb4d8 100644 --- a/src/system-w32ce.c +++ b/src/system-w32ce.c @@ -27,26 +27,72 @@ #include <time.h> #include <fcntl.h> #include <windows.h> +#include <winioctl.h> +#include <devload.h> #include "assuan-defs.h" #include "debug.h" +#define GPGCEDEV_IOCTL_GET_RVID \ + CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define GPGCEDEV_IOCTL_MAKE_PIPE \ + CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) + + + -assuan_fd_t -assuan_fdopen (int fd) +static wchar_t * +utf8_to_wchar (const char *string) { - assuan_fd_t ifd = (assuan_fd_t)fd; - assuan_fd_t ofd; + int n; + size_t nbytes; + wchar_t *result; + + if (!string) + return NULL; - if (! DuplicateHandle(GetCurrentProcess(), ifd, - GetCurrentProcess(), &ofd, 0, - TRUE, DUPLICATE_SAME_ACCESS)) + n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); + if (n < 0) { - gpg_err_set_errno (EIO); - return ASSUAN_INVALID_FD; + gpg_err_set_errno (EINVAL); + return NULL; + } + + nbytes = (size_t)(n+1) * sizeof(*result); + if (nbytes / sizeof(*result) != (n+1)) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + result = malloc (nbytes); + if (!result) + return NULL; + + n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); + if (n < 0) + { + free (result); + gpg_err_set_errno (EINVAL); + result = NULL; } - return ofd; + return result; +} + +/* Convenience function. */ +static void +free_wchar (wchar_t *string) +{ + if (string) + free (string); +} + + + +assuan_fd_t +assuan_fdopen (int fd) +{ + return (assuan_fd_t)fd; } @@ -56,60 +102,149 @@ assuan_fdopen (int fd) void __assuan_usleep (assuan_context_t ctx, unsigned int usec) { - if (!usec) - return; - - Sleep (usec / 1000); + if (usec) + Sleep (usec / 1000); } -/* Create a pipe with one inheritable end. Default implementation. */ -int -__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) +/* Prepare a pipe. Returns a handle which is, depending on WRITE_END, + will either act the read or as the write end of the pipe. The + other value returned is a rendezvous id used to complete the pipe + creation with _assuan_w32ce_finish_pipe. The rendezvous id may be + passed to another process and that process may finish the pipe + creation. This creates the interprocess pipe. The rendezvous id + is not a handle but a plain number; there is no release function + and care should be taken not to pass it to a function expecting a + handle. */ +HANDLE +_assuan_w32ce_prepare_pipe (int *r_rvid, int write_end) { - HANDLE rh; - HANDLE wh; - HANDLE th; - SECURITY_ATTRIBUTES sec_attr; + HANDLE hd; + LONG rvid; + + ActivateDevice (L"Drivers\\GnuPG_Device", 0); + + /* Note: Using "\\$device\\GPG1" should be identical to "GPG1:". + However this returns an invalid parameter error without having + called GPG_Init in the driver. The docs mention something about + RegisterAFXEx but that API is not documented. */ + hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hd != INVALID_HANDLE_VALUE) + { + if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_GET_RVID, + NULL, 0, &rvid, sizeof rvid, NULL, NULL)) + { + DWORD lastrc = GetLastError (); + CloseHandle (hd); + hd = INVALID_HANDLE_VALUE; + SetLastError (lastrc); + } + else + *r_rvid = rvid; + } + + return hd; +} + - memset (&sec_attr, 0, sizeof (sec_attr)); - sec_attr.nLength = sizeof (sec_attr); - sec_attr.bInheritHandle = FALSE; +/* Create a pipe. WRITE_END shall have the opposite value of the one + pssed to _assuan_w32ce_prepare_pipe; see there for more + details. */ +HANDLE +_assuan_w32ce_finish_pipe (int rvid, int write_end) +{ + HANDLE hd; - if (!CreatePipe (&rh, &wh, &sec_attr, 0)) + hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL); + if (hd != INVALID_HANDLE_VALUE) { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, - "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); - gpg_err_set_errno (EIO); - return -1; + if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_MAKE_PIPE, + &rvid, sizeof rvid, NULL, 0, NULL, NULL)) + { + DWORD lastrc = GetLastError (); + CloseHandle (hd); + hd = INVALID_HANDLE_VALUE; + SetLastError (lastrc); + } + } + + return hd; +} + + +/* WindowsCE does not provide a pipe feature. However we need + something like a pipe to convey data between processes and in some + cases within a process. This replacement is not only used by + libassuan but exported and thus usable by gnupg and gpgme as well. */ +DWORD +_assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, + LPSECURITY_ATTRIBUTES sec_attr, DWORD size) +{ + HANDLE hd[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + int rvid; + int rc = 0; + + hd[0] = _assuan_w32ce_prepare_pipe (&rvid, 0); + if (hd[0] != INVALID_HANDLE_VALUE) + { + hd[1] = _assuan_w32ce_finish_pipe (rvid, 1); + if (hd[1] != INVALID_HANDLE_VALUE) + rc = 1; + else + { + DWORD lastrc = GetLastError (); + CloseHandle (hd[0]); + hd[0] = INVALID_HANDLE_VALUE; + SetLastError (lastrc); + } } + + *read_hd = hd[0]; + *write_hd = hd[1]; + return rc; +} + - if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh, - GetCurrentProcess(), &th, 0, - TRUE, DUPLICATE_SAME_ACCESS )) +/* Create a pipe with one inheritable end. Default implementation. + If INHERIT_IDX is 0, the read end of the pipe is made inheritable; + with INHERIT_IDX is 1 the write end will be inheritable. The + question now is how we create an inheritable pipe end under windows + CE were handles are process local objects? The trick we employ is + to defer the actual creation to the other end: We create an + incomplete pipe and pass a rendezvous id to the other end + (process). The other end now uses the rendezvous id to lookup the + pipe in our device driver, creates a new handle and uses that one + to finally establish the pipe. */ +int +__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) +{ + HANDLE hd; + int rvid; + + hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx); + if (hd == INVALID_HANDLE_VALUE) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, - "DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1)); - CloseHandle (rh); - CloseHandle (wh); + "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); gpg_err_set_errno (EIO); return -1; } - if (inherit_idx == 0) + + if (inherit_idx) { - CloseHandle (rh); - rh = th; + fd[0] = hd; + fd[1] = (void*)rvid; } else { - CloseHandle (wh); - wh = th; + fd[0] = (void*)rvid; + fd[1] = hd; } - - fd[0] = rh; - fd[1] = wh; - return 0; } @@ -143,9 +278,13 @@ __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) read if recv detects that it is not a network socket. */ int res; + TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_read", ctx, + "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); + res = recv (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) { + TRACE_LOG1 ("recv failed: rc=%d", (int)WSAGetLastError ()); switch (WSAGetLastError ()) { case WSAENOTSOCK: @@ -155,6 +294,7 @@ __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) res = ReadFile (fd, buffer, size, &nread, NULL); if (! res) { + TRACE_LOG1 ("ReadFile failed: rc=%d", (int)GetLastError ()); switch (GetLastError ()) { case ERROR_BROKEN_PIPE: @@ -184,7 +324,7 @@ __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) break; } } - return res; + return TRACE_SYSRES (res); } @@ -198,14 +338,19 @@ __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, write if send detects that it is not a network socket. */ int res; - res = send (HANDLE2SOCKET (fd), buffer, size, 0); + TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_write", ctx, + "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); + + res = send ((int)fd, buffer, size, 0); if (res == -1 && WSAGetLastError () == WSAENOTSOCK) { DWORD nwrite; + TRACE_LOG ("send call failed - trying WriteFile"); res = WriteFile (fd, buffer, size, &nwrite, NULL); if (! res) { + TRACE_LOG1 ("WriteFile failed: rc=%d", (int)GetLastError ()); switch (GetLastError ()) { case ERROR_BROKEN_PIPE: @@ -222,7 +367,9 @@ __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, else res = (int) nwrite; } - return res; + else if (res == -1) + TRACE_LOG1 ("send call failed: rc=%d", (int)GetLastError ()); + return TRACE_SYSRES (res); } @@ -253,17 +400,43 @@ __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, CMDLINE gets the address of a newly allocated string. */ static int build_w32_commandline (assuan_context_t ctx, const char * const *argv, - char **cmdline) + assuan_fd_t fd0, assuan_fd_t fd1, assuan_fd_t fd2, + int fd2_isnull, + char **cmdline) { int i, n; const char *s; char *buf, *p; + char fdbuf[3*30]; + p = fdbuf; + *p = 0; + if (fd0 != ASSUAN_INVALID_FD) + { + snprintf (p, 25, "-&S0=%d ", (int)fd0); + p += strlen (p); + } + if (fd1 != ASSUAN_INVALID_FD) + { + snprintf (p, 25, "-&S1=%d ", (int)fd1); + p += strlen (p); + } + if (fd2 != ASSUAN_INVALID_FD) + { + if (fd2_isnull) + strcpy (p, "-&S2=null "); + else + snprintf (p, 25, "-&S2=%d ", (int)fd2); + p += strlen (p); + } + *cmdline = NULL; - n = 0; + n = strlen (fdbuf); for (i=0; (s = argv[i]); i++) { - n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ + 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. */ @@ -274,10 +447,14 @@ build_w32_commandline (assuan_context_t ctx, const char * const *argv, if (! buf) return -1; + p = stpcpy (p, fdbuf); for (i = 0; argv[i]; i++) { - if (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\"")) @@ -296,7 +473,7 @@ build_w32_commandline (assuan_context_t ctx, const char * const *argv, p = stpcpy (p, argv[i]); } - *cmdline= buf; + *cmdline = buf; return 0; } @@ -309,7 +486,6 @@ __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { - SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ @@ -317,117 +493,95 @@ __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, 0, /* Returns pid. */ 0 /* Returns tid. */ }; - STARTUPINFO si; assuan_fd_t fd; assuan_fd_t *fdp; + assuan_fd_t fd_err; + int fd_err_isnull = 0; char *cmdline; - HANDLE nullfd = INVALID_HANDLE_VALUE; - - /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env - variable. However this requires us to write a full environment - handler, because the strings are expected in sorted order. The - suggestion given in the MS Reference Library, to save the old - value, changeit, create proces and restore it, is not thread - safe. */ - /* Build the command line. */ - if (build_w32_commandline (ctx, argv, &cmdline)) - return -1; + /* Dup stderr to /dev/null unless it is in the list of FDs to be + passed to the child. Well we don't actually open nul because + that is not available on Windows, but use our hack for it. + Because an RVID of 0 is an invalid value and HANDLES will never + have this value either, we test for this as well. */ - /* Start the process. */ - memset (&sec_attr, 0, sizeof sec_attr); - sec_attr.nLength = sizeof sec_attr; - sec_attr.bInheritHandle = FALSE; - - memset (&si, 0, sizeof si); - si.cb = sizeof (si); - si.dwFlags = STARTF_USESTDHANDLES; - /* FIXME: Dup to nul if ASSUAN_INVALID_FD. */ - si.hStdInput = fd_in; - si.hStdOutput = fd_out; + /* FIXME: CHECKOUT WHAT TO DO WITH STDERR HERE. WE NEED TO DEFINE + WHETHER THE FD_CHILD_LIST HAS HANDLES OR RENDEZVOUS IDS. */ - /* Dup stderr to /dev/null unless it is in the list of FDs to be - passed to the child. */ fd = assuan_fd_from_posix_fd (fileno (stderr)); fdp = fd_child_list; if (fdp) { - for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++) + for (; *fdp != ASSUAN_INVALID_FD && *fdp != 0 && *fdp != fd; fdp++) ; } if (!fdp || *fdp == ASSUAN_INVALID_FD) + fd_err_isnull = 1; + fd_err = fd; + + if (build_w32_commandline (ctx, argv, fd_in, fd_out, fd_err, fd_err_isnull, + &cmdline)) { - nullfd = CreateFileW (L"nul", GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, NULL); - if (nullfd == INVALID_HANDLE_VALUE) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "can't open `nul': %s", _assuan_w32_strerror (ctx, -1)); - _assuan_free (ctx, cmdline); - gpg_err_set_errno (EIO); - return -1; - } - si.hStdError = nullfd; + return -1; } - else - si.hStdError = fd; - - /* Note: We inherit all handles flagged as inheritable. This seems - to be a security flaw but there seems to be no way of selecting - handles to inherit. */ - /* _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n", */ - /* name, cmdline); */ - if (!CreateProcess (name, /* Program to start. */ - cmdline, /* Command line arguments. */ - &sec_attr, /* Process security attributes. */ - &sec_attr, /* Thread security attributes. */ - TRUE, /* Inherit handles. */ - (CREATE_DEFAULT_ERROR_MODE - | CREATE_SUSPENDED), /* Creation flags. */ - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - )) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx, - "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); - _assuan_free (ctx, cmdline); - if (nullfd != INVALID_HANDLE_VALUE) - CloseHandle (nullfd); - gpg_err_set_errno (EIO); + TRACE2 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "path=`%s' cmdline=`%s'", name, cmdline); + + { + wchar_t *wcmdline, *wname; + + wcmdline = utf8_to_wchar (cmdline); + _assuan_free (ctx, cmdline); + if (!wcmdline) return -1; - } - _assuan_free (ctx, cmdline); - if (nullfd != INVALID_HANDLE_VALUE) - CloseHandle (nullfd); + wname = utf8_to_wchar (name); + if (!wname) + { + free_wchar (wcmdline); + return -1; + } + + if (!CreateProcess (wname, /* Program to start. */ + wcmdline, /* 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.*/ + )) + { + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); + free_wchar (wname); + free_wchar (wcmdline); + gpg_err_set_errno (EIO); + return -1; + } + free_wchar (wname); + free_wchar (wcmdline); + } ResumeThread (pi.hThread); - CloseHandle (pi.hThread); - /* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ - /* " dwProcessID=%d dwThreadId=%d\n", */ - /* pi.hProcess, pi.hThread, */ - /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + TRACE4 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, + "CreateProcess ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); + CloseHandle (pi.hThread); + *r_pid = (pid_t) pi.hProcess; - - /* No need to modify peer process, as we don't change the handle - names. However this also means we are not safe, as we inherit - too many handles. Should use approach similar to gpgme and glib - using a helper process. */ - return 0; } -/* FIXME: Add some sort of waitpid function that covers GPGME and - gpg-agent's use of assuan. */ static pid_t __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options) @@ -446,6 +600,7 @@ __assuan_socketpair (assuan_context_t ctx, int namespace, int style, return -1; } + /* The default system hooks for assuan contexts. */ struct assuan_system_hooks _assuan_system_hooks = diff --git a/src/system.c b/src/system.c index 22d7a0b..1f180e9 100644 --- a/src/system.c +++ b/src/system.c @@ -170,6 +170,22 @@ _assuan_close (assuan_context_t ctx, assuan_fd_t fd) } +/* Same as assuan_close but used for the inheritable end of a + pipe. */ +int +_assuan_close_inheritable (assuan_context_t ctx, assuan_fd_t fd) +{ + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "_assuan_close", ctx, + "fd=0x%x", fd); + +#ifdef HAVE_W32CE_SYSTEM + return 0; /* Nothing to do because it is a rendezvous id. */ +#else + return (ctx->system.close) (ctx, fd); +#endif +} + + ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) diff --git a/src/sysutils.c b/src/sysutils.c index c7e20a8..1760f44 100644 --- a/src/sysutils.c +++ b/src/sysutils.c @@ -34,17 +34,9 @@ #include "assuan-defs.h" -#ifdef HAVE_W32CE_SYSTEM -#define GPGCEDEV_IOCTL_SET_HANDLE \ - CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define GPGCEDEV_IOCTL_MAKE_PIPE \ - CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) -#endif /*HAVE_W32CE_SYSTEM*/ - - /* This is actually a dummy function to make sure that is module is - not empty. Sokme compilers barf on that. */ + not empty. Some compilers barf on empty modules. */ const char * _assuan_sysutils_blurb (void) { @@ -141,74 +133,3 @@ _assuan_getenv (const char *name) } #endif /*HAVE_W32CE_SYSTEM*/ - -#ifdef HAVE_W32_SYSTEM -/* WindowsCE does not provide a pipe feature. However we need - something like a pipe to convey data between processes and in some - cases within a process. This replacement is not only used by - libassuan but exported and thus usable by gnupg and gpgme as well. */ -DWORD -_assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, - LPSECURITY_ATTRIBUTES sec_attr, DWORD size) -{ -#ifdef HAVE_W32CE_SYSTEM - HANDLE hd[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - - *read_hd = *write_hd = INVALID_HANDLE_VALUE; - - ActivateDevice (L"Drivers\\GnuPG_Device", 0); - - /* Note: Using "\\$device\\GPG1" should be identical to "GPG1:". - However this returns an invalid parameter error without having - called GPG_Init in the driver. The docs mention something about - RegisterAFXEx but that API is not documented. */ - hd[0] = CreateFile (L"GPG1:", GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hd[0] == INVALID_HANDLE_VALUE) - return 0; - - if (!DeviceIoControl (hd[0], GPGCEDEV_IOCTL_SET_HANDLE, - &hd[0], sizeof hd[0], NULL, 0, NULL, NULL)) - fprintf (stderr, "GPGCEDEV_IOCTL_SET_HANDLE(0) failed: %d\n", - (int)GetLastError ()); - - hd[1] = CreateFile (L"GPG1:", GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL); - if (hd[1] == INVALID_HANDLE_VALUE) - { - DWORD lasterr = GetLastError (); - CloseHandle (hd[0]); - SetLastError (lasterr); - return 0; - } - if (!DeviceIoControl (hd[1], GPGCEDEV_IOCTL_SET_HANDLE, - &hd[1], sizeof hd[1], NULL, 0, NULL, NULL)) - fprintf (stderr, "GPGCEDEV_IOCTL_SET_HANDLE(1) failed: %d\n", - (int)GetLastError ()); - - if (!DeviceIoControl (hd[0], GPGCEDEV_IOCTL_MAKE_PIPE, - &hd[1], sizeof hd[1], NULL, 0, NULL, NULL)) - { - fprintf (stderr, "GPGCEDEV_IOCTL_MAKE_PIPE failed: %d\n", - (int)GetLastError ()); - if (hd[0] != INVALID_HANDLE_VALUE) - CloseHandle (hd[0]); - if (hd[1] != INVALID_HANDLE_VALUE) - CloseHandle (hd[1]); - return 0; - } - else - { - *read_hd = hd[0]; - *write_hd = hd[1]; - return 1; - } -#else /*!HAVE_W32CE_SYSTEM*/ - return CreatePipe (read_hd, write_hd, sec_attr, size); -#endif /*!HAVE_W32CE_SYSTEM*/ -} - -#endif /*!HAVE_W32_SYSTEM*/ - diff --git a/tests/ChangeLog b/tests/ChangeLog new file mode 100644 index 0000000..c263c65 --- /dev/null +++ b/tests/ChangeLog @@ -0,0 +1,67 @@ +2010-03-17 Werner Koch <[email protected]> + + * pipeconnect.c: New. Based on fdpassing.c + +2010-02-24 Werner Koch <[email protected]> + + * ce-server.c: New. + + * ce-createpipe.c [W32CE]: New. + +2010-01-27 Werner Koch <[email protected]> + + * common.h (SOCKET2HANDLE, HANDLE2SOCKET): New. + +2009-11-05 Marcus Brinkmann <[email protected]> + + * fdpassing.c (main): Call assuan_pipe_connect instead + of assuan_pipe_connect_ext. + +2009-11-04 Werner Koch <[email protected]> + + * fdpassing.c (register_commands): Add NULL arg to + assuan_register_command. + +2009-09-19 Marcus Brinkmann <[email protected]> + + * fdpassing.c: Update to new API. + +2009-08-26 Marcus Brinkmann <[email protected]> + + * Makefile.am (AM_CFLAGS, LDADD): Add gpg-error. + * fdpassing.c: Change error values to gpg-error ones. + +2008-11-03 Marcus Brinkmann <[email protected]> + + * fdpassing.c (register_commands): Add missing initializer + to silence gcc -W warning. + +2006-10-10 Werner Koch <[email protected]> + + * Makefile.am (LDADD): Add NETLIBS. + +2006-09-19 Werner Koch <[email protected]> + + * fdpassing.c: Reverted Marcus changes. + (client): New arg FNAME to replace hardwired file name. + (main): Pass motd to client. + * Makefile.am (AM_CPPFLAGS): Removed. + (EXTRA_DIST): Add motd. + +2006-09-19 Marcus Brinkmann <[email protected]> + + * fdpassing.c (MOTD): New macro. + * Makefile.am (AM_CPPFLAGS): New variable. + * motd: New file. + + + Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + diff --git a/tests/Makefile.am b/tests/Makefile.am index 692748c..ef68924 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,7 +25,7 @@ EXTRA_DIST = motd ce-createpipe.c BUILT_SOURCES = CLEANFILES = -TESTS = +TESTS = pipeconnect if HAVE_W32CE_SYSTEM w32cetools = ce-createpipe diff --git a/tests/ce-createpipe.c b/tests/ce-createpipe.c index 810edbc..b44784c 100644 --- a/tests/ce-createpipe.c +++ b/tests/ce-createpipe.c @@ -49,7 +49,7 @@ reader_thread (void *arg) log_error ("reader: ReadFile failed: rc=%d\n", (int)GetLastError ()); break; } - log_info ("reader: red %d bytes\n", (int)nread); + log_info ("reader: read %d bytes\n", (int)nread); log_printhex ("got: ", buffer, nread); } @@ -117,7 +117,7 @@ run_test (void) switch (WaitForMultipleObjects (2, threads, FALSE, INFINITE)) { case WAIT_OBJECT_0: - log_info ("reader thread finished firstt\n"); + log_info ("reader thread finished first\n"); break; case WAIT_OBJECT_0 + 1: log_info ("writer thread finished first\n"); diff --git a/tests/ce-server.c b/tests/ce-server.c index 6ff3cae..9975e53 100644 --- a/tests/ce-server.c +++ b/tests/ce-server.c @@ -898,6 +898,8 @@ cmd_run (assuan_context_t ctx, char *line) #endif /*HAVE_W32CE_SYSTEM*/ + + static const char hlp_newdataport[] = "NEWDATAPORT\n" diff --git a/tests/pipeconnect.c b/tests/pipeconnect.c new file mode 100644 index 0000000..ddb4a06 --- /dev/null +++ b/tests/pipeconnect.c @@ -0,0 +1,397 @@ +/* pipeconnect.c - Check the assuan_pipe_connect call. + Copyright (C) 2006, 2009, 2010 Free Software Foundation, Inc. + + This file is part of Assuan. + + Assuan 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 3 of + the License, or (at your option) any later version. + + Assuan 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, see <http://www.gnu.org/licenses/>. + */ + +/* + This tests creates a program which starts an assuan server and runs + some simple tests on it. The other program is actually the same + program but called with the option --server. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include "../src/assuan.h" +#include "common.h" + +static assuan_fd_t my_stdin = ASSUAN_INVALID_FD; +static assuan_fd_t my_stdout = ASSUAN_INVALID_FD; +static assuan_fd_t my_stderr = ASSUAN_INVALID_FD; + + +static gpg_error_t +cmd_echo (assuan_context_t ctx, char *line) +{ + log_info ("got ECHO command (%s)\n", line); + + assuan_send_data (ctx, line, strlen (line)); + + return 0; +} + + +static gpg_error_t +cmd_cat (assuan_context_t ctx, char *line) +{ + assuan_fd_t fd, fdout; + int c; + FILE *fp, *fpout; + int nbytes; + + log_info ("got CAT command (%s)\n", line); + + fd = assuan_get_input_fd (ctx); + if (fd == ASSUAN_INVALID_FD) + return gpg_error (GPG_ERR_ASS_NO_INPUT); + fdout = assuan_get_output_fd (ctx); + if (fdout == ASSUAN_INVALID_FD) + return gpg_error (GPG_ERR_ASS_NO_OUTPUT); + fp = fdopen (fd, "r"); + if (!fp) + { + log_error ("fdopen failed on input fd: %s\n", strerror (errno)); + return gpg_error (GPG_ERR_ASS_GENERAL); + } + + fpout = fdopen (fdout, "w"); + if (!fpout) + { + log_error ("fdopen failed on output fd: %s\n", strerror (errno)); + fclose (fp); + return gpg_error (GPG_ERR_ASS_GENERAL); + } + + nbytes = 0; + while ( (c=getc (fp)) != -1) + { + putc (c, fpout); + nbytes++; + } + log_info ("done printing %d bytes to output fd\n", nbytes); + + /* Fixme: This also closes the original fd. */ + fclose (fp); + fclose (fpout); + return 0; +} + + +static gpg_error_t +server_register_commands (assuan_context_t ctx) +{ + static struct + { + const char *name; + gpg_error_t (*handler) (assuan_context_t, char *line); + } table[] = + { + { "ECHO", cmd_echo }, + { "CAT", cmd_cat }, + { "INPUT", NULL }, + { "OUTPUT", NULL }, + { NULL, NULL } + }; + int i; + gpg_error_t rc; + + for (i=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL); + if (rc) + return rc; + } + return 0; +} + + +static void +run_server (int enable_debug) +{ + int rc; + assuan_context_t ctx; + assuan_fd_t filedes[2]; + + filedes[0] = my_stdin; + filedes[1] = my_stdout; + + rc = assuan_new (&ctx); + if (rc) + log_fatal ("assuan_new failed: %s\n", gpg_strerror (rc)); + + rc = assuan_init_pipe_server (ctx, filedes); + if (rc) + log_fatal ("assuan_init_pipe_server failed: %s\n", + gpg_strerror (rc)); + + rc = server_register_commands (ctx); + if (rc) + log_fatal ("register_commands failed: %s\n", gpg_strerror(rc)); + + if (enable_debug) + assuan_set_log_stream (ctx, stderr); + + for (;;) + { + rc = assuan_accept (ctx); + if (rc) + { + if (rc != -1) + log_error ("assuan_accept failed: %s\n", gpg_strerror (rc)); + break; + } + + log_info ("client connected. Client's pid is %ld\n", + (long)assuan_get_pid (ctx)); + + rc = assuan_process (ctx); + if (rc) + log_error ("assuan_process failed: %s\n", gpg_strerror (rc)); + } + + assuan_release (ctx); +} + + + + + + + +static gpg_error_t +data_cb (void *opaque, const void *buffer, size_t length) +{ + (void)opaque; + + if (buffer) + printf ("Received data `%.*s'\n", (int)length, (char*)buffer); + return 0; +} + + +static void +run_client (const char *servername) +{ + gpg_error_t err; + assuan_context_t ctx; + assuan_fd_t no_close_fds[2]; + const char *arglist[5]; + + no_close_fds[0] = fileno (stderr); + no_close_fds[1] = ASSUAN_INVALID_FD; + + arglist[0] = servername; + arglist[1] = "--server"; + arglist[2] = debug? "--debug" : verbose? "--verbose":NULL; + arglist[3] = NULL; + + err = assuan_new (&ctx); + if (err) + log_fatal ("assuan_new failed: %s\n", gpg_strerror (err)); + + err = assuan_pipe_connect (ctx, servername, arglist, no_close_fds, + NULL, NULL, 0); + if (err) + { + log_error ("assuan_pipe_connect failed: %s\n", + gpg_strerror (err)); + assuan_release (ctx); + return; + } + + log_info ("server started; pid is %ld\n", + (long)assuan_get_pid (ctx)); + + err = assuan_transact (ctx, "ECHO Your lucky number is 3552664958674928. " + "Watch for it everywhere.", + data_cb, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("sending ECHO failed: %s\n", gpg_strerror (err)); + return; + } + + err = assuan_transact (ctx, "BYE", NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("sending BYE failed: %s\n", gpg_strerror (err)); + return; + } + + assuan_release (ctx); + return; +} + + +static void +parse_std_file_handles (int *argcp, char ***argvp) +{ +#ifdef HAVE_W32CE_SYSTEM + int argc = *argcp; + char **argv = *argvp; + const char *s; + assuan_fd_t fd; + int i; + int fixup = 0; + + if (!argc) + return; + + for (argc--, argv++; argc; argc--, argv++) + { + s = *argv; + if (*s == '-' && s[1] == '&' && s[2] == 'S' + && (s[3] == '0' || s[3] == '1' || s[3] == '2') + && s[4] == '=' + && (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null"))) + { + if (s[5] == 'n') + fd = ASSUAN_INVALID_FD; + else + fd = _assuan_w32ce_finish_pipe (atoi (s+5), s[3] != '0'); + switch (s[3] - '0') + { + case 0: my_stdin = fd; break; + case 1: my_stdout = fd; break; + case 2: my_stderr = fd; break; + } + + fixup++; + } + else + break; + } + + if (fixup) + { + argc = *argcp; + argc -= fixup; + *argcp = argc; + + argv = *argvp; + for (i=1; i < argc; i++) + argv[i] = argv[i + fixup]; + for (; i < argc + fixup; i++) + argv[i] = NULL; + } +#else + (void)argcp; + (void)argvp; + my_stdin = 0; + my_stdout = 1; + my_stderr = 2; +#endif +} + + +/* + M A I N + */ +int +main (int argc, char **argv) +{ + gpg_error_t err; + const char *myname = "no-pgm"; + int last_argc = -1; + int server = 0; + int silent_client = 0; + int silent_server = 0; + + parse_std_file_handles (&argc, &argv); + if (argc) + { + myname = *argv; + log_set_prefix (*argv); + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--help")) + { + printf ("usage: %s [options]\n" + "\n" + "Options:\n" + " --verbose Show what is going on\n" + " --server Run in server mode\n", + log_get_prefix ()); + exit (0); + } + if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose = debug = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--server")) + { + server = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--silent-server")) + { + silent_server = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--silent-client")) + { + silent_client = 1; + argc--; argv++; + } + else + log_fatal ("invalid option `%s' (try --help)\n", *argv); + } + + log_set_prefix (xstrconcat (log_get_prefix (), + server? ".server":".client", NULL)); + assuan_set_assuan_log_prefix (log_get_prefix ()); + + err = assuan_sock_init (); + if (err) + log_fatal ("socket init failed: %s\n", gpg_strerror (err)); + + if (server) + { + log_info ("server started\n"); + if (debug && !silent_server) + assuan_set_assuan_log_stream (stderr); + run_server (debug && !silent_server); + log_info ("server finished\n"); + } + else + { + log_info ("client started\n"); + if (debug && !silent_client) + assuan_set_assuan_log_stream (stderr); + run_client (myname); + log_info ("client finished\n"); + } + + return errorcount ? 1 : 0; +} + |