diff --git a/ChangeLog b/ChangeLog index 803cb28..c26fbf7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,401 +1,406 @@ +2011-03-28 Werner Koch + + * configure.ac (AC_CHECK_HEADERS): Check for sys/select.h which is + needed by one test utility. + 2010-12-23 Werner Koch * configure.ac: Add a git commit identifier to the version. 2010-11-01 Marcus Brinkmann * configure.ac: Check for fcntl.h. 2010-11-01 Werner Koch * configure.ac: Check for sys/types.h, sys/stat.h, sys/time and unistd.h 2010-08-19 Werner Koch * configure.ac (AH_TOP, AH_BOTTOM): New. Define GPG_ERR_ENABLE_ERRNO_MACROS. 2010-08-09 Werner Koch Release 2.0.1 * configure.ac: Set LT version to C1/A1/R0. 2010-04-20 Werner Koch * configure.ac: Change wording of the no-funopen warning. 2010-03-22 Werner Koch * configure.ac (CC_FOR_BUILD): Add test. 2010-03-17 Werner Koch * tests/ChangeLog: New. Move all relevant entries to there. 2010-02-25 Werner Koch * m4/libtool.m4 (_LT_CHECK_MAGIC_METHOD): Fix for non x86 mingw targets. 2010-02-11 Werner Koch * configure.ac (inet_pton): Check for it. 2010-02-04 Werner Koch * configure.ac (AC_TYPE_UINT16_T): New. 2010-01-26 Werner Koch * configure.ac (NETLIBS) [W32CE]: Use -lws2. 2010-01-22 Werner Koch * configure.ac: Require libgpg-error 1.8. (HAVE_W32CE_SYSTEM): New am_defines and am_conditionals. * ltmain.sh (wrappers_required): Don't set for mingw32ce. * autogen.sh: Add option --build-w32ce. Remove --disable-shared from --build-w32. 2010-01-08 Marcus Brinkmann Released 2.0.0. 2010-01-05 Marcus Brinkmann * configure.ac (_DARWIN_C_SOURCE): Define on frapple. 2009-12-22 Marcus Brinkmann * configure.ac: Do not use echo -n. 2009-12-15 Marcus Brinkmann * configure.ac: Bump version to 2.0.0. 2009-10-16 Marcus Brinkmann * autogen.sh: Remove --with-pth-prefix from configure invocation. * configure.ac (_ASSUAN_IN_LIBASSUAN, PTH_SYSCALL_SOFT): Do not set anymore. (GNUPG_PATH_PTH): Don't invoke. (HAVE_PTH): Remove conditional. (LIBASSUAN_CONFIG_THREAD_MODULES): Removed. 2009-10-08 Marcus Brinkmann * configure.ac: AC_REPLACE_FUNCS for vasprintf. 2009-09-19 Marcus Brinkmann * configure.ac: Check for stdint.h and inttypes.h. Invoke AC_TYPE_UINTPTR_T. 2009-09-08 Marcus Brinkmann * m4/gpg-error.m4: New file. 2009-09-01 Marcus Brinkmann * configure.ac: Set BUILD_REVISION and update calculation of data for build info. Update libtool macros. Set NETLIBS for W32 targets. 2009-08-26 Marcus Brinkmann * configure.ac: Test for versioned symbols support. (LIBASSUAN_LT_CURRENT, LIBASSUAN_LT_AGE) (LIBASSUAN_LT_REVISION): New, set to 0. (LIBASSUAN_CONFIG_API_VERSION): Bump to 2. (AC_CONFIG_MACRO_DIR, AC_DISABLE_STATIC, AC_LIBTOOL_WIN32_DLL) (AC_LIBTOOL_RC, AC_PROG_LIBTOOL, AM_PATH_GPG_ERROR): Invoke. (AC_PROG_RANLIB): Don't invoke. (HAVE_W32_SYSTEM): New AM conditional. (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. 2009-01-22 Werner Koch * configure.ac: Check for nanoleep only in libc. 2008-05-25 Werner Koch Released 1.0.5. 2008-05-23 Werner Koch * configure.ac: Use -fPIC with GCC under Linux. 2007-12-12 Werner Koch Released 1.0.4. * config.sub, config.guess: Update to version 2007-11-19. 2007-08-24 Werner Koch Released 1.0.3. Switched license of the library code back to LGPLv2.1. See NEWS. * COPYING.LIB: Replaced by LPGLv2.1 2007-07-05 Werner Koch Released 1.0.2. Relicensed to LGPLv3. * COPYING: Replaced by GPLv3. * COPYING.LESSER: Removed. * COPYING.LIB: New. * Makefile.am (ACLOCAL_AMFLAGS): Do not create gzipped tarball. 2007-07-03 Werner Koch * configure.ac (NETLIBS): Use ws2_32 instead of wsock32. 2007-06-15 Werner Koch * autogen.sh: Use = and not == in test to be POSIXly correct. Change shell back to /bin/sh. 2007-06-15 Marcus Brinkmann * autogen.sh: Require bash. 2007-05-30 Werner Koch * autogen.sh <--build-w32>: Modernize. 2007-05-29 Werner Koch * 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. 2007-05-24 Werner Koch * configure.ac: Use -Wpointer-arith is possible. 2006-11-22 Werner Koch Released 1.0.1. 2006-11-21 Werner Koch * configure.ac (AH_BOTTOM): Define PTH_SYSCALL_SOFT to 0. (AC_INIT): Use the SVN magic. * m4/gnupg-pth.m4 (GNUPG_PTH_VERSION_CHECK): Use --all with pth-config. 2006-11-15 Werner Koch * autogen.sh: Add convenience option --build-amd64. 2006-10-31 Werner Koch Released 1.0.0. 2006-10-20 Werner Koch * Makefile.am (stowinstall): New convenience target. 2006-10-10 Werner Koch Released 0.9.3. * configure.ac: Check for cmsghdr. (USE_DESCRIPTOR_PASSING): Define it then. 2006-10-09 Werner Koch * m4/gnupg-pth.m4: New. Taked from GnuPG. 2006-10-04 Werner Koch Released 0.9.2. 2006-10-04 Werner Koch Released 0.9.1. * configure.ac (AB_INIT): New. * m4/autobuild.m4: New. 2006-09-14 Werner Koch Released 0.9.0. * configure.ac: Check for S_PEERCRED. Include check for socklen_t. * m4/sys_socket_h.m4, m4/onceonly.m4, m4/socklen.m4: New. * m4/Makefile.am: New. 2006-09-05 Werner Koch * configure.ac (AH_BOTTOM): Define _ASSUAN_IN_LIBASSUAN. 2005-10-24 Werner Koch * COPYING.LESSER: Added. * README.CVS: Renamed to .. * README.SVN: .. this. 2005-10-08 Marcus Brinkmann * configure.ac: Check for socket library and add it to LIBASSUAN_CONFIG_LIBS if necessary. 2005-10-07 Marcus Brinkmann * configure.ac: Invoke AC_CANONICAL_HOST. Define _XOPEN_SOURCE, _XOPEN_SOURCE_EXTENDED and __EXTENSIONS__ on Solaris. Add stpcy as replacement function. Add setenv as replacement function (and check for unistd.h). 2005-06-20 Werner Koch Released 0.6.10. 2004-12-22 Werner Koch Released 0.6.9. For security reasons switched to automake 1.9. 2004-12-18 Werner Koch * autogen.sh: Add Option --build-w32. 2004-12-07 Werner Koch * configure.ac: Define HAVE_W32_SYSTEM and HAVE_DOSISH_SYSTEM. Add -lwsock2 to the config lib flags for W32. 2004-11-25 Werner Koch Released 0.6.8. 2004-09-27 Werner Koch * config.sub, config.guess: Updated. 2004-06-23 Marcus Brinkmann * configure.ac: Check for . 2004-06-08 Werner Koch Released 0.6.6. 2004-04-02 Thomas Schwinge * autogen.sh: Added ACLOCAL_FLAGS. 2004-02-20 Werner Koch Released 0.6.4. 2004-02-11 Werner Koch * autogen.sh (check_version): Removed bashism and simplified. 2004-01-29 Werner Koch Released 0.6.3. 2003-12-18 Werner Koch Released 0.6.2. 2003-12-08 Werner Koch * TODO: New. * Makefile.am: Add README.CVS and autogen.sh. Removed m4/Makefile. * README.CVS: New. * autogen.sh: Revamped. * configure.ac: Add automake version number for autogen.sh use. 2003-11-17 Werner Koch Released 0.6.1. 2003-08-06 Werner Koch Released 0.6.0. 2003-07-29 Werner Koch * configure.ac: Cleanups for newer autoconf. 2003-07-29 gettextize * Makefile.am (EXTRA_DIST): Add config.rpath. * configure.ac (AC_CONFIG_FILES): Add po/Makefile.in, 2003-04-28 gettextize * Makefile.am (SUBDIRS): Add m4. (ACLOCAL_AMFLAGS): New variable. (EXTRA_DIST): New variable. * configure.ac (AC_CONFIG_FILES): Add po/Makefile.in, 2003-02-18 Neal H. Walfield * COPYING: New file. 2003-02-18 Neal H. Walfield * configure.ac: Fix typo. (AC_CONFIG_FILES): Remove common/Makefile.am. * common: Remove directory. 2003-02-18 Neal H. Walfield * common: New directory. * Makefile.am (SUBDIRS): Add common. * configure.ac: Check for funopen. If not present, check for fopencookie and implement it in terms of that. Otherwise, fail. (AC_CONFIG_FILES): Add common/Makefile. 2003-02-18 Neal H. Walfield * configure.ac (AC_CONFIG_FILES): Add src/libassuan-config. (LIBASSUAN_CONFIG_LIBS, LIBASSUAN_CONFIG_CFLAGS): New variables. AC_SUBST them. 2003-02-17 Neal H. Walfield * AUTHORS: New file. * INSTALL: New file. * Makefile.am: New file. * NEWS: New file. * README: New file. * autogen.sh: New file, copied from newpg. * configure.ac: New file, imported from newpg. * depcomp: New file. * install-sh: New file. * missing: New file. * mkinstalldirs: New file. * doc: New directory. * src: New directory. * tests: New directory. 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 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/configure.ac b/configure.ac index b7de45b..d29c82e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,370 +1,371 @@ # configure.ac - for libassuan # Copyright (C) 2001-2003, 2006, 2007, 2009 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 2.1 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 . # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) min_automake_version="1.10" # Remember to change the version number immediately *after* a release. # Set my_issvn to "yes" for non-released code. Remember to run an # "svn up" and "autogen.sh" right before creating a distribution. m4_define([my_version], [2.0.2]) m4_define([my_issvn], [yes]) m4_define([svn_revision], m4_esyscmd([printf "%d" $( (svn info 2>/dev/null \ || echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q;}')])) m4_define([git_revision], m4_esyscmd([git branch -v 2>/dev/null \ | awk '/^\* / {printf "%s",$3}'])) AC_INIT([libassuan], [my_version[]m4_if(my_issvn,[yes], [m4_if(git_revision,[],[-svn[]svn_revision],[-git[]git_revision])])], [bug-libassuan@gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed/changed: AGE=0) # LIBASSUAN_LT_CURRENT=1 LIBASSUAN_LT_AGE=1 LIBASSUAN_LT_REVISION=0 # If the API is changed in an incompatible way: increment the next counter. LIBASSUAN_CONFIG_API_VERSION=2 ############################################## AC_SUBST(LIBASSUAN_LT_CURRENT) AC_SUBST(LIBASSUAN_LT_AGE) AC_SUBST(LIBASSUAN_LT_REVISION) BUILD_REVISION=m4_if(git_revision,[],[svn_revision],[git_revision]) PACKAGE=$PACKAGE_NAME VERSION=$PACKAGE_VERSION AM_INIT_AUTOMAKE AM_MAINTAINER_MODE AC_CONFIG_SRCDIR(src/assuan.h.in) AC_CONFIG_MACRO_DIR(m4) AM_CONFIG_HEADER(config.h) AC_CANONICAL_HOST AB_INIT AC_GNU_SOURCE AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package]) AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT", [Bug report address]) # Don't default to build static libs. LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # For now we hardcode the use of version scripts. It would be better # to write a test for this or even implement this within libtool. have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; *-apple-darwin*) AC_DEFINE(_XOPEN_SOURCE, 500, Activate POSIX interface on MacOS X) AC_DEFINE(_DARWIN_C_SOURCE, 1, Activate CMSG_LEN/CMSG_SPACE on MacOS X) ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") AH_TOP([ #ifndef _ASSUAN_CONFIG_H_INCLUDED #define _ASSUAN_CONFIG_H_INCLUDED /* Enable gpg-error's strerror macro under W32CE. */ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 ]) AH_BOTTOM([ #endif /*_ASSUAN_CONFIG_H_INCLUDED*/ ]) # Checks for programs. missing_dir=`cd $ac_aux_dir && pwd` AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) AC_PROG_AWK AC_PROG_CC AC_PROG_CPP AM_PROG_CC_C_O if test "x$ac_cv_prog_cc_c89" = "xno" ; then AC_MSG_ERROR([[No C-89 compiler found]]) fi AC_PROG_INSTALL 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" AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]),_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi fi -# +# # Options depending on the host OS. -# +# have_dosish_system=no have_w32_system=no have_w32ce_system=no case "${host}" in *-linux*) if test "$GCC" = yes; then CFLAGS="$CFLAGS -fPIC -DPIC" fi ;; *-mingw32ce*) have_dosish_system=yes have_w32_system=yes have_w32ce_system=yes ;; *-mingw32*) have_dosish_system=yes have_w32_system=yes ;; *-solaris*) AC_DEFINE(_XOPEN_SOURCE, 500, Activate extensions on Solaris) AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Activate extensions on Solaris) AC_DEFINE(__EXTENSIONS__, 1, Activate extensions on Solaris) ;; esac if test "$have_dosish_system" = yes; then AC_DEFINE(HAVE_DOSISH_SYSTEM,1, - [Defined if we run on some of the PCDOS like systems + [Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) with special properties like no file modes]) fi dnl AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes) if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1,[Defined if we run on a W32 API based system]) if test "$have_w32ce_system" = yes; then AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE]) fi BUILD_TIMESTAMP=`date --iso-8601=minutes` AC_SUBST(BUILD_TIMESTAMP) - changequote(,)dnl + changequote(,)dnl BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` changequote([,])dnl case "$VERSION" in *-svn*) BUILD_FILEVERSION="${BUILD_FILEVERSION}0" ;; *-cvs) BUILD_FILEVERSION="${BUILD_FILEVERSION}0" ;; *-rc*) BUILD_FILEVERSION="${BUILD_FILEVERSION}1" ;; *) BUILD_FILEVERSION="${BUILD_FILEVERSION}2" ;; esac fi AC_SUBST(BUILD_REVISION) AC_SUBST(BUILD_TIMESTAMP) AC_SUBST(BUILD_FILEVERSION) AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) # Check for network libraries. They are needed for tests. AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt, [NETLIBS="-lsocket $NETLIBS"])) AC_SUBST(NETLIBS) if test "$have_w32_system" = yes; then if test "$have_w32ce_system" = yes; then NETLIBS="-lws2 $NETLIBS" else # FIXME: Check why we need to use ws2_32 and document that. NETLIBS="-lws2_32 $NETLIBS" fi fi # For src/libassuan-config.in LIBASSUAN_CONFIG_LIB="-lassuan" LIBASSUAN_CONFIG_CFLAGS="" LIBASSUAN_CONFIG_EXTRA_LIBS= if test x"$NETLIBS" != x; then LIBASSUAN_CONFIG_EXTRA_LIBS="$LIBASSUAN_CONFIG_EXTRA_LIBS $NETLIBS" fi AC_SUBST(LIBASSUAN_CONFIG_LIB) AC_SUBST(LIBASSUAN_CONFIG_CFLAGS) AC_SUBST(LIBASSUAN_CONFIG_API_VERSION) AC_SUBST(LIBASSUAN_CONFIG_EXTRA_LIBS) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([string.h locale.h sys/uio.h stdint.h inttypes.h \ - sys/types.h sys/stat.h unistd.h sys/time.h fcntl.h]) + sys/types.h sys/stat.h unistd.h sys/time.h fcntl.h \ + sys/select.h ]) AC_TYPE_UINTPTR_T AC_TYPE_UINT16_T # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T AC_TYPE_SIGNAL AC_DECL_SYS_SIGLIST gl_HEADER_SYS_SOCKET gl_TYPE_SOCKLEN_T AC_CHECK_MEMBER(struct cmsghdr.cmsg_len, [use_descriptor_passing=yes], [use_descriptor_passing=no AC_MSG_WARN([ *** *** Data structure for sending ancillary data missing. *** Descriptor passing won't work. ***])],[ #include #include #include #include #include #include #if HAVE_SYS_UIO_H #include #endif #include ]) if test "$use_descriptor_passing" = "yes"; then AC_DEFINE(USE_DESCRIPTOR_PASSING, 1, [Defined if descriptor passing is supported]) fi AM_CONDITIONAL(USE_DESCRIPTOR_PASSING, test "$use_descriptor_passing" = "yes") # Checking for libgpg-error. AM_PATH_GPG_ERROR(1.8,, AC_MSG_ERROR([libgpg-error was not found])) # # Checks for library functions. # AC_CHECK_FUNCS([flockfile funlockfile inet_pton]) # On some systems (e.g. Solaris) nanosleep requires linking to librl. # Given that we use nanosleep only as an optimization over a select # based wait function we want it only if it is available in libc. _save_libs="$LIBS" AC_SEARCH_LIBS([nanosleep], [], [AC_DEFINE(HAVE_NANOSLEEP,1, [Define to 1 if you have the `nanosleep' function in libc.])]) LIBS="$_save_libs" # Check for funopen AC_CHECK_FUNCS(funopen) if test $ac_cv_func_funopen != yes; then # No funopen but we can implement that in terms of fopencookie. AC_CHECK_FUNCS(fopencookie) if test $ac_cv_func_fopencookie = yes; then AC_LIBOBJ([funopen]) else AC_MSG_WARN([ *** *** No implementation of fopencookie or funopen available. *** The assuan_get_data_fp function won't work; see the *** manual for details. GnuPG does not require this feature. ***]) fi fi AC_REPLACE_FUNCS(isascii) AC_REPLACE_FUNCS(putc_unlocked) AC_REPLACE_FUNCS(memrchr) AC_REPLACE_FUNCS(stpcpy) AC_CHECK_HEADERS(unistd.h) AC_REPLACE_FUNCS(setenv) AC_REPLACE_FUNCS(vasprintf) # # Check for the getsockopt SO_PEERCRED # AC_MSG_CHECKING(for SO_PEERCRED) AC_CACHE_VAL(assuan_cv_sys_so_peercred, - [AC_TRY_COMPILE([#include ], - [struct ucred cr; + [AC_TRY_COMPILE([#include ], + [struct ucred cr; int cl = sizeof cr; getsockopt (1, SOL_SOCKET, SO_PEERCRED, &cr, &cl);], assuan_cv_sys_so_peercred=yes, assuan_cv_sys_so_peercred=no) ]) -AC_MSG_RESULT($assuan_cv_sys_so_peercred) +AC_MSG_RESULT($assuan_cv_sys_so_peercred) if test $assuan_cv_sys_so_peercred = yes; then AC_DEFINE(HAVE_SO_PEERCRED, 1, [Defined if SO_PEERCRED is supported (Linux specific)]) fi # Create the config files. AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([m4/Makefile]) AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([doc/Makefile]) AC_CONFIG_FILES([tests/Makefile]) AC_CONFIG_FILES([src/libassuan-config], [chmod +x src/libassuan-config]) AC_CONFIG_FILES([src/versioninfo.rc]) AC_OUTPUT diff --git a/src/assuan-handler.c b/src/assuan-handler.c index 692bdd0..bd57ca0 100644 --- a/src/assuan-handler.c +++ b/src/assuan-handler.c @@ -1,1008 +1,1009 @@ /* assuan-handler.c - dispatch commands - Copyright (C) 2001, 2002, 2003, 2007, 2009 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2007, 2009, + 2011 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 2.1 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 . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "assuan-defs.h" #include "debug.h" #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(a) ((a) >= '0' && (a) <= '9') static int my_strcasecmp (const char *a, const char *b); #define PROCESS_DONE(ctx, rc) \ ((ctx)->in_process_next ? assuan_process_done ((ctx), (rc)) : (rc)) static gpg_error_t dummy_handler (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASSUAN_SERVER_FAULT, "no handler registered")); } static gpg_error_t std_handler_nop (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, 0); /* okay */ } static gpg_error_t std_handler_cancel (assuan_context_t ctx, char *line) { if (ctx->cancel_notify_fnc) /* Return value ignored. */ ctx->cancel_notify_fnc (ctx, line); return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } static gpg_error_t std_handler_option (assuan_context_t ctx, char *line) { char *key, *value, *p; for (key=line; spacep (key); key++) ; if (!*key) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "argument required")); if (*key == '=') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "no option name given")); for (value=key; *value && !spacep (value) && *value != '='; value++) ; if (*value) { if (spacep (value)) *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (*value == '=') { *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (!*value) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option argument expected")); } if (*value) { for (p = value + strlen(value) - 1; p > value && spacep (p); p--) ; if (p > value) *++p = 0; /* strip trailing spaces */ } } if (*key == '-' && key[1] == '-' && key[2]) key += 2; /* the double dashes are optional */ if (*key == '-') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option should not begin with one dash")); if (ctx->option_handler_fnc) return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value)); return PROCESS_DONE (ctx, 0); } static gpg_error_t std_handler_bye (assuan_context_t ctx, char *line) { if (ctx->bye_notify_fnc) /* Return value ignored. */ ctx->bye_notify_fnc (ctx, line); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); /* pretty simple :-) */ ctx->process_complete = 1; return PROCESS_DONE (ctx, 0); } static gpg_error_t std_handler_auth (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } static gpg_error_t std_handler_reset (assuan_context_t ctx, char *line) { gpg_error_t err = 0; if (ctx->reset_notify_fnc) err = ctx->reset_notify_fnc (ctx, line); if (! err) { assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); _assuan_uds_close_fds (ctx); } return PROCESS_DONE (ctx, err); } static gpg_error_t std_handler_help (assuan_context_t ctx, char *line) { unsigned int i; char buf[ASSUAN_LINELENGTH]; const char *helpstr; size_t n; n = strcspn (line, " \t\n"); if (!n) { /* Print all commands. If a help string is available and that starts with the command name, print the first line of the help string. */ for (i = 0; i < ctx->cmdtbl_used; i++) { n = strlen (ctx->cmdtbl[i].name); helpstr = ctx->cmdtbl[i].helpstr; if (helpstr && !strncmp (ctx->cmdtbl[i].name, helpstr, n) && (!helpstr[n] || helpstr[n] == '\n' || helpstr[n] == ' ') && (n = strcspn (helpstr, "\n")) ) snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr); else snprintf (buf, sizeof (buf), "# %s", ctx->cmdtbl[i].name); buf[ASSUAN_LINELENGTH - 1] = '\0'; assuan_write_line (ctx, buf); } } else { /* Print the help for the given command. */ int c = line[n]; line[n] = 0; for (i=0; ctx->cmdtbl[i].name; i++) if (!my_strcasecmp (line, ctx->cmdtbl[i].name)) break; line[n] = c; if (!ctx->cmdtbl[i].name) return PROCESS_DONE (ctx, set_error (ctx,GPG_ERR_UNKNOWN_COMMAND,NULL)); helpstr = ctx->cmdtbl[i].helpstr; if (!helpstr) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_FOUND, NULL)); do { n = strcspn (helpstr, "\n"); snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr); helpstr += n; if (*helpstr == '\n') helpstr++; buf[ASSUAN_LINELENGTH - 1] = '\0'; assuan_write_line (ctx, buf); } while (*helpstr); } return PROCESS_DONE (ctx, 0); } static gpg_error_t std_handler_end (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } gpg_error_t assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd) { char *endp; if ((strncmp (line, "FD", 2) && strncmp (line, "fd", 2)) || (line[2] != '=' && line[2] != '\0' && !spacep(&line[2]))) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "FD[=] expected"); line += 2; if (*line == '=') { line ++; if (!digitp (*line)) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required"); #ifdef HAVE_W32_SYSTEM /* Fixme: For a W32/64bit system we will need to change the cast and the conversion function. */ *rfd = (void*)strtoul (line, &endp, 10); #else *rfd = strtoul (line, &endp, 10); #endif /* Remove that argument so that a notify handler won't see it. */ memset (line, ' ', endp? (endp-line):strlen(line)); if (*rfd == ctx->inbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as inbound fd"); if (*rfd == ctx->outbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as outbound fd"); return 0; } else /* Our peer has sent the file descriptor. */ return assuan_receivefd (ctx, rfd); } /* Format is INPUT FD= */ static gpg_error_t std_handler_input (assuan_context_t ctx, char *line) { gpg_error_t rc; assuan_fd_t fd, oldfd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) return PROCESS_DONE (ctx, rc); #ifdef HAVE_W32CE_SYSTEM oldfd = fd; fd = _assuan_w32ce_finish_pipe ((int)fd, 0); if (fd == INVALID_HANDLE_VALUE) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_PARAMETER, "rvid conversion failed")); TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_input", ctx, "turned RVID 0x%x into handle 0x%x", oldfd, fd); #endif if (ctx->input_notify_fnc) { oldfd = ctx->input_fd; ctx->input_fd = fd; rc = ctx->input_notify_fnc (ctx, line); if (rc) ctx->input_fd = oldfd; } else if (!rc) ctx->input_fd = fd; return PROCESS_DONE (ctx, rc); } /* Format is OUTPUT FD= */ static gpg_error_t std_handler_output (assuan_context_t ctx, char *line) { gpg_error_t rc; assuan_fd_t fd, oldfd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) return PROCESS_DONE (ctx, rc); #ifdef HAVE_W32CE_SYSTEM oldfd = fd; fd = _assuan_w32ce_finish_pipe ((int)fd, 1); if (fd == INVALID_HANDLE_VALUE) return PROCESS_DONE (ctx, set_error (ctx, gpg_err_code_from_syserror (), "rvid conversion failed")); TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_output", ctx, "turned RVID 0x%x into handle 0x%x", oldfd, fd); #endif if (ctx->output_notify_fnc) { oldfd = ctx->output_fd; ctx->output_fd = fd; rc = ctx->output_notify_fnc (ctx, line); if (rc) ctx->output_fd = oldfd; } else if (!rc) ctx->output_fd = fd; return PROCESS_DONE (ctx, rc); } /* This is a table with the standard commands and handler for them. The table is used to initialize a new context and associate strings with default handlers */ static struct { const char *name; gpg_error_t (*handler)(assuan_context_t, char *line); int always; /* always initialize this command */ } std_cmd_table[] = { { "NOP", std_handler_nop, 1 }, { "CANCEL", std_handler_cancel, 1 }, { "OPTION", std_handler_option, 1 }, { "BYE", std_handler_bye, 1 }, { "AUTH", std_handler_auth, 1 }, { "RESET", std_handler_reset, 1 }, { "END", std_handler_end, 1 }, { "HELP", std_handler_help, 1 }, { "INPUT", std_handler_input, 0 }, { "OUTPUT", std_handler_output, 0 }, { NULL, NULL, 0 } }; /** * assuan_register_command: * @ctx: the server context * @cmd_name: A string with the command name * @handler: The handler function to be called or NULL to use a default * handler. * HELPSTRING * * Register a handler to be used for a given command. Note that * several default handlers are already regsitered with a new context. * This function however allows to override them. * * Return value: 0 on success or an error code **/ gpg_error_t assuan_register_command (assuan_context_t ctx, const char *cmd_name, assuan_handler_t handler, const char *help_string) { int i, cmd_index = -1; const char *s; if (cmd_name && !*cmd_name) cmd_name = NULL; if (!cmd_name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!handler) { /* find a default handler. */ for (i=0; (s=std_cmd_table[i].name) && strcmp (cmd_name, s); i++) ; if (!s) { /* Try again but case insensitive. */ for (i=0; (s=std_cmd_table[i].name) && my_strcasecmp (cmd_name, s); i++) ; } if (s) handler = std_cmd_table[i].handler; if (!handler) handler = dummy_handler; /* Last resort is the dummy handler. */ } if (!ctx->cmdtbl) { ctx->cmdtbl_size = 50; ctx->cmdtbl = _assuan_calloc (ctx, ctx->cmdtbl_size, sizeof *ctx->cmdtbl); if (!ctx->cmdtbl) return _assuan_error (ctx, gpg_err_code_from_syserror ()); ctx->cmdtbl_used = 0; } else if (ctx->cmdtbl_used >= ctx->cmdtbl_size) { struct cmdtbl_s *x; x = _assuan_realloc (ctx, ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x); if (!x) return _assuan_error (ctx, gpg_err_code_from_syserror ()); ctx->cmdtbl = x; ctx->cmdtbl_size += 50; } for (i=0; icmdtbl_used; i++) { if (!my_strcasecmp (cmd_name, ctx->cmdtbl[i].name)) { cmd_index = i; break; } } if (cmd_index == -1) cmd_index = ctx->cmdtbl_used++; ctx->cmdtbl[cmd_index].name = cmd_name; ctx->cmdtbl[cmd_index].handler = handler; ctx->cmdtbl[cmd_index].helpstr = help_string; return 0; } /* Return the name of the command currently processed by a handler. The string returned is valid until the next call to an assuan function on the same context. Returns NULL if no handler is executed or the command is not known. */ const char * assuan_get_command_name (assuan_context_t ctx) { return ctx? ctx->current_cmd_name : NULL; } gpg_error_t assuan_register_pre_cmd_notify (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char *cmd)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->pre_cmd_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_post_cmd_notify (assuan_context_t ctx, void (*fnc)(assuan_context_t, gpg_error_t)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->post_cmd_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_bye_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->bye_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_reset_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->reset_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_cancel_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->cancel_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_option_handler (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char*, const char*)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->option_handler_fnc = fnc; return 0; } gpg_error_t assuan_register_input_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->input_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_output_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->output_notify_fnc = fnc; return 0; } /* Helper to register the standards commands */ gpg_error_t _assuan_register_std_commands (assuan_context_t ctx) { gpg_error_t rc; int i; for (i = 0; std_cmd_table[i].name; i++) { if (std_cmd_table[i].always) { rc = assuan_register_command (ctx, std_cmd_table[i].name, NULL, NULL); if (rc) return rc; } } return 0; } /* Process the special data lines. The "D " has already been removed from the line. As all handlers this function may modify the line. */ static gpg_error_t handle_data_line (assuan_context_t ctx, char *line, int linelen) { return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL); } /* like ascii_strcasecmp but assume that B is already uppercase */ static int my_strcasecmp (const char *a, const char *b) { if (a == b) return 0; for (; *a && *b; a++, b++) { if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b) break; } return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b); } /* Parse the line, break out the command, find it in the command table, remove leading and white spaces from the arguments, call the handler with the argument line and return the error. */ static gpg_error_t dispatch_command (assuan_context_t ctx, char *line, int linelen) { gpg_error_t err; char *p; const char *s; int shift, i; /* Note that as this function is invoked by assuan_process_next as well, we need to hide non-critical errors with PROCESS_DONE. */ if (*line == 'D' && line[1] == ' ') /* divert to special handler */ /* FIXME: Depending on the final implementation of handle_data_line, this may be wrong here. For example, if a user callback is invoked, and that callback is responsible for calling assuan_process_done, then this is wrong. */ return PROCESS_DONE (ctx, handle_data_line (ctx, line+2, linelen-2)); for (p=line; *p && *p != ' ' && *p != '\t'; p++) ; if (p==line) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "leading white-space")); if (*p) { /* Skip over leading WS after the keyword */ *p++ = 0; while ( *p == ' ' || *p == '\t') p++; } shift = p - line; for (i=0; (s=ctx->cmdtbl[i].name); i++) { if (!strcmp (line, s)) break; } if (!s) { /* and try case insensitive */ for (i=0; (s=ctx->cmdtbl[i].name); i++) { if (!my_strcasecmp (line, s)) break; } } if (!s) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_UNKNOWN_CMD, NULL)); line += shift; linelen -= shift; if (ctx->pre_cmd_notify_fnc) { err = ctx->pre_cmd_notify_fnc(ctx, ctx->cmdtbl[i].name); if (err) return PROCESS_DONE(ctx, err); } /* fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */ ctx->current_cmd_name = ctx->cmdtbl[i].name; err = ctx->cmdtbl[i].handler (ctx, line); ctx->current_cmd_name = NULL; return err; } /* Call this to acknowledge the current command. */ gpg_error_t assuan_process_done (assuan_context_t ctx, gpg_error_t rc) { if (!ctx->in_command) return _assuan_error (ctx, GPG_ERR_ASS_GENERAL); ctx->in_command = 0; /* Check for data write errors. */ if (ctx->outbound.data.fp) { /* Flush the data lines. */ fclose (ctx->outbound.data.fp); ctx->outbound.data.fp = NULL; if (!rc && ctx->outbound.data.error) rc = ctx->outbound.data.error; } else { /* Flush any data send without using the data FP. */ assuan_send_data (ctx, NULL, 0); if (!rc && ctx->outbound.data.error) rc = ctx->outbound.data.error; } /* Error handling. */ if (!rc) { if (ctx->process_complete) { /* No error checking because the peer may have already disconnect. */ assuan_write_line (ctx, "OK closing connection"); ctx->finish_handler (ctx); } else rc = assuan_write_line (ctx, ctx->okay_line ? ctx->okay_line : "OK"); } else { char errline[300]; const char *text = ctx->err_no == rc ? ctx->err_str : NULL; char ebuf[50]; gpg_strerror_r (rc, ebuf, sizeof (ebuf)); sprintf (errline, "ERR %d %.50s <%.30s>%s%.100s", rc, ebuf, gpg_strsource (rc), text? " - ":"", text?text:""); rc = assuan_write_line (ctx, errline); } if (ctx->post_cmd_notify_fnc) ctx->post_cmd_notify_fnc (ctx, rc); ctx->flags.confidential = 0; if (ctx->okay_line) { _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; } return rc; } static gpg_error_t process_next (assuan_context_t ctx) { gpg_error_t rc; /* What the next thing to do is depends on the current state. However, we will always first read the next line. The client is required to write full lines without blocking long after starting a partial line. */ rc = _assuan_read_line (ctx); if (_assuan_error_is_eagain (ctx, rc)) return 0; if (gpg_err_code (rc) == GPG_ERR_EOF) { ctx->process_complete = 1; return 0; } if (rc) return rc; if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) /* Comment lines are ignored. */ return 0; /* Now we have a line that really means something. It could be one of the following things: First, if we are not in a command already, it is the next command to dispatch. Second, if we are in a command, it can only be the response to an INQUIRE reply. */ if (!ctx->in_command) { ctx->in_command = 1; ctx->outbound.data.error = 0; ctx->outbound.data.linelen = 0; /* Dispatch command and return reply. */ ctx->in_process_next = 1; rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); ctx->in_process_next = 0; } else if (ctx->in_inquire) { /* FIXME: Pick up the continuation. */ rc = _assuan_inquire_ext_cb (ctx); } else { /* Should not happen. The client is sending data while we are in a command and not waiting for an inquire. We log an error and discard it. */ TRACE0 (ctx, ASSUAN_LOG_DATA, "process_next", ctx, "unexpected client data"); rc = 0; } return rc; } /* This function should be invoked when the assuan connected FD is ready for reading. If the equivalent to EWOULDBLOCK is returned (this should be done by the command handler), assuan_process_next should be invoked the next time the connected FD is readable. Eventually, the caller will finish by invoking assuan_process_done. DONE is set to 1 if the connection has ended. */ gpg_error_t assuan_process_next (assuan_context_t ctx, int *done) { gpg_error_t rc; if (done) *done = 0; ctx->process_complete = 0; do { rc = process_next (ctx); } while (!rc && !ctx->process_complete && assuan_pending_line (ctx)); if (done) *done = !!ctx->process_complete; return rc; } static gpg_error_t process_request (assuan_context_t ctx) { gpg_error_t rc; if (ctx->in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); do { rc = _assuan_read_line (ctx); } while (_assuan_error_is_eagain (ctx, rc)); if (gpg_err_code (rc) == GPG_ERR_EOF) { ctx->process_complete = 1; return 0; } if (rc) return rc; if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) return 0; /* comment line - ignore */ ctx->in_command = 1; ctx->outbound.data.error = 0; ctx->outbound.data.linelen = 0; /* dispatch command and return reply */ rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); return assuan_process_done (ctx, rc); } /** * assuan_process: * @ctx: assuan context * * This function is used to handle the assuan protocol after a * connection has been established using assuan_accept(). This is the * main protocol handler. * * Return value: 0 on success or an error code if the assuan operation * failed. Note, that no error is returned for operational errors. **/ gpg_error_t assuan_process (assuan_context_t ctx) { gpg_error_t rc; ctx->process_complete = 0; do { rc = process_request (ctx); } while (!rc && !ctx->process_complete); return rc; } /** * assuan_get_active_fds: * @ctx: Assuan context * @what: 0 for read fds, 1 for write fds * @fdarray: Caller supplied array to store the FDs * @fdarraysize: size of that array * * Return all active filedescriptors for the given context. This * function can be used to select on the fds and call * assuan_process_next() if there is an active one. The first fd in * the array is the one used for the command connection. * * Note, that write FDs are not yet supported. * * Return value: number of FDs active and put into @fdarray or -1 on * error which is most likely a too small fdarray. **/ int assuan_get_active_fds (assuan_context_t ctx, int what, assuan_fd_t *fdarray, int fdarraysize) { int n = 0; if (!ctx || fdarraysize < 2 || what < 0 || what > 1) return -1; if (!what) { if (ctx->inbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->inbound.fd; } else { if (ctx->outbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->outbound.fd; if (ctx->outbound.data.fp) #if defined(HAVE_W32CE_SYSTEM) fdarray[n++] = (void*)fileno (ctx->outbound.data.fp); #elif defined(HAVE_W32_SYSTEM) fdarray[n++] = (void*)_get_osfhandle (fileno (ctx->outbound.data.fp)); #else fdarray[n++] = fileno (ctx->outbound.data.fp); #endif } return n; } /* Two simple wrappers to make the expected function types match. */ #ifdef HAVE_FUNOPEN static int fun1_cookie_write (void *cookie, const char *buffer, int orig_size) { return _assuan_cookie_write_data (cookie, buffer, orig_size); } #endif /*HAVE_FUNOPEN*/ #ifdef HAVE_FOPENCOOKIE static ssize_t fun2_cookie_write (void *cookie, const char *buffer, size_t orig_size) { return _assuan_cookie_write_data (cookie, buffer, orig_size); } #endif /*HAVE_FOPENCOOKIE*/ /* Return a FP to be used for data output. The FILE pointer is valid until the end of a handler. So a close is not needed. Assuan does all the buffering needed to insert the status line as well as the required line wappping and quoting for data lines. We use GNU's custom streams here. There should be an alternative implementaion for systems w/o a glibc, a simple implementation could use a child process */ FILE * assuan_get_data_fp (assuan_context_t ctx) { #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) if (ctx->outbound.data.fp) return ctx->outbound.data.fp; #ifdef HAVE_FUNOPEN ctx->outbound.data.fp = funopen (ctx, 0, fun1_cookie_write, 0, _assuan_cookie_write_flush); #else ctx->outbound.data.fp = funopen (ctx, 0, fun2_cookie_write, 0, _assuan_cookie_write_flush); #endif ctx->outbound.data.error = 0; return ctx->outbound.data.fp; #else gpg_err_set_errno (ENOSYS); return NULL; #endif } /* Set the text used for the next OK reponse. This string is automatically reset to NULL after the next command. */ gpg_error_t assuan_set_okay_line (assuan_context_t ctx, const char *line) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!line) { _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; } else { /* FIXME: we need to use gcry_is_secure() to test whether we should allocate the entire line in secure memory */ char *buf = _assuan_malloc (ctx, 3 + strlen(line) + 1); if (!buf) return _assuan_error (ctx, gpg_err_code_from_syserror ()); strcpy (buf, "OK "); strcpy (buf+3, line); _assuan_free (ctx, ctx->okay_line); ctx->okay_line = buf; } return 0; } gpg_error_t assuan_write_status (assuan_context_t ctx, const char *keyword, const char *text) { char buffer[256]; char *helpbuf; size_t n; gpg_error_t ae; if ( !ctx || !keyword) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!text) text = ""; n = 2 + strlen (keyword) + 1 + strlen (text) + 1; if (n < sizeof (buffer)) { strcpy (buffer, "S "); strcat (buffer, keyword); if (*text) { strcat (buffer, " "); strcat (buffer, text); } ae = assuan_write_line (ctx, buffer); } else if ( (helpbuf = _assuan_malloc (ctx, n)) ) { strcpy (helpbuf, "S "); strcat (helpbuf, keyword); if (*text) { strcat (helpbuf, " "); strcat (helpbuf, text); } ae = assuan_write_line (ctx, helpbuf); _assuan_free (ctx, helpbuf); } else ae = 0; return ae; } diff --git a/tests/ChangeLog b/tests/ChangeLog index c2c3f56..8d402ad 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,71 +1,74 @@ +2011-03-28 Werner Koch + + * ce-server.c: Include sys/select.h if needed. Fixes bug#1328. + 2010-10-11 Werner Koch * Makefile.am (w32cetools): Move ce-server to here. 2010-03-17 Werner Koch * pipeconnect.c: New. Based on fdpassing.c 2010-02-24 Werner Koch * ce-server.c: New. * ce-createpipe.c [W32CE]: New. 2010-01-27 Werner Koch * common.h (SOCKET2HANDLE, HANDLE2SOCKET): New. 2009-11-05 Marcus Brinkmann * fdpassing.c (main): Call assuan_pipe_connect instead of assuan_pipe_connect_ext. 2009-11-04 Werner Koch * fdpassing.c (register_commands): Add NULL arg to assuan_register_command. 2009-09-19 Marcus Brinkmann * fdpassing.c: Update to new API. 2009-08-26 Marcus Brinkmann * Makefile.am (AM_CFLAGS, LDADD): Add gpg-error. * fdpassing.c: Change error values to gpg-error ones. 2008-11-03 Marcus Brinkmann * fdpassing.c (register_commands): Add missing initializer to silence gcc -W warning. 2006-10-10 Werner Koch * Makefile.am (LDADD): Add NETLIBS. 2006-09-19 Werner Koch * 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 * 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/ce-server.c b/tests/ce-server.c index 9975e53..24cf734 100644 --- a/tests/ce-server.c +++ b/tests/ce-server.c @@ -1,1416 +1,1418 @@ /* ce-server.c - An Assuan testbed for W32CE; server code Copyright (C) 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 . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM # define WIN32_LEAN_AND_MEAN # include #else # include # include # include # include +# ifdef HAVE_SYS_SELECT_H +# include +# else #endif #include #ifdef HAVE_W32CE_SYSTEM # ifndef FILE_ATTRIBUTE_ROMSTATICREF # define FILE_ATTRIBUTE_ROMSTATICREF FILE_ATTRIBUTE_OFFLINE # endif extern BOOL GetStdioPathW (int, wchar_t *, DWORD *); extern BOOL SetStdioPathW (int, const wchar_t *); #endif /*!HAVE_W32CE_SYSTEM*/ #include "../src/assuan.h" #include "common.h" /* The port we are using by default. */ static short server_port = 15898; /* Flag set to indicate a shutdown. */ static int shutdown_pending; /* An object to keep track of file descriptors. */ struct fdinfo_s { struct fdinfo_s *next; assuan_fd_t fd; /* The descriptor. */ }; typedef struct fdinfo_s *fdinfo_t; /* The local state of a connection. */ struct state_s { /* The current working directory - access using get_cwd(). */ - char *cwd; - + char *cwd; + /* If valid, a socket in listening state created by the dataport command. */ assuan_fd_t dataport_listen_fd; /* If valid the socket accepted for the dataport. */ assuan_fd_t dataport_accepted_fd; /* The error code from a dataport accept operation. */ gpg_error_t dataport_accept_err; /* A list of all unused descriptors created by dataport commands. */ fdinfo_t dataport_fds; /* The descriptor set by the DATAPORT command. */ assuan_fd_t dataport_fd; }; typedef struct state_s *state_t; /* Local prototypes. */ static gpg_error_t cmd_newdataport_cont (void *opaque, gpg_error_t err, unsigned char *data, size_t datalen); /* A wrapper around read to make it work under Windows with HANDLES and socket descriptors. Takes care of EINTR on POSIX. */ static int my_read (assuan_fd_t fd, void *buffer, size_t size) { int res; #ifdef HAVE_W32_SYSTEM res = recv (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) { switch (WSAGetLastError ()) { case WSAENOTSOCK: { DWORD nread = 0; - + res = ReadFile (fd, buffer, size, &nread, NULL); if (!res) { switch (GetLastError ()) { case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; default: - gpg_err_set_errno (EIO); + gpg_err_set_errno (EIO); } res = -1; } else res = (int) nread; } break; - + case WSAEWOULDBLOCK: gpg_err_set_errno (EAGAIN); break; case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; default: gpg_err_set_errno (EIO); break; } } return res; #else /*!HAVE_W32_SYSTEM*/ do res = read (fd, buffer, size); while (res == -1 && errno == EINTR); return res; #endif /*!HAVE_W32_SYSTEM*/ } /* Extended version of write(2) to guarantee that all bytes are written. Returns 0 on success or -1 and ERRNO on failure. NOTE: This function does not return the number of bytes written, so any error must be treated as fatal for this connection as the state of the receiver is unknown. This works best if blocking is allowed (so EAGAIN cannot occur). Under Windows this function handles socket descriptors and system handles. */ static int my_writen (assuan_fd_t fd, const char *buffer, size_t length) { while (length) { int nwritten; #ifdef HAVE_W32_SYSTEM nwritten = send (HANDLE2SOCKET (fd), buffer, length, 0); if (nwritten == -1 && WSAGetLastError () == WSAENOTSOCK) { DWORD nwrite; - + nwritten = WriteFile (fd, buffer, length, &nwrite, NULL); if (!nwritten) { switch (GetLastError ()) { - case ERROR_BROKEN_PIPE: + case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: gpg_err_set_errno (EPIPE); break; - + default: gpg_err_set_errno (EIO); break; } nwritten= -1; } else nwritten = (int)nwrite; } #else /*!HAVE_W32_SYSTEM*/ nwritten = write (fd, buffer, length); #endif /*!HAVE_W32_SYSTEM*/ if (nwritten < 0) { if (errno == EINTR) continue; return -1; /* write error */ } length -= nwritten; buffer += nwritten; } return 0; /* okay */ } -static state_t +static state_t new_state (void) { state_t state = xcalloc (1, sizeof *state); state->dataport_listen_fd = ASSUAN_INVALID_FD; state->dataport_accepted_fd = ASSUAN_INVALID_FD; state->dataport_fd = ASSUAN_INVALID_FD; return state; } - -static void + +static void release_state (state_t state) { fdinfo_t fi, fi2; if (!state) return; xfree (state->cwd); if (state->dataport_fd != ASSUAN_INVALID_FD) assuan_sock_close (state->dataport_fd); if (state->dataport_listen_fd != ASSUAN_INVALID_FD) assuan_sock_close (state->dataport_listen_fd); if (state->dataport_accepted_fd != ASSUAN_INVALID_FD) assuan_sock_close (state->dataport_accepted_fd); for (fi=state->dataport_fds; fi; fi = fi2) { fi2 = fi->next; if (fi->fd != ASSUAN_INVALID_FD) assuan_sock_close (fi->fd); } xfree (state); } /* Helper to print a message while leaving a command and to acknowledge the command. */ static gpg_error_t leave_cmd (assuan_context_t ctx, gpg_error_t err) { if (err) { const char *name = assuan_get_command_name (ctx); if (!name) name = "?"; if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) log_error ("command '%s' failed: %s\n", name, gpg_strerror (err)); else log_error ("command '%s' failed: %s <%s>\n", name, gpg_strerror (err), gpg_strsource (err)); } return assuan_process_done (ctx, err); } #ifdef HAVE_W32CE_SYSTEM static char * wchar_to_utf8 (const wchar_t *string) { int n; size_t length = wcslen (string); char *result; n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL); if (n < 0 || (n+1) <= 0) log_fatal ("WideCharToMultiByte failed\n"); result = xmalloc (n+1); n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL); if (n < 0) log_fatal ("WideCharToMultiByte failed\n"); - + result[n] = 0; return result; } static wchar_t * utf8_to_wchar (const char *string) { int n; size_t length = strlen (string); wchar_t *result; size_t nbytes; n = MultiByteToWideChar (CP_UTF8, 0, string, length, NULL, 0); if (n < 0 || (n+1) <= 0) log_fatal ("MultiByteToWideChar failed\n"); nbytes = (size_t)(n+1) * sizeof(*result); - if (nbytes / sizeof(*result) != (n+1)) + if (nbytes / sizeof(*result) != (n+1)) log_fatal ("utf8_to_wchar: integer overflow\n"); result = xmalloc (nbytes); n = MultiByteToWideChar (CP_UTF8, 0, string, length, result, n); if (n < 0) log_fatal ("MultiByteToWideChar failed\n"); result[n] = 0; - + return result; } #endif /*HAVE_W32CE_SYSTEM*/ #ifndef HAVE_W32CE_SYSTEM static char * gnu_getcwd (void) { size_t size = 100; - + while (1) { char *buffer = xmalloc (size); if (getcwd (buffer, size) == buffer) return buffer; xfree (buffer); if (errno != ERANGE) return 0; size *= 2; } } #endif /*!HAVE_W32CE_SYSTEM*/ /* Return the current working directory. The returned string is valid as long as STATE->cwd is not changed. */ static const char * get_cwd (state_t state) { if (!state->cwd) { /* No working directory yet. On WindowsCE make it the module directory of this process. */ #ifdef HAVE_W32_SYSTEM char *p; #endif #ifdef HAVE_W32CE_SYSTEM wchar_t buf[MAX_PATH+1]; size_t n; n = GetModuleFileName (NULL, buf, MAX_PATH); if (!n) state->cwd = xstrdup ("/"); else { buf[n] = 0; state->cwd = wchar_to_utf8 (buf); p = strrchr (state->cwd, '\\'); if (p) *p = 0; } #else state->cwd = gnu_getcwd (); #endif #ifdef HAVE_W32_SYSTEM for (p=state->cwd; *p; p++) if (*p == '\\') *p = '/'; #endif /*HAVE_W32_SYSTEM*/ } return state->cwd; } static gpg_error_t reset_notify (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); fdinfo_t fi, fi2; /* Close all lingering dataport connections. */ for (fi=state->dataport_fds; fi; fi = fi2) { fi2 = fi->next; if (fi->fd != ASSUAN_INVALID_FD) assuan_sock_close (fi->fd); } state->dataport_fds = NULL; return 0; } static gpg_error_t input_notify (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); assuan_fd_t fd = assuan_get_input_fd (ctx); fdinfo_t fi; if (fd != ASSUAN_INVALID_FD) { /* The fd is now in use use - remove it from the unused list. */ for (fi=state->dataport_fds; fi; fi = fi->next) if (fi->fd == fd) fi->fd = ASSUAN_INVALID_FD; } return 0; } static gpg_error_t output_notify (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); assuan_fd_t fd = assuan_get_output_fd (ctx); fdinfo_t fi; if (fd != ASSUAN_INVALID_FD) { /* The fd is now in use - remove it from the unused list. */ for (fi=state->dataport_fds; fi; fi = fi->next) if (fi->fd == fd) fi->fd = ASSUAN_INVALID_FD; } return 0; } -static const char hlp_echo[] = +static const char hlp_echo[] = "ECHO \n" "\n" "Print LINE as data lines.\n"; static gpg_error_t cmd_echo (assuan_context_t ctx, char *line) { gpg_error_t err; err = assuan_send_data (ctx, line, strlen (line)); return leave_cmd (ctx, err); } -static const char hlp_cat[] = +static const char hlp_cat[] = "CAT []\n" "\n" "Copy the content of FILENAME to the descriptor set by the OUTPUT\n" "command. If no OUTPUT command has been given, send the content\n" "using data lines. Without FILENAME take the content from the\n" "descriptor set by the INPUT command; if a DATAPORT has been set\n" "this descriptor is used for I/O and the INOPUT/OUTPUT descriptors\n" "are not touched."; static gpg_error_t cmd_cat (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); gpg_error_t err = 0; assuan_fd_t fd_in = ASSUAN_INVALID_FD; assuan_fd_t fd_out = ASSUAN_INVALID_FD; FILE *fp_in = NULL; char buf[256]; size_t nread; int use_dataport = 0; if (*line) { fp_in = fopen (line, "rb"); if (!fp_in) err = gpg_error_from_syserror (); else fd_out = assuan_get_output_fd (ctx); } else if (state->dataport_fd != ASSUAN_INVALID_FD) { use_dataport = 1; fd_in = state->dataport_fd; fd_out = state->dataport_fd; } else if ((fd_in = assuan_get_input_fd (ctx)) != ASSUAN_INVALID_FD) { /* This FD is actually a socket descriptor. We can't fdopen it because under Windows we ust use recv(2) instead of read(2). Note that on POSIX systems there is no difference between libc file descriptors and socket descriptors. */ fd_out = assuan_get_output_fd (ctx); } else err = gpg_error (GPG_ERR_ASS_NO_INPUT); if (err) goto leave; do { if (fp_in) { nread = fread (buf, 1, sizeof buf, fp_in); if (nread < sizeof buf) { if (ferror (fp_in)) err = gpg_error_from_syserror (); else if (feof (fp_in)) err = gpg_error (GPG_ERR_EOF); } } else { int n; nread = 0; n = my_read (fd_in, buf, sizeof buf); if (n < 0) err = gpg_error_from_syserror (); else if (!n) err = gpg_error (GPG_ERR_EOF); else nread = n; } - + if (fd_out != ASSUAN_INVALID_FD) { if (nread && my_writen (fd_out, buf, nread)) err = gpg_error_from_syserror (); } else if (nread) err = assuan_send_data (ctx, buf, nread); } while (!err); if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; leave: if (fp_in) fclose (fp_in); if (use_dataport) { if (state->dataport_fd != ASSUAN_INVALID_FD) { assuan_sock_close (state->dataport_fd); state->dataport_fd = ASSUAN_INVALID_FD; } } else { assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); } return leave_cmd (ctx, err); } -static const char hlp_pwd[] = +static const char hlp_pwd[] = "PWD\n" "\n" "Print the curent working directory of this session.\n"; static gpg_error_t cmd_pwd (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); gpg_error_t err; const char *string; - + string = get_cwd (state); err = assuan_send_data (ctx, string, strlen (string)); return leave_cmd (ctx, err); } -static const char hlp_cd[] = +static const char hlp_cd[] = "CD [dir]\n" "\n" "Change the curretn directory of the session.\n"; static gpg_error_t cmd_cd (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); gpg_error_t err = 0; char *newdir, *p; for (p=line; *p; p++) if (*p == '\\') *p = '/'; if (!*line) { xfree (state->cwd); state->cwd = NULL; get_cwd (state); } else { if (*line == '/') newdir = xstrdup (line); else newdir = xstrconcat (get_cwd (state), "/", line, NULL); - + while (strlen(newdir) > 1 && line[strlen(newdir)-1] == '/') line[strlen(newdir)-1] = 0; xfree (state->cwd); state->cwd = newdir; } return leave_cmd (ctx, err); } #ifdef HAVE_W32CE_SYSTEM -static const char hlp_ls[] = +static const char hlp_ls[] = "LS []\n" "\n" "List the files described by PATTERN.\n"; static gpg_error_t cmd_ls (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); gpg_error_t err; WIN32_FIND_DATA fi; char buf[500]; HANDLE hd; char *p, *fname; wchar_t *wfname; if (!*line) fname = xstrconcat (get_cwd (state), "/*", NULL); else if (*line == '/' || *line == '\\') fname = xstrdup (line); else fname = xstrconcat (get_cwd (state), "/", line, NULL); for (p=fname; *p; p++) if (*p == '/') *p = '\\'; assuan_write_status (ctx, "PATTERN", fname); wfname = utf8_to_wchar (fname); xfree (fname); hd = FindFirstFile (wfname, &fi); free (wfname); if (hd == INVALID_HANDLE_VALUE) { log_info ("FindFirstFile returned %d\n", GetLastError ()); err = gpg_error_from_syserror (); /* Works for W32CE. */ goto leave; } do { DWORD attr = fi.dwFileAttributes; fname = wchar_to_utf8 (fi.cFileName); - snprintf (buf, sizeof buf, + snprintf (buf, sizeof buf, "%c%c%c%c%c%c%c%c%c%c%c%c%c %7lu%c %s\n", (attr & FILE_ATTRIBUTE_DIRECTORY) ? ((attr & FILE_ATTRIBUTE_DEVICE)? 'c':'d'):'-', (attr & FILE_ATTRIBUTE_READONLY)? 'r':'-', (attr & FILE_ATTRIBUTE_HIDDEN)? 'h':'-', (attr & FILE_ATTRIBUTE_SYSTEM)? 's':'-', (attr & FILE_ATTRIBUTE_ARCHIVE)? 'a':'-', (attr & FILE_ATTRIBUTE_COMPRESSED)? 'c':'-', (attr & FILE_ATTRIBUTE_ENCRYPTED)? 'e':'-', (attr & FILE_ATTRIBUTE_INROM)? 'R':'-', (attr & FILE_ATTRIBUTE_REPARSE_POINT)? 'P':'-', (attr & FILE_ATTRIBUTE_ROMMODULE)? 'M':'-', (attr & FILE_ATTRIBUTE_ROMSTATICREF)? 'R':'-', (attr & FILE_ATTRIBUTE_SPARSE_FILE)? 'S':'-', (attr & FILE_ATTRIBUTE_TEMPORARY)? 't':'-', (unsigned long)fi.nFileSizeLow, fi.nFileSizeHigh? 'X':' ', fname); free (fname); err = assuan_send_data (ctx, buf, strlen (buf)); if (!err) err = assuan_send_data (ctx, NULL, 0); } while (!err && FindNextFile (hd, &fi)); if (err) ; else if (GetLastError () == ERROR_NO_MORE_FILES) err = 0; else { log_info ("FindNextFile returned %d\n", GetLastError ()); - err = gpg_error_from_syserror (); + err = gpg_error_from_syserror (); } FindClose (hd); leave: return leave_cmd (ctx, err); } #endif /*HAVE_W32CE_SYSTEM*/ #ifdef HAVE_W32CE_SYSTEM -static const char hlp_run[] = +static const char hlp_run[] = "RUN []\n" "\n" "Run the program in FILENAME with the arguments ARGS.\n" "This creates a new process and waits for it to finish.\n" "FIXME: The process' stdin is connected to the file set by the\n" "INPUT command; stdout and stderr to the one set by OUTPUT.\n"; static gpg_error_t cmd_run (assuan_context_t ctx, char *line) { /* state_t state = assuan_get_pointer (ctx); */ gpg_error_t err; BOOL w32ret; PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; char *p; wchar_t *pgmname = NULL; wchar_t *cmdline = NULL; int code; DWORD exc; int idx; struct { HANDLE hd[2]; int oldname_valid; wchar_t oldname[MAX_PATH]; } pipes[3]; for (idx=0; idx < 3; idx++) { pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE; pipes[idx].oldname_valid = 0; } p = strchr (line, ' '); if (p) { *p = 0; pgmname = utf8_to_wchar (line); for (p++; *p && *p == ' '; p++) ; cmdline = utf8_to_wchar (p); } else pgmname = utf8_to_wchar (line); { char *tmp1 = wchar_to_utf8 (pgmname); char *tmp2 = wchar_to_utf8 (cmdline); log_info ("CreateProcess, path=`%s' cmdline=`%s'\n", tmp1, tmp2); xfree (tmp2); xfree (tmp1); } /* Redirect the standard handles. */ /* Create pipes. */ for (idx=0; idx < 3; idx++) { if (!_assuan_w32ce_create_pipe (&pipes[idx].hd[0], &pipes[idx].hd[1], NULL, 0)) { err = gpg_error_from_syserror (); log_error ("CreatePipe failed: %d\n", GetLastError ()); pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE; goto leave; } } /* Save currently assigned devices. */ for (idx=0; idx < 3; idx++) { DWORD dwlen = MAX_PATH; if (!GetStdioPathW (idx, pipes[idx].oldname, &dwlen)) { err = gpg_error_from_syserror (); log_error ("GetStdioPath failed: %d\n", GetLastError ()); goto leave; } pipes[idx].oldname_valid = 1; } /* Connect the pipes. */ { if (!SetStdioPathW (1, L"\\mystdout.log")) { err = gpg_error_from_syserror (); log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ()); goto leave; } if (!SetStdioPathW (2, L"\\mystderr.log")) { err = gpg_error_from_syserror (); log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ()); goto leave; } } /* Create the process, restore the devices and check the error. */ w32ret = CreateProcess (pgmname, /* Program to start. */ cmdline, /* Command line arguments. */ NULL, /* Process security. Not used. */ NULL, /* Thread security. Not used. */ FALSE, /* Inherit handles. Not used. */ CREATE_SUSPENDED, /* Creation flags. */ NULL, /* Environment. Not used. */ NULL, /* Use current dir. Not used. */ NULL, /* Startup information. Not used. */ &pi /* Returns process information. */ ); for (idx=0; idx < 3; idx++) { if (pipes[idx].oldname_valid) { if (!SetStdioPathW (idx, pipes[idx].oldname)) log_error ("SetStdioPath(%d) failed during restore: %d\n", idx, GetLastError ()); else pipes[idx].oldname_valid = 0; } } if (!w32ret) { /* Error checking after restore so that the messages are visible. */ log_error ("CreateProcess failed: %d\n", GetLastError ()); err = gpg_error_from_syserror (); goto leave; } - log_info ("CreateProcess ready: hProcess=%p hThread=%p" - " dwProcessID=%d dwThreadId=%d\n", + log_info ("CreateProcess ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); ResumeThread (pi.hThread); - CloseHandle (pi.hThread); + CloseHandle (pi.hThread); code = WaitForSingleObject (pi.hProcess, INFINITE); - switch (code) + switch (code) { case WAIT_FAILED: err = gpg_error_from_syserror ();; log_error ("waiting for process %d to terminate failed: %d\n", (int)pi.dwProcessId, GetLastError ()); break; case WAIT_OBJECT_0: if (!GetExitCodeProcess (pi.hProcess, &exc)) { err = gpg_error_from_syserror ();; log_error ("error getting exit code of process %d: %s\n", (int)pi.dwProcessId, GetLastError () ); } else if (exc) { log_info ("error running process: exit status %d\n", (int)exc); err = gpg_error (GPG_ERR_GENERAL); } else { err = 0; } break; - + default: err = gpg_error_from_syserror ();; log_error ("WaitForSingleObject returned unexpected " "code %d for pid %d\n", code, (int)pi.dwProcessId); break; } CloseHandle (pi.hProcess); - + leave: for (idx=0; idx < 3; idx++) { if (pipes[idx].oldname_valid) { if (!SetStdioPathW (idx, pipes[idx].oldname)) log_error ("SetStdioPath(%d) failed during restore: %d\n", idx, GetLastError ()); else pipes[idx].oldname_valid = 0; } } for (idx=0; idx < 3; idx++) { if (pipes[idx].hd[0] != INVALID_HANDLE_VALUE) CloseHandle (pipes[idx].hd[0]); if (pipes[idx].hd[1] != INVALID_HANDLE_VALUE) CloseHandle (pipes[idx].hd[1]); } xfree (cmdline); xfree (pgmname); return leave_cmd (ctx, err); } #endif /*HAVE_W32CE_SYSTEM*/ -static const char hlp_newdataport[] = +static const char hlp_newdataport[] = "NEWDATAPORT\n" "\n" "Create a new dataport. The server creates a listening socket and\n" "issues the inquiry:\n" " INQUIRE CONNECT-TO \n" "The client is expected to connect to PORT of the server and confirm\n" "this by sending just an \"END\". In turn the server sends:\n" " S FDINFO \n" "With N being the local descriptor for the accepted connection. This\n" "descriptor may now be used with INPUT or OUTPUT commands."; struct cmd_dataport_locals { assuan_context_t ctx; int passthru; int port; }; static gpg_error_t cmd_newdataport (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); gpg_error_t err = 0; struct sockaddr_in addr; socklen_t addrlen; struct cmd_dataport_locals *cont; char inqline[100]; cont = xmalloc (sizeof *cont); cont->ctx = ctx; cont->passthru = 0; cont->port = 0; if (state->dataport_listen_fd != ASSUAN_INVALID_FD) { log_error ("Oops, still listening on a dataport socket\n"); state->dataport_listen_fd = ASSUAN_INVALID_FD; } if (state->dataport_accepted_fd != ASSUAN_INVALID_FD) { log_error ("Oops, still holding an accepted dataport socket\n"); state->dataport_accepted_fd = ASSUAN_INVALID_FD; } state->dataport_accept_err = 0; state->dataport_listen_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0); if (state->dataport_listen_fd == ASSUAN_INVALID_FD) { err = gpg_error_from_syserror (); log_error ("socket() failed: %s\n", strerror (errno)); goto leave; } addr.sin_family = AF_INET; addr.sin_port = 0; addr.sin_addr.s_addr = htonl (INADDR_ANY); if (assuan_sock_bind (state->dataport_listen_fd, (struct sockaddr *)&addr, sizeof addr)) { err = gpg_error_from_syserror (); log_error ("listen() failed: %s\n", strerror (errno)); goto leave; } - + if (listen (HANDLE2SOCKET (state->dataport_listen_fd), 1)) { err = gpg_error_from_syserror (); log_error ("listen() failed: %s\n", strerror (errno)); goto leave; } addrlen = sizeof addr; if (getsockname (HANDLE2SOCKET (state->dataport_listen_fd), (struct sockaddr *)&addr, &addrlen)) { err = gpg_error_from_syserror (); log_error ("getsockname() failed: %s\n", strerror (errno)); goto leave; } cont->port = ntohs (addr.sin_port); if (verbose) log_info ("server now also listening on port %d\n", cont->port); snprintf (inqline, sizeof inqline, "CONNECT-TO %d", cont->port); err = assuan_inquire_ext (ctx, inqline, 0, cmd_newdataport_cont, cont); if (!err) return 0; /* Transfer to continuation. */ leave: cont->passthru = 1; return cmd_newdataport_cont (cont, err, NULL, 0); } /* Continuation used by cmd_newdataport. */ static gpg_error_t cmd_newdataport_cont (void *opaque, gpg_error_t err, unsigned char *data, size_t datalen) { struct cmd_dataport_locals *cont = opaque; assuan_context_t ctx = cont->ctx; state_t state = assuan_get_pointer (ctx); char numbuf[35]; fdinfo_t fi; if (cont->passthru || err) goto leave; err = state->dataport_accept_err; if (err) goto leave; if (state->dataport_listen_fd != ASSUAN_INVALID_FD || state->dataport_accepted_fd == ASSUAN_INVALID_FD) { err = gpg_error (GPG_ERR_MISSING_ACTION); goto leave; } for (fi = state->dataport_fds; fi; fi = fi->next) if (fi->fd == ASSUAN_INVALID_FD) break; if (!fi) { fi = xcalloc (1, sizeof *fi); fi->next = state->dataport_fds; state->dataport_fds = fi; } fi->fd = state->dataport_accepted_fd; state->dataport_accepted_fd = ASSUAN_INVALID_FD; /* Note that under Windows the FD is the socket descriptor. Socket descriptors are neither handles nor libc file descriptors. */ snprintf (numbuf, sizeof numbuf, "%d", HANDLE2SOCKET (fi->fd)); err = assuan_write_status (ctx, "FDINFO", numbuf); leave: if (state->dataport_listen_fd != ASSUAN_INVALID_FD) { assuan_sock_close (state->dataport_listen_fd); state->dataport_listen_fd = ASSUAN_INVALID_FD; } if (state->dataport_accepted_fd != ASSUAN_INVALID_FD) { assuan_sock_close (state->dataport_accepted_fd); state->dataport_accepted_fd = ASSUAN_INVALID_FD; } xfree (cont); return leave_cmd (ctx, err); } static const char hlp_dataport[] = "DATAPORT FD[=]\n" "\n" "Set the file descriptor to read and write data via port.\n" "This is similar to the \"INPUT\" and \"OUTPUT\" commands\n" "but useful for socketpairs."; static gpg_error_t cmd_dataport (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); gpg_error_t err; assuan_fd_t fd; if (state->dataport_fd != ASSUAN_INVALID_FD) { assuan_sock_close (state->dataport_fd); state->dataport_fd = ASSUAN_INVALID_FD; } err = assuan_command_parse_fd (ctx, line, &fd); if (!err && fd != ASSUAN_INVALID_FD) { fdinfo_t fi; state->dataport_fd = fd; /* The fd is now in use use - remove it from the unused list. */ for (fi=state->dataport_fds; fi; fi = fi->next) if (fi->fd == fd) fi->fd = ASSUAN_INVALID_FD; } return leave_cmd (ctx, err); } -static const char hlp_getinfo[] = +static const char hlp_getinfo[] = "GETINFO \n" "\n" "Multipurpose function to return a variety of information.\n" "Supported values for WHAT are:\n" "\n" " version - Return the version of the program.\n" " pid - Return the process id of the server.\n" " dataports - Return a list of usused dataports."; static gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) { state_t state = assuan_get_pointer (ctx); gpg_error_t err = 0; char numbuf[50]; if (!strcmp (line, "version")) { const char *s = VERSION; err = assuan_send_data (ctx, s, strlen (s)); } else if (!strcmp (line, "pid")) { snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ()); err = assuan_send_data (ctx, numbuf, strlen (numbuf)); } else if (!strcmp (line, "dataports")) { fdinfo_t fi; int any = 0; for (fi=state->dataport_fds; !err && fi; fi = fi->next) { if (fi->fd != ASSUAN_INVALID_FD) { snprintf (numbuf, sizeof numbuf, "%s%d", any? " ":"", HANDLE2SOCKET (fi->fd)); any = 1; err = assuan_send_data (ctx, numbuf, strlen (numbuf)); } } } else err = gpg_error (GPG_ERR_ASS_PARAMETER); return leave_cmd (ctx, err); } -static const char hlp_shutdown[] = +static const char hlp_shutdown[] = "SHUTDOWN\n" "\n" "Shutdown the server process\n"; static gpg_error_t cmd_shutdown (assuan_context_t ctx, char *line) { (void)line; shutdown_pending = 1; return leave_cmd (ctx, 0);; } static gpg_error_t register_commands (assuan_context_t ctx) { static struct { const char *name; gpg_error_t (*handler) (assuan_context_t, char *line); const char * const help; } table[] = { #ifdef HAVE_W32CE_SYSTEM { "LS", cmd_ls, hlp_ls }, { "RUN", cmd_run, hlp_run }, #endif { "PWD", cmd_pwd, hlp_pwd }, { "CD", cmd_cd, hlp_cd }, { "ECHO", cmd_echo, hlp_echo }, { "CAT", cmd_cat, hlp_cat }, { "NEWDATAPORT", cmd_newdataport, hlp_newdataport }, { "DATAPORT", cmd_dataport, hlp_dataport }, { "INPUT", NULL }, { "OUTPUT", NULL }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "SHUTDOWN", cmd_shutdown, hlp_shutdown }, { NULL, NULL } }; int i; gpg_error_t rc; for (i=0; table[i].name; i++) { - rc = assuan_register_command (ctx, table[i].name, + rc = assuan_register_command (ctx, table[i].name, table[i].handler, table[i].help); if (rc) return rc; } return 0; } static assuan_fd_t get_connection_fd (assuan_context_t ctx) { assuan_fd_t fds[5]; if (assuan_get_active_fds (ctx, 0, fds, DIM (fds)) < 1) log_fatal ("assuan_get_active_fds failed\n"); if (fds[0] == ASSUAN_INVALID_FD) log_fatal ("assuan_get_active_fds returned invalid conenction fd\n"); return fds[0]; } /* Startup the server. */ static void server (void) { gpg_error_t err; assuan_fd_t server_fd; assuan_sock_nonce_t server_nonce; int one = 1; struct sockaddr_in name; assuan_context_t ctx; state_t state = NULL; err = assuan_new (&ctx); if (err) log_fatal ("assuan_new failed: %s\n", gpg_strerror (err)); server_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0); if (server_fd == ASSUAN_INVALID_FD) log_fatal ("socket() failed: %s\n", strerror (errno)); - if (setsockopt (HANDLE2SOCKET (server_fd), + if (setsockopt (HANDLE2SOCKET (server_fd), SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof one)) log_error ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno)); name.sin_family = AF_INET; name.sin_port = htons (server_port); name.sin_addr.s_addr = htonl (INADDR_ANY); if (assuan_sock_bind (server_fd, (struct sockaddr *) &name, sizeof name)) log_fatal ("bind() failed: %s\n", strerror (errno)); - if (assuan_sock_get_nonce ((struct sockaddr*)&name, sizeof name, + if (assuan_sock_get_nonce ((struct sockaddr*)&name, sizeof name, &server_nonce)) log_fatal ("assuan_sock_get_nonce failed: %s\n", strerror (errno)); /* Register the nonce with the context so that assuan_accept knows about it. We can't do that directly in assuan_sock_bind because we want these socket wrappers to be context neutral and drop in replacement for the standard socket functions. */ assuan_set_sock_nonce (ctx, &server_nonce); if (listen (HANDLE2SOCKET (server_fd), 5)) log_fatal ("listen() failed: %s\n", strerror (errno)); log_info ("server listening on port %hd\n", server_port); err = assuan_init_socket_server (ctx, server_fd, 0); if (err) log_fatal ("assuan_init_socket_server failed: %s\n", gpg_strerror (err)); err = register_commands (ctx); if (err) log_fatal ("register_commands failed: %s\n", gpg_strerror(err)); if (debug) assuan_set_log_stream (ctx, stderr); assuan_register_reset_notify (ctx, reset_notify); assuan_register_input_notify (ctx, input_notify); assuan_register_output_notify (ctx, output_notify); - + state = new_state (); - + assuan_set_pointer (ctx, state); while (!shutdown_pending) { int done; err = assuan_accept (ctx); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF || err == -1) log_error ("assuan_accept failed: %s\n", gpg_strerror (err)); break; } - + log_info ("client connected. Client's pid is %ld\n", (long)assuan_get_pid (ctx)); do { /* We need to use select here so that we can accept supplemental connections from the client as requested by the DATAPORT command. */ fd_set rfds; int connfd, datafd, max_fd; connfd = HANDLE2SOCKET (get_connection_fd (ctx)); FD_ZERO (&rfds); FD_SET (connfd, &rfds); max_fd = connfd; if (state->dataport_listen_fd != ASSUAN_INVALID_FD) { datafd = HANDLE2SOCKET (state->dataport_listen_fd); FD_SET (datafd, &rfds); if (datafd > max_fd) max_fd = datafd; } else datafd = -1; if (select (max_fd + 1, &rfds, NULL, NULL, NULL) > 0) { if (datafd != -1 && FD_ISSET (datafd, &rfds)) { struct sockaddr_in clnt_addr; socklen_t len = sizeof clnt_addr; int fd; fd = accept (datafd, (struct sockaddr*)&clnt_addr, &len); if (fd == -1) { err = gpg_err_code_from_syserror (); assuan_sock_close (state->dataport_listen_fd); state->dataport_listen_fd = ASSUAN_INVALID_FD; log_error ("accepting on dataport failed: %s\n", gpg_strerror (err)); state->dataport_accept_err = err; err = 0; } else { /* No more need for the listening socket. */ assuan_sock_close (state->dataport_listen_fd); state->dataport_listen_fd = ASSUAN_INVALID_FD; /* Record the accepted fd. */ state->dataport_accept_err = 0; state->dataport_accepted_fd = SOCKET2HANDLE (fd); } } if (FD_ISSET (connfd, &rfds)) { err = assuan_process_next (ctx, &done); } } } while (!err && !done && !shutdown_pending); if (err) log_error ("assuan_process failed: %s\n", gpg_strerror (err)); } - + assuan_sock_close (server_fd); assuan_release (ctx); release_state (state); } -/* - +/* + M A I N */ -int +int main (int argc, char **argv) { gpg_error_t err; int last_argc = -1; if (argc) { 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", log_get_prefix ()); exit (0); } if (!strcmp (*argv, "--verbose")) { verbose = 1; argc--; argv++; } else if (!strcmp (*argv, "--debug")) { verbose = debug = 1; argc--; argv++; } } assuan_set_assuan_log_prefix (log_prefix); if (debug) assuan_set_assuan_log_stream (stderr); err = assuan_sock_init (); if (err) log_fatal ("assuan_sock_init failed: %s\n", gpg_strerror (err)); log_info ("server starting...\n"); server (); log_info ("server finished\n"); assuan_sock_deinit (); return errorcount ? 1 : 0; } -